[OBSOLETE] Thermostat Manager - An alternative to Thermostat Mode Director

I agree. I’d rather creat my own logic.

I have a heat pump with aux/emergency heat as well. Thermostats installed with the system are Honeywell Vision Pro 8000. They connect via Wi-Fi and are supported in ST via the “Honeywell TCC (Connect)” smartapp.

Installation manual with wiring diagram

https://forwardthinking.honeywell.com/related_links/thermostats/wifi-visionpro-8000/install/Installation_Manual.pdf

Alright fellas. I added the capability to force emergency heat mode. The code was pretty straightforward so I went ahead and updated the repository. I’ll keep looking over some of the other suggestions and will keep you posted.

Hal, for now, let me suggest changing the heating threshold to 70. Thresholds == setpoints is OK. Thermostat Manager won’t send commands to enable a mode it is already in. I’m still thinking over the program changes. I’ll keep you posted.

1 Like

I noted but haven’t yet used the “Set for specific mode(s)” option.

If I want to set different thresholds for Day and Night, I need to add instances of Thermostat Manager, correct?

That is correct.

1 Like

Pacman45,

This change has been implemented. I feel pretty good about the results so I went ahead and put them in the repo. Just update your software and you’ll have it. There is a new emergency heat settings page that includes the option to switch to emergency heat mode based on an outdoor sensor. Additionally, if the temperature outside rises above the user configured emergency heat threshold, the system will return to normal heat mode. As always, there is also a disable slider so you don’t have to remove the values to turn it off. You can also optionally have emergency heat mode always activate in place of regular heat mode.

I did look into alternative outdoor temperature sources but from what I saw they don’t seem to be very reliable. I’ll just leave it at this for now.

As discussed, I cannot test these new features myself. I am counting on you! Please test it out and let me know how it goes.

Next I’ll work on implementing multi-thermostat capability.

I have HVAC technician coming next week to install the thermostat I need. My efforts to install the Radio Thermostat CT-101 failed and the HVAC technician I had in last week didn’t want to try and get it to work—he wanted to sell me a $700 Honeywell T6 which I got conflicting information as to whether it was compatible with SmartThings or not. I finally called the HVAC company that installed my heat pump 2 years ago and that technician is coming out next week to set me up. I’ll run it through the tests after that.

Maybe slightly off topic but my Honeywell TCC SmartApp silently ceased working after the most recent ST hub firmware update.

After unsuccessfully trying to remove the SmartApp, I was able to get it working by going back thru its configuration page.

I removed all instances of Thermostat Manager while trying to remove TCC so had to recreate those.

Hi Jordan,

It looks like your app will allow me to select my Weather Station Tile as an external temperature source so I am excited about that.

I’m concerned about unnecessary cycling as the outside temperature rises and falls past the emergency heat threshold. How hard would it be to have a user configurable setting higher than that threshold for switching back to normal heat. That would minimize cycling between emergency heat and normal heat. I was going to use an 8°-10° spread like 25° and 33 or 35°. So if the temperature dropped to 24° overnight, the system would switch to emergency heat and bypass the heat pump. It would remain in that mode during the day’s temperature fluctuations until a slight warmup occurred to 35°. Then it would stay in normal heat until it got real cold again. Just a thought. If you’re busy with other projects I understand. I actually was going to attempt to figure out the code myself, but I don’t quite understand GitHub and forking. I don’t want to mess your stuff up.

Matt

Ah, that’s good. I did intend it that way but I haven’t tried it. From what I read on the forums the temperature from that system can be unreliable. Let me know how it works for you.

Already built it in. If your outside temperature sensor rises above the emergency heat threshold, the system will switch back to normal heat mode (so long as the inside temperature is not above the cooling threshold that is). Let me know if I am misunderstanding your request.

All,

I should mention that the 20181011 build of Thermostat Manager renamed the variable for the Smart Home Monitor Based SetPoint Enforcement disable slider. In the settings, it is labeled, “Disable Smart Home Monitor Based SetPoint Enforcement”. This may have been ill advised, and I’ll avoid changing these variables in the future but it is done now.

For everyone who hasn’t installed or re-installed Thermostat Manager since this build number, you will need to change the value of this slider (enable or disable it) and then save Thermostat Manager. Then you can change it back to the value that you want and save again in order for it to function properly. I recommend doing this even if you don’t use Smart Home Monitor Based SetPoint Enforcement. Sorry for the inconvenience.

If you have installed or re-installed since October 11, 2018, then you can disregard this message.

I’ve been searching for a smartapp that will delay turning back on a thermostat after a contact sensor is closed. I see this has the delay to turn off when opened, so I’m wondering if this functionality could be easily added.

Here’s the reasoning. Radiant heat heats everything in the room by contact. In my case, this is a garage that has a massive amount of cement. When the garage door opens, cool air rushes in, however, my floor and everything in it is still warm. If I close the garage door, the air will heat up to a significant degree over a period of time without using the thermostat. So I’d like to set some relative delay to that activation. I imagine this would be good for high traffic doors as well. A hold down timer.

I had similar request about year ago so I modified the code to be able to do the following:
-change the set point based on the home mode. So for example when I go to sleep I set the night mode on that triggers the smart app to send new setpoints.
-add delay on off timers. There are 2 delay timers on open contact, the first one is modifying the setpoint and the second is switching off the device. I use this to avoid too often switching on/off a device, so for example in heating mode it will first decrease the set point after 1st timer expires (say shortly leaving the door open) and it will switch off the device upon 2nd timer expire.
I am lousy in programming so my solution was to hardcode the mode names.
I also don’t use the automatic heat /cool mode change so I’m not sure If I messed up something there but the 2 functions described above work.
My idea was upon contact open to set the setpoint to the outside temp but in the meantime my outside temp sensor link coming from Domoticz broke so instead I hardcoded the temp also.
Below is my modified code
/**

  • Thermostat Manager
  • Build 2018040102
  • Copyright 2018 Darko Gjorgjievski (from v2018040102 by Jordan Markwell
  • Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except
  • in compliance with the License. You may obtain a copy of the License at:
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  • on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  • for the specific language governing permissions and limitations under the License.
  • ChangeLog:
  •  	05: added disable switch for auto change heat/cool mode
    
  •  	04: added on delay timer on closed contact
    
  •      03: added disable switch  
    
  •      02: added change setpoints based on Home Mode   
    
  •      01: on open window set the setpoint to outside temperature instead of power off   
    

*/

definition(
name: “Thermostat Manager DG”,
namespace: “darkogorgievski”,
author: “Darko Gjorgjievski”,
description: “Automatically changes the thermostat mode in response to changes in temperature that exceed user defined thresholds.”,
category: “Convenience”,
iconUrl: “https://s3.amazonaws.com/smartapp-icons/Meta/temp_thermo.png”,
iconX2Url: “https://s3.amazonaws.com/smartapp-icons/Meta/temp_thermo@2x.png”,
iconX3Url: “https://s3.amazonaws.com/smartapp-icons/Meta/temp_thermo@2x.png”,
pausable: false
)

preferences {
page(name: “mainPage”)
page(name: “setPointPage”)
page(name: “notificationPage”)
page(name: “energySaverPage”)
}

def mainPage() {
dynamicPage(name: “mainPage”, title: “Thermostat Manager”, install: true, uninstall: true) {
section() {
paragraph “Automatically changes the thermostat mode in response to changes in temperature that exceed user defined thresholds.”
}
section(“Main Configuration”) {
input “thermostat”, “capability.thermostat”, title: “Thermostat”, multiple: false, required: true
// DG on
input “outsideTemperature”, “capability.thermostat”, title: “Outside Temperature”, multiple: false, required: false
// DG off
paragraph “When the temperature rises higher than the cooling threshold, Thermostat Manager will set cooling mode. Recommended value: 75F (24C)”
input name: “coolingThreshold”, title: “Cooling Threshold”, type: “number”, required: false
paragraph “When the temperature falls below the heating threshold, Thermostat Manager will set heating mode. Recommended value: 70F (21C)”
input name: “heatingThreshold”, title: “Heating Threshold”, type: “number”, required: false
// dg on
input name: “disableSwitch”, title: “Enable Manager Switch”, type: “capability.switch”, multiple: false, required: false
input name: “disableModeSwitch”, title: “Enable Mode Change Switch”, type: “capability.switch”, multiple: false, required: false

		paragraph "Enter setpoints for different modes"
        input name: "coolingSetpointHome", title: "Cooling Setpoint Home", type: "number", required: false
		input name: "heatingSetpointHome", title: "Heating Setpoint Home", type: "number", required: false
		input name: "coolingSetpointAway", title: "Cooling Setpoint Away", type: "number", required: false
		input name: "heatingSetpointAway", title: "Heating Setpoint Away", type: "number", required: false
		input name: "coolingSetpointDecaSpijat", title: "Cooling Setpoint DecaSpijat", type: "number", required: false
		input name: "heatingSetpointDecaSpijat", title: "Heating Setpoint DecaSpijat", type: "number", required: false
		input name: "coolingSetpointNight", title: "Cooling Setpoint Night", type: "number", required: false
		input name: "heatingSetpointNight", title: "Heating Setpoint Night", type: "number", required: false
		input name: "coolingSetpointOdmor", title: "Cooling Setpoint Odmor", type: "number", required: false
		input name: "heatingSetpointOdmor", title: "Heating Setpoint Odmor", type: "number", required: false
		
	//dg off       
	}

    section("Tips") {
        paragraph "If you set the cooling threshold at the lowest setting you use in your modes and you set the heating threshold at the highest setting you use in your modes, you will not need to create multiple instances of Thermostat Manager."
        paragraph "If you want to use Thermostat Manager to set cooling mode only or to set heating mode only, remove the value for the threshold that you want to be ignored or set it to 0."
    }
    section("Optional Settings") {
        input name: "setFan", title: "Maintain Auto Fan Mode", type: "bool", defaultValue: true, required: true
        input name: "manualOverride", title: "Allow Manual Thermostat Off to Override Thermostat Manager", type: "bool", defaultValue: false, required: true
        input name: "debug", title: "Debug Logging", type: "bool", defaultValue: false, required: true
        input name: "disable", title: "Disable Thermostat Manager", type: "bool", defaultValue: false, required: true
        
        href "setPointPage", title: "Smart Home Monitor Based SetPoint Enforcement"
        href "energySaverPage", title: "Energy Saver"
        href "notificationPage", title: "Notification Settings"
        
        label(title: "Assign a name", required: false)
        mode(title: "Set for specific mode(s)")
    }
}

}

def setPointPage() {
dynamicPage(name: “setPointPage”, title: “Smart Home Monitor Based SetPoint Enforcement”) {
section() {
paragraph “These optional settings allow you use Thermostat Manager to set your thermostat’s cooling and heating setPoints based on the status of Smart Home Monitor; SmartThings’ built-in security system. SetPoints will be set only when a thermostat mode change occurs (e.g. heating to cooling) and only the setPoint for the incoming mode will be set (e.g. A change from heating mode to cooling mode would prompt the cooling setPoint to be set).”
}
section(“Disarmed Status”) {
input name: “offCoolingSetPoint”, title: “Cooling SetPoint”, type: “number”, required: false
input name: “offHeatingSetPoint”, title: “Heating SetPoint”, type: “number”, required: false
}
section(“Armed (stay) Status”) {
input name: “stayCoolingSetPoint”, title: “Cooling SetPoint”, type: “number”, required: false
input name: “stayHeatingSetPoint”, title: “Heating SetPoint”, type: “number”, required: false
}
section(“Armed (away) Status”) {
input name: “awayCoolingSetPoint”, title: “Cooling SetPoint”, type: “number”, required: false
input name: “awayHeatingSetPoint”, title: “Heating SetPoint”, type: “number”, required: false
}
section() {
input name: “disableSHMSPEnforce”, title: “Disable Smart Home Monitor Based SetPoint Enforcement”, type: “bool”, defaultValue: false, required: true
}
}
}

def notificationPage() {
dynamicPage(name:“notificationPage”, title:“Notification Settings”) {
section() {
input(name: “recipients”, title: “Select Notification Recipients”, type: “contact”, required: false) {
input name: “phone”, title: “Enter Phone Number of Text Message Notification Recipient”, type: “phone”, required: false
}
input name: “pushNotify”, title: “Send Push Notifications”, type: “bool”, defaultValue: false, required: true
input name: “disableNotifications”, title: “Disable Notifications”, type: “bool”, defaultValue: false, required: true
}
}
}

def energySaverPage() {
dynamicPage(name:“energySaverPage”, title:“Energy Saver”) {
section() {
paragraph “Energy Saver will temporarily pause the thermostat (by placing it in “off” mode) in the case that any selected contact sensors are left open for a specified number of minutes.”
input name: “contact”, title: “Contact Sensors”, type: “capability.contactSensor”, multiple: true, required: false
input name: “openContactAdjust”, title: “Timeout to adjust setpoint on open contact in minutes”, type: “number”, required: false
input name: “openContactToOff”, title: “Timeout to power off on open contact in minutes”, type: “number”, required: false
input name: “closedContactDelay”, title: “Restart delay closed contact in minutes”, type: “number”, required: false
input name: “disableEnergySaver”, title: “Disable Energy Saver”, type: “bool”, defaultValue: false, required: true
}
}
}

def installed() {
log.debug “Installed with settings: ${settings}”
initialize()
}

def updated() {
state.clear()

log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()

}

def initialize() {
//subscribe(thermostat, “temperature”, tempHandler)
subscribe(contact, “contact.open”, contactOpenHandler)
subscribe(contact, “contact.closed”, contactClosedHandler)
//subscribe(location, “alarmSystemStatus”, alarmStatusHandler)
subscribe(location, “mode”, modeChangeHandler)
//state.lastHomeMode = location.mode
//log.debug “Thermostat Manager - Smart Home Mode: ${state.lastHomeMode}”
}

def modeChangeHandler(event) {
def openContact = contact?.currentValue(“contact”)?.contains(“open”)
def currentTemp = thermostat.currentValue(“temperature”)
def coolingSetpoint = thermostat.currentValue(“coolingSetpoint”)
def heatingSetpoint = thermostat.currentValue(“heatingSetpoint”)
def thermostatMode = thermostat.currentValue(“thermostatMode”)
def fanMode = thermostat.currentValue(“thermostatFanMode”)
def homeMode = location.mode
def securityStatus = location.currentValue(“alarmSystemStatus”)
def disableManager = disableSwitch?.currentValue(“switch”)?.contains(“off”)

if (!openContact && state.lastThermostatMode) {
    // Invalid values could potentially occur if using multiple instances of Thermostat Manager.
    state.clear()
}

if (debug) {
    if (!disableEnergySaver && !disableManager && contact) {
        log.debug "Thermostat Manager - At least one contact is open: ${openContact}"
        if (state.lastThermostatMode) { log.debug "Thermostat Manager is currently paused." }
    }
    log.debug "Thermostat Manager - Smart Home Monitor Status: ${securityStatus}"
    log.debug "Thermostat Manager - Hello Home Mode: ${homeMode}"
    log.debug "Thermostat Manager - Fan Mode: ${fanMode}"
    log.debug "Thermostat Manager - Mode: ${thermostatMode}"
    log.debug "Thermostat Manager - Cooling Setpoint: ${coolingSetpoint} | Heating Setpoint: ${heatingSetpoint}"
    log.debug "Thermostat Manager - Temperature: ${currentTemp}"
	log.debug "Thermostat Manager - Temperature: ${thermostat}"
	
	log.debug "Thermostat Manager - coolingSetpointHome: ${coolingSetpointHome}"
	log.debug "Thermostat Manager - heatingSetpointHome: ${heatingSetpointHome}"
	log.debug "Thermostat Manager - coolingSetpointAway: ${coolingSetpointAway}"
	log.debug "Thermostat Manager - heatingSetpointAway: ${heatingSetpointAway}"
	log.debug "Thermostat Manager - coolingSetpointDecaSpijat: ${coolingSetpointDecaSpijat}"
	log.debug "Thermostat Manager - heatingSetpointDecaSpijat: ${heatingSetpointDecaSpijat}"
	log.debug "Thermostat Manager - coolingSetpointNight: ${coolingSetpointNight}"
	log.debug "Thermostat Manager - heatingSetpointNight: ${heatingSetpointNight}"
	log.debug "Thermostat Manager - coolingSetpointOdmor: ${coolingSetpointOdmor}"
	log.debug "Thermostat Manager - heatingSetpointOdmor: ${heatingSetpointOdmor}"
	log.debug "disable ${disable}"
	log.debug "disableEnergySaver ${disableEnergySaver}"
	log.debug "disableManager ${disableManager}"
	log.debug "manualOverride ${manualOverride}"
	log.debug !(state.lastThermostatMode)
	log.debug (thermostatMode != "off")
	log.debug "disableSHMSPEnforce ${disableSHMSPEnforce}"
}

if ( (!disable) && !disableManager && (setFan) && (fanMode != "auto") ) {
    logNNotify("Thermostat Manager setting ${thermostat} fan mode auto.")
    thermostat.fanAuto()
}

// Temperature setPoints only stick for the active mode.
if (
        !disable && !disableManager && (disableEnergySaver || !state.lastThermostatMode) &&
        ( !manualOverride || (manualOverride && (thermostatMode != "off") ) ) 
   ) {
    
    if (disableSHMSPEnforce) {
		switch (homeMode) {
			case "Home":
				logNNotify("Thermostat Manager - ${thermostat} Setting Home mode.")
				if ((coolingSetpointHome)  && (thermostatMode == "cool")) {
					runIn( 2, enforceCoolingSetPoint, [data: [setPoint: coolingSetpointHome] ] )}
				else if ((heatingSetpointHome) && (thermostatMode == "heat")) {
					runIn( 2, enforceHeatingSetPoint, [data: [setPoint: heatingSetpointHome] ] )}
			break
			case "DecaSpijat":
				logNNotify("Thermostat Manager - ${thermostat} Setting DecaSpijat mode.")
				if ((coolingSetpointDecaSpijat) && (thermostatMode == "cool")) {
					runIn( 2, enforceCoolingSetPoint, [data: [setPoint: coolingSetpointDecaSpijat] ] )}
				else if ((heatingSetpointDecaSpijat) && (thermostatMode == "heat")) {
					runIn( 2, enforceHeatingSetPoint, [data: [setPoint: heatingSetpointDecaSpijat] ] )}
			break
			case "Away":
				logNNotify("Thermostat Manager - ${thermostat} Setting Away mode.")
				if ((coolingSetpointAway) && (thermostatMode == "cool")) {
					runIn( 2, enforceCoolingSetPoint, [data: [setPoint: coolingSetpointAway] ] )}
				else if ((heatingSetpointAway) && (thermostatMode == "heat")) {
					runIn( 2, enforceHeatingSetPoint, [data: [setPoint: heatingSetpointAway] ] )}
			break
			case "Night":
				logNNotify("Thermostat Manager - ${thermostat} Setting Night mode.")
				if ((coolingSetpointNight) && (thermostatMode == "cool")) {
					runIn( 2, enforceCoolingSetPoint, [data: [setPoint: coolingSetpointNight] ] )}
				else if ((heatingSetpointNight) && (thermostatMode == "heat")) {
					runIn( 2, enforceHeatingSetPoint, [data: [setPoint: heatingSetpointNight] ] )}
			break
			case "Odmor":
				logNNotify("Thermostat Manager - ${thermostat} Setting Odmor mode.")
				if ((coolingSetpointOdmor) && (thermostatMode == "cool")) {
					runIn( 2, enforceCoolingSetPoint, [data: [setPoint: coolingSetpointOdmor] ] )}
				else if ((heatingSetpointOdmor) && (thermostatMode == "heat")) {
					runIn( 2, enforceHeatingSetPoint, [data: [setPoint: heatingSetpointOdmor] ] )}
			break
			default:
    return "ERROR"
		}
	}

}

}

def tempHandler(event) {
def openContact = contact?.currentValue(“contact”)?.contains(“open”)
def currentTemp = thermostat.currentValue(“temperature”)
def coolingSetpoint = thermostat.currentValue(“coolingSetpoint”)
def heatingSetpoint = thermostat.currentValue(“heatingSetpoint”)
def thermostatMode = thermostat.currentValue(“thermostatMode”)
def fanMode = thermostat.currentValue(“thermostatFanMode”)
def homeMode = location.mode
def securityStatus = location.currentValue(“alarmSystemStatus”)

if (!openContact && state.lastThermostatMode) {
    // Invalid values could potentially occur if using multiple instances of Thermostat Manager.
    state.clear()
}

if (debug) {
    if (!disableEnergySaver && !disableManager && contact) {
        log.debug "Thermostat Manager - At least one contact is open: ${openContact}"
        if (state.lastThermostatMode) { log.debug "Thermostat Manager is currently paused." }
    }
    log.debug "Thermostat Manager - Smart Home Monitor Status: ${securityStatus}"
    log.debug "Thermostat Manager - Hello Home Mode: ${homeMode}"
    log.debug "Thermostat Manager - Fan Mode: ${fanMode}"
    log.debug "Thermostat Manager - Mode: ${thermostatMode}"
    log.debug "Thermostat Manager - Cooling Setpoint: ${coolingSetpoint} | Heating Setpoint: ${heatingSetpoint}"
    log.debug "Thermostat Manager - Temperature: ${currentTemp}"
}

if ( (!disable) && !disableManager && (setFan) && (fanMode != "auto") ) {
    logNNotify("Thermostat Manager setting ${thermostat} fan mode auto.")
    thermostat.fanAuto()
}

// Temperature setPoints only stick for the active mode.
if (
        !disable && !disableManager && !disableModeSwitch && (disableEnergySaver || !state.lastThermostatMode) &&
        ( (thermostatMode != "cool") && ( !manualOverride || (manualOverride && (thermostatMode != "off") ) ) ) &&
        coolingThreshold && ( Math.round(currentTemp) > Math.round(coolingThreshold) )
   ) {
    
    logNNotify("Thermostat Manager - The temperature has risen to ${currentTemp}. Setting ${thermostat} cooling mode.")
    thermostat.cool()
    
    if (!disableSHMSPEnforce) {
        if ( (securityStatus == "off") && (offCoolingSetPoint) ) {
            // SetPoints won't be changed unless the thermostat is already in the required mode.
            runIn( 60, enforceCoolingSetPoint, [data: [setPoint: offCoolingSetPoint] ] )
        } else if ( (securityStatus == "stay") && (stayCoolingSetPoint) ) {
            runIn( 60, enforceCoolingSetPoint, [data: [setPoint: stayCoolingSetPoint] ] )
        } else if ( (securityStatus == "away") && (awayCoolingSetPoint) ) {
            runIn( 60, enforceCoolingSetPoint, [data: [setPoint: awayCoolingSetPoint] ] )
        }
    }
} else if (
        !disable && !disableManager && (disableEnergySaver || !state.lastThermostatMode) &&
        ( (thermostatMode != "heat") && ( !manualOverride || (manualOverride && (thermostatMode != "off") ) ) ) &&
        heatingThreshold && ( Math.round(currentTemp) < Math.round(heatingThreshold) )
    ) {
    
    logNNotify("Thermostat Manager - The temperature has fallen to ${currentTemp}. Setting ${thermostat} heating mode.")
    thermostat.heat()
    
    if (!disableSHMSPEnforce) {
        if ( (securityStatus == "off") && (offHeatingSetPoint) ) {
            runIn( 60, enforceHeatingSetPoint, [data: [setPoint: offHeatingSetPoint] ] )
        } else if ( (securityStatus == "stay") && (stayHeatingSetPoint) ) {
            runIn( 60, enforceHeatingSetPoint, [data: [setPoint: stayHeatingSetPoint] ] )
        } else if ( (securityStatus == "away") && (awayHeatingSetPoint) ) {
            runIn( 60, enforceHeatingSetPoint, [data: [setPoint: awayHeatingSetPoint] ] )
        }
    }
} else if (debug) {
    log.debug "Thermostat Manager standing by."
}

}

def logNNotify(message) {
log.info message
if ( (!disableNotifications) && ( (location.contactBookEnabled && recipients) || phone || pushNotify ) ) {
if (location.contactBookEnabled && recipients) {
sendNotificationToContacts(message, recipients)
} else if (phone) {
sendSms(phone, message)
}

    if (pushNotify) {
        sendPush(message)
    }
} else {
    sendNotificationEvent(message)
}

}

def enforceCoolingSetPoint(data) {
logNNotify(“Thermostat Manager is setting the {thermostat} cooling setPoint to {data.setPoint}.”)
thermostat.setCoolingSetpoint(data.setPoint)
}

def enforceHeatingSetPoint(data) {
logNNotify(“Thermostat Manager is setting the {thermostat} heating setPoint to {data.setPoint}.”)
thermostat.setHeatingSetpoint(data.setPoint)
}

def contactOpenHandler(event) {
def thermostatMode = thermostat.currentValue(“thermostatMode”)

if (debug) {
    log.debug "Thermostat Manager - A contact has been opened."
}

if (!disable && !disableManager && !disableEnergySaver && (thermostatMode != "off") && !state.openContactReported) {
    // If the thermostat is not off and all of the contacts were closed previously.
    state.openContactReported = true
	unschedule(closedContactDelaySub)
    runIn( (openContactToOff * 60), openContactPause )
	runIn( (openContactAdjust * 60), openContactSetPointToOutside )
	
    log.debug "Thermostat Manager - A contact has been opened. Initiating countdown to thermostat pause."
}

}

def contactClosedHandler(event) {
if (debug) {
log.debug “Thermostat Manager - A contact has been closed.”
}

if (!disable && !disableManager && !disableEnergySaver && state.openContactReported) {
    // If there was an open contact previously.
    if ( !contact.currentValue("contact").contains("open") ) {
        // All monitored contacts have been closed. Discontinue any existing countdown.
        log.debug "Thermostat Manager - All contacts have been closed. Discontinuing any existing thermostat pause countdown."
        unschedule(openContactPause)
		unschedule(openContactSetPointToOutside)
        
		runIn( (closedContactDelay * 60), ClosedContactDelaySub )
        
        state.openContactReported = false
    }
}

}

def ClosedContactDelaySub() {
if (state.lastThermostatMode) {
// If the thermostat is currently paused, restore it to its previous state.
if (state.lastThermostatMode == “cool”) {
//logNNotify(“Thermostat Manager - All contacts have been closed. Restoring cooling mode.”)
thermostat.cool()
enforceCoolingSetPoint (setPoint: state.lastCoolingSetpoint )
logNNotify(“Thermostat Manager - All contacts have been closed. Restoring {thermostat} cooling mode with setpoint to {state.lastCoolingSetpoint} °C.”)
} else if (state.lastThermostatMode == “auto”) {
logNNotify(“Thermostat Manager - All contacts have been closed. Restoring {thermostat} auto mode.") thermostat.auto() } else { // state.openContactReported will not be set unless (state.lastThermostatMode != "off"). // It appears that most thermostat device handlers disallow, "emergency heat" mode. logNNotify("Thermostat Manager - All contacts have been closed. Setting {thermostat} heating mode.”)
thermostat.heat()
enforceHeatingSetPoint (setPoint: state.lastHeatingSetpoint )
logNNotify(“Thermostat Manager - All contacts have been closed. Restoring {thermostat} heating mode with setpoint to {state.lastHeatingSetpoint} °C.”)
}
state.lastThermostatMode = null
}
}

def openContactPause() {
def thermostatMode = thermostat.currentValue(“thermostatMode”)

if ( (thermostatMode != "off") && contact.currentValue("contact").contains("open") ) {
    // If the thermostat is not in, "off" mode and any monitored contact is open.
    state.lastThermostatMode = thermostat.currentValue("thermostatMode")
    logNNotify("Thermostat Manager is turning the thermostat ${thermostat} off temporarily due to an open contact.")
    thermostat.off()
} else { // If the thermostat was turned off after an open contact was reported or no monitored contacts remain open.
    state.clear()
}

}

//dg on
def openContactSetPointToOutside() {
def thermostatMode = thermostat.currentValue(“thermostatMode”)
def outsideTemSP = outsideTemperature.currentValue(“temperature”)
//if (( outsideTemSP < 10 ) && (thermostatMode = “heat”)) {
if ((thermostatMode = “heat”)) {
outsideTemSP =10
}
//else if (( outsideTemSP > 30 ) && (thermostatMode = “cool”)) {
else if ((thermostatMode = “cool”)) {
outsideTemSP =30
}
if ((thermostatMode != “off”) && contact.currentValue(“contact”).contains(“open”) ) {
// If the thermostat is not in, “off” mode and any monitored contact is open.
state.lastThermostatMode = thermostat.currentValue(“thermostatMode”)
state.lastHeatingSetpoint = thermostat.currentValue(“heatingSetpoint”)
state.lastCoolingSetpoint = thermostat.currentValue(“coolingSetpoint”)
log.debug “state.lastHeatingSetpoint {state.lastHeatingSetpoint}" log.debug "state.lastCoolingSetpoint {state.lastCoolingSetpoint}”

    if (thermostatMode == "heat") {
		enforceHeatingSetPoint (setPoint: outsideTemSP )
		}
	else if  (thermostatMode == "cool") {
		enforceCoolingSetPoint (setPoint: outsideTemSP )
		}
    logNNotify("Thermostat Manager is adjusting the thermostat ${thermostat} setpoint to ${outsideTemSP} °C temporarily due to an open contact.")
//enforceHeatingSetPoint, [data: [setPoint: offHeatingSetPoint] ]
} else { // If the thermostat was turned off after an open contact was reported or no monitored contacts remain open.
    state.clear()
}

}
//dg off

Hi Jordan,

I’m not sure you grasped the essence of my last question regarding a user selectable “return-to-normal-heat mode” parameter that would be different from the original “set emergency heat mode” parameter. I understand there are installations where minimizing the time spent in emergency heat mode is important. Mine is not one of them. My backup heat is the original gas-fired water circulated baseboard heat that does a wonderful job as a primary heating system. It’s just more expensive to run than the heat pump at cool to mildly cold outdoor temps. When the outside temperature dips below 30, I want my emergency heat running full time when the temperature is fluctuating above and below 30° during daytime/nighttime over the next week or so. When a relative warm front comes through and temps climb back up close to 40°, then I want the heat pump to be primary source. I went ahead and modified your code to create that higher “return-to-normal heat threshold” and placed it in a fork in github. I’ll create a pull request for you to evaluate. I also created another experimental capability. Sometimes here in the northeast the overnight lows get down in the single digits or lower and if I let my tile routines run their nightly ritual, it will be a miserable morning waiting for the indoor temperature to rise to a comfortable level. I created a “bitter-cold” setting that just changes the location mode from “Home” to “Home—Bitter Cold” and excluded my overnight tile routines from running in that mode. That may not be a capability you are interested in, but it works great here!

The emergency heat mode is working like a champ!!

Could be, but the thing is that I did it without adding a new parameter to the program. The way I did it was to have the system revert to heat pump mode when the outdoor temperature rises above the, “emergency heat threshold”. Adding another parameter could cause chaos with other parts of the program unless you set a lot of conditions… but since we already have a parameter we can use, I saw no need for it. To me it sounds like what you are asking for is turn on EMERG HEAT if the temperature falls below 30, but don’t turn off the EMERG HEAT (and switch back to heat pump) unless the temperature rises above 40…

I’ll take a look. Could be I still don’t quite understand what you are asking for.

Thanks for reporting the results to me. Glad it is working well.

This sounds like an unusual request. Am I correct in my understanding that you have both a centrally heated garage and a heated floor in your garage?

I’ll take a look at this but I’m not sure it’d be a good candidate for the main branch.

Not at all, only a single heating system, but let’s simplify. Let’s say there’s a thermostat right inside a door that leads to the outside. Door opens, ST shuts the thermostat off. Great, this does that. Door closes, thermostat turns back on. Door opens again, repeat. Door closes… etc… etc… But let’s say this is just a busy part of the day. 8 kids coming home from school or something. It would be helpful to have a hold-down timer to keep the thermostat from turning back for some set number of minutes to keep the heating system from thrashing. If the door closes, wait X until turning it back on.

Sounds reasonable. I’ll add it. I’ll let you know when it gets implemented.

That’s awesome. Thank you muchly.