From e453c337e2fafe74a83c01034d887329321e87a9 Mon Sep 17 00:00:00 2001 From: Vinay Rao Date: Sun, 23 Aug 2015 18:22:44 -0700 Subject: [PATCH 1/4] Updating smartpower outlet device type to use zigbee library --- .../smartpower-outlet.groovy | 113 ++++++++++-------- 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index d5cdf74..d196049 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -1,8 +1,19 @@ /** - * CentraLite Switch + * Copyright 2015 SmartThings + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License + * for the specific language governing permissions and limitations under the License. + * + * SmartPower Outlet (CentraLite) * * Author: SmartThings - * Date: 2013-12-02 + * Date: 2015-08-23 */ metadata { // Automatically generated. Make future change here. @@ -35,18 +46,20 @@ metadata { tiles(scale: 2) { multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4){ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { - attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" - attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821" + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff" + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" + attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff" + attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" } tileAttribute ("power", key: "SECONDARY_CONTROL") { attributeState "power", label:'${currentValue} W' } } - + standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" } - + main "switch" details(["switch","refresh"]) } @@ -54,67 +67,61 @@ metadata { // Parse incoming device messages to generate events def parse(String description) { - log.debug "Parse description $description" - def name = null - def value = null + log.debug "description is $description" - // save heartbeat (i.e. last time we got a message from device) - state.heartbeat = Calendar.getInstance().getTimeInMillis() + def finalResult = zigbee.getKnownDescription(description) + //TODO: Remove this after getKnownDescription can parse it automatically + if (!finalResult) + finalResult = zigbee.getPowerDescription(zigbee.parseDescriptionAsMap(description)) - if (description?.startsWith("read attr -")) { - def descMap = parseDescriptionAsMap(description) - log.debug "Read attr: $description" - if (descMap.cluster == "0006" && descMap.attrId == "0000") { - name = "switch" - value = descMap.value.endsWith("01") ? "on" : "off" - } else if (descMap.cluster.equalsIgnoreCase("0B04") && descMap.attrId.equalsIgnoreCase("050b")) { - def reportValue = descMap.value - name = "power" - //power divisor is 10 - value = Integer.parseInt(reportValue, 16) / 10 + if (finalResult) { + log.info finalResult + if (finalResult.type == "update") { + log.info "$device updates: ${finalResult.value}" + } + else if (finalResult.type == "power") { + def powerValue = (finalResult.value as Integer)/10 + sendEvent(name: "power", value: powerValue) + /* + Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10 + + power level is an integer. The exact power level with correct units needs to be handled in the device type + to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702 + */ + } + else { + sendEvent(name: finalResult.type, value: finalResult.value) } - } else if (description?.startsWith("on/off:")) { - log.debug "Switch command" - name = "switch" - value = description?.endsWith(" 1") ? "on" : "off" } - - def result = createEvent(name: name, value: value) - log.debug "Parse returned ${result?.descriptionText}" - return result -} - -def parseDescriptionAsMap(description) { - (description - "read attr - ").split(",").inject([:]) { map, param -> - def nameAndValue = param.split(":") - map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] + else { + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug zigbee.parseDescriptionAsMap(description) } } -// Commands to device -def on() { - 'zcl on-off on' -} - def off() { - 'zcl on-off off' + zigbee.off() } -def meter() { - "st rattr 0x${device.deviceNetworkId} 1 0xB04 0x50B" +def on() { + zigbee.on() } def refresh() { - sendEvent(name: "heartbeat", value: "alive", displayed:false) - [ - "st rattr 0x${device.deviceNetworkId} 1 0xB04 0x50B" - ] + zigbee.onOffRefresh() + zigbee.refreshData("0x0B04", "0x050B") } def configure() { - def configCmds = [ - "zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 200", - "zdo bind 0x${device.deviceNetworkId} 1 1 0xB04 {${device.zigbeeId}} {}", "delay 200" - ] - return configCmds + refresh() // send refresh cmds as part of config + zigbee.onOffConfig() + powerConfig() + refresh() +} + +//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s) +//min change in value is 01 +def powerConfig() { + [ + //Meter (Power) Reporting + "zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200", + "zcl global send-me-a-report 0x0B04 0x050B 0x2A 1 600 {01}", + "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500" + ] } From 91719ba5cac3763e15cebda01a7ba8315d28e360 Mon Sep 17 00:00:00 2001 From: Vinay Rao Date: Tue, 25 Aug 2015 13:10:18 -0700 Subject: [PATCH 2/4] Updating the power config based on the discussion with CentraLite and the sniffer data --- .../smartpower-dimming-outlet.groovy | 7 +++---- .../smartpower-outlet.src/smartpower-outlet.groovy | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy b/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy index 7f6bfa1..51084ec 100644 --- a/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy +++ b/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy @@ -327,10 +327,9 @@ def levelConfig() { //min change in value is 05 def powerConfig() { [ - //Meter (Power) Reporting - "zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 0x0B04 0x050B 0x2A 1 600 {05}", - "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500" + "zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200", + "zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite + "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" ] } diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index d196049..a33ed59 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -119,9 +119,8 @@ def configure() { //min change in value is 01 def powerConfig() { [ - //Meter (Power) Reporting - "zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 0x0B04 0x050B 0x2A 1 600 {01}", - "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500" + "zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200", + "zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite + "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" ] } From b10308fe5cd21c32f5ee2df7913dfa5804674c0c Mon Sep 17 00:00:00 2001 From: Vinay Rao Date: Tue, 25 Aug 2015 20:57:27 -0700 Subject: [PATCH 3/4] Handling the power issues via configuring --- .../smartpower-dimming-outlet.groovy | 3 +- .../smartpower-outlet.groovy | 32 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy b/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy index 51084ec..d77fe0a 100644 --- a/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy +++ b/devicetypes/smartthings/smartpower-dimming-outlet.src/smartpower-dimming-outlet.groovy @@ -287,7 +287,8 @@ def isDescriptionPower(descMap) { def powerValue = "undefined" if (descMap.cluster == "0B04") { if (descMap.attrId == "050b") { - powerValue = convertHexToInt(descMap.value) + if(descMap.value!="ffff") + powerValue = convertHexToInt(descMap.value) } } else if (descMap.clusterId == "0B04") { diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index a33ed59..4876815 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -70,9 +70,10 @@ def parse(String description) { log.debug "description is $description" def finalResult = zigbee.getKnownDescription(description) + //TODO: Remove this after getKnownDescription can parse it automatically - if (!finalResult) - finalResult = zigbee.getPowerDescription(zigbee.parseDescriptionAsMap(description)) + if (!finalResult && description!="updated") + finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description)) if (finalResult) { log.info finalResult @@ -124,3 +125,30 @@ def powerConfig() { "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" ] } + +private getEndpointId() { + new BigInteger(device.endpointId, 16).toString() +} + +//TODO: Remove this after getKnownDescription can parse it automatically +def getPowerDescription(descMap) { + def powerValue = "undefined" + if (descMap.cluster == "0B04") { + if (descMap.attrId == "050b") { + if(descMap.value!="ffff") + powerValue = convertHexToInt(descMap.value) + } + } + else if (descMap.clusterId == "0B04") { + if(descMap.command=="07"){ + return [type: "update", value : "power (0B04) capability configured successfully"] + } + } + + if (powerValue != "undefined"){ + return [type: "power", value : powerValue] + } + else { + return "false" + } +} From 25ce6096b789c684bbb1c22f8ac1f04dcd0d8055 Mon Sep 17 00:00:00 2001 From: Vinay Rao Date: Wed, 26 Aug 2015 20:34:19 -0700 Subject: [PATCH 4/4] using the library converter and returning empty map --- .../smartpower-outlet.src/smartpower-outlet.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index 4876815..9df21c2 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -136,7 +136,7 @@ def getPowerDescription(descMap) { if (descMap.cluster == "0B04") { if (descMap.attrId == "050b") { if(descMap.value!="ffff") - powerValue = convertHexToInt(descMap.value) + powerValue = zigbee.convertHexToInt(descMap.value) } } else if (descMap.clusterId == "0B04") { @@ -149,6 +149,6 @@ def getPowerDescription(descMap) { return [type: "power", value : powerValue] } else { - return "false" + return [:] } }