multiAttributeTile example for Nest thermostat

devicetype
nest

(Bob) #1

Continuing the discussion from Show your Rule Machine automations:
Spinning this off so as to not clutter up the RM Automations thread.
@Luvien, @ryan.ray: Here is the code snippet you requested that produces the multiAttributeTile arrangement asked about:

[code]/**
/**

  • Nest v2
  • Copyright 2015, Mr. Lucky
  • based on ‘Nest Direct’ v1 code by Brian Steere dianoga7@3dgo.net

*/
preferences {
input(“username”, “text”, title: “Username”, description: ‘Your Nest username (usually an e-mail address)’)
input(“password”, “password”, title: “Password”, description: ‘Your Nest password’)
input(“serial”, “text”, title: “Serial #”, description: ‘The serial number of your thermostat (found under SETTINGS > TECHNICAL INFO)’)
}

metadata {
definition (name: “Nest v2”, namespace: “HDFLucky”, author: “Mr. Lucky”) {
capability "Actuator"
capability "Polling"
capability "Presence Sensor"
capability "Relative Humidity Measurement"
capability "Sensor"
capability "Temperature Measurement"
capability “Thermostat”

	command "away"
	command "present"
	command "setPresence"
	command "heatingSetpointUp"
	command "heatingSetpointDown"
	command "coolingSetpointUp"
	command "coolingSetpointDown"
	command "setFahrenheit"
	command "setCelsius"
    
	attribute "temperatureUnit", "string"
}

tiles(scale: 2) {
	multiAttributeTile(name:"temperature", type:"thermostat", width: 6, height: 4) {
		tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
			attributeState("default", label:'${currentValue}°', /*icon:"st.Home.home1", */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: "#ff8426"]
			]
			)
		}
		tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
			attributeState("level", label:'${currentValue}%')
		}
		tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
			attributeState("idle", backgroundColor:"#44b621")
			attributeState("heating", backgroundColor:"#ffa81e")
			attributeState("cooling", backgroundColor:"#269bd2")
		}
		tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
			attributeState("off", label:'${name}')
			attributeState("heat", label:'${name}')
			attributeState("cool", label:'${name}')
			attributeState("auto", label:'${name}')
		}
		tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
			attributeState("default", label:'${currentValue}')
		}
		tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
			attributeState("default", label:'${currentValue}')
		}
	}

	standardTile("heatingSetpointUp", "device.heatingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "heatingSetpointUp", label:' Heat ', action:"heatingSetpointUp", icon:"st.thermostat.thermostat-up"
	}

	standardTile("coolingSetpointUp", "device.coolingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "coolingSetpointUp", label:' Cool ', action:"coolingSetpointUp", icon:"st.thermostat.thermostat-up"
	}

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

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

	standardTile("heatingSetpointDown", "device.heatingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "heatingSetpointDown", label:' Heat ', action:"heatingSetpointDown", icon:"st.thermostat.thermostat-down"
	}

	standardTile("coolingSetpointDown", "device.coolingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "coolingSetpointDown", label:' Cool ', action:"coolingSetpointDown", icon:"st.thermostat.thermostat-down"
	}

	standardTile("thermostatMode", "device.thermostatMode", inactiveLabel: true, decoration: "flat", width: 2, height: 2) {
		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", width: 2, height: 2) {
		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"
	}

	standardTile("presence", "device.presence", decoration: "flat", width: 2, height: 2) {
		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", decoration: "flat", width: 2, height: 2) {
		state "default", action:"polling.poll", icon:"st.secondary.refresh"
	}

	standardTile("thermostatOperatingState", "device.thermostatOperatingState", decoration: "flat", width: 2, height: 2) {
		state "idle", action:"polling.poll", label:'${name}', icon: 'http://cdn.device-icons.smartthings.com/sonos/pause-icon@2x.png'
		state "cooling", action:"polling.poll", label:' ', icon: "st.thermostat.cooling"
		state "heating", action:"polling.poll", label:' ', icon: "st.thermostat.heating"
		state "fan only", action:"polling.poll", label:'${name}', icon: "st.Appliances.appliances11"
	}

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

	standardTile("temperatureUnit", "device.temperatureUnit", decoration: "flat", width: 2, height: 2) {
		state "fahrenheit",  label: "°F", icon: "st.Weather.weather2", action:"setCelsius"
		state "celsius", label: "°C", icon: "st.Weather.weather2", action:"setFahrenheit"
	}

	main(["temperature", "thermostatOperatingState"])
	details(["temperature", "heatingSetpointUp", "heatingSetpointDown", "thermostatMode", "coolingSetpointUp", "coolingSetpointDown", "heatingSetpoint", "coolingSetpoint", "presence", "thermostatFanMode", "thermostatOperatingState", "refresh"])
    }

}
[/code]
Just copy/paste from the top of the file down to the parse method statement. If you are on iOS, you can try uncommenting the icon on the first attributeState line. It renders the icon and current temp too small on Android.


(LJ) #2

Thanks!

I updated the code, looks nice! Thanks ! But uncommenting the icon doesnt make them appear in iOS.

LJ


(Bob) #3

Gee, what a surprise. Who would think that there would be inconsistencies within ST?! :wink:


(Michael Stroh) #4

Thanks for posting your code. Working perfect for me on Google Nexus 6p!


(Bob) #5

You’re welcome (and nice phone!).


(Michael Stroh) #6

I love the Nexus 6P. I really believe it is the best phone out there. Leaps ahead of the others. I like android, but my wife has a iPhone 6S+ and I play with that too. Seeing both side by side I think the Nexus is much better for how I use the phone. She has no interest in customizing, personalizing, or updating it in any way. For her the plain, never changes iPhone ecosystem works. I like to tweak. Hence the reason I like android and Smart Things! (obligatory keep on topic comment)


(Bob) #7

For anyone bitten by the recent Android app update (and probably iOS) here is the corrected code snippet that the update broke:

[code]/**
/**

  • Nest v2
  • Copyright 2015, Mr. Lucky
  • based on ‘Nest Direct’ v1 code by Brian Steere dianoga7@3dgo.net

*/
preferences {
input(“username”, “text”, title: “Username”, description: ‘Your Nest username (usually an e-mail address)’)
input(“password”, “password”, title: “Password”, description: ‘Your Nest password’)
input(“serial”, “text”, title: “Serial #”, description: ‘The serial number of your thermostat (found under SETTINGS > TECHNICAL INFO)’)
}

metadata {
definition (name: “Nest v2”, namespace: “HDFLucky”, author: “Mr. Lucky”) {
capability "Actuator"
capability "Polling"
capability "Presence Sensor"
capability "Relative Humidity Measurement"
capability "Sensor"
capability "Temperature Measurement"
capability “Thermostat”

	command "away"
	command "present"
	command "setPresence"
	command "heatingSetpointUp"
	command "heatingSetpointDown"
	command "coolingSetpointUp"
	command "coolingSetpointDown"
	command "setFahrenheit"
	command "setCelsius"
    
	attribute "temperatureUnit", "string"
}

tiles(scale: 2) {
	multiAttributeTile(name:"temperature", type:"thermostat", width: 6, height: 4) {
		tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
			attributeState("default", label:'${currentValue}°', /*icon:"st.Home.home1", */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: "#ff8426"]
			]
			)
		}
		tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
			attributeState("level", label:'${currentValue}%')
		}
		tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
			attributeState("idle", backgroundColor:"#44b621")
			attributeState("heating", backgroundColor:"#ffa81e")
			attributeState("cooling", backgroundColor:"#269bd2")
		}
		tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
			attributeState("off", label:'${name}')
			attributeState("heat", label:'${name}')
			attributeState("cool", label:'${name}')
			attributeState("auto", label:'${name}')
		}
		tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
			attributeState("default", label:'${currentValue}')
		}
		tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
			attributeState("default", label:'${currentValue}')
		}
	}

	standardTile("heatingSetpointUp", "device.heatingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "heatingSetpointUp", label:' Heat ', action:"heatingSetpointUp", icon:"st.thermostat.thermostat-up"
	}

	standardTile("coolingSetpointUp", "device.coolingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "coolingSetpointUp", label:' Cool ', action:"coolingSetpointUp", icon:"st.thermostat.thermostat-up"
	}

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

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

	standardTile("heatingSetpointDown", "device.heatingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "heatingSetpointDown", label:' Heat ', action:"heatingSetpointDown", icon:"st.thermostat.thermostat-down"
	}

	standardTile("coolingSetpointDown", "device.coolingSetpoint", decoration: "flat", width: 1, height: 1) {
		state "coolingSetpointDown", label:' Cool ', action:"coolingSetpointDown", icon:"st.thermostat.thermostat-down"
	}

	standardTile("thermostatMode", "device.thermostatMode", inactiveLabel: true, decoration: "flat", width: 2, height: 2) {
		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", width: 2, height: 2) {
		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"
	}

	standardTile("setPresence", "device.presence", decoration: "flat", width: 2, height: 2) {
		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", decoration: "flat", width: 2, height: 2) {
		state "default", action:"polling.poll", icon:"st.secondary.refresh"
	}

	standardTile("thermostatOperatingState", "device.thermostatOperatingState", decoration: "flat", width: 2, height: 2) {
		state "idle", action:"polling.poll", label:'${name}', icon: 'http://cdn.device-icons.smartthings.com/sonos/pause-icon@2x.png'
		state "cooling", action:"polling.poll", label:' ', icon: "st.thermostat.cooling"
		state "heating", action:"polling.poll", label:' ', icon: "st.thermostat.heating"
		state "fan only", action:"polling.poll", label:'${name}', icon: "st.Appliances.appliances11"
	}

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

	standardTile("temperatureUnit", "device.temperatureUnit", decoration: "flat", width: 2, height: 2) {
		state "fahrenheit",  label: "°F", icon: "st.Weather.weather2", action:"setCelsius"
		state "celsius", label: "°C", icon: "st.Weather.weather2", action:"setFahrenheit"
	}

	main(["temperature", "thermostatOperatingState"])
	details(["temperature", "heatingSetpointUp", "heatingSetpointDown", "thermostatMode", "coolingSetpointUp", "coolingSetpointDown", "heatingSetpoint", "coolingSetpoint", "setPresence", "thermostatFanMode", "thermostatOperatingState", "refresh"])
    }

}
[/code]


(Michael Stroh) #8

What changed/broke?

Everything seemed to be working, updated code though without any problems.


(Bob) #9

See:
https://community.smartthings.com/t/android-2-1-0-release-notes-3-7-2016/41549/21


(Mikemaat) #10

Ive made some of my own modifications to the code. These include:

  • Flipped Heat and Cool set points to match direction of heat and cool on Nest
  • Flipped the up/down buttons to more so follow UI standards
  • Created a blank tile to allow lining up all the rest of the tiles
  • Created a hidden tile that enables the user to set their own icon and display the temperature in their “Things” list
  • Tweaked the blue color to match the SmartThings app title bar

 /*
 *  Nest v2
 *
 *  Copyright 2015, Mr. Lucky
 *  based on 'Nest Direct' v1 code by Brian Steere <dianoga7@3dgo.net>
 *
 *  Modifications/Improvements made by Mike Maat on April 8, 2016
 *
 */

 preferences {
 	input("username", "text", title: "Username", description: 'Your Nest username (usually an e-mail address)')
 	input("password", "password", title: "Password", description: 'Your Nest password')
 	input("serial", "text", title: "Serial #", description: 'The serial number of your thermostat (found under SETTINGS > TECHNICAL INFO)')
 }
 metadata {
 	definition (name: "Nest v2", namespace: "HDFLucky", author: "Mr. Lucky") {
 		capability "Actuator"
 		capability "Polling"
 		capability "Presence Sensor"
 		capability "Relative Humidity Measurement"
 		capability "Sensor"
 		capability "Temperature Measurement"
 		capability "Thermostat"
 		command "away"
 		command "present"
 		command "setPresence"
 		command "heatingSetpointUp"
 		command "heatingSetpointDown"
 		command "coolingSetpointUp"
 		command "coolingSetpointDown"
 		command "setFahrenheit"
 		command "setCelsius"
 		attribute "temperatureUnit", "string"
 	}
     tiles(scale: 2) {
 	multiAttributeTile(name:"temperature", type:"thermostat", width: 6, height: 4) {
 		tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
 			attributeState("default", label:'${currentValue}°', /*icon:"st.Home.home1", */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: "#ff8426"]
 			]
 			)
 		}
 		tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
 			attributeState("level", label:'${currentValue}%')
 		}
 		tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
 			attributeState("idle", backgroundColor:"#44b621")
 			attributeState("heating", backgroundColor:"#ffa81e")
 			attributeState("cooling", backgroundColor:"#269bd2")
 		}
 		tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
 			attributeState("off", label:'${name}')
 			attributeState("heat", label:'${name}')
 			attributeState("cool", label:'${name}')
 			attributeState("auto", label:'${name}')
 		}
 		tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
 			attributeState("default", label:'${currentValue}')
 		}
 		tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
 			attributeState("default", label:'${currentValue}')
 		}
 	}

 	standardTile("spacerTile", "spacerTile", decoration: "flat", width: 2, height: 1) {
 	}
     
 	standardTile("heatingSetpointUp", "device.heatingSetpoint", decoration: "flat", width: 1, height: 1) {
 		state "heatingSetpointUp", label:' Heat ', action:"heatingSetpointUp", icon:"st.thermostat.thermostat-up"
 	}

	standardTile("coolingSetpointUp", "device.coolingSetpoint", decoration: "flat", width: 1, height: 1) {
 		state "coolingSetpointUp", label:' Cool ', action:"coolingSetpointUp", icon:"st.thermostat.thermostat-up"
 	}

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

 	valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2) {
 		state "default", label:'${currentValue}°', unit:"Cool", backgroundColor:"#00A0DC"
 	}

 	standardTile("heatingSetpointDown", "device.heatingSetpoint", decoration: "flat", width: 1, height: 1) {
 		state "heatingSetpointDown", label:' Heat ', action:"heatingSetpointDown", icon:"st.thermostat.thermostat-down"
 	}

 	standardTile("coolingSetpointDown", "device.coolingSetpoint", decoration: "flat", width: 1, height: 1) {
 		state "coolingSetpointDown", label:' Cool ', action:"coolingSetpointDown", icon:"st.thermostat.thermostat-down"
 	}

 	standardTile("thermostatMode", "device.thermostatMode", inactiveLabel: true, decoration: "flat", width: 2, height: 2) {
 		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", width: 2, height: 2) {
 		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"
 	}

 	standardTile("setPresence", "device.presence", decoration: "flat", width: 2, height: 2) {
 		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", decoration: "flat", width: 2, height: 2) {
 		state "default", action:"polling.poll", icon:"st.secondary.refresh"
 	}

 	standardTile("thermostatOperatingState", "device.thermostatOperatingState", decoration: "flat", width: 2, height: 2) {
 		state "idle", action:"polling.poll", label:'${name}', icon: 'http://cdn.device-icons.smartthings.com/sonos/pause-icon@2x.png'
 		state "cooling", action:"polling.poll", label:' ', icon: "st.thermostat.cooling"
 		state "heating", action:"polling.poll", label:' ', icon: "st.thermostat.heating"
 		state "fan only", action:"polling.poll", label:'${name}', icon: "st.Appliances.appliances11"
 	}

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

 	standardTile("temperatureUnit", "device.temperatureUnit", decoration: "flat", width: 2, height: 2) {
 		state "fahrenheit",  label: "°F", icon: "st.Weather.weather2", action:"setCelsius"
 		state "celsius", label: "°C", icon: "st.Weather.weather2", action:"setFahrenheit"
 	}
     
     standardTile("hiddenIconTile", "device.temperature", decoration: "flat", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
 		state "default", label:'${currentValue}°', icon: "st.Weather.weather2", backgroundColor: "#79B821"
 	}

 	main("hiddenIconTile")
 	details(["temperature", "coolingSetpointDown", "coolingSetpointUp", "spacerTile", "heatingSetpointDown", "heatingSetpointUp", "coolingSetpoint", "thermostatMode", "heatingSetpoint", "setPresence", "thermostatFanMode", "refresh","temperatureUnit"])
     }
 }

 // 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()
 }