[OBSOLETE] Thermostat Mode Director

Thanks for the really cool app!

I don’t know if I am doing this wrong or not, but I setup the feature that turns off thermostat after contact is open for 5 minutes. This works, but it doesn’t restore thermostat back after contact closes. Is there another step I am missing?

I am using the app from github as of today’s date.

Thanks, Eddie

@slagle (and/or other folks that are making use of Outside Temperature to turn their HVAC on or off…):

Perhaps I’m late to the party here, but I’m having trouble grasping why the outside temperature should affect the set points of one’s thermostat or force shut it OFF completely…

Let’s just focus on Cooling for example… (So I don’t have to talk about two ends of the spectrum … the Heating mode is just the opposite case):

  1. Your “normal daytime” cooling setpoint is an economical 73* (i.e., at only indoor temperatures of 73* and above, the AC will turn on and stay on until it cools down to 73* or below – it probably will go a little below due to momentum of the cooling process, especially in a system where the fan continues for a while after the compressor turns off).

  2. In your “off” range, if outside temperature is 40* to 70* (I presume, inclusive), then that forces the AC into fully “off” mode. Let’s call the outside temperature 69* for this story.

  3. But the thermostat all by itself would have been very unlikely to have turned on the AC, since if it is below 70* outside, i.e., 69*, then it quite likely is below your setpoint of 73* inside too.

  4. Still, consider that still possibly, the indoor temperature could be reaching 74* or 82* or 96* … due to sun on your roof or a hot stove… but since the outside temperature is still 69*, you’ve still got your AC shutdown, and thus a house that can exceed the Cooling setpoint by any amount … quite uncomfortably.

So – I don’t understand why is this a desirable way to operate a thermostat?

  • If you have a high temperature tolerance of “pretty comfortable” then why not just set a higher cooling setpoint (i.e., 77* instead of 75*) regardless of the outdoor temperature?

  • In other words – I don’t understand the reason that outdoor temperature should affect indoor sensitivity.

  • If the sole purpose is to set the overall HVAC to the correct operation type (heating vs. cooling), presuming some thermostats don’t automatically change mode or you have the heating and cooling setpoint temperatures to be “too close to each other” that you accidentally cool the house in Winter; then I can understand changing the operation type to either heating or cooling based on an outside trigger, but why have an operation type of “off” for the range in-between?

  • So for a manual operating mode switch, once it hits the “warm weather” trigger outside (perhaps a 75* day), then the thermostat should be switched into Cooling mode and remain in cooling mode, until the “cool weather” trigger outside (perhaps a 65* day), then the thermostat is switched into Heating mode. In the meantime, completely regardless of the outside temperature, the thermostat does its job and maintains either the cooling or heating setpoint (but not both, unless it has it’s own automatic mode change).


I super appreciate your help! I must be missing some “point” here, and I hope my example scenario isn’t too confusing or nonsensical.

Simple answer.

The house I lived in before I moved would actually fluctuate ±4* or so depending on if the sun was out or not. It was still.comfortable for me to live in but too confusing for my thermostat to keep up with. So… Thermostat director was born.

After this app was fully created and integrated into my home I saved on average $56 a month in heating and cooling costs.

Also another considerations. Like my HVAC system was super inefficient so for instance if my internal temp reached 73* and my setpoint for cooling was 72, it would take 30ish minutes to cool that 1 degree. What I found is on mild days it was better to just turn off my thermostat as it would never get aboive 73-74 and I saved tons of cash. :smile:

Hey Tim, thanks for such a fast response … but 'Fraid I’m still dense. We can take this to PM if you think I’m cluttering this Thread.

So please help me understand the details of the scenario of your old house…

  1. Did you have a unit that did both Heating and Cooling?

  2. Did the Thermostat automatically select Heating or Cooling based on their respective setpoints, or Did the Thermostat require you to manually choose between Heating Mode or Cooling Mode?

  3. What would have been your preferred “average inside temperature” Heating and Cooling setpoints, respectively?

  4. What do you mean by “too confusing for my thermostat to keep up with”? Did the HVAC switch from Heating to Cooling mode automatically too often, and/or did the HVAC overheat or overcool the home by +4* and -4*, or … well, what did you mean? :wink:?

5 . Why not just raise your Cooling setpoint to 74* all the time, then, since that was, evidently, the temperature you found tolerable and efficient?

This was barely tolerable but i would tolerate it on mild days to save $$$

My HVAC would actually heat and cool on the same day in some cases and heating my home on a warm day seemed like a rediculous proposition to me lol.

Basically this was a way to automate my current cost saving methods i employed based on how it felt outside Without having to also pay attention to the heat/cooling set points. This was the best way to do it so my hello home phrases wouldn’t need to be changed daily for different set points etc. And the result was hundreds saved per year.

maybe living in my ex home for a month would give you and idea of the HVAC crapfest i was in lol.

I definitely understand the motivation and that “feel” may be more important than the relatively “binary” mode of the thermostat … and that lots of factors affect “feel”; including humidity and temperature variances within your home (upstairs, downstairs, near windows, proximity to ducts, under covers, etc.).

Believe me… I’m a “thermostat junkie” myself … 72* may feel perfect, but an hour later it’s too cold or too hot, even if the AC is a 100% perfect one at maintaining exactly 72* +/- 0.5*.

This above IS pretty much the only clear case for using outdoor temperature sensor – i.e., to change the mode of the HVAC to either exclusively “Heating” or exclusively “Cooling” – or forcing it to do exclusive Heating or Cooling by forcing large large set points at one end or the other.

But that doesn’t explain the 40* to 70* range of your original post.

For example, I would configure:

  1. If outside temperature above 75*, then force HVAC into Cooling mode only, by setting the Heating Setpoint to 50*. The Cooling setpoint would be set to a normal, but efficient, desired temperature of 73*.

  2. If outside temperature below 65*, then force HVAC into Heating mode only, by setting the Cooling Setpoint to 90*. The Heating setpoint would be set to a normal, but efficient, desired temperature of 67*.

  3. If you live in the desert with a large outside temperature swing, it shouldn’t be a problem because the inside Setpoints would avoid activating the wrong mode:

e.g,. Hot day, 77* outside, so HVAC is in “Cooling mode” and maintains 73* inside. At night, cold wind drives the outside temperature to 63*, switching the HVAC to “Heating mode”, but the house retains heat and slowly cools from 73* to 70* and never turns on the heat because the Heating Setpoint is 67* – i.e., plenty of buffer range to avoid the HVAC from rapidly changing modes.

I think we are on the same page now :smile:

Perhaps at least in the same chapter, if not the exact same page :wink:.

I’ve isolated my understanding to the case where someone has an HVAC (heating and cooling in one system) that frequently switches from “heating mode” to “cooling mode” because the inside temperature swing exceeds the gap between the Heating Set Point and the Cooling Set Point.

Do some Thermostats only have a single combined Set Point with automatic switching between heating and cooling mode??? (Then, Indeed, that would justify shutting off the entire system if the outside temperature were in a range that caused frequent cycling around that set point.). But really? Do they?

1 Like

I like it based on an outside temp if for the only reason is that it reminds me to open a window, rather than spend more energy cooling the house

1 Like

Definitely sounds like a use case for sending a push notification (and combine with deactivating the HVAC upon window open). Otherwise your house starts to warm up for no announced reason.

Overall I really like this app. I also use your Hello, Home Phrase Director app and the Keep Me Cozy app to control temperature set points for my Nest thermostat based on presence and time of day (e.g. Away - Day, Home - Morning, Home - Day, etc.).

The one issue I’ve found is that while my thermostat is off in the neutral temperature range, the temperature set points won’t update if a mode change occurs. A common situation is my wife and I leave the house in the morning (causing Away - Day to execute) while in a neutral temperature range. That should change the cooling set point to 80 degrees. The upper threshold of the neutral range usually won’t be exceeded until mid-morning or later. When that happens the cooling set point is 74 degrees, which was the last cooling temperature set before the neutral temperature range was reached.

Do you know if it would be possible to update heating and cooling set points while in off mode, or does the thermostat have to be in either heating or cooling modes to change set points?

It should update. It sounds like this is something with Nest. The thermostat I use will update the setpoints in any mode. I bet this is something in the Nest device type or something Nest itself restricts. :frowning:

I’m using a Nest device type developed by dianoga. Overall it works great. Here is the code for the device type:

/**

  • Nest Direct
  • Author: dianoga7@3dgo.net
  • Code: https://github.com/smartthings-users/device-type.nest
  • INSTALLATION
  • =========================================
    1. Create a new device type (https://graph.api.smartthings.com/ide/devices)
  • Name: Nest
    
  • Author: dianoga7@3dgo.net
    
  • Capabilities:
    
  •     Polling
    
  •     Relative Humidity Measurement
    
  •     Thermostat
    
  •     Temperature Measurement
    
  •     Presence Sensor
    
  •     Sensor
    
  • Custom Attributes:
    
  •     temperatureUnit
    
  • Custom Commands:
    
  •     away
    
  •     present
    
  •     setPresence
    
  •     heatingSetpointUp
    
  •     heatingSetpointDown
    
  •     coolingSetpointUp
    
  •     coolingSetpointDown
    
  •     setFahrenheit
    
  •     setCelsius
    
    1. If you want to switch from slider controls to buttons, comment out the slider details line and uncomment the button details line.
    1. Create a new device (https://graph.api.smartthings.com/device/list)
  • Name: Your Choice
    
  • Device Network Id: Your Choice
    
  • Type: Nest (should be the last option)
    
  • Location: Choose the correct location
    
  • Hub/Group: Leave blank
    
    1. Update device preferences
  • Click on the new device to see the details.
    
  • Click the edit button next to Preferences
    
  • Fill in your information.
    
  • To find your serial number, login to http://home.nest.com. Click on the thermostat
    
  • you want to control. Under settings, go to Technical Info. Your serial number is
    
  • the second item.
    
    1. That’s it, you’re done.
  • Copyright © 2013 Brian Steere dianoga7@3dgo.net
  • Permission is hereby granted, free of charge, to any person obtaining a copy of this
  • software and associated documentation files (the “Software”), to deal in the Software
  • without restriction, including without limitation the rights to use, copy, modify,
  • merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  • permit persons to whom the Software is furnished to do so, subject to the following
  • conditions: The above copyright notice and this permission notice shall be included
  • in all copies or substantial portions of the Software.
  • THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  • INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  • PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  • HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  • CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  • OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */

preferences {
input(“username”, “text”, title: “Username”, description: “Your Nest username (usually an email address)”)
input(“password”, “password”, title: “Password”, description: “Your Nest password”)
input(“serial”, “text”, title: “Serial #”, description: “The serial number of your thermostat”)
}

// for the UI
metadata {
definition (name: “Nest”, namespace: “smartthings-users”, author: "dianoga7@3dgo.net") {
capability "Polling"
capability "Relative Humidity Measurement"
capability "Thermostat"
capability "Temperature Measurement"
capability "Presence Sensor"
capability “Sensor”

	command "away"
	command "present"
	command "setPresence"
	command "heatingSetpointUp"
	command "heatingSetpointDown"
	command "coolingSetpointUp"
	command "coolingSetpointDown"
	command "setFahrenheit"
	command "setCelsius"

	attribute "temperatureUnit", "string"
}

simulator {
	// TODO: define status and reply messages here
}

tiles {
	valueTile("temperature", "device.temperature", canChangeIcon: true, canChangeBackground:true) {
		state("temperature", label: '${currentValue}°', backgroundColors: [
			// Celsius Color Range
			[value: 0, color: "#153591"],
			[value: 7, color: "#1e9cbb"],
			[value: 15, color: "#90d2a7"],
			[value: 23, color: "#44b621"],
			[value: 29, color: "#f1d801"],
			[value: 33, color: "#d04e00"],
			[value: 36, color: "#bc2323"],
			// Fahrenheit Color Range
			[value: 40, color: "#153591"],
			[value: 44, color: "#1e9cbb"],
			[value: 59, color: "#90d2a7"],
			[value: 74, color: "#44b621"],
			[value: 84, color: "#f1d801"],
			[value: 92, color: "#d04e00"],
			[value: 96, color: "#bc2323"]
		]
		)
	}

	standardTile("thermostatMode", "device.thermostatMode", inactiveLabel: true, decoration: "flat") {
		state("auto", action:"thermostat.off", icon: "st.thermostat.auto")
		state("off", action:"thermostat.cool", icon: "st.thermostat.heating-cooling-off")
		state("cool", action:"thermostat.heat", icon: "st.thermostat.cool")
		state("heat", action:"thermostat.auto", icon: "st.thermostat.heat")
	}

	standardTile("thermostatFanMode", "device.thermostatFanMode", inactiveLabel: true, decoration: "flat") {
		state "auto", action:"thermostat.fanOn", icon: "st.thermostat.fan-auto"
		state "on", action:"thermostat.fanCirculate", icon: "st.thermostat.fan-on"
		state "circulate", action:"thermostat.fanAuto", icon: "st.thermostat.fan-circulate"
	}

	valueTile("heatingSetpoint", "device.heatingSetpoint") {
		state "default", label:'${currentValue}°', unit:"Heat", backgroundColor:"#bc2323"
	}

	valueTile("coolingSetpoint", "device.coolingSetpoint") {
		state "default", label:'${currentValue}°', unit:"Cool", backgroundColor:"#1e9cbb"
	}

	standardTile("thermostatOperatingState", "device.thermostatOperatingState", inactiveLabel: false, decoration: "flat") {
		state "idle", action:"polling.poll", label:'${name}', icon: "st.sonos.pause-icon"
		state "cooling", action:"polling.poll", label:'  ', icon: "st.thermostat.cooling", backgroundColor:"#1e9cbb"
		state "heating", action:"polling.poll", label:'  ', icon: "st.thermostat.heating", backgroundColor:"#bc2323"
		state "fan only", action:"polling.poll", label:'${name}', icon: "st.Appliances.appliances11"
	}

	valueTile("humidity", "device.humidity", inactiveLabel: false) {
		state "default", label:'${currentValue}%', unit:"Humidity"
	}

	standardTile("presence", "device.presence", inactiveLabel: false, decoration: "flat") {
		state "present", label:'${name}', action:"away", icon: "st.Home.home2"
		state "not present", label:'away', action:"present", icon: "st.Transportation.transportation5"
	}

	standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
		state "default", action:"polling.poll", icon:"st.secondary.refresh"
	}

	standardTile("temperatureUnit", "device.temperatureUnit", canChangeIcon: false) {
		state "fahrenheit",  label: "°F", icon: "st.alarm.temperature.normal", action:"setCelsius"
		state "celsius", label: "°C", icon: "st.alarm.temperature.normal", action:"setFahrenheit"
	}

	controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
		state "setHeatingSetpoint", label:'Set temperature to', action:"thermostat.setHeatingSetpoint"
	}
	controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
		state "setCoolingSetpoint", label:'Set temperature to', action:"thermostat.setCoolingSetpoint"
	}

	standardTile("heatingSetpointUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
		state "heatingSetpointUp", label:'  ', action:"heatingSetpointUp", icon:"st.thermostat.thermostat-up", backgroundColor:"#bc2323"
	}

	standardTile("heatingSetpointDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
		state "heatingSetpointDown", label:'  ', action:"heatingSetpointDown", icon:"st.thermostat.thermostat-down", backgroundColor:"#bc2323"
	}

	standardTile("coolingSetpointUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
		state "coolingSetpointUp", label:'  ', action:"coolingSetpointUp", icon:"st.thermostat.thermostat-up", backgroundColor:"#1e9cbb"
	}

	standardTile("coolingSetpointDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
		state "coolingSetpointDown", label:'  ', action:"coolingSetpointDown", icon:"st.thermostat.thermostat-down", backgroundColor:"#1e9cbb"
	}

	main(["temperature", "thermostatOperatingState", "humidity"])

	// ============================================================
	// Slider or Buttons...
	// To expose buttons, comment out the first detials line below and uncomment the second details line below.
	// To expose sliders, uncomment the first details line below and comment out the second details line below.

	// details(["temperature", "thermostatOperatingState", "humidity", "thermostatMode", "thermostatFanMode", "presence", "heatingSetpoint", "heatSliderControl", "coolingSetpoint", "coolSliderControl", "temperatureUnit", "refresh"])
	details(["temperature", "thermostatOperatingState", "humidity", "thermostatMode", "thermostatFanMode", "presence", "heatingSetpointDown", "heatingSetpoint", "heatingSetpointUp", "coolingSetpointDown", "coolingSetpoint", "coolingSetpointUp", "temperatureUnit", "refresh"])

	// ============================================================

}

}

// parse events into attributes
def parse(String description) {

}

// handle commands
def setHeatingSetpoint(temp) {
def latestThermostatMode = device.latestState(‘thermostatMode’)
def temperatureUnit = device.latestValue(‘temperatureUnit’)

switch (temperatureUnit) {
	case "celsius":
		if (temp) {
			if (temp < 9) {
				temp = 9
			}
			if (temp > 32) {
				temp = 32
			}
			if (latestThermostatMode.stringValue == 'auto') {
				api('temperature', ['target_change_pending': true, 'target_temperature_low': temp]) {
					sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: temperatureUnit, state: "heat")
				}
			} else if (latestThermostatMode.stringValue == 'heat') {
				api('temperature', ['target_change_pending': true, 'target_temperature': temp]) {
						sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: temperatureUnit, state: "heat")
				}
			}
		}
		break;
	default:
		if (temp) {
			if (temp < 51) {
				temp = 51
			}
			if (temp > 89) {
				temp = 89
			}
			if (latestThermostatMode.stringValue == 'auto') {
				api('temperature', ['target_change_pending': true, 'target_temperature_low': fToC(temp)]) {
					sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: temperatureUnit, state: "heat")
				}
			} else if (latestThermostatMode.stringValue == 'heat') {
				api('temperature', ['target_change_pending': true, 'target_temperature': fToC(temp)]) {
					sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: temperatureUnit, state: "heat")
				}
			}
		}
		break;
}
poll()

}

def coolingSetpointUp(){
int newSetpoint = device.currentValue(“coolingSetpoint”) + 1
log.debug "Setting cool set point up to: ${newSetpoint}"
setCoolingSetpoint(newSetpoint)
}

def coolingSetpointDown(){
int newSetpoint = device.currentValue(“coolingSetpoint”) - 1
log.debug "Setting cool set point down to: ${newSetpoint}"
setCoolingSetpoint(newSetpoint)
}

def setCoolingSetpoint(temp) {
def latestThermostatMode = device.latestState(‘thermostatMode’)
def temperatureUnit = device.latestValue(‘temperatureUnit’)

switch (temperatureUnit) {
	case "celsius":
		if (temp) {
			if (temp < 9) {
				temp = 9
			}
			if (temp > 32) {
				temp = 32
			}
			if (latestThermostatMode.stringValue == 'auto') {
				api('temperature', ['target_change_pending': true, 'target_temperature_high': temp]) {
					sendEvent(name: 'coolingSetpoint', value: coolingSetpoint, unit: temperatureUnit, state: "cool")
				}
			} else if (latestThermostatMode.stringValue == 'cool') {
				api('temperature', ['target_change_pending': true, 'target_temperature': temp]) {
					sendEvent(name: 'coolingSetpoint', value: coolingSetpoint, unit: temperatureUnit, state: "cool")
				}
			}
		}
		break;
	default:
		if (temp) {
			if (temp < 51) {
				temp = 51
			}
			if (temp > 89) {
				temp = 89
			}
			if (latestThermostatMode.stringValue == 'auto') {
				api('temperature', ['target_change_pending': true, 'target_temperature_high': fToC(temp)]) {
					sendEvent(name: 'coolingSetpoint', value: coolingSetpoint, unit: temperatureUnit, state: "cool")
				}
			} else if (latestThermostatMode.stringValue == 'cool') {
				api('temperature', ['target_change_pending': true, 'target_temperature': fToC(temp)]) {
					sendEvent(name: 'coolingSetpoint', value: coolingSetpoint, unit: temperatureUnit, state: "cool")
				}
			}
		}
		break;
}
poll()

}

def heatingSetpointUp(){
int newSetpoint = device.currentValue(“heatingSetpoint”) + 1
log.debug "Setting heat set point up to: ${newSetpoint}"
setHeatingSetpoint(newSetpoint)
}

def heatingSetpointDown(){
int newSetpoint = device.currentValue(“heatingSetpoint”) - 1
log.debug "Setting heat set point down to: ${newSetpoint}"
setHeatingSetpoint(newSetpoint)
}

def setFahrenheit() {
def temperatureUnit = "fahrenheit"
log.debug "Setting temperatureUnit to: ${temperatureUnit}"
sendEvent(name: “temperatureUnit”, value: temperatureUnit)
poll()
}

def setCelsius() {
def temperatureUnit = "celsius"
log.debug "Setting temperatureUnit to: ${temperatureUnit}"
sendEvent(name: “temperatureUnit”, value: temperatureUnit)
poll()
}

def off() {
setThermostatMode(‘off’)
}

def heat() {
setThermostatMode(‘heat’)
}

def emergencyHeat() {
setThermostatMode(‘heat’)
}

def cool() {
setThermostatMode(‘cool’)
}

def auto() {
setThermostatMode(‘range’)
}

def setThermostatMode(mode) {
mode = mode == ‘emergency heat’? ‘heat’ : mode

api('thermostat_mode', ['target_change_pending': true, 'target_temperature_type': mode]) {
	mode = mode == 'range' ? 'auto' : mode
	sendEvent(name: 'thermostatMode', value: mode)
	poll()
}

}

def fanOn() {
setThermostatFanMode(‘on’)
}

def fanAuto() {
setThermostatFanMode(‘auto’)
}

def fanCirculate() {
setThermostatFanMode(‘circulate’)
}

def setThermostatFanMode(mode) {
def modes = [
on: [‘fan_mode’: ‘on’],
auto: [‘fan_mode’: ‘auto’],
circulate: [‘fan_mode’: ‘duty-cycle’, ‘fan_duty_cycle’: 900]
]

api('fan_mode', modes.getAt(mode)) {
	sendEvent(name: 'thermostatFanMode', value: mode)
	poll()
}

}

def away() {
setPresence(‘away’)
sendEvent(name: ‘presence’, value: ‘not present’)
}

def present() {
setPresence(‘present’)
sendEvent(name: ‘presence’, value: ‘present’)
}

def setPresence(status) {
log.debug "Status: $status"
api(‘presence’, [‘away’: status == ‘away’, ‘away_timestamp’: new Date().getTime(), ‘away_setter’: 0]) {
poll()
}
}

def poll() {
log.debug "Executing ‘poll’"
api(‘status’, []) {
data.device = it.data.device.getAt(settings.serial)
data.shared = it.data.shared.getAt(settings.serial)
data.structureId = it.data.link.getAt(settings.serial).structure.tokenize(’.’)[1]
data.structure = it.data.structure.getAt(data.structureId)

	data.device.fan_mode = data.device.fan_mode == 'duty-cycle'? 'circulate' : data.device.fan_mode
	data.structure.away = data.structure.away ? 'away' : 'present'

	log.debug(data.shared)

	def humidity = data.device.current_humidity
	def temperatureType = data.shared.target_temperature_type
	def fanMode = data.device.fan_mode
	def heatingSetpoint = '--'
	def coolingSetpoint = '--'

	temperatureType = temperatureType == 'range' ? 'auto' : temperatureType

	sendEvent(name: 'humidity', value: humidity)
	sendEvent(name: 'thermostatFanMode', value: fanMode)
	sendEvent(name: 'thermostatMode', value: temperatureType)

	def temperatureUnit = device.latestValue('temperatureUnit')

	switch (temperatureUnit) {
		case "celsius":
			def temperature = Math.round(data.shared.current_temperature)
			def targetTemperature = Math.round(data.shared.target_temperature)

			if (temperatureType == "cool") {
				coolingSetpoint = targetTemperature
			} else if (temperatureType == "heat") {
				heatingSetpoint = targetTemperature
			} else if (temperatureType == "auto") {
				coolingSetpoint = Math.round(data.shared.target_temperature_high)
				heatingSetpoint = Math.round(data.shared.target_temperature_low)
			}

			sendEvent(name: 'temperature', value: temperature, unit: temperatureUnit, state: temperatureType)
			sendEvent(name: 'coolingSetpoint', value: coolingSetpoint, unit: temperatureUnit, state: "cool")
			sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: temperatureUnit, state: "heat")
			break;
		default:
			def temperature = Math.round(cToF(data.shared.current_temperature))
			def targetTemperature = Math.round(cToF(data.shared.target_temperature))

			if (temperatureType == "cool") {
				coolingSetpoint = targetTemperature
			} else if (temperatureType == "heat") {
				heatingSetpoint = targetTemperature
			} else if (temperatureType == "auto") {
				coolingSetpoint = Math.round(cToF(data.shared.target_temperature_high))
				heatingSetpoint = Math.round(cToF(data.shared.target_temperature_low))
			}

			sendEvent(name: 'temperature', value: temperature, unit: temperatureUnit, state: temperatureType)
			sendEvent(name: 'coolingSetpoint', value: coolingSetpoint, unit: temperatureUnit, state: "cool")
			sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: temperatureUnit, state: "heat")
			break;
		}

	switch (device.latestValue('presence')) {
		case "present":
			if (data.structure.away == 'away') {
				sendEvent(name: 'presence', value: 'not present')
			}
			break;
		case "not present":
			if (data.structure.away == 'present') {
				sendEvent(name: 'presence', value: 'present')
			}
			break;
	}

	if (data.shared.hvac_ac_state) {
		sendEvent(name: 'thermostatOperatingState', value: "cooling")
	} else if (data.shared.hvac_heater_state) {
		sendEvent(name: 'thermostatOperatingState', value: "heating")
	} else if (data.shared.hvac_fan_state) {
		sendEvent(name: 'thermostatOperatingState', value: "fan only")
	} else {
		sendEvent(name: 'thermostatOperatingState', value: "idle")
	}
}

}

def api(method, args = [], success = {}) {
if(!isLoggedIn()) {
log.debug "Need to login"
login(method, args, success)
return
}

def methods = [
	'status': [uri: "/v2/mobile/${data.auth.user}", type: 'get'],
	'fan_mode': [uri: "/v2/put/device.${settings.serial}", type: 'post'],
	'thermostat_mode': [uri: "/v2/put/shared.${settings.serial}", type: 'post'],
	'temperature': [uri: "/v2/put/shared.${settings.serial}", type: 'post'],
	'presence': [uri: "/v2/put/structure.${data.structureId}", type: 'post']
]

def request = methods.getAt(method)

log.debug "Logged in"
doRequest(request.uri, args, request.type, success)

}

// Need to be logged in before this is called. So don’t call this. Call api.
def doRequest(uri, args, type, success) {
log.debug “Calling $type : $uri : $args”

if(uri.charAt(0) == '/') {
	uri = "${data.auth.urls.transport_url}${uri}"
}

def params = [
	uri: uri,
	headers: [
		'X-nl-protocol-version': 1,
		'X-nl-user-id': data.auth.userid,
		'Authorization': "Basic ${data.auth.access_token}"
	],
	body: args
]

def postRequest = { response ->
	if (response.getStatus() == 302) {
		def locations = response.getHeaders("Location")
		def location = locations[0].getValue()
		log.debug "redirecting to ${location}"
		doRequest(location, args, type, success)
	} else {
		success.call(response)
	}
}

try {
	if (type == 'post') {
		httpPostJson(params, postRequest)
	} else if (type == 'get') {
		httpGet(params, postRequest)
	}
} catch (Throwable e) {
	login()
}

}

def login(method = null, args = [], success = {}) {
def params = [
uri: ‘https://home.nest.com/user/login’,
body: [username: settings.username, password: settings.password]
]

httpPost(params) {response ->
	data.auth = response.data
	data.auth.expires_in = Date.parse('EEE, dd-MMM-yyyy HH:mm:ss z', response.data.expires_in).getTime()
	log.debug data.auth

	api(method, args, success)
}

}

def isLoggedIn() {
if(!data.auth) {
log.debug "No data.auth"
return false
}

def now = new Date().getTime();
return data.auth.expires_in > now

}

def cToF(temp) {
return (temp * 1.8 + 32).toDouble()
}

def fToC(temp) {
return ((temp - 32) / 1.8).toDouble()
}

Any thoughts on what might be causing the set points to not change when in off mode?

I took a look at the device type code last night, and it appears that temperatures are set while in auto, heat and cool modes but not off (at least that’s my best guess). Of course, feel free to correct me if I’m wrong because I’m not much for writing code outside of a database using SQL or SAS. :blush:

I’ll do some tinkering to see if I can get this working for the off mode also.

1 Like

Sorry it took me so long to get back on this. Is there a way to be able to select my WeMo switch, rather than my thermostat itself, as my switch controls my window AC Unit? I don’t see the option anywhere, only my actual Ecobee thermostat is listed, but that doesn’t work for my use case. Thanks!

Edit: Scratch that, I was looking at the Thermostat director, not WIndow one. I have the right app, but trying to setup I get an error “You are not authorized to perform the requested operation” when trying to set it up (under Thermostat and Doors)

I have a question…i see this app turns off the thermostat when windows are open…but I don’t have temperature sensors in my room because I use the ones that came with my ecobee 3…i would like to recommend a setting the automatically turns on the thermostat to its last state solely based on the door/windows alone…(Not temperature sensor). So for example…let’s say I wanted the thermostat set to auto mode…and someone opens a window…thermostat should turn off…but once that window is closed again it’s would be nice if the thermostat would automatically go back into auto mode right away.

This is already baked into the app :smile:

how long should it take for the thermostat to turn back on. maybe i have something configured wrong because when my windows are open the thermostat turns off, but when they are closed i have to manually turn it back on? its been about 15 minutes and my thermostat is still on off mode.

Which thermostat are you using?

ecobee3…the main display says (78 and holding) but it also say “OFF” at the same time. it never displays both when 3rd party software is not being used.