From 15dc550c1577688378c130cb5795bd4e80bd5072 Mon Sep 17 00:00:00 2001 From: ALamchaS Date: Wed, 11 Jul 2018 23:11:37 +0200 Subject: [PATCH 01/17] ICP-5348 Fix for ST Classic preferences (#3214) * Initial formatting ande EOL unification. * [ICP-5348] Description change. * [ICP-5348] Fix for parameters 80 and 24. --- .../fibaro-motion-sensor-zw5.groovy | 245 +++++++++--------- 1 file changed, 128 insertions(+), 117 deletions(-) diff --git a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy index 7aaa4a1a528..430cfe750b2 100644 --- a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy @@ -1,16 +1,16 @@ /** - * Fibaro Motion Sensor ZW5 + * Fibaro Motion Sensor ZW5 * - * Copyright 2016 Fibar Group S.A. + * Copyright 2016 Fibar Group S.A. * - * 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: + * 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 + * 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. + * 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. * */ metadata { @@ -67,9 +67,9 @@ metadata { valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") { state "battery", label: '${currentValue}% battery', unit: "" } - + valueTile("motionTile", "device.motionText", inactiveLabel: false, width: 3, height: 2, decoration: "flat") { - state "motionText", label:'${currentValue}', action:"resetMotionTile" + state "motionText", label: '${currentValue}', action: "resetMotionTile" } valueTile("multiStatus", "device.multiStatus", inactiveLable: false, width: 3, height: 2, decoration: "flat") { @@ -81,36 +81,37 @@ metadata { } preferences { - input ( - title: "Fibaro Motion Sensor ZW5 manual", - description: "Tap to view the manual.", - image: "http://manuals.fibaro.com/wp-content/uploads/2017/02/ms_icon.png", - url: "http://manuals.fibaro.com/content/manuals/en/FGMS-001/FGMS-001-EN-T-v2.1.pdf", - type: "href", - element: "href" + input( + title: "Fibaro Motion Sensor ZW5 manual", + description: "Tap to view the manual.", + image: "http://manuals.fibaro.com/wp-content/uploads/2017/02/ms_icon.png", + url: "http://manuals.fibaro.com/content/manuals/en/FGMS-001/FGMS-001-EN-T-v2.1.pdf", + type: "href", + element: "href" ) - parameterMap().findAll{(it.num as Integer) != 54}.each { - input ( - title: "${it.num}. ${it.title}", - description: it.descr, - type: "paragraph", - element: "paragraph" + parameterMap().findAll { (it.num as Integer) != 54 }.each { + input( + title: "${it.num}. ${it.title}", + description: it.descr, + type: "paragraph", + element: "paragraph" ) - - input ( - name: it.key, - title: null, - description: "Default: $it.def" , - type: it.type, - options: it.options, - range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, - defaultValue: it.def, - required: false + def defVal = it.def as Integer + def descrDefVal = it.options ? it.options.get(defVal) : defVal + input( + name: it.key, + title: null, + description: "Default: $descrDefVal", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.def, + required: false ) } - input ( name: "logging", title: "Logging", type: "boolean", required: false ) + input(name: "logging", title: "Logging", type: "boolean", required: false) } } @@ -207,7 +208,7 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelR map.value = cmd.scaledSensorValue.toInteger().toString() map.unit = "lux" break - // case [25,52,53,54]: Note this valid use of a list is failing, why? + // case [25,52,53,54]: Note this valid use of a list is failing, why? case 25: case 52: case 53: @@ -266,7 +267,7 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { logging("${device.displayName} woke up", "debug") def cmds = [] - if ( state.wakeUpInterval?.state == "notSynced" && state.wakeUpInterval?.value != null ) { + if (state.wakeUpInterval?.state == "notSynced" && state.wakeUpInterval?.value != null) { cmds << zwave.wakeUpV2.wakeUpIntervalSet(seconds: state.wakeUpInterval.value as Integer, nodeid: zwaveHubNodeId) state.wakeUpInterval.state = "synced" } @@ -278,7 +279,7 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 3, scale: 1)) cmds << "delay 1200" cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation()) - runIn(1,"syncNext") + runIn(1, "syncNext") [event, response(cmds)] } @@ -334,7 +335,7 @@ def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cm def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { logging("${device.displayName} - Configuration report: ${cmd}", "debug") - def paramKey = parameterMap().find( {it.num == cmd.parameterNumber } ).key + def paramKey = parameterMap().find({ it.num == cmd.parameterNumber }).key logging("${device.displayName} - Parameter ${paramKey} value is ${cmd.scaledConfigurationValue} expected " + state."$paramKey".value, "debug") state."$paramKey".state = (state."$paramKey".value == cmd.scaledConfigurationValue) ? "synced" : "incorrect" syncNext() @@ -396,8 +397,8 @@ private motionEvent(Integer sensorType, value) { sendEvent(name: "motionText", value: "Vibration:\n${value} MMI", displayed: false) break case 52..54: - sendEvent(name: axisMap[sensorType], value: value , displayed: false) - runIn(2,"axisEvent") + sendEvent(name: axisMap[sensorType], value: value, displayed: false) + runIn(2, "axisEvent") break } } @@ -405,7 +406,8 @@ private motionEvent(Integer sensorType, value) { private axisEvent() { logging("${device.displayName} - Executing axisEvent() values are: ${device.currentValue("xAxis")}, ${device.currentValue("yAxis")}, ${device.currentValue("zAxis")}", "debug") def xAxis = Math.round((device.currentValue("xAxis") as Float) * 100) - def yAxis = Math.round((device.currentValue("yAxis") as Float) * 100) // * 100 because from what I can tell apps expect data in cm/s2 + def yAxis = Math.round((device.currentValue("yAxis") as Float) * 100) + // * 100 because from what I can tell apps expect data in cm/s2 def zAxis = Math.round((device.currentValue("zAxis") as Float) * 100) sendEvent(name: "motionText", value: "X: ${device.currentValue("xAxis")}\nY: ${device.currentValue("yAxis")}\nZ: ${device.currentValue("zAxis")}", displayed: false) sendEvent(name: "threeAxis", value: "${xAxis},${yAxis},${zAxis}", isStateChange: true, displayed: false) @@ -415,16 +417,18 @@ private syncStart() { boolean syncNeeded = false Integer settingValue = null parameterMap().each { - if(settings."$it.key" != null || it.num == 54) { - if (state."$it.key" == null) { state."$it.key" = [value: null, state: "synced"] } - if ( (it.num as Integer) == 54 ) { + if (settings."$it.key" != null || it.num == 54) { + if (state."$it.key" == null) { + state."$it.key" = [value: null, state: "synced"] + } + if ((it.num as Integer) == 54) { settingValue = (((settings."temperatureHigh" as Integer) == 0) ? 0 : 1) + (((settings."temperatureLow" as Integer) == 0) ? 0 : 2) - } else if ( (it.num as Integer) in [55,56] ) { + } else if ((it.num as Integer) in [55, 56]) { settingValue = (((settings."$it.key" as Integer) == 0) ? state."$it.key".value : settings."$it.key") as Integer } else { settingValue = (settings."$it.key" instanceof Integer ? settings."$it.key" as Integer : settings."$it.key" as Float) } - if (state."$it.key".value != settingValue || state."$it.key".state != "synced" ) { + if (state."$it.key".value != settingValue || state."$it.key".state != "synced") { state."$it.key".value = settingValue state."$it.key".state = "notSynced" syncNeeded = true @@ -432,17 +436,17 @@ private syncStart() { } } - if ( syncNeeded ) { + if (syncNeeded) { logging("${device.displayName} - sync needed.", "debug") multiStatusEvent("Sync pending. Please wake up the device by pressing the B button.", true) } } private syncNext() { - logging("${device.displayName} - Executing syncNext()","debug") + logging("${device.displayName} - Executing syncNext()", "debug") def cmds = [] - for ( param in parameterMap() ) { - if ( state."$param.key"?.value != null && state."$param.key"?.state in ["notSynced","inProgress"] ) { + for (param in parameterMap()) { + if (state."$param.key"?.value != null && state."$param.key"?.state in ["notSynced", "inProgress"]) { multiStatusEvent("Sync in progress. (param: ${param.num})", true) state."$param.key"?.state = "inProgress" cmds << response(encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$param.key".value, parameterNumber: param.num, size: param.size))) @@ -452,23 +456,23 @@ private syncNext() { } if (cmds) { runIn(10, "syncCheck") - sendHubCommand(cmds,1000) + sendHubCommand(cmds, 1000) } else { runIn(1, "syncCheck") } } private syncCheck() { - logging("${device.displayName} - Executing syncCheck()","debug") + logging("${device.displayName} - Executing syncCheck()", "debug") def failed = [] def incorrect = [] def notSynced = [] parameterMap().each { - if (state."$it.key"?.state == "incorrect" ) { + if (state."$it.key"?.state == "incorrect") { incorrect << it - } else if ( state."$it.key"?.state == "failed" ) { + } else if (state."$it.key"?.state == "failed") { failed << it - } else if ( state."$it.key"?.state in ["inProgress","notSynced"] ) { + } else if (state."$it.key"?.state in ["inProgress", "notSynced"]) { notSynced << it } } @@ -481,7 +485,9 @@ private syncCheck() { multiStatusEvent("Sync incomplete! Wake up the device again by pressing the B button.", true, true) } else { sendHubCommand(response(encap(zwave.wakeUpV1.wakeUpNoMoreInformation()))) - if (device.currentValue("multiStatus")?.contains("Sync")) { multiStatusEvent("Sync OK.", true, true) } + if (device.currentValue("multiStatus")?.contains("Sync")) { + multiStatusEvent("Sync OK.", true, true) + } } } @@ -502,82 +508,87 @@ private logging(text, type = "debug") { ## Device Configuration ## ########################## */ + private Map cmdVersions() { - [0x5E: 1, 0x86: 1, 0x72: 2, 0x59: 1, 0x80: 1, 0x73: 1, 0x56: 1, 0x22: 1, 0x31: 5, 0x98: 1, 0x7A: 3, 0x20: 1, 0x5A: 1, 0x85: 2, 0x84: 2, 0x71: 3, 0x8E: 1, 0x70: 2, 0x30: 1, 0x9C: 1] //Fibaro Motion Sensor ZW5 + [0x5E: 1, 0x86: 1, 0x72: 2, 0x59: 1, 0x80: 1, 0x73: 1, 0x56: 1, 0x22: 1, 0x31: 5, 0x98: 1, 0x7A: 3, 0x20: 1, 0x5A: 1, 0x85: 2, 0x84: 2, 0x71: 3, 0x8E: 1, 0x70: 2, 0x30: 1, 0x9C: 1] + //Fibaro Motion Sensor ZW5 } -private parameterMap() {[ - [key: "motionSensitivity", num: 1, size: 2, type: "enum", options: [ - 15: "High sensitivity", - 100: "Medium sensitivity", - 200: "Low sensitivity" +private parameterMap() { + [ + [key : "motionSensitivity", num: 1, size: 2, type: "enum", options: [ + 15 : "High sensitivity", + 100: "Medium sensitivity", + 200: "Low sensitivity" ], def: 15, min: 8, max: 255, title: "Motion detection - sensitivity", descr: ""], - [key: "motionBlindTime", num: 2, size: 1, type: "enum", options: [ - 1: "1 s", - 3: "2 s", - 5: "3 s", - 7: "4 s", - 9: "5 s", - 11: "6 s", - 13: "7 s", - 15: "8 s" + [key : "motionBlindTime", num: 2, size: 1, type: "enum", options: [ + 1 : "1 s", + 3 : "2 s", + 5 : "3 s", + 7 : "4 s", + 9 : "5 s", + 11: "6 s", + 13: "7 s", + 15: "8 s" ], def: 15, title: "Motion detection - blind time", descr: "PIR sensor is “blind” (insensitive) to motion after last detection for the amount of time specified in this parameter. (1-8 in sec.)"], - [key: "motionCancelationDelay", num: 6, size: 2, type: "number", def: 30, min: 1, max: 32767, title: "Motion detection - alarm cancellation delay", + [key : "motionCancelationDelay", num: 6, size: 2, type: "number", def: 30, min: 1, max: 32767, title: "Motion detection - alarm cancellation delay", descr: "Time period after which the motion alarm will be cancelled in the main controller. (1-32767 sec.)"], - [key: "motionOperatingMode", num: 8, size: 1, type: "enum", options: [0: "Always Active (default)", 1: "Active During Day", 2: "Active During Night"], def: "0", title: "Motion detection - operating mode", + [key : "motionOperatingMode", num: 8, size: 1, type: "enum", options: [0: "Always Active (default)", 1: "Active During Day", 2: "Active During Night"], def: "0", title: "Motion detection - operating mode", descr: "This parameter determines in which part of day the PIR sensor will be active."], - [key: "motionNightDay", num: 9, size: 2, type: "number", def: 200, min: 1, max: 32767, title: "Motion detection - night/day", + [key : "motionNightDay", num: 9, size: 2, type: "number", def: 200, min: 1, max: 32767, title: "Motion detection - night/day", descr: "This parameter defines the difference between night and day in terms of light intensity, used in parameter 8. (1-32767 lux)"], - [key: "tamperCancelationDelay", num: 22, size: 2, type: "number", def: 30, min: 1, max: 32767, title: "Tamper - alarm cancellation delay", + [key : "tamperCancelationDelay", num: 22, size: 2, type: "number", def: 30, min: 1, max: 32767, title: "Tamper - alarm cancellation delay", descr: "Time period after which a tamper alarm will be cancelled in the main controller. (1-32767 in sec.)"], - [key: "tamperOperatingMode", num: 24, size: 1, type: "enum", options: [0: "tamper only (default)", 1: "tamper and earthquake detector", 2: "tamper and orientation"], def: "0", title: "Tamper - operating modes", + [key : "tamperOperatingMode", num: 24, size: 1, type: "enum", options: [0: "tamper only (default)", 1: "tamper and earthquake detector", 2: "tamper and orientation"], def: "0", title: "Tamper - operating modes", descr: "This parameter determines function of the tamper and sent reports. It is an advanced feature serving much more functions than just detection of tampering."], - [key: "illuminanceThreshold", num: 40, size: 2, type: "number", def: 200, min: 0, max: 32767, title: "Illuminance report - threshold", + [key : "illuminanceThreshold", num: 40, size: 2, type: "number", def: 200, min: 0, max: 32767, title: "Illuminance report - threshold", descr: "This parameter determines the change in light intensity level (in lux) resulting in illuminance report being sent to the main controller. (1-32767 in lux)"], - [key: "illuminanceInterval", num: 42, size: 2, type: "number", def: 3600, min: 0, max: 32767, title: "Illuminance report - interval", + [key : "illuminanceInterval", num: 42, size: 2, type: "number", def: 3600, min: 0, max: 32767, title: "Illuminance report - interval", descr: "Time interval between consecutive illuminance reports. The reports are sent even if there is no change in the light intensity. (1-3276 in sec)"], - [key: "temperatureThreshold", num: 60, size: 2, type: "enum", options: [ - 3: "0.5°F/0.3°C", - 6: "1°F/0.6°C", - 10: "2°F/1 °C", - 17: "3°F/1.7°C", - 22: "4°F/2.2°C", - 28: "5°F/2.8°C" + [key : "temperatureThreshold", num: 60, size: 2, type: "enum", options: [ + 3 : "0.5°F/0.3°C", + 6 : "1°F/0.6°C", + 10: "2°F/1 °C", + 17: "3°F/1.7°C", + 22: "4°F/2.2°C", + 28: "5°F/2.8°C" ], def: 10, min: 0, max: 255, title: "Temperature report - threshold", descr: "This parameter determines the change in measured temperature that will result in new temperature report being sent to the main controller."], - [key: "ledMode", num: 80, size: 1, type: "enum", options: [ - 0: "LED inactive", - 1: "Temp Dependent (1 long blink)", - 2: "Flashlight Mode (1 long blink)", - 3: "White (1 long blink)", - 4: "Red (1 long blink)", - 5: "Green (1 long blink)", - 6: "Blue (1 long blink)", - 7: "Yellow (1 long blink)", - 8: "Cyan (1 long blink)", - 9: "Magenta (1 long blink)", - 10: "Temp dependent (1 long 1 short blink) (default)", - 11: "Flashlight Mode (1 long 1 short blink)", - 12: "White (1 long 1 short blink)", - 13: "Red (1 long 1 short blink)", - 14: "Green (1 long 1 short blink)", - 15: "Blue (1 long 1 short blink)", - 16: "Yellow (1 long 1 short blink)", - 17: "Cyan (1 long 1 short blink)", - 18: "Magenta (1 long 1 short blink)", - 19: "Temp Dependent (1 long 2 short blink)", - 20: "White (1 long 2 short blinks)", - 21: "Red (1 long 2 short blinks)", - 22: "Green (1 long 2 short blinks)", - 23: "Blue (1 long 2 short blinks)", - 24: "Yellow (1 long 2 short blinks)", - 25: "Cyan (1 long 2 short blinks)", - 26: "Magenta (1 long 2 short blinks)" + [key : "ledMode", num: 80, size: 1, type: "enum", options: [ + 0 : "LED inactive", + 1 : "Temp Dependent (1 long blink)", + 2 : "Flashlight Mode (1 long blink)", + 3 : "White (1 long blink)", + 4 : "Red (1 long blink)", + 5 : "Green (1 long blink)", + 6 : "Blue (1 long blink)", + 7 : "Yellow (1 long blink)", + 8 : "Cyan (1 long blink)", + 9 : "Magenta (1 long blink)", + 10: "Temp dependent (1 long 1 short blink) (default)", + 11: "Flashlight Mode (1 long 1 short blink)", + 12: "White (1 long 1 short blink)", + 13: "Red (1 long 1 short blink)", + 14: "Green (1 long 1 short blink)", + 15: "Blue (1 long 1 short blink)", + 16: "Yellow (1 long 1 short blink)", + 17: "Cyan (1 long 1 short blink)", + 18: "Magenta (1 long 1 short blink)", + 19: "Temp Dependent (1 long 2 short blink)", + 20: "White (1 long 2 short blinks)", + 21: "Red (1 long 2 short blinks)", + 22: "Green (1 long 2 short blinks)", + 23: "Blue (1 long 2 short blinks)", + 24: "Yellow (1 long 2 short blinks)", + 25: "Cyan (1 long 2 short blinks)", + 26: "Magenta (1 long 2 short blinks)" ], def: "10", title: "Visual LED indicator - signalling mode", descr: "This parameter determines the way in which visual indicator behaves after motion has been detected."], - [key: "ledBrightness", num: 81, size: 1, type: "number", def: 50, min: 0, max: 100, title: "Visual LED indicator - brightness", + [key : "ledBrightness", num: 81, size: 1, type: "number", def: 50, min: 0, max: 100, title: "Visual LED indicator - brightness", descr: "This parameter determines the brightness of the visual LED indicator when indicating motion. (1-100%)"], - [key: "ledLowBrightness", num: 82, size: 2, type: "number", def: 100, min: 0, max: 32767, title: "Visual LED indicator - illuminance for low indicator brightness", + [key : "ledLowBrightness", num: 82, size: 2, type: "number", def: 100, min: 0, max: 32767, title: "Visual LED indicator - illuminance for low indicator brightness", descr: "Light intensity level below which brightness of visual indicator is set to 1% (1-32767 lux)"], - [key: "ledHighBrightness", num: 83, size: 2, type: "number", def: 1000, min: 0, max: 32767, title: "Visual LED indicator - illuminance for high indicator brightness", + [key : "ledHighBrightness", num: 83, size: 2, type: "number", def: 1000, min: 0, max: 32767, title: "Visual LED indicator - illuminance for high indicator brightness", descr: "Light intensity level above which brightness of visual indicator is set to 100%. (value of parameter 82 to 32767 in lux)"] -]} + ] +} + From 18a30efc24ddce7c02a4769265bc5d97ceb0406b Mon Sep 17 00:00:00 2001 From: ALamchaS Date: Wed, 11 Jul 2018 23:13:59 +0200 Subject: [PATCH 02/17] ICP-5345 Hotfix for preference change (#3245) * [ICP-5345 Change initial three axis text to reflect it is disabled by default. * [ICP-5345] Hotfix for preference change. --- .../fibaro-motion-sensor-zw5.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy index 430cfe750b2..047ef9fd3c6 100644 --- a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy @@ -129,6 +129,9 @@ def updated() { } else if (tamperValue == "inactive") { sendEvent(name: "tamper", value: "clear", displayed: false) } + if (settings.tamperOperatingMode == "0") { + sendEvent(name: "motionText", value: "Disabled", displayed: false) + } syncStart() } @@ -591,4 +594,3 @@ private parameterMap() { descr: "Light intensity level above which brightness of visual indicator is set to 100%. (value of parameter 82 to 32767 in lux)"] ] } - From 0f008b056d0d73fd82f88c52ea88c04966e55a2a Mon Sep 17 00:00:00 2001 From: Mariusz A Date: Thu, 12 Jul 2018 00:10:24 +0200 Subject: [PATCH 03/17] Devc 580 Create generic basic heat sensor DTH for FireAngel Thermistek ZHT-630 Heat Alarm/Detector (#2821) * DEVC-580 Create generic basic heat sensor DTH for FireAngel ZHT-630 Heat Detector/Alarm create new z-wave-basic-heat-alarm.groovy for temperature alarm capability add FireAngel ZHT-630 support create read.me * DEVC-580 Copyright date correction Indent changes to tabs Device name correction * DEVC-580 Create generic basic heat sensor DTH for FireAngel Thermistek ZHT-630 Heat Alarm/Detector Event value for smoke clear fixed * Devc 580 Create generic basic heat sensor DTH for FireAngel Thermistek ZHT-630 Heat Alarm/Detector Post Review Fixes Add correct health check interval Unnecessary files removal installed function fixed Readme.MD corrected * DEV-580 Create generic basic heat sensor DTH for FireAngel Thermistek ZHT-630 Heat Alarm/Detector Device Name fix --- .../zwave-basic-heat-alarm.src/README.md | 61 +++++++ .../zwave-basic-heat-alarm.groovy | 164 ++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 devicetypes/smartthings/zwave-basic-heat-alarm.src/README.md create mode 100644 devicetypes/smartthings/zwave-basic-heat-alarm.src/zwave-basic-heat-alarm.groovy diff --git a/devicetypes/smartthings/zwave-basic-heat-alarm.src/README.md b/devicetypes/smartthings/zwave-basic-heat-alarm.src/README.md new file mode 100644 index 00000000000..eda450353df --- /dev/null +++ b/devicetypes/smartthings/zwave-basic-heat-alarm.src/README.md @@ -0,0 +1,61 @@ +# Z-wave Basic Heat Alarm + +Cloud Execution + +Works with: + +* FireAngel Thermistek ZHT-630 Heat Alarm/Detector + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Temperature Alarm** - measure extreme heat +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +ZHT-630 Heat Alarm/Detector is a Z-wave sleepy device and checks in every 4 hour. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (8*60 + 2)mins = 482 mins. + +* __482min__ checkInterval for FireAngel Thermoptek ZHT-630 Heat Alarm/Detector + +## Battery Specification + +FireAngel Thermistek ZHT-630 Heat Alarm/Detector One CR2 battery required + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [FireAngel Thermistek ZHT-630 Heat Alarm/Detector Troubleshooting Tips] +### To connect the FireAngel ZHT-630 Heat Detector/Alarm with the SmartThings Hub +``` +Insert the batterry into Z-Wave module (provided separately). + +Put the Hub in Add Device mode +While the Hub searches, Triple-press Z-Wave module button on back of Z-Wave using Pin, the LED will show a quick blink one per second +The process may take as long as 30s +Upon successful inclusion, The Z-Wave module LED will flash 3 times +When the device is discovered, it will be listed at the top of the screen +Tap the device to rename it and tap Done +When finished, tap Save +Tap Ok to confirm +``` +### To exclude the FireAngel Thermistek ZHT-630 Heat Alarm/Detector +If the FireAngel Thermistek ZHT-630 Heat Alarm/Detector was not discovered, you may need to reset, or exclude, the device before it can successfully connect with the SmartThings Hub. To do this in the SmartThings mobile app: +``` +Put the Hub in General Device Exclusion Mode +Triple-press Z-Wave module button on back of Z-Wave module using Pin, the LED will show a quick double-blink one per second +The process may take as long as 30s +Upon successful exculsion, The Z-Wave module LED will flash 5 times +After the app indicates that the device was successfully removed from SmartThings, follow the first set of instructions above to connect the First Alert device. +``` \ No newline at end of file diff --git a/devicetypes/smartthings/zwave-basic-heat-alarm.src/zwave-basic-heat-alarm.groovy b/devicetypes/smartthings/zwave-basic-heat-alarm.src/zwave-basic-heat-alarm.groovy new file mode 100644 index 00000000000..610859095c5 --- /dev/null +++ b/devicetypes/smartthings/zwave-basic-heat-alarm.src/zwave-basic-heat-alarm.groovy @@ -0,0 +1,164 @@ +/** + * Copyright 2018 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. + * + */ +metadata { + definition(name: "Z-Wave Basic Heat Alarm", namespace: "smartthings", author: "SmartThings") { + capability "Temperature Alarm" + capability "Sensor" + capability "Battery" + capability "Health Check" + + //zw:S type:0701 mfr:026F prod:0001 model:0002 ver:1.07 zwv:4.24 lib:03 cc:5E,86,72,5A,73,80,71,85,59,84 role:06 ff:8C01 ui:8C01 + fingerprint mfr: "026F ", prod: "0001", model: "0002", deviceJoinName: "FireAngel Thermistek Alarm" + } + + simulator { + status "battery 100%": "command: 8003, payload: 64" + status "battery 5%": "command: 8003, payload: 05" + status "HeatNotification": "command: 7105, payload: 00 00 00 FF 04 02 80 4E" + status "HeatClearNotification": "command: 7105, payload: 00 00 00 FF 04 00 80 05" + status "HeatTestNotification": "command: 7105, payload: 00 00 00 FF 04 07 80 05" + } + + tiles(scale: 2) { + multiAttributeTile(name: "heat", type: "lighting", width: 6, height: 4) { + tileAttribute("device.heat", key: "PRIMARY_CONTROL") { + attributeState("cleared", label: "cleared", icon: "st.alarm.smoke.clear", backgroundColor: "#ffffff") + attributeState("detected", label: "HEAT", icon: "st.alarm.smoke.smoke", backgroundColor: "#e86d13") + attributeState("tested", label: "TEST", icon: "st.alarm.smoke.test", backgroundColor: "#e86d13") + } + } + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label: '${currentValue}% battery', unit: "" + } + + main "heat" + details(["heat", "battery"]) + } +} + +def installed() { + def cmds = [] + // Device checks in every 4 hours, this interval allows us to miss one check-in notification before marking offline + cmds << createEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + cmds << createHeatEvents("heatClear") + cmds.each { cmd -> sendEvent(cmd) } +} + +def updated() { + // Device checks in every 4 hours, this interval allows us to miss one check-in notification before marking offline + sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +def getCommandClassVersions() { + [ + 0x80: 1, // Battery + 0x84: 1, // Wake Up + 0x71: 3, // Alarm + 0x72: 1, // Manufacturer Specific + ] +} + +def parse(String description) { + def results = [] + if (description.startsWith("Err")) { + results << createEvent(descriptionText: description, displayed: true) + } else { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + results += zwaveEvent(cmd) + } + } + log.debug "'$description' parsed to ${results.inspect()}" + return results +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) { + def results = [] + results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false) + if (!state.lastbatt || (now() - state.lastbatt) >= 56 * 60 * 60 * 1000) { + results << response([ + zwave.batteryV1.batteryGet().format(), + "delay 2000", + zwave.wakeUpV1.wakeUpNoMoreInformation().format() + ]) + } else { + results << response(zwave.wakeUpV1.wakeUpNoMoreInformation()) + } + return results +} + +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + def map = [name: "battery", unit: "%", isStateChange: true] + state.lastbatt = now() + if (cmd.batteryLevel == 0xFF) { + map.value = 1 + map.descriptionText = "$device.displayName battery is low!" + } else { + map.value = cmd.batteryLevel + } + return createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + def event = [displayed: false] + event.linkText = device.label ?: device.name + event.descriptionText = "$event.linkText: $cmd" + return createEvent(event) +} + +def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + def result = null + if (cmd.notificationType == 0x04) { // Heat Alarm + switch (cmd.event) { + case 0x00: + case 0xFE: + result = createHeatEvents("heatClear") + break + case 0x01: //Overheat detected + case 0x02: //Overheat detected Unknown Location + case 0x03: //Rapid Temperature Rise + case 0x03: //Rapid Temperature Rise Unknown Location + result = createHeatEvents("heat") + break + case 0x07: + result = createHeatEvents("tested") + break + } + } + return result +} + +def createHeatEvents(name) { + def result = null + def text = null + switch (name) { + case "heat": + text = "$device.displayName heat was detected!" + result = createEvent(name: "heat", value: "detected", descriptionText: text) + break + case "tested": + text = "$device.displayName heat tested" + result = createEvent(name: "heat", value: "tested", descriptionText: text) + break + case "heatClear": + text = "$device.displayName heat is clear" + result = createEvent(name: "heat", value: "cleared", descriptionText: text) + break + case "testClear": + text = "$device.displayName heat cleared" + result = createEvent(name: "heat", value: "cleared", descriptionText: text) + break + } + return result +} \ No newline at end of file From e8dd5108f22ea221ec3b9b010e9d3282fa8b1d4e Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Wed, 11 Jul 2018 17:24:34 -0700 Subject: [PATCH 04/17] ONEAPP-5548 Set GE Link to use Generic Dimmer ui metadata --- devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy b/devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy index 0935032e1d4..692f1fc27db 100644 --- a/devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy +++ b/devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy @@ -42,7 +42,7 @@ */ metadata { - definition (name: "GE Link Bulb", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.light", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false) { + definition (name: "GE Link Bulb", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.light", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, mnmn: "SmartThings", vid: "generic-dimmer") { capability "Actuator" capability "Configuration" From be6ae557be3b085eae12e04f29e87d984acc977b Mon Sep 17 00:00:00 2001 From: ALamchaS Date: Fri, 13 Jul 2018 20:37:10 +0200 Subject: [PATCH 05/17] [DEVC-603] Update device join names as per partner's request. (#3253) --- .../zwave-motion-light-sensor.src/README.md | 22 +++++++++---------- .../zwave-motion-light-sensor.groovy | 6 ++--- .../zwave-siren.src/zwave-siren.groovy | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/devicetypes/smartthings/zwave-motion-light-sensor.src/README.md b/devicetypes/smartthings/zwave-motion-light-sensor.src/README.md index c34996d7a46..08d5b6afdfe 100644 --- a/devicetypes/smartthings/zwave-motion-light-sensor.src/README.md +++ b/devicetypes/smartthings/zwave-motion-light-sensor.src/README.md @@ -5,7 +5,7 @@ Cloud Execution Works with: * Dome Motion Detector DMMS1 -* Coolcam NEO Motion Sensor NAS-PD02ZU-T +* NEO Coolcam Motion Sensor NAS-PD02ZU-T ## Table of contents @@ -24,27 +24,27 @@ Works with: ## Device Health -Dome Motion Detector DMMS1 is a Z-wave sleepy device and checks in every 12 hour. +Dome Motion Detector DMMS1 is a Z-wave sleepy device and checks in every 12 hour. Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (12*60 + 2)mins = 1442 mins. -Coolcam NEO Motion Sensor NAS-PD02ZU-T is a Z-wave sleepy device and checks in every 12 hour. +NEO Coolcam Motion Sensor NAS-PD02ZU-T is a Z-wave sleepy device and checks in every 12 hour. Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (12*60 + 2)mins = 1442 mins. * __1442min__ checkInterval for Dome Motion Detector -* __1442min__ checkInterval for Coolcam NEO Motion Sensor +* __1442min__ checkInterval for NEO Coolcam Motion Sensor ## Battery Specification Dome Motion Detector - 1xCR123A battery is required. -Coolcam NEO Motion Sensor - 1xCR123A battery is required. +NEO Coolcam Motion Sensor - 1xCR123A battery is required. ## Troubleshooting If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. Pairing needs to be tried again by placing the device closer to the hub. Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: -* [Dome Motion Detector and Coolcam NEO Motion Sensor Troubleshooting Tips] -### To connect the Dome Motion Detector or Coolcam NEO Motion Sensor with the SmartThings Hub +* [Dome Motion Detector and NEO Coolcam Motion Sensor Troubleshooting Tips] +### To connect the Dome Motion Detector or NEO Coolcam Motion Sensor with the SmartThings Hub ``` Insert the batterry into Z-Wave module (provided separately). @@ -57,13 +57,13 @@ Tap the device to rename it and tap Done When finished, tap Save Tap Ok to confirm ``` -### To exclude the Dome Motion Detector or Coolcam NEO Motion Sensor -If the the Dome Motion Detector or Coolcam NEO Motion Sensor was not discovered, you may need to reset, or ?exclude,? the device before it can successfully connect with the SmartThings Hub. +### To exclude the Dome Motion Detector or NEO Coolcam Motion Sensor +If the the Dome Motion Detector or NEO Coolcam Motion Sensor was not discovered, you may need to reset, or ?exclude,? the device before it can successfully connect with the SmartThings Hub. ``` Put the Hub in General Device Exclusion Mode quickly Triple-press Z-Wave button on located inside the globe The process may take as long as 30s Upon successful exclusion, The Z-Wave module LED will quickly blink 5 times (500ms) -After the app indicates that the device was successfully removed from SmartThings, follow the first set of instructions above to connect the Dome Motion Detector or Coolcam NEO Motion device. +After the app indicates that the device was successfully removed from SmartThings, follow the first set of instructions above to connect the Dome Motion Detector or NEO Coolcam Motion device. ``` - \ No newline at end of file + diff --git a/devicetypes/smartthings/zwave-motion-light-sensor.src/zwave-motion-light-sensor.groovy b/devicetypes/smartthings/zwave-motion-light-sensor.src/zwave-motion-light-sensor.groovy index 4a4b1a98614..9d3c2d39076 100644 --- a/devicetypes/smartthings/zwave-motion-light-sensor.src/zwave-motion-light-sensor.groovy +++ b/devicetypes/smartthings/zwave-motion-light-sensor.src/zwave-motion-light-sensor.groovy @@ -26,7 +26,7 @@ metadata { //zw:S type:0701 mfr:021F prod:0003 model:0083 ver:3.92 zwv:4.05 lib:06 cc:5E,86,72,5A,73,80,31,71,30,70,85,59,84 role:06 ff:8C07 ui:8C07 fingerprint mfr: "021F", prod: "0003", model: "0083", deviceJoinName: "Dome Motion/Light Sensor" //zw:S type:0701 mfr:0258 prod:0003 model:008D ver:3.80 zwv:4.38 lib:06 cc:5E,86,72,5A,73,80,31,71,30,70,85,59,84 role:06 ff:8C07 ui:8C07 - fingerprint mfr: "0258", prod: "0003", model: "008D", deviceJoinName: "Coolcam Neo Motion/Light Sensor" + fingerprint mfr: "0258", prod: "0003", model: "008D", deviceJoinName: "NEO Coolcam Motion/Light Sensor" } simulator { @@ -87,7 +87,7 @@ def getDeviceWakeUpInterval() { switch (zwaveInfo?.mfr) { case "021F": deviceWakeIntervalValue = 12 //Dome reports once in 12h break - case "0258": deviceWakeIntervalValue = 12 //Coolcam Neo reports once in 12h + case "0258": deviceWakeIntervalValue = 12 //NEO Coolcam reports once in 12h break default: deviceWakeIntervalValue = 4 //Default Z-Wave battery device reports once in 4h } @@ -193,4 +193,4 @@ def sensorMotionEvent(value) { result << createEvent(name: "motion", value: "inactive", descriptionText: "$device.displayName motion has stopped") } return result -} \ No newline at end of file +} diff --git a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy index cb4693bb17c..c3928a63391 100644 --- a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy +++ b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy @@ -28,7 +28,7 @@ metadata { capability "Health Check" fingerprint inClusters: "0x20,0x25,0x86,0x80,0x85,0x72,0x71" - fingerprint mfr: "0258", prod: "0003", model: "0088", deviceJoinName: "Neo Coolcam Siren Alarm" + fingerprint mfr: "0258", prod: "0003", model: "0088", deviceJoinName: "NEO Coolcam Siren Alarm" fingerprint mfr: "021F", prod: "0003", model: "0088", deviceJoinName: "Dome Siren" fingerprint mfr: "0060", prod: "000C", model: "0001", deviceJoinName: "Utilitech Siren" //zw:F type:1005 mfr:0258 prod:0003 model:1088 ver:2.94 zwv:4.38 lib:06 cc:5E,86,72,5A,73,70,85,59,25,71,87,80 role:07 ff:8F00 ui:8F00 From 9c2770d41b0f41098c142de8bc06a4c248aaadea Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 16 Jul 2018 10:50:23 -0700 Subject: [PATCH 06/17] ONEAPP-5765 Fibaro Flood sensor battery reporting fix (#3249) Typo in the code wasn't checking for low battery value --- .../fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy index 0253c092b11..0ac7f7b21bb 100644 --- a/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy @@ -259,7 +259,7 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { log.debug "location: "+location def timeDate = location.timeZone ? new Date().format("yyyy MMM dd EEE h:mm:ss a", location.timeZone) : new Date().format("yyyy MMM dd EEE h:mm:ss") - if (value == 0xFF) { // Special value for low battery alert + if (cmd.batteryLevel == 0xFF) { // Special value for low battery alert sendEvent(name: "battery", value: 1, descriptionText: "${device.displayName} has a low battery", isStateChange: true) } else { sendEvent(name: "battery", value: cmd.batteryLevel, descriptionText: "Current battery level") From 99c19a277a6d8a183acfc6c6670733f3faf1434f Mon Sep 17 00:00:00 2001 From: ALamchaS Date: Mon, 16 Jul 2018 20:23:37 +0200 Subject: [PATCH 07/17] [ICP-5298] Updated fingerprint for new hardware version of Zipato Siren Alarm. (#3258) --- devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy index c3928a63391..f9ed139e9c8 100644 --- a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy +++ b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy @@ -31,8 +31,8 @@ metadata { fingerprint mfr: "0258", prod: "0003", model: "0088", deviceJoinName: "NEO Coolcam Siren Alarm" fingerprint mfr: "021F", prod: "0003", model: "0088", deviceJoinName: "Dome Siren" fingerprint mfr: "0060", prod: "000C", model: "0001", deviceJoinName: "Utilitech Siren" - //zw:F type:1005 mfr:0258 prod:0003 model:1088 ver:2.94 zwv:4.38 lib:06 cc:5E,86,72,5A,73,70,85,59,25,71,87,80 role:07 ff:8F00 ui:8F00 - fingerprint mfr: "0258", prod: "0003", model: "1088", deviceJoinName: "Zipato Siren Alarm" + //zw:F type:1005 mfr:0131 prod:0003 model:1083 ver:2.17 zwv:6.02 lib:06 cc:5E,9F,55,73,86,85,8E,59,72,5A,25,71,87,70,80,6C role:07 ff:8F00 ui:8F00 + fingerprint mfr: "0131", prod: "0003", model: "1083", deviceJoinName: "Zipato Siren Alarm" } simulator { From a128a7ccdaeb8b89fb7d36d4471814df9a6ded1c Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 16 Jul 2018 13:43:10 -0700 Subject: [PATCH 08/17] ICP-5372 Coolcam light switch and child device have different icons in OneApp (#3238) The light capability affects the icon in OneApp --- .../smartthings/zwave-dual-switch.src/zwave-dual-switch.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-dual-switch.src/zwave-dual-switch.groovy b/devicetypes/smartthings/zwave-dual-switch.src/zwave-dual-switch.groovy index bacc5a5823c..808a3eebded 100644 --- a/devicetypes/smartthings/zwave-dual-switch.src/zwave-dual-switch.groovy +++ b/devicetypes/smartthings/zwave-dual-switch.src/zwave-dual-switch.groovy @@ -15,7 +15,6 @@ metadata { definition(name: "Z-Wave Dual Switch", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch") { capability "Actuator" capability "Health Check" - capability "Light" capability "Refresh" capability "Sensor" capability "Switch" From 73214a73fdf86c4d17aafc84b7c8e50e8eef8b93 Mon Sep 17 00:00:00 2001 From: ALamchaS Date: Mon, 16 Jul 2018 23:43:33 +0200 Subject: [PATCH 09/17] [WWST-889] Danalock V3 Z-Wave genericized DTH with no codes support. (#3255) * [WWST-889] Danalock V3 Z-Wave genericized DTH with no codes support. * [WWST-889] Review: Removed unused code. --- .../zwave-lock-without-codes.groovy | 571 ++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 devicetypes/smartthings/zwave-lock-without-codes.src/zwave-lock-without-codes.groovy diff --git a/devicetypes/smartthings/zwave-lock-without-codes.src/zwave-lock-without-codes.groovy b/devicetypes/smartthings/zwave-lock-without-codes.src/zwave-lock-without-codes.groovy new file mode 100644 index 00000000000..0cb903b8c43 --- /dev/null +++ b/devicetypes/smartthings/zwave-lock-without-codes.src/zwave-lock-without-codes.groovy @@ -0,0 +1,571 @@ +/** + * Z-Wave Lock + * + * Copyright 2018 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. + * + */ +metadata { + definition(name: "Z-Wave Lock Without Codes", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, mnmn: "SmartThings", vid: "generic-lock-2") { + capability "Actuator" + capability "Lock" + capability "Refresh" + capability "Sensor" + capability "Battery" + capability "Health Check" + capability "Configuration" + + fingerprint mfr: "010E", prod: "0009", model: "0001", deviceJoinName: "Danalock V3 Smart Lock" + } + + simulator { + } + + tiles(scale: 2) { + multiAttributeTile(name: "toggle", type: "generic", width: 6, height: 4) { + tileAttribute("device.lock", key: "PRIMARY_CONTROL") { + attributeState "locked", label: 'locked', action: "lock.unlock", icon: "st.locks.lock.locked", backgroundColor: "#00A0DC", nextState: "unlocking" + attributeState "unlocked", label: 'unlocked', action: "lock.lock", icon: "st.locks.lock.unlocked", backgroundColor: "#ffffff", nextState: "locking" + attributeState "unlocked with timeout", label: 'unlocked', action: "lock.lock", icon: "st.locks.lock.unlocked", backgroundColor: "#ffffff", nextState: "locking" + attributeState "unknown", label: "unknown", action: "lock.lock", icon: "st.locks.lock.unknown", backgroundColor: "#ffffff", nextState: "locking" + attributeState "locking", label: 'locking', icon: "st.locks.lock.locked", backgroundColor: "#00A0DC" + attributeState "unlocking", label: 'unlocking', icon: "st.locks.lock.unlocked", backgroundColor: "#ffffff" + } + } + standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: 'lock', action: "lock.lock", icon: "st.locks.lock.locked", nextState: "locking" + } + standardTile("unlock", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: 'unlock', action: "lock.unlock", icon: "st.locks.lock.unlocked", nextState: "unlocking" + } + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label: '${currentValue}% battery', unit: "" + } + standardTile("refresh", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh" + } + + main "toggle" + details(["toggle", "lock", "unlock", "battery", "refresh"]) + } +} + +import physicalgraph.zwave.commands.doorlockv1.* + +/** + * Called on app installed + */ +def installed() { + // Device-Watch pings if no device events received for 1 hour (checkInterval) + sendEvent(name: "checkInterval", value: 1 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + scheduleInstalledCheck() +} + +/** + * Verify that we have actually received the lock's initial states. + * If not, verify that we have at least requested them or request them, + * and check again. + */ +def scheduleInstalledCheck() { + runIn(120, installedCheck, [forceForLocallyExecuting: true]) +} + +def installedCheck() { + if (device.currentState("lock") && device.currentState("battery")) { + unschedule("installedCheck") + } else { + // We might have called updated() or configure() at some point but not have received a reply, so don't flood the network + if (!state.lastLockDetailsQuery || secondsPast(state.lastLockDetailsQuery, 2 * 60)) { + def actions = updated() + + if (actions) { + sendHubCommand(actions.toHubAction()) + } + } + + scheduleInstalledCheck() + } +} + +/** + * Called on app uninstalled + */ +def uninstalled() { + def deviceName = device.displayName + log.trace "[DTH] Executing 'uninstalled()' for device $deviceName" + sendEvent(name: "lockRemoved", value: device.id, isStateChange: true, displayed: false) +} + +/** + * Executed when the user taps on the 'Done' button on the device settings screen. Sends the values to lock. + * + * @return hubAction: The commands to be executed + */ +def updated() { + // Device-Watch pings if no device events received for 1 hour (checkInterval) + sendEvent(name: "checkInterval", value: 1 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + + def hubAction = null + try { + def cmds = [] + if (!device.currentState("lock") || !state.configured) { + log.debug "Returning commands for lock operation get and battery get" + if (!state.configured) { + cmds << doConfigure() + } + cmds << refresh() + hubAction = response(delayBetween(cmds, 30 * 1000)) + } + } catch (e) { + log.warn "updated() threw $e" + } + hubAction +} + +/** + * Configures the device to settings needed by SmarthThings at device discovery time + */ +def configure() { + log.trace "[DTH] Executing 'configure()' for device ${device.displayName}" + def cmds = doConfigure() + log.debug "Configure returning with commands := $cmds" + cmds +} + +/** + * Returns the list of commands to be executed when the device is being configured/paired + */ +def doConfigure() { + log.trace "[DTH] Executing 'doConfigure()' for device ${device.displayName}" + state.configured = true + def cmds = [] + cmds << secure(zwave.doorLockV1.doorLockOperationGet()) + if (zwaveInfo.mfr != "010E") { + cmds << secure(zwave.batteryV1.batteryGet()) + cmds = delayBetween(cmds, 30 * 1000) + } + + state.lastLockDetailsQuery = now() + + log.debug "Do configure returning with commands := $cmds" + cmds +} + +/** + * Responsible for parsing incoming device messages to generate events + * + * @param description : The incoming description from the device + * + * @return result: The list of events to be sent out + * + */ +def parse(String description) { + log.trace "[DTH] Executing 'parse(String description)' for device ${device.displayName} with description = $description" + + def result = null + if (description.startsWith("Err")) { + if (state.sec) { + result = createEvent(descriptionText: description, isStateChange: true, displayed: false) + } else { + result = createEvent( + descriptionText: "This lock failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.", + eventType: "ALERT", + name: "secureInclusion", + value: "failed", + displayed: true, + ) + } + } else { + def cmd = zwave.parse(description, [0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1]) + if (cmd) { + result = zwaveEvent(cmd) + } + } + log.debug "[DTH] parse() - returning result=$result" + result +} + +/** + * Responsible for parsing SecurityMessageEncapsulation command + * + * @param cmd : The SecurityMessageEncapsulation command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + log.trace "[DTH] Executing 'zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation)' with cmd = $cmd" + def encapsulatedCommand = cmd.encapsulatedCommand([0x62: 1, 0x71: 2, 0x80: 1, 0x85: 2, 0x63: 1, 0x98: 1, 0x86: 1]) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } +} + +/** + * Responsible for parsing NetworkKeyVerify command + * + * @param cmd : The NetworkKeyVerify command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) { + log.trace "[DTH] Executing 'zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify)' with cmd = $cmd" + createEvent(name: "secureInclusion", value: "success", descriptionText: "Secure inclusion was successful", isStateChange: true) +} + +/** + * Responsible for parsing SecurityCommandsSupportedReport command + * + * @param cmd : The SecurityCommandsSupportedReport command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) { + log.trace "[DTH] Executing 'zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport)' with cmd = $cmd" + state.sec = cmd.commandClassSupport.collect { String.format("%02X ", it) }.join() + if (cmd.commandClassControl) { + state.secCon = cmd.commandClassControl.collect { String.format("%02X ", it) }.join() + } + createEvent(name: "secureInclusion", value: "success", descriptionText: "Lock is securely included", isStateChange: true) +} + +/** + * Responsible for parsing DoorLockOperationReport command + * + * @param cmd : The DoorLockOperationReport command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(DoorLockOperationReport cmd) { + log.trace "[DTH] Executing 'zwaveEvent(DoorLockOperationReport)' with cmd = $cmd" + def result = [] + + unschedule("followupStateCheck") + unschedule("stateCheck") + + // DoorLockOperationReport is called when trying to read the lock state or when the lock is locked/unlocked from the DTH or the smart app + def map = [name: "lock"] + map.data = [lockName: device.displayName] + if (cmd.doorLockMode == 0xFF) { + map.value = "locked" + map.descriptionText = "Locked" + } else if (cmd.doorLockMode >= 0x40) { + map.value = "unknown" + map.descriptionText = "Unknown state" + } else if (cmd.doorLockMode == 0x01) { + map.value = "unlocked with timeout" + map.descriptionText = "Unlocked with timeout" + } else { + map.value = "unlocked" + map.descriptionText = "Unlocked" + } + return result ? [createEvent(map), *result] : createEvent(map) +} + +/** + * Responsible for parsing AlarmReport command + * + * @param cmd : The AlarmReport command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) { + log.trace "[DTH] Executing 'zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport)' with cmd = $cmd" + def result = [] + + if (cmd.zwaveAlarmType == 6) { + result = handleAccessAlarmReport(cmd) + } else if (cmd.zwaveAlarmType == 8) { + //I don't this is supported now, but better safe than sorry. + result = handleBatteryAlarmReport(cmd) + } else { + result = handleAlarmReportUsingAlarmType(cmd) + } + + result = result ?: null + log.debug "[DTH] zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport) returning with result = $result" + result +} + +/** + * Responsible for handling Access AlarmReport command + * + * @param cmd : The AlarmReport command to be parsed + * + * @return The event(s) to be sent out + * + */ +private def handleAccessAlarmReport(cmd) { + log.trace "[DTH] Executing 'handleAccessAlarmReport' with cmd = $cmd" + def result = [] + def map = null + def codeID, changeType, codeName + def deviceName = device.displayName + if (1 <= cmd.zwaveAlarmEvent && cmd.zwaveAlarmEvent < 10) { + map = [name: "lock", value: (cmd.zwaveAlarmEvent & 1) ? "locked" : "unlocked"] + } + switch (cmd.zwaveAlarmEvent) { + case 1: // Manually locked + map.descriptionText = "Locked manually" + map.data = [method: (cmd.alarmLevel == 2) ? "keypad" : "manual"] + break + case 2: // Manually unlocked + map.descriptionText = "Unlocked manually" + map.data = [method: "manual"] + break + case 3: // Locked by command + map.descriptionText = "Locked" + map.data = [method: "command"] + break + case 4: // Unlocked by command + map.descriptionText = "Unlocked" + map.data = [method: "command"] + break + case 7: + map = [name: "lock", value: "unknown", descriptionText: "Unknown state"] + map.data = [method: "manual"] + break + case 8: + map = [name: "lock", value: "unknown", descriptionText: "Unknown state"] + map.data = [method: "command"] + break + case 9: // Auto locked + map = [name: "lock", value: "locked", data: [method: "auto"]] + map.descriptionText = "Auto locked" + break + case 0xA: + map = [name: "lock", value: "unknown", descriptionText: "Unknown state"] + map.data = [method: "auto"] + break + case 0xB: + map = [name: "lock", value: "unknown", descriptionText: "Unknown state"] + break + case 0x13: + map = [name: "tamper", value: "detected", descriptionText: "Keypad attempts exceed code entry limit", isStateChange: true, displayed: true] + break + default: + map = [displayed: false, descriptionText: "Alarm event ${cmd.alarmType} level ${cmd.alarmLevel}"] + break + } + + if (map) { + if (map.data) { + map.data.lockName = deviceName + } else { + map.data = [lockName: deviceName] + } + result << createEvent(map) + } + result = result.flatten() + result +} + +/** + * Responsible for handling Battery AlarmReport command + * + * @param cmd : The AlarmReport command to be parsed + * + * @return The event(s) to be sent out + */ +private def handleBatteryAlarmReport(cmd) { + log.trace "[DTH] Executing 'handleBatteryAlarmReport' with cmd = $cmd" + def result = [] + def deviceName = device.displayName + def map = null + switch (cmd.zwaveAlarmEvent) { + case 0x0A: + map = [name: "battery", value: 1, descriptionText: "Battery level critical", displayed: true, data: [lockName: deviceName]] + break + case 0x0B: + map = [name: "battery", value: 0, descriptionText: "Battery too low to operate lock", isStateChange: true, displayed: true, data: [lockName: deviceName]] + break + default: + map = [displayed: false, descriptionText: "Alarm event ${cmd.alarmType} level ${cmd.alarmLevel}"] + break + } + result << createEvent(map) + result +} + +/** + * Responsible for parsing BatteryReport command + * + * @param cmd : The BatteryReport command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + log.trace "[DTH] Executing 'zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport)' with cmd = $cmd" + def map = [name: "battery", unit: "%"] + if (cmd.batteryLevel == 0xFF) { + map.value = 1 + map.descriptionText = "${device.displayName} has a low battery" + map.isStateChange = true + } else { + map.value = cmd.batteryLevel + } + state.lastbatt = now() + if (cmd.batteryLevel == 0 && device.latestValue("battery") > 20) { + // Danalock reports 00 when batteries are changed. We do not know what is the real level at this point. + // We will ignore this level to mimic normal operation of the device (battery level is refreshed only when motor is operating) + log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" + } else { + createEvent(map) + } + +} + + +/** + * Responsible for parsing zwave command + * + * @param cmd : The zwave command to be parsed + * + * @return The event(s) to be sent out + * + */ +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.trace "[DTH] Executing 'zwaveEvent(physicalgraph.zwave.Command)' with cmd = $cmd" + createEvent(displayed: false, descriptionText: "$cmd") +} + +/** + * Executes lock and then check command with a delay on a lock + */ +def lockAndCheck(doorLockMode) { + def cmds = [] + cmds << zwave.doorLockV1.doorLockOperationSet(doorLockMode: doorLockMode) + cmds << zwave.doorLockV1.doorLockOperationGet() + if (zwaveInfo.mfr == "010E") { + //Danalock checks battery only when motor is turned on. + cmds << zwave.batteryV1.batteryGet() + } + secureSequence(cmds, 4200) +} + +/** + * Executes lock command on a lock + */ +def lock() { + log.trace "[DTH] Executing lock() for device ${device.displayName}" + lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_SECURED) +} + +/** + * Executes unlock command on a lock + */ +def unlock() { + log.trace "[DTH] Executing unlock() for device ${device.displayName}" + lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED) +} + +/** + * Executes unlock with timeout command on a lock + */ +def unlockWithTimeout() { + if (zwaveInfo.mfr == "010E") { + //Danalock V3 handles timeout as a parameter that causes all normal unlock() commands to have timeout + log.trace "[DTH] Executing unlock() for device ${device.displayName}" + } else { + log.trace "[DTH] Executing unlockWithTimeout() for device ${device.displayName}" + lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED_WITH_TIMEOUT) + } +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + */ +def ping() { + log.trace "[DTH] Executing ping() for device ${device.displayName}" + runIn(30, followupStateCheck) + if (zwaveInfo.mfr == "010E") { + secure(zwave.doorLockV1.doorLockOperationGet()) + } else { + secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]) + } +} + +/** + * Checks the door lock state. Also, schedules checking of door lock state every one hour. + */ +def followupStateCheck() { + runEvery1Hour(stateCheck) + stateCheck() +} + +/** + * Checks the door lock state + */ +def stateCheck() { + sendHubCommand(new physicalgraph.device.HubAction(secure(zwave.doorLockV1.doorLockOperationGet()))) +} + +/** + * Called when the user taps on the refresh button + */ +def refresh() { + log.trace "[DTH] Executing refresh() for device ${device.displayName}" + if (zwaveInfo.mfr == "010E") { + secure(zwave.doorLockV1.doorLockOperationGet()) + } else { + secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]) + } +} + +/** + * Encapsulates a command + * + * @param cmd : The command to be encapsulated + * + * @returns ret: The encapsulated command + */ +private secure(physicalgraph.zwave.Command cmd) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() +} + +/** + * Encapsulates list of command and adds a delay + * + * @param commands : The list of command to be encapsulated + * @param delay : The delay between commands + * + * @returns The encapsulated commands + */ +private secureSequence(commands, delay = 4200) { + delayBetween(commands.collect { secure(it) }, delay) +} + +/** + * Checks if the time elapsed from the provided timestamp is greater than the number of senconds provided + * + * @param timestamp : The timestamp + * @param seconds : The number of seconds + * + * @returns true if elapsed time is greater than number of seconds provided, else false + */ +private Boolean secondsPast(timestamp, seconds) { + if (!(timestamp instanceof Number)) { + if (timestamp instanceof Date) { + timestamp = timestamp.time + } else if ((timestamp instanceof String) && timestamp.isNumber()) { + timestamp = timestamp.toLong() + } else { + return true + } + } + return (now() - timestamp) > (seconds * 1000) +} + From d9d1cbf437cad635e6d8e3eaee8f6b65a5f9672e Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 16 Jul 2018 15:44:12 -0700 Subject: [PATCH 10/17] ICP-5250 Fibaro Wall Plug US not updating power (#3250) * ICP-5250 Fibaro Wall Plug US not updating power * ICP-5250 Fibaro Wall Plug US not updating power Style fixes * ICP-5250 Fibaro Wall Plug US not updating power Style fixes --- .../fibaro-wall-plug-us-zw5.groovy | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/devicetypes/fibargroup/fibaro-wall-plug-us-zw5.src/fibaro-wall-plug-us-zw5.groovy b/devicetypes/fibargroup/fibaro-wall-plug-us-zw5.src/fibaro-wall-plug-us-zw5.groovy index 885bbecbca6..dfbddfbed9e 100644 --- a/devicetypes/fibargroup/fibaro-wall-plug-us-zw5.src/fibaro-wall-plug-us-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-wall-plug-us-zw5.src/fibaro-wall-plug-us-zw5.groovy @@ -304,24 +304,34 @@ def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cm def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep=null) { log.warn "${device.displayName} - MeterReport received, value: ${cmd.scaledMeterValue} scale: ${cmd.scale} ep: $ep" - if (ep==1) { + if (!ep || ep==1) { log.warn "chanell1" switch (cmd.scale) { - case 0: sendEvent([name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]); break; - case 2: sendEvent([name: "power", value: cmd.scaledMeterValue, unit: "W"]); break; + case 0: sendEvent([name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]); + break; + case 2: sendEvent([name: "power", value: cmd.scaledMeterValue, unit: "W"]); + break; + } + if (device.currentValue("energy") != null) { + multiStatusEvent("${device.currentValue("power")} W / ${device.currentValue("energy")} kWh") + } else { + multiStatusEvent("${device.currentValue("power")} W / 0.00 kWh") } - if (device.currentValue("energy") != null) {multiStatusEvent("${device.currentValue("power")} W / ${device.currentValue("energy")} kWh")} - else {multiStatusEvent("${device.currentValue("power")} W / 0.00 kWh")} } if (ep==2) { log.warn "chanell2" switch (cmd.scale) { - case 0: getChild(2)?.sendEvent([name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]); break; - case 2: getChild(2)?.sendEvent([name: "power", value: cmd.scaledMeterValue, unit: "W"]); break; + case 0: getChild(2)?.sendEvent([name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]); + break; + case 2: getChild(2)?.sendEvent([name: "power", value: cmd.scaledMeterValue, unit: "W"]); + break; + } + if (device.currentValue("energy") != null) { + ch2MultiStatusEvent("${getChild(2)?.currentValue("power")} W / ${getChild(2)?.currentValue("energy")} kWh") + } else { + ch2MultiStatusEvent("${getChild(2)?.currentValue("power")} W / 0.00 kWh") } - if (device.currentValue("energy") != null) {ch2MultiStatusEvent("${getChild(2)?.currentValue("power")} W / ${getChild(2)?.currentValue("energy")} kWh")} - else {ch2MultiStatusEvent("${getChild(2)?.currentValue("power")} W / 0.00 kWh")} } } From 6c34a469b66189ba35657dadf778bf1f0f6a60cd Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 16 Jul 2018 16:04:42 -0700 Subject: [PATCH 11/17] WWST-1093 Fibaro Wall Plug UK Fingerprint (#3248) * WWST-1093 Fibaro Wall Plug UK Fingerprint * WWST-1093 Fibaro Wall Plug UK Fixes duplicate fingerprint and adds UK fingerprint to correct DTH --- .../fibaro-wall-plug-eu-zw5.src/fibaro-wall-plug-eu-zw5.groovy | 3 ++- .../zwave-metering-switch-secure.groovy | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/fibargroup/fibaro-wall-plug-eu-zw5.src/fibaro-wall-plug-eu-zw5.groovy b/devicetypes/fibargroup/fibaro-wall-plug-eu-zw5.src/fibaro-wall-plug-eu-zw5.groovy index 76d6edc2f46..3b977448e5c 100644 --- a/devicetypes/fibargroup/fibaro-wall-plug-eu-zw5.src/fibaro-wall-plug-eu-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-wall-plug-eu-zw5.src/fibaro-wall-plug-eu-zw5.groovy @@ -12,7 +12,8 @@ metadata { command "reset" - fingerprint mfr: "010F", prod: "0602", model: "1001", deviceJoinName: "Fibaro Wall Plug EU ZW5" + fingerprint mfr: "010F", prod: "0602", model: "1001", deviceJoinName: "Fibaro Wall Plug EU ZW5" // EU + fingerprint mfr: "010F", prod: "1801", model: "1000", deviceJoinName: "Fibaro Wall Plug ZW5" // UK fingerprint deviceId: "0x1001", inClusters:"0x5E,0x22,0x59,0x56,0x7A,0x32,0x71,0x73,0x31,0x85,0x70,0x72,0x5A,0x8E,0x25,0x86" } diff --git a/devicetypes/smartthings/zwave-metering-switch-secure.src/zwave-metering-switch-secure.groovy b/devicetypes/smartthings/zwave-metering-switch-secure.src/zwave-metering-switch-secure.groovy index 5290fca2c4a..a362f7a3235 100644 --- a/devicetypes/smartthings/zwave-metering-switch-secure.src/zwave-metering-switch-secure.groovy +++ b/devicetypes/smartthings/zwave-metering-switch-secure.src/zwave-metering-switch-secure.groovy @@ -26,8 +26,7 @@ metadata { command "reset" fingerprint deviceId: "0x1001", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x32, 0x8E, 0x71, 0x73, 0x98, 0x31, 0x25, 0x86", outClusters: "" - fingerprint mfr:"0072", prod:"0501", model:"0F06", deviceJoinName: "Fibaro Wall Plug ZW5" - fingerprint mfr: "010F", prod: "0602", model: "1001", deviceJoinName: "Fibaro Wall Plug ZW5" + fingerprint mfr: "0072", prod: "0501", model: "0F06", deviceJoinName: "Fibaro Wall Plug ZW5" // US } // simulator metadata From 9a9ac07877e1c1cab19ca30337d7688eacc90957 Mon Sep 17 00:00:00 2001 From: ALamchaS Date: Tue, 17 Jul 2018 20:35:02 +0200 Subject: [PATCH 12/17] [WWST-1134] Adding Ecolink Tilt Sensor fingerprint to generic Z-Wave Door/Window Sensor DTH. (#3264) --- .../zwave-door-window-sensor.src/zwave-door-window-sensor.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy b/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy index 0de25ffbc33..1661ab222c3 100644 --- a/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy +++ b/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy @@ -35,6 +35,7 @@ metadata { fingerprint mfr: "0086", prod: "0102", model: "0059", deviceJoinName: "Aeotec Recessed Door Sensor" fingerprint mfr: "014A", prod: "0001", model: "0002", deviceJoinName: "Ecolink Door/Window Sensor" fingerprint mfr: "014A", prod: "0001", model: "0003", deviceJoinName: "Ecolink Tilt Sensor" + fingerprint mfr: "014A", prod: "0004", model: "0003", deviceJoinName: "Ecolink Tilt Sensor" fingerprint mfr: "011A", prod: "0601", model: "0903", deviceJoinName: "Enerwave Magnetic Door/Window Sensor" fingerprint mfr: "014F", prod: "2001", model: "0102", deviceJoinName: "Nortek GoControl Door/Window Sensor" fingerprint mfr: "0063", prod: "4953", model: "3031", deviceJoinName: "Jasco Hinge Pin Door Sensor" From 3f6be7acfef5153419cbe9ebe64e449bc73b3f48 Mon Sep 17 00:00:00 2001 From: BYDengS <40649638+BYDengS@users.noreply.github.com> Date: Tue, 17 Jul 2018 13:20:21 -0700 Subject: [PATCH 13/17] Wwst 951 Orvibo Moisture Sensor (#3234) * Add a new DTH for Orvibo Moisture Sensor. --- .../orvibo-Moisture-Sensor.groovy | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy diff --git a/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy new file mode 100644 index 00000000000..c2ef93e692b --- /dev/null +++ b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy @@ -0,0 +1,150 @@ +/* + * Copyright 2018 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. + * + * Orvibo Moisture Sensor + * + * Author: Deng Biaoyi/biaoyi.deng@samsung.com + * + * Date:2018-07-03 + */ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition(name: "Orvibo Moisture Sensor", namespace: "smartthings", author: "SmartThings", vid: "generic-leak", mnmn:"SmartThings", ocfDeviceType: "x.com.st.d.sensor.moisture") { + capability "Configuration" + capability "Refresh" + capability "Water Sensor" + capability "Sensor" + capability "Health Check" + capability "Battery" + + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", outClusters: "0019", manufacturer: "Heiman", model: "2f077707a13f4120846e0775df7e2efe" + } + + simulator { + + status "dry": "zone status 0x0020 -- extended status 0x00" + status "wet": "zone status 0x0021 -- extended status 0x00" + + for (int i = 0; i <= 90; i += 10) { + status "battery 0021 0x${i}": "read attr - raw: 8C900100010A21000020C8, dni: 8C90, endpoint: 01, cluster: 0001, size: 0A, attrId: 0021, result: success, encoding: 20, value: ${i}" + } + } + + tiles(scale: 2) { + multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){ + tileAttribute ("device.water", key: "PRIMARY_CONTROL") { + attributeState "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff" + attributeState "wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc" + } + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", action: "refresh.refresh", icon: "st.secondary.refresh" + } + + main "water" + details(["water", "battery", "refresh"]) + } +} + +def parse(String description) { + log.debug "description: $description" + + def result + Map map = zigbee.getEvent(description) + + if (!map) { + if (description?.startsWith('zone status')) { + map = getMoistureResult(description) + } else if(description?.startsWith('enroll request')){ + List cmds = zigbee.enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + }else { + Map descMap = zigbee.parseDescriptionAsMap(description) + if (descMap?.clusterInt == 0x0500 && descMap.attrInt == 0x0002) { + map = getMoistureResult(description) + } else if (descMap?.clusterInt == 0x0001 && descMap?.attrInt == 0x0021 && descMap?.commandInt != 0x07 && descMap?.value) { + map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) + } + } + } + if(map&&!result){ + result = createEvent(map) + } + log.debug "Parse returned $result" + + result +} + +def ping() { + refresh() +} + +def refresh() { + log.debug "Refreshing Values" + def refreshCmds = [] + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + + zigbee.enrollResponse() + + refreshCmds +} + +def installed(){ + log.debug "call installed()" + sendEvent(name: "checkInterval", value: 20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) +} + +def configure() { + sendEvent(name: "checkInterval", value: 20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + + log.debug "Configuring Reporting" + def configCmds = [] + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) + refresh() + configCmds +} + +def getMoistureResult(description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + def value = zs?.isAlarm1Set()?"wet":"dry" + [ + name : 'water', + value : value, + descriptionText: "${device.displayName} is $value", + translatable : true + ] +} + +def getBatteryPercentageResult(rawValue) { + log.debug "Battery Percentage" + def result = [:] + + if (0 <= rawValue && rawValue <= 200) { + result.name = 'battery' + result.translatable = true + result.value = Math.round(rawValue / 2) + result.descriptionText = "${device.displayName} battery was ${result.value}%" + } + + log.debug "${device.displayName} battery was ${result.value}%" + result +} \ No newline at end of file From ca7c57ccafa12ed5b34cf45fe6cf0230e1600756 Mon Sep 17 00:00:00 2001 From: FenMei007S <40883570+FenMei007S@users.noreply.github.com> Date: Wed, 18 Jul 2018 04:20:39 +0800 Subject: [PATCH 14/17] WWST-967 Add DTH for orvibo temp/humidity sensor (#3231) * Add DTH for orvibo temp/humidity sensor * update orvibo temp/humidity sensor dth * Fix humidity not report issue * update format * Modify temp/humidity sensor dth to support orvibo device * add device name for orvibo device * add manufacturer and update format --- .../smartsense-temp-humidity-sensor.groovy | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy index 6b1d902bb18..94c496b466e 100644 --- a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy @@ -26,6 +26,7 @@ metadata { capability "Sensor" fingerprint endpointId: "01", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003" + fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0003,0402", manufacturer: "Heiman", model: "b467083cfc864f5e826459e5d8ea6079", deviceJoinName: "Orvibo Temperature & Humidity Sensor" } simulator { @@ -138,10 +139,19 @@ def ping() { def refresh() { log.debug "refresh temperature, humidity, and battery" - return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware - zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware - zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + - zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + + def manufacturer = device.getDataValue("manufacturer") + + if (manufacturer == "Heiman") { + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, [destEndpoint: 0x01])+ + zigbee.readAttribute(0x0402, 0x0000, [destEndpoint: 0x01])+ + zigbee.readAttribute(0x0405, 0x0000, [destEndpoint: 0x02]) + } else { + return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware + zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + } } def configure() { @@ -153,10 +163,18 @@ def configure() { // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity // battery minReport 30 seconds, maxReportTime 6 hrs by default - return refresh() + - zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0x104E]) + // New firmware - zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0xC2DF]) + // Original firmware - zigbee.batteryConfig() + - zigbee.temperatureConfig(30, 300) - + def manufacturer = device.getDataValue("manufacturer") + if (manufacturer == "Heiman") { + return refresh() + + zigbee.temperatureConfig(30, 300) + + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) + + zigbee.configureReporting(0x0405, 0x0000, DataType.UINT16, 30, 3600, 100, [destEndpoint: 0x02]) + } else { + return refresh() + + zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0x104E]) + // New firmware + zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0xC2DF]) + // Original firmware + zigbee.batteryConfig() + + zigbee.temperatureConfig(30, 300) + } } + From 8f88e4cf5e7a0cf36ca4ac42f7f4d2951b8c34d4 Mon Sep 17 00:00:00 2001 From: FenMei007S <40883570+FenMei007S@users.noreply.github.com> Date: Wed, 18 Jul 2018 04:21:54 +0800 Subject: [PATCH 15/17] WWST-963 Add DTH for orvibo smoke sensor (#3229) * Add DTH for orvibo smoke sensor * update orvibo smoke sensor dth * update format * change check interval, avoid marking offline before receiving device status report * change config report of battery * change dth to smoke generic * change device type name * add space after keyword --- .../zigbee-smoke-sensor.groovy | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 devicetypes/smartthings/zigbee-smoke-sensor.src/zigbee-smoke-sensor.groovy diff --git a/devicetypes/smartthings/zigbee-smoke-sensor.src/zigbee-smoke-sensor.groovy b/devicetypes/smartthings/zigbee-smoke-sensor.src/zigbee-smoke-sensor.groovy new file mode 100644 index 00000000000..8af75458dd2 --- /dev/null +++ b/devicetypes/smartthings/zigbee-smoke-sensor.src/zigbee-smoke-sensor.groovy @@ -0,0 +1,141 @@ + /* + * Copyright 2018 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. + * Author : Fen Mei / f.mei@samsung.com + * Date : 2018-07-06 + */ + +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition (name: "Zigbee Smoke Sensor", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "x.com.st.d.sensor.smoke", vid: "generic-smoke") { + capability "Smoke Detector" + capability "Sensor" + capability "Battery" + capability "Configuration" + capability "Refresh" + capability "Health Check" + + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500", outClusters: "", manufacturer: "Heiman", model: "b5db59bfd81e4f1f95dc57fdbba17931", deviceJoinName: "Orvibo Smoke Sensor" + } + + tiles { + standardTile("smoke", "device.smoke", width: 2, height: 2) { + state("clear", label:"Clear", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff") + state("detected", label:"Smoke!", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13") + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label: '${currentValue}% battery', unit: "" + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", action: "refresh.refresh", icon: "st.secondary.refresh" + } + + main "smoke" + details(["smoke", "battery", "refresh"]) + } +} + +def installed(){ + log.debug "installed" + refresh() +} + +def parse(String description) { + log.debug "description(): $description" + def map = zigbee.getEvent(description) + if (!map) { + if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } else { + map = parseAttrMessage(description) + } + } + log.debug "Parse returned $map" + def result = map ? createEvent(map) : [:] + if (description?.startsWith('enroll request')) { + List cmds = zigbee.enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it)} + } + return result +} + +def parseAttrMessage(String description){ + def descMap = zigbee.parseDescriptionAsMap(description) + def map = [:] + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) { + map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) + } else if (descMap?.clusterInt == 0x0500 && descMap.attrInt == 0x0002) { + def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16)) + map = translateZoneStatus(zs) + } + return map; +} + +def parseIasMessage(String description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + return getDetectedResult(zs.isAlarm1Set() || zs.isAlarm2Set()) +} + +private Map translateZoneStatus(ZoneStatus zs) { + return getDetectedResult(zs.isAlarm1Set() || zs.isAlarm2Set()) +} + +private Map getBatteryPercentageResult(rawValue) { + log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" + def result = [:] + + if (0 <= rawValue && rawValue <= 200) { + result.name = 'battery' + result.translatable = true + result.value = Math.round(rawValue / 2) + result.descriptionText = "${device.displayName} battery was ${result.value}%" + } + + return result +} +def getDetectedResult(value) { + def detected = value ? 'detected': 'clear' + String descriptionText = "${device.displayName} smoke ${detected}" + return [name:'smoke', + value: detected, + descriptionText:descriptionText, + translatable:true] +} + +def refresh() { + log.debug "Refreshing Values" + def refreshCmds = [] + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + return refreshCmds +} +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + log.debug "ping " + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) +} +def configure() { + log.debug "configure" + sendEvent(name: "checkInterval", value:6 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + + return zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) + refresh() +} + From fead801a4319d041718400127d277f7a93a9070d Mon Sep 17 00:00:00 2001 From: jinkangzhangS <40658559+jinkangzhangS@users.noreply.github.com> Date: Tue, 17 Jul 2018 16:23:09 -0400 Subject: [PATCH 16/17] WWST-955 Orvibo Motion Sensor (#3210) * add Orvibo Motion sensor DTH add Orvibo Motion sensor DTH referring to Orvibo ZCL info. * add Orvibo Motion sensor DTH add Orvibo Motion sensor DTH referring to Orvibo ZCL info. * update Orvibo motion sensor. update Orvibo motion sensor. * remove used code update Orvibo motion sensor , removed used dth code * update orvibo dth update orvibo dth * add model id for orvibo sensor. add model id in fingerprint , fixed battery return null bug . * add ovribo motion sensor install inital state poll [body] * using tab installed space . * remove fake attribute in install function remove command configrure report . the orvibo vendor did not support it . The battery attribute is reported without bind and reporting CFG. the NXP plan reports the electricity once in 20 minutes. * optimize the dth code * remove whitespace --- .../Orvibo-Motion-Sensor.groovy | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 devicetypes/smartthings/orvibo-motion-sensor.src/Orvibo-Motion-Sensor.groovy diff --git a/devicetypes/smartthings/orvibo-motion-sensor.src/Orvibo-Motion-Sensor.groovy b/devicetypes/smartthings/orvibo-motion-sensor.src/Orvibo-Motion-Sensor.groovy new file mode 100644 index 00000000000..836fd700f29 --- /dev/null +++ b/devicetypes/smartthings/orvibo-motion-sensor.src/Orvibo-Motion-Sensor.groovy @@ -0,0 +1,140 @@ +/* + * Copyright 2018 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. + * Author : jinkang zhang / jk0218.zhang@samsung.com + * Date : 2018-07-04 + */ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType +metadata { + definition(name: "Orvibo Motion Sensor", namespace: "smartthings", author: "SmartThings", runLocally: false, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, mnmn: "SmartThings", vid: "generic-motion-2") { + capability "Motion Sensor" + capability "Configuration" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Sensor" + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "ORVIBO",model:"895a2d80097f4ae2b2d40500d5e03dcc" + } + simulator { + status "active": "zone status 0x0001 -- extended status 0x00" + for (int i = 0; i <= 100; i += 11) { + status "battery ${i}%": "read attr - raw: 2E6D01000108210020C8, dni: 2E6D, endpoint: 01, cluster: 0001, size: 08, attrId: 0021, encoding: 20, value: ${i}" + } + } + tiles(scale: 2) { + multiAttributeTile(name: "motion", type: "generic", width: 6, height: 4) { + tileAttribute("device.motion", key: "PRIMARY_CONTROL") { + attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#00A0DC" + attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#cccccc" + } + } + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label: '${currentValue}% battery', unit: "" + } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", action: "refresh.refresh", icon: "st.secondary.refresh" + } + main(["motion"]) + details(["motion","battery", "refresh"]) + } +} +def stopMotion() { + log.debug "motion inactive" + sendEvent(getMotionResult(false)) +} +def installed(){ + log.debug "installed" + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + +} +def parse(String description) { + log.debug "description(): $description" + def map = zigbee.getEvent(description) + if(!map){ + if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + motionHandler(description); + } else { + map = batteyHandler(description); + } + } + log.debug "Parse returned $map" + def result = map ? createEvent(map) : [:] + if (description?.startsWith('enroll request')) { + List cmds = zigbee.enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + return result +} +def batteyHandler(String description){ + def descMap = zigbee.parseDescriptionAsMap(description) + def map = [:] + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) { + map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) + } + return map; +} +def motionHandler(String description){ + //inactive + def isActive = zigbee.translateStatusZoneType19(description) + if (isActive) { + def timeout = 3 + log.debug "Stopping motion in ${timeout} seconds" + runIn(timeout, stopMotion) + } +} +def parseIasMessage(String description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + return getMotionResult(zs.isAlarm1Set() || zs.isAlarm2Set()) +} +def getBatteryPercentageResult(rawValue) { + log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" + def result = [:] + if (0 <= rawValue && rawValue <= 200) { + result.name = 'battery' + result.translatable = true + result.value = Math.round(rawValue / 2) + result.descriptionText = "${device.displayName} battery was ${result.value}%" + } + return result +} +def getMotionResult(value) { + def descriptionText = value ? "${device.displayName} detected motion" : "${device.displayName} motion has stopped" + return [ + name : 'motion', + value : value ? 'active' : 'inactive', + descriptionText : descriptionText, + translatable : true + ] +} +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + log.debug "ping " + return zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) +} +def refresh() { + log.debug "Refreshing Values" + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + + zigbee.enrollResponse() +} +def configure() { + log.debug "configure" + sendEvent(name: "checkInterval", value:20 * 60 + 2*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) +} From 33f62409046943016eb1b0a7fe832af4b0886506 Mon Sep 17 00:00:00 2001 From: BYDengS <40649638+BYDengS@users.noreply.github.com> Date: Tue, 17 Jul 2018 13:23:33 -0700 Subject: [PATCH 17/17] WWST-947 ORVIBO Contact Sensor (#3209) * Add Orvibo Contact Sensor. --- .../Orvibo-Contact-Sensor.groovy | 151 ++++++++++++++++++ .../smartsense-moisture-sensor.groovy | 0 2 files changed, 151 insertions(+) create mode 100755 devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy mode change 100644 => 100755 devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy new file mode 100755 index 00000000000..1510a0ca325 --- /dev/null +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -0,0 +1,151 @@ +/** + * Copyright 2018 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. + * + * + * Orvibo Contact Sensor + * + * Author: Deng Biaoyi + * + * Date:2018-07-03 + */ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition(name: "Orvibo Contact Sensor", namespace: "smartthings", author: "biaoyi.deng@samsung.com", runLocally: false, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, mnmn:"SmartThings", vid:"generic-contact-3", ocfDeviceType: "x.com.st.d.sensor.contact") { + capability "Battery" + capability "Configuration" + capability "Contact Sensor" + capability "Refresh" + capability "Health Check" + capability "Sensor" + + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "ORVIBO", model: "e70f96b3773a4c9283c6862dbafb6a99" + } + + simulator { + + status "open": "zone status 0x0021 -- extended status 0x00" + status "close": "zone status 0x0000 -- extended status 0x00" + + for (int i = 0; i <= 90; i += 10) { + status "battery 0x${i}": "read attr - raw: 2E6D01000108210020C8, dni: 2E6D, endpoint: 01, cluster: 0001, size: 08, attrId: 0021, encoding: 20, value: ${i}" + } + } + + tiles(scale: 2) { + multiAttributeTile(name: "contact", type: "generic", width: 6, height: 4) { + tileAttribute("device.contact", key: "PRIMARY_CONTROL") { + attributeState "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#e86d13" + attributeState "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#00A0DC" + } + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label: '${currentValue}% battery', unit: "" + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", action: "refresh.refresh", icon: "st.secondary.refresh" + } + + main(["contact"]) + details(["contact", "battery", "refresh"]) + } +} + +def parse(String description) { + log.debug "description: $description" + + def result = [:] + Map map = zigbee.getEvent(description) + if (!map) { + if (description?.startsWith('zone status')) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + map = zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed') + result = createEvent(map) + }else if(description?.startsWith('enroll request')){ + List cmds = zigbee.enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + }else { + Map descMap = zigbee.parseDescriptionAsMap(description) + if (descMap?.clusterInt == 0x0001 && descMap?.commandInt != 0x07 && descMap?.value) { + if(descMap?.attrInt==0x0021){ + map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) + result = createEvent(map) + } + } else if (descMap?.clusterInt == 0x0500 && descMap?.attrInt == 0x0002) { + def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16)) + map = getContactResult(zs.isAlarm1Set() ? "open" : "closed") + result = createEvent(map) + } + } + }else{ + result = createEvent(map) + } + log.debug "Parse returned $result" + + result +} + +def installed(){ + log.debug "call installed()" + sendEvent(name: "checkInterval", value:20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + refresh() +} +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + log.debug "ping is called" + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) +} + +def refresh() { + log.debug "Refreshing Battery and ZONE Status" + def refreshCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + refreshCmds + zigbee.enrollResponse() +} + +def configure() { + sendEvent(name: "checkInterval", value:20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + log.debug "Configuring Reporting, IAS CIE, and Bindings." + //The electricity attribute is reported without bind and reporting CFG. The TI plan reports the power once in about 10 minutes; the NXP plan reports the electricity once in 20 minutes + refresh() +} + +def getBatteryPercentageResult(rawValue) { + log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" + def result = [:] + + if (0 <= rawValue && rawValue <= 200) { + result.name = 'battery' + result.translatable = true + result.value = Math.round(rawValue / 2) + result.descriptionText = "${device.displayName} battery was ${result.value}%" + } + + result +} + +def getContactResult(value) { + log.debug 'Contact Status' + def linkText = getLinkText(device) + def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}" + [ + name : 'contact', + value : value, + descriptionText: descriptionText + ] +} \ No newline at end of file diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy old mode 100644 new mode 100755