From 91c358c3a605d1d073740ba84ecd2d8b1153e3f1 Mon Sep 17 00:00:00 2001 From: Duncan McKee Date: Sun, 5 Mar 2017 18:46:53 -0500 Subject: [PATCH 01/12] Everspring ST814 and ST815 Multilevel Sensors --- .../everspring-illuminance-sensor.groovy | 163 +++++++++++++++ .../everspring-st814.groovy | 188 ++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 devicetypes/smartthings/everspring-illuminance-sensor.src/everspring-illuminance-sensor.groovy create mode 100644 devicetypes/smartthings/everspring-st814.src/everspring-st814.groovy diff --git a/devicetypes/smartthings/everspring-illuminance-sensor.src/everspring-illuminance-sensor.groovy b/devicetypes/smartthings/everspring-illuminance-sensor.src/everspring-illuminance-sensor.groovy new file mode 100644 index 0000000..6a202f8 --- /dev/null +++ b/devicetypes/smartthings/everspring-illuminance-sensor.src/everspring-illuminance-sensor.groovy @@ -0,0 +1,163 @@ +/** + * Copyright 2017 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. + * + * Everspring ST815 Illuminance Sensor + * + * Author: SmartThings + * Date: 2017-3-4 + */ + +metadata { + definition (name: "Everspring Illuminance Sensor", namespace: "smartthings", author: "SmartThings") { + capability "Illuminance Measurement" + capability "Battery" + capability "Configuration" + capability "Sensor" + capability "Health Check" + + fingerprint mfr:"0060", prod:"0007", model:"0001" + } + + simulator { + for( int i = 0; i <= 100; i += 20 ) { + status "illuminace ${i} lux": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport( + scaledSensorValue: i, precision: 0, sensorType: 3, scale: 1).incomingMessage() + } + + for( int i = 0; i <= 100; i += 20 ) { + status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport( + batteryLevel: i).incomingMessage() + } + + status "wakeup": "command: 8407, payload: " + } + + tiles(scale: 2) { + valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { + state "temperature", label:'${currentValue}°', + backgroundColors:[ + [value: 32, color: "#153591"], + [value: 44, color: "#1e9cbb"], + [value: 59, color: "#90d2a7"], + [value: 74, color: "#44b621"], + [value: 84, color: "#f1d801"], + [value: 92, color: "#d04e00"], + [value: 98, color: "#bc2323"] + ] + } + valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) { + state "humidity", label:'${currentValue}% humidity', unit:"" + } + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + main( ["temperature", "humidity"] ) + details( ["temperature", "humidity", "battery"] ) + } +} + +def updated() { + state.configured = false +} + +def parse(String description) { + def result = [] + + def cmd = zwave.parse(description, [0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2]) + + if (cmd) { + result = zwaveEvent(cmd) + } + + if (result instanceof List) { + log.debug "Parsed '$description' to ${result.collect { it.respondsTo("toHubAction") ? it.toHubAction() : it }}" + } else { + log.debug "Parsed '$description' to ${result}" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + def result = [ + createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false) + ] + if (state.configured) { + result << response(zwave.batteryV1.batteryGet()) + } else { + result << response(configure()) + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) { + if (cmd.alarmType == 1 && cmd.alarmType == 0xFF) { + return createEvent(descriptionText: "${device.displayName} battery is low", isStateChange: true) + } else if (cmd.alarmType == 2 && cmd.alarmLevel == 1) { + return createEvent(descriptionText: "${device.displayName} powered up", isStateChange: false) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) { + + def map = [:] + switch( cmd.sensorType ) { + case 3: + // luminance + map.value = cmd.scaledSensorValue.toInteger().toString() + map.unit = "lux" + map.name = "illuminance" + break; + } + + return createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport 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 + } + + def response_cmds = [] + if (!currentTemperature) { + response_cmds << zwave.sensorMultilevelV2.sensorMultilevelGet().format() + response_cmds << "delay 1000" + } + response_cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() + + return [createEvent(map), response(response_cmds)] +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Unhandled: ${cmd.toString()}" + return [:] +} + +def configure() { + state.configured = true + sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + delayBetween([ + // Auto report time interval in minutes + zwave.configurationV1.configurationSet(parameterNumber: 5, size: 2, scaledConfigurationValue: 20).format(), + + // Auto report lux change threshold + zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: 30).format(), + + // Get battery – report triggers WakeUpNMI + zwave.batteryV1.batteryGet().format() + ]) +} \ No newline at end of file diff --git a/devicetypes/smartthings/everspring-st814.src/everspring-st814.groovy b/devicetypes/smartthings/everspring-st814.src/everspring-st814.groovy new file mode 100644 index 0000000..47b2448 --- /dev/null +++ b/devicetypes/smartthings/everspring-st814.src/everspring-st814.groovy @@ -0,0 +1,188 @@ +/** + * Copyright 2017 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. + * + * Everspring ST814 Temperature/Humidity Sensor + * + * Author: SmartThings + * Date: 2017-3-4 + */ + +metadata { + definition (name: "Everspring ST814", namespace: "smartthings", author: "SmartThings") { + capability "Temperature Measurement" + capability "Relative Humidity Measurement" + capability "Battery" + capability "Configuration" + capability "Sensor" + capability "Health Check" + + fingerprint mfr:"0060", prod:"0006", model:"0001" + } + + simulator { + for( int i = 0; i <= 100; i += 20 ) { + status "temperature ${i}F": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport( + scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1).incomingMessage() + } + + for( int i = 0; i <= 100; i += 20 ) { + status "humidity ${i}%": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport( + scaledSensorValue: i, precision: 0, sensorType: 5).incomingMessage() + } + + for( int i = 0; i <= 100; i += 20 ) { + status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport( + batteryLevel: i).incomingMessage() + } + status "wakeup": "command: 8407, payload: " + } + + tiles(scale: 2) { + valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { + state "temperature", label:'${currentValue}°', + backgroundColors:[ + [value: 32, color: "#153591"], + [value: 44, color: "#1e9cbb"], + [value: 59, color: "#90d2a7"], + [value: 74, color: "#44b621"], + [value: 84, color: "#f1d801"], + [value: 92, color: "#d04e00"], + [value: 98, color: "#bc2323"] + ] + } + valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) { + state "humidity", label:'${currentValue}% humidity', unit:"" + } + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + main( ["temperature", "humidity"] ) + details( ["temperature", "humidity", "battery"] ) + } +} + +def updated() { + state.configured = false +} + +def parse(String description) { + def result = [] + + def cmd = zwave.parse(description, [0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2]) + + if (cmd) { + result = zwaveEvent(cmd) + } + + if (result instanceof List) { + log.debug "Parsed '$description' to ${result.collect { it.respondsTo("toHubAction") ? it.toHubAction() : it }}" + } else { + log.debug "Parsed '$description' to ${result}" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + def result = [ + createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false) + ] + if (state.configured) { + result << response(zwave.batteryV1.batteryGet()) + } else { + result << response(configure()) + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) { + if (cmd.alarmType == 1 && cmd.alarmType == 0xFF) { + return createEvent(descriptionText: "${device.displayName} battery is low", isStateChange: true) + } else if (cmd.alarmType == 2 && cmd.alarmLevel == 1) { + return createEvent(descriptionText: "${device.displayName} powered up", isStateChange: false) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) { + + def map = [:] + switch( cmd.sensorType ) { + case 1: + /* temperature */ + def cmdScale = cmd.scale == 1 ? "F" : "C" + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) + map.unit = getTemperatureScale() + map.name = "temperature" + break + case 5: + /* humidity */ + map.value = cmd.scaledSensorValue.toInteger().toString() + map.unit = "%" + map.name = "humidity" + break + } + + return createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport 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 + } + + def response_cmds = [] + if (!currentTemperature) { + response_cmds << zwave.sensorMultilevelV2.sensorMultilevelGet().format() + response_cmds << "delay 1000" + } + response_cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() + + return [createEvent(map), response(response_cmds)] +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + def result = null + def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2]) + log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}") + if (encapsulatedCommand) { + result = zwaveEvent(encapsulatedCommand) + } + result +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Unhandled: ${cmd.toString()}" + return [:] +} + +def configure() { + state.configured = true + sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + delayBetween([ + // Auto report time interval in minutes + zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: 20).format(), + + // Auto report temperature change threshold + zwave.configurationV1.configurationSet(parameterNumber: 7, size: 1, scaledConfigurationValue: 2).format(), + + // Auto report humidity change threshold + zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: 5).format(), + + // Get battery – report triggers WakeUpNMI + zwave.batteryV1.batteryGet().format() + ]) +} \ No newline at end of file From 52c57f66fb612501062f4613788436d9b83c5cc6 Mon Sep 17 00:00:00 2001 From: Duncan McKee Date: Mon, 6 Mar 2017 13:31:32 -0500 Subject: [PATCH 02/12] DVCSMP-2493 Notify Multi-Channel Control app when root device is deleted --- .../zwave-device-multichannel.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devicetypes/smartthings/zwave-device-multichannel.src/zwave-device-multichannel.groovy b/devicetypes/smartthings/zwave-device-multichannel.src/zwave-device-multichannel.groovy index 14b05d0..54f62d2 100644 --- a/devicetypes/smartthings/zwave-device-multichannel.src/zwave-device-multichannel.groovy +++ b/devicetypes/smartthings/zwave-device-multichannel.src/zwave-device-multichannel.groovy @@ -75,6 +75,10 @@ def parse(String description) { return result } +def uninstalled() { + sendEvent(name: "epEvent", value: "delete all", isStateChange: true, displayed: false, descriptionText: "Delete endpoint devices") +} + def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) { [ createEvent(descriptionText: "${device.displayName} woke up", isStateChange:true), response(["delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]) ] From 12bb6c0492d8e926964a5bed19c7ae51dddbf07e Mon Sep 17 00:00:00 2001 From: Parijat Das Date: Thu, 23 Mar 2017 21:12:09 -0700 Subject: [PATCH 03/12] Added health-check for Z-wave Water Valve --- .../zwave-water-valve.src/.st-ignore | 2 + .../zwave-water-valve.src/README.md | 38 +++++++++++++++++++ .../zwave-water-valve.groovy | 16 ++++++++ 3 files changed, 56 insertions(+) create mode 100644 devicetypes/smartthings/zwave-water-valve.src/.st-ignore create mode 100644 devicetypes/smartthings/zwave-water-valve.src/README.md diff --git a/devicetypes/smartthings/zwave-water-valve.src/.st-ignore b/devicetypes/smartthings/zwave-water-valve.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/zwave-water-valve.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/zwave-water-valve.src/README.md b/devicetypes/smartthings/zwave-water-valve.src/README.md new file mode 100644 index 0000000..d5ad277 --- /dev/null +++ b/devicetypes/smartthings/zwave-water-valve.src/README.md @@ -0,0 +1,38 @@ +# Z-Wave Water Valve + +Cloud Execution + +Works with: + +* [Leak Intelligence Leak Gopher Water Shutoff Valve](https://www.smartthings.com/works-with-smartthings/other/leak-intelligence-leak-gopher-water-shutoff-valve) + + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#Troubleshooting) + +## Capabilities + +* **Actuator** - represents that a Device has commands +* **Health Check** - indicates ability to get device health notifications +* **Valve** - allows for the control of a valve device +* **Polling** - represents that poll() can be implemented for the device +* **Refresh** - _refresh()_ command for status updates +* **Sensor** - detects sensor events + +## Device Health + +SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE` + +* __32min__ checkInterval + +## 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: +* [Leak Intelligence Leak Gopher Water Shutoff Valve Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/209631423-Leak-Gopher-Z-Wave-Valve-Control) + + diff --git a/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy b/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy index 8826760..fec1a46 100644 --- a/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy +++ b/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy @@ -14,12 +14,14 @@ metadata { definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings") { capability "Actuator" + capability "Health Check" capability "Valve" capability "Polling" capability "Refresh" capability "Sensor" fingerprint deviceId: "0x1006", inClusters: "0x25" + fingerprint mfr:"0173", prod:"0003", model:"0002", deviceJoinName: "Leak Intelligence Leak Gopher Water Shutoff Valve" } // simulator metadata @@ -53,7 +55,14 @@ metadata { } +def installed() { + // Device-Watch simply pings if no device events received for 32min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + def updated() { + // Device-Watch simply pings if no device events received for 32min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) response(refresh()) } @@ -114,6 +123,13 @@ def poll() { zwave.switchBinaryV1.switchBinaryGet().format() } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + refresh() +} + def refresh() { log.debug "refresh() is called" def commands = [zwave.switchBinaryV1.switchBinaryGet().format()] From f0de2f1a194b66324b08c0310e0943fe3345845f Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Thu, 18 May 2017 09:56:17 -0500 Subject: [PATCH 04/12] DVCSMP-2659 Reduce Hello Home Phrase Detector Logs --- .../hello-home-phrase-director.groovy | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/smartapps/tslagle13/hello-home-phrase-director.src/hello-home-phrase-director.groovy b/smartapps/tslagle13/hello-home-phrase-director.src/hello-home-phrase-director.groovy index cb78696..78d889e 100644 --- a/smartapps/tslagle13/hello-home-phrase-director.src/hello-home-phrase-director.groovy +++ b/smartapps/tslagle13/hello-home-phrase-director.src/hello-home-phrase-director.groovy @@ -125,19 +125,19 @@ if(allOk) { if(everyoneIsAway() && (state.sunMode == "sunrise")) { - log.info("Home is Empty Setting New Away Mode") + log.debug("Home is Empty Setting New Away Mode") def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60 runIn(delay, "setAway") } if(everyoneIsAway() && (state.sunMode == "sunset")) { - log.info("Home is Empty Setting New Away Mode") + log.debug("Home is Empty Setting New Away Mode") def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60 runIn(delay, "setAway") } else { - log.info("Home is Occupied Setting New Home Mode") + log.debug("Home is Occupied Setting New Home Mode") setHome() @@ -152,7 +152,7 @@ log.debug("Checking if everyone is away") if(everyoneIsAway()) { - log.info("Nobody is home, running away sequence") + log.debug("Nobody is home, running away sequence") def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60 runIn(delay, "setAway") } @@ -161,7 +161,7 @@ else { def lastTime = state[evt.deviceId] if (lastTime == null || now() - lastTime >= 1 * 60000) { - log.info("Someone is home, running home sequence") + log.debug("Someone is home, running home sequence") setHome() } state[evt.deviceId] = now() @@ -175,14 +175,14 @@ if(everyoneIsAway()) { if(state.sunMode == "sunset") { def message = "Performing \"${awayNight}\" for you as requested." - log.info(message) + log.debug(message) sendAway(message) location.helloHome.execute(settings.awayNight) } else if(state.sunMode == "sunrise") { def message = "Performing \"${awayDay}\" for you as requested." - log.info(message) + log.debug(message) sendAway(message) location.helloHome.execute(settings.awayDay) } @@ -192,19 +192,19 @@ } else { - log.info("Somebody returned home before we set to '${newAwayMode}'") + log.debug("Somebody returned home before we set to '${newAwayMode}'") } } //set home mode when house is occupied def setHome() { sendOutOfDateNotification() - log.info("Setting Home Mode!!") + log.debug("Setting Home Mode!!") if(anyoneIsHome()) { if(state.sunMode == "sunset"){ if (location.mode != "${homeModeNight}"){ def message = "Performing \"${homeNight}\" for you as requested." - log.info(message) + log.debug(message) sendHome(message) location.helloHome.execute(settings.homeNight) } @@ -213,7 +213,7 @@ if(state.sunMode == "sunrise"){ if (location.mode != "${homeModeDay}"){ def message = "Performing \"${homeDay}\" for you as requested." - log.info(message) + log.debug(message) sendHome(message) location.helloHome.execute(settings.homeDay) } @@ -329,4 +329,4 @@ sendNotification("Your version of Hello, Home Phrase Director is currently out of date. Please look for the new version of Hello, Home Phrase Director now called 'Routine Director' in the marketplace.") state.lastTime = (new Date() + 31).getTime() } - } \ No newline at end of file + } From e7713caec984e62b79ce20bb846341d6136cc579 Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Fri, 19 May 2017 14:09:08 -0500 Subject: [PATCH 05/12] DVCSMP-2665 Prevent 'Smart Nightlight' from over scheduling --- .../smartthings/smart-nightlight.src/smart-nightlight.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/smart-nightlight.src/smart-nightlight.groovy b/smartapps/smartthings/smart-nightlight.src/smart-nightlight.groovy index 5afb0be..7894fc9 100644 --- a/smartapps/smartthings/smart-nightlight.src/smart-nightlight.groovy +++ b/smartapps/smartthings/smart-nightlight.src/smart-nightlight.groovy @@ -98,7 +98,7 @@ def motionHandler(evt) { else { state.motionStopTime = now() if(delayMinutes) { - runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: false]) + runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: true]) } else { turnOffMotionAfterDelay() } From 97c9ec7a95491446a038e8c942d206e7e2d649ad Mon Sep 17 00:00:00 2001 From: Zach Varberg Date: Mon, 22 May 2017 11:52:13 -0500 Subject: [PATCH 06/12] Fix NPE for smartsense multi There was a common NPE caused by assuming there would be additional attributes reported with one acceleration value. This corrects it to instead properly handle the case where there weren't additional attributes. This resolves: https://smartthings.atlassian.net/browse/DVCSMP-2668 --- .../smartsense-multi-sensor.src/smartsense-multi-sensor.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index 2629724..2fca3bc 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -178,7 +178,7 @@ private List handleAcceleration(descMap) { result += parseAxis(descMap.additionalAttrs) } } else if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0012) { - def addAttrs = descMap.additionalAttrs + def addAttrs = descMap.additionalAttrs ?: [] addAttrs << ["attrInt": descMap.attrInt, "value": descMap.value] result += parseAxis(addAttrs) } From 06faa988c832256b23a190e5c2fbd0379a70906c Mon Sep 17 00:00:00 2001 From: Dave Hastings Date: Mon, 22 May 2017 17:47:23 -0700 Subject: [PATCH 07/12] DVCSMP-2672 updating read attribute for valve capability updating tile for fortrezz --- .../fortrezz-water-valve.groovy | 21 +++++++++++-------- .../zigbee-valve.src/zigbee-valve.groovy | 2 +- .../zwave-water-valve.groovy | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/devicetypes/smartthings/fortrezz-water-valve.src/fortrezz-water-valve.groovy b/devicetypes/smartthings/fortrezz-water-valve.src/fortrezz-water-valve.groovy index 5c8901a..998787a 100644 --- a/devicetypes/smartthings/fortrezz-water-valve.src/fortrezz-water-valve.groovy +++ b/devicetypes/smartthings/fortrezz-water-valve.src/fortrezz-water-valve.groovy @@ -34,19 +34,22 @@ metadata { } // tile definitions - tiles { - standardTile("contact", "device.contact", width: 2, height: 2, canChangeIcon: true) { - state "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing" - state "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" - state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC" - state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff" + tiles(scale: 2) { + multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){ + tileAttribute ("device.valve", key: "PRIMARY_CONTROL") { + attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing" + attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" + attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC" + attributeState "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff" + } } - standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { + + standardTile("refresh", "device.valve", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" } - main "contact" - details(["contact","refresh"]) + main "valve" + details(["valve","refresh"]) } } diff --git a/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy b/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy index 82ca256..2dab99f 100644 --- a/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy +++ b/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy @@ -39,7 +39,7 @@ metadata { tiles(scale: 2) { multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){ - tileAttribute ("device.contact", key: "PRIMARY_CONTROL") { + tileAttribute ("device.valve", key: "PRIMARY_CONTROL") { attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing" attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing" diff --git a/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy b/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy index dcfe452..4636291 100644 --- a/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy +++ b/devicetypes/smartthings/zwave-water-valve.src/zwave-water-valve.groovy @@ -37,7 +37,7 @@ metadata { // tile definitions tiles(scale: 2) { multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){ - tileAttribute ("device.contact", key: "PRIMARY_CONTROL") { + tileAttribute ("device.valve", key: "PRIMARY_CONTROL") { attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing" attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC" @@ -45,7 +45,7 @@ metadata { } } - standardTile("refresh", "device.contact", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + standardTile("refresh", "device.valve", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" } From 1dae5d39369eda24efd43db4a063a6a9bd11a4fc Mon Sep 17 00:00:00 2001 From: Parijat Das Date: Thu, 25 May 2017 18:56:38 +0530 Subject: [PATCH 08/12] Added health check implementation for Aeon MultiSensor --- .../aeon-multisensor.src/.st-ignore | 2 + .../aeon-multisensor.src/README.md | 48 +++++++++++++++++++ .../aeon-multisensor.groovy | 18 +++++++ 3 files changed, 68 insertions(+) create mode 100644 devicetypes/smartthings/aeon-multisensor.src/.st-ignore create mode 100644 devicetypes/smartthings/aeon-multisensor.src/README.md diff --git a/devicetypes/smartthings/aeon-multisensor.src/.st-ignore b/devicetypes/smartthings/aeon-multisensor.src/.st-ignore new file mode 100644 index 0000000..71af75c --- /dev/null +++ b/devicetypes/smartthings/aeon-multisensor.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md \ No newline at end of file diff --git a/devicetypes/smartthings/aeon-multisensor.src/README.md b/devicetypes/smartthings/aeon-multisensor.src/README.md new file mode 100644 index 0000000..aa61b0d --- /dev/null +++ b/devicetypes/smartthings/aeon-multisensor.src/README.md @@ -0,0 +1,48 @@ +# Aeon Multisensor + +Cloud Execution + +Works with: + +* [Aeotec MultiSensor (DSB05-ZWUS)](https://www.smartthings.com/products/aeotec-multisensor-5) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Motion Sensor** - can detect motion +* **Temperature Measurement** - defines device measures current temperature +* **Relative Humidity Measurement** - allow reading the relative humidity from devices that support it +* **Illuminance Measurement** - gives the illuminance reading from devices that support it +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Health Check** - indicates ability to get device health notifications + + +## Device Health + +Aeon Labs MultiSensor is polled by the hub. +As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins. +Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for +the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row, +it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time. + +* __32min__ checkInterval + +## Battery Specification + +Four AAA batteries are 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: +* [Aeon MultiSensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206157226-How-to-connect-Aeon-Labs-MultiSensors) \ No newline at end of file diff --git a/devicetypes/smartthings/aeon-multisensor.src/aeon-multisensor.groovy b/devicetypes/smartthings/aeon-multisensor.src/aeon-multisensor.groovy index 0044570..b6f3135 100644 --- a/devicetypes/smartthings/aeon-multisensor.src/aeon-multisensor.groovy +++ b/devicetypes/smartthings/aeon-multisensor.src/aeon-multisensor.groovy @@ -20,6 +20,7 @@ metadata { capability "Illuminance Measurement" capability "Sensor" capability "Battery" + capability "Health Check" fingerprint deviceId: "0x2001", inClusters: "0x30,0x31,0x80,0x84,0x70,0x85,0x72,0x86" } @@ -93,6 +94,16 @@ metadata { } } +def installed(){ +// Device-Watch simply pings if no device events received for 32min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +def updated(){ +// Device-Watch simply pings if no device events received for 32min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + // Parse incoming device messages to generate events def parse(String description) { @@ -179,6 +190,13 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { [:] } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + secure(zwave.batteryV1.batteryGet()) +} + def configure() { delayBetween([ // send binary sensor report instead of basic set for motion From 7f3cd0cdaa59da69b362dcd8fd0d14a7300ecf13 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Fri, 26 May 2017 01:39:05 -0700 Subject: [PATCH 09/12] ICP-1016 Fix default value for states for Aeon Siren Gen5 --- devicetypes/smartthings/aeon-siren.src/aeon-siren.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeon-siren.src/aeon-siren.groovy b/devicetypes/smartthings/aeon-siren.src/aeon-siren.groovy index 3eb9770..8c6ecc2 100644 --- a/devicetypes/smartthings/aeon-siren.src/aeon-siren.groovy +++ b/devicetypes/smartthings/aeon-siren.src/aeon-siren.groovy @@ -61,6 +61,8 @@ metadata { def installed() { // Device-Watch simply pings if no device events received for 32min(checkInterval) sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + + response(secure(zwave.basicV1.basicGet())) } def updated() { @@ -163,4 +165,4 @@ private secure(physicalgraph.zwave.Command cmd) { * */ def ping() { secure(zwave.basicV1.basicGet()) -} \ No newline at end of file +} From fc1f1508adee47f693c9308fcd5a4618da0c66db Mon Sep 17 00:00:00 2001 From: Bob Florian Date: Sat, 6 May 2017 09:40:01 -0700 Subject: [PATCH 10/12] MSA-1951 Added Inovelli 2-Channel Smart Plug --- .../inovelli-2-channel-smart-plug.groovy | 266 ++++++++++++++++++ .../switch-child-device.groovy | 49 ++++ 2 files changed, 315 insertions(+) create mode 100644 devicetypes/erocm123/inovelli-2-channel-smart-plug.src/inovelli-2-channel-smart-plug.groovy create mode 100644 devicetypes/erocm123/switch-child-device.src/switch-child-device.groovy diff --git a/devicetypes/erocm123/inovelli-2-channel-smart-plug.src/inovelli-2-channel-smart-plug.groovy b/devicetypes/erocm123/inovelli-2-channel-smart-plug.src/inovelli-2-channel-smart-plug.groovy new file mode 100644 index 0000000..2aabca2 --- /dev/null +++ b/devicetypes/erocm123/inovelli-2-channel-smart-plug.src/inovelli-2-channel-smart-plug.groovy @@ -0,0 +1,266 @@ +/** + * + * Inovelli 2-Channel Smart Plug + * + * github: Eric Maycock (erocm123) + * Date: 2017-04-27 + * Copyright Eric Maycock + * + * Includes all configuration parameters and ease of advanced configuration. + * + * 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: "Inovelli 2-Channel Smart Plug", namespace: "erocm123", author: "Eric Maycock") { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Polling" + capability "Refresh" + capability "Health Check" + fingerprint manufacturer: "015D", prod: "0221", model: "251C" + } + simulator {} + preferences {} + tiles { + multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) { + tileAttribute("device.switch", key: "PRIMARY_CONTROL") { + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff" + attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" + attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff" + } + } + standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh" + } + main(["switch"]) + details(["switch", + childDeviceTiles("all"), "refresh" + ]) + } +} +def parse(String description) { + def result = [] + def cmd = zwave.parse(description) + if (cmd) { + result += zwaveEvent(cmd) + logging("Parsed ${cmd} to ${result.inspect()}", 1) + } else { + logging("Non-parsed event: ${description}", 2) + } + return result +} +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { + logging("BasicReport ${cmd} - ep ${ep}", 2) + if (ep) { + def event + childDevices.each { + childDevice -> + if (childDevice.deviceNetworkId == "$device.deviceNetworkId-ep$ep") { + childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off") + } + } + if (cmd.value) { + event = [createEvent([name: "switch", value: "on"])] + } else { + def allOff = true + childDevices.each { + n -> + if (n.currentState("switch").value != "off") allOff = false + } + if (allOff) { + event = [createEvent([name: "switch", value: "off"])] + } else { + event = [createEvent([name: "switch", value: "on"])] + } + } + return event + } +} +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { + logging("BasicSet ${cmd}", 2) + def result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital") + def cmds = [] + cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1) + cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2) + return [result, response(commands(cmds))] // returns the result of reponse() +} +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { + logging("SwitchBinaryReport ${cmd} - ep ${ep}", 2) + if (ep) { + def event + def childDevice = childDevices.find { + it.deviceNetworkId == "$device.deviceNetworkId-ep$ep" + } + if (childDevice) childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off") + if (cmd.value) { + event = [createEvent([name: "switch", value: "on"])] + } else { + def allOff = true + childDevices.each { + n-> + if (n.currentState("switch").value != "off") allOff = false + } + if (allOff) { + event = [createEvent([name: "switch", value: "off"])] + } else { + event = [createEvent([name: "switch", value: "on"])] + } + } + return event + } else { + def result = createEvent(name: "switch", value: cmd.value ? "on" : "off", type: "digital") + def cmds = [] + cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 1) + cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), 2) + return [result, response(commands(cmds))] // returns the result of reponse() + } +} +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + logging("MultiChannelCmdEncap ${cmd}", 2) + def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1]) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) + } +} +def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { + logging("ManufacturerSpecificReport ${cmd}", 2) + def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) + logging("msr: $msr", 2) + updateDataValue("MSR", msr) +} +def zwaveEvent(physicalgraph.zwave.Command cmd) { + // This will capture any commands not handled by other instances of zwaveEvent + // and is recommended for development so you can see every command the device sends + logging("Unhandled Event: ${cmd}", 2) +} +def on() { + logging("on()", 1) + commands([ + zwave.switchAllV1.switchAllOn(), + encap(zwave.switchBinaryV1.switchBinaryGet(), 1), + encap(zwave.switchBinaryV1.switchBinaryGet(), 2) + ]) +} +def off() { + logging("off()", 1) + commands([ + zwave.switchAllV1.switchAllOff(), + encap(zwave.switchBinaryV1.switchBinaryGet(), 1), + encap(zwave.switchBinaryV1.switchBinaryGet(), 2) + ]) +} +void childOn(String dni) { + logging("childOn($dni)", 1) + def cmds = [] + cmds << new physicalgraph.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0xFF), channelNumber(dni)))) + cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni)))) + sendHubCommand(cmds, 1000) +} +void childOff(String dni) { + logging("childOff($dni)", 1) + def cmds = [] + cmds << new physicalgraph.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0x00), channelNumber(dni)))) + cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni)))) + sendHubCommand(cmds, 1000) +} +void childRefresh(String dni) { + logging("childRefresh($dni)", 1) + def cmds = [] + cmds << new physicalgraph.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni)))) + sendHubCommand(cmds, 1000) +} +def poll() { + logging("poll()", 1) + commands([ + encap(zwave.switchBinaryV1.switchBinaryGet(), 1), + encap(zwave.switchBinaryV1.switchBinaryGet(), 2), + ]) +} +def refresh() { + logging("refresh()", 1) + commands([ + encap(zwave.switchBinaryV1.switchBinaryGet(), 1), + encap(zwave.switchBinaryV1.switchBinaryGet(), 2), + ]) +} +def ping() { + logging("ping()", 1) + refresh() +} +def installed() { + logging("installed()", 1) + command(zwave.manufacturerSpecificV1.manufacturerSpecificGet()) + createChildDevices() +} +def updated() { + logging("updated()", 1) + if (!childDevices) { + createChildDevices() + } else if (device.label != state.oldLabel) { + childDevices.each { + if (it.label == "${state.oldLabel} (CH${channelNumber(it.deviceNetworkId)})") { + def newLabel = "${device.displayName} (CH${channelNumber(it.deviceNetworkId)})" + it.setLabel(newLabel) + } + } + state.oldLabel = device.label + } + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + sendEvent(name: "needUpdate", value: device.currentValue("needUpdate"), displayed: false, isStateChange: true) +} +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + logging("${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd2Integer(cmd.configurationValue)}'", 2) +} +private encap(cmd, endpoint) { + if (endpoint) { + zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } else { + cmd + } +} +private command(physicalgraph.zwave.Command cmd) { + if (state.sec) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } +} +private commands(commands, delay = 1000) { + delayBetween(commands.collect { + command(it) + }, delay) +} +private channelNumber(String dni) { + dni.split("-ep")[-1] as Integer +} +private void createChildDevices() { + state.oldLabel = device.label + for (i in 1..2) { + addChildDevice("Switch Child Device", "${device.deviceNetworkId}-ep${i}", null, [completedSetup: true, label: "${device.displayName} (CH${i})", + isComponent: true, componentName: "ep$i", componentLabel: "Channel $i" + ]) + } +} + +private def logging(message, level) { + if (logLevel != "0") { + switch (logLevel) { + case "1": + if (level > 1) log.debug "$message" + break + case "99": + log.debug "$message" + break + } + } +} diff --git a/devicetypes/erocm123/switch-child-device.src/switch-child-device.groovy b/devicetypes/erocm123/switch-child-device.src/switch-child-device.groovy new file mode 100644 index 0000000..19f997d --- /dev/null +++ b/devicetypes/erocm123/switch-child-device.src/switch-child-device.groovy @@ -0,0 +1,49 @@ +/** + * Switch Child Device + * + * Copyright 2017 Eric Maycock + * + * 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: "Switch Child Device", namespace: "erocm123", author: "Eric Maycock") { + capability "Switch" + capability "Actuator" + capability "Sensor" + capability "Refresh" + } + + tiles { + multiAttributeTile(name:"switch", type: "lighting", width: 3, height: 4, canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"turningOn" + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"turningOff" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" + } + } + standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + } +} + +void on() { + parent.childOn(device.deviceNetworkId) +} + +void off() { + parent.childOff(device.deviceNetworkId) +} + +void refresh() { + parent.childRefresh(device.deviceNetworkId) +} From 79a6a6472e3b3bc035ef19e216030843339526a3 Mon Sep 17 00:00:00 2001 From: jackchi Date: Mon, 29 May 2017 11:02:48 -0700 Subject: [PATCH 11/12] [DHF-7] [ICP-1084] Adds scheduled 1 hr ping for offline devices; updates checkInterval to 1 hour --- .../zwave-lock.src/zwave-lock.groovy | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index 50b251f..bbd9ac8 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -86,13 +86,13 @@ import physicalgraph.zwave.commands.doorlockv1.* import physicalgraph.zwave.commands.usercodev1.* def installed() { - // Device-Watch simply pings if no device events received for 32min(checkInterval) - sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // 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]) } def updated() { - // Device-Watch simply pings if no device events received for 32min(checkInterval) - sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // 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]) try { if (!state.init) { state.init = true @@ -152,6 +152,10 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupported def zwaveEvent(DoorLockOperationReport cmd) { def result = [] + + unschedule("followupStateCheck") + unschedule("stateCheck") + def map = [ name: "lock" ] if (cmd.doorLockMode == 0xFF) { map.value = "locked" @@ -365,7 +369,7 @@ def zwaveEvent(UserCodeReport cmd) { code = state["set$name"] ?: decrypt(state[name]) ?: "****" state.remove("set$name".toString()) } else { - map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: code ] ] + map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: code ], isStateChange: true ] map.descriptionText = "$device.displayName code $cmd.userIdentifier is set" map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode) map.isStateChange = true @@ -456,11 +460,12 @@ def zwaveEvent(physicalgraph.zwave.commands.timev1.TimeGet cmd) { def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { // The old Schlage locks use group 1 for basic control - we don't want that, so unsubscribe from group 1 def result = [ createEvent(name: "lock", value: cmd.value ? "unlocked" : "locked") ] - result << response(zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId)) - if (state.assoc != zwaveHubNodeId) { - result << response(zwave.associationV1.associationGet(groupingIdentifier:2)) - } - result + def cmds = [ + zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId).format(), + "delay 1200", + zwave.associationV1.associationGet(groupingIdentifier:2).format() + ] + [result, response(cmds)] } def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { @@ -530,11 +535,18 @@ def unlockwtimeout() { lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED_WITH_TIMEOUT) } -/** - * PING is used by Device-Watch in attempt to reach the Device - * */ def ping() { - refresh() + runIn(30, followupStateCheck) + secure(zwave.doorLockV1.doorLockOperationGet()) +} + +def followupStateCheck() { + runEvery1Hour(stateCheck) + stateCheck() +} + +def stateCheck() { + sendHubCommand(new physicalgraph.device.HubAction(secure(zwave.doorLockV1.doorLockOperationGet()))) } def refresh() { From d864c5e3b3d4735133876cada6e256a7c082b473 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Fri, 26 May 2017 01:18:02 -0700 Subject: [PATCH 12/12] ICP-1076 Fix Aeon Door/Window Sensor 6 initial value of null --- .../zwave-door-window-sensor.groovy | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 0cf6d46..835d7af 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 @@ -113,9 +113,9 @@ def updated() { def configure() { commands([ - zwave.manufacturerSpecificV2.manufacturerSpecificGet(), - zwave.batteryV1.batteryGet() - ], 6000) + zwave.sensorBinaryV2.sensorBinaryGet(sensorType: zwave.sensorBinaryV2.SENSOR_TYPE_DOOR_WINDOW), + zwave.manufacturerSpecificV2.manufacturerSpecificGet() + ], 1000) } def sensorValueEvent(value) { @@ -190,11 +190,17 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) cmds << command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()) cmds << "delay 1200" } + + if (device.currentValue("contact") == null) { // Incase our initial request didn't make it + cmds << command(zwave.sensorBinaryV2.sensorBinaryGet(sensorType: zwave.sensorBinaryV2.SENSOR_TYPE_DOOR_WINDOW)) + } + if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) { cmds << command(zwave.batteryV1.batteryGet()) - } else { + } else { // If we check the battery state we will send NoMoreInfo in the handler for BatteryReport so that we definitely get the report cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() } + [event, response(cmds)] }