Last active
December 30, 2015 02:09
-
-
Save rappleg/7760460 to your computer and use it in GitHub Desktop.
CentraLite Thermostat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* CentraLite Thermostat | |
* | |
* Author: SmartThings | |
* Date: 2013-12-02 | |
*/ | |
metadata { | |
// simulator metadata | |
simulator { | |
status "off" : "command: 4003, payload: 00" | |
status "heat" : "command: 4003, payload: 01" | |
status "cool" : "command: 4003, payload: 02" | |
status "auto" : "command: 4003, payload: 03" | |
status "emergencyHeat" : "command: 4003, payload: 04" | |
status "fanAuto" : "command: 4403, payload: 00" | |
status "fanOn" : "command: 4403, payload: 01" | |
status "fanCirculate" : "command: 4403, payload: 06" | |
status "heat 60" : "command: 4303, payload: 01 01 3C" | |
status "heat 68" : "command: 4303, payload: 01 01 44" | |
status "heat 72" : "command: 4303, payload: 01 01 48" | |
status "cool 72" : "command: 4303, payload: 02 01 48" | |
status "cool 76" : "command: 4303, payload: 02 01 4C" | |
status "cool 80" : "command: 4303, payload: 02 01 50" | |
status "temp 58" : "command: 3105, payload: 01 22 02 44" | |
status "temp 62" : "command: 3105, payload: 01 22 02 6C" | |
status "temp 70" : "command: 3105, payload: 01 22 02 BC" | |
status "temp 74" : "command: 3105, payload: 01 22 02 E4" | |
status "temp 78" : "command: 3105, payload: 01 22 03 0C" | |
status "temp 82" : "command: 3105, payload: 01 22 03 34" | |
status "idle" : "command: 4203, payload: 00" | |
status "heating" : "command: 4203, payload: 01" | |
status "cooling" : "command: 4203, payload: 02" | |
status "fan only" : "command: 4203, payload: 03" | |
status "pending heat" : "command: 4203, payload: 04" | |
status "pending cool" : "command: 4203, payload: 05" | |
status "vent economizer": "command: 4203, payload: 06" | |
// reply messages | |
reply "0201": "read attr - raw: 637A0102010C00000029AF08, dni: 637A, endpoint: 01, cluster: 0201, size: 0C, attrId: 0000, encoding: 0029, value: 08af" | |
} | |
tiles { | |
valueTile("temperature", "device.temperature", width: 2, height: 2) { | |
state("temperature", label:'${currentValue}°', unit:"F", | |
backgroundColors:[ | |
[value: 31, color: "#153591"], | |
[value: 44, color: "#1e9cbb"], | |
[value: 59, color: "#90d2a7"], | |
[value: 74, color: "#44b621"], | |
[value: 84, color: "#f1d801"], | |
[value: 95, color: "#d04e00"], | |
[value: 96, color: "#bc2323"] | |
] | |
) | |
} | |
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { | |
state "off", label:'${name}', action:"switchMode" | |
state "heat", label:'${name}', action:"switchMode" | |
state "cool", label:'${name}', action:"switchMode" | |
} | |
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") { | |
state "fanAuto", label:'${name}', action:"switchFanMode" | |
state "fanOn", label:'${name}', action:"switchFanMode" | |
} | |
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) { | |
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00" | |
} | |
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") { | |
state "heat", label:'${currentValue}° heat', unit:"F", backgroundColor:"#ffffff" | |
} | |
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) { | |
state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#1e9cbb" | |
} | |
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") { | |
state "cool", label:'${currentValue}° cool', unit:"F", backgroundColor:"#ffffff" | |
} | |
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { | |
state "default", action:"polling.poll", icon:"st.secondary.refresh" | |
} | |
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") { | |
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" | |
} | |
main "temperature" | |
details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh", "configure"]) | |
} | |
} | |
// parse events into attributes | |
def parse(String description) { | |
log.debug "Parse description $description" | |
def map = [:] | |
if (description?.startsWith("read attr -")) { | |
// TODO: support more than just temperature | |
log.debug "Read attr" | |
def reportValue = description.split(",").find {it.split(":")[0].trim() == "value"}?.split(":")[1].trim() | |
map.name = "temperature" | |
map.value = toFarenheit(Integer.parseInt(reportValue, 16) / 100) | |
} | |
def result = null | |
if (map) { | |
result = createEvent(map) | |
} | |
log.debug "Parse returned ${result?.descriptionText}" | |
return result | |
} | |
def refresh() | |
{ | |
log.debug "refresh called" | |
"st rattr 0x${device.deviceNetworkId} 1 0x201 0" | |
} | |
def go() | |
{ | |
log.debug "before:go tile tapped" | |
poll() | |
log.debug "after" | |
} | |
def poll() { | |
log.debug "Executing 'poll'" | |
refresh() | |
} | |
def parseEventData(Map results) | |
{ | |
log.debug "parsing data $results" | |
if(results) | |
{ | |
results.each { name, value -> | |
def linkText = getLinkText(device) | |
def isStateChange = isTemperatureStateChange(device, name, value.toString()) | |
sendEvent( | |
name: name, | |
value: value, | |
unit: "F", | |
linkText: linkText, | |
descriptionText: getThermostatDescriptionText(name, value, linkText), | |
handlerName: name, | |
isStateChange: isStateChange, | |
displayed: isStateChange | |
) | |
} | |
} | |
} | |
private getThermostatDescriptionText(name, value, linkText) | |
{ | |
if(name == "temperature") | |
{ | |
return "$linkText was $value°F" | |
} | |
else if(name == "heatingSetpoint") | |
{ | |
return "latest heating setpoint was $value°F" | |
} | |
else if(name == "coolingSetpoint") | |
{ | |
return "latest cooling setpoint was $value°F" | |
} | |
} | |
def toCelsius(degreesF) { | |
log.debug "Farenheit: $degreesF" | |
def celsius = ((degreesF - 32) * 5/9 as Double).round(2) | |
log.debug "Celsius: $celsius" | |
celsius | |
} | |
def toFarenheit(degreesC) { | |
log.debug "Celsius: $degreesC" | |
def farenheit = ((degreesC * 9/5 + 32) as Double).round(0) | |
log.debug "Farenheit: $farenheit" | |
farenheit | |
} | |
def setHeatingSetpoint(degreesF) { | |
def celsius = toCelsius(degreesF) | |
log.debug "setHeatingSetpoint({$celsius})" | |
sendEvent("name":"heatingSetpoint", "value":celsius) | |
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius) + "}" | |
} | |
def setCoolingSetpoint(degreesF) { | |
def celsius = toCelsius(degreesF) | |
log.debug "setCoolingSetpoint({$celsius})" | |
sendEvent("name":"coolingSetpoint", "value":celsius) | |
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius) + "}" | |
} | |
def modes() { | |
["off", "heat", "cool"] | |
} | |
def switchMode() { | |
log.debug "switchMode" | |
def currentMode = device.currentState("thermostatMode")?.value | |
def lastTriedMode = getDataByName("lastTriedMode") ?: currentMode ?: "off" | |
def supportedModes = getDataByName("supportedModes") | |
def modeOrder = modes() | |
def index = modeOrder.indexOf(lastTriedMode) | |
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } | |
def nextMode = next(lastTriedMode) | |
if (supportedModes?.contains(currentMode)) { | |
while (!supportedModes.contains(nextMode) && nextMode != "off") { | |
nextMode = next(nextMode) | |
} | |
} | |
switchToMode(nextMode) | |
} | |
def switchToMode(nextMode) { | |
def supportedModes = getDataByName("supportedModes") | |
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" | |
if (nextMode in modes()) { | |
updateState("lastTriedMode", nextMode) | |
return "$nextMode"() | |
} else { | |
log.debug("no mode method '$nextMode'") | |
} | |
} | |
def switchFanMode() { | |
def currentFanMode = device.currentState("thermostatFanMode")?.value | |
log.debug "switching fan from current mode: $currentFanMode" | |
def returnCommand | |
switch (currentFanMode) { | |
case "fanAuto": | |
returnCommand = "st wattr 0x${device.deviceNetworkId} 1 0x202 0x30 {05}" | |
break | |
case "fanOn": | |
returnCommand = "st wattr 0x${device.deviceNetworkId} 1 0x202 0x30 {04}" | |
break | |
} | |
if(!currentFanMode) { returnCommand = switchToFanMode("fanOn") } | |
returnCommand | |
} | |
def switchToFanMode(nextMode) { | |
def supportedFanModes = getDataByName("supportedFanModes") | |
log.debug "supportedFanModes: $supportedFanModes" | |
log.debug "switching to fan mode: $nextMode" | |
def returnCommand | |
if(nextMode == "fanAuto") { | |
if(!supportedFanModes || supportedFanModes.contains("fanAuto")) { | |
returnCommand = fanAuto() | |
} else { | |
returnCommand = switchToFanMode("fanOn") | |
} | |
} else if(nextMode == "fanOn") { | |
if(!supportedFanModes || supportedFanModes.contains("fanOn")) { | |
returnCommand = fanOn() | |
} | |
} | |
returnCommand | |
} | |
def getDataByName(String name) { | |
state[name] ?: device.getDataValue(name) | |
} | |
def setThermostatMode(String value) { | |
log.debug "setThermostatMode({$value})" | |
"$value"() | |
} | |
def setThermostatFanMode(String value) { | |
log.debug "setThermostatFanMode({$value})" | |
"$value"() | |
} | |
def off() { | |
log.debug "off" | |
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {00}" | |
} | |
def heat() { | |
log.debug "heat" | |
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {04}" | |
} | |
def cool() { | |
log.debug "cool" | |
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {03}" | |
} | |
def fanOn() { | |
log.debug "fanOn" | |
"st wattr 0x${device.deviceNetworkId} 1 0x202 0x30 {04}" | |
} | |
def fanAuto() { | |
log.debug "fanAuto" | |
"st wattr 0x${device.deviceNetworkId} 1 0x202 0x30 {05}" | |
} | |
def configure() { | |
log.debug "binding to Thermostat and Fan Control cluster" | |
[ | |
"zdo bind 0x${device.deviceNetworkId} 1 1 0x201 {${device.zigbeeId}} {}", "delay 200", | |
"zdo bind 0x${device.deviceNetworkId} 1 1 0x202 {${device.zigbeeId}} {}" | |
] | |
} | |
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