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 75c40ae..51bde7b 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") { @@ -327,10 +328,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 6d7f795..e663629 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, canChangeIcon: true){ 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,88 @@ 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) - 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 + //TODO: Remove this after getKnownDescription can parse it automatically + if (!finalResult && description!="updated") + finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description)) + + 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() { + [ + "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" + ] +} + +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 = zigbee.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 [:] + } }