From 81c074366029157f00c9db2bb4465d7fc11dbc99 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Wed, 25 Sep 2019 20:28:10 +0200 Subject: [PATCH 1/7] [WWST-5510] Add ilumi Tunable White Bulb to aeon-multiwhite-bulb (#11322) * Add ilumi Tunable White Bulb to aeon-multiwhite-bulb * Name fix --- .../aeon-multiwhite-bulb.src/aeon-multiwhite-bulb.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/aeon-multiwhite-bulb.src/aeon-multiwhite-bulb.groovy b/devicetypes/smartthings/aeon-multiwhite-bulb.src/aeon-multiwhite-bulb.groovy index cef199f22a3..81d8f6c854c 100644 --- a/devicetypes/smartthings/aeon-multiwhite-bulb.src/aeon-multiwhite-bulb.groovy +++ b/devicetypes/smartthings/aeon-multiwhite-bulb.src/aeon-multiwhite-bulb.groovy @@ -30,6 +30,7 @@ metadata { fingerprint mfr: "0371", prod: "0103", model: "0001", deviceJoinName: "Aeon LED Bulb 6 Multi-White" //US fingerprint mfr: "0371", prod: "0003", model: "0001", deviceJoinName: "Aeon LED Bulb 6 Multi-White" //EU + fingerprint mfr: "0300", prod: "0003", model: "0004", deviceJoinName: "ilumin Tunable White" } simulator { From a4b3576a123524ffd48af6dad93ec8378a1ebcf2 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Thu, 26 Sep 2019 17:08:40 -0500 Subject: [PATCH 2/7] Child devices of hub connected devices not being added to room on join Some hub connected DTHs were not setting the hubId when creating child devices. This was resulting in the child devices not being shown in the join screen in the app which had the side effect that they were not being added to the room the user selected when enabling join. https://smartthings.atlassian.net/browse/ICP-11101 --- .../inovelli-2-channel-smart-plug.groovy | 10 ++++++++-- .../fibaro-double-switch-2-zw5.groovy | 6 ++++-- .../fibaro-wall-plug-us-zw5.groovy | 6 ++++-- .../aeon-key-fob.src/aeon-key-fob.groovy | 11 ++++++++--- .../eaton-5-scene-keypad.groovy | 16 +++++++++------- .../zooz-power-strip.src/zooz-power-strip.groovy | 11 ++++++++--- 6 files changed, 41 insertions(+), 19 deletions(-) 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 index 46dbcd1fa1d..1ee57352507 100644 --- 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 @@ -254,8 +254,14 @@ private channelNumber(String dni) { 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" + addChildDevice("Switch Child Device", + "${device.deviceNetworkId}-ep${i}", + device.hubId, + [completedSetup: true, + label: "${device.displayName} (CH${i})", + isComponent: true, + componentName: "ep$i", + componentLabel: "Channel $i" ]) } } diff --git a/devicetypes/fibargroup/fibaro-double-switch-2-zw5.src/fibaro-double-switch-2-zw5.groovy b/devicetypes/fibargroup/fibaro-double-switch-2-zw5.src/fibaro-double-switch-2-zw5.groovy index 32ca8418770..0a9b7f63346 100644 --- a/devicetypes/fibargroup/fibaro-double-switch-2-zw5.src/fibaro-double-switch-2-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-double-switch-2-zw5.src/fibaro-double-switch-2-zw5.groovy @@ -265,8 +265,10 @@ private createChildDevices() { addChildDevice( "Fibaro Double Switch 2 - USB", "${device.deviceNetworkId}-2", - null, - [completedSetup: true, label: "${device.displayName} (CH2)", isComponent: false] + device.hubId, + [completedSetup: true, + label: "${device.displayName} (CH2)", + isComponent: false] ) } catch (Exception e) { logging("${device.displayName} - error attempting to create child device: "+e, "debug") 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 cfff3472f74..c9f385d29e6 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 @@ -241,8 +241,10 @@ private createChildDevices() { addChildDevice( "Fibaro Wall Plug USB", "${device.deviceNetworkId}-2", - null, - [completedSetup: true, label: "${device.displayName} (CH2)", isComponent: false] + device.hubId, + [completedSetup: true, + label: "${device.displayName} (CH2)", + isComponent: false] ) } diff --git a/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy b/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy index dff6e9b1c14..fbb19db11e6 100644 --- a/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy +++ b/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy @@ -218,9 +218,14 @@ private void createChildDevices() { Integer buttons = (device.currentState("numberOfButtons").value).toBigInteger() for (i in 1..buttons) { - def child = addChildDevice("Child Button", "${device.deviceNetworkId}/${i}", null, - [completedSetup: true, label: "${device.displayName} button ${i}", - isComponent: true, componentName: "button$i", componentLabel: "Button $i"]) + def child = addChildDevice("Child Button", + "${device.deviceNetworkId}/${i}", + device.hubId, + [completedSetup: true, + label: "${device.displayName} button ${i}", + isComponent: true, + componentName: "button$i", + componentLabel: "Button $i"]) child.sendEvent(name: "supportedButtonValues", value: JsonOutput.toJson(["pushed", "held"]), displayed: false) child.sendEvent(name: "button", value: "pushed", data: [buttonNumber: 1], displayed: false) diff --git a/devicetypes/smartthings/eaton-5-scene-keypad.src/eaton-5-scene-keypad.groovy b/devicetypes/smartthings/eaton-5-scene-keypad.src/eaton-5-scene-keypad.groovy index f03d79bce5c..95afcea1a98 100644 --- a/devicetypes/smartthings/eaton-5-scene-keypad.src/eaton-5-scene-keypad.groovy +++ b/devicetypes/smartthings/eaton-5-scene-keypad.src/eaton-5-scene-keypad.groovy @@ -320,13 +320,15 @@ private updateLocalSwitchState(childId, state) { private addChildSwitches() { for (i in 2..5) { String childDni = "${device.deviceNetworkId}/$i" - def child = addChildDevice("Child Switch", childDni, null, [ - completedSetup: true, - label : "$device.displayName Switch $i", - isComponent : true, - componentName : "switch$i", - componentLabel: "Switch $i" - ]) + def child = addChildDevice("Child Switch", + childDni, + device.hubId, + [completedSetup: true, + label : "$device.displayName Switch $i", + isComponent : true, + componentName : "switch$i", + componentLabel: "Switch $i" + ]) child.sendEvent(name: "switch", value: "off") } } diff --git a/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy b/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy index 564793925c6..c424edc93af 100644 --- a/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy +++ b/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy @@ -198,9 +198,14 @@ private void onOffCmd(value, endpoint = null) { private void createChildDevices() { state.oldLabel = device.label for (i in 1..5) { - addChildDevice("Zooz Power Strip Outlet", "${device.deviceNetworkId}-ep${i}", null, - [completedSetup: true, label: "${device.displayName} (CH${i})", - isComponent: true, componentName: "ch$i", componentLabel: "Channel $i"]) + addChildDevice("Zooz Power Strip Outlet", + "${device.deviceNetworkId}-ep${i}", + device.hubId, + [completedSetup: true, + label: "${device.displayName} (CH${i})", + isComponent: true, + componentName: "ch$i", + componentLabel: "Channel $i"]) } } From 98490efd83bac60a378379c6b06eb65949061540 Mon Sep 17 00:00:00 2001 From: ZWozniakS <48519140+ZWozniakS@users.noreply.github.com> Date: Fri, 27 Sep 2019 19:49:57 +0200 Subject: [PATCH 3/7] WWST-5295 Added FP for Inovelli Red Series (#11339) --- .../zwave-metering-switch.src/zwave-metering-switch.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy index 1ee71062f79..2b11cf4fdb2 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -47,6 +47,7 @@ metadata { fingerprint mfr: "0371", prod: "0003", model: "00AF", deviceJoinName: "Aeotec Smart Switch 7", ocfDeviceType: "oic.d.smartplug" //EU fingerprint mfr: "0371", prod: "0103", model: "00AF", deviceJoinName: "Aeotec Smart Switch 7", ocfDeviceType: "oic.d.smartplug" //US fingerprint mfr: "0060", prod: "0004", model: "000B", deviceJoinName: "Everspring Smart Plug", ocfDeviceType: "oic.d.smartplug" //US + fingerprint mfr: "031E", prod: "0002", model: "0001", deviceJoinName: "Inovelli Switch Red Series" //US } // simulator metadata From 83c35be8a2c9f1a65c11f45727c07122b6aa7f61 Mon Sep 17 00:00:00 2001 From: ZouLianghuiS Date: Sat, 28 Sep 2019 20:29:27 +0800 Subject: [PATCH 4/7] [ICP-11165] Zigbee ewelink motion sensor & ewelink contact sensor, fix health check issue (#11343) --- .../Orvibo-Contact-Sensor.groovy | 12 +++++++++--- .../zigbee-motion-detector.groovy | 9 +++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy mode change 100755 => 100644 devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.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 old mode 100755 new mode 100644 index 4d05834ccff..fb906937c1a --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -126,9 +126,15 @@ def refresh() { } def configure() { - sendEvent(name: "checkInterval", value:20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) - def cmds = [] def manufacturer = getDataValue("manufacturer") + + if (manufacturer == "eWeLink") { + sendEvent(name: "checkInterval", value:2 * 60 * 60 + 5 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + } else { + sendEvent(name: "checkInterval", value:20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + } + def cmds = [] + 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 if (manufacturer == "Aurora") { @@ -185,4 +191,4 @@ def getContactResult(value) { value : value, descriptionText: descriptionText ] -} \ No newline at end of file +} diff --git a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy old mode 100755 new mode 100644 index 9c7fe2873d5..360589c7498 --- a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy +++ b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy @@ -159,6 +159,11 @@ def refresh() { def configure() { log.debug "configure" - sendEvent(name: "checkInterval", value:20 * 60 + 2*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + def manufacturer = getDataValue("manufacturer") + if (manufacturer == "eWeLink") { + sendEvent(name: "checkInterval", value:2 * 60 * 60 + 5 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + } else { + sendEvent(name: "checkInterval", value:20 * 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, 1200, 0x10) + refresh() -} \ No newline at end of file +} From 2ee6144ed67478e70541706377366a22c6a36701 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 30 Sep 2019 10:17:49 -0700 Subject: [PATCH 5/7] DVCSMP-3664 Fix NPE in zigbee metering plug (#11345) --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy index 494b8238120..13b017267db 100755 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -85,13 +85,13 @@ def parse(String description) { attrData.each { def map = [:] - if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_HISTORICAL_CONSUMPTION) { + if (it.value && it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_HISTORICAL_CONSUMPTION) { log.debug "power" map.name = "power" map.value = zigbee.convertHexToInt(it.value)/powerDiv map.unit = "W" } - else if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_READING_INFO_SET) { + else if (it.value && it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_READING_INFO_SET) { log.debug "energy" map.name = "energy" map.value = zigbee.convertHexToInt(it.value)/energyDiv From fbaf63a793dcbd50559c8618575f26c5ca5ec5f0 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Tue, 1 Oct 2019 19:40:55 +0200 Subject: [PATCH 6/7] [WWST-5165] Everspring Radiator Thermostat integration (#11285) * New DTH Z-Wave Radiator Thermostat * Tiles changes, added some range and mode checks. * JSON encoding --- .../zwave-radiator-thermostat.groovy | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy new file mode 100644 index 00000000000..614a84a3af9 --- /dev/null +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -0,0 +1,259 @@ +/** + * Copyright 2019 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 Radiator Thermostat", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.thermostat") { + capability "Thermostat Mode" + capability "Refresh" + capability "Battery" + capability "Thermostat Heating Setpoint" + capability "Health Check" + capability "Thermostat" + capability "Temperature Measurement" + + fingerprint mfr: "0060", prod: "0015", model: "0001", deviceJoinName: "Everspring Thermostatic Radiator Valve", mnmn: "SmartThings", vid: "generic-radiator-thermostat" + //this DTH is sending temperature setpoint commands using Celsius scale and assumes that they'll be handled correctly by device + //if new device added to this DTH won't be able to do that, make sure to you'll handle conversion in a right way + } + + tiles(scale: 2) { + multiAttributeTile(name:"thermostat", type:"general", width:6, height:4, canChangeIcon: false) { + tileAttribute("device.thermostatMode", key: "PRIMARY_CONTROL") { + attributeState("off", action:"switchMode", nextState:"...", icon: "st.thermostat.heating-cooling-off") + attributeState("heat", action:"switchMode", nextState:"...", icon: "st.thermostat.heat") + attributeState("emergency heat", action:"switchMode", nextState:"...", icon: "st.thermostat.emergency-heat") + } + tileAttribute("device.temperature", key: "SECONDARY_CONTROL") { + attributeState("temperature", label:'${currentValue}°', icon: "st.alarm.temperature.normal", + backgroundColors:[ + // Celsius + [value: 0, color: "#153591"], + [value: 7, color: "#1e9cbb"], + [value: 15, color: "#90d2a7"], + [value: 23, color: "#44b621"], + [value: 28, color: "#f1d801"], + [value: 35, color: "#d04e00"], + [value: 37, color: "#bc2323"], + // Fahrenheit + [value: 40, color: "#153591"], + [value: 44, color: "#1e9cbb"], + [value: 59, color: "#90d2a7"], + [value: 74, color: "#44b621"], + [value: 84, color: "#f1d801"], + [value: 95, color: "#d04e00"], + [value: 96, color: "#bc2323"] + ] + ) + } + tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") { + attributeState("default", label: '${currentValue}', unit: "°", defaultState: true) + } + } + controlTile("thermostatMode", "device.thermostatMode", "enum", width: 2 , height: 2, supportedStates: "device.supportedThermostatModes") { + state("off", action: "setThermostatMode", label: 'Off', icon: "st.thermostat.heating-cooling-off") + state("heat", action: "setThermostatMode", label: 'Heat', icon: "st.thermostat.heat") + state("emergency heat", action:"setThermostatMode", label: 'Emergency heat', icon: "st.thermostat.emergency-heat") + } + controlTile("heatingSetpoint", "device.heatingSetpoint", "slider", + sliderType: "HEATING", + debouncePeriod: 750, + range: "device.heatingSetpointRange", + width: 2, height: 2) { + state "default", action:"setHeatingSetpoint", label:'${currentValue}', backgroundColor: "#E86D13" + } + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label: 'Battery:\n${currentValue}%', unit: "%" + } + standardTile("refresh", "command.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "refresh", label: 'refresh', action: "refresh.refresh", icon: "st.secondary.refresh-icon" + } + main "thermostat" + details(["thermostat", "thermostatMode", "heatingSetpoint", "battery", "refresh"]) + } +} + +def initialize() { + sendEvent(name: "checkInterval", value: 4 * 60 * 60 + 24 * 60 , displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + sendEvent(name: "supportedThermostatModes", value: thermostatSupportedModes.encodeAsJson(), displayed: false) + sendEvent(name: "heatingSetpointRange", value: [minHeatingSetpointTemperature, maxHeatingSetpointTemperature], displayed: false) + response(refresh()) +} + +def installed() { + initialize() +} + +def updated() { + initialize() +} + +def parse(String description) { + def result = null + def cmd = zwave.parse(description) + if (cmd) { + result = zwaveEvent(cmd) + } else { + log.warn "${device.displayName} - no-parsed event: ${description}" + } + log.debug "Parse returned: ${result}" + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand() + if (encapsulatedCommand) { + log.debug "SecurityMessageEncapsulation into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "unable to extract secure command from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + + + +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + def value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel + def map = [name: "battery", value: value, unit: "%"] + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) { + def map = [name: "thermostatMode", data:[supportedThermostatModes: thermostatSupportedModes.encodeAsJson()]] + switch (cmd.mode) { + case 1: + map.value = "heat" + break + case 11: + map.value = "emergency heat" + break + case 0: + map.value = "off" + break + } + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd) { + def deviceTemperatureScale = cmd.scale ? 'F' : 'C' + createEvent(name: "heatingSetpoint", value: convertTemperatureIfNeeded(cmd.scaledValue, deviceTemperatureScale, cmd.precision), unit: temperatureScale) +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def deviceTemperatureScale = cmd.scale ? 'F' : 'C' + createEvent(name: "temperature", value: convertTemperatureIfNeeded(cmd.scaledSensorValue, deviceTemperatureScale, cmd.precision), unit: temperatureScale) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.warn "Unhandled command: ${cmd}" + [:] +} + +def setThermostatMode(String mode) { + def modeValue = 0 + if (thermostatSupportedModes.contains(mode)) { + switch (mode) { + case "heat": + modeValue = 1 + break + case "emergency heat": + modeValue = 11 + break + case "off": + modeValue = 0 + break + } + } else { + log.debug "Unsupported mode ${mode}" + } + + [ + secure(zwave.thermostatModeV2.thermostatModeSet(mode: modeValue)), + "delay 2000", + secure(zwave.thermostatModeV2.thermostatModeGet()) + ] +} + +def heat() { + setThermostatMode("heat") +} + +def emergencyHeat() { + setThermostatMode("emergency heat") +} + +def off() { + setThermostatMode("off") +} + +def setHeatingSetpoint(setpoint) { + setpoint = temperatureScale == 'C' ? setpoint : fahrenheitToCelsius(setpoint) + setpoint = Math.max(Math.min(setpoint, maxHeatingSetpointTemperature), minHeatingSetpointTemperature) + [ + secure(zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: setpoint, setpointType: 1, size: 2])), + "delay 2000", + secure(zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: 1)) + ] +} + +def refresh() { + def cmds = [ + secure(zwave.batteryV1.batteryGet()), + secure(zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: 1)), + secure(zwave.sensorMultilevelV5.sensorMultilevelGet()), + secure(zwave.thermostatModeV2.thermostatModeGet()) + ] + + delayBetween(cmds, 2500) +} + +def ping() { + refresh() +} + +private secure(cmd) { + if (zwaveInfo.zw.endsWith("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } +} + +private getMaxHeatingSetpointTemperature() { + if (isEverspringRadiatorThermostat()) { + temperatureScale == 'C' ? 35 : 95 + } else { + temperatureScale == 'C' ? 30 : 86 + } +} + +private getMinHeatingSetpointTemperature() { + if (isEverspringRadiatorThermostat()) { + temperatureScale == 'C' ? 15 : 59 + } else { + temperatureScale == 'C' ? 10 : 50 + } +} + +private getThermostatSupportedModes() { + if (isEverspringRadiatorThermostat()) { + ["off", "heat", "emergency heat"] + } else { + ["off","heat"] + } +} + +private isEverspringRadiatorThermostat() { + zwaveInfo.mfr == "0060" && zwaveInfo.prod == "0015" +} From ee0a439022f12242e85894d6acde6a64a1dc68b9 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Thu, 3 Oct 2019 09:12:46 -0500 Subject: [PATCH 7/7] Fix window shade commands not being visible on device view The latest device plugin expects that devices implementing the Window Shade capability have created the supportedWindowShadeCommands event to indicate which commands they support. If that event hasnt' been generated then the plugin doesn't render the device properly. --- devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy | 2 +- .../springs-window-fashions-shade.groovy | 9 +++++++++ .../zigbee-window-shade.src/zigbee-window-shade.groovy | 6 ++++++ .../zwave-window-shade.src/zwave-window-shade.groovy | 9 +++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy b/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy index 3dce3ec841e..f73c65af0c8 100644 --- a/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy +++ b/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy @@ -308,7 +308,7 @@ def configure() { sendEvent(name: "windowShade", value: "unknown") log.debug "Configuring Reporting and Bindings." sendEvent(name: "checkInterval", value: (2 * 60 * 60 + 10 * 60), displayed: true, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) def attrs_refresh = zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) + zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) + diff --git a/devicetypes/smartthings/springs-window-fashions-shade.src/springs-window-fashions-shade.groovy b/devicetypes/smartthings/springs-window-fashions-shade.src/springs-window-fashions-shade.groovy index b576947aa0f..45687fb4ef0 100644 --- a/devicetypes/smartthings/springs-window-fashions-shade.src/springs-window-fashions-shade.groovy +++ b/devicetypes/smartthings/springs-window-fashions-shade.src/springs-window-fashions-shade.groovy @@ -11,6 +11,9 @@ * for the specific language governing permissions and limitations under the License. * */ +import groovy.json.JsonOutput + + metadata { definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { capability "Window Shade" @@ -111,6 +114,7 @@ def getCheckInterval() { def installed() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) response(refresh()) } @@ -242,6 +246,11 @@ def presetPosition() { zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() } +def pause() { + log.debug "pause()" + stop() +} + def stop() { log.debug "stop()" zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() diff --git a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy index b3331d7945b..33014ddca5d 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -11,6 +11,8 @@ * 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. */ + +import groovy.json.JsonOutput import physicalgraph.zigbee.zcl.DataType metadata { @@ -201,6 +203,10 @@ def refresh() { return cmds } +def installed() { + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) +} + def configure() { // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) log.info "configure()" diff --git a/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy b/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy index f8709453105..7015b52076c 100644 --- a/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy +++ b/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy @@ -11,6 +11,9 @@ * for the specific language governing permissions and limitations under the License. * */ +import groovy.json.JsonOutput + + metadata { definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { capability "Window Shade" @@ -106,6 +109,7 @@ def getCheckInterval() { def installed() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) response(refresh()) } @@ -222,6 +226,11 @@ def presetPosition() { setLevel(preset ?: state.preset ?: 50) } +def pause() { + log.debug "pause()" + stop() +} + def stop() { log.debug "stop()" zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format()