Skip to content

Instantly share code, notes, and snippets.

@tylerlange
Created March 21, 2015 20:40
Show Gist options
  • Save tylerlange/61f29f852930a68948e9 to your computer and use it in GitHub Desktop.
Save tylerlange/61f29f852930a68948e9 to your computer and use it in GitHub Desktop.
/**
* Zen Thermostat
*
* Author: Zen Within
* Date: 2015-02-21
*/
metadata {
// Automatically generated. Make future change here.
definition (name: "Zen Thermostat", namespace: "zenwithin", author:
"ZenWithin") {
capability "Actuator"
//capability "Temperature Measurement"
capability "Thermostat"
capability "Configuration"
capability "Refresh"
capability "Sensor"
fingerprint profileId: "0104", endpointId: "01", inClusters:
"0000,0001,0003,0004,0005,0020,0201,0202,0204,0B05", outClusters: "000A,
0019"
attribute "temperatureUnit", "number"
command "setpointUp"
command "setpointDown"
command "setCelsius"
command "setFahrenheit"
}
// simulator metadata
simulator { }
tiles {
valueTile("frontTile", "device.temperature", width: 1, height: 1) {
state("temperature", label:'${currentValue}=C2=B0',
backgroundColor:"#e8e3d8")
}
valueTile("temperature", "device.temperature", width: 1, height: 1)
{
state("temperature", label:'${currentValue}=C2=B0',
backgroundColor:"#0A1E2C")
}
standardTile("fanMode", "device.thermostatFanMode") {
state "fanAuto", action:"thermostat.setThermostatFanMode",
backgroundColor:"#e8e3d8", icon:"st.thermostat.fan-auto"
state "fanOn", action:"thermostat.setThermostatFanMode",
backgroundColor:"#e8e3d8", icon:"st.thermostat.fan-on"
}
standardTile("temperatureUnit", "device.temperatureUnit") {
state "C", label: "=C2=B0C", icon: "st.alarm.temperature.normal=
",
action:"setFahrenheit"
state "F", label: "=C2=B0F", icon: "st.alarm.temperature.norma=
l",
action:"setCelsius"
}
standardTile("mode", "device.thermostatMode") {
state "off", action:"thermostat.setThermostatMode",
backgroundColor:"#e8e3d8", icon:"st.thermostat.heating-cooling-off"
state "cool", action:"thermostat.setThermostatMode",
backgroundColor:"#90d0e8", icon:"st.thermostat.cool"
state "heat", action:"thermostat.setThermostatMode",
backgroundColor:"#ff6e7e", icon:"st.thermostat.heat"
//state "auto", action:"thermostat.setThermostatMode",
backgroundColor:"#e8e3d8", icon:"st.thermostat.auto"
}
valueTile("thermostatSetpoint", "device.thermostatSetpoint", width:
2, height: 2) {
state "off", label:'${currentValue}=C2=B0', unit: "C",
backgroundColor:"#e8e3d8"
state "heat", label:'${currentValue}=C2=B0', unit: "C",
backgroundColor:"#e8e3d8"
state "cool", label:'${currentValue}=C2=B0', unit: "C",
backgroundColor:"#e8e3d8"
}
valueTile("heatingSetpoint", "device.heatingSetpoint",
inactiveLabel: false, decoration: "flat") {
state "heat", label:'${currentValue}=C2=B0 heat', unit:"F",
backgroundColor:"#ffffff"
}
valueTile("coolingSetpoint", "device.coolingSetpoint",
inactiveLabel: false, decoration: "flat") {
state "cool", label:'${currentValue}=C2=B0 cool', unit:"F",
backgroundColor:"#ffffff"
}
standardTile("setpointUp", "device.thermostatSetpoint") {
state "setpointUp", action:"setpointUp",
icon:"st.thermostat.thermostat-up"
}
standardTile("setpointDown", "device.thermostatSetpoint") {
state "setpointDown", action:"setpointDown",
icon:"st.thermostat.thermostat-down"
}
standardTile("refresh", "device.temperature") {
state "default", action:"refresh.refresh",
icon:"st.secondary.refresh"
}
standardTile("configure", "device.configure") {
state "configure", label:'', action:"configuration.configure",
icon:"st.secondary.configure"
}
main "frontTile"
details(["temperature", "fanMode", "mode", "thermostatSetpoint",
"setpointUp", "setpointDown", "temperatureUnit", "refresh", "configure"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parse description $description"
def map =3D [:]
def activeSetpoint =3D null
if (description?.startsWith("read attr -"))
{
def descMap =3D parseDescriptionAsMap(description)
// Thermostat Cluster Attribute Read Response
if (descMap.cluster =3D=3D "0201" && descMap.attrId =3D=3D "0000")
{
map.name =3D "temperature"
map.value =3D getTemperature(descMap.value)
}
else if (descMap.cluster =3D=3D "0201" && descMap.attrId =3D=3D "00=
1c")
{
map.name =3D "thermostatMode"
map.value =3D getModeMap()[descMap.value]
if (map.value =3D=3D "cool") {
activeSetpoint =3D device.currentValue("coolingSetpoint")
} else if (map.value =3D=3D "heat") {
activeSetpoint =3D device.currentValue("heatingSetpoint")
}
sendEvent("name":"thermostatSetpoint", "value":activeSetpoint)
}
else if (descMap.cluster =3D=3D "0201" && descMap.attrId =3D=3D "00=
11")
{
log.debug "COOL SET POINT"
map.name =3D "coolingSetpoint"
map.value =3D getTemperature(descMap.value)
if (device.currentState("thermostatMode")?.value =3D=3D "cool")=
{
activeSetpoint =3D map.value
log.debug "Active set point value: $activeSetpoint"
sendEvent("name":"thermostatSetpoint", "value":activeSetpoint)
}
}
else if (descMap.cluster =3D=3D "0201" && descMap.attrId =3D=3D "00=
12")
{
log.debug "HEAT SET POINT"
map.name =3D "heatingSetpoint"
map.value =3D getTemperature(descMap.value)
if (device.currentState("thermostatMode")?.value =3D=3D "heat")=
{
activeSetpoint =3D map.value
sendEvent("name":"thermostatSetpoint", "value":activeSetpoint)
}
}
// Fan Controll Cluster Attribute Read Response
else if (descMap.cluster =3D=3D "0202" && descMap.attrId =3D=3D "00=
00")
{
map.name =3D "thermostatFanMode"
map.value =3D getFanModeMap()[descMap.value]
}
else if (descMap.cluster =3D=3D "0204" && descMap.attrId =3D=3D "00=
00")
{
map.name =3D "temperatureUnit"
map.value =3D getTemperatureDisplayModeMap()[descMap.value]
}
}// End of Read Attribute Response
def result =3D null
if (map) {
result =3D createEvent(map)
}
log.debug "Parse returned $map"
return result
}
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Help Functions - Don't use=
log.debug in all these
functins =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue =3D param.split(":")
map +=3D [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
def getModeMap() { [
"00":"off",
"03":"cool",
"04":"heat",
]}
def getFanModeMap() { [
"04":"fanOn",
"05":"fanAuto"
]}
def getTemperatureDisplayModeMap() { [
"00":"C",
"01":"F"
]}
def getTemperature(value)
{
def celsius =3D Integer.parseInt(value, 16) / 100
def returnValue =3D celsius
if(getTemperatureScale() =3D=3D "F"){
returnValue =3D Math.round(celsiusToFahrenheit(celsius))
}
return returnValue
}
def getTemperatureScale(){
return device.currentState("temperatureUnit")?.value
}
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Setpoints =3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
def setpointUp()
{
def currentMode =3D device.currentState("thermostatMode")?.value
def currentUnit =3D getTemperatureScale()
// check if heating or cooling setpoint needs to be changed
int nextLevel =3D device.currentValue("thermostatSetpoint") + 1
log.debug "Next level: $nextLevel"
// check the limits
if(currentUnit =3D=3D "C")
{
if (currentMode =3D=3D "cool")
{
if(nextLevel > 36)
{
nextLevel =3D 36
}
} else if (currentMode =3D=3D "heat")
{
if(nextLevel > 32)
{
nextLevel =3D 32
}
}
}
else //in degF unit
{
if (currentMode =3D=3D "cool")
{
if(nextLevel > 96)
{
nextLevel =3D 96
}
} else if (currentMode =3D=3D "heat")
{
if(nextLevel > 89)
{
nextLevel =3D 89
}
}
}
log.debug "setpointUp() - mode: ${currentMode} unit: ${currentUnit}
value: ${nextLevel}"
setSetpoint(nextLevel)
}
def setpointDown()
{
def currentMode =3D device.currentState("thermostatMode")?.value
def currentUnit =3D getTemperatureScale()
// check if heating or cooling setpoint needs to be changed
int nextLevel =3D device.currentValue("thermostatSetpoint") - 1
// check the limits
if (currentUnit =3D=3D "C")
{
if (currentMode =3D=3D "cool")
{
if(nextLevel < 8)
{
nextLevel =3D 8
}
} else if (currentMode =3D=3D "heat")
{
if(nextLevel < 10)
{
nextLevel =3D 10
}
}
}
else //in degF unit
{
if (currentMode =3D=3D "cool")
{
if (nextLevel < 47)
{
nextLevel =3D 47
}
} else if (currentMode =3D=3D "heat")
{
if (nextLevel < 50)
{
nextLevel =3D 50
}
}
}
log.debug "setpointDown() - mode: ${currentMode} unit: ${currentUnit}
value: ${nextLevel}"
setSetpoint(nextLevel)
}
def setSetpoint(degrees)
{
def temperatureScale =3D getTemperatureScale()
def currentMode =3D device.currentState("thermostatMode")?.value
def degreesInteger =3D degrees as Integer
sendEvent("name":"thermostatSetpoint", "value":degreesInteger)
def celsius =3D (getTemperatureScale() =3D=3D "C") ? degreesInteger :
(fahrenheitToCelsius(degreesInteger) as Double).round(2)
if (currentMode =3D=3D "cool") {
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" +
hex(celsius*100) + "}"
}
else if (currentMode =3D=3D "heat") {
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" +
hex(celsius*100) + "}"
}
}
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Thermostat Mode =3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
def modes() {
["off", "heat", "cool"]
}
def setThermostatMode()
{
def currentMode =3D device.currentState("thermostatMode")?.value
def modeOrder =3D modes()
def index =3D modeOrder.indexOf(currentMode)
def next =3D index >=3D 0 && index < modeOrder.size() - 1 ? modeOrder[i=
ndex
+ 1] : modeOrder[0]
log.debug "setThermostatMode - switching from $currentMode to $next"
"$next"()
}
def setThermostatMode(String value) {
"$value"()
}
def off() {
sendEvent("name":"thermostatMode", "value":"off")
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {00}"
}
def cool() {
def coolingSetpoint =3D device.currentValue("coolingSetpoint")
log.debug "Cool set point: $coolingSetpoint"
sendEvent("name":"thermostatMode", "value":"cool")
sendEvent("name":"thermostatSetpoint","value":coolingSetpoint)
[
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {03}", "delay
800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x11"
]
}
def heat() {
def heatingSetpoint =3D device.currentValue("heatingSetpoint")
log.debug "Heat set point: $heatingSetpoint"
sendEvent("name":"thermostatMode","value":"heat")
sendEvent("name":"thermostatSetpoint","value":heatingSetpoint)
[
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {04}", "delay
800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x12"
]
}
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Fan Mode =3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
def setThermostatFanMode()
{
def currentFanMode =3D device.currentState("thermostatFanMode")?.value
def returnCommand
switch (currentFanMode) {
case "fanAuto":
returnCommand =3D fanOn()
break
case "fanOn":
returnCommand =3D fanAuto()
break
}
if(!currentFanMode) {
returnCommand =3D fanAuto()
}
log.debug "setThermostatFanMode - switching from $currentFanMode to
$returnCommand"
returnCommand
}
def setThermostatFanMode(String value) {
"$value"()
}
def on() {
fanOn()
}
def fanOn() {
sendEvent("name":"thermostatFanMode", "value":"fanOn")
"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {04}"
}
def auto() {
fanAuto()
}
def fanAuto() {
sendEvent("name":"thermostatFanMode", "value":"fanAuto")
"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {05}"
}
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Display Unit - degF or deg=
C =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
def setFahrenheit()
{
// Update Display Unit
sendEvent("name":"temperatureUnit", "value":"F")
// send zigbee command
[
"raw 0x0020 {11 00 02 02 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"st wattr 0x${device.deviceNetworkId} 1 0x204 0 0x30 {01}", "delay
800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x11", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x12", "delay 800",
"raw 0x0020 {11 00 02 02 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
}
def setCelsius()
{
// Update Display Unit
sendEvent("name":"temperatureUnit", "value":"C")
// send zigbee command
[
"raw 0x0020 {11 00 02 02 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"st wattr 0x${device.deviceNetworkId} 1 0x204 0 0x30 {00}", "delay
800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x11", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x12", "delay 800",
"raw 0x0020 {11 00 02 1C 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
}
// =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D SmartThings Default Fucnti=
ons: refresh, configure, poll
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
def refresh()
{
log.debug "refresh() - update attributes "
[
//Set long poll interval to 2 qs
"raw 0x0020 {11 00 02 02 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
//This is sent in this specific order to ensure that the
temperature values are received after the unit/mode
"st rattr 0x${device.deviceNetworkId} 1 0x204 0", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x1C", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x11", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x12", "delay 800",
"st rattr 0x${device.deviceNetworkId} 1 0x202 0", "delay 800",
//Set long poll interval to 28 qs (7 seconds)
"raw 0x0020 {11 00 02 1C 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
}
def configure()
{
log.debug "configure() - binding & attribute report"
[
//Set long poll interval to 2 qs
"raw 0x0020 {11 00 02 02 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
//Bindings for Thermostat and Fan Control
"zdo bind 0x${device.deviceNetworkId} 1 1 0x201
{${device.zigbeeId}} {}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} 1 1 0x202
{${device.zigbeeId}} {}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} 1 1 0x204
{${device.zigbeeId}} {}", "delay 500",
//Thermostat - Configure Report
"zcl global send-me-a-report 0x201 0 0x29 1 300 {6400}", "delay
100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zcl global send-me-a-report 0x201 0x0011 0x29 1 300 {6400}",
"delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zcl global send-me-a-report 0x201 0x0012 0x29 1 300 {6400}",
"delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zcl global send-me-a-report 0x201 0x001C 0x30 1 300 {}", "delay
100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
//Fan Control - Configure Report
"zcl global send-me-a-report 0x202 0 0x30 1 300 {}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
//Thermostat UI - Read Attribute Report (attribute not reportable=
)
"st rattr 0x${device.deviceNetworkId} 1 0x204 0", "delay 800",
//Set long poll interval to 28 qs (7 seconds)
"raw 0x0020 {11 00 02 1C 00 00 00}", "delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
]
}
//def poll()
//{
// // leave it out because it will kill the battery
//}
private hex(value) {
new BigInteger(Math.round(value).toString()).toString(16)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment