MSA-52: Device Type for Fidure A1730 ZigBee HA 1.2 Wireless Home Automation Thermostats. Includes features such as:

- Temperature and Mode Control
- Setpoint Hold
- Viewing HVAC status
-  Remote Locking of Thermostat Keys
This commit is contained in:
Armand Raz
2015-08-26 19:52:11 -05:00
parent 7571309def
commit e56086aac3

View File

@@ -0,0 +1,887 @@
/**
* Fidure Thermostat, Based on ZigBee thermostat (SmartThings)
*
* Author: Fidure
* Date: 2014-12-13
* Updated: 2015-08-26
*/
metadata {
// Automatically generated. Make future change here.
definition (name: "Fidure Thermostat", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Temperature Measurement"
capability "Thermostat"
capability "Configuration"
capability "Refresh"
capability "Sensor"
capability "Polling"
attribute "displayTemperature","number"
attribute "displaySetpoint", "string"
command "raiseSetpoint"
command "lowerSetpoint"
attribute "upButtonState", "string"
attribute "downButtonState", "string"
attribute "runningMode", "string"
attribute "lockLevel", "string"
command "setThermostatTime"
command "lock"
attribute "prorgammingOperation", "number"
attribute "prorgammingOperationDisplay", "string"
command "Program"
attribute "setpointHold", "string"
attribute "setpointHoldDisplay", "string"
command "Hold"
attribute "holdExpiary", "string"
attribute "lastTimeSync", "string"
attribute "thermostatOperatingState", "string"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0201,0204,0B05", outClusters: "000A, 0019"
}
// simulator metadata
simulator { }
// pref
preferences {
input ("hold_time", "enum", title: "Default Hold Time in Hours",
description: "Default Hold Duration in hours",
range: "1..24", options: ["No Hold", "2 Hours", "4 Hours", "8 Hours", "12 Hours", "1 Day"],
displayDuringSetup: false)
input ("sync_clock", "boolean", title: "Synchronize Thermostat Clock Automatically?", options: ["Yes","No"])
input ("lock_level", "enum", title: "Thermostat Screen Lock Level", options: ["Full","Mode Only", "Setpoint"])
}
tiles {
valueTile("temperature", "displayTemperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 0, color: "#153591"],
[value: 7, color: "#1e9cbb"],
[value: 15, color: "#90d2a7"],
[value: 23, color: "#44b621"],
[value: 29, color: "#f1d801"],
[value: 35, color: "#d04e00"],
[value: 36, color: "#bc2323"],
// fahrenheit range
[value: 37, 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", action:"thermostat.setThermostatMode", icon:"st.thermostat.heating-cooling-off"
state "cool", action:"thermostat.setThermostatMode", icon:"st.thermostat.cool"
state "heat", action:"thermostat.setThermostatMode", icon:"st.thermostat.heat"
state "auto", action:"thermostat.setThermostatMode", icon:"st.thermostat.auto"
}
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
state "fanAuto", label:'${name}', action:"thermostat.setThermostatFanMode"
state "fanOn", label:'${name}', action:"thermostat.setThermostatFanMode"
}
standardTile("hvacStatus", "thermostatOperatingState", inactiveLabel: false, decoration: "flat") {
state "Resting", label: 'Resting'
state "Heating", icon:"st.thermostat.heating"
state "Cooling", icon:"st.thermostat.cooling"
}
standardTile("lock", "lockLevel", inactiveLabel: false, decoration: "flat") {
state "Unlocked", action:"lock", label:'${name}'
state "Mode Only", action:"lock", label:'${name}'
state "Setpoint", action:"lock", label:'${name}'
state "Full", action:"lock", label:'${name}'
}
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false, range: "$min..$max") {
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, range: "$min..$max") {
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.temperature", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
valueTile("scheduleText", "prorgammingOperation", inactiveLabel: false, decoration: "flat", width: 2) {
state "default", label: 'Schedule'
}
valueTile("schedule", "prorgammingOperationDisplay", inactiveLabel: false, decoration: "flat") {
state "default", action:"Program", label: '${currentValue}'
}
valueTile("hold", "setpointHoldDisplay", inactiveLabel: false, decoration: "flat", width: 3) {
state "setpointHold", action:"Hold", label: '${currentValue}'
}
valueTile("setpoint", "displaySetpoint", width: 2, height: 2)
{
state("displaySetpoint", label: '${currentValue}°',
backgroundColor: "#919191")
}
standardTile("upButton", "upButtonState", decoration: "flat", inactiveLabel: false) {
state "normal", action:"raiseSetpoint", backgroundColor:"#919191", icon:"st.thermostat.thermostat-up"
state "pressed", action:"raiseSetpoint", backgroundColor:"#ff0000", icon:"st.thermostat.thermostat-up"
}
standardTile("downButton", "downButtonState", decoration: "flat", inactiveLabel: false) {
state "normal", action:"lowerSetpoint", backgroundColor:"#919191", icon:"st.thermostat.thermostat-down"
state "pressed", action:"lowerSetpoint", backgroundColor:"#ff9191", icon:"st.thermostat.thermostat-down"
}
main "temperature"
details([ "temperature", "mode", "hvacStatus","setpoint","upButton","downButton","scheduleText", "schedule", "hold",
"heatSliderControl", "heatingSetpoint","coolSliderControl", "coolingSetpoint", "lock", "refresh", "configure"])
}
}
def getMin() {
try
{
if (getTemperatureScale() == "C")
return 10
else
return 50
} catch (all)
{
return 10
}
}
def getMax() {
try {
if (getTemperatureScale() == "C")
return 30
else
return 86
} catch (all)
{
return 86
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parse description $description"
def result = []
if (description?.startsWith("read attr -")) {
//TODO: Parse RAW strings for multiple attributes
def descMap = parseDescriptionAsMap(description)
log.debug "Desc Map: $descMap"
for ( atMap in descMap.attrs)
{
def map = [:]
if (descMap.cluster == "0201")
{
//log.trace "attribute: ${atMap.attrId} "
switch(atMap.attrId.toLowerCase())
{
case "0000":
map.name = "temperature"
map.value = getTemperature(atMap.value)
result += createEvent("name":"displayTemperature", "value": getDisplayTemperature(atMap.value))
break;
case "0005":
//log.debug "hex time: ${descMap.value}"
if (atMap.encoding == "23")
{
map.name = "holdExpiary"
map.value = "${convertToTime(atMap.value).getTime()}"
//log.trace "HOLD EXPIRY: ${atMap.value} is ${map.value}"
updateHoldLabel("HoldExp", "${map.value}")
}
break;
case "0011":
map.name = "coolingSetpoint"
map.value = getDisplayTemperature(atMap.value)
updateSetpoint(map.name,map.value)
break;
case "0012":
map.name = "heatingSetpoint"
map.value = getDisplayTemperature(atMap.value)
updateSetpoint(map.name,map.value)
break;
case "001c":
map.name = "thermostatMode"
map.value = getModeMap()[atMap.value]
updateSetpoint(map.name,map.value)
break;
case "001e": //running mode enum8
map.name = "runningMode"
map.value = getModeMap()[atMap.value]
updateSetpoint(map.name,map.value)
break;
case "0023": // setpoint hold enum8
map.name = "setpointHold"
map.value = getHoldMap()[atMap.value]
updateHoldLabel("Hold", map.value)
break;
case "0024": // hold duration int16u
map.name = "setpointHoldDuration"
map.value = Integer.parseInt("${atMap.value}", 16)
break;
case "0025": // thermostat programming operation bitmap8
map.name = "prorgammingOperation"
def val = getProgrammingMap()[Integer.parseInt("${atMap.value}", 16) & 0x01]
result += createEvent("name":"prorgammingOperationDisplay", "value": val)
map.value = atMap.value
break;
case "0029":
// relay state
map.name = "thermostatOperatingState"
map.value = getThermostatOperatingState(atMap.value)
break;
}
} else if (descMap.cluster == "0204")
{
if (atMap.attrId == "0001")
{
map.name = "lockLevel"
map.value = getLockMap()[atMap.value]
}
}
if (map) {
result += createEvent(map)
}
}
}
log.debug "Parse returned $result"
return result
}
def parseDescriptionAsMap(description) {
def map = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
def attrId = map.get('attrId')
def encoding = map.get('encoding')
def value = map.get('value')
def result = map.get('result')
def list = [];
if (getDataLengthByType(map.get('encoding')) < map.get('value').length()) {
def raw = map.get('raw')
def size = Long.parseLong(''+ map.get('size'), 16)
def index = 12;
def len
//log.trace "processing multi attributes"
while((index-12) < size) {
attrId = flipHexStringEndianness(raw[index..(index+3)])
index+= 4;
if (result == "success")
index+=2;
encoding = raw[index..(index+1)]
index+= 2;
len =getDataLengthByType(encoding)
value = flipHexStringEndianness(raw[index..(index+len-1)])
index+=len;
list += ['attrId': "$attrId", 'encoding':"$encoding", 'value': "$value"]
}
}
else
list += ['attrId': "$attrId", 'encoding': "$encoding", 'value': "$value"]
map.remove('value')
map.remove('encoding')
map.remove('attrId')
map += ['attrs' : list ]
}
def flipHexStringEndianness(s)
{
s = s.reverse()
def sb = new StringBuilder()
for (int i=0; i < s.length() -1; i+=2)
sb.append(s.charAt(i+1)).append(s.charAt(i))
sb
}
def getDataLengthByType(t)
{
// number of bytes in each static data type
def map = ["08":1, "09":2, "0a":3, "0b":4, "0c":5, "0d":6, "0e":7, "0f":8, "10":1, "18":1, "19":2, "1a":3, "1b":4,
"1c":5,"1d":6, "1e":7, "1f":8, "20":1, "21":2, "22":3, "23":4, "24":5, "25":6, "26":7, "27":8, "28":1, "29":2,
"2a":3, "2b":4, "2c":5, "2d":6, "2e":7, "2f":8, "30":1, "31":2, "38":2, "39":4, "40":8, "e0":4, "e1":4, "e2":4,
"e8":2, "e9":2, "ea":4, "f0":8, "f1":16]
// return number of hex chars
return map.get(t) * 2
}
def getProgrammingMap() { [
0:"Off",
1:"On"
]}
def getModeMap() { [
"00":"off",
"01":"auto",
"03":"cool",
"04":"heat"
]}
def getFanModeMap() { [
"04":"fanOn",
"05":"fanAuto"
]}
def getHoldMap()
{[
"00":"Off",
"01":"On"
]}
def updateSetpoint(attrib, val)
{
def cool = device.currentState("coolingSetpoint")?.value
def heat = device.currentState("heatingSetpoint")?.value
def runningMode = device.currentState("runningMode")?.value
def mode = device.currentState("thermostatMode")?.value
def value = '--';
if ("heat" == mode && heat != null)
value = heat;
else if ("cool" == mode && cool != null)
value = cool;
else if ("auto" == mode && runningMode == "cool" && cool != null)
value = cool;
else if ("auto" == mode && runningMode == "heat" && heat != null)
value = heat;
sendEvent("name":"displaySetpoint", "value": value)
}
def raiseSetpoint()
{
sendEvent("name":"upButtonState", "value": "pressed")
sendEvent("name":"upButtonState", "value": "normal")
adjustSetpoint(5)
}
def lowerSetpoint()
{
sendEvent("name":"downButtonState", "value": "pressed")
sendEvent("name":"downButtonState", "value": "normal")
adjustSetpoint(-5)
}
def adjustSetpoint(value)
{
def runningMode = device.currentState("runningMode")?.value
def mode = device.currentState("thermostatMode")?.value
//default to both heat and cool
def modeData = 0x02
if ("heat" == mode || "heat" == runningMode)
modeData = "00"
else if ("cool" == mode || "cool" == runningMode)
modeData = "01"
def amountData = String.format("%02X", value)[-2..-1]
"st cmd 0x${device.deviceNetworkId} 1 0x201 0 {" + modeData + " " + amountData + "}"
}
def getDisplayTemperature(value)
{
def t = Integer.parseInt("$value", 16);
if (getTemperatureScale() == "C") {
t = (((t + 4) / 10) as Integer) / 10;
} else {
t = ((10 *celsiusToFahrenheit(t/100)) as Integer)/ 10;
}
return t;
}
def updateHoldLabel(attr, value)
{
def currentHold = (device?.currentState("setpointHold")?.value)?: "..."
def holdExp = device?.currentState("holdExpiary")?.value
holdExp = holdExp?: "${(new Date()).getTime()}"
if ("Hold" == attr)
{
currentHold = value
}
if ("HoldExp" == attr)
{
holdExp = value
}
boolean past = ( (new Date(holdExp.toLong()).getTime()) < (new Date().getTime()))
if ("HoldExp" == attr)
{
if (!past)
currentHold = "On"
else
currentHold = "Off"
}
def holdString = (currentHold == "On")?
( (past)? "Is On" : "Ends ${compareWithNow(holdExp.toLong())}") :
((currentHold == "Off")? " is Off" : " ...")
sendEvent("name":"setpointHoldDisplay", "value": "Hold ${holdString}")
}
def getSetPointHoldDuration()
{
def holdTime = 0
if (settings.hold_time?.contains("Hours"))
{
holdTime = Integer.parseInt(settings.hold_time[0..1].trim())
}
else if (settings.hold_time?.contains("Day"))
{
holdTime = Integer.parseInt(settings.hold_time[0..1].trim()) * 24
}
def currentHoldDuration = device.currentState("setpointHoldDuration")?.value
if (Short.parseShort('0'+ (currentHoldDuration?: 0)) != (holdTime * 60))
{
[
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x24 0x21 {" +
String.format("%04X", ((holdTime * 60) as Short)) // switch to zigbee endian
+ "}", "delay 100",
"st rattr 0x${device.deviceNetworkId} 1 0x201 0x24", "delay 200",
]
} else
{
[]
}
}
def Hold()
{
def currentHold = device.currentState("setpointHold")?.value
def next = (currentHold == "On") ? "00" : "01"
def nextHold = getHoldMap()[next]
sendEvent("name":"setpointHold", "value":nextHold)
// set the duration first if it's changed
[
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x23 0x30 {$next}", "delay 100" ,
"raw 0x201 {04 21 11 00 00 05 00 }","delay 200", // hold expiry time
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
] + getSetPointHoldDuration()
}
def compareWithNow(d)
{
long mins = (new Date(d)).getTime() - (new Date()).getTime()
mins /= 1000 * 60;
log.trace "mins: ${mins}"
boolean past = (mins < 0)
def ret = (past)? "" : "in "
if (past)
mins *= -1;
float t = 0;
// minutes
if (mins < 60)
{
ret += (mins as Integer) + " min" + ((mins > 1)? 's' : '')
}else if (mins < 1440)
{
t = ( Math.round((14 + mins)/30) as Integer) / 2
ret += t + " hr" + ((t > 1)? 's' : '')
} else
{
t = (Math.round((359 + mins)/720) as Integer) / 2
ret += t + " day" + ((t > 1)? 's' : '')
}
ret += (past)? " ago": ""
log.trace "ret: ${ret}"
ret
}
def convertToTime(data)
{
def time = Integer.parseInt("$data", 16) as long;
time *= 1000;
time += 946684800000; // 481418694
time -= location.timeZone.getRawOffset() + location.timeZone.getDSTSavings();
def d = new Date(time);
//log.trace "converted $data to Time $d"
return d;
}
def Program()
{
def currentSched = device.currentState("prorgammingOperation")?.value
def next = Integer.parseInt(currentSched?: "00", 16);
if ( (next & 0x01) == 0x01)
next = next & 0xfe;
else
next = next | 0x01;
def nextSched = getProgrammingMap()[next & 0x01]
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x25 0x18 {$next}"
}
def getThermostatOperatingState(value)
{
String[] m = [ "heating", "cooling", "fan", "Heat2", "Cool2", "Fan2", "Fan3"]
String desc = 'idle'
value = Integer.parseInt(''+value, 16)
// only check for 1-stage for A1730
for ( i in 0..2 ) {
if (value & 1 << i)
desc = m[i]
}
desc
}
def checkLastTimeSync(delay)
{
def lastSync = device.currentState("lastTimeSync")?.value
if (!lastSync)
lastSync = "${new Date(0)}"
if (settings.sync_clock ?: false && lastSync != new Date(0))
{
sendEvent("name":"lastTimeSync", "value":"${new Date(0)}")
}
long duration = (new Date()).getTime() - (new Date(lastSync)).getTime()
// log.debug "check Time: $lastSync duration: ${duration} settings.sync_clock: ${settings.sync_clock}"
if (duration > 86400000)
{
sendEvent("name":"lastTimeSync", "value":"${new Date()}")
return setThermostatTime()
}
return []
}
def readAttributesCommand(cluster, attribList)
{
def attrString = ''
for ( val in attribList ) {
attrString += ' ' + String.format("%02X %02X", val & 0xff , (val >> 8) & 0xff)
}
//log.trace "list: " + attrString
["raw "+ cluster + " {00 00 00 $attrString}","delay 100",
"send 0x${device.deviceNetworkId} 1 1", "delay 100",
]
}
def refresh()
{
log.debug "refresh called"
// log.trace "list: " + readAttributesCommand(0x201, [0x1C,0x1E,0x23])
readAttributesCommand(0x201, [0x00,0x11,0x12]) +
readAttributesCommand(0x201, [0x1C,0x1E,0x23]) +
readAttributesCommand(0x201, [0x24,0x25,0x29]) +
[
"st rattr 0x${device.deviceNetworkId} 1 0x204 0x01", "delay 200", // lock status
"raw 0x201 {04 21 11 00 00 05 00 }" , "delay 500", // hold expiary
"send 0x${device.deviceNetworkId} 1 1" , "delay 1500",
] + checkLastTimeSync(2000)
}
def poll() {
log.trace "poll called"
refresh()
}
def getTemperature(value) {
def celsius = Integer.parseInt("$value", 16) / 100
if(getTemperatureScale() == "C"){
return celsius as Integer
} else {
return celsiusToFahrenheit(celsius) as Integer
}
}
def setHeatingSetpoint(degrees) {
def temperatureScale = getTemperatureScale()
def degreesInteger = degrees as Integer
sendEvent("name":"heatingSetpoint", "value":degreesInteger)
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius*100) + "}"
}
def setCoolingSetpoint(degrees) {
def degreesInteger = degrees as Integer
sendEvent("name":"coolingSetpoint", "value":degreesInteger)
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius*100) + "}"
}
def modes() {
["off", "heat", "cool"]
}
def setThermostatFanMode() {
def currentFanMode = device.currentState("thermostatFanMode")?.value
//log.debug "switching fan from current mode: $currentFanMode"
def returnCommand
switch (currentFanMode) {
case "fanAuto":
returnCommand = fanOn()
break
case "fanOn":
returnCommand = fanAuto()
break
}
if(!currentFanMode) { returnCommand = fanAuto() }
returnCommand
}
def setThermostatMode() {
def currentMode = device.currentState("thermostatMode")?.value
def modeOrder = modes()
def index = modeOrder.indexOf(currentMode)
def next = index >= 0 && index < modeOrder.size() - 1 ? modeOrder[index + 1] : modeOrder[0]
setThermostatMode(next)
}
def setThermostatMode(String next) {
def val = (getModeMap().find { it.value == next }?.key)?: "00"
// log.trace "mode changing to $next sending value: $val"
sendEvent("name":"thermostatMode", "value":"$next")
["st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {$val}"] +
refresh()
}
def setThermostatFanMode(String value) {
log.debug "setThermostatFanMode({$value})"
"$value"()
}
def off() {
setThermostatMode("off")
}
def cool() {
setThermostatMode("cool")}
def heat() {
setThermostatMode("heat")
}
def auto() {
setThermostatMode("auto")
}
def on() {
fanOn()
}
def fanOn() {
sendEvent("name":"thermostatFanMode", "value":"fanOn")
"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {04}"
}
def fanAuto() {
sendEvent("name":"thermostatFanMode", "value":"fanAuto")
"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {05}"
}
def updated()
{
def lastSync = device.currentState("lastTimeSync")?.value
if ((settings.sync_clock ?: false) == false)
{
log.debug "resetting last sync time. Used to be: $lastSync"
sendEvent("name":"lastTimeSync", "value":"${new Date(0)}")
}
}
def getLockMap()
{[
"00":"Unlocked",
"01":"Mode Only",
"02":"Setpoint",
"03":"Full",
"04":"Full",
"05":"Full",
]}
def lock()
{
def currentLock = device.currentState("lockLevel")?.value
def val = getLockMap().find { it.value == currentLock }?.key
//log.debug "current lock is: ${val}"
if (val == "00")
val = getLockMap().find { it.value == (settings.lock_level ?: "Full") }?.key
else
val = "00"
"st rattr 0x${device.deviceNetworkId} 1 0x204 0x01"
}
def setThermostatTime()
{
if ((settings.sync_clock ?: false))
{
log.debug "sync time is disabled, leaving"
return []
}
Date date = new Date();
String zone = location.timeZone.getRawOffset() + " DST " + location.timeZone.getDSTSavings();
long millis = date.getTime(); // Millis since Unix epoch
millis -= 946684800000; // adjust for ZigBee EPOCH
// adjust for time zone and DST offset
millis += location.timeZone.getRawOffset() + location.timeZone.getDSTSavings();
//convert to seconds
millis /= 1000;
// print to a string for hex capture
String s = String.format("%08X", millis);
// hex capture for message format
String data = " " + s.substring(6, 8) + " " + s.substring(4, 6) + " " + s.substring(2, 4)+ " " + s.substring(0, 2);
[
"raw 0x201 {04 21 11 00 02 0f 00 23 ${data} }",
"send 0x${device.deviceNetworkId} 1 ${endpointId}"
]
}
def configure() {
[
"zdo bind 0x${device.deviceNetworkId} 1 1 0x201 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0x201 0x0000 0x29 20 300 {19 00}", // report temperature changes over 0.2C
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl global send-me-a-report 0x201 0x001C 0x30 10 305 { }", // mode
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 500",
"zcl global send-me-a-report 0x201 0x0025 0x18 10 310 { 00 }", // schedule on/off
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 500",
"zcl global send-me-a-report 0x201 0x001E 0x30 10 315 { 00 }", // running mode
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 500",
"zcl global send-me-a-report 0x201 0x0011 0x29 10 320 {32 00}", // cooling setpoint delta: 0.5C (0x3200 in little endian)
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 500",
"zcl global send-me-a-report 0x201 0x0012 0x29 10 320 {32 00}", // cooling setpoint delta: 0.5C (0x3200 in little endian)
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 500",
"zcl global send-me-a-report 0x201 0x0029 0x19 10 325 { 00 }", "delay 200", // relay status
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 500",
"zcl global send-me-a-report 0x201 0x0023 0x30 10 330 { 00 }", // hold
"send 0x${device.deviceNetworkId} 1 ${endpointId}","delay 1500",
] + refresh()
}
private hex(value) {
new BigInteger(Math.round(value).toString()).toString(16)
}
private getEndpointId()
{
new BigInteger(device.endpointId, 16).toString()
}