From 2780cc051d2d058eb5c7712c077f3dd8435fd50c Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 13 Jul 2020 20:54:26 +0200 Subject: [PATCH 001/422] ICP-13368 Specified metadata for Sinope TH1500ZB (#36733) --- .../th1500zb-sinope-thermostat.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy index bedf7760734..158c3836207 100644 --- a/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1500zb-sinope-thermostat.src/th1500zb-sinope-thermostat.groovy @@ -41,7 +41,7 @@ preferences { command "heatLevelUp" command "heatLevelDown" - fingerprint manufacturer: "Sinope Technologies", model: "TH1500ZB", deviceJoinName: "Sinope Thermostat" //Sinope TH1500ZB Thermostat + fingerprint manufacturer: "Sinope Technologies", model: "TH1500ZB", deviceJoinName: "Sinope Thermostat", mnmn: "SmartThings", vid: "SmartThings-smartthings-TH1300ZB_Sinope_Thermostat" //Sinope TH1500ZB Thermostat } //-------------------------------------------------------------------------------------------------------- From 57361fcec23c755d307dc2c52af0f3053397b3ed Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 13 Jul 2020 21:00:36 +0200 Subject: [PATCH 002/422] [ICP-13365] Added Tamper Alert for Aeotec Door/Window Sensor 7 Pro (#36732) * Added Tamper Alert for Aeotec Door/Window Sensor 7 Pro * Actual tamper reports handler * Added initial tamper event --- .../zwave-door-window-sensor.groovy | 17 +++++++++++++---- 1 file changed, 13 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 e3e533bec83..566decd9772 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 @@ -23,6 +23,7 @@ metadata { capability "Battery" capability "Configuration" capability "Health Check" + capability "Tamper Alert" fingerprint deviceId: "0x2001", inClusters: "0x30,0x80,0x84,0x85,0x86,0x72", deviceJoinName: "Open/Closed Sensor" fingerprint deviceId: "0x07", inClusters: "0x30", deviceJoinName: "Open/Closed Sensor" @@ -58,9 +59,9 @@ metadata { fingerprint mfr: "0371", prod: "0102", model: "00BB", deviceJoinName: "Aeotec Open/Closed Sensor" //US //Aeotec Recessed Door Sensor 7 fingerprint mfr: "0371", prod: "0002", model: "00BB", deviceJoinName: "Aeotec Open/Closed Sensor" //EU //Aeotec Recessed Door Sensor 7 fingerprint mfr: "0109", prod: "2022", model: "2201", deviceJoinName: "Vision Open/Closed Sensor" //AU //Vision Recessed Door Sensor - fingerprint mfr: "0371", prod: "0002", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor" //EU //Aeotec Door/Window Sensor 7 Pro - fingerprint mfr: "0371", prod: "0102", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor" //US //Aeotec Door/Window Sensor 7 Pro - fingerprint mfr: "0371", prod: "0202", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor" //AU //Aeotec Door/Window Sensor 7 Pro + fingerprint mfr: "0371", prod: "0002", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //EU //Aeotec Door/Window Sensor 7 Pro + fingerprint mfr: "0371", prod: "0102", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //US //Aeotec Door/Window Sensor 7 Pro + fingerprint mfr: "0371", prod: "0202", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //AU //Aeotec Door/Window Sensor 7 Pro } // simulator metadata @@ -122,6 +123,7 @@ def installed() { // this is the nuclear option because the device often goes to sleep before we can poll it sendEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open") sendEvent(name: "battery", unit: "%", value: 100) + sendEvent(name: "tamper", value: "clear") response(initialPoll()) } @@ -171,10 +173,13 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm } else if (cmd.notificationType == 0x07) { if (cmd.v1AlarmType == 0x07) { // special case for nonstandard messages from Monoprice door/window sensors result << sensorValueEvent(cmd.v1AlarmLevel) + } else if (cmd.event == 0x00) { + result << createEvent(name: "tamper", value: "clear") } else if (cmd.event == 0x01 || cmd.event == 0x02) { result << sensorValueEvent(1) } else if (cmd.event == 0x03) { - result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true) + runIn(10, clearTamper, [overwrite: true, forceForLocallyExecuting: true]) + result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName was tampered") if (!state.MSR) result << response(command(zwave.manufacturerSpecificV2.manufacturerSpecificGet())) } else if (cmd.event == 0x05 || cmd.event == 0x06) { result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true) @@ -370,3 +375,7 @@ def retypeBasedOnMSR() { private isEnerwave() { zwaveInfo?.mfr?.equals("011A") && zwaveInfo?.prod?.equals("0601") && zwaveInfo?.model?.equals("0901") } + +def clearTamper() { + sendEvent(name: "tamper", value: "clear") +} From f6bbe9340e3a8364b112600ffa06947e8e027596 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Mon, 13 Jul 2020 21:01:42 +0200 Subject: [PATCH 003/422] ICP-11324 - new UI Metadata for Sylvania LIGHTIFY Edge-lit flushmount (#36894) --- .../zigbee-white-color-temperature-bulb.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index de24197e484..6606b52bc79 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -97,7 +97,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Under Cabinet TW", deviceJoinName: "SYLVANIA Light" //SYLVANIA Smart Under Cabinet TW fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B05, FC01", outClusters: "0019", manufacturer: "LEDVANCE", model: "BR30 TW", deviceJoinName: "SYLVANIA Light" //SYLVANIA Smart+ Adustable White BR30 fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B05, FC01", outClusters: "0019", manufacturer: "LEDVANCE", model: "RT TW", deviceJoinName: "SYLVANIA Light" //SYLVANIA Smart+ Adustable White RT5/6 - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Edge-lit flushmount", deviceJoinName: "SYLVANIA Light" //SYLVANIA SMART+ Flush Mount + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Edge-lit flushmount", deviceJoinName: "SYLVANIA Light", mnmn: "SmartThings", vid: "generic-color-temperature-ceiling-light-2700K-6500K" //SYLVANIA SMART+ Flush Mount // Leedarson fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B05, 1000, FEDC", outClusters: "000A, 0019", manufacturer: "Smarthome", model: "S111-202A", deviceJoinName: "Leedarson Light" //Leedarson Tunable White Bulb A19 From 994bdf1938c22283004b1a367bcacdbab84a74d1 Mon Sep 17 00:00:00 2001 From: ADUROSMART ERIA <52692745+adurosmart@users.noreply.github.com> Date: Tue, 14 Jul 2020 03:59:16 +0800 Subject: [PATCH 004/422] Fixed 'parseAduroSmartButtonMessage' function in zigbee-mulit-button.groovy (#36892) Fixed 'parseAduroSmartButtonMessage' function in zigbee-mulit-button.groovy file, the bug that there will be multiple events when pressing a button. other file not change, only want merge zigbee-mulit-button.groovy . Co-authored-by: Andy Yi --- .../zigbee-multi-button.groovy | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy index 6929234bfe6..1c1ece30a0a 100644 --- a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy +++ b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy @@ -279,23 +279,7 @@ private getButtonName() { private Map parseAduroSmartButtonMessage(Map descMap){ def buttonState = "pushed" def buttonNumber = 0 - if (descMap.clusterInt == zigbee.ONOFF_CLUSTER) { - if (descMap.command == "01") { - buttonNumber = 1 - } else if (descMap.command == "00") { - buttonNumber = 4 - } - } else if (descMap.clusterInt == zigbee.LEVEL_CONTROL_CLUSTER) { - if (descMap.command == "02") { - def data = descMap.data - def d0 = data[0] - if (d0 == "00") { - buttonNumber = 2 - } else if (d0 == "01") { - buttonNumber = 3 - } - } - } else if (descMap.clusterInt == ADUROSMART_SPECIFIC_CLUSTER) { + if (descMap.clusterInt == ADUROSMART_SPECIFIC_CLUSTER) { def list2 = descMap.data buttonNumber = (list2[1] as int) + 1 } From d156132406a24ba3a49304190dfae628376cf032 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 13 Jul 2020 13:03:40 -0700 Subject: [PATCH 005/422] ICP-12929 Standardize how Fibaro preference default text is created (#36909) * ICP-12929 Standardize how Fibaro preference default text is created There was an existing pattern to to print the enum value of a preference rather than the raw enum, so I've just adopted it across all the fibaro DTHs that were using the incorrect method. * fixup --- .../fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy | 6 ++++-- .../fibaro-door-window-sensor-2.groovy | 7 ++++--- .../fibaro-double-switch-2-zw5.groovy | 5 +++-- .../fibaro-motion-sensor-zw5.groovy | 4 ++-- .../fibaro-single-switch-2-zw5.groovy | 5 +++-- .../fibaro-wall-plug-eu-zw5.groovy | 5 +++-- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy index 2ec083842e9..2c35cb117c8 100644 --- a/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy @@ -67,7 +67,7 @@ metadata { } preferences { - parameterMap().findAll{(it.num as Integer) != 54}.each { + parameterMap().each { input ( title: "${it.num}. ${it.title}", description: it.descr, @@ -75,10 +75,12 @@ metadata { element: "paragraph" ) + def defVal = it.def as Integer + def descrDefVal = it.options ? it.options.get(defVal) : defVal input ( name: it.key, title: null, - description: "Default: $it.def" , + description: "$descrDefVal", type: it.type, options: it.options, range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, diff --git a/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy b/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy index 7bb49ba3728..d7d0ce0dfee 100644 --- a/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy +++ b/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy @@ -89,18 +89,19 @@ metadata { required: false ) - parameterMap().findAll{(it.num as Integer) != 54}.each { + parameterMap().each { input ( title: "${it.num}. ${it.title}", description: it.descr, type: "paragraph", element: "paragraph" ) - + def defVal = it.def as Integer + def descrDefVal = it.options ? it.options.get(defVal) : defVal input ( name: it.key, title: null, - description: "Default: $it.def" , + description: "$descrDefVal", type: it.type, options: it.options, range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, 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 d5b3550d8e8..01e563ef9b3 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 @@ -50,11 +50,12 @@ metadata { type: "paragraph", element: "paragraph" ) - + def defVal = it.def as Integer + def descrDefVal = it.options ? it.options.get(defVal) : defVal input ( name: it.key, title: null, - description: "Default: $it.def" , + description: "$descrDefVal", type: it.type, options: it.options, range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, 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 5da49a3d6f9..2443d2f86cc 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 @@ -89,7 +89,7 @@ metadata { type: "paragraph", element: "paragraph" ) - parameterMap().findAll { (it.num as Integer) != 54 }.each { + parameterMap().each { input( title: "${it.num}. ${it.title}", description: it.descr, @@ -101,7 +101,7 @@ metadata { input( name: it.key, title: null, - description: "Default: $descrDefVal", + description: "$descrDefVal", type: it.type, options: it.options, range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, diff --git a/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy b/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy index f86701f6afe..6fdbc6262d5 100644 --- a/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy @@ -52,11 +52,12 @@ metadata { type: "paragraph", element: "paragraph" ) - + def defVal = it.def as Integer + def descrDefVal = it.options ? it.options.get(defVal) : defVal input ( name: it.key, title: null, - description: "Default: $it.def" , + description: "$descrDefVal", type: it.type, options: it.options, range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, 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 74896f194e9..2f425ade486 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 @@ -52,11 +52,12 @@ metadata { type: "paragraph", element: "paragraph" ) - + def defVal = it.def as Integer + def descrDefVal = it.options ? it.options.get(defVal) : defVal input ( name: it.key, title: null, - description: "Default: $it.def" , + description: "$descrDefVal", type: it.type, options: it.options, range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, From 3e02ee802fc281d9797fda3a2bed1512675f7234 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Tue, 14 Jul 2020 01:21:29 +0200 Subject: [PATCH 006/422] [WWST-6165] New DTH for Qubino Flush Shutter (#36082) * New DTH for Qubino Flush Shutter * Deleted Switch Level capability, changed refresh() method, reworded preferences descriptions * Removed unnecessary import statement, added child switch multilevel DTH --- .../qubino-flush-shutter.groovy | 488 ++++++++++++++++++ .../child-switch-multilevel.groovy | 39 ++ 2 files changed, 527 insertions(+) create mode 100644 devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy create mode 100644 devicetypes/smartthings/child-switch-multilevel.src/child-switch-multilevel.groovy diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy new file mode 100644 index 00000000000..90916e144e1 --- /dev/null +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -0,0 +1,488 @@ +/** + * Copyright 2020 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: "Qubino Flush Shutter", namespace: "qubino", author: "SmartThings", ocfDeviceType: "oic.d.blind", mcdSync: true) { + capability "Window Shade" + capability "Window Shade Level" + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Health Check" + capability "Configuration" + + //zw:L type:1107 mfr:0159 prod:0003 model:0052 ver:1.01 zwv:4.05 lib:03 cc:5E,86,72,5A,73,20,27,25,26,32,60,85,8E,59,70 ccOut:20,26 epc:2 + fingerprint mfr: "0159", prod: "0003", model: "0052", deviceJoinName: "Qubino Window Treatment" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" + } + } + valueTile("shadeLevel", "device.level", width: 4, height: 1) { + state "shadeLevel", label: 'Shade is ${currentValue}% up', defaultState: true + } + controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { + state "shadeLevel", action:"switch level.setLevel" + } + valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) { + state "default", label:'${currentValue} W' + } + valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) { + state "default", label:'${currentValue} kWh' + } + standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" + } + + main "windowShade" + details(["windowShade", "shadeLevel", "levelSliderControl", "power", "energy", "refresh"]) + } + + preferences { + parameterMap.each { + input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") + + switch (it.type) { + case "enum": + input(name: it.key, title: "Select", type: "enum", options: it.values, defaultValue: it.defaultValue, required: false) + break + case "range": + input(name: it.key, type: "number", title: "Set value (range ${it.range})", defaultValue: it.defaultValue, range: it.range, required: false) + break + } + } + } +} + +def installed() { + state.currentMode = null + state.childDevices = [:] + state.venetianBlindDni = null + state.temperatureSensorDni = null + sendHubCommand(encap(zwave.configurationV2.configurationGet(parameterNumber: 71))) + sendEvent(name: "checkInterval", value: 2 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // Preferences template begin + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + def preferenceName = it.key + "Boolean" + settings."$preferenceName" = true + state.currentPreferencesState."$it.key".status = "synced" + } + // Preferences template end + sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) +} + +def updated() { + // Preferences template begin + parameterMap.each { + if (isPreferenceChanged(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + } else if (!state.currentPreferencesState."$it.key".value) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + syncConfiguration() + // Preferences template end +} + +private syncConfiguration() { + def commands = [] + parameterMap.each { + try { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } else if (state.currentPreferencesState."$it.key".status == "disablePending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + } catch (e) { + log.warn "There's been an issue with preference: ${it.key}" + } + } + sendHubCommand(commands) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + // Preferences template begin + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find( {it.parameterNumber == cmd.parameterNumber} ) + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + if (settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + handleConfigurationChange(cmd) + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } + // Preferences template end + handleConfigurationChange(cmd) +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + switch (preference.type) { + case "range": + return settings."$parameterKey" + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isPreferenceChanged(preference) { + if (settings."$preference.key" != null) { + def value = state.currentPreferencesState."$preference.key" + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } else { + return false + } +} + +def handleConfigurationChange(confgurationReport) { + switch (confgurationReport.parameterNumber) { + case 71: //Operating mode + switch (confgurationReport.scaledConfigurationValue) { + case 0: // Shutter + checkAndTriggerModeChange("windowShade") + break + case 1: // Venetian + checkAndTriggerModeChange("windowShadeVenetian") + break + } + log.info "Current device's mode is: ${state.currentMode}" + break + case 72: + state.timeOfVenetianMovement = confgurationReport.scaledConfigurationValue + break + default: + log.info "Parameter no. ${confgurationReport.parameterNumber} has no specific handler" + break + } +} + +private checkAndTriggerModeChange(reportedMode) { + if (state.currentMode != reportedMode) { + state.currentMode = reportedMode + createVenetianBlindsChildDeviceIfNeeded() + } +} + +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 multilevelChildInstalled(childDni) { + state.timeOfVenetianMovement = 150 + sendHubCommand(encap(zwave.switchMultilevelV3.switchMultilevelGet(), 2)) +} + +def close() { + setShadeLevel(0x64) +} + +def open() { + setShadeLevel(0x00) +} + +def pause() { + sendHubCommand(encap(zwave.switchMultilevelV3.switchMultilevelStopLevelChange())) +} + +def setLevelChild(level, childDni) { + setSlats(level) +} + +def setLevel(level) { + state.blindsLastCommand = currentLevel > level ? "opening" : "closing" + setShadeLevel(level) +} + +def setShadeLevel(level) { + log.debug "Setting shade level: ${level}" + def currentLevel = Integer.parseInt(device.currentState("shadeLevel").value) + state.blindsLastCommand = currentLevel > level ? "opening" : "closing" + state.shadeTarget = level + encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level))) +} + +def setSlats(level) { + def time = (int) (state.timeOfVenetianMovement * 1.1) + sendHubCommand([ + encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 2), + "delay ${time}", + encap(zwave.switchMultilevelV3.switchMultilevelGet(), 2) + ]) +} + +def refresh() { + encap(zwave.switchMultilevelV3.switchMultilevelGet()) +} + +def ping() { + response(refresh()) +} + +def configure() { + def configurationCommands = [] + configurationCommands += encap(zwave.associationV1.associationSet(groupingIdentifier: 7, nodeId: [zwaveHubNodeId])) + configurationCommands += encap(zwave.meterV3.meterGet(scale: 0x00)) + configurationCommands += encap(zwave.meterV3.meterGet(scale: 0x02)) + configurationCommands += encap(zwave.switchMultilevelV3.switchMultilevelGet()) + configurationCommands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: 1, parameterNumber: 40, size: 1)) + + delayBetween(configurationCommands) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand() + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } else { + log.warn "unable to extract secure command from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd, ep = null) { + log.debug "SwitchMultilevelReport ${cmd} from endpoint: ${ep}" + if (cmd.value != 0xFE) { + if (ep != 2) { + shadeEvent(cmd.value) + } else { + def event = [name: "level", value: cmd.value != 0x63 ? cmd.value : 100] + sendEventsToVenetianBlind([event]) + } + } else { + log.warn "Something went wrong with calibration, position of blind is unknown" + if (ep == 2) { + sendEventsToVenetianBlind([[name: "level", value: 0]]) + } else { + [ + createEvent([name: "windowShade", value: "unknown"]), + createEvent([name: "shadeLevel", value: 0]) + ] + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelSet cmd, ep = null) { + sendHubCommand(encap(zwave.meterV3.meterGet(scale: 0x02))) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { + log.debug "BasicReport ${cmd}" + if (cmd.value != 0xFE && ep != 2) { + shadeEvent(cmd.value) + } else { + log.warn "Something went wrong with calibration, position of blind is unknown" + } +} + +private shadeEvent(value) { + def shadeValue + def events = [] + if (!value) { + shadeValue = "open" + } else if (value == 0x63) { + shadeValue = "closed" + } else { + shadeValue = "partially open" + } + events += createEvent([name: "windowShade", value: shadeValue]) + events += createEvent([name: "shadeLevel", value: value != 0x63 ? value : 100]) + + events +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { + def events = [] + if (cmd.meterType == 0x01) { + def eventMap = [:] + if (cmd.scale == 0x00) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + events += createEvent(eventMap) + } else if (cmd.scale == 0x02) { + eventMap.name = "power" + eventMap.value = Math.round(cmd.scaledMeterValue) + eventMap.unit = "W" + events += createEvent(eventMap) + if (cmd.scaledMeterValue) { + events += createEvent([name: "windowShade", value: state.blindsLastCommand]) + events += createEvent([name: "shadeLevel", value: state.shadeTarget, displayed: false]) + } else { + events += response(encap(zwave.switchMultilevelV3.switchMultilevelGet())) + } + } + } + events +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd, ep = null) { + log.debug "SensorMultilevelReport ${cmd}" + (ep ? " from endpoint $ep" : "") + def map = [:] + switch (cmd.sensorType) { + case 1: + map.name = "temperature" + def cmdScale = cmd.scale == 1 ? "F" : "C" + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) + map.unit = getTemperatureScale() + break + default: + map.descriptionText = cmd.toString() + } + def child = childDevices.find { it.deviceNetworkId == state.temperatureSensorDni } + if (!child) { + child = createChildDevice("qubinoTemperatureSensor", "Qubino Temperature Sensor", "Qubino Temperature Sensor", 3, "qubino", false) + state.temperatureSensorDni = child.deviceNetworkId + } + child?.sendEvent(map) + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, ep = null) { + log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private sendEventsToVenetianBlind(events) { + if (state.venetianBlindDni) { + def child = childDevices.find { it.deviceNetworkId == state.venetianBlindDni } + events.each { + child.sendEvent(it) + } + createEvent(descriptionText: "Venetian Blinds level has been updated") + } else { + log.warn "There's no venetian child device to send events to" + } +} + +private createChildDevice(componentName, componentLabel, dthName, childIt, namespace = "smartthings", isComponent = true) { + try { + def childDni = "${device.deviceNetworkId}:$childIt" + def child = addChildDevice(namespace, dthName, childDni, device.getHub().getId(), [ + completedSetup: true, + label : componentLabel, + isComponent : isComponent, + componentName : componentName, + componentLabel: componentLabel + ]) + return child + } catch(Exception e) { + log.debug "Exception: ${e}" + } +} + +private createVenetianBlindsChildDeviceIfNeeded() { + if (state.currentMode.contains("Venetian")) { + state.venetianBlindDni = createChildDevice("venetianBlind", "Venetian Blind", "Child Switch Multilevel", 2).deviceNetworkId + } +} + +private getParameterMap() {[ + [ + name: "Operating modes", key: "operatingModes", type: "enum", + parameterNumber: 71, size: 1, defaultValue: 0, + values: [ + 0: "Shutter mode", + 1: "Venetian mode (up/down and slate rotation)" + ], + description: "Set the device's operating mode." + ], + [ + name: "Slats tilting full turn time", key: "slatsTiltingFullTurnTime", type: "range", + parameterNumber: 72, size: 2, defaultValue: 150, + range: "0..32767", + description: "Specify the time required to rotate the slats 180 degrees. (100 = 1 second)" + ], + [ + name: "Slats position", key: "slatsPosition", type: "enum", + parameterNumber: 73, size: 1, defaultValue: 1, + values: [ + 0: "Slats return to previously set position only in case of Z-wave control (not valid for limit switch positions)", + 1: "Slats return to previously set position in case of Z-wave control, push-button operation or when the lower limit switch is reached" + ], + description: "This parameter defines slats position after up/down movement through Z-wave or push-buttons." + ], + [ + name: "Motor moving up/down time", key: "motorMovingUp/DownTime", type: "range", + parameterNumber: 74, size: 2, defaultValue: 0, + range: "0..32767", + description: "Set the amount of time it takes to completely open or close shutter. Check manual for more detailed guidance." + ], + [ + name: "Motor operation detection", key: "motorOperationDetection", type: "range", + parameterNumber: 76, size: 1, defaultValue: 30, + range: "0..127", + description: "Power usage threshold which will be interpreted as motor reaching the limit switch." + ], + [ + name: "Forced Shutter calibration", key: "forcedShutterCalibration", type: "enum", + parameterNumber: 78, size: 1, defaultValue: 0, + values: [ + 0: "0: Calibration finished or not started", + 1: "1: Start calibration process" + ], + description: "By modifying the parameters setting from 0 to 1 a Shutter enters the calibration mode. When calibration process is finished, completing full cycle - up, down and up, set this parameter value back to 0." + ] +]} \ No newline at end of file diff --git a/devicetypes/smartthings/child-switch-multilevel.src/child-switch-multilevel.groovy b/devicetypes/smartthings/child-switch-multilevel.src/child-switch-multilevel.groovy new file mode 100644 index 00000000000..8aeb35bd32c --- /dev/null +++ b/devicetypes/smartthings/child-switch-multilevel.src/child-switch-multilevel.groovy @@ -0,0 +1,39 @@ +/** + * Copyright 2020 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: "Child Switch Multilevel", namespace: "smartthings", author: "SmartThings") { + capability "Switch Level" + capability "Actuator" + capability "Sensor" + } + + tiles(scale: 2) { + valueTile("level", "device.level", width: 4, height: 1) { + state "level", label: 'Level: ${currentValue}% ', defaultState: true + } + + main "level" + details(["level"]) + } +} + +def installed() { + parent.multilevelChildInstalled(device.deviceNetworkId) +} + +def setLevel(level) { + def currentLevel = Integer.parseInt(device.currentState("level").value) + parent.setLevelChild(level, device.deviceNetworkId, currentLevel) +} From 9d3542e8d49139b05aa37089d3d1fbeb77549f96 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 14 Jul 2020 10:58:18 -0700 Subject: [PATCH 007/422] CHAD-5256 Update child DNIs when the parent rejoins (#36908) * CHAD-5256 Update child DNIs when the parent rejoins --- .../zigbee-multi-switch.groovy | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 472bde68821..9ee9fa06aab 100755 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -78,15 +78,20 @@ metadata { } def installed() { - createChildDevices() - updateDataValue("onOff", "catchall") - refresh() + createChildDevices() + updateDataValue("onOff", "catchall") + refresh() } def updated() { - log.debug "updated()" - updateDataValue("onOff", "catchall") - refresh() + log.debug "updated()" + updateDataValue("onOff", "catchall") + for (child in childDevices) { + if (!child.deviceNetworkId.startsWith(device.deviceNetworkId)) { //parent DNI has changed after rejoin + child.setDeviceNetworkId("${device.deviceNetworkId}:${getChildEndpoint(child.deviceNetworkId)}") + } + } + refresh() } def parse(String description) { @@ -120,7 +125,7 @@ def parse(String description) { private void createChildDevices() { def x = getChildCount() for (i in 2..x) { - addChildDevice("Child Switch Health", "${device.deviceNetworkId}:0${i}", device.hubId, + addChildDevice("Child Switch Health", "${device.deviceNetworkId}:${i}", device.hubId, [completedSetup: true, label: "${device.displayName[0..-2]}${i}", isComponent: false]) } } @@ -162,12 +167,12 @@ def refresh() { if (isOrvibo()) { zigbee.readAttribute(zigbee.ONOFF_CLUSTER, 0x0000, [destEndpoint: 0xFF]) } else { - def cmds = zigbee.onOffRefresh() - def x = getChildCount() - for (i in 2..x) { - cmds += zigbee.readAttribute(zigbee.ONOFF_CLUSTER, 0x0000, [destEndpoint: i]) - } - return cmds + def cmds = zigbee.onOffRefresh() + def x = getChildCount() + for (i in 2..x) { + cmds += zigbee.readAttribute(zigbee.ONOFF_CLUSTER, 0x0000, [destEndpoint: i]) + } + return cmds } } @@ -204,17 +209,17 @@ def configure() { if (isOrvibo()) { //the orvibo switch will send out device anounce message at ervery 2 mins as heart beat,setting 0x0099 to 1 will disable it. def cmds = zigbee.writeAttribute(zigbee.BASIC_CLUSTER, 0x0099, 0x20, 0x01, [mfgCode: 0x0000]) - cmds += refresh() - return cmds + cmds += refresh() + return cmds } else { //other devices supported by this DTH in the future - def cmds = zigbee.onOffConfig(0, 120) - def x = getChildCount() - for (i in 2..x) { - cmds += zigbee.configureReporting(zigbee.ONOFF_CLUSTER, 0x0000, 0x10, 0, 120, null, [destEndpoint: i]) - } - cmds += refresh() - return cmds + def cmds = zigbee.onOffConfig(0, 120) + def x = getChildCount() + for (i in 2..x) { + cmds += zigbee.configureReporting(zigbee.ONOFF_CLUSTER, 0x0000, 0x10, 0, 120, null, [destEndpoint: i]) + } + cmds += refresh() + return cmds } } From a30009e3a612ec26a757df50e06d086559e16033 Mon Sep 17 00:00:00 2001 From: BJanuszS <61963402+BJanuszS@users.noreply.github.com> Date: Wed, 15 Jul 2020 16:32:54 +0200 Subject: [PATCH 008/422] [ICP-8763] Zigbee Metering Plug: Prevent duplicate switch events (#36353) Zigbee Metering Plug: Prevent duplicate switch events --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 9 ++++++--- 1 file changed, 6 insertions(+), 3 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 308273fe479..49c2eeb99cd 100755 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -66,12 +66,16 @@ def getATTRIBUTE_HISTORICAL_CONSUMPTION() { 0x0400 } def parse(String description) { log.debug "description is $description" def event = zigbee.getEvent(description) + def descMap = zigbee.parseDescriptionAsMap(description) def powerDiv = 1 def energyDiv = 100 if (event) { log.info "event enter:$event" - if (event.name== "power") { + if (event.name == "switch" && !descMap.isClusterSpecific && descMap.commandInt == 0x0B) { + log.info "Ignoring default response with desc map: $descMap" + return [:] + } else if (event.name== "power") { event.value = event.value/powerDiv event.unit = "W" } else if (event.name== "energy") { @@ -82,7 +86,6 @@ def parse(String description) { sendEvent(event) } else { List result = [] - def descMap = zigbee.parseDescriptionAsMap(description) log.debug "Desc Map: $descMap" List attrData = [[clusterInt: descMap.clusterInt ,attrInt: descMap.attrInt, value: descMap.value]] @@ -153,4 +156,4 @@ def configure() { zigbee.onOffConfig() + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 1, 600, 1) + zigbee.electricMeasurementPowerConfig(1, 600, 1) -} +} \ No newline at end of file From a0fa62fe10b35aab28993d49fbd1617ebe098c97 Mon Sep 17 00:00:00 2001 From: "SmartThings, Inc" Date: Wed, 15 Jul 2020 14:29:49 -0400 Subject: [PATCH 009/422] =?UTF-8?q?issue=20fix=20:=20ICP-12604:=20Supporte?= =?UTF-8?q?d=20temperature=20values=20=E2=80=8B=E2=80=8Bfor=20the=20follow?= =?UTF-8?q?ing=20options.=20(#35327)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @shrakuma ICP-12604: Supported temperature values ​​for the following options. - Ambient limit - 5-36 degrees in Celsius, or 41-96 degrees in Fahrenheit. - Floor high limit - 5-36 degrees in Celsius, or 41-96 degrees in Fahrenheit. - Floor low limit - 5-36 degrees in Celsius, or 41-96 degrees in Fahrenheit. In the ST application, we can enter from 5-96 (from 5 degrees Celsius to 96 degrees Fahrenheit) → this is confusing. Sinope thermostats support Celsius and Fahrenheit unit systems - this option can be changed manually on the device. Co-authored-by: Vincent G. Beauregrad --- .../th1300zb-sinope-thermostat.groovy | 40 +++++++++++-------- .../th1400zb-sinope-thermostat.groovy | 30 +++++++------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy index 17e229d8187..948359d6807 100644 --- a/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1300zb-sinope-thermostat.src/th1300zb-sinope-thermostat.groovy @@ -1,6 +1,6 @@ /** Copyright Sinopé Technologies -1.3.0 +1.3.1 SVN-571 * 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. @@ -21,12 +21,12 @@ metadata { input("FloorSensorTypeParam", "enum", title:"Probe type (Default: 10k)", description: "Choose floor sensors probe. The floor sensor provided with the thermostats are 10K.", options: ["10k", "12k"], multiple: false, required: false) - input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit (5C to 36C / 41F to 96)", range: "5..96", - description: "The maximum ambient temperature limit when in floor control mode.", required: false) - input("FloorLimitMinParam", "number", title:"Floor low limit (5C to 34C / 41F to 93F)", range: "5..93", - description: "The minimum temperature limit of the floor when in ambient control mode.", required: false) - input("FloorLimitMaxParam", "number", title:"Floor high limit (7C to 36C / 45F to 96F)", range: "7..96", - description: "The maximum temperature limit of the floor when in ambient control mode.", required: false) + input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit in Celsius (5C to 36C)", range: "5..36", + description: "The maximum ambient temperature limit in Celsius when in floor control mode.", required: false) + input("FloorLimitMinParam", "number", title:"Floor low limit in Celsius (5C to 34C)", range: "5..34", + description: "The minimum temperature limit in Celsius of the floor when in ambient control mode.", required: false) + input("FloorLimitMaxParam", "number", title:"Floor high limit in Celsius (7C to 36C)", range: "7..36", + description: "The maximum temperature limit of the floor in Celsius when in ambient control mode.", required: false) // input("AuxLoadParam", "number", title:"Auxiliary load value (Default: 0)", range:("0..65535"), // description: "Enter the power in watts of the heating element connected to the auxiliary output.", required: false) input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") @@ -155,6 +155,14 @@ def updated() { runEvery15Minutes(refresh_misc) + if(KbdLockParam == "Lock" || KbdLockParam == '0'){ + traceEvent(settings.logFilter,"device lock",settings.trace) + cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x01) + } + else{ + traceEvent(settings.logFilter,"device unlock",settings.trace) + cmds += zigbee.writeAttribute(0x0204, 0x0001, 0x30, 0x00) + } if(AirFloorModeParam == "Floor" || AirFloorModeParam == '1'){//Floor mode traceEvent(settings.logFilter,"Set to Floor mode",settings.trace) cmds += zigbee.writeAttribute(0xFF01, 0x0105, 0x30, 0x0002) @@ -196,15 +204,15 @@ def updated() { if(FloorMaxAirTemperatureParam){ def MaxAirTemperatureValue traceEvent(settings.logFilter,"FloorMaxAirTemperature param. scale: ${state?.scale}, Param value: ${FloorMaxAirTemperatureParam}",settings.trace) - if(state?.scale == 'F') + if(FloorMaxAirTemperatureParam >= 41) { - MaxAirTemperatureValue = fahrenheitToCelsius(FloorMaxAirTemperatureParam).toInteger() + MaxAirTemperatureValue = checkTemperature(FloorMaxAirTemperatureParam)//check if the temperature is between the maximum and minimum + MaxAirTemperatureValue = fahrenheitToCelsius(MaxAirTemperatureValue).toInteger() } else//state?.scale == 'C' { MaxAirTemperatureValue = FloorMaxAirTemperatureParam.toInteger() } - MaxAirTemperatureValue = checkTemperature(MaxAirTemperatureValue)//check if the temperature is between the maximum and minimum MaxAirTemperatureValue = MaxAirTemperatureValue * 100 cmds += zigbee.writeAttribute(0xFF01, 0x0108, 0x29, MaxAirTemperatureValue) } @@ -216,15 +224,15 @@ def updated() { if(FloorLimitMinParam){ def FloorLimitMinValue traceEvent(settings.logFilter,"FloorLimitMin param. scale: ${state?.scale}, Param value: ${FloorLimitMinParam}",settings.trace) - if(state?.scale == 'F') + if(FloorLimitMinParam >= 41) { - FloorLimitMinValue = fahrenheitToCelsius(FloorLimitMinParam).toInteger() + FloorLimitMinValue = checkTemperature(FloorLimitMinParam)//check if the temperature is between the maximum and minimum + FloorLimitMinValue = fahrenheitToCelsius(FloorLimitMinValue).toInteger() } else//state?.scale == 'C' { FloorLimitMinValue = FloorLimitMinParam.toInteger() } - FloorLimitMinValue = checkTemperature(FloorLimitMinValue)//check if the temperature is between the maximum and minimum FloorLimitMinValue = FloorLimitMinValue * 100 cmds += zigbee.writeAttribute(0xFF01, 0x0109, 0x29, FloorLimitMinValue) } @@ -236,15 +244,15 @@ def updated() { if(FloorLimitMaxParam){ def FloorLimitMaxValue traceEvent(settings.logFilter,"FloorLimitMax param. scale: ${state?.scale}, Param value: ${FloorLimitMaxParam}",settings.trace) - if(state?.scale == 'F') + if(FloorLimitMaxParam >= 45) { - FloorLimitMaxValue = fahrenheitToCelsius(FloorLimitMaxParam).toInteger() + FloorLimitMaxValue = checkTemperature(FloorLimitMaxParam)//check if the temperature is between the maximum and minimum + FloorLimitMaxValue = fahrenheitToCelsius(FloorLimitMaxValue).toInteger() } else//state?.scale == 'C' { FloorLimitMaxValue = FloorLimitMaxParam.toInteger() } - FloorLimitMaxValue = checkTemperature(FloorLimitMaxValue)//check if the temperature is between the maximum and minimum FloorLimitMaxValue = FloorLimitMaxValue * 100 cmds += zigbee.writeAttribute(0xFF01, 0x010A, 0x29, FloorLimitMaxValue) } diff --git a/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy b/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy index f3d244beea1..bb8aeb3fddb 100644 --- a/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy +++ b/devicetypes/sinope-technologies/th1400zb-sinope-thermostat.src/th1400zb-sinope-thermostat.groovy @@ -26,12 +26,12 @@ metadata { // input("PumpProtectionParam", "enum", titile: "Pump Protection (Default: Off)", options: ["On", "Off"], required: false // description: "Activate the main output 1 minute every 24 hours to ensure the hydronics system pump does not seize.") - input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit (5C to 36C / 41F to 96)", range: "5..96", - description: "The maximum ambient temperature limit \nwhen in floor control mode.", required: false) - input("FloorLimitMinParam", "number", title:"Floor low limit (5C to 34C / 41F to 93F)", range: "5..93", - description: "The minimum temperature limit of the floor when in ambient control mode.", required: false) - input("FloorLimitMaxParam", "number", title:"Floor high limit (7C to 36C / 45F to 96F)", range: "7..96", - description: "The maximum temperature limit of the floor when in ambient control mode.", required: false) + input("FloorMaxAirTemperatureParam", "number", title:"Ambient limit in Celsius (5C to 36C)", range: "5..36", + description: "The maximum ambient temperature limit in Celsius when in floor control mode.", required: false) + input("FloorLimitMinParam", "number", title:"Floor low limit in Celsius (5C to 34C)", range: "5..34", + description: "The minimum temperature limit in Celsius of the floor when in ambient control mode.", required: false) + input("FloorLimitMaxParam", "number", title:"Floor high limit in Celsius (7C to 36C)", range: "7..36", + description: "The maximum temperature limit of the floor in Celsius when in ambient control mode.", required: false) // input("AuxLoadParam", "number", title:"Auxiliary load value (Default: 0)", range:("0..65535"), // description: "Enter the power in watts of the heating element connected to the auxiliary output.", required: false) input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") @@ -246,15 +246,15 @@ def updated() { if(FloorMaxAirTemperatureParam){ def MaxAirTemperatureValue traceEvent(settings.logFilter,"FloorMaxAirTemperature param. scale: ${state?.scale}, Param value: ${FloorMaxAirTemperatureParam}",settings.trace) - if(state?.scale == 'F') + if(FloorMaxAirTemperatureParam >= 41) { - MaxAirTemperatureValue = fahrenheitToCelsius(FloorMaxAirTemperatureParam).toInteger() + MaxAirTemperatureValue = checkTemperature(FloorMaxAirTemperatureParam)//check if the temperature is between the maximum and minimum + MaxAirTemperatureValue = fahrenheitToCelsius(MaxAirTemperatureValue).toInteger() } else//state?.scale == 'C' { MaxAirTemperatureValue = FloorMaxAirTemperatureParam.toInteger() } - MaxAirTemperatureValue = checkTemperature(MaxAirTemperatureValue)//check if the temperature is between the maximum and minimum MaxAirTemperatureValue = MaxAirTemperatureValue * 100 cmds += zigbee.writeAttribute(0xFF01, 0x0108, 0x29, MaxAirTemperatureValue) } @@ -266,15 +266,15 @@ def updated() { if(FloorLimitMinParam){ def FloorLimitMinValue traceEvent(settings.logFilter,"FloorLimitMin param. scale: ${state?.scale}, Param value: ${FloorLimitMinParam}",settings.trace) - if(state?.scale == 'F') + if(FloorLimitMinParam >= 41) { - FloorLimitMinValue = fahrenheitToCelsius(FloorLimitMinParam).toInteger() + FloorLimitMinValue = checkTemperature(FloorLimitMinParam)//check if the temperature is between the maximum and minimum + FloorLimitMinValue = fahrenheitToCelsius(FloorLimitMinValue).toInteger() } else//state?.scale == 'C' { FloorLimitMinValue = FloorLimitMinParam.toInteger() } - FloorLimitMinValue = checkTemperature(FloorLimitMinValue)//check if the temperature is between the maximum and minimum FloorLimitMinValue = FloorLimitMinValue * 100 cmds += zigbee.writeAttribute(0xFF01, 0x0109, 0x29, FloorLimitMinValue) } @@ -286,15 +286,15 @@ def updated() { if(FloorLimitMaxParam){ def FloorLimitMaxValue traceEvent(settings.logFilter,"FloorLimitMax param. scale: ${state?.scale}, Param value: ${FloorLimitMaxParam}",settings.trace) - if(state?.scale == 'F') + if(FloorLimitMaxParam >= 45) { - FloorLimitMaxValue = fahrenheitToCelsius(FloorLimitMaxParam).toInteger() + FloorLimitMaxValue = checkTemperature(FloorLimitMaxParam)//check if the temperature is between the maximum and minimum + FloorLimitMaxValue = fahrenheitToCelsius(FloorLimitMaxValue).toInteger() } else//state?.scale == 'C' { FloorLimitMaxValue = FloorLimitMaxParam.toInteger() } - FloorLimitMaxValue = checkTemperature(FloorLimitMaxValue)//check if the temperature is between the maximum and minimum FloorLimitMaxValue = FloorLimitMaxValue * 100 cmds += zigbee.writeAttribute(0xFF01, 0x010A, 0x29, FloorLimitMaxValue) } From a99281d4cbbd0a85aaf9f7db3442ed4e3a9be34a Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 16 Jul 2020 20:43:07 +0200 Subject: [PATCH 010/422] Fingerprint for Ajax Online RGBCCT (#37167) --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index c5314b7120d..4e6e539a8e8 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -92,6 +92,9 @@ metadata { // Q Smart Lights fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B05, 1000, FEDC", outClusters: "000A, 0019", manufacturer: "Neuhaus Lighting Group", model: "ZBT-ExtendedColor", deviceJoinName: "Q-Smart Light", mnmn:"SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" + + // Ajax Online + fingerprint manufacturer: "Ajaxonline", model: "AJ-RGBCCT 5 in 1", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" } // UI tile definitions From 99d1312464d783bb924836fe815c00561f8811c2 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Mon, 20 Jul 2020 18:29:16 +0800 Subject: [PATCH 011/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Multi Switch (#37787) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Multi Switch * Update zigbee-multi-switch.groovy Updated comments with "Raw Description" * Update zigbee-multi-switch.groovy Fix of spacing Co-authored-by: 啦啦 王 Co-authored-by: Konrad K <33450498+KKlimczukS@users.noreply.github.com> --- .../zigbee-multi-switch.groovy | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy old mode 100755 new mode 100644 index 9ee9fa06aab..872962f5fdd --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -48,6 +48,14 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S240R-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S240R-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S340-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S340-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S340R-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S340R-ZB + + // eWeLink + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "eWeLink", model: "ZB-SW02", deviceJoinName: "eWeLink Switch" //eWeLink 2 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "eWeLink", model: "ZB-SW03", deviceJoinName: "eWeLink Switch" //eWeLink 3 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "eWeLink", model: "ZB-SW04", deviceJoinName: "eWeLink Switch" //eWeLink 4 Gang Switch 1 } // simulator metadata simulator { @@ -232,9 +240,9 @@ private getChildCount() { return 3 } else if (device.getDataValue("model") == "E220-KR2N0Z0-HA") { return 2 - } else if (device.getDataValue("model") == "E220-KR3N0Z0-HA") { + } else if (device.getDataValue("model") == "E220-KR3N0Z0-HA" || device.getDataValue("model") == "ZB-SW03") { return 3 - } else if (device.getDataValue("model") == "E220-KR4N0Z0-HA") { + } else if (device.getDataValue("model") == "E220-KR4N0Z0-HA" || device.getDataValue("model") == "ZB-SW04") { return 4 } else if (device.getDataValue("model") == "E220-KR5N0Z0-HA") { return 5 From 9452fe2155f64b3c1e1fdbd5b2d95a6c34f294be Mon Sep 17 00:00:00 2001 From: "SmartThings, Inc" Date: Mon, 20 Jul 2020 18:11:37 -0400 Subject: [PATCH 012/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Switch (#35158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 啦啦 王 From fc5a7a9e3c25da5f626d0ab1f28fc7a6f493285e Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Tue, 21 Jul 2020 10:22:56 -0700 Subject: [PATCH 013/422] ICP-13140 For IKEA blinds return the current state of the blinds when pause is called if the blinds are not moving (#38018) * ICP-13140 For IKEA blinds return the current state of the blinds when pause is called if the blinds are not moving * Always send pause command over Zigbee * Fix "display" -> "displayed" in sendEvent --- .../zigbee-window-shade-battery.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index 7967fe26990..797ddd4f432 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -189,6 +189,10 @@ def setLevel(data, rate = null) { def pause() { log.info "pause()" + // If the window shade isn't moving when we receive a pause() command then just echo back the current state for the mobile client. + if (device.currentValue("windowShade") != "opening" && device.currentValue("windowShade") != "closing") { + sendEvent(name: "windowShade", value: device.currentValue("windowShade"), isStateChange: true, displayed: false) + } zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) } From be3a7e39bd9c2d1795dc322587ba7de723238fcf Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 21 Jul 2020 10:44:23 -0700 Subject: [PATCH 014/422] =?UTF-8?q?CHAD-5172=20Send=20follow-ups=20to=20ve?= =?UTF-8?q?rify=20that=20window=20shade=20level=20changes=20o=E2=80=A6=20(?= =?UTF-8?q?#36279)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CHAD-5172 Send follow-ups to verify that window shade level changes occurred We noticed what seems to be a device bug when window shades send us unsolicited battery reports at the same time they are told to open/close. This seems to cause them to send us immediate position updates rather than waiting until the action is completed. This change should send periodic gets until we see the value we want, up to 5 times, or about 25s after the intial command. * fixup * logic changes * guarantee overwrite * state variables do not work with += operator * add a +/- 2 range for the level * spacing fix and re-arrange logic --- .../springs-window-fashions-shade.groovy | 38 ++++++++++++---- .../zwave-window-shade.groovy | 43 +++++++++++++------ 2 files changed, 59 insertions(+), 22 deletions(-) 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 f60918cd15f..7af556576ba 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 @@ -164,6 +164,7 @@ private handleLevelReport(physicalgraph.zwave.Command cmd) { shadeValue = "partially open" descriptionText = "${device.displayName} shade is ${level}% open" } + checkLevelReport(level) def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) @@ -181,15 +182,6 @@ def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelS response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } -def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { - def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) - updateDataValue("MSR", msr) - if (cmd.manufacturerName) { - updateDataValue("manufacturer", cmd.manufacturerName) - } - createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) -} - def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { def map = [ name: "battery", unit: "%" ] if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { @@ -222,6 +214,7 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def open() { log.debug "open()" def level = switchDirection ? 0 : 99 + levelChangeFollowUp(level) zwave.basicV1.basicSet(value: level).format() // zwave.basicV1.basicSet(value: 0xFF).format() } @@ -229,6 +222,7 @@ def open() { def close() { log.debug "close()" def level = switchDirection ? 99 : 0 + levelChangeFollowUp(level) zwave.basicV1.basicSet(value: level).format() //zwave.basicV1.basicSet(value: 0).format() } @@ -239,6 +233,7 @@ def setLevel(value, duration = null) { level = switchDirection ? 99-level : level if (level < 0) level = 0 if (level > 99) level = 99 + levelChangeFollowUp(level) zwave.basicV1.basicSet(value: level).format() } @@ -266,4 +261,29 @@ def refresh() { zwave.switchMultilevelV1.switchMultilevelGet().format(), zwave.batteryV1.batteryGet().format() ], 1500) +} + +def levelChangeFollowUp(expectedLevel) { + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) +} + +def checkLevelReport(value) { + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } +} + +def checkLevel() { + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } } \ No newline at end of file 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 044695d0492..c43694d37e9 100644 --- a/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy +++ b/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy @@ -149,6 +149,7 @@ private handleLevelReport(physicalgraph.zwave.Command cmd) { shadeValue = "partially open" descriptionText = "${device.displayName} shade is ${level}% open" } + checkLevelReport(level) def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) @@ -166,15 +167,6 @@ def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelS response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } -def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { - def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) - updateDataValue("MSR", msr) - if (cmd.manufacturerName) { - updateDataValue("manufacturer", cmd.manufacturerName) - } - createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) -} - def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { def map = [ name: "battery", unit: "%" ] if (cmd.batteryLevel == 0xFF) { @@ -194,6 +186,7 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { } def open() { + levelChangeFollowUp(99) log.debug "open()" /*delayBetween([ zwave.basicV1.basicSet(value: 0xFF).format(), @@ -203,6 +196,7 @@ def open() { } def close() { + levelChangeFollowUp(0) log.debug "close()" /*delayBetween([ zwave.basicV1.basicSet(value: 0x00).format(), @@ -216,10 +210,8 @@ def setLevel(value, duration = null) { Integer level = value as Integer if (level < 0) level = 0 if (level > 99) level = 99 - delayBetween([ - zwave.basicV1.basicSet(value: level).format(), - zwave.switchMultilevelV1.switchMultilevelGet().format() - ]) + levelChangeFollowUp(level) + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { @@ -247,3 +239,28 @@ def refresh() { zwave.batteryV1.batteryGet().format() ], 1500) } + +def levelChangeFollowUp(expectedLevel) { + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) +} + +def checkLevelReport(value) { + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } +} + +def checkLevel() { + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } +} From e6c5e4242ad776b0ff1e9a66cba9afc3fd965b9f Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 21 Jul 2020 11:45:33 -0700 Subject: [PATCH 015/422] CHAD-5280 Cap number of battery queries (#37869) * CHAD-5280 Cap number of battery queries Locks will query for battery state after a battery replace event, but in some cases, the lock will go offline before responding resulting in an infinite loop of querying the battery, which can clog the z-wave message queue. * state variables do not work with += operator * this should break any existing devices out of the loop * fixup --- .../zwave-lock-without-codes.groovy | 5 ++++- devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) 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 index 1494fabd1f6..963bfb03712 100644 --- 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 @@ -408,6 +408,7 @@ private def handleBatteryAlarmReport(cmd) { case 0x01: //power has been applied, check if the battery level updated log.debug "Batteries replaced. Queueing a battery get." runIn(10, "queryBattery", [overwrite: true, forceForLocallyExecuting: true]) + state.batteryQueries = 0 result << response(secure(zwave.batteryV1.batteryGet())) break; case 0x0A: @@ -693,9 +694,11 @@ private Boolean secondsPast(timestamp, seconds) { private queryBattery() { log.debug "Running queryBattery" - if (!state.lastbatt || now() - state.lastbatt > 10*1000) { + if (state.batteryQueries == null) state.batteryQueries = 0 + if ((!state.lastbatt || now() - state.lastbatt > 10*1000) && state.batteryQueries < 5) { log.debug "It's been more than 10s since battery was updated after a replacement. Querying battery." runIn(10, "queryBattery", [overwrite: true, forceForLocallyExecuting: true]) + state.batteryQueries = state.batteryQueries + 1 sendHubCommand(secure(zwave.batteryV1.batteryGet())) } } diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index bdeb41f5ee0..3a17e4763cb 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -635,6 +635,7 @@ private def handleBatteryAlarmReport(cmd) { case 0x01: //power has been applied, check if the battery level updated log.debug "Batteries replaced. Queueing a battery get." runIn(10, "queryBattery", [overwrite: true, forceForLocallyExecuting: true]) + state.batteryQueries = 0 result << response(secure(zwave.batteryV1.batteryGet())) break; case 0x0A: @@ -774,6 +775,7 @@ private def handleAlarmReportUsingAlarmType(cmd) { map = [ descriptionText: "Batteries replaced", isStateChange: true ] log.debug "Batteries replaced. Queueing a battery check." runIn(10, "queryBattery", [overwrite: true, forceForLocallyExecuting: true]) + state.batteryQueries = 0 result << response(secure(zwave.batteryV1.batteryGet())) break case 131: // Disabled user entered at keypad @@ -1809,9 +1811,11 @@ def readCodeSlotId(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) { private queryBattery() { log.debug "Running queryBattery" - if (!state.lastbatt || now() - state.lastbatt > 10*1000) { + if (state.batteryQueries == null) state.batteryQueries = 0 + if ((!state.lastbatt || now() - state.lastbatt > 10*1000) && state.batteryQueries < 5) { log.debug "It's been more than 10s since battery was updated after a replacement. Querying battery." runIn(10, "queryBattery", [overwrite: true, forceForLocallyExecuting: true]) + state.batteryQueries = state.batteryQueries + 1 sendHubCommand(secure(zwave.batteryV1.batteryGet())) } } From 567e0915624e19aed2983adbf164b366e6b833c9 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Wed, 22 Jul 2020 19:53:59 +0200 Subject: [PATCH 016/422] [WWST-2446_2447] Add Qubino Relay 1/1D to existing DTH (#37124) * Add Qubino Relay 1/1D to existing DTH --- .../qubino-flush-2-relay.groovy | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 431dadc8a5e..2dbeb85dd8b 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -24,6 +24,8 @@ metadata { command "reset" fingerprint mfr: "0159", prod: "0002", model: "0051", deviceJoinName: "Qubino Switch 1" //Qubino Flush 2 Relay + fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Switch" //Qubino Flush 1 Relay + fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Switch" //Qubino Flush 1D Relay } tiles(scale: 2) { @@ -70,7 +72,12 @@ metadata { } def installed() { - state.numberOfSwitches = 2 + if (zwaveInfo?.model.equals("0051")) { + state.numberOfSwitches = 2 + } else { + state.numberOfSwitches = 1 + } + if (!childDevices) { addChildSwitches(state.numberOfSwitches) } @@ -93,7 +100,7 @@ def updated() { } // Preferences template begin parameterMap.each { - if (isPreferenceChanged(it)) { + if (isPreferenceChanged(it) && !excludeParameterFromSync(it)) { log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" state.currentPreferencesState."$it.key".status = "syncPending" } else if (!state.currentPreferencesState."$it.key".value) { @@ -104,6 +111,20 @@ def updated() { // Preferences template end } +def excludeParameterFromSync(preference){ + def exclude = false + if (preference.key == "outputQ2SwitchSelection") { + if (zwaveInfo?.model?.equals("0052") || zwaveInfo?.model?.equals("0053")) { + exclude = true + } + } + + if (exclude) { + log.warn "Preference no ${preference.parameterNumber} - ${preference.key} is not supported by this device" + } + return exclude +} + private syncConfiguration() { def commands = [] parameterMap.each { @@ -446,6 +467,6 @@ private getParameterMap() {[ 0: "When system is turned off the output is 0V (NC).", 1: "When system is turned off the output is 230V (NO).", ], - description: "Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " + description: "(Only for Qubino Flush 2 Relay) Set value means the type of the device that is connected to the Q2 output. The device type can be normally open (NO) or normally close (NC). " ] ]} \ No newline at end of file From 12f6b55a830a418aff6c5ac99e6e5571cc11cd64 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 22 Jul 2020 19:59:46 +0200 Subject: [PATCH 017/422] [ICP-13246] Stelpro Thermostats preferences changes (#32881) * Changes to Stelpro preferences * fixup! Changes to Stelpro preferences * Copied 'secondsPast' method from another DTH. * Switched back to old logic, with new preference description and events time inverval * Forcing to show every changig current state event * Revert "Forcing to show every changig current state event" This reverts commit 7077e0e2e36baf25fd1ed4d403881b88b3847192. * Minor fixes * Comment change --- .../stelpro-ki-thermostat.groovy | 36 ++++++++++++++++--- .../stelpro-ki-zigbee-thermostat.groovy | 11 +++--- .../stelpro-maestro-thermostat.groovy | 11 +++--- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/devicetypes/stelpro/stelpro-ki-thermostat.src/stelpro-ki-thermostat.groovy b/devicetypes/stelpro/stelpro-ki-thermostat.src/stelpro-ki-thermostat.groovy index 21d0355b679..5d5ad2e97c3 100644 --- a/devicetypes/stelpro/stelpro-ki-thermostat.src/stelpro-ki-thermostat.groovy +++ b/devicetypes/stelpro/stelpro-ki-thermostat.src/stelpro-ki-thermostat.groovy @@ -50,11 +50,12 @@ metadata { preferences { section { - input("heatdetails", "enum", title: "Do you want a detailed operating state notification?", options: ["No", "Yes"], defaultValue: "No", required: true, displayDuringSetup: true) + input("heatdetails", "enum", title: "Do you want to see detailed operating state events in the activity history? There may be many.", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: true) } section { - input title: "Outdoor Temperature", description: "To get the current outdoor temperature to display on your thermostat enter your zip code or postal code below and make sure that your SmartThings location has a Geolocation configured (typically used for geofencing).", displayDuringSetup: false, type: "paragraph", element: "paragraph" - input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "[Do not use space](Blank = No Forecast)") + input(title: "Outdoor Temperature", description: "To get the current outdoor temperature to display on your thermostat enter your zip code or postal code below and make sure that your SmartThings location has a Geolocation configured (typically used for geofencing). Do not use space. If you don't want a forecast, leave it blank.", + displayDuringSetup: false, type: "paragraph", element: "paragraph") + input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "") } } @@ -290,7 +291,7 @@ def zwaveEvent(thermostatsetpointv2.ThermostatSetpointReport cmd) { def map = [:] if (cmd.scaledValue >= 327 || - cmd.setpointType != thermostatsetpointv2.ThermostatSetpointReport.SETPOINT_TYPE_HEATING_1) { + cmd.setpointType != thermostatsetpointv2.ThermostatSetpointReport.SETPOINT_TYPE_HEATING_1) { return [:] } temp = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision) @@ -366,7 +367,10 @@ def zwaveEvent(thermostatoperatingstatev1.ThermostatOperatingStateReport cmd) { map.name = "thermostatOperatingState" map.value = operatingState - if (settings.heatdetails == "No") { + // If the user does not want to see the Idle and Heating events in the event history, + // don't show them. Otherwise, don't show them more frequently than 5 minutes. + if (settings.heatdetails == "No" || + !secondsPast(device.currentState("thermostatOperatingState")?.getLastUpdated(), 60 * 5)) { map.displayed = false } } else { @@ -556,3 +560,25 @@ def fanCirculate() { def setThermostatFanMode() { log.trace "${device.displayName} does not support fan mode" } + +/** + * 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) +} diff --git a/devicetypes/stelpro/stelpro-ki-zigbee-thermostat.src/stelpro-ki-zigbee-thermostat.groovy b/devicetypes/stelpro/stelpro-ki-zigbee-thermostat.src/stelpro-ki-zigbee-thermostat.groovy index 997addd618b..7be4b28616a 100644 --- a/devicetypes/stelpro/stelpro-ki-zigbee-thermostat.src/stelpro-ki-zigbee-thermostat.groovy +++ b/devicetypes/stelpro/stelpro-ki-zigbee-thermostat.src/stelpro-ki-zigbee-thermostat.groovy @@ -53,11 +53,12 @@ metadata { preferences { section { input("lock", "enum", title: "Do you want to lock your thermostat's physical keypad?", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: false) - input("heatdetails", "enum", title: "Do you want a detailed operating state notification?", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: true) + input("heatdetails", "enum", title: "Do you want to see detailed operating state events in the activity history? There may be many.", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: true) } section { - input title: "Outdoor Temperature", description: "To get the current outdoor temperature to display on your thermostat enter your zip code or postal code below and make sure that your SmartThings location has a Geolocation configured (typically used for geofencing).", displayDuringSetup: false, type: "paragraph", element: "paragraph" - input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "[Do not use space](Blank = No Forecast)") + input(title: "Outdoor Temperature", description: "To get the current outdoor temperature to display on your thermostat enter your zip code or postal code below and make sure that your SmartThings location has a Geolocation configured (typically used for geofencing). Do not use space. If you don't want a forecast, leave it blank.", + displayDuringSetup: false, type: "paragraph", element: "paragraph") + input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "") } } @@ -308,9 +309,9 @@ def parse(String description) { } // If the user does not want to see the Idle and Heating events in the event history, - // don't show them. Otherwise, don't show them more frequently than 30 seconds. + // don't show them. Otherwise, don't show them more frequently than 5 minutes. if (settings.heatdetails == "No" || - !secondsPast(device.currentState("thermostatOperatingState")?.getLastUpdated(), 30)) { + !secondsPast(device.currentState("thermostatOperatingState")?.getLastUpdated(), 60 * 5)) { map.displayed = false } map = validateOperatingStateBugfix(map) diff --git a/devicetypes/stelpro/stelpro-maestro-thermostat.src/stelpro-maestro-thermostat.groovy b/devicetypes/stelpro/stelpro-maestro-thermostat.src/stelpro-maestro-thermostat.groovy index 0a316f4de85..59f1aae7773 100644 --- a/devicetypes/stelpro/stelpro-maestro-thermostat.src/stelpro-maestro-thermostat.groovy +++ b/devicetypes/stelpro/stelpro-maestro-thermostat.src/stelpro-maestro-thermostat.groovy @@ -54,11 +54,12 @@ metadata { preferences { section { input("lock", "enum", title: "Do you want to lock your thermostat's physical keypad?", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: false) - input("heatdetails", "enum", title: "Do you want a detailed operating state notification?", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: true) + input("heatdetails", "enum", title: "Do you want to see detailed operating state events in the activity history? There may be many.", options: ["No", "Yes"], defaultValue: "No", required: false, displayDuringSetup: true) } section { - input title: "Outdoor Temperature", description: "To get the current outdoor temperature to display on your thermostat enter your zip code or postal code below and make sure that your SmartThings location has a Geolocation configured (typically used for geofencing).", displayDuringSetup: false, type: "paragraph", element: "paragraph" - input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "[Do not use space](Blank = No Forecast)") + input(title: "Outdoor Temperature", description: "To get the current outdoor temperature to display on your thermostat enter your zip code or postal code below and make sure that your SmartThings location has a Geolocation configured (typically used for geofencing). Do not use space. If you don't want a forecast, leave it blank.", + displayDuringSetup: false, type: "paragraph", element: "paragraph") + input("zipcode", "text", title: "ZipCode (Outdoor Temperature)", description: "") } /* input("away_setpoint", "enum", title: "Away setpoint", options: ["5", "5.5", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "10.5", "11", "11.5", "12", "12.5", "13", "13.5", "14", "14.5", "15", "5.5", "15.5", "16", "16.5", "17", "17.5", "18", "18.5", "19", "19.5", "20", "20.5", "21", "21.5", "22", "22.5", "23", "24", "24.5", "25", "25.5", "26", "26.5", "27", "27.5", "28", "28.5", "29", "29.5", "30"], defaultValue: "21", required: true) @@ -279,9 +280,9 @@ def parse(String description) { } // If the user does not want to see the Idle and Heating events in the event history, - // don't show them. Otherwise, don't show them more frequently than 30 seconds. + // don't show them. Otherwise, don't show them more frequently than 5 minutes. if (settings.heatdetails == "No" || - !secondsPast(device.currentState("thermostatOperatingState")?.getLastUpdated(), 30)) { + !secondsPast(device.currentState("thermostatOperatingState")?.getLastUpdated(), 60 * 5)) { map.displayed = false } } From e6a30056e8e6b12c17e36f1d57df9bdd37d2030d Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Wed, 22 Jul 2020 21:34:25 +0200 Subject: [PATCH 018/422] [ICP-13322, ICP-13326, ICP-13336, ICP-13345, ICP-13346, ICP-13355] - fixes for Qubino Dimmers (#37228) * fixes: ICP-13322, ICP-13326, ICP-13355 * fixes2: ICP-13322, ICP-13326, ICP-13355 * fixes3: ICP-13322, ICP-13326, ICP-13355 * fix for ICP-13345 - add meterGet commands * Fix for adding elements to commands collection * ICP-13336 - Removed option 3, because it does not apply to Qubino Temperature Sensor ZMNHEA1. --- .../qubino-dimmer.src/qubino-dimmer.groovy | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index f364229a214..8cea8b62461 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -122,7 +122,6 @@ def installed() { state.currentPreferencesState."$it.key".value = getPreferenceValue(it) state.currentPreferencesState."$it.key".status = "synced" } - readConfigurationFromTheDevice() // Preferences template end } @@ -167,13 +166,13 @@ def excludeParameterFromSync(preference){ return exclude } -private readConfigurationFromTheDevice() { +private getReadConfigurationFromTheDeviceCommands() { def commands = [] parameterMap.each { state.currentPreferencesState."$it.key".status = "reverseSyncPending" commands += zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber) } - sendHubCommand(encapCommands(commands)) + commands } private syncConfiguration() { @@ -232,7 +231,15 @@ def configure() { commands << zwave.associationV1.associationSet(groupingIdentifier:5, nodeId:[zwaveHubNodeId]) commands << zwave.associationV1.associationSet(groupingIdentifier:6, nodeId:[zwaveHubNodeId]) commands << zwave.multiChannelV3.multiChannelEndPointGet() - commands + getRefreshCommands() + commands += getRefreshCommands() + + // 1% is default Minimum dimming value for dimmers, + // when device is set to 1% - it turns off and device does not send any level reports + // Minimum dimming value has to be set to 2%, so the device's internal range would be 2-100% + // Still, for users it will relatively be 1-100% on the UI and device will report it. + // Parameter no. 60 – Minimum dimming value + commands << zwave.configurationV2.configurationSet(scaledConfigurationValue: 2, parameterNumber: 60, size: 1) + commands += getReadConfigurationFromTheDeviceCommands() encapCommands(commands) } @@ -334,7 +341,7 @@ def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, ep = null) { if(input1SwitchType == INPUT_TYPE_POTENTIOMETER) { log.debug "BasicSet: ${cmd} / INPUT_TYPE_POTENTfIOMETER" - response(zwave.switchMultilevelV3.switchMultilevelGet()) + sendHubCommand(encap(zwave.switchMultilevelV3.switchMultilevelGet())) } else if (input1SwitchType == INPUT_TYPE_BI_STABLE_SWITCH) { log.debug "BasicSet: ${cmd} / INPUT_TYPE_BI_STABLE_SWITCH" dimmerEvents(cmd) @@ -482,6 +489,11 @@ def setLevel(value, duration = null) { commands << zwave.switchMultilevelV3.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration) commands << zwave.switchMultilevelV3.switchMultilevelGet() + if(supportsPowerMeter()){ + commands << zwave.meterV2.meterGet(scale: 0) + commands << zwave.meterV2.meterGet(scale: 2) + } + encapCommands(commands, getStatusDelay) } @@ -570,11 +582,9 @@ private getParameterMap() {[ values: [ 0: "Default value - Mono-stable switch type (push button) – button quick press turns between previous set dimmer value and zero)", 1: "Bi-stable switch type (on/off toggle switch)", - 2: "Potentiometer (applies to Flush Dimmer 0-10V only, dimmer is using set value the last received from potentiometer or from z-wave controller)", - 3: "0-10V Temperature sensor (regulated output, applies to Flush Dimmer 0-10V only)" + 2: "Potentiometer (applies to Flush Dimmer 0-10V only, dimmer is using set value the last received from potentiometer or from z-wave controller)" ], - description: "Set input based on device type (switch, potentiometer, temperature sensor,..)." + - "After parameter change to value 3 first exclude module (without setting parameters to default value) then wait at least 30s and then re include the module! " + description: "Set input based on device type (mono-stable switch, bi-stable switch, potentiometer)." ], [ name: "Input 2 switch type (applies to Qubino Flush Dimmer only)", key: "inputsSwitchTypes", type: "enum", From 455802177ec9ba071aa65824353fa527a3fb2aa3 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 23 Jul 2020 00:36:05 +0200 Subject: [PATCH 019/422] Added fingerprint for Ajax Online Filament Bulb (#38132) --- .../zigbee-white-color-temperature-bulb.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 6606b52bc79..fe9fc667764 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -117,6 +117,9 @@ metadata { // Third Reality fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RSL011Z", deviceJoinName: "RealityLight Light" //RealityLight fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RSL012Z", deviceJoinName: "RealityLight Light" //RealityLight + + // Ajax Online + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", manufacturer: "Ajax Online", model: "CCT", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-color-temperature-bulb-2200K-6500K" // Ajax Online Filament Bulb } // UI tile definitions From 0b40a1a2ce04eea42052663ce4f45e8c631bcd81 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Thu, 23 Jul 2020 09:54:07 -0700 Subject: [PATCH 020/422] CHAD-5256 Hotfix to update child DNIs of zigbee-multi-switch (#38146) * CHAD-5256 change re-addressing code There was more code using the leading 0 of child devices than I first noticed, so the leading zero in the child DNI has simply been re-added. * adds a fix for devices that were migrated while this bug was active. * add code to avoid a bug encountered if installed and updated are called in succession --- .../zigbee-multi-switch.groovy | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 9ee9fa06aab..3435eda3e68 100755 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -87,8 +87,9 @@ def updated() { log.debug "updated()" updateDataValue("onOff", "catchall") for (child in childDevices) { - if (!child.deviceNetworkId.startsWith(device.deviceNetworkId)) { //parent DNI has changed after rejoin - child.setDeviceNetworkId("${device.deviceNetworkId}:${getChildEndpoint(child.deviceNetworkId)}") + if (!child.deviceNetworkId.startsWith(device.deviceNetworkId) || //parent DNI has changed after rejoin + !child.deviceNetworkId.split(':')[-1].startsWith('0')) { + child.setDeviceNetworkId("${device.deviceNetworkId}:0${getChildEndpoint(child.deviceNetworkId)}") } } refresh() @@ -123,10 +124,12 @@ def parse(String description) { } private void createChildDevices() { - def x = getChildCount() - for (i in 2..x) { - addChildDevice("Child Switch Health", "${device.deviceNetworkId}:${i}", device.hubId, - [completedSetup: true, label: "${device.displayName[0..-2]}${i}", isComponent: false]) + if (!childDevices) { + def x = getChildCount() + for (i in 2..x) { + addChildDevice("Child Switch Health", "${device.deviceNetworkId}:0${i}", device.hubId, + [completedSetup: true, label: "${device.displayName[0..-2]}${i}", isComponent: false]) + } } } From de4ddded1ca31ac503a4e61e1699c4746bee274d Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 23 Jul 2020 23:24:52 +0200 Subject: [PATCH 021/422] ICP-13392 Added fingerprints of device with updated firmware alongside one configuration command (#38227) --- .../zwave-basic-window-shade.groovy | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy b/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy index b9e91c4e904..1f8ced29483 100644 --- a/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy +++ b/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy @@ -27,6 +27,8 @@ metadata { fingerprint mfr:"0086", prod:"0003", model:"008D", deviceJoinName: "Aeotec Window Treatment" //Aeotec Nano Shutter fingerprint mfr:"0086", prod:"0103", model:"008D", deviceJoinName: "Aeotec Window Treatment" //Aeotec Nano Shutter + fingerprint mfr:"0371", prod:"0003", model:"008D", deviceJoinName: "Aeotec Window Treatment" //Aeotec Nano Shutter + fingerprint mfr:"0371", prod:"0103", model:"008D", deviceJoinName: "Aeotec Window Treatment" //Aeotec Nano Shutter } tiles(scale: 2) { @@ -149,7 +151,10 @@ def updated() { def configure() { log.debug "Configure..." - response(secure(zwave.configurationV1.configurationSet(parameterNumber: 80, size: 1, scaledConfigurationValue: 1))) + response([ + secure(zwave.configurationV1.configurationSet(parameterNumber: 80, size: 1, scaledConfigurationValue: 1)), + secure(zwave.configurationV1.configurationSet(parameterNumber: 85, size: 1, scaledConfigurationValue: 1)) + ]) } private secure(cmd) { From 6be0c351ffea4e735d139a1310b8645cd6c37ed9 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 24 Jul 2020 15:44:20 +0200 Subject: [PATCH 022/422] 'displayed' flag set to false for configuration event (#21920) --- .../zwave-basic-window-shade.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy b/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy index 1f8ced29483..eb670ddf29b 100644 --- a/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy +++ b/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy @@ -139,7 +139,7 @@ def ping() { def installed() { log.debug "Installed ${device.displayName}" sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - sendEvent(name: "availableCurtainPowerButtons", value: JsonOutput.toJson(["open", "close", "pause"])) + sendEvent(name: "availableCurtainPowerButtons", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) state.shadeState = "paused" state.reverseDirection = reverseDirection ? reverseDirection : false } From 0cd26a3261f456157abb082e3183d430c1c59176 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 24 Jul 2020 20:44:21 +0200 Subject: [PATCH 023/422] [WWST-6159] Qubino Flush Shutter DC - fingerprint and improvements (#38295) * Added fingerprint for Qubino Flush Shutter DC * Opening/closing report also for manual actions --- .../qubino-flush-shutter.groovy | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy index 90916e144e1..22cd56f40f1 100644 --- a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -24,7 +24,9 @@ metadata { capability "Configuration" //zw:L type:1107 mfr:0159 prod:0003 model:0052 ver:1.01 zwv:4.05 lib:03 cc:5E,86,72,5A,73,20,27,25,26,32,60,85,8E,59,70 ccOut:20,26 epc:2 - fingerprint mfr: "0159", prod: "0003", model: "0052", deviceJoinName: "Qubino Window Treatment" + fingerprint mfr: "0159", prod: "0003", model: "0052", deviceJoinName: "Qubino Window Treatment" // Qubino Flush Shutter (110-230 VAC) + //zw:L type:1107 mfr:0159 prod:0003 model:0053 ver:1.01 zwv:4.05 lib:03 cc:5E,86,72,5A,73,20,27,25,26,32,85,8E,59,70 ccOut:20,26 + fingerprint mfr: "0159", prod: "0003", model: "0053", deviceJoinName: "Qubino Window Treatment" // Qubino Flush Shutter DC } tiles(scale: 2) { @@ -227,7 +229,12 @@ def open() { } def pause() { - sendHubCommand(encap(zwave.switchMultilevelV3.switchMultilevelStopLevelChange())) + def currentShadeState = device.currentState("windowShade").value + if (currentShadeState == "opening" || currentShadeState == "closing") { + encap(zwave.switchMultilevelV3.switchMultilevelStopLevelChange()) + } else { + encap(zwave.switchMultilevelV3.switchMultilevelGet()) + } } def setLevelChild(level, childDni) { @@ -235,15 +242,11 @@ def setLevelChild(level, childDni) { } def setLevel(level) { - state.blindsLastCommand = currentLevel > level ? "opening" : "closing" setShadeLevel(level) } def setShadeLevel(level) { log.debug "Setting shade level: ${level}" - def currentLevel = Integer.parseInt(device.currentState("shadeLevel").value) - state.blindsLastCommand = currentLevel > level ? "opening" : "closing" - state.shadeTarget = level encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level))) } @@ -257,7 +260,10 @@ def setSlats(level) { } def refresh() { - encap(zwave.switchMultilevelV3.switchMultilevelGet()) + [ + encap(zwave.switchMultilevelV3.switchMultilevelGet()), + encap(zwave.meterV3.meterGet(scale: 0x00)), + ] } def ping() { @@ -313,6 +319,9 @@ def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelR } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelSet cmd, ep = null) { + def currentLevel = Integer.parseInt(device.currentState("shadeLevel").value) + state.blindsLastCommand = currentLevel > cmd.value ? "opening" : "closing" + state.shadeTarget = cmd.value sendHubCommand(encap(zwave.meterV3.meterGet(scale: 0x02))) } @@ -355,7 +364,7 @@ def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) eventMap.value = Math.round(cmd.scaledMeterValue) eventMap.unit = "W" events += createEvent(eventMap) - if (cmd.scaledMeterValue) { + if (Math.round(cmd.scaledMeterValue)) { events += createEvent([name: "windowShade", value: state.blindsLastCommand]) events += createEvent([name: "shadeLevel", value: state.shadeTarget, displayed: false]) } else { From 07e3bb3aff32a3765ec0e59ad4a5d18a74420a74 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 24 Jul 2020 20:51:24 +0200 Subject: [PATCH 024/422] [ICP-13423] Qubino Flush 2 Relay - Added initial poll (#38316) --- .../qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 2dbeb85dd8b..9a1d7e87193 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -92,6 +92,9 @@ def installed() { state.currentPreferencesState."$it.key".status = "synced" } // Preferences template end + response([ + refresh((1..state.numberOfSwitches).toList()) + ]) } def updated() { From 7cfa6466863a276559ea8929a43e33490d476599 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 24 Jul 2020 20:54:21 +0200 Subject: [PATCH 025/422] WWST-6946 Somfy Glydea Ultra fingerprint (#38386) --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 c2ac0d29c7d..98f1a878f1b 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -33,6 +33,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.8", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "KG0001", deviceJoinName: "Window Treatment" //Smart Curtain Motor(BCM300D) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "DY0010", deviceJoinName: "Window Treatment" //Smart Curtain Motor(DT82TV) + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Somfy", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra } preferences { @@ -193,7 +194,7 @@ def pause() { } def presetPosition() { - setLevel(preset ?: 50) + setLevel(preset ?: 50) } /** @@ -271,7 +272,7 @@ private List readDeviceBindingTable() { } def shouldInvertLiftPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() + return isIkeaKadrilj() || isIkeaFyrtur() || isSomfyGlydea() } def reportsBatteryPercentage() { @@ -285,3 +286,7 @@ def isIkeaKadrilj() { def isIkeaFyrtur() { device.getDataValue("model") == "FYRTUR block-out roller blind" } + +def isSomfyGlydea() { + device.getDataValue("model") == "Glydea Somfy" +} \ No newline at end of file From 40889471d5583b425b4514e30ffa49549bbf8cf3 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 27 Jul 2020 16:06:07 -0700 Subject: [PATCH 026/422] DevWS add fingerprint for Ecolink DWZB1-ECO door/window sensor (#38312) --- .../smartsense-open-closed-sensor.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index c4d67acc681..bc297558972 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -33,6 +33,7 @@ metadata { fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "Contact Sensor-A", deviceJoinName: "SYLVANIA Open/Closed Sensor" //Sylvania SMART+ Contact and Temperature Smart Sensor fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Visonic", model: "MCT-340 E", deviceJoinName: "Visonic Open/Closed Sensor" //Visonic Door/Window Sensor fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "Ecolink", model: "4655BC0-R", deviceJoinName: "Ecolink Open/Closed Sensor", mnmn: "SmartThings", vid: "ecolink-door-sensor" //Ecolink Door/Window Sensor + fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "Ecolink", model: "DWZB1-ECO", deviceJoinName: "Ecolink Open/Closed Sensor", mnmn: "SmartThings", vid: "ecolink-door-sensor" //Ecolink Door/Window Sensor fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05,FC01,FC02", outClusters: "0003,0019", manufacturer: "iMagic by GreatStar", model: "1116-S", deviceJoinName: "Iris Open/Closed Sensor" //Iris Contact Sensor fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Bosch", model: "RFMS-ZBMS", deviceJoinName: "Bosch Open/Closed Sensor" //Bosch multi-sensor fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Megaman", model: "MS601/z1", deviceJoinName: "INGENIUM Open/Closed Sensor" //INGENIUM ZB Magnetic ON/OFF Sensor From 54f88e5c1deeaac960d9c234fd4f1e03d818da05 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 28 Jul 2020 13:20:08 +0200 Subject: [PATCH 027/422] ICP-12883,ICP-12849, ICP-12851 - Add mnmn and VID to: Dome DMMS1 and NEO Coolcam Motion Light Sensor fingerprints (#30195) * ICP-12883 - added mnmn and VID to Dome Motion/Light Sensor DMMS1 fingerprint * ICP-12883 - Device specific VID in Dome Motion/Light Sensor DMMS1 fingerprint * ICP-12849, ICP-12851 - Add mnmn and a device specific VID to NEO Coolcam Motion/Light Sensor fingerprints --- .../zwave-motion-light-sensor.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 5ad9cfc9084..e17f6f69651 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,11 +26,11 @@ metadata { capability "Configuration" //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 Sensor" //Dome Motion/Light Sensor + fingerprint mfr: "021F", prod: "0003", model: "0083", deviceJoinName: "Dome Motion/Light Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-Dome_Motion_Light_Sensor_DMMS1" //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: "NEO Coolcam Motion Sensor" //NEO Coolcam Motion/Light Sensor + fingerprint mfr: "0258", prod: "0003", model: "008D", deviceJoinName: "NEO Coolcam Motion/Light Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-NEO_Coolcam_Motion_Light_Sensor" //zw:S type:0701 mfr:0258 prod:0003 model:108D 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 EU version - fingerprint mfr: "0258", prod: "0003", model: "108D", deviceJoinName: "NEO Coolcam Motion Sensor" //NEO Coolcam Motion/Light Sensor + fingerprint mfr: "0258", prod: "0003", model: "108D", deviceJoinName: "NEO Coolcam Motion/Light Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-NEO_Coolcam_Motion_Light_Sensor" fingerprint mfr: "017F", prod: "0101", model: "0001", deviceJoinName: "Wink Motion Sensor" } From ed676f0e3df0970a2573904746d23c6a7a1b50eb Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 28 Jul 2020 18:53:25 +0200 Subject: [PATCH 028/422] ICP-10928 - Zigbee RGBW Bulb: Fix color temperature value on initial join, code refactoring. (#30421) Set the color temperature to 5000K on initial join. 5000K was chosen because it is one of the values that is doesn't change when converted to mireds and back again and because it is in the supported range of all the devices that have fingerprints in this DTH. The code was also refactored to make more use of the Zigbee library. --- .../zigbee-rgbw-bulb.groovy | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 4e6e539a8e8..87e0ffab340 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -135,7 +135,6 @@ private getHUE_COMMAND() { 0x00 } private getSATURATION_COMMAND() { 0x03 } private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 } private getCOLOR_CONTROL_CLUSTER() { 0x0300 } -private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 } // Parse incoming device messages to generate events def parse(String description) { @@ -203,12 +202,9 @@ def ping() { def refresh() { zigbee.onOffRefresh() + - zigbee.levelRefresh() + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + - zigbee.onOffConfig(0, 300) + - zigbee.levelConfig() + zigbee.levelRefresh() + + zigbee.colorTemperatureRefresh() + + zigbee.hueSaturationRefresh() } def configure() { @@ -217,17 +213,24 @@ def configure() { // enrolls with default periodic reporting until newer 5 min interval is confirmed sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity - refresh() + + def cmds = [] + if(device.currentState("colorTemperature")?.value == null) { + cmds += zigbee.setColorTemperature(5000) + } + + cmds += refresh() + + // OnOff, level minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity + zigbee.onOffConfig(0, 300) + + zigbee.levelConfig(0, 300) + cmds } def setColorTemperature(value) { value = value as Integer - def tempInMired = Math.round(1000000 / value) - def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4)) - zigbee.command(COLOR_CONTROL_CLUSTER, 0x0A, "$finalHex 0000") + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.setColorTemperature(value) + + zigbee.colorTemperatureRefresh() } //Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature @@ -262,20 +265,19 @@ private getScaledSaturation(value) { def setColor(value){ log.trace "setColor($value)" zigbee.on() + - zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND, - getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND, + getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") + + zigbee.hueSaturationRefresh() } def setHue(value) { zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) } def setSaturation(value) { zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") + - zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) } def installed() { From 25ef9e1c1878fe8eabad18229072b32dd7646cc9 Mon Sep 17 00:00:00 2001 From: "Bartlomiej Janusz/Home IoT Development (IoT) /SRPOL/Associate/Samsung Electronics" Date: Wed, 29 Jul 2020 13:50:30 +0200 Subject: [PATCH 029/422] Handled values for energy and power events --- .../zigbee-metering-plug.groovy | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 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 49c2eeb99cd..a680e7c4900 100755 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -67,8 +67,6 @@ def parse(String description) { log.debug "description is $description" def event = zigbee.getEvent(description) def descMap = zigbee.parseDescriptionAsMap(description) - def powerDiv = 1 - def energyDiv = 100 if (event) { log.info "event enter:$event" @@ -76,10 +74,10 @@ def parse(String description) { log.info "Ignoring default response with desc map: $descMap" return [:] } else if (event.name== "power") { - event.value = event.value/powerDiv + event.value = event.value/getPowerDiv() event.unit = "W" } else if (event.name== "energy") { - event.value = event.value/energyDiv + event.value = event.value/getEnergyDiv() event.unit = "kWh" } log.info "event outer:$event" @@ -98,13 +96,13 @@ def parse(String description) { 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.value = zigbee.convertHexToInt(it.value)/getPowerDiv() map.unit = "W" } 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 + map.value = zigbee.convertHexToInt(it.value)/getEnergyDiv() map.unit = "kWh" } @@ -155,5 +153,18 @@ def configure() { return refresh() + zigbee.onOffConfig() + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 1, 600, 1) + - zigbee.electricMeasurementPowerConfig(1, 600, 1) + zigbee.electricMeasurementPowerConfig(1, 600, 1) + + zigbee.simpleMeteringPowerConfig() +} + +private int getPowerDiv() { + isSengledOutlet() ? 10 : 1 +} + +private int getEnergyDiv() { + isSengledOutlet() ? 10000 : 100 +} + +private boolean isSengledOutlet() { + device.getDataValue("model") == "E1C-NB7" } \ No newline at end of file From 054e5e12852274dc8d2fcf6f5d4c7bd5c09e7813 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 30 Jul 2020 20:12:22 +0200 Subject: [PATCH 030/422] WWST-6286 Added fingerprint and some tweaks for Aeotec Radiator Thermostat (#38798) --- .../zwave-radiator-thermostat.groovy | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index a4c79ca74ab..7e4ee20830b 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -27,6 +27,7 @@ metadata { //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 fingerprint mfr: "0002", prod: "0115", model: "A010", deviceJoinName: "POPP Thermostat", mnmn: "SmartThings", vid: "generic-radiator-thermostat-2" //POPP Radiator Thermostat Valve + fingerprint mfr: "0371", prod: "0002", model: "0015", deviceJoinName: "Aeotec Thermostat", mnmn: "SmartThings", vid: "aeotec-radiator-thermostat" //Aeotec Radiator Thermostat ZWA021 } tiles(scale: 2) { @@ -168,6 +169,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeRepor map.value = "heat" break case 11: + case 15: map.value = "emergency heat" break case 0: @@ -213,7 +215,11 @@ def setThermostatMode(String mode) { modeValue = 1 break case "emergency heat": - modeValue = 11 + if (isAeotecRadiatorThermostat()) { + modeValue = 15 + } else { + modeValue = 11 + } break case "off": modeValue = 0 @@ -295,7 +301,7 @@ def multiEncap(cmds) { private getMaxHeatingSetpointTemperature() { if (isEverspringRadiatorThermostat()) { temperatureScale == 'C' ? 35 : 95 - } else if (isPoppRadiatorThermostat()) { + } else if (isPoppRadiatorThermostat() || isAeotecRadiatorThermostat()) { temperatureScale == 'C' ? 28 : 82 } else { temperatureScale == 'C' ? 30 : 86 @@ -307,13 +313,15 @@ private getMinHeatingSetpointTemperature() { temperatureScale == 'C' ? 15 : 59 } else if (isPoppRadiatorThermostat()) { temperatureScale == 'C' ? 4 : 39 + } else if (isAeotecRadiatorThermostat()) { + temperatureScale == 'C' ? 8 : 47 } else { temperatureScale == 'C' ? 10 : 50 } } private getThermostatSupportedModes() { - if (isEverspringRadiatorThermostat()) { + if (isEverspringRadiatorThermostat() || isAeotecRadiatorThermostat()) { ["off", "heat", "emergency heat"] } else if (isPoppRadiatorThermostat()) { //that's just for looking fine in Classic ["heat"] @@ -336,4 +344,8 @@ private isEverspringRadiatorThermostat() { private isPoppRadiatorThermostat() { zwaveInfo.mfr == "0002" && zwaveInfo.prod == "0115" +} + +private isAeotecRadiatorThermostat() { + zwaveInfo.mfr == "0371" && zwaveInfo.prod == "0002" } \ No newline at end of file From ec0d7564892c3d85365447e28079dc427a04730b Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Thu, 30 Jul 2020 12:15:44 -0700 Subject: [PATCH 031/422] CHAD-5336 Make sure that temperature offset calculations for Zigbee devices are correct and rounded to one decimal (#38946) --- .../plaidsystems/spruce-sensor.src/spruce-sensor.groovy | 4 +--- .../ecolink-zigbee-water-freeze-sensor.groovy | 2 +- .../ezex-temp-humidity-sensor.groovy | 2 +- .../leaksmart-water-sensor.src/leaksmart-water-sensor.groovy | 2 +- .../smartsense-button.src/smartsense-button.groovy | 2 +- .../smartsense-garage-door-multi.groovy | 4 +--- .../smartsense-garage-door-sensor-button.groovy | 4 +--- .../smartsense-moisture-sensor.groovy | 2 +- .../smartsense-motion-sensor.groovy | 2 +- .../smartsense-multi-sensor.groovy | 2 +- .../smartthings/smartsense-multi.src/smartsense-multi.groovy | 4 +--- .../smartsense-open-closed-sensor.groovy | 2 +- .../smartsense-temp-humidity-sensor.groovy | 2 +- .../smartsense-virtual-open-closed.groovy | 4 +--- .../tyco-door-window-sensor.groovy | 4 +--- .../zigbee-motion-temp-humidity-sensor.groovy | 4 ++-- .../zigbee-sound-sensor.src/zigbee-sound-sensor.groovy | 4 ++-- 17 files changed, 19 insertions(+), 31 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy index a44f7ebb848..1e37af84c0f 100644 --- a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy +++ b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy @@ -258,9 +258,7 @@ private Map getTemperatureResult(value) { def linkText = getLinkText(device) if (tempOffset) { - def offset = tempOffset as int - def v = value as int - value = v + offset + value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } def descriptionText = "${linkText} is ${value}°${temperatureScale}" return [ diff --git a/devicetypes/smartthings/ecolink-zigbee-water-freeze-sensor.src/ecolink-zigbee-water-freeze-sensor.groovy b/devicetypes/smartthings/ecolink-zigbee-water-freeze-sensor.src/ecolink-zigbee-water-freeze-sensor.groovy index bf9f733b314..0c07ee5bfa4 100644 --- a/devicetypes/smartthings/ecolink-zigbee-water-freeze-sensor.src/ecolink-zigbee-water-freeze-sensor.groovy +++ b/devicetypes/smartthings/ecolink-zigbee-water-freeze-sensor.src/ecolink-zigbee-water-freeze-sensor.groovy @@ -90,7 +90,7 @@ def parse(String description) { } else if (map.name == "temperature") { freezeStatus(map.value) if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? "${device.displayName} was ${map.value}°C" : "${device.displayName} was ${map.value}°F" map.translatable = true diff --git a/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy b/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy index f7cf2aafc35..1b7fb7f29fa 100755 --- a/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy @@ -76,7 +76,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F' map.translatable = true diff --git a/devicetypes/smartthings/leaksmart-water-sensor.src/leaksmart-water-sensor.groovy b/devicetypes/smartthings/leaksmart-water-sensor.src/leaksmart-water-sensor.groovy index 29ae7e842ae..4d43052aa41 100644 --- a/devicetypes/smartthings/leaksmart-water-sensor.src/leaksmart-water-sensor.groovy +++ b/devicetypes/smartthings/leaksmart-water-sensor.src/leaksmart-water-sensor.groovy @@ -74,7 +74,7 @@ def parse(String description) { map = parseAttrMessage(description) } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? "${device.displayName} was ${map.value}°C" : "${device.displayName} was ${map.value}°F" map.translatable = true diff --git a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy index 35b509130a8..e4cfdb55d1f 100755 --- a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy +++ b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy @@ -140,7 +140,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) map.unit = getTemperatureScale() } map.descriptionText = getTemperatureScale() == 'C' ? "${ device.displayName } was ${ map.value }°C" : "${ device.displayName } was ${ map.value }°F" diff --git a/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy b/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy index 4268aeeeb6d..04ce5baff1c 100644 --- a/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy +++ b/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy @@ -266,9 +266,7 @@ private getTempResult(part, description) { def temperatureScale = getTemperatureScale() def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) if (tempOffset) { - def offset = tempOffset as int - def v = value as int - value = v + offset + value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } def linkText = getLinkText(device) def descriptionText = "$linkText was $value°$temperatureScale" diff --git a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy index 458b5403173..50226cce856 100644 --- a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy +++ b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy @@ -298,9 +298,7 @@ private getTempResult(part, description) { def temperatureScale = getTemperatureScale() def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) if (tempOffset) { - def offset = tempOffset as int - def v = value as int - value = v + offset + value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } def linkText = getLinkText(device) def descriptionText = "$linkText was $value°$temperatureScale" diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy index b580548a17f..e57cd991447 100755 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -139,7 +139,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F' map.translatable = true diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index fe4f1d39717..cfcad896320 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -153,7 +153,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F' map.translatable = true 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 9490552dd92..f17aa367b46 100755 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -165,7 +165,7 @@ def parse(String description) { } else if (maps[0].name == "temperature") { def map = maps[0] if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F' map.translatable = true diff --git a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy index 4a3b55c3e4b..5393df351f2 100644 --- a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy +++ b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy @@ -314,9 +314,7 @@ private Map getTempResult(part, description) { def temperatureScale = getTemperatureScale() def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) if (tempOffset) { - def offset = tempOffset as int - def v = value as int - value = v + offset + value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } def linkText = getLinkText(device) def descriptionText = "$linkText was $value°$temperatureScale" diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index c4d67acc681..3580f8604f3 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -127,7 +127,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F' map.translatable = true 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 7b01b9ad0f9..b7dc41ea645 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 @@ -102,7 +102,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C' : '{{ device.displayName }} was {{ value }}°F' map.translatable = true diff --git a/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy b/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy index 08a5af60a27..c4607775b7a 100644 --- a/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy +++ b/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy @@ -278,9 +278,7 @@ private getTempResult(part, description) { def temperatureScale = getTemperatureScale() def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) if (tempOffset) { - def offset = tempOffset as int - def v = value as int - value = v + offset + value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } def linkText = getLinkText(device) def descriptionText = "$linkText was $value°$temperatureScale" diff --git a/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy b/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy index 51b509d9ebf..c43fe82be2b 100644 --- a/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy +++ b/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy @@ -208,9 +208,7 @@ private Map getTemperatureResult(value) { log.debug 'TEMP' def linkText = getLinkText(device) if (tempOffset) { - def offset = tempOffset as int - def v = value as int - value = v + offset + value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } def descriptionText = "${linkText} was ${value}°${temperatureScale}" return [ diff --git a/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy b/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy index fb4518c38d8..62a78293fb1 100644 --- a/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy @@ -129,7 +129,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? "${device.displayName} temperature was ${map.value}°C" : "${device.displayName} temperature was ${map.value}°F" map.translatable = true @@ -250,4 +250,4 @@ def configure() { zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000, DataType.UINT16, 30, 3600, 100) return refresh() + configCmds -} \ No newline at end of file +} diff --git a/devicetypes/smartthings/zigbee-sound-sensor.src/zigbee-sound-sensor.groovy b/devicetypes/smartthings/zigbee-sound-sensor.src/zigbee-sound-sensor.groovy index 0806d2489d8..d650f22b940 100644 --- a/devicetypes/smartthings/zigbee-sound-sensor.src/zigbee-sound-sensor.groovy +++ b/devicetypes/smartthings/zigbee-sound-sensor.src/zigbee-sound-sensor.groovy @@ -83,7 +83,7 @@ def parse(String description) { } } else if (map.name == "temperature") { if (tempOffset) { - map.value = map.value + tempOffset + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) } map.descriptionText = temperatureScale == 'C' ? "${device.displayName} was ${map.value}°C" : "${device.displayName} was ${map.value}°F" map.translatable = true @@ -184,4 +184,4 @@ def configure() { private boolean isZoneMessage(description) { return (description?.startsWith('zone status') || description?.startsWith('zone report')) -} \ No newline at end of file +} From a49e547517eb0272b1ac25f94a4deb6d2eb99ca3 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Sat, 1 Aug 2020 08:39:22 -0500 Subject: [PATCH 032/422] SmartPower Outlet: Fix icon for the outletv4 https://smartthings.atlassian.net/browse/ONEAPP-31381 --- .../smartthings/smartpower-outlet.src/smartpower-outlet.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index aac2602b9a8..c90836b4906 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -28,7 +28,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "SmartThings Outlet" //Outlet fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "SmartThings Outlet" //Outlet fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Centralite Outlet" //Outlet - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 000F, 0B04", outClusters: "0019", manufacturer: "SmartThings", model: "outletv4", deviceJoinName: "SmartThings Outlet", mnmn: "smartthings", vid: "SmartThings-smartthings-SmartPower_Outlet" //Outlet + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 000F, 0B04", outClusters: "0019", manufacturer: "SmartThings", model: "outletv4", deviceJoinName: "SmartThings Outlet", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartPower_Outlet" //Outlet fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0006,0009,0B04", outClusters: "0019", manufacturer: "Samjin", model: "outlet", deviceJoinName: "SmartThings Outlet" //Outlet fingerprint profileId: "0010", inClusters: "0000 0003 0004 0005 0006 0008 0702 0B05", outClusters: "0019", manufacturer: "innr", model: "SP 120", deviceJoinName: "Innr Outlet" //Innr Smart Plug From 94885a1a066a19debe9117d21f386f20f1080c9c Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Mon, 3 Aug 2020 19:42:10 +0200 Subject: [PATCH 033/422] ICP-13200 - longer delay between BasicSet and BasicGet commands (#38875) --- .../fibaro-walli-double-switch.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy b/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy index cc5a990f53d..a6357b31202 100644 --- a/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy +++ b/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy @@ -346,7 +346,7 @@ private onOffCmd(value, endpoint = 1) { delayBetween([ encap(zwave.basicV1.basicSet(value: value), endpoint), encap(zwave.basicV1.basicGet(), endpoint) - ]) + ], 1000) } private refreshAll(includeMeterGet = true) { From daaf9987e715290e0564b29d28daec3e6acfa399 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 3 Aug 2020 11:59:22 -0700 Subject: [PATCH 034/422] Add fingerprint for CST thermostat Honeywell T6 Pro (#39100) --- .../zwave-battery-thermostat.src/zwave-battery-thermostat.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy b/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy index 7316fca3f1d..8b3c57747df 100644 --- a/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy +++ b/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy @@ -38,6 +38,7 @@ metadata { fingerprint inClusters: "0x43,0x40,0x44,0x31,0x80", deviceJoinName: "Thermostat" fingerprint mfr: "014F", prod: "5442", model: "5431", deviceJoinName: "Linear Thermostat" //Linear Z-Wave Thermostat fingerprint mfr: "014F", prod: "5442", model: "5436", deviceJoinName: "GoControl Thermostat" //GoControl Z-Wave Thermostat + fingerprint mfr: "0039", prod: "0011", model: "0008", deviceJoinName: "Honeywell Thermostat" //Honeywell T6 Pro Z-Wave Thermostat } tiles { From 773d630de1c2dd329df09fd28bd65f48923c74e0 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 4 Aug 2020 10:53:47 -0700 Subject: [PATCH 035/422] CHAD-5355 Zigbee lock event de-duplication (#39101) * Uses logic similar to what we do in z-wave locks to delay events created from lock attribute reports in case we receive a much more detailed operation event later. If the operation event is sent first, the lock attribute event will be marked as not displayed. * always delay lock attribute reports * add log message * fixup * fixup --- .../zigbee-lock-without-codes.groovy | 14 ++++++++++++++ .../smartthings/zigbee-lock.src/zigbee-lock.groovy | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy index fe89eca8655..1844303ae48 100644 --- a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy +++ b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy @@ -207,6 +207,15 @@ private def parseAttributeResponse(String description) { responseMap.value = "unknown" responseMap.descriptionText = "Unknown state" } + if (responseMap.value) { + /* delay this event for a second in the hopes that we get the operation event (which has more info). + If we don't get one, then it's okay to send. If we send the event with more info first, the event + with less info will be marked as not displayed + */ + log.debug "Lock attribute report received: ${responseMap.value}. Delaying event." + runIn(1, "delayLockEvent", [data : [map : responseMap]]) + return [:] + } } else { return null } @@ -214,6 +223,11 @@ private def parseAttributeResponse(String description) { return result } +def delayLockEvent(data) { + log.debug "Sending cached lock operation: ${data.map}" + sendEvent(data.map) +} + private def parseIasMessage(String description) { ZoneStatus zs = zigbee.parseZoneStatus(description) def responseMap = [ name: "battery", value: zs.isBatterySet() ? 5 : 55] diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 0d489842b89..323f6dd7867 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -493,6 +493,15 @@ private def parseAttributeResponse(String description) { responseMap.value = "unknown" responseMap.descriptionText = "Unknown state" } + if (responseMap.value) { + /* delay this event for a second in the hopes that we get the operation event (which has more info). + If we don't get one, then it's okay to send. If we send the event with more info first, the event + with less info will be marked as not displayed + */ + log.debug "Lock attribute report received: ${responseMap.value}. Delaying event." + runIn(1, "delayLockEvent", [data : [map : responseMap]]) + return [:] + } } else if (clusterInt == CLUSTER_DOORLOCK && attrInt == DOORLOCK_ATTR_MIN_PIN_LENGTH && descMap.value) { def minCodeLength = Integer.parseInt(descMap.value, 16) responseMap = [name: "minCodeLength", value: minCodeLength, descriptionText: "Minimum PIN length is ${minCodeLength}", displayed: false] @@ -517,6 +526,11 @@ private def parseAttributeResponse(String description) { return result } +def delayLockEvent(data) { + log.debug "Sending cached lock operation: ${data.map}" + sendEvent(data.map) +} + /** * Responsible for handling command responses * From a4c295f5da2cf100dc29d801ef105c50896618d5 Mon Sep 17 00:00:00 2001 From: ZWozniakS <48519140+ZWozniakS@users.noreply.github.com> Date: Thu, 6 Aug 2020 20:24:55 +0200 Subject: [PATCH 036/422] WWST-6984,WWST-6989,WWST-6994 Fingerprints for Dawon Power Manager Smart Switches (US) (#39724) Co-authored-by: Zuzanna Wozniak/Home IoT Development (IoT) /SRPOL/Engineer/Samsung Electronics --- .../dawon-zwave-wall-smart-switch.groovy | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/dawon-zwave-wall-smart-switch.src/dawon-zwave-wall-smart-switch.groovy b/devicetypes/smartthings/dawon-zwave-wall-smart-switch.src/dawon-zwave-wall-smart-switch.groovy index 122cfbd74ff..19819cabd11 100644 --- a/devicetypes/smartthings/dawon-zwave-wall-smart-switch.src/dawon-zwave-wall-smart-switch.groovy +++ b/devicetypes/smartthings/dawon-zwave-wall-smart-switch.src/dawon-zwave-wall-smart-switch.groovy @@ -20,9 +20,12 @@ metadata { capability "Sensor" capability "Health Check" - fingerprint mfr: "018C", prod: "0061", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // addChildDevice "Dawon Smart Switch${endpoint}" 1 //Dawon Temp/Humidity Sensor - fingerprint mfr: "018C", prod: "0062", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // addChildDevice "Dawon Smart Switch${endpoint}" 2 //Dawon Temp/Humidity Sensor - fingerprint mfr: "018C", prod: "0063", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // addChildDevice "Dawon Smart Switch${endpoint}" 3 //Dawon Temp/Humidity Sensor + fingerprint mfr: "018C", prod: "0061", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // KR // addChildDevice "Dawon Smart Switch${endpoint}" 1 //Dawon Temp/Humidity Sensor + fingerprint mfr: "018C", prod: "0062", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // KR // addChildDevice "Dawon Smart Switch${endpoint}" 2 //Dawon Temp/Humidity Sensor + fingerprint mfr: "018C", prod: "0063", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // KR // addChildDevice "Dawon Smart Switch${endpoint}" 3 //Dawon Temp/Humidity Sensor + fingerprint mfr: "018C", prod: "0064", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // US // addChildDevice "Dawon Smart Switch${endpoint}" 1 //Dawon Temp/Humidity Sensor + fingerprint mfr: "018C", prod: "0065", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // US // addChildDevice "Dawon Smart Switch${endpoint}" 2 //Dawon Temp/Humidity Sensor + fingerprint mfr: "018C", prod: "0066", model: "0001", deviceJoinName: "Dawon Multipurpose Sensor" // US // addChildDevice "Dawon Smart Switch${endpoint}" 3 //Dawon Temp/Humidity Sensor } preferences { @@ -332,11 +335,11 @@ private changeSwitch(endpoint, value) { } private getNumberOfChildFromModel() { - if (zwaveInfo.prod.equals("0063")) { + if ((zwaveInfo.prod.equals("0063")) || (zwaveInfo.prod.equals("0066"))) { return 3 - } else if (zwaveInfo.prod.equals("0062")) { + } else if ((zwaveInfo.prod.equals("0062")) || (zwaveInfo.prod.equals("0065"))) { return 2 - } else if (zwaveInfo.prod.equals("0061")) { + } else if ((zwaveInfo.prod.equals("0061")) || (zwaveInfo.prod.equals("0064"))) { return 1 } else { return 0 From 878f39a6264343bce15816219454c229f545c8e4 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Mon, 10 Aug 2020 08:52:35 -0700 Subject: [PATCH 037/422] CHAD-4340 Update Window Shade devices to use Window Shade Level (#30151) --- .../axis/axis-gear-st.src/axis-gear-st.groovy | 151 ++++--- .../qubino-flush-shutter.groovy | 5 +- .../springs-window-fashions-shade.groovy | 416 +++++++++--------- .../zigbee-window-shade-battery.groovy | 99 +++-- .../zigbee-window-shade.groovy | 79 +++- .../zwave-window-shade.groovy | 374 ++++++++-------- 6 files changed, 612 insertions(+), 512 deletions(-) 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 e9913b1128f..20628511266 100644 --- a/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy +++ b/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy @@ -1,8 +1,9 @@ import groovy.json.JsonOutput metadata { - definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") { + definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") { capability "Window Shade" + capability "Window Shade Level" capability "Window Shade Preset" capability "Switch Level" capability "Battery" @@ -10,18 +11,18 @@ metadata { capability "Health Check" capability "Actuator" capability "Configuration" - + // added in for Google Assistant Operability - capability "Switch" - + capability "Switch" + //Custom Commandes to achieve 25% increment control command "ShadesUp" command "ShadesDown" - + // command to stop blinds command "stop" command "getversion" - + fingerprint profileID: "0104", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear fingerprint profileId: "0104", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear fingerprint endpointID: "01, C4", profileId: "0104, C25D", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear @@ -36,7 +37,7 @@ metadata { //Updated 2019-08-09 - minor changes and improvements, onoff state reporting fixed //Updated 2019-11-11 - minor changes } - + tiles(scale: 2) { multiAttributeTile(name:"windowShade", type: "lighting", width: 3, height: 3) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { @@ -44,10 +45,10 @@ metadata { attributeState("partially open", label: 'Partially Open', action:"close", icon:"http://i.imgur.com/vBA17WL.png", backgroundColor:"#ffcc33", nextState: "closing") attributeState("closed", label: 'Closed', action:"open", icon:"http://i.imgur.com/mtHdMse.png", backgroundColor:"#bbbbdd", nextState: "opening") attributeState("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ffcc33", nextState: "stopping") - attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping") - attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") - attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") - attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping") + attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") } tileAttribute ("device.level", key: "VALUE_CONTROL") { attributeState("VALUE_UP", action: "ShadesUp") @@ -61,9 +62,9 @@ metadata { state("closed", label:'Closed', action:"open", icon:"http://i.imgur.com/SAiEADI.png", backgroundColor:"#bbbbdd", nextState: "opening") state("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ffcc33", nextState: "stopping") state("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#bbbbdd", nextState: "stopping") - state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") - state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") - state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") } controlTile("mediumSlider", "device.level", "slider",decoration:"flat",height:2, width: 2, inactiveLabel: true) { state("level", action:"switch level.setLevel") @@ -86,7 +87,7 @@ metadata { preferences { input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, required: false, displayDuringSetup: true, range:"1..100" } - + main(["main"]) details(["windowShade", "mediumSlider", "contPause", "home", "version", "battery", "refresh"]) } @@ -117,31 +118,31 @@ private getMIN_WINDOW_COVERING_VERSION() {1093} //Custom command to increment blind position by 25 % def ShadesUp() { - def shadeValue = device.latestValue("level") as Integer ?: 0 - + def shadeValue = device.latestValue("shadeLevel") as Integer ?: device.latestValue("level") as Integer ?: 0 + if (shadeValue < 100) { shadeValue = Math.min(25 * (Math.round(shadeValue / 25) + 1), 100) as Integer } - else { + else { shadeValue = 100 } //sendEvent(name:"level", value:shadeValue, displayed:true) - setLevel(shadeValue) + setShadeLevel(shadeValue) //sendEvent(name: "windowShade", value: "opening") } //Custom command to decrement blind position by 25 % def ShadesDown() { - def shadeValue = device.latestValue("level") as Integer ?: 0 - + def shadeValue = device.latestValue("shadeLevel") as Integer ?: device.latestValue("level") as Integer ?: 0 + if (shadeValue > 0) { shadeValue = Math.max(25 * (Math.round(shadeValue / 25) - 1), 0) as Integer } - else { + else { shadeValue = 0 } //sendEvent(name:"level", value:shadeValue, displayed:true) - setLevel(shadeValue) + setShadeLevel(shadeValue) //sendEvent(name: "windowShade", value: "closing") } @@ -160,11 +161,11 @@ def stop() { } else { if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ - return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) + return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) } else { sendEvent(name: "windowShade", value: "stoppingNS") - return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000]) + return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000]) } } } @@ -176,42 +177,39 @@ def pause() { //Send Command through setLevel() def on() { log.info "on()" - sendEvent(name: "windowShade", value: "opening") sendEvent(name: "switch", value: "on") - - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { - zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN) - } - else { - setLevel(100) - } + open() } //Send Command through setLevel() def off() { log.info "off()" - sendEvent(name: "windowShade", value: "closing") sendEvent(name: "switch", value: "off") close() - //zigbee.off() } //Command to set the blind position (%) and log the event def setLevel(value, rate=null) { log.info "setLevel ($value)" - + + setShadeLevel(value) +} + +def setShadeLevel(value) { + log.info "setShadeLevel ($value)" Integer currentLevel = state.level - + def i = value as Integer - sendEvent(name:"level", value: value, displayed:true) - + sendEvent(name:"level", value: value, unit:"%", displayed: false) + sendEvent(name:"shadeLevel", value: value, unit:"%", displayed:true) + if ( i == 0) { sendEvent(name: "switch", value: "off") } else { sendEvent(name: "switch", value: "on") } - + if (i > currentLevel) { sendEvent(name: "windowShade", value: "opening") } @@ -219,7 +217,7 @@ def setLevel(value, rate=null) { sendEvent(name: "windowShade", value: "closing") } //setWindowShade(i) - + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ zigbee.command(CLUSTER_WINDOWCOVERING,WINDOWCOVERING_CMD_GOTOLIFTPERCENTAGE, zigbee.convertToHexString(100-i,2)) } @@ -236,8 +234,8 @@ def open() { zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN) } else { - setLevel(100) - } + setShadeLevel(100) + } } //Send Command through setLevel() def close() { @@ -247,13 +245,13 @@ def close() { zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_CLOSE) } else { - setLevel(0) + setShadeLevel(0) } } def presetPosition() { log.info "presetPosition()" - setLevel(preset ?: state.preset ?: 50) + setShadeLevel(preset ?: state.preset ?: 50) } //Reporting of Battery & position levels @@ -262,7 +260,7 @@ def ping(){ return refresh() } -//Set blind State based on position (which shows appropriate image) +//Set blind State based on position (which shows appropriate image) def setWindowShade(value) { if ((value>0)&&(value<99)){ sendEvent(name: "windowShade", value: "partially open", displayed:true) @@ -279,7 +277,7 @@ def setWindowShade(value) { def refresh() { log.debug "parse() refresh" def cmds_refresh = null - + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ cmds_refresh = zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) } @@ -287,24 +285,24 @@ def refresh() { cmds_refresh = zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) } - - cmds_refresh = cmds_refresh + + + cmds_refresh = cmds_refresh + zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) + zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) - + log.info "refresh() --- cmds: $cmds_refresh" - + return cmds_refresh } def getversion () { //state.currentVersion = 0 - sendEvent(name: "version", value: "Checking Version ... ") + sendEvent(name: "version", value: "Checking Version ... ") return zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) } //configure reporting -def configure() { +def configure() { state.currentVersion = 0 sendEvent(name: "windowShade", value: "unknown") log.debug "Configuring Reporting and Bindings." @@ -316,33 +314,42 @@ def configure() { zigbee.readAttribute(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE) + zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) + zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) - - def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) + + + def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE, 0x10, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, 0x20, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY, 0x20, 1, 3600, 0x01) - + log.info "configure() --- cmds: $cmds" return attrs_refresh + cmds } def parse(String description) { log.trace "parse() --- description: $description" - + Map map = [:] + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + def event = zigbee.getEvent(description) if (event && description?.startsWith('on/off')) { log.trace "sendEvent(event)" sendEvent(event) } - + else if ((description?.startsWith('read attr -')) || (description?.startsWith('attr report -'))) { map = parseReportAttributeMessage(description) def result = map ? createEvent(map) : null + + if (map.name == "level") { + result = [result, createEvent([name: "shadeLevel", value: map.value, unit: map.unit])] + } + log.debug "parse() --- returned: $result" return result - } + } } private Map parseReportAttributeMessage(String description) { @@ -366,23 +373,27 @@ private Map parseReportAttributeMessage(String description) { //Set icon based on device feedback for the open, closed, & partial configuration resultMap.value = levelValue state.level = levelValue + resultMap.unit = "%" + resultMap.displayed = false setWindowShade(levelValue) } else if (descMap.clusterInt == CLUSTER_LEVEL && descMap.attrInt == LEVEL_ATTR_LEVEL) { //log.debug "parse() --- returned level :$state.currentVersion " - def currentLevel = state.level - + def currentLevel = state.level + resultMap.name = "level" def levelValue = Math.round(Integer.parseInt(descMap.value, 16)) def levelValuePercent = Math.round((levelValue/255)*100) //Set icon based on device feedback for the open, closed, & partial configuration resultMap.value = levelValuePercent state.level = levelValuePercent - + resultMap.unit = "%" + resultMap.displayed = false + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { //Integer currentLevel = state.level - sendEvent(name:"level", value: levelValuePercent, displayed:true) - + sendEvent(name:"level", value: levelValuePercent, unit: "%", displayed: false) + if (levelValuePercent > currentLevel) { sendEvent(name: "windowShade", value: "opening") } else if (levelValuePercent < currentLevel) { @@ -396,21 +407,21 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.clusterInt == CLUSTER_BASIC && descMap.attrInt == BASIC_ATTR_SWBUILDID) { resultMap.name = "version" def versionString = descMap.value - + StringBuilder output = new StringBuilder("") StringBuilder output2 = new StringBuilder("") - + for (int i = 0; i < versionString.length(); i += 2) { String str = versionString.substring(i, i + 2) - output.append((char) (Integer.parseInt(str, 16))) + output.append((char) (Integer.parseInt(str, 16))) if (i > 19) { output2.append((char) (Integer.parseInt(str, 16))) } - } - + } + def current = Integer.parseInt(output2.toString()) state.currentVersion = current - resultMap.value = output.toString() + resultMap.value = output.toString() } else { log.debug "parseReportAttributeMessage() --- ignoring attribute" diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy index 22cd56f40f1..7728e4078f0 100644 --- a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -12,6 +12,7 @@ * for the specific language governing permissions and limitations under the License. * */ +import groovy.json.JsonOutput metadata { definition (name: "Qubino Flush Shutter", namespace: "qubino", author: "SmartThings", ocfDeviceType: "oic.d.blind", mcdSync: true) { @@ -92,7 +93,7 @@ def installed() { state.currentPreferencesState."$it.key".status = "synced" } // Preferences template end - sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) } def updated() { @@ -251,7 +252,7 @@ def setShadeLevel(level) { } def setSlats(level) { - def time = (int) (state.timeOfVenetianMovement * 1.1) + def time = (int) (state.timeOfVenetianMovement * 1.1) sendHubCommand([ encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 2), "delay ${time}", 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 7af556576ba..48a0482e4e2 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 @@ -15,275 +15,285 @@ import groovy.json.JsonOutput metadata { - definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { - capability "Window Shade" - capability "Window Shade Preset" - capability "Battery" - capability "Refresh" - capability "Health Check" - capability "Actuator" - capability "Sensor" - - command "stop" - - capability "Switch Level" // until we get a Window Shade Level capability - - // This device handler is specifically for SWF window coverings - // -// fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade" -// fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade" - fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Springs Window Treatment" //Window Shade - fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Springs Window Treatment" //Roller Shade - } - - simulator { - status "open": "command: 2603, payload: FF" - status "closed": "command: 2603, payload: 00" - status "10%": "command: 2603, payload: 0A" - status "66%": "command: 2603, payload: 42" - status "99%": "command: 2603, payload: 63" - status "battery 100%": "command: 8003, payload: 64" - status "battery low": "command: 8003, payload: FF" - - // reply messages - reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" - reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" - reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" - reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" - } - - tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){ - tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" - attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open" - attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" - } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"setLevel" - } - } - - standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { - state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" - } - - standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" - state "disabled", label:'', action:"", icon:"st.secondary.refresh" - } - - valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { - state "battery", label:'batt.', unit:"", - backgroundColors:[ - [value: 0, color: "#bc2323"], - [value: 6, color: "#44b621"] - ] - } - - preferences { - input "switchDirection", "bool", title: "Flip the orientation of the shade", defaultValue: false, required: false, displayDuringSetup: false -// input "preset", "number", title: "Default half-open position (1-100). Springs Window Fashions users should consult their manuals.", defaultValue: 50, required: false, displayDuringSetup: false - } - - main(["windowShade"]) - details(["windowShade", "home", "refresh", "battery"]) - - } + definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { + capability "Window Shade" + capability "Window Shade Level" + capability "Window Shade Preset" + capability "Switch Level" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Actuator" + capability "Sensor" + + command "stop" + + // This device handler is specifically for SWF window coverings + // + //fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade" + //fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade" + fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Springs Window Treatment" //Window Shade + fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Springs Window Treatment" //Roller Shade + } + + simulator { + status "open": "command: 2603, payload: FF" + status "closed": "command: 2603, payload: 00" + status "10%": "command: 2603, payload: 0A" + status "66%": "command: 2603, payload: 42" + status "99%": "command: 2603, payload: 63" + status "battery 100%": "command: 8003, payload: 64" + status "battery low": "command: 8003, payload: FF" + + // reply messages + reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" + reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" + reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" + reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){ + tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" + attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#00A0DC", nextState:"partially open" + attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" + } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" + } + } + + standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { + state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" + } + + standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" + state "disabled", label:'', action:"", icon:"st.secondary.refresh" + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'batt.', unit:"", + backgroundColors:[ + [value: 0, color: "#bc2323"], + [value: 6, color: "#44b621"] + ] + } + + preferences { + input "switchDirection", "bool", title: "Flip the orientation of the shade", defaultValue: false, required: false, displayDuringSetup: false + //input "preset", "number", title: "Default half-open position (1-100). Springs Window Fashions users should consult their manuals.", defaultValue: 50, required: false, displayDuringSetup: false + } + + main(["windowShade"]) + details(["windowShade", "home", "refresh", "battery"]) + + } } def parse(String description) { - def result = null - //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) - // TODO: Workaround manual parsing of v4 multilevel report - def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value - if (cmd) { - result = zwaveEvent(cmd) - } - log.debug "Parsed '$description' to ${result.inspect()}" - return result + def result = null + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + + //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) + // TODO: Workaround manual parsing of v4 multilevel report + def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug "Parsed '$description' to ${result.inspect()}" + return result } def getCheckInterval() { - // These are battery-powered devices, and it's not very critical - // to know whether they're online or not – 12 hrs - 4 * 60 * 60 + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + 4 * 60 * 60 } 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()) + 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()) } def updated() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - def cmds = [] - if (!device.latestState("battery")) { - cmds << zwave.batteryV1.batteryGet().format() - } - - if (!device.getDataValue("MSR")) { - cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() - } - - log.debug("Updated with settings $settings") - cmds << zwave.switchMultilevelV1.switchMultilevelGet().format() - response(cmds) + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + def cmds = [] + if (!device.latestState("battery")) { + cmds << zwave.batteryV1.batteryGet().format() + } + + if (!device.getDataValue("MSR")) { + cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() + } + + log.debug("Updated with settings $settings") + cmds << zwave.switchMultilevelV1.switchMultilevelGet().format() + response(cmds) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } private handleLevelReport(physicalgraph.zwave.Command cmd) { - def descriptionText = null - def shadeValue = null - - def level = cmd.value as Integer - level = switchDirection ? 99-level : level - if (level >= 99) { - level = 100 - shadeValue = "open" - } else if (level <= 0) { - level = 0 // unlike dimmer switches, the level isn't saved when closed - shadeValue = "closed" - } else { - shadeValue = "partially open" - descriptionText = "${device.displayName} shade is ${level}% open" - } - checkLevelReport(level) - def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) - def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) - - def result = [stateEvent, levelEvent] - if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { - log.debug "requesting battery" - state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row - result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) - } - result + def descriptionText = null + def shadeValue = null + + def level = cmd.value as Integer + level = switchDirection ? 99-level : level + if (level >= 99) { + level = 100 + shadeValue = "open" + } else if (level <= 0) { + level = 0 // unlike dimmer switches, the level isn't saved when closed + shadeValue = "closed" + } else { + shadeValue = "partially open" + descriptionText = "${device.displayName} shade is ${level}% open" + } + checkLevelReport(level) + + def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) + def shadeLevelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") + def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: shadeLevelEvent.isStateChange) + + def result = [stateEvent, shadeLevelEvent, levelEvent] + if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { + log.debug "requesting battery" + state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row + result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) + } + result } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { - [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), - response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] + [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { - def map = [ name: "battery", unit: "%" ] - if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { - map.value = 1 - map.descriptionText = "${device.displayName} has a low battery" - map.isStateChange = true - } else { - map.value = cmd.batteryLevel - } - state.lastbatt = now() - if (map.value <= 1 && device.latestValue("battery") != null && device.latestValue("battery") - map.value > 20) { - // Springs shades sometimes erroneously report a low battery when rapidly actuated manually. They'll still - // refuse to actuate after one of these reports, but this will limit the bad data that gets surfaced - log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" - } else { - createEvent(map) - } + def map = [ name: "battery", unit: "%" ] + if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { + map.value = 1 + map.descriptionText = "${device.displayName} has a low battery" + map.isStateChange = true + } else { + map.value = cmd.batteryLevel + } + state.lastbatt = now() + if (map.value <= 1 && device.latestValue("battery") != null && device.latestValue("battery") - map.value > 20) { + // Springs shades sometimes erroneously report a low battery when rapidly actuated manually. They'll still + // refuse to actuate after one of these reports, but this will limit the bad data that gets surfaced + log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" + } else { + createEvent(map) + } } def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { - // the docs we got said that the device would send a notification report, but we've determined that - // is not true + // the docs we got said that the device would send a notification report, but we've determined that + // is not true } def zwaveEvent(physicalgraph.zwave.Command cmd) { - log.debug "unhandled $cmd" - return [] + log.debug "unhandled $cmd" + return [] } def open() { - log.debug "open()" - def level = switchDirection ? 0 : 99 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() - // zwave.basicV1.basicSet(value: 0xFF).format() + log.debug "open()" + + setShadeLevel(99) // Handle switchDirection in setShadeLevel } def close() { - log.debug "close()" - def level = switchDirection ? 99 : 0 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() - //zwave.basicV1.basicSet(value: 0).format() + log.debug "close()" + + setShadeLevel(0) // Handle switchDirection in setShadeLevel } def setLevel(value, duration = null) { - log.debug "setLevel(${value.inspect()})" - Integer level = value as Integer - level = switchDirection ? 99-level : level - if (level < 0) level = 0 - if (level > 99) level = 99 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() + log.debug "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + Integer level = Math.max(Math.min(value as Integer, 99), 0) + + level = switchDirection ? 99-level : level + + log.debug "setShadeLevel($value) -> $level" + + levelChangeFollowUp(level) // Follow up in a few seconds to make sure the shades didn't "forget" to send us level updates + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { - zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() + zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() } def pause() { - log.debug "pause()" - stop() + log.debug "pause()" + stop() } def stop() { - log.debug "stop()" - zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() + log.debug "stop()" + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() } def ping() { - zwave.switchMultilevelV1.switchMultilevelGet().format() + zwave.switchMultilevelV1.switchMultilevelGet().format() } def refresh() { - log.debug "refresh()" - delayBetween([ - zwave.switchMultilevelV1.switchMultilevelGet().format(), - zwave.batteryV1.batteryGet().format() - ], 1500) + log.debug "refresh()" + delayBetween([ + zwave.switchMultilevelV1.switchMultilevelGet().format(), + zwave.batteryV1.batteryGet().format() + ], 1500) } def levelChangeFollowUp(expectedLevel) { - state.expectedValue = expectedLevel - state.levelChecks = 0 - runIn(5, "checkLevel", [overwrite: true]) + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) } def checkLevelReport(value) { - if (state.expectedValue != null) { - if ((state.expectedValue == 99 && value >= 99) || - (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { - unschedule("checkLevel") - } - } + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } } def checkLevel() { - if (state.levelChecks != null && state.levelChecks < 5) { - state.levelChecks = state.levelChecks + 1 - runIn(5, "checkLevel", [overwrite: true]) - sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) - } else { - unschedule("checkLevel") - } -} \ No newline at end of file + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } +} diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index 797ddd4f432..f2445c53c9a 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -11,6 +11,7 @@ * 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 @@ -21,6 +22,7 @@ metadata { capability "Configuration" capability "Refresh" capability "Window Shade" + capability "Window Shade Level" capability "Window Shade Preset" capability "Health Check" capability "Switch Level" @@ -36,13 +38,16 @@ metadata { } tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" - attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" - attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" - attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" - attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" + attributeState "open", label: 'Open', action: "close", icon: "st.shades.shade-open", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "st.shades.shade-closed", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "st.shades.shade-open", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "st.shades.shade-opening", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "st.shades.shade-closing", backgroundColor: "#ffffff", nextState: "partially open" + } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" } } standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { @@ -54,18 +59,12 @@ metadata { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } - valueTile("shadeLevel", "device.level", width: 4, height: 1) { - state "level", label: 'Shade is ${currentValue}% up', defaultState: true - } valueTile("batteryLevel", "device.battery", width: 2, height: 2) { state "battery", label:'${currentValue}% battery', unit:"" } - controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { - state "level", action:"switch level.setLevel" - } main "windowShade" - details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh", "batteryLevel"]) + details(["windowShade", "contPause", "presetPosition", "refresh", "batteryLevel"]) } } @@ -87,27 +86,31 @@ private List collectAttributes(Map descMap) { if (descMap.additionalAttrs) { descMaps.addAll(descMap.additionalAttrs) } - return descMaps -} -def installed() { - log.debug "installed" - sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) + return descMaps } // Parse incoming device messages to generate events def parse(String description) { log.debug "description:- ${description}" + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + if (description?.startsWith("read attr -")) { Map descMap = zigbee.parseDescriptionAsMap(description) + if (isBindingTableMessage(description)) { parseBindingTableMessage(description) } else if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) { log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}" List descMaps = collectAttributes(descMap) def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT } + if (liftmap && liftmap.value) { def newLevel = zigbee.convertHexToInt(liftmap.value) + if (shouldInvertLiftPercentage()) { // some devices report % level of being closed (instead of % level of being opened) // inverting that logic is needed here to avoid a code duplication @@ -121,18 +124,23 @@ def parse(String description) { levelEventHandler(valueInt) } else if (reportsBatteryPercentage() && descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && zigbee.convertHexToInt(descMap?.attrId) == BATTERY_PERCENTAGE_REMAINING && descMap.value) { def batteryLevel = zigbee.convertHexToInt(descMap.value) + batteryPercentageEventHandler(batteryLevel) } } } def levelEventHandler(currentLevel) { - def lastLevel = device.currentValue("level") + def lastLevel = device.currentValue("shadeLevel") ?: device.currentValue("level") // Try shadeLevel, if not use level and pass to logic below + log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" + if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { - sendEvent(name: "level", value: currentLevel) + sendEvent(name: "shadeLevel", value: currentLevel, unit: "%") + sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) + if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { @@ -147,8 +155,9 @@ def levelEventHandler(currentLevel) { } def updateFinalState() { - def level = device.currentValue("level") + def level = device.currentValue("shadeLevel") log.debug "updateFinalState: ${level}" + if (level > 0 && level < 100) { sendEvent(name: "windowShade", value: "partially open") } @@ -163,28 +172,40 @@ def batteryPercentageEventHandler(batteryLevel) { def close() { log.info "close()" - setLevel(0) + + setShadeLevel(0) } def open() { log.info "open()" - setLevel(100) + + setShadeLevel(100) } -def setLevel(data, rate = null) { - log.info "setLevel()" +def setLevel(value, rate = null) { + log.info "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + log.info "setShadeLevel($value)" + + Integer level = Math.max(Math.min(value as Integer, 100), 0) def cmd + if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { // some devices keeps % level of being closed (instead of % level of being opened) // inverting that logic is needed here - data = 100 - data + level = 100 - level } - cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2)) + cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(level, 2)) } else { - cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) + cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(level * 255 / 100), 2)) } - cmd + + return cmd } def pause() { @@ -197,7 +218,7 @@ def pause() { } def presetPosition() { - setLevel(preset ?: 50) + setShadeLevel(preset ?: 50) } /** @@ -210,21 +231,32 @@ def ping() { def refresh() { log.info "refresh()" def cmds + if (supportsLiftPercentage()) { cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT) } else { cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL) } + return cmds } +def installed() { + log.debug "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) + def cmds + log.info "configure()" + + // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + log.debug "Configuring Reporting and Bindings." - def cmds if (supportsLiftPercentage()) { cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 2, 600, null) } else { @@ -248,6 +280,7 @@ def usesLocalGroupBinding() { private def parseBindingTableMessage(description) { Integer groupAddr = getGroupAddrFromBindingTable(description) + if (groupAddr) { List cmds = addHubToGroup(groupAddr) cmds?.collect { new physicalgraph.device.HubAction(it) } @@ -258,7 +291,9 @@ private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) def groupEntry = btr?.table_entries?.find { it.dstAddrMode == 1 } + log.info "Found ${groupEntry}" + !groupEntry?.dstAddr ?: Integer.parseInt(groupEntry.dstAddr, 16) } 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 98f1a878f1b..7ad3527fa07 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -22,6 +22,7 @@ metadata { capability "Configuration" capability "Refresh" capability "Window Shade" + capability "Window Shade Level" capability "Window Shade Preset" capability "Health Check" capability "Switch Level" @@ -41,7 +42,7 @@ metadata { } tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" @@ -49,6 +50,9 @@ metadata { attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" + } } standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc" @@ -59,15 +63,9 @@ metadata { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } - valueTile("shadeLevel", "device.level", width: 4, height: 1) { - state "level", label: 'Shade is ${currentValue}% up', defaultState: true - } - controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { - state "level", action:"switch level.setLevel" - } main "windowShade" - details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh"]) + details(["windowShade", "contPause", "presetPosition", "refresh"]) } } @@ -89,22 +87,31 @@ private List collectAttributes(Map descMap) { if (descMap.additionalAttrs) { descMaps.addAll(descMap.additionalAttrs) } + return descMaps } // Parse incoming device messages to generate events def parse(String description) { log.debug "description:- ${description}" + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + if (description?.startsWith("read attr -")) { Map descMap = zigbee.parseDescriptionAsMap(description) + if (isBindingTableMessage(description)) { parseBindingTableMessage(description) } else if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) { log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}" List descMaps = collectAttributes(descMap) def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT } + if (liftmap && liftmap.value) { def newLevel = zigbee.convertHexToInt(liftmap.value) + if (shouldInvertLiftPercentage()) { // some devices report % level of being closed (instead of % level of being opened) // inverting that logic is needed here to avoid a code duplication @@ -118,18 +125,23 @@ def parse(String description) { levelEventHandler(valueInt) } else if (reportsBatteryPercentage() && descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && zigbee.convertHexToInt(descMap?.attrId) == BATTERY_PERCENTAGE_REMAINING && descMap.value) { def batteryLevel = zigbee.convertHexToInt(descMap.value) + batteryPercentageEventHandler(batteryLevel) } } } def levelEventHandler(currentLevel) { - def lastLevel = device.currentValue("level") + def lastLevel = device.currentValue("shadeLevel") ?: device.currentValue("level") // Try shadeLevel, if not use level and pass to logic below + log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" + if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { - sendEvent(name: "level", value: currentLevel) + sendEvent(name: "shadeLevel", value: currentLevel, unit: "%") + sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) + if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { @@ -144,8 +156,9 @@ def levelEventHandler(currentLevel) { } def updateFinalState() { - def level = device.currentValue("level") + def level = device.currentValue("shadeLevel") log.debug "updateFinalState: ${level}" + if (level > 0 && level < 100) { sendEvent(name: "windowShade", value: "partially open") } @@ -158,10 +171,6 @@ def batteryPercentageEventHandler(batteryLevel) { } } -def supportsLiftPercentage() { - device.getDataValue("manufacturer") != "Feibit Co.Ltd" -} - def close() { log.info "close()" zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_CLOSE) @@ -172,19 +181,29 @@ def open() { zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_OPEN) } -def setLevel(data, rate = null) { - log.info "setLevel()" +def setLevel(value, rate = null) { + log.info "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + log.info "setShadeLevel($value)" + + Integer level = Math.max(Math.min(value as Integer, 100), 0) def cmd + if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { // some devices keeps % level of being closed (instead of % level of being opened) // inverting that logic is needed here - data = 100 - data + level = 100 - level } - cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2)) + cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(level, 2)) } else { - cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) + cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(level * 255 / 100), 2)) } + return cmd } @@ -194,7 +213,7 @@ def pause() { } def presetPosition() { - setLevel(preset ?: 50) + setShadeLevel(preset ?: 50) } /** @@ -207,25 +226,32 @@ def ping() { def refresh() { log.info "refresh()" def cmds + if (supportsLiftPercentage()) { cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT) } else { cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL) } + return cmds } def installed() { + log.debug "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) + def cmds + log.info "configure()" + + // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + log.debug "Configuring Reporting and Bindings." - def cmds if (supportsLiftPercentage()) { cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 0, 600, null) } else { @@ -249,6 +275,7 @@ def usesLocalGroupBinding() { private def parseBindingTableMessage(description) { Integer groupAddr = getGroupAddrFromBindingTable(description) + if (groupAddr) { List cmds = addHubToGroup(groupAddr) cmds?.collect { new physicalgraph.device.HubAction(it) } @@ -259,7 +286,9 @@ private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) def groupEntry = btr?.table_entries?.find { it.dstAddrMode == 1 } + log.info "Found ${groupEntry}" + !groupEntry?.dstAddr ?: Integer.parseInt(groupEntry.dstAddr, 16) } @@ -271,6 +300,10 @@ private List readDeviceBindingTable() { ["zdo mgmt-bind 0x${device.deviceNetworkId} 0", "delay 200"] } +def supportsLiftPercentage() { + device.getDataValue("manufacturer") != "Feibit Co.Ltd" +} + def shouldInvertLiftPercentage() { return isIkeaKadrilj() || isIkeaFyrtur() || isSomfyGlydea() } 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 c43694d37e9..67554aa4a70 100644 --- a/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy +++ b/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy @@ -15,252 +15,262 @@ import groovy.json.JsonOutput metadata { - definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { - capability "Window Shade" - capability "Window Shade Preset" - capability "Battery" - capability "Refresh" - capability "Health Check" - capability "Actuator" - capability "Sensor" - - command "stop" - - capability "Switch Level" // until we get a Window Shade Level capability - - // This device handler is specifically for non-SWF position-aware window coverings - // - fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade - fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade -// fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds" -// fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade" - } - - simulator { - status "open": "command: 2603, payload: FF" - status "closed": "command: 2603, payload: 00" - status "10%": "command: 2603, payload: 0A" - status "66%": "command: 2603, payload: 42" - status "99%": "command: 2603, payload: 63" - status "battery 100%": "command: 8003, payload: 64" - status "battery low": "command: 8003, payload: FF" - - // reply messages - reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" - reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" - reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" - reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" - } - - tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4){ - tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" - attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open" - attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" - } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"setLevel" - } - } - - standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { - state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" - } - - standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" - state "disabled", label:'', action:"", icon:"st.secondary.refresh" - } - - valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { - state "battery", label:'${currentValue}% battery', unit:"" - } - - preferences { - input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false - } - - main(["windowShade"]) - details(["windowShade", "home", "refresh", "battery"]) - - } + definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { + capability "Window Shade" + capability "Window Shade Level" + capability "Window Shade Preset" + capability "Switch Level" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Actuator" + capability "Sensor" + + command "stop" + + // This device handler is specifically for non-SWF position-aware window coverings + // + fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade + fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade +// fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds" +// fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade" + } + + simulator { + status "open": "command: 2603, payload: FF" + status "closed": "command: 2603, payload: 00" + status "10%": "command: 2603, payload: 0A" + status "66%": "command: 2603, payload: 42" + status "99%": "command: 2603, payload: 63" + status "battery 100%": "command: 8003, payload: 64" + status "battery low": "command: 8003, payload: FF" + + // reply messages + reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" + reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" + reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" + reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4){ + tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" + attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#00A0DC", nextState:"partially open" + attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" + } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" + } + } + + standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { + state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" + } + + standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" + state "disabled", label:'', action:"", icon:"st.secondary.refresh" + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + preferences { + input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false + } + + main(["windowShade"]) + details(["windowShade", "home", "refresh", "battery"]) + + } } def parse(String description) { - def result = null - //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) - // TODO: Workaround manual parsing of v4 multilevel report - def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value - if (cmd) { - result = zwaveEvent(cmd) - } - log.debug "Parsed '$description' to ${result.inspect()}" - return result + def result = null + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + + //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) + // TODO: Workaround manual parsing of v4 multilevel report + def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug "Parsed '$description' to ${result.inspect()}" + return result } def getCheckInterval() { - // These are battery-powered devices, and it's not very critical - // to know whether they're online or not – 12 hrs - 4 * 60 * 60 + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + 4 * 60 * 60 } 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()) + 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()) } def updated() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - if (!device.latestState("battery")) { - response(zwave.batteryV1.batteryGet()) - } + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + if (!device.latestState("battery")) { + response(zwave.batteryV1.batteryGet()) + } } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } private handleLevelReport(physicalgraph.zwave.Command cmd) { - def descriptionText = null - def shadeValue = null - - def level = cmd.value as Integer - if (level >= 99) { - level = 100 - shadeValue = "open" - } else if (level <= 0) { - level = 0 // unlike dimmer switches, the level isn't saved when closed - shadeValue = "closed" - } else { - shadeValue = "partially open" - descriptionText = "${device.displayName} shade is ${level}% open" - } - checkLevelReport(level) - def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) - def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) - - def result = [stateEvent, levelEvent] - if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { - log.debug "requesting battery" - state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row - result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) - } - result + def descriptionText = null + def shadeValue = null + + def level = cmd.value as Integer + if (level >= 99) { + level = 100 + shadeValue = "open" + } else if (level <= 0) { + level = 0 // unlike dimmer switches, the level isn't saved when closed + shadeValue = "closed" + } else { + shadeValue = "partially open" + descriptionText = "${device.displayName} shade is ${level}% open" + } + checkLevelReport(level) + + def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) + def shadeLevelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") + def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: shadeLevelEvent.isStateChange) + + def result = [stateEvent, shadeLevelEvent, levelEvent] + if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { + log.debug "requesting battery" + state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row + result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) + } + result } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { - [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), - response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] + [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } 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 - } - state.lastbatt = now() - createEvent(map) + 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() + + createEvent(map) } def zwaveEvent(physicalgraph.zwave.Command cmd) { - log.debug "unhandled $cmd" - return [] + log.debug "unhandled $cmd" + return [] } def open() { - levelChangeFollowUp(99) - log.debug "open()" - /*delayBetween([ - zwave.basicV1.basicSet(value: 0xFF).format(), - zwave.switchMultilevelV1.switchMultilevelGet().format() - ], 1000)*/ - zwave.basicV1.basicSet(value: 99).format() + log.debug "open()" + + setShadeLevel(99) } def close() { - levelChangeFollowUp(0) - log.debug "close()" - /*delayBetween([ - zwave.basicV1.basicSet(value: 0x00).format(), - zwave.switchMultilevelV1.switchMultilevelGet().format() - ], 1000)*/ - zwave.basicV1.basicSet(value: 0).format() + log.debug "close()" + + setShadeLevel(0) } def setLevel(value, duration = null) { - log.debug "setLevel(${value.inspect()})" - Integer level = value as Integer - if (level < 0) level = 0 - if (level > 99) level = 99 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() + log.debug "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + Integer level = Math.max(Math.min(value as Integer, 99), 0) + + log.debug "setShadeLevel($value) -> $level" + + levelChangeFollowUp(level) // Follow up in a few seconds to make sure the shades didn't "forget" to send us level updates + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { - setLevel(preset ?: state.preset ?: 50) + setLevel(preset ?: state.preset ?: 50) } def pause() { - log.debug "pause()" - stop() + log.debug "pause()" + + stop() } def stop() { - log.debug "stop()" - zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() + log.debug "stop()" + + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() } def ping() { - zwave.switchMultilevelV1.switchMultilevelGet().format() + zwave.switchMultilevelV1.switchMultilevelGet().format() } def refresh() { - log.debug "refresh()" - delayBetween([ - zwave.switchMultilevelV1.switchMultilevelGet().format(), - zwave.batteryV1.batteryGet().format() - ], 1500) + log.debug "refresh()" + delayBetween([ + zwave.switchMultilevelV1.switchMultilevelGet().format(), + zwave.batteryV1.batteryGet().format() + ], 1500) } def levelChangeFollowUp(expectedLevel) { - state.expectedValue = expectedLevel - state.levelChecks = 0 - runIn(5, "checkLevel", [overwrite: true]) + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) } def checkLevelReport(value) { - if (state.expectedValue != null) { - if ((state.expectedValue == 99 && value >= 99) || - (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { - unschedule("checkLevel") - } - } + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } } def checkLevel() { - if (state.levelChecks != null && state.levelChecks < 5) { - state.levelChecks = state.levelChecks + 1 - runIn(5, "checkLevel", [overwrite: true]) - sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) - } else { - unschedule("checkLevel") - } + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } } From e63468defbebe70fae0370eace3523ec50f65e93 Mon Sep 17 00:00:00 2001 From: ADUROSMART ERIA <52692745+adurosmart@users.noreply.github.com> Date: Thu, 13 Aug 2020 03:00:19 +0800 Subject: [PATCH 038/422] update parseAduroSmartButtonMessage function (#40488) Some customers reported that the buttons 1 and 4 are not sensitive, so restore the zigbee.ONOFF_CLUSTER event trigger of button 1 and button 4, which will increase the success rate of event reporting after the button is pressed Co-authored-by: Andy Yi --- .../zigbee-multi-button.src/zigbee-multi-button.groovy | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy index 1c1ece30a0a..317bab3c551 100644 --- a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy +++ b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy @@ -279,7 +279,13 @@ private getButtonName() { private Map parseAduroSmartButtonMessage(Map descMap){ def buttonState = "pushed" def buttonNumber = 0 - if (descMap.clusterInt == ADUROSMART_SPECIFIC_CLUSTER) { + if (descMap.clusterInt == zigbee.ONOFF_CLUSTER) { + if (descMap.command == "01") { + buttonNumber = 1 + } else if (descMap.command == "00") { + buttonNumber = 4 + } + } else if (descMap.clusterInt == ADUROSMART_SPECIFIC_CLUSTER) { def list2 = descMap.data buttonNumber = (list2[1] as int) + 1 } From 22dc7337f8e80a6476f13c9c04f7c64cc57c5041 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Fri, 14 Aug 2020 01:54:06 +0200 Subject: [PATCH 039/422] [ICP-13162] Add alarm event when tamper is triggered (#36895) * Add alarm event when tamper is triggered * Add off event * Fixes * Change time in runIn method, add chime off event --- .../aeotec-doorbell-siren-6.groovy | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy index 099ebce9159..5a0e53ee343 100644 --- a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy +++ b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy @@ -142,6 +142,10 @@ def both() { on() } +def chime() { + on() +} + def ping() { def cmds = [ encap(zwave.basicV1.basicGet()) @@ -201,26 +205,30 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm switch (cmd.event) { case 0x09: //TAMPER sendEvent(name: "tamper", value: "detected") - runIn(10, "clearTamper") + sendEvent(name: "alarm", value: "both") + runIn(2, "clearTamperAndAlarm") break case 0x01: //ON if (state.lastTriggeredSound == 1) { - createEvent([name: "alarm", value: "both"]) - createEvent([name: "chime", value: "chime"]) + sendEvent(name: "chime", value: "chime") + sendEvent(name: "alarm", value: "both") } else { setActiveSound(state.lastTriggeredSound) } break case 0x00: //OFF resetActiveSound() - createEvent([name: "tamper", value: "clear"]) + sendEvent(name: "tamper", value: "clear") + sendEvent(name: "alarm", value: "off") + sendEvent(name: "chime", value: "off") break } } } -def clearTamper() { +def clearTamperAndAlarm() { sendEvent(name: "tamper", value: "clear") + sendEvent(name: "alarm", value: "off") } def setOnChild(deviceDni) { @@ -252,7 +260,7 @@ def resetActiveSound() { def setActiveSound(soundId) { String childDni = "${device.deviceNetworkId}:${soundId}" def child = childDevices.find { it.deviceNetworkId == childDni } - child?.sendEvent(name: "chime", value: "on") + child?.sendEvent(name: "chime", value: "chime") child?.sendEvent(name: "alarm", value: "both") } From dd90f4eebb14817cd003116c214625275878b852 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 18 Aug 2020 11:25:59 -0700 Subject: [PATCH 040/422] Revert "CHAD-4340 Update Window Shade devices to use Window Shade Level (#30151)" This reverts commit 878f39a6264343bce15816219454c229f545c8e4. --- .../axis/axis-gear-st.src/axis-gear-st.groovy | 151 +++---- .../qubino-flush-shutter.groovy | 5 +- .../springs-window-fashions-shade.groovy | 416 +++++++++--------- .../zigbee-window-shade-battery.groovy | 99 ++--- .../zigbee-window-shade.groovy | 79 +--- .../zwave-window-shade.groovy | 374 ++++++++-------- 6 files changed, 512 insertions(+), 612 deletions(-) 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 20628511266..e9913b1128f 100644 --- a/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy +++ b/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy @@ -1,9 +1,8 @@ import groovy.json.JsonOutput metadata { - definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") { + definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") { capability "Window Shade" - capability "Window Shade Level" capability "Window Shade Preset" capability "Switch Level" capability "Battery" @@ -11,18 +10,18 @@ metadata { capability "Health Check" capability "Actuator" capability "Configuration" - + // added in for Google Assistant Operability - capability "Switch" - + capability "Switch" + //Custom Commandes to achieve 25% increment control command "ShadesUp" command "ShadesDown" - + // command to stop blinds command "stop" command "getversion" - + fingerprint profileID: "0104", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear fingerprint profileId: "0104", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear fingerprint endpointID: "01, C4", profileId: "0104, C25D", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear @@ -37,7 +36,7 @@ metadata { //Updated 2019-08-09 - minor changes and improvements, onoff state reporting fixed //Updated 2019-11-11 - minor changes } - + tiles(scale: 2) { multiAttributeTile(name:"windowShade", type: "lighting", width: 3, height: 3) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { @@ -45,10 +44,10 @@ metadata { attributeState("partially open", label: 'Partially Open', action:"close", icon:"http://i.imgur.com/vBA17WL.png", backgroundColor:"#ffcc33", nextState: "closing") attributeState("closed", label: 'Closed', action:"open", icon:"http://i.imgur.com/mtHdMse.png", backgroundColor:"#bbbbdd", nextState: "opening") attributeState("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ffcc33", nextState: "stopping") - attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping") - attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") - attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") - attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping") + attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") } tileAttribute ("device.level", key: "VALUE_CONTROL") { attributeState("VALUE_UP", action: "ShadesUp") @@ -62,9 +61,9 @@ metadata { state("closed", label:'Closed', action:"open", icon:"http://i.imgur.com/SAiEADI.png", backgroundColor:"#bbbbdd", nextState: "opening") state("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ffcc33", nextState: "stopping") state("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#bbbbdd", nextState: "stopping") - state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") - state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") - state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") } controlTile("mediumSlider", "device.level", "slider",decoration:"flat",height:2, width: 2, inactiveLabel: true) { state("level", action:"switch level.setLevel") @@ -87,7 +86,7 @@ metadata { preferences { input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, required: false, displayDuringSetup: true, range:"1..100" } - + main(["main"]) details(["windowShade", "mediumSlider", "contPause", "home", "version", "battery", "refresh"]) } @@ -118,31 +117,31 @@ private getMIN_WINDOW_COVERING_VERSION() {1093} //Custom command to increment blind position by 25 % def ShadesUp() { - def shadeValue = device.latestValue("shadeLevel") as Integer ?: device.latestValue("level") as Integer ?: 0 - + def shadeValue = device.latestValue("level") as Integer ?: 0 + if (shadeValue < 100) { shadeValue = Math.min(25 * (Math.round(shadeValue / 25) + 1), 100) as Integer } - else { + else { shadeValue = 100 } //sendEvent(name:"level", value:shadeValue, displayed:true) - setShadeLevel(shadeValue) + setLevel(shadeValue) //sendEvent(name: "windowShade", value: "opening") } //Custom command to decrement blind position by 25 % def ShadesDown() { - def shadeValue = device.latestValue("shadeLevel") as Integer ?: device.latestValue("level") as Integer ?: 0 - + def shadeValue = device.latestValue("level") as Integer ?: 0 + if (shadeValue > 0) { shadeValue = Math.max(25 * (Math.round(shadeValue / 25) - 1), 0) as Integer } - else { + else { shadeValue = 0 } //sendEvent(name:"level", value:shadeValue, displayed:true) - setShadeLevel(shadeValue) + setLevel(shadeValue) //sendEvent(name: "windowShade", value: "closing") } @@ -161,11 +160,11 @@ def stop() { } else { if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ - return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) + return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) } else { sendEvent(name: "windowShade", value: "stoppingNS") - return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000]) + return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000]) } } } @@ -177,39 +176,42 @@ def pause() { //Send Command through setLevel() def on() { log.info "on()" + sendEvent(name: "windowShade", value: "opening") sendEvent(name: "switch", value: "on") - open() + + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { + zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN) + } + else { + setLevel(100) + } } //Send Command through setLevel() def off() { log.info "off()" + sendEvent(name: "windowShade", value: "closing") sendEvent(name: "switch", value: "off") close() + //zigbee.off() } //Command to set the blind position (%) and log the event def setLevel(value, rate=null) { log.info "setLevel ($value)" - - setShadeLevel(value) -} - -def setShadeLevel(value) { - log.info "setShadeLevel ($value)" + Integer currentLevel = state.level - + def i = value as Integer - sendEvent(name:"level", value: value, unit:"%", displayed: false) - sendEvent(name:"shadeLevel", value: value, unit:"%", displayed:true) - + sendEvent(name:"level", value: value, displayed:true) + if ( i == 0) { sendEvent(name: "switch", value: "off") } else { sendEvent(name: "switch", value: "on") } - + if (i > currentLevel) { sendEvent(name: "windowShade", value: "opening") } @@ -217,7 +219,7 @@ def setShadeLevel(value) { sendEvent(name: "windowShade", value: "closing") } //setWindowShade(i) - + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ zigbee.command(CLUSTER_WINDOWCOVERING,WINDOWCOVERING_CMD_GOTOLIFTPERCENTAGE, zigbee.convertToHexString(100-i,2)) } @@ -234,8 +236,8 @@ def open() { zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN) } else { - setShadeLevel(100) - } + setLevel(100) + } } //Send Command through setLevel() def close() { @@ -245,13 +247,13 @@ def close() { zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_CLOSE) } else { - setShadeLevel(0) + setLevel(0) } } def presetPosition() { log.info "presetPosition()" - setShadeLevel(preset ?: state.preset ?: 50) + setLevel(preset ?: state.preset ?: 50) } //Reporting of Battery & position levels @@ -260,7 +262,7 @@ def ping(){ return refresh() } -//Set blind State based on position (which shows appropriate image) +//Set blind State based on position (which shows appropriate image) def setWindowShade(value) { if ((value>0)&&(value<99)){ sendEvent(name: "windowShade", value: "partially open", displayed:true) @@ -277,7 +279,7 @@ def setWindowShade(value) { def refresh() { log.debug "parse() refresh" def cmds_refresh = null - + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ cmds_refresh = zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) } @@ -285,24 +287,24 @@ def refresh() { cmds_refresh = zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) } - - cmds_refresh = cmds_refresh + + + cmds_refresh = cmds_refresh + zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) + zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) - + log.info "refresh() --- cmds: $cmds_refresh" - + return cmds_refresh } def getversion () { //state.currentVersion = 0 - sendEvent(name: "version", value: "Checking Version ... ") + sendEvent(name: "version", value: "Checking Version ... ") return zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) } //configure reporting -def configure() { +def configure() { state.currentVersion = 0 sendEvent(name: "windowShade", value: "unknown") log.debug "Configuring Reporting and Bindings." @@ -314,42 +316,33 @@ def configure() { zigbee.readAttribute(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE) + zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) + zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) - - def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) + + + def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE, 0x10, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, 0x20, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY, 0x20, 1, 3600, 0x01) - + log.info "configure() --- cmds: $cmds" return attrs_refresh + cmds } def parse(String description) { log.trace "parse() --- description: $description" - + Map map = [:] - if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { - sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") - } - def event = zigbee.getEvent(description) if (event && description?.startsWith('on/off')) { log.trace "sendEvent(event)" sendEvent(event) } - + else if ((description?.startsWith('read attr -')) || (description?.startsWith('attr report -'))) { map = parseReportAttributeMessage(description) def result = map ? createEvent(map) : null - - if (map.name == "level") { - result = [result, createEvent([name: "shadeLevel", value: map.value, unit: map.unit])] - } - log.debug "parse() --- returned: $result" return result - } + } } private Map parseReportAttributeMessage(String description) { @@ -373,27 +366,23 @@ private Map parseReportAttributeMessage(String description) { //Set icon based on device feedback for the open, closed, & partial configuration resultMap.value = levelValue state.level = levelValue - resultMap.unit = "%" - resultMap.displayed = false setWindowShade(levelValue) } else if (descMap.clusterInt == CLUSTER_LEVEL && descMap.attrInt == LEVEL_ATTR_LEVEL) { //log.debug "parse() --- returned level :$state.currentVersion " - def currentLevel = state.level - + def currentLevel = state.level + resultMap.name = "level" def levelValue = Math.round(Integer.parseInt(descMap.value, 16)) def levelValuePercent = Math.round((levelValue/255)*100) //Set icon based on device feedback for the open, closed, & partial configuration resultMap.value = levelValuePercent state.level = levelValuePercent - resultMap.unit = "%" - resultMap.displayed = false - + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { //Integer currentLevel = state.level - sendEvent(name:"level", value: levelValuePercent, unit: "%", displayed: false) - + sendEvent(name:"level", value: levelValuePercent, displayed:true) + if (levelValuePercent > currentLevel) { sendEvent(name: "windowShade", value: "opening") } else if (levelValuePercent < currentLevel) { @@ -407,21 +396,21 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.clusterInt == CLUSTER_BASIC && descMap.attrInt == BASIC_ATTR_SWBUILDID) { resultMap.name = "version" def versionString = descMap.value - + StringBuilder output = new StringBuilder("") StringBuilder output2 = new StringBuilder("") - + for (int i = 0; i < versionString.length(); i += 2) { String str = versionString.substring(i, i + 2) - output.append((char) (Integer.parseInt(str, 16))) + output.append((char) (Integer.parseInt(str, 16))) if (i > 19) { output2.append((char) (Integer.parseInt(str, 16))) } - } - + } + def current = Integer.parseInt(output2.toString()) state.currentVersion = current - resultMap.value = output.toString() + resultMap.value = output.toString() } else { log.debug "parseReportAttributeMessage() --- ignoring attribute" diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy index 7728e4078f0..22cd56f40f1 100644 --- a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -12,7 +12,6 @@ * for the specific language governing permissions and limitations under the License. * */ -import groovy.json.JsonOutput metadata { definition (name: "Qubino Flush Shutter", namespace: "qubino", author: "SmartThings", ocfDeviceType: "oic.d.blind", mcdSync: true) { @@ -93,7 +92,7 @@ def installed() { state.currentPreferencesState."$it.key".status = "synced" } // Preferences template end - sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) + sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) } def updated() { @@ -252,7 +251,7 @@ def setShadeLevel(level) { } def setSlats(level) { - def time = (int) (state.timeOfVenetianMovement * 1.1) + def time = (int) (state.timeOfVenetianMovement * 1.1) sendHubCommand([ encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 2), "delay ${time}", 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 48a0482e4e2..7af556576ba 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 @@ -15,285 +15,275 @@ import groovy.json.JsonOutput metadata { - definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { - capability "Window Shade" - capability "Window Shade Level" - capability "Window Shade Preset" - capability "Switch Level" - capability "Battery" - capability "Refresh" - capability "Health Check" - capability "Actuator" - capability "Sensor" - - command "stop" - - // This device handler is specifically for SWF window coverings - // - //fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade" - //fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade" - fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Springs Window Treatment" //Window Shade - fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Springs Window Treatment" //Roller Shade - } - - simulator { - status "open": "command: 2603, payload: FF" - status "closed": "command: 2603, payload: 00" - status "10%": "command: 2603, payload: 0A" - status "66%": "command: 2603, payload: 42" - status "99%": "command: 2603, payload: 63" - status "battery 100%": "command: 8003, payload: 64" - status "battery low": "command: 8003, payload: FF" - - // reply messages - reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" - reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" - reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" - reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" - } - - tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){ - tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" - attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" - attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" - attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#00A0DC", nextState:"partially open" - attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" - } - tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { - attributeState "shadeLevel", action:"setShadeLevel" - } - } - - standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { - state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" - } - - standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" - state "disabled", label:'', action:"", icon:"st.secondary.refresh" - } - - valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { - state "battery", label:'batt.', unit:"", - backgroundColors:[ - [value: 0, color: "#bc2323"], - [value: 6, color: "#44b621"] - ] - } - - preferences { - input "switchDirection", "bool", title: "Flip the orientation of the shade", defaultValue: false, required: false, displayDuringSetup: false - //input "preset", "number", title: "Default half-open position (1-100). Springs Window Fashions users should consult their manuals.", defaultValue: 50, required: false, displayDuringSetup: false - } - - main(["windowShade"]) - details(["windowShade", "home", "refresh", "battery"]) - - } + definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { + capability "Window Shade" + capability "Window Shade Preset" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Actuator" + capability "Sensor" + + command "stop" + + capability "Switch Level" // until we get a Window Shade Level capability + + // This device handler is specifically for SWF window coverings + // +// fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade" +// fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade" + fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Springs Window Treatment" //Window Shade + fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Springs Window Treatment" //Roller Shade + } + + simulator { + status "open": "command: 2603, payload: FF" + status "closed": "command: 2603, payload: 00" + status "10%": "command: 2603, payload: 0A" + status "66%": "command: 2603, payload: 42" + status "99%": "command: 2603, payload: 63" + status "battery 100%": "command: 8003, payload: 64" + status "battery low": "command: 8003, payload: FF" + + // reply messages + reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" + reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" + reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" + reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){ + tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" + attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" + attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" + attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open" + attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" + } + tileAttribute ("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action:"setLevel" + } + } + + standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { + state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" + } + + standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" + state "disabled", label:'', action:"", icon:"st.secondary.refresh" + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'batt.', unit:"", + backgroundColors:[ + [value: 0, color: "#bc2323"], + [value: 6, color: "#44b621"] + ] + } + + preferences { + input "switchDirection", "bool", title: "Flip the orientation of the shade", defaultValue: false, required: false, displayDuringSetup: false +// input "preset", "number", title: "Default half-open position (1-100). Springs Window Fashions users should consult their manuals.", defaultValue: 50, required: false, displayDuringSetup: false + } + + main(["windowShade"]) + details(["windowShade", "home", "refresh", "battery"]) + + } } def parse(String description) { - def result = null - - if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { - sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") - } - - //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) - // TODO: Workaround manual parsing of v4 multilevel report - def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value - if (cmd) { - result = zwaveEvent(cmd) - } - log.debug "Parsed '$description' to ${result.inspect()}" - return result + def result = null + //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) + // TODO: Workaround manual parsing of v4 multilevel report + def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug "Parsed '$description' to ${result.inspect()}" + return result } def getCheckInterval() { - // These are battery-powered devices, and it's not very critical - // to know whether they're online or not – 12 hrs - 4 * 60 * 60 + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + 4 * 60 * 60 } 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()) + 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()) } def updated() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - def cmds = [] - if (!device.latestState("battery")) { - cmds << zwave.batteryV1.batteryGet().format() - } - - if (!device.getDataValue("MSR")) { - cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() - } - - log.debug("Updated with settings $settings") - cmds << zwave.switchMultilevelV1.switchMultilevelGet().format() - response(cmds) + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + def cmds = [] + if (!device.latestState("battery")) { + cmds << zwave.batteryV1.batteryGet().format() + } + + if (!device.getDataValue("MSR")) { + cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() + } + + log.debug("Updated with settings $settings") + cmds << zwave.switchMultilevelV1.switchMultilevelGet().format() + response(cmds) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } private handleLevelReport(physicalgraph.zwave.Command cmd) { - def descriptionText = null - def shadeValue = null - - def level = cmd.value as Integer - level = switchDirection ? 99-level : level - if (level >= 99) { - level = 100 - shadeValue = "open" - } else if (level <= 0) { - level = 0 // unlike dimmer switches, the level isn't saved when closed - shadeValue = "closed" - } else { - shadeValue = "partially open" - descriptionText = "${device.displayName} shade is ${level}% open" - } - checkLevelReport(level) - - def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) - def shadeLevelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") - def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: shadeLevelEvent.isStateChange) - - def result = [stateEvent, shadeLevelEvent, levelEvent] - if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { - log.debug "requesting battery" - state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row - result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) - } - result + def descriptionText = null + def shadeValue = null + + def level = cmd.value as Integer + level = switchDirection ? 99-level : level + if (level >= 99) { + level = 100 + shadeValue = "open" + } else if (level <= 0) { + level = 0 // unlike dimmer switches, the level isn't saved when closed + shadeValue = "closed" + } else { + shadeValue = "partially open" + descriptionText = "${device.displayName} shade is ${level}% open" + } + checkLevelReport(level) + def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) + def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) + + def result = [stateEvent, levelEvent] + if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { + log.debug "requesting battery" + state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row + result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) + } + result } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { - [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), - response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] + [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { - def map = [ name: "battery", unit: "%" ] - if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { - map.value = 1 - map.descriptionText = "${device.displayName} has a low battery" - map.isStateChange = true - } else { - map.value = cmd.batteryLevel - } - state.lastbatt = now() - if (map.value <= 1 && device.latestValue("battery") != null && device.latestValue("battery") - map.value > 20) { - // Springs shades sometimes erroneously report a low battery when rapidly actuated manually. They'll still - // refuse to actuate after one of these reports, but this will limit the bad data that gets surfaced - log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" - } else { - createEvent(map) - } + def map = [ name: "battery", unit: "%" ] + if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { + map.value = 1 + map.descriptionText = "${device.displayName} has a low battery" + map.isStateChange = true + } else { + map.value = cmd.batteryLevel + } + state.lastbatt = now() + if (map.value <= 1 && device.latestValue("battery") != null && device.latestValue("battery") - map.value > 20) { + // Springs shades sometimes erroneously report a low battery when rapidly actuated manually. They'll still + // refuse to actuate after one of these reports, but this will limit the bad data that gets surfaced + log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" + } else { + createEvent(map) + } } def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { - // the docs we got said that the device would send a notification report, but we've determined that - // is not true + // the docs we got said that the device would send a notification report, but we've determined that + // is not true } def zwaveEvent(physicalgraph.zwave.Command cmd) { - log.debug "unhandled $cmd" - return [] + log.debug "unhandled $cmd" + return [] } def open() { - log.debug "open()" - - setShadeLevel(99) // Handle switchDirection in setShadeLevel + log.debug "open()" + def level = switchDirection ? 0 : 99 + levelChangeFollowUp(level) + zwave.basicV1.basicSet(value: level).format() + // zwave.basicV1.basicSet(value: 0xFF).format() } def close() { - log.debug "close()" - - setShadeLevel(0) // Handle switchDirection in setShadeLevel + log.debug "close()" + def level = switchDirection ? 99 : 0 + levelChangeFollowUp(level) + zwave.basicV1.basicSet(value: level).format() + //zwave.basicV1.basicSet(value: 0).format() } def setLevel(value, duration = null) { - log.debug "setLevel($value)" - - setShadeLevel(value) -} - -def setShadeLevel(value) { - Integer level = Math.max(Math.min(value as Integer, 99), 0) - - level = switchDirection ? 99-level : level - - log.debug "setShadeLevel($value) -> $level" - - levelChangeFollowUp(level) // Follow up in a few seconds to make sure the shades didn't "forget" to send us level updates - zwave.basicV1.basicSet(value: level).format() + log.debug "setLevel(${value.inspect()})" + Integer level = value as Integer + level = switchDirection ? 99-level : level + if (level < 0) level = 0 + if (level > 99) level = 99 + levelChangeFollowUp(level) + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { - zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() + zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() } def pause() { - log.debug "pause()" - stop() + log.debug "pause()" + stop() } def stop() { - log.debug "stop()" - zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() + log.debug "stop()" + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() } def ping() { - zwave.switchMultilevelV1.switchMultilevelGet().format() + zwave.switchMultilevelV1.switchMultilevelGet().format() } def refresh() { - log.debug "refresh()" - delayBetween([ - zwave.switchMultilevelV1.switchMultilevelGet().format(), - zwave.batteryV1.batteryGet().format() - ], 1500) + log.debug "refresh()" + delayBetween([ + zwave.switchMultilevelV1.switchMultilevelGet().format(), + zwave.batteryV1.batteryGet().format() + ], 1500) } def levelChangeFollowUp(expectedLevel) { - state.expectedValue = expectedLevel - state.levelChecks = 0 - runIn(5, "checkLevel", [overwrite: true]) + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) } def checkLevelReport(value) { - if (state.expectedValue != null) { - if ((state.expectedValue == 99 && value >= 99) || - (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { - unschedule("checkLevel") - } - } + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } } def checkLevel() { - if (state.levelChecks != null && state.levelChecks < 5) { - state.levelChecks = state.levelChecks + 1 - runIn(5, "checkLevel", [overwrite: true]) - sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) - } else { - unschedule("checkLevel") - } -} + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } +} \ No newline at end of file diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index f2445c53c9a..797ddd4f432 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -11,7 +11,6 @@ * 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 @@ -22,7 +21,6 @@ metadata { capability "Configuration" capability "Refresh" capability "Window Shade" - capability "Window Shade Level" capability "Window Shade Preset" capability "Health Check" capability "Switch Level" @@ -38,16 +36,13 @@ metadata { } tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label: 'Open', action: "close", icon: "st.shades.shade-open", backgroundColor: "#00A0DC", nextState: "closing" - attributeState "closed", label: 'Closed', action: "open", icon: "st.shades.shade-closed", backgroundColor: "#ffffff", nextState: "opening" - attributeState "partially open", label: 'Partially open', action: "close", icon: "st.shades.shade-open", backgroundColor: "#00A0DC", nextState: "closing" - attributeState "opening", label: 'Opening', action: "pause", icon: "st.shades.shade-opening", backgroundColor: "#00A0DC", nextState: "partially open" - attributeState "closing", label: 'Closing', action: "pause", icon: "st.shades.shade-closing", backgroundColor: "#ffffff", nextState: "partially open" - } - tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { - attributeState "shadeLevel", action:"setShadeLevel" + attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" } } standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { @@ -59,12 +54,18 @@ metadata { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } + valueTile("shadeLevel", "device.level", width: 4, height: 1) { + state "level", label: 'Shade is ${currentValue}% up', defaultState: true + } valueTile("batteryLevel", "device.battery", width: 2, height: 2) { state "battery", label:'${currentValue}% battery', unit:"" } + controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { + state "level", action:"switch level.setLevel" + } main "windowShade" - details(["windowShade", "contPause", "presetPosition", "refresh", "batteryLevel"]) + details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh", "batteryLevel"]) } } @@ -86,31 +87,27 @@ private List collectAttributes(Map descMap) { if (descMap.additionalAttrs) { descMaps.addAll(descMap.additionalAttrs) } - return descMaps } +def installed() { + log.debug "installed" + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) +} + // Parse incoming device messages to generate events def parse(String description) { log.debug "description:- ${description}" - - if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { - sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") - } - if (description?.startsWith("read attr -")) { Map descMap = zigbee.parseDescriptionAsMap(description) - if (isBindingTableMessage(description)) { parseBindingTableMessage(description) } else if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) { log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}" List descMaps = collectAttributes(descMap) def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT } - if (liftmap && liftmap.value) { def newLevel = zigbee.convertHexToInt(liftmap.value) - if (shouldInvertLiftPercentage()) { // some devices report % level of being closed (instead of % level of being opened) // inverting that logic is needed here to avoid a code duplication @@ -124,23 +121,18 @@ def parse(String description) { levelEventHandler(valueInt) } else if (reportsBatteryPercentage() && descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && zigbee.convertHexToInt(descMap?.attrId) == BATTERY_PERCENTAGE_REMAINING && descMap.value) { def batteryLevel = zigbee.convertHexToInt(descMap.value) - batteryPercentageEventHandler(batteryLevel) } } } def levelEventHandler(currentLevel) { - def lastLevel = device.currentValue("shadeLevel") ?: device.currentValue("level") // Try shadeLevel, if not use level and pass to logic below - + def lastLevel = device.currentValue("level") log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" - if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { - sendEvent(name: "shadeLevel", value: currentLevel, unit: "%") - sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) - + sendEvent(name: "level", value: currentLevel) if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { @@ -155,9 +147,8 @@ def levelEventHandler(currentLevel) { } def updateFinalState() { - def level = device.currentValue("shadeLevel") + def level = device.currentValue("level") log.debug "updateFinalState: ${level}" - if (level > 0 && level < 100) { sendEvent(name: "windowShade", value: "partially open") } @@ -172,40 +163,28 @@ def batteryPercentageEventHandler(batteryLevel) { def close() { log.info "close()" - - setShadeLevel(0) + setLevel(0) } def open() { log.info "open()" - - setShadeLevel(100) + setLevel(100) } -def setLevel(value, rate = null) { - log.info "setLevel($value)" - - setShadeLevel(value) -} - -def setShadeLevel(value) { - log.info "setShadeLevel($value)" - - Integer level = Math.max(Math.min(value as Integer, 100), 0) +def setLevel(data, rate = null) { + log.info "setLevel()" def cmd - if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { // some devices keeps % level of being closed (instead of % level of being opened) // inverting that logic is needed here - level = 100 - level + data = 100 - data } - cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(level, 2)) + cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2)) } else { - cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(level * 255 / 100), 2)) + cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) } - - return cmd + cmd } def pause() { @@ -218,7 +197,7 @@ def pause() { } def presetPosition() { - setShadeLevel(preset ?: 50) + setLevel(preset ?: 50) } /** @@ -231,32 +210,21 @@ def ping() { def refresh() { log.info "refresh()" def cmds - if (supportsLiftPercentage()) { cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT) } else { cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL) } - return cmds } -def installed() { - log.debug "installed" - - sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) -} - def configure() { - def cmds - - log.info "configure()" - // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) + log.info "configure()" sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - log.debug "Configuring Reporting and Bindings." + def cmds if (supportsLiftPercentage()) { cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 2, 600, null) } else { @@ -280,7 +248,6 @@ def usesLocalGroupBinding() { private def parseBindingTableMessage(description) { Integer groupAddr = getGroupAddrFromBindingTable(description) - if (groupAddr) { List cmds = addHubToGroup(groupAddr) cmds?.collect { new physicalgraph.device.HubAction(it) } @@ -291,9 +258,7 @@ private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) def groupEntry = btr?.table_entries?.find { it.dstAddrMode == 1 } - log.info "Found ${groupEntry}" - !groupEntry?.dstAddr ?: Integer.parseInt(groupEntry.dstAddr, 16) } 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 7ad3527fa07..98f1a878f1b 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -22,7 +22,6 @@ metadata { capability "Configuration" capability "Refresh" capability "Window Shade" - capability "Window Shade Level" capability "Window Shade Preset" capability "Health Check" capability "Switch Level" @@ -42,7 +41,7 @@ metadata { } tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" @@ -50,9 +49,6 @@ metadata { attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" } - tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { - attributeState "shadeLevel", action:"setShadeLevel" - } } standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc" @@ -63,9 +59,15 @@ metadata { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } + valueTile("shadeLevel", "device.level", width: 4, height: 1) { + state "level", label: 'Shade is ${currentValue}% up', defaultState: true + } + controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { + state "level", action:"switch level.setLevel" + } main "windowShade" - details(["windowShade", "contPause", "presetPosition", "refresh"]) + details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh"]) } } @@ -87,31 +89,22 @@ private List collectAttributes(Map descMap) { if (descMap.additionalAttrs) { descMaps.addAll(descMap.additionalAttrs) } - return descMaps } // Parse incoming device messages to generate events def parse(String description) { log.debug "description:- ${description}" - - if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { - sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") - } - if (description?.startsWith("read attr -")) { Map descMap = zigbee.parseDescriptionAsMap(description) - if (isBindingTableMessage(description)) { parseBindingTableMessage(description) } else if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) { log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}" List descMaps = collectAttributes(descMap) def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT } - if (liftmap && liftmap.value) { def newLevel = zigbee.convertHexToInt(liftmap.value) - if (shouldInvertLiftPercentage()) { // some devices report % level of being closed (instead of % level of being opened) // inverting that logic is needed here to avoid a code duplication @@ -125,23 +118,18 @@ def parse(String description) { levelEventHandler(valueInt) } else if (reportsBatteryPercentage() && descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && zigbee.convertHexToInt(descMap?.attrId) == BATTERY_PERCENTAGE_REMAINING && descMap.value) { def batteryLevel = zigbee.convertHexToInt(descMap.value) - batteryPercentageEventHandler(batteryLevel) } } } def levelEventHandler(currentLevel) { - def lastLevel = device.currentValue("shadeLevel") ?: device.currentValue("level") // Try shadeLevel, if not use level and pass to logic below - + def lastLevel = device.currentValue("level") log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" - if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { - sendEvent(name: "shadeLevel", value: currentLevel, unit: "%") - sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) - + sendEvent(name: "level", value: currentLevel) if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { @@ -156,9 +144,8 @@ def levelEventHandler(currentLevel) { } def updateFinalState() { - def level = device.currentValue("shadeLevel") + def level = device.currentValue("level") log.debug "updateFinalState: ${level}" - if (level > 0 && level < 100) { sendEvent(name: "windowShade", value: "partially open") } @@ -171,6 +158,10 @@ def batteryPercentageEventHandler(batteryLevel) { } } +def supportsLiftPercentage() { + device.getDataValue("manufacturer") != "Feibit Co.Ltd" +} + def close() { log.info "close()" zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_CLOSE) @@ -181,29 +172,19 @@ def open() { zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_OPEN) } -def setLevel(value, rate = null) { - log.info "setLevel($value)" - - setShadeLevel(value) -} - -def setShadeLevel(value) { - log.info "setShadeLevel($value)" - - Integer level = Math.max(Math.min(value as Integer, 100), 0) +def setLevel(data, rate = null) { + log.info "setLevel()" def cmd - if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { // some devices keeps % level of being closed (instead of % level of being opened) // inverting that logic is needed here - level = 100 - level + data = 100 - data } - cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(level, 2)) + cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2)) } else { - cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(level * 255 / 100), 2)) + cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) } - return cmd } @@ -213,7 +194,7 @@ def pause() { } def presetPosition() { - setShadeLevel(preset ?: 50) + setLevel(preset ?: 50) } /** @@ -226,32 +207,25 @@ def ping() { def refresh() { log.info "refresh()" def cmds - if (supportsLiftPercentage()) { cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT) } else { cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL) } - return cmds } def installed() { - log.debug "installed" - sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) } def configure() { - def cmds - - log.info "configure()" - // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) + log.info "configure()" sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - log.debug "Configuring Reporting and Bindings." + def cmds if (supportsLiftPercentage()) { cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 0, 600, null) } else { @@ -275,7 +249,6 @@ def usesLocalGroupBinding() { private def parseBindingTableMessage(description) { Integer groupAddr = getGroupAddrFromBindingTable(description) - if (groupAddr) { List cmds = addHubToGroup(groupAddr) cmds?.collect { new physicalgraph.device.HubAction(it) } @@ -286,9 +259,7 @@ private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) def groupEntry = btr?.table_entries?.find { it.dstAddrMode == 1 } - log.info "Found ${groupEntry}" - !groupEntry?.dstAddr ?: Integer.parseInt(groupEntry.dstAddr, 16) } @@ -300,10 +271,6 @@ private List readDeviceBindingTable() { ["zdo mgmt-bind 0x${device.deviceNetworkId} 0", "delay 200"] } -def supportsLiftPercentage() { - device.getDataValue("manufacturer") != "Feibit Co.Ltd" -} - def shouldInvertLiftPercentage() { return isIkeaKadrilj() || isIkeaFyrtur() || isSomfyGlydea() } 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 67554aa4a70..c43694d37e9 100644 --- a/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy +++ b/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy @@ -15,262 +15,252 @@ import groovy.json.JsonOutput metadata { - definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { - capability "Window Shade" - capability "Window Shade Level" - capability "Window Shade Preset" - capability "Switch Level" - capability "Battery" - capability "Refresh" - capability "Health Check" - capability "Actuator" - capability "Sensor" - - command "stop" - - // This device handler is specifically for non-SWF position-aware window coverings - // - fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade - fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade -// fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds" -// fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade" - } - - simulator { - status "open": "command: 2603, payload: FF" - status "closed": "command: 2603, payload: 00" - status "10%": "command: 2603, payload: 0A" - status "66%": "command: 2603, payload: 42" - status "99%": "command: 2603, payload: 63" - status "battery 100%": "command: 8003, payload: 64" - status "battery low": "command: 8003, payload: FF" - - // reply messages - reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" - reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" - reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" - reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" - } - - tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4){ - tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" - attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" - attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" - attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#00A0DC", nextState:"partially open" - attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" - } - tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { - attributeState "shadeLevel", action:"setShadeLevel" - } - } - - standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { - state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" - } - - standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" - state "disabled", label:'', action:"", icon:"st.secondary.refresh" - } - - valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { - state "battery", label:'${currentValue}% battery', unit:"" - } - - preferences { - input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false - } - - main(["windowShade"]) - details(["windowShade", "home", "refresh", "battery"]) - - } + definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { + capability "Window Shade" + capability "Window Shade Preset" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Actuator" + capability "Sensor" + + command "stop" + + capability "Switch Level" // until we get a Window Shade Level capability + + // This device handler is specifically for non-SWF position-aware window coverings + // + fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade + fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade +// fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds" +// fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade" + } + + simulator { + status "open": "command: 2603, payload: FF" + status "closed": "command: 2603, payload: 00" + status "10%": "command: 2603, payload: 0A" + status "66%": "command: 2603, payload: 42" + status "99%": "command: 2603, payload: 63" + status "battery 100%": "command: 8003, payload: 64" + status "battery low": "command: 8003, payload: FF" + + // reply messages + reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" + reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" + reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" + reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4){ + tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" + attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" + attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" + attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open" + attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" + } + tileAttribute ("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action:"setLevel" + } + } + + standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { + state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" + } + + standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" + state "disabled", label:'', action:"", icon:"st.secondary.refresh" + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + preferences { + input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false + } + + main(["windowShade"]) + details(["windowShade", "home", "refresh", "battery"]) + + } } def parse(String description) { - def result = null - - if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { - sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") - } - - //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) - // TODO: Workaround manual parsing of v4 multilevel report - def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value - if (cmd) { - result = zwaveEvent(cmd) - } - log.debug "Parsed '$description' to ${result.inspect()}" - return result + def result = null + //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) + // TODO: Workaround manual parsing of v4 multilevel report + def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug "Parsed '$description' to ${result.inspect()}" + return result } def getCheckInterval() { - // These are battery-powered devices, and it's not very critical - // to know whether they're online or not – 12 hrs - 4 * 60 * 60 + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + 4 * 60 * 60 } 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()) + 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()) } def updated() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - if (!device.latestState("battery")) { - response(zwave.batteryV1.batteryGet()) - } + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + if (!device.latestState("battery")) { + response(zwave.batteryV1.batteryGet()) + } } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } private handleLevelReport(physicalgraph.zwave.Command cmd) { - def descriptionText = null - def shadeValue = null - - def level = cmd.value as Integer - if (level >= 99) { - level = 100 - shadeValue = "open" - } else if (level <= 0) { - level = 0 // unlike dimmer switches, the level isn't saved when closed - shadeValue = "closed" - } else { - shadeValue = "partially open" - descriptionText = "${device.displayName} shade is ${level}% open" - } - checkLevelReport(level) - - def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) - def shadeLevelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") - def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: shadeLevelEvent.isStateChange) - - def result = [stateEvent, shadeLevelEvent, levelEvent] - if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { - log.debug "requesting battery" - state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row - result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) - } - result + def descriptionText = null + def shadeValue = null + + def level = cmd.value as Integer + if (level >= 99) { + level = 100 + shadeValue = "open" + } else if (level <= 0) { + level = 0 // unlike dimmer switches, the level isn't saved when closed + shadeValue = "closed" + } else { + shadeValue = "partially open" + descriptionText = "${device.displayName} shade is ${level}% open" + } + checkLevelReport(level) + def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) + def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) + + def result = [stateEvent, levelEvent] + if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { + log.debug "requesting battery" + state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row + result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) + } + result } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { - [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), - response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] + [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } 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 - } - - state.lastbatt = now() - - createEvent(map) + 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() + createEvent(map) } def zwaveEvent(physicalgraph.zwave.Command cmd) { - log.debug "unhandled $cmd" - return [] + log.debug "unhandled $cmd" + return [] } def open() { - log.debug "open()" - - setShadeLevel(99) + levelChangeFollowUp(99) + log.debug "open()" + /*delayBetween([ + zwave.basicV1.basicSet(value: 0xFF).format(), + zwave.switchMultilevelV1.switchMultilevelGet().format() + ], 1000)*/ + zwave.basicV1.basicSet(value: 99).format() } def close() { - log.debug "close()" - - setShadeLevel(0) + levelChangeFollowUp(0) + log.debug "close()" + /*delayBetween([ + zwave.basicV1.basicSet(value: 0x00).format(), + zwave.switchMultilevelV1.switchMultilevelGet().format() + ], 1000)*/ + zwave.basicV1.basicSet(value: 0).format() } def setLevel(value, duration = null) { - log.debug "setLevel($value)" - - setShadeLevel(value) -} - -def setShadeLevel(value) { - Integer level = Math.max(Math.min(value as Integer, 99), 0) - - log.debug "setShadeLevel($value) -> $level" - - levelChangeFollowUp(level) // Follow up in a few seconds to make sure the shades didn't "forget" to send us level updates - zwave.basicV1.basicSet(value: level).format() + log.debug "setLevel(${value.inspect()})" + Integer level = value as Integer + if (level < 0) level = 0 + if (level > 99) level = 99 + levelChangeFollowUp(level) + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { - setLevel(preset ?: state.preset ?: 50) + setLevel(preset ?: state.preset ?: 50) } def pause() { - log.debug "pause()" - - stop() + log.debug "pause()" + stop() } def stop() { - log.debug "stop()" - - zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() + log.debug "stop()" + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() } def ping() { - zwave.switchMultilevelV1.switchMultilevelGet().format() + zwave.switchMultilevelV1.switchMultilevelGet().format() } def refresh() { - log.debug "refresh()" - delayBetween([ - zwave.switchMultilevelV1.switchMultilevelGet().format(), - zwave.batteryV1.batteryGet().format() - ], 1500) + log.debug "refresh()" + delayBetween([ + zwave.switchMultilevelV1.switchMultilevelGet().format(), + zwave.batteryV1.batteryGet().format() + ], 1500) } def levelChangeFollowUp(expectedLevel) { - state.expectedValue = expectedLevel - state.levelChecks = 0 - runIn(5, "checkLevel", [overwrite: true]) + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) } def checkLevelReport(value) { - if (state.expectedValue != null) { - if ((state.expectedValue == 99 && value >= 99) || - (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { - unschedule("checkLevel") - } - } + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } } def checkLevel() { - if (state.levelChecks != null && state.levelChecks < 5) { - state.levelChecks = state.levelChecks + 1 - runIn(5, "checkLevel", [overwrite: true]) - sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) - } else { - unschedule("checkLevel") - } + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } } From 84469ef4e5f8b02ffce2f7db04728e13d2cd300c Mon Sep 17 00:00:00 2001 From: "k.klimczuk2" Date: Wed, 19 Aug 2020 11:24:32 +0200 Subject: [PATCH 041/422] WWST-7064 - Fingerprint for Innr EU Smart Plug SP 220 --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index c7789983b16..84b8add3add 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -53,6 +53,7 @@ metadata { fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, FFFF", outClusters: "0019", manufacturer: "MEGAMAN", model: "BSZTM005", deviceJoinName: "INGENIUM Switch" //INGENIUM ZB Mains Switching Module // Innr + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 220", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 222", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 224", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug From 6eb1d3071f45d77c8d03585d1d4eb5e594a7f8e5 Mon Sep 17 00:00:00 2001 From: "k.klimczuk2" Date: Wed, 19 Aug 2020 11:29:36 +0200 Subject: [PATCH 042/422] WWST-7065 - Fingerprint for Innr Smart Color Bulb E26 AE 280 C --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 87e0ffab340..9b2ce34265e 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -58,6 +58,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", manufacturer: "innr", model: "BY 285 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Smart Bulb Color fingerprint manufacturer: "innr", model: "RB 250 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Smart Candle Colour fingerprint manufacturer: "innr", model: "RS 230 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Smart GU10 Spot Colour + fingerprint manufacturer: "innr", model: "AE 280 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Smart Color Bulb E26 AE 280 C // Müller Licht fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B05, 1000, FEDC", outClusters: "000A, 0019", manufacturer: "MLI", model: "ZBT-ExtendedColor", deviceJoinName: "Tint Light", mnmn:"SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Müller Licht Bulb White+Color From 952eed4eb2b01552ee8b1695638b7a089e12ea04 Mon Sep 17 00:00:00 2001 From: "k.klimczuk2" Date: Wed, 19 Aug 2020 11:32:17 +0200 Subject: [PATCH 043/422] WWST-7066, WWST-7067, WWST-7068 - Fingerprints for Innr Smart Outdoor Flex Light 2m Colour OFL 120 C, Innr Smart Outdoor Flex Light 4m Colour OFL 140 C, Innr Smart Outdoor Spot Light Colour OSL 130 C --- devicetypes/smartthings/zll-rgbw-bulb.src/zll-rgbw-bulb.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zll-rgbw-bulb.src/zll-rgbw-bulb.groovy b/devicetypes/smartthings/zll-rgbw-bulb.src/zll-rgbw-bulb.groovy index 8005ee4dafd..e3cb42aa128 100644 --- a/devicetypes/smartthings/zll-rgbw-bulb.src/zll-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zll-rgbw-bulb.src/zll-rgbw-bulb.groovy @@ -44,6 +44,9 @@ metadata { // Innr fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "innr", model: "RB 185 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" //Innr Smart Bulb Color fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "innr", model: "FL 130 C", deviceJoinName: "Innr Light" //Innr Flex Light Color + fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "innr", model: "OFL 120 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Outdoor Flex Light Colour 2m + fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "innr", model: "OFL 140 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Outdoor Flex Light Colour 4m + fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "innr", model: "OSL 130 C", deviceJoinName: "Innr Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-1800K-6500K" //Innr Smart Outdoor Spot Light Colour OSL 130 C // OSRAM fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", "manufacturer":"OSRAM", "model":"Classic A60 RGBW", deviceJoinName: "OSRAM Light" //OSRAM SMART+ LED Classic A60 RGBW From 1c9918d79ecfa09d2c07b2ef27d8c973a6dd4f96 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Wed, 19 Aug 2020 21:51:24 +0200 Subject: [PATCH 044/422] [ICP-13444] Delete child switches on Flush Relay 1/1D (#40329) * Delete child switches on Flush Relay 1/1D --- .../qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 9a1d7e87193..b6b799ee961 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -78,7 +78,7 @@ def installed() { state.numberOfSwitches = 1 } - if (!childDevices) { + if (!childDevices && state.numberOfSwitches > 1) { addChildSwitches(state.numberOfSwitches) } sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) @@ -93,12 +93,12 @@ def installed() { } // Preferences template end response([ - refresh((1..state.numberOfSwitches).toList()) + refresh((1..state.numberOfSwitches).toList()) ]) } def updated() { - if (!childDevices) { + if (!childDevices && state.numberOfSwitches > 1) { addChildSwitches(state.numberOfSwitches) } // Preferences template begin From 77f03307da93a755d9f5de99de3ccae813ac74c9 Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Thu, 20 Aug 2020 12:52:13 -0700 Subject: [PATCH 045/422] Aeon hem gen5 association fix and Configuration update (#40990) * Resolve association issue and updated Parameters Resolved association Group 1 association to Node ID 01, ensures HEM Gen5 is properly associated - Line 142 (delete node association) - Line 143 (reassign SmartThings to Association Group 1) Parameter settings (Line 162 - 167) to report kWh and Watt total every 300 seconds. Selective reporting disabled - Param 101 = 3 - Param 102 = 0 - Param 103 = 0 - Param 111 = 300 - Param 90 = 0 - Param 13 = 0 * Change definition name back to default Line 20 --- .../aeon-home-energy-meter.groovy | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy b/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy index 7ac225567a8..9cd3d420225 100644 --- a/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy +++ b/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy @@ -139,6 +139,8 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def refresh() { log.debug "refresh()..." delayBetween([ + encap(zwave.associationV2.associationRemove(groupingIdentifier: 1, nodeId:[])), // Refresh Node ID in Group 1 + encap(zwave.associationV2.associationSet(groupingIdentifier: 1, nodeId:zwaveHubNodeId)), //Assign Node ID of SmartThings to Group 1 encap(zwave.meterV2.meterGet(scale: 0)), encap(zwave.meterV2.meterGet(scale: 2)) ]) @@ -157,13 +159,12 @@ def configure() { log.debug "configure()..." if (isAeotecHomeEnergyMeter()) delayBetween([ - encap(zwave.configurationV1.configurationSet(parameterNumber: 255, size: 4, scaledConfigurationValue: 1)), // Reset the device to the default settings - encap(zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 1)), // report power in Watts... + encap(zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 3)), // report total power in Watts and total energy in kWh... + encap(zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 0)), // disable group 2... + encap(zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0)), // disable group 3... encap(zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300)), // ...every 5 min - encap(zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 2)), // report energy in kWh... - encap(zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 300)), // ...every 5 min - zwave.configurationV1.configurationSet(parameterNumber: 90, size: 1, scaledConfigurationValue: 1).format(), // enabling automatic reports... - zwave.configurationV1.configurationSet(parameterNumber: 91, size: 2, scaledConfigurationValue: 10).format() // ...every 10W change + encap(zwave.configurationV1.configurationSet(parameterNumber: 90, size: 1, scaledConfigurationValue: 0)), // enabling automatic reports, disabled selective reporting... + encap(zwave.configurationV1.configurationSet(parameterNumber: 13, size: 1, scaledConfigurationValue: 0)) //disable CRC16 encapsulation ], 500) else if (isQubinoSmartMeter()) delayBetween([ @@ -213,4 +214,4 @@ private isAeotecHomeEnergyMeter() { private isQubinoSmartMeter() { zwaveInfo.model.equals("0052") -} \ No newline at end of file +} From 5075c3ea9803f56869f7d8727b2c64be73a98532 Mon Sep 17 00:00:00 2001 From: "Vincent G. Beauregrad" Date: Mon, 24 Aug 2020 11:01:25 -0400 Subject: [PATCH 046/422] adding vid and mnmn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adding vid and mnmn that corrects translation problems to french adding new Sinopé valve device VA4220ZB @shrakuma, could you please have a look at this merge request Thank you --- .../va4200wz-va4200zb-sinope-valve.groovy | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy index da0602ea1cd..1cc0172b0e3 100644 --- a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy +++ b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy @@ -1,6 +1,6 @@ /** Copyright Sinopé Technologies -1..0 +1.3.0 SVN-571 * 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. @@ -25,8 +25,9 @@ metadata { capability "Power Source" capability "Health Check" - fingerprint manufacturer: "Sinope Technologies", model: "VA4200WZ", deviceJoinName: "Sinope Valve" //VA4200WZ - fingerprint manufacturer: "Sinope Technologies", model: "VA4200ZB", deviceJoinName: "Sinope Valve" //VA4200ZB + fingerprint manufacturer: "Sinope Technologies", model: "VA4200WZ", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4200WZ + fingerprint manufacturer: "Sinope Technologies", model: "VA4200ZB", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4200ZB + fingerprint manufacturer: "Sinope Technologies", model: "VA4220ZB", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4220ZB } tiles(scale: 2) { From 7b0d0461bd547fbbdce15ca4e0b0dbd40248cf96 Mon Sep 17 00:00:00 2001 From: ZWozniakS <48519140+ZWozniakS@users.noreply.github.com> Date: Tue, 25 Aug 2020 19:38:18 +0200 Subject: [PATCH 047/422] [ICP-13278] Added preferences for Aeotec Nano Dimmer to adjusting the dimmer level (#37125) * Added preferences for Aeotec Nano Dimmer to adjusting the dimmer level * Added requested changes * Changed name of the method * Fix * Updated the description of the needed preference and removed unnecessary one * Fix Co-authored-by: Zuzanna Wozniak/Home IoT Development (IoT) /SRPOL/Engineer/Samsung Electronics --- .../zwave-metering-dimmer.groovy | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index 430838072b5..945ab48af3c 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -94,6 +94,24 @@ metadata { main(["switch","power","energy"]) details(["switch", "power", "energy", "refresh", "reset"]) + + preferences { + section { + input( + title: "Settings Available For Aeotec Nano Dimmer Only", + type: "paragraph", + element: "paragraph" + ) + input( + title: "Set the MIN brightness level (Aeotec Nano Dimmer Only):", + description: "This may need to be adjusted for bulbs that are not dimming properly.", + name: "minDimmingLevel", + type: "number", + range: "0..99", + defaultValue: 0 + ) + } + } } def getCommandClassVersions() { @@ -114,7 +132,15 @@ def installed() { 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()) + + def results = [] + results << refresh() + + if (isAeotecNanoDimmer()) { + results << getAeotecNanoDimmerConfigurationCommands() + } + + response(results) } // parse events into attributes @@ -229,8 +255,14 @@ def setLevel(level, rate = null) { def configure() { log.debug "configure()" + def result = [] + if (isAeotecNanoDimmer()) { + state.configured = false + result << response(getAeotecNanoDimmerConfigurationCommands()) + } + log.debug "Configure zwaveInfo: "+zwaveInfo if (zwaveInfo.mfr == "0086") { // Aeon Labs meter @@ -270,6 +302,36 @@ def normalizeLevel(level) { level == 99 ? 100 : level } +def getAeotecNanoDimmerConfigurationCommands() { + def result = [] + Integer minDimmingLevel = (settings.minDimmingLevel as Integer) ?: 0 // default value (parameter 131) for Aeotec Nano Dimmer + + if (!state.minDimmingLevel) { + state.minDimmingLevel = 0 // default value (parameter 131) for Aeotec Nano Dimmer + } + + if (!state.configured || (minDimmingLevel != state.minDimmingLevel)) { + state.configured = false // this flag needs to be set to false when settings are changed (and the device was initially configured before) + result << encap(zwave.configurationV1.configurationSet(parameterNumber: 131, size: 1, scaledConfigurationValue: minDimmingLevel)) + result << encap(zwave.configurationV1.configurationGet(parameterNumber: 131)) + } + + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + if (isAeotecNanoDimmer()) { + if (cmd.parameterNumber == 131) { + state.minDimmingLevel = cmd.scaledConfigurationValue + state.configured = true + } + + log.debug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd.configurationValue}'" + } + + return [:] +} + /* * Security encapsulation support: */ @@ -319,3 +381,7 @@ private encap(physicalgraph.zwave.Command cmd) { private encapSequence(cmds, Integer delay=250) { delayBetween(cmds.collect{ encap(it) }, delay) } + +private isAeotecNanoDimmer() { + zwaveInfo?.mfr?.equals("0086") && zwaveInfo?.model?.equals("006F") +} From 04a3c16bb4f1da0eeb01bc5971ecc8d43301a5f4 Mon Sep 17 00:00:00 2001 From: Juhaki Park <37175353+juhaki@users.noreply.github.com> Date: Wed, 26 Aug 2020 02:41:21 +0900 Subject: [PATCH 048/422] [ICP-13555] EZEX power meter patch. (#41807) [ICP-13555] EZEX power meter patch. --- .../zigbee-power-meter.src/zigbee-power-meter.groovy | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy index 860d517cf6e..d86972d89a4 100755 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -52,8 +52,13 @@ def parse(String description) { if (event) { log.info event if (event.name == "power") { - event.value = event.value/1000 - event.unit = "W" + if (event.cluster == 0x0B04 && event.attrId == 0x050b) { + event.value = event.value + event.unit = "W" + } else { + event.value = event.value/1000 + event.unit = "W" + } } else if (event.name == "energy") { event.value = event.value/1000000 event.unit = "kWh" From 2eac88062f57944029dbcfdc364bafba2f6b2cee Mon Sep 17 00:00:00 2001 From: Juhaki Park <37175353+juhaki@users.noreply.github.com> Date: Wed, 26 Aug 2020 11:23:18 +0900 Subject: [PATCH 049/422] [ICP-13555] EZEX power meter patch #2. [ICP-13555] EZEX power meter patch #2. --- .../zigbee-power-meter.src/zigbee-power-meter.groovy | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy index d86972d89a4..99b9b56c318 100755 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -52,8 +52,10 @@ def parse(String description) { if (event) { log.info event if (event.name == "power") { - if (event.cluster == 0x0B04 && event.attrId == 0x050b) { - event.value = event.value + def descMap = zigbee.parseDescriptionAsMap(description) + log.debug "event : Desc Map: $descMap" + if (descMap.clusterInt == 0x0B04 && descMap.attrInt == 0x050b) { + event.value = event.value/10 event.unit = "W" } else { event.value = event.value/1000 @@ -82,6 +84,12 @@ def parse(String description) { map.value = zigbee.convertHexToInt(it.value)/1000 map.unit = "W" } + if (it.clusterInt == 0x0B04 && it.attrInt == 0x050b) { + log.debug "meter" + map.name = "power" + map.value = zigbee.convertHexToInt(it.value)/10 + map.unit = "W" + } if (it.clusterInt == 0x0702 && it.attrInt == 0x0000) { log.debug "energy" map.name = "energy" From 8cd38bd83adc60e9fc91bd891be926a0dbc561aa Mon Sep 17 00:00:00 2001 From: "Bartlomiej Janusz/Home IoT Development (IoT) /SRPOL/Associate/Samsung Electronics" Date: Thu, 27 Aug 2020 11:12:49 +0200 Subject: [PATCH 050/422] Somfy Sonesse fingerprint --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 98f1a878f1b..e8f4e28e40a 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -34,6 +34,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "KG0001", deviceJoinName: "Window Treatment" //Smart Curtain Motor(BCM300D) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "DY0010", deviceJoinName: "Window Treatment" //Smart Curtain Motor(DT82TV) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Somfy", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack } preferences { @@ -272,7 +273,7 @@ private List readDeviceBindingTable() { } def shouldInvertLiftPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() || isSomfyGlydea() + return isIkeaKadrilj() || isIkeaFyrtur() || isSomfyGlydea() || isSomfySonesse() } def reportsBatteryPercentage() { @@ -289,4 +290,9 @@ def isIkeaFyrtur() { def isSomfyGlydea() { device.getDataValue("model") == "Glydea Somfy" +} + +def isSomfySonesse() { + // the default model name can be changed by the user from the Somfy Set&Go bluetooth app. + device.getDataValue("model") == "Roller" } \ No newline at end of file From 5ea86c42f4f41512b5a2e11ff3c1dc387da859d7 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Thu, 27 Aug 2020 20:52:21 +0200 Subject: [PATCH 051/422] [WWST-6729] DTH for Inovelli Dimmer LZW31-SN (#37229) * Added fingerprint for Inovelli Dimmer LZW31-SN * WWST-6729 Added mcd dth for Inovelli Dimmer LZW31-SN * removed old reference to Inovelli Dimmer * Changed child dth name, fixed formatting, removed unnecessary capabilities, fixed child dni * Added configure method, fixed parameters range, fixed formatting * Removed unecessary parameters * Deleted LED Bar color preference from settings view, shortened delay between commands * Level event is created on every level change * Deleted minHubCoreVersion added mnmn * Added child buttons * Changed button event from up/down to pushed, button labels and supported values. Deleted unnecessary code. * Fixed energy rounding --- .../child-color-control.groovy | 37 ++ .../inovelli-dimmer.groovy | 578 ++++++++++++++++++ 2 files changed, 615 insertions(+) create mode 100644 devicetypes/smartthings/child-color-control.src/child-color-control.groovy create mode 100644 devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy diff --git a/devicetypes/smartthings/child-color-control.src/child-color-control.groovy b/devicetypes/smartthings/child-color-control.src/child-color-control.groovy new file mode 100644 index 00000000000..38d90a05d88 --- /dev/null +++ b/devicetypes/smartthings/child-color-control.src/child-color-control.groovy @@ -0,0 +1,37 @@ +/* Copyright 2020 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. +* +* Child Color Selection +* +* Copyright 2020 SmartThings +* +*/ +metadata { + definition(name: "Child Color Control", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings") { + capability "Color Control" + capability "Actuator" + } + + tiles(scale: 2){ + multiAttributeTile(name:"switch", type: "generic", width: 6, height: 4, canChangeIcon: true){ + tileAttribute ("device.color", key: "COLOR_CONTROL") { + attributeState "color", action:"setColor" + } + } + + main(["switch"]) + details(["switch"]) + } +} + +def setColor(value) { + parent.childSetColor(value) +} diff --git a/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy b/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy new file mode 100644 index 00000000000..f0ca4278d04 --- /dev/null +++ b/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy @@ -0,0 +1,578 @@ +/* Copyright 2020 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. +* +* Inovelli Dimmer +* +* Copyright 2020 SmartThings +* +*/ +metadata { + definition(name: "Inovelli Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch", mcdSync: true) { + capability "Actuator" + capability "Configuration" + capability "Energy Meter" + capability "Health Check" + capability "Refresh" + capability "Sensor" + capability "Switch" + capability "Switch Level" + capability "Power Meter" + + fingerprint mfr: "031E", prod: "0001", model: "0001", deviceJoinName: "Inovelli Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Inovelli_Dimmer" //Inovelli Dimmer LZW31-SN + } + + tiles(scale: 2) { + multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) { + tileAttribute("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff" + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" + attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff" + attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" + } + tileAttribute("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action: "switch level.setLevel" + } + } + valueTile("power", "device.power", width: 2, height: 2) { + state "default", label: '${currentValue} W' + } + valueTile("energy", "device.energy", width: 2, height: 2) { + state "default", label: '${currentValue} kWh' + } + standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh" + } + } + + main(["switch", "power", "energy"]) + details(["switch", "power", "energy", "refresh"]) + + preferences { + // Preferences template begin + parameterMap.each { + input(title: it.name, description: it.description, type: "paragraph", element: "paragraph") + + switch (it.type) { + case "boolRange": + input( + name: it.key + "Boolean", type: "bool", title: "Enable", description: "If you disable this option, it will overwrite setting below.", + defaultValue: it.defaultValue != it.disableValue, required: false + ) + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + case "boolean": + input( + type: "paragraph", element: "paragraph", + description: "Option enabled: ${it.activeDescription}\n" + "Option disabled: ${it.inactiveDescription}" + ) + input( + name: it.key, type: "bool", title: "Enable", + defaultValue: it.defaultValue == it.activeOption, required: false + ) + break + case "enum": + input( + name: it.key, title: "Select", type: "enum", + options: it.values, defaultValue: it.defaultValue, required: false + ) + break + case "range": + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + } + } + // Preferences template end + } +} + +private getUP_BUTTON(){ 1 } +private getDOWN_BUTTON(){ 2 } +private getCONFIGURATION_BUTTON(){ 3 } + +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]) + + // Preferences template begin + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + if (it.type == "boolRange" && getPreferenceValue(it) == it.disableValue) { + state.currentPreferencesState."$it.key".status = "disablePending" + } else { + state.currentPreferencesState."$it.key".status = "synced" + } + } +// Preferences template end + createChildButtonDevices() + def value = ['pushed', 'pushed_2x', 'pushed_3x', 'pushed_4x', 'pushed_5x'].encodeAsJson() + sendEvent(name: "supportedButtonValues", value: value) + sendEvent(name: "numberOfButtons", value: 3, displayed: true) + createChildDevice("smartthings", "Child Color Control", "${device.deviceNetworkId}:4", "LED Bar", "LEDColorConfiguration") +} + +def configure() { + sendHubCommand(getReadConfigurationFromTheDeviceCommands()) +} + +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]) + // Preferences template begin + parameterMap.each { + if (isPreferenceChanged(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + if (it.type == "boolRange") { + def preferenceName = it.key + "Boolean" + + if (isNotNull(settings."$preferenceName")) { + if (!settings."$preferenceName") { + state.currentPreferencesState."$it.key".status = "disablePending" + } else if (state.currentPreferencesState."$it.key".status == "disabled") { + state.currentPreferencesState."$it.key".status = "syncPending" + } + } else { + state.currentPreferencesState."$it.key".status = "syncPending" + } + } + } else if (state.currentPreferencesState."$it.key".value == null) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + syncConfiguration() + // Preferences template end + + response(refresh()) +} + +private getReadConfigurationFromTheDeviceCommands() { + def commands = [] + parameterMap.each { + state.currentPreferencesState."$it.key".status = "reverseSyncPending" + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + commands +} + +private syncConfiguration() { + def commands = [] + log.debug "syncConfiguration ${settings}" + parameterMap.each { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } else if (state.currentPreferencesState."$it.key".status == "disablePending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + } + sendHubCommand(commands) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + if (cmd.parameterNumber == 13) { + handleLEDPreferenceEvent(cmd) + } else { + // Preferences template begin + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find({ it.parameterNumber == cmd.parameterNumber }) + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + log.debug "settings.key ${settings."$key"} preferenceValue ${preferenceValue}" + + if (state.currentPreferencesState."$key".status == "reverseSyncPending") { + log.debug "reverseSyncPending" + state.currentPreferencesState."$key".value = preferenceValue + state.currentPreferencesState."$key".status = "synced" + } else { + if (preferenceValue instanceof String && settings."$key" == preferenceValue.toBoolean()) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + } else if (preferenceValue instanceof Integer && settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + } else if (preference.type == "boolRange") { + log.debug "${state.currentPreferencesState."$key".status}" + if (state.currentPreferencesState."$key".status == "disablePending" && preferenceValue == preference.disableValue) { + state.currentPreferencesState."$key".status = "disabled" + } else { + runIn(5, "syncConfiguration", [overwrite: true]) + } + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } + } + // Preferences template end + } +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + case "boolean": + return String.valueOf(preference.optionActive == integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + log.debug "settings parameter key ${settings."$parameterKey"} ${preference} " + switch (preference.type) { + case "boolean": + return settings."$parameterKey" ? preference.optionActive : preference.optionInactive + case "boolRange": + def parameterKeyBoolean = parameterKey + "Boolean" + return !isNotNull(settings."$parameterKeyBoolean") || settings."$parameterKeyBoolean" ? settings."$parameterKey" : preference.disableValue + case "range": + return settings."$parameterKey" + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isNotNull(value) { + return value != null +} + +private isPreferenceChanged(preference) { + if (isNotNull(settings."$preference.key")) { + if (preference.type == "boolRange") { + def boolName = preference.key + "Boolean" + if (state.currentPreferencesState."$preference.key".status == "disabled") { + return settings."$boolName" + } else { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" || !settings."$boolName" + } + } else { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } + } else { + return false + } +} + +def parse(String description) { + def result = null + if (description != "updated") { + def cmd = zwave.parse(description) + if (cmd) { + result = zwaveEvent(cmd) + log.debug("'$description' parsed to $result") + } else { + log.debug("Couldn't zwave.parse '$description'") + } + } + log.debug "parsed '${description}' to ${result.inspect()}" + result +} + +def handleLEDPreferenceEvent(cmd) { + def hueState = [name: "hue", value: "${Math.round(zwaveValueToHuePercent(cmd.scaledConfigurationValue))}"] + def childDni = "${device.deviceNetworkId}:4" + def childDevice = childDevices.find { it.deviceNetworkId == childDni } + childDevice?.sendEvent(hueState) + childDevice?.sendEvent(name: "saturation", value: "100") +} + +def createChildDevice(childDthNamespace, childDthName, childDni, childComponentLabel, childComponentName) { + try { + log.debug "Creating a child device: ${childDthNamespace}, ${childDthName}, ${childDni}, ${childComponentLabel}, ${childComponentName}" + addChildDevice(childDthNamespace, childDthName, childDni, device.hub.id, + [ + completedSetup: true, + label : childComponentLabel, + isComponent : true, + componentName : childComponentName, + componentLabel: childComponentLabel + ]) + } catch (Exception e) { + log.debug "Exception: ${e}" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + dimmerEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { + dimmerEvents(cmd) +} + +def dimmerEvents(physicalgraph.zwave.Command cmd) { + def switchEvent = createEvent([name: "switch", value: cmd.value ? "on" : "off", descriptionText: "$device.displayName was turned ${cmd.value ? "on" : "off"}"]) + def dimmerEvent = createEvent([name: "level", value: cmd.value == 99 ? 100 : cmd.value, unit: "%"]) + def result = [switchEvent, dimmerEvent] + if (switchEvent.isStateChange) { + result << response(["delay 1000", zwave.meterV3.meterGet(scale: 2).format()]) + } + return result +} + +def on() { + encapSequence([ + zwave.basicV1.basicSet(value: 0xFF), + zwave.basicV1.basicGet() + ], 1000) +} + +def off() { + encapSequence([ + zwave.basicV1.basicSet(value: 0x00), + zwave.basicV1.basicGet() + ], 1000) +} + +def setLevel(level) { + if (level > 99) level = 99 + encapSequence([ + zwave.basicV1.basicSet(value: level), + zwave.switchMultilevelV1.switchMultilevelGet() + ], 1000) +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1 && cmd.scale == 0) { + map = [name: "energy", value: cmd.scaledMeterValue.toDouble().round(1), unit: "kWh"] + } else if (cmd.meterType == 1 && cmd.scale == 2) { + map = [name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W"] + } + createEvent(map) +} + +private getButtonLabel() { + [ + "Up button", + "Down button", + "Configuration button" + ] +} + +private void createChildButtonDevices() { + for (buttonNumber in 1..3) { + def child = addChildDevice("smartthings", "Child Button", "${device.deviceNetworkId}:${buttonNumber}", device.hub.id, + [ + completedSetup: true, + label : buttonLabel[buttonNumber - 1], + isComponent : true, + componentName : "button$buttonNumber", + componentLabel: buttonLabel[buttonNumber - 1] + ]) + + def value = buttonNumber == 3 ? ['pushed'] : ['pushed', 'pushed_2x', 'pushed_3x', 'pushed_4x', 'pushed_5x'] + child.sendEvent(name: "supportedButtonValues", value: value.encodeAsJSON(), displayed: false) + child.sendEvent(name: "numberOfButtons", value: 1, displayed: false) + child.sendEvent(name: "button", value: "pushed", data: [buttonNumber: 1], displayed: false) + } +} + +def sendButtonEvent(gesture, buttonNumber) { + def event = createEvent([name: "button", value: gesture, data: [buttonNumber: buttonNumber], isStateChange: true]) + String childDni = "${device.deviceNetworkId}:$buttonNumber" + def child = childDevices.find { it.deviceNetworkId == childDni } + child?.sendEvent(event) + return createEvent([name: "button", value: gesture, data: [buttonNumber: buttonNumber], isStateChange: true, displayed: false]) +} + +def labelForGesture( attribute) { + def gesture = "pushed" + if (attribute == 0) { + gesture; + } else { + def number = attribute - 1; + "${gesture}_${number}x"; + } +} + +def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + log.info("CentralSceneNotification, keyAttributes=${cmd.keyAttributes}, sceneNumber=${cmd.sceneNumber}") + def singleClick = 0; + def multipleClicks = [3, 4, 5, 6] + def supportedAttributes = [singleClick] + multipleClicks + int attribute = cmd.keyAttributes + int scene = cmd.sceneNumber + if (scene == 1 && attribute in supportedAttributes) { + sendButtonEvent(labelForGesture(attribute), DOWN_BUTTON); + } else if (scene == 2 && attribute in supportedAttributes) { + sendButtonEvent(labelForGesture(attribute), UP_BUTTON); + } else if (scene == 3 && attribute == singleClick) { + sendButtonEvent("pushed", CONFIGURATION_BUTTON) + } else { + log.warn("Unhandled scene notification, keyAttributes=${attribute}, sceneNumber=${scene}") + } +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + // Handles all Z-Wave commands we aren't interested in + log.debug "${cmd}" + [:] +} + +def childSetColor(value) { + sendHubCommand setColorCmd(value) +} + +def setColorCmd(value) { + if (value.hue == null || value.saturation == null) return + def ledColor = Math.round(huePercentToZwaveValue(value.hue)) + encapSequence([ + zwave.configurationV2.configurationSet(scaledConfigurationValue: ledColor, parameterNumber: 13, size: 2), + zwave.configurationV2.configurationGet(parameterNumber: 13) + ], 1000) +} + +private huePercentToZwaveValue(value) { + return value <= 2 ? 0 : (value >= 98 ? 255 : value / 100 * 255) +} + +private zwaveValueToHuePercent(value) { + return value <= 2 ? 0 : (value >= 254 ? 100 : value / 255 * 100) +} + +def refresh() { + encapSequence([ + zwave.basicV1.basicGet(), + zwave.meterV3.meterGet(scale: 0) + ], 1000) +} + +/* +* Security encapsulation support: +*/ + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + log.debug "Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract Secure command from $cmd" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { + def version = commandClassVersions[cmd.commandClass as Integer] + def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) + def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data) + if (encapsulatedCommand) { + log.debug "Parsed Crc16Encap into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract CRC16 command from $cmd" + } +} + +private secEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using Secure Encapsulation, command: $cmd" + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() +} + +private crcEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using CRC16 Encapsulation, command: $cmd" + zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format() +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + + if (zwaveInfo.zw.endsWith("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private encapSequence(cmds, Integer delay = 250) { + delayBetween(cmds.collect { encap(it) }, delay) +} + +private getParameterMap() { + [ + [ + name : "Dimming Speed", key: "dimmingSpeed", type: "range", + parameterNumber: 1, size: 1, defaultValue: 3, + range : "1..100", + description : "How fast or slow the light turns on when you hold the switch in seconds (ie: dimming from 10-20%, 80-60%, etc). Value 0 - Instant On. This parameter can be set without a HUB from the Configuration Button. Finally, if you are using a,dumb switch in a 3-Way setting, this parameter will not work if you manually press the dumb switch (it will only work if you press the smart switch)." + ], + [ + name : "Power On State", key: "powerOnState", type: "range", + parameterNumber: 11, size: 1, defaultValue: 0, + range : "0..101", + description : "When power is restored, the switch reverts to either On, Off, or Last Level. Example of how the values work: 0 = Off, 1-100 = Specific % On, 101 = Returns to Level before Power Outage. This parameter can be set without a HUB from the Configuration Button." + ], + [ + name : "LED Indicator Intensity", key: "ledIndicatorIntensity", type: "range", + parameterNumber: 14, size: 1, defaultValue: 5, + range : "0..10", + description : "This will set the intensity of the LED bar (ie: how bright it is). Example of how the values work: 0 = Off, 1 = Low, 5 = Medium, 10 = High. This parameter can be set without a HUB from the Configuration Button." + ], + [ + name : "LED Indicator Intensity (When Off)", key: "ledIndicatorIntensity(WhenOff)", type: "range", + parameterNumber: 15, size: 1, defaultValue: 1, + range : "0..10", + description : "This is the intensity of the LED bar when the switch is off. Example of how the values work: 0 = Off, 1 = Low, 5 = Medium, 10 = High. This parameter can be set without a HUB from the Configuration Button." + ], + [ + name : "LED Indicator Timeout", key: "ledIndicatorTimeout", type: "range", + parameterNumber: 17, size: 1, defaultValue: 3, + range : "0..10", + description : "Changes the amount of time the RGB Bar shows the Dim level if the LED Bar has been disabled. Example of how the values work: 0 = Always off, 1 = 1 second after level is adjusted." + ], + [ + name : "Dimming Speed (Z-Wave)", key: "dimmingSpeed(Z-Wave)", type: "range", + parameterNumber: 2, size: 1, defaultValue: 101, + range : "0..101", + description : "How fast or slow the light turns dim when you adjust the switch remotely (ie: dimming from 10-20%, 80-60%, etc). Entering the value of 101 = Keeps the switch in sync with Parameter 1." + ], + [ + name : "Ramp Rate", key: "rampRate", type: "range", + parameterNumber: 3, size: 1, defaultValue: 101, + range : "0..101", + description : "How fast or slow the light turns on when you press the switch 1x to bring from On to Off or Off to On. Entering the value of 101 = Keeps the switch in sync with Parameter 1." + ], + [ + name : "Ramp Rate (Z-Wave)", key: "rampRate(Z-Wave)", type: "range", + parameterNumber: 4, size: 1, defaultValue: 101, + range : "0..101", + description : "How fast or slow the light turns on when you bring your switch from On to Off or Off to On remotely. Entering the value of 101 = Keeps the switch in sync with Parameter 1." + ], + [ + name : "Invert Switch", key: "invertSwitch", type: "boolean", + parameterNumber: 7, size: 1, defaultValue: 0, + optionInactive : 0, inactiveDescription: "Disabled", + optionActive : 1, activeDescription: "Enabled", + description : "Inverts the switch" + ], + [ + name : "Auto Off Timer", key: "autoOffTimer", type: "boolRange", + parameterNumber: 8, size: 2, defaultValue: 0, + range : "1..32767", disableValue: 0, + description : "Automatically turns the switch off after x amount of seconds (value 0 = Disabled)" + ] + ] +} From 8ae0948495d3bfc083c99af2e8c87b67cf900d90 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Fri, 28 Aug 2020 21:16:57 +0200 Subject: [PATCH 052/422] ICP-11574 - query device for current status in response to WakeUpNotification (#42036) * ICP-11574 - query device for current status in response to WakeUpNotification --- .../zwave-basic-smoke-alarm.groovy | 11 +++++++---- .../zwave-smoke-alarm.src/zwave-smoke-alarm.groovy | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy b/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy index a2153b19da9..3d4dd97e472 100644 --- a/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy +++ b/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy @@ -191,13 +191,16 @@ def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd, def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) { results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false) if (!state.lastbatt || (now() - state.lastbatt) >= 56*60*60*1000) { - results << response([ + results << response(delayBetween([ + zwave.notificationV3.notificationGet(notificationType: 0x01).format(), zwave.batteryV1.batteryGet().format(), - "delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation().format() - ]) + ]), 2000 ) } else { - results << response(zwave.wakeUpV1.wakeUpNoMoreInformation()) + results << response(delayBetween([ + zwave.notificationV3.notificationGet(notificationType: 0x01).format(), + zwave.wakeUpV1.wakeUpNoMoreInformation().format() + ]), 2000 ) } } diff --git a/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy b/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy index 2ea02b249d9..ab720ea56c3 100644 --- a/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy +++ b/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy @@ -191,13 +191,16 @@ def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd, def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) { results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false) if (!state.lastbatt || (now() - state.lastbatt) >= 56*60*60*1000) { - results << response([ + results << response(delayBetween([ + zwave.notificationV3.notificationGet(notificationType: 0x01).format(), zwave.batteryV1.batteryGet().format(), - "delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation().format() - ]) + ]), 2000 ) } else { - results << response(zwave.wakeUpV1.wakeUpNoMoreInformation()) + results << response(delayBetween([ + zwave.notificationV3.notificationGet(notificationType: 0x01).format(), + zwave.wakeUpV1.wakeUpNoMoreInformation().format() + ]), 2000 ) } } From 1d977896b4cca5f9492f752a7cce0e8901a4bbad Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Fri, 28 Aug 2020 22:23:46 +0200 Subject: [PATCH 053/422] WWST-6725 Added fingerprint for Inovelli Dimmer LZW31 (#42221) --- .../inovelli-dimmer.src/inovelli-dimmer.groovy | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy b/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy index f0ca4278d04..c883a81c017 100644 --- a/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy +++ b/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy @@ -27,6 +27,7 @@ metadata { capability "Power Meter" fingerprint mfr: "031E", prod: "0001", model: "0001", deviceJoinName: "Inovelli Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Inovelli_Dimmer" //Inovelli Dimmer LZW31-SN + fingerprint mfr: "031E", prod: "0003", model: "0001", deviceJoinName: "Inovelli Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Inovelli_Dimmer_LZW31" //Inovelli Dimmer LZW31 } tiles(scale: 2) { @@ -119,10 +120,12 @@ def installed() { } } // Preferences template end - createChildButtonDevices() - def value = ['pushed', 'pushed_2x', 'pushed_3x', 'pushed_4x', 'pushed_5x'].encodeAsJson() - sendEvent(name: "supportedButtonValues", value: value) - sendEvent(name: "numberOfButtons", value: 3, displayed: true) + if(isInovelliDimmerLZW31SN()) { + createChildButtonDevices() + def value = ['pushed', 'pushed_2x', 'pushed_3x', 'pushed_4x', 'pushed_5x'].encodeAsJson() + sendEvent(name: "supportedButtonValues", value: value) + sendEvent(name: "numberOfButtons", value: 3, displayed: true) + } createChildDevice("smartthings", "Child Color Control", "${device.deviceNetworkId}:4", "LED Bar", "LEDColorConfiguration") } @@ -511,6 +514,10 @@ private encapSequence(cmds, Integer delay = 250) { delayBetween(cmds.collect { encap(it) }, delay) } +private isInovelliDimmerLZW31SN(){ + zwaveInfo.mfr.equals("031E") && zwaveInfo.prod.equals("0001") && zwaveInfo.model.equals("0001") +} + private getParameterMap() { [ [ From e9b99d03c77ccfdf4c3aff41e9caa90011b58716 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 1 Sep 2020 10:00:22 -0700 Subject: [PATCH 054/422] CHAD-5236 CHAD-5235 Add translation strings for Fibaro Smoke & Z-Wave Siren (#42146) * CHAD-5236 Add translation strings for Z-Wave Siren * CHAD-5235 Add translation strings for Fibaro Smoke Sensor * update in response to testing and verification --- .../fibaro-smoke-sensor.groovy | 59 +- .../i18n/messages.properties | 1463 +++++++++++++++++ .../zwave-siren.src/i18n/messages.properties | 306 ++++ .../zwave-siren.src/zwave-siren.groovy | 8 +- 4 files changed, 1812 insertions(+), 24 deletions(-) create mode 100644 devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties create mode 100644 devicetypes/smartthings/zwave-siren.src/i18n/messages.properties diff --git a/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy b/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy index 17a192ff070..cc4e9ef70d2 100644 --- a/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy +++ b/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy @@ -54,25 +54,25 @@ metadata { input description: "Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN", title: "To check smoke detection state", displayDuringSetup: true, type: "paragraph", element: "paragraph" input description: "Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings", - title: "Advanced Configuration", displayDuringSetup: true, type: "paragraph", element: "paragraph" - input "smokeSensorSensitivity", "enum", title: "Smoke Sensor Sensitivity", options: ["High","Medium","Low"], defaultValue: "${smokeSensorSensitivity}", displayDuringSetup: true - input "zwaveNotificationStatus", "enum", title: "Notifications Status", options: ["disabled","casing opened","exceeding temperature threshold", "lack of Z-Wave range", "all notifications"], + title: "Advanced settings", displayDuringSetup: true, type: "paragraph", element: "paragraph" + input "smokeSensorSensitivity", "enum", title: "Smoke sensor sensitivity", options: ["High", "Medium", "Low"], defaultValue: "${smokeSensorSensitivity}", displayDuringSetup: true + input "zwaveNotificationStatus", "enum", title: "Notifications", options: ["None", "Casing opened", "Exceeding temperature threshold", "Lack of Z-Wave range", "All"], // defaultValue: "${zwaveNotificationStatus}", displayDuringSetup: true //Setting the default to casing opened so it can work in SmartThings mobile app. - defaultValue: "casing opened", displayDuringSetup: true - input "visualIndicatorNotificationStatus", "enum", title: "Visual Indicator Notifications Status", - options: ["disabled","casing opened","exceeding temperature threshold", "lack of Z-Wave range", "all notifications"], + defaultValue: "Casing opened", displayDuringSetup: true + input "visualIndicatorNotificationStatus", "enum", title: "Visual indicator notifications status", + options: ["None", "Casing opened", "Exceeding temperature threshold", "Lack of Z-Wave range", "All"], defaultValue: "${visualIndicatorNotificationStatus}", displayDuringSetup: true - input "soundNotificationStatus", "enum", title: "Sound Notifications Status", - options: ["disabled","casing opened","exceeding temperature threshold", "lack of Z-Wave range", "all notifications"], + input "soundNotificationStatus", "enum", title: "Sound notifications status", + options: ["None", "Casing opened", "Exceeding temperature threshold", "Lack of Z-Wave range", "All"], defaultValue: "${soundNotificationStatus}", displayDuringSetup: true - input "temperatureReportInterval", "enum", title: "Temperature Report Interval", - options: ["reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${temperatureReportInterval}", displayDuringSetup: true - input "temperatureReportHysteresis", "number", title: "Temperature Report Hysteresis", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true - input "temperatureThreshold", "number", title: "Overheat Temperature Threshold", description: "Available settings: 0 or 2-100 C", range: "0..100", displayDuringSetup: true - input "excessTemperatureSignalingInterval", "enum", title: "Excess Temperature Signaling Interval", + input "temperatureReportInterval", "enum", title: "Temperature report interval", + options: ["Reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${temperatureReportInterval}", displayDuringSetup: true + input "temperatureReportHysteresis", "number", title: "Temperature report hysteresis", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true + input "temperatureThreshold", "number", title: "Overheat temperature threshold", description: "Available settings: 0 or 2-100 C", range: "0..100", displayDuringSetup: true + input "excessTemperatureSignalingInterval", "enum", title: "Excess temperature signaling interval", options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${excessTemperatureSignalingInterval}", displayDuringSetup: true - input "lackOfZwaveRangeIndicationInterval", "enum", title: "Lack of Z-Wave Range Indication Interval", + input "lackOfZwaveRangeIndicationInterval", "enum", title: "Lack of Z-Wave range indication interval", options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${lackOfZwaveRangeIndicationInterval}", displayDuringSetup: true } tiles (scale: 2){ @@ -119,9 +119,28 @@ metadata { def updated() { log.debug "Updated with settings: ${settings}" + if(!state.legacySettingsUpdated) updateLegacySettings() setConfigured("false") //wait until the next time device wakeup to send configure command } +def updateLegacySettings() { + + def legacyNotificationOptionMap = [ + "disabled" : "None", + "casing opened" : "Casing opened", + "exceeding temperature threshold" : "Exceeding temperature threshold", + "lack of Z-Wave range" : "Lack of Z-Wave range", + "all notifications" : "All" + ] + + device.updateSetting("temperatureReportInterval", temperatureReportInterval == "reports inactive" ?: "Reports inactive") + + device.updateSetting("zwaveNotificationStatus", legacyNotificationOptionMap[zwaveNotificationStatus] ?: zwaveNotificationStatus) + device.updateSetting("visualIndicatorNotificationStatus", legacyNotificationOptionMap[visualIndicatorNotificationStatus] ?: visualIndicatorNotificationStatus) + device.updateSetting("soundNotificationStatus", legacyNotificationOptionMap[soundNotificationStatus] ?: soundNotificationStatus) + + state.legacySettingsUpdated = true +} def parse(String description) { @@ -481,15 +500,15 @@ private def getTimeOptionValueMap() { [ "12 hours" : 4320, "18 hours" : 6480, "24 hours" : 8640, - "reports inactive" : 0, + "Reports inactive" : 0, ]} private def getNotificationOptionValueMap() { [ - "disabled" : 0, - "casing opened" : 1, - "exceeding temperature threshold" : 2, - "lack of Z-Wave range" : 4, - "all notifications" : 7, + "None" : 0, + "Casing opened" : 1, + "Exceeding temperature threshold" : 2, + "Lack of Z-Wave range" : 4, + "All" : 7, ]} private command(physicalgraph.zwave.Command cmd) { diff --git a/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties b/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties new file mode 100644 index 00000000000..0f31fa75b74 --- /dev/null +++ b/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties @@ -0,0 +1,1463 @@ +# Copyright 2020 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. + +# Device Preferences +'''Excess temperature signaling interval'''.en=Excess temperature signalling interval +'''Excess temperature signaling interval'''.en-gb=Excess temperature signalling interval +'''Excess temperature signaling interval'''.en-us=Excess temperature signaling interval +'''Excess temperature signaling interval'''.en-ca=Excess temperature signaling interval +'''Excess temperature signaling interval'''.sq=Intervali i sinjalizimit për tejkalim temperature +'''Excess temperature signaling interval'''.ar=الفاصل الزمني لإشارة تجاوز درجة الحرارة +'''Excess temperature signaling interval'''.be=Інтэрвал падачы сігналу аб празмернай тэмпературы +'''Excess temperature signaling interval'''.sr-ba=Interval signalizacije previsoke temperature +'''Excess temperature signaling interval'''.bg=Интервал на сигнализиране за превишена температура +'''Excess temperature signaling interval'''.ca=Interval de senyalització de temperatura excessiva +'''Excess temperature signaling interval'''.zh-cn=超过温度信号间隔 +'''Excess temperature signaling interval'''.zh-hk=超過溫度訊號時間間隔 +'''Excess temperature signaling interval'''.zh-tw=溫度超標訊號時間間隔 +'''Excess temperature signaling interval'''.hr=Interval signalizacije previsoke temperature +'''Excess temperature signaling interval'''.cs=Překročení intervalu signalizace teploty +'''Excess temperature signaling interval'''.da=Signalinterval ved for høj temperatur +'''Excess temperature signaling interval'''.nl=Signaalinterval hoge temperatuur +'''Excess temperature signaling interval'''.et=Liigsest temperatuurist märkuandmise välp +'''Excess temperature signaling interval'''.fi=Lämpötilan ylityksen ilmoitusaikaväli +'''Excess temperature signaling interval'''.fr=Intervalle de signalement du dépassement de température +'''Excess temperature signaling interval'''.fr-ca=Intervalle de signalement du dépassement de température +'''Excess temperature signaling interval'''.de=Übertemperatur-Signalintervall +'''Excess temperature signaling interval'''.el=Διάστημα σήμανσης υπερβολικής θερμοκρασίας +'''Excess temperature signaling interval'''.iw=מרווח איתות של טמפרטורה חריגה +'''Excess temperature signaling interval'''.hi-in=अत्यधिक तापमान संकेत अंतराल +'''Excess temperature signaling interval'''.hu=Pluszhőmérséklet jelzésének időköze +'''Excess temperature signaling interval'''.is=Tími milli tilkynninga um of háan hita +'''Excess temperature signaling interval'''.in=Interval sinyal suhu berlebih +'''Excess temperature signaling interval'''.it=Intervallo di segnalazione temperatura in eccesso +'''Excess temperature signaling interval'''.ja=温度超過を通知する間隔 +'''Excess temperature signaling interval'''.ko=과도한 온도 변화 알림 간격 +'''Excess temperature signaling interval'''.lv=Pārmērīgas temperatūras signalizācijas intervāls +'''Excess temperature signaling interval'''.lt=Viršytos temperatūros pranešimo intervalas +'''Excess temperature signaling interval'''.ms=Selang pengisyaratan suhu berlebihan +'''Excess temperature signaling interval'''.no=Intervall for signal om overskredet temperatur +'''Excess temperature signaling interval'''.pl=Interwał sygnalizowania zbyt wysokiej temperatury +'''Excess temperature signaling interval'''.pt=Intervalo de sinalização de temperatura excessiva +'''Excess temperature signaling interval'''.ro=Interval de semnalizare temperatură excesivă +'''Excess temperature signaling interval'''.ru=Интервал между сигналами о чрезмерном повышении температуры +'''Excess temperature signaling interval'''.sr=Interval signaliranja prekomerne temperature +'''Excess temperature signaling interval'''.sk=Interval signalizácie prekročenia teploty +'''Excess temperature signaling interval'''.sl=Interval signaliziranja prekomerne temperature +'''Excess temperature signaling interval'''.es=Intervalo de advertencia de exceso de temperatura +'''Excess temperature signaling interval'''.sv=Signalintervall för för hög temperatur +'''Excess temperature signaling interval'''.th=ช่วงเวลาการส่งสัญญาณอุณหภูมิส่วนเกิน +'''Excess temperature signaling interval'''.tr=Aşırı sıcaklık sinyali aralığı +'''Excess temperature signaling interval'''.uk=Інтервал сигналу про перевищення температури +'''Excess temperature signaling interval'''.vi=Chu kỳ báo hiệu nhiệt độ quá mức +'''Smoke sensor sensitivity'''.en=Smoke sensor sensitivity +'''Smoke sensor sensitivity'''.en-gb=Smoke sensor sensitivity +'''Smoke sensor sensitivity'''.en-us=Smoke sensor sensitivity +'''Smoke sensor sensitivity'''.en-ca=Smoke sensor sensitivity +'''Smoke sensor sensitivity'''.sq=Ndjeshmëria e sensorit të tymit +'''Smoke sensor sensitivity'''.ar=حساسية مستشعر الدخان +'''Smoke sensor sensitivity'''.be=Адчувальнасць датчыка дыму +'''Smoke sensor sensitivity'''.sr-ba=Osjetljivost senzora dima +'''Smoke sensor sensitivity'''.bg=Чувствителност на сензора за дим +'''Smoke sensor sensitivity'''.ca=Sensibilitat del sensor de fum +'''Smoke sensor sensitivity'''.zh-cn=烟雾传感器灵敏度 +'''Smoke sensor sensitivity'''.zh-hk=煙霧感應器靈敏度 +'''Smoke sensor sensitivity'''.zh-tw=煙霧偵測器靈敏度 +'''Smoke sensor sensitivity'''.hr=Osjetljivost senzora dima +'''Smoke sensor sensitivity'''.cs=Citlivost detektoru kouře +'''Smoke sensor sensitivity'''.da=Følsomhed af røgsensor +'''Smoke sensor sensitivity'''.nl=Gevoeligheid rooksensor +'''Smoke sensor sensitivity'''.et=Suitsuanduri tundlikkus +'''Smoke sensor sensitivity'''.fi=Savutunnistimen herkkyys +'''Smoke sensor sensitivity'''.fr=Sensibilité du détecteur de fumée +'''Smoke sensor sensitivity'''.fr-ca=Sensibilité du détecteur de fumée +'''Smoke sensor sensitivity'''.de=Rauchmelderempfindlichkeit +'''Smoke sensor sensitivity'''.el=Ευαισθησία αισθητήρα καπνού +'''Smoke sensor sensitivity'''.iw=רגישות חיישן העשן +'''Smoke sensor sensitivity'''.hi-in=स्मोक सेंसर की संवेदनशीलता +'''Smoke sensor sensitivity'''.hu=Füstérzékelő érzékenysége +'''Smoke sensor sensitivity'''.is=Næmi reykskynjara +'''Smoke sensor sensitivity'''.in=Sensitivitas sensor asap +'''Smoke sensor sensitivity'''.it=Sensibilità del rilevatore di fumo +'''Smoke sensor sensitivity'''.ja=煙センサーの感度 +'''Smoke sensor sensitivity'''.ko=연기 센서 민감도 +'''Smoke sensor sensitivity'''.lv=Dūmu sensora jutība +'''Smoke sensor sensitivity'''.lt=Dūmų jutiklio jautrumas +'''Smoke sensor sensitivity'''.ms=Kesensitifan penderia asap +'''Smoke sensor sensitivity'''.no=Røyksensorfølsomhet +'''Smoke sensor sensitivity'''.pl=Czułość czujnika dymu +'''Smoke sensor sensitivity'''.pt=Sensibilidade do sensor de fumo +'''Smoke sensor sensitivity'''.ro=Sensibilitate senzor de fum +'''Smoke sensor sensitivity'''.ru=Чувствительность датчика дыма +'''Smoke sensor sensitivity'''.sr=Osetljivost senzora dima +'''Smoke sensor sensitivity'''.sk=Citlivosť senzora dymu +'''Smoke sensor sensitivity'''.sl=Občutljivost senzorja dima +'''Smoke sensor sensitivity'''.es=Sensibilidad del sensor de humo +'''Smoke sensor sensitivity'''.sv=Brandvarnarens känslighet +'''Smoke sensor sensitivity'''.th=ความไวเซ็นเซอร์ควัน +'''Smoke sensor sensitivity'''.tr=Duman sensörü hassasiyeti +'''Smoke sensor sensitivity'''.uk=Чутливість датчика диму +'''Smoke sensor sensitivity'''.vi=Độ nhạy của cảm biến khói +'''Low'''.en=Low +'''Low'''.en-gb=Low +'''Low'''.en-us=Low +'''Low'''.en-ca=Low +'''Low'''.sq=E ulët +'''Low'''.ar=منخفضة +'''Low'''.be=Нізкая +'''Low'''.sr-ba=Nisko +'''Low'''.bg=Ниска +'''Low'''.ca=Baixa +'''Low'''.zh-cn=低 +'''Low'''.zh-hk=低 +'''Low'''.zh-tw=低 +'''Low'''.hr=Niska +'''Low'''.cs=Nízká +'''Low'''.da=Lav +'''Low'''.nl=Laag +'''Low'''.et=Madal +'''Low'''.fi=Pieni +'''Low'''.fr=Faible +'''Low'''.fr-ca=Faible +'''Low'''.de=Niedrig +'''Low'''.el=Χαμηλή +'''Low'''.iw=נמוכה +'''Low'''.hi-in=कम +'''Low'''.hu=Alacsony +'''Low'''.is=Lítið +'''Low'''.in=Rendah +'''Low'''.it=Bassa +'''Low'''.ja=低 +'''Low'''.ko=낮음 +'''Low'''.lv=Zems +'''Low'''.lt=Mažas +'''Low'''.ms=Rendah +'''Low'''.no=Lav +'''Low'''.pl=Niska +'''Low'''.pt=Baixa +'''Low'''.ro=Redusă +'''Low'''.ru=Низкая +'''Low'''.sr=Niska +'''Low'''.sk=Nízka +'''Low'''.sl=Nizko +'''Low'''.es=Baja +'''Low'''.sv=Lågt +'''Low'''.th=ต่ำ +'''Low'''.tr=Düşük +'''Low'''.uk=Низький +'''Low'''.vi=Thấp +'''Lack of Z-Wave range'''.en=Lack of Z-Wave range +'''Lack of Z-Wave range'''.en-gb=Lack of Z-Wave range +'''Lack of Z-Wave range'''.en-us=Lack of Z-Wave range +'''Lack of Z-Wave range'''.en-ca=Lack of Z-Wave range +'''Lack of Z-Wave range'''.sq=Mungon intervali Z-Wave +'''Lack of Z-Wave range'''.ar=غياب نطاق Z-Wave +'''Lack of Z-Wave range'''.be=Адсутнічае дыяпазон Z-Wave +'''Lack of Z-Wave range'''.sr-ba=Nedostatak opsega uređaja Z-Wave +'''Lack of Z-Wave range'''.bg=Липса на обхват на Z-Wave +'''Lack of Z-Wave range'''.ca=Interval de manca de senyal de Z-Wave +'''Lack of Z-Wave range'''.zh-cn=缺少 Z 波范围 +'''Lack of Z-Wave range'''.zh-hk=Z-Wave 範圍不足 +'''Lack of Z-Wave range'''.zh-tw=缺少 Z-Wave 覆蓋範圍 +'''Lack of Z-Wave range'''.hr=Nedostatak dometa uređaja Z-Wave +'''Lack of Z-Wave range'''.cs=Nedostatečný rozsah Z-Wave +'''Lack of Z-Wave range'''.da=Manglende Z-Wave-rækkevidde +'''Lack of Z-Wave range'''.nl=Slecht Z-Wave-bereik +'''Lack of Z-Wave range'''.et=Puudub Z-Wave’i vahemik +'''Lack of Z-Wave range'''.fi=Z-Wave-alueen puute +'''Lack of Z-Wave range'''.fr=Absence de réseau Z-Wave +'''Lack of Z-Wave range'''.fr-ca=Absence de réseau Z-Wave +'''Lack of Z-Wave range'''.de=Mangelnde Z-Wave-Reichweite +'''Lack of Z-Wave range'''.el=Έλλειψη εύρους Z-Wave +'''Lack of Z-Wave range'''.iw=Z-Wave מחוץ לטווח +'''Lack of Z-Wave range'''.hi-in=Z-Wave रेंज में कमी +'''Lack of Z-Wave range'''.hu=Hiányzó Z-Wave-hatótáv +'''Lack of Z-Wave range'''.is=Z-Wave-svæði vantar +'''Lack of Z-Wave range'''.in=Rentang kekurangan Gelombang-Z +'''Lack of Z-Wave range'''.it=Assenza di gamma Z-Wave +'''Lack of Z-Wave range'''.ja=Z-Waveレンジの不足 +'''Lack of Z-Wave range'''.ko=Z-Wave 네트워크 커뮤니케이션 부족 +'''Lack of Z-Wave range'''.lv=Nav Z-Wave diapazona +'''Lack of Z-Wave range'''.lt=Nėra „Z-Wave“ veikimo diapazono +'''Lack of Z-Wave range'''.ms=Kekurangan julat Z-Wave +'''Lack of Z-Wave range'''.no=Mangel på Z-Wave-område +'''Lack of Z-Wave range'''.pl=Brak zakresu Z-Wave +'''Lack of Z-Wave range'''.pt=Falta de alcance Z-Wave +'''Lack of Z-Wave range'''.ro=Z-Wave nu este în aria de acoperire +'''Lack of Z-Wave range'''.ru=Недостаточный диапазон Z-Wave +'''Lack of Z-Wave range'''.sr=Nedostatak Z-Wave opsega +'''Lack of Z-Wave range'''.sk=Nedostatočný rozsah Z-Wave +'''Lack of Z-Wave range'''.sl=Manjka doseg Z-Wave +'''Lack of Z-Wave range'''.es=Ausencia de alcance de Z-Wave +'''Lack of Z-Wave range'''.sv=Ett Z-Wave-intervall saknas +'''Lack of Z-Wave range'''.th=การขาดช่วง Z-Wave +'''Lack of Z-Wave range'''.tr=Z-Wave kapsamı yok +'''Lack of Z-Wave range'''.uk=Немає підключення до мережі Z-Wave +'''Lack of Z-Wave range'''.vi=Thiếu phạm vi Z-Wave +'''Lack of Z-Wave range indication interval'''.en=Lack of Z-Wave range indication interval +'''Lack of Z-Wave range indication interval'''.en-gb=Lack of Z-Wave range indication interval +'''Lack of Z-Wave range indication interval'''.en-us=Lack of Z-Wave range indication interval +'''Lack of Z-Wave range indication interval'''.en-ca=Lack of Z-Wave range indication interval +'''Lack of Z-Wave range indication interval'''.sq=Mungon intervali i treguesit për Z-Wave +'''Lack of Z-Wave range indication interval'''.ar=الفاصل الزمني لمؤشر غياب نطاق Z-Wave +'''Lack of Z-Wave range indication interval'''.be=Адсутнічае інтэрвал вызначэння дыяпазону Z-Wave +'''Lack of Z-Wave range indication interval'''.sr-ba=Nedostatak intervala indikacije opsega uređaja Z-Wave +'''Lack of Z-Wave range indication interval'''.bg=Липса на интервал за индикация на обхвата на Z-Wave +'''Lack of Z-Wave range indication interval'''.ca=Interval d'indicació de manca de senyal de Z-Wave +'''Lack of Z-Wave range indication interval'''.zh-cn=缺少 Z 波范围指示间隔 +'''Lack of Z-Wave range indication interval'''.zh-hk=Z-Wave 範圍指示時間間隔不足 +'''Lack of Z-Wave range indication interval'''.zh-tw=缺少 Z-Wave 覆蓋範圍指示時間間隔 +'''Lack of Z-Wave range indication interval'''.hr=Nedostatak intervala indikacije dometa uređaja Z-Wave +'''Lack of Z-Wave range indication interval'''.cs=Nedostatečný interval indikace rozsahu Z-Wave +'''Lack of Z-Wave range indication interval'''.da=Interval for indikation af manglende Z-Wave-rækkevidde +'''Lack of Z-Wave range indication interval'''.nl=Interval indicatie slecht Z-Wave-bereik +'''Lack of Z-Wave range indication interval'''.et=Puudub Z-Wave’i vahemiku näidustuse välp +'''Lack of Z-Wave range indication interval'''.fi=Z-Wave-alueen näyttöajan puute +'''Lack of Z-Wave range indication interval'''.fr=Intervalle d'indication de l'absence de réseau Z-Wave +'''Lack of Z-Wave range indication interval'''.fr-ca=Intervalle d'indication de l'absence de réseau Z-Wave +'''Lack of Z-Wave range indication interval'''.de=Anzeigeintervall bei mangelnder Z-Wave-Reichweite +'''Lack of Z-Wave range indication interval'''.el=Διάστημα έλλειψης ένδειξης εύρους Z-Wave +'''Lack of Z-Wave range indication interval'''.iw=מרווח ציון Z-Wave מחוץ לטווח +'''Lack of Z-Wave range indication interval'''.hi-in=Z-Wave रेंज में कमी संकेत का अंतराल +'''Lack of Z-Wave range indication interval'''.hu=Hiányzó Z-Wave-hatótávjelzési időköz +'''Lack of Z-Wave range indication interval'''.is=Tími milli tilkynninga um að Z-Wave-svæði vanti +'''Lack of Z-Wave range indication interval'''.in=Interval indikasi rentang kekurangan Gelombang-Z +'''Lack of Z-Wave range indication interval'''.it=Assenza di intervallo di indicazione gamma Z-Wave +'''Lack of Z-Wave range indication interval'''.ja=Z-Waveレンジの不足を指摘する間隔 +'''Lack of Z-Wave range indication interval'''.ko=Z-Wave 네트워크 커뮤니케이션 부족 표시 간격 +'''Lack of Z-Wave range indication interval'''.lv=Nav Z-Wave diapazona indikācijas intervāla +'''Lack of Z-Wave range indication interval'''.lt=„Z-Wave“ veikimo diapazono nebuvimo nurodymo intervalas +'''Lack of Z-Wave range indication interval'''.ms=Kekurangan selang penunjuk julat Z-Wave +'''Lack of Z-Wave range indication interval'''.no=Intervall for mangel på Z-Wave-områdeindikasjon +'''Lack of Z-Wave range indication interval'''.pl=Brak interwału wskazywania zakresu Z-Wave +'''Lack of Z-Wave range indication interval'''.pt=Falta de intervalo de indicação de alcance Z-Wave +'''Lack of Z-Wave range indication interval'''.ro=Interval de timp pentru a semnaliza că Z-Wave nu este în raza de acoperire +'''Lack of Z-Wave range indication interval'''.ru=Интервал индикации о недостаточном диапазоне Z-Wave +'''Lack of Z-Wave range indication interval'''.sr=Nedostatak intervala indikacije Z-Wave opsega +'''Lack of Z-Wave range indication interval'''.sk=Interval indikácie nedostatočného rozsahu Z-Wave +'''Lack of Z-Wave range indication interval'''.sl=Manjka interval za prikazovanje dosega Z-Wave +'''Lack of Z-Wave range indication interval'''.es=Intervalo de indicación de ausencia de alcance de Z-Wave +'''Lack of Z-Wave range indication interval'''.sv=Ett indikeringsintervall för Z-Wave-intervallet saknas +'''Lack of Z-Wave range indication interval'''.th=ระยะเวลาการระบุการขาดช่วง Z-Wave +'''Lack of Z-Wave range indication interval'''.tr=Z-Wave kapsam belirtimi aralığı yok +'''Lack of Z-Wave range indication interval'''.uk=Немає інтервалу індикації про підключення до мережі Z-Wave +'''Lack of Z-Wave range indication interval'''.vi=Chu kỳ chỉ báo thiếu phạm vi Z-Wave +'''Available settings: 0 or 2-100 C'''.en=When it's hotter than the temperature you set, you'll get a notification. You can set 0°C or 2-100°C. +'''Available settings: 0 or 2-100 C'''.en-gb=When it's hotter than the temperature you set, you'll get a notification. You can set 0°C or 2-100°C. +'''Available settings: 0 or 2-100 C'''.en-us=When it's hotter than the temperature you set, you'll get a notification. You can set 0°C or 2-100°C. +'''Available settings: 0 or 2-100 C'''.en-ca=When it's hotter than the temperature you set, you'll get a notification. You can set 0°C or 2-100°C. +'''Available settings: 0 or 2-100 C'''.sq=Do të marrësh një njoftim kur të jetë më nxehtë se temperatura që cilëson ti. Mund të cilësosh 0°C ose 2-100°C. +'''Available settings: 0 or 2-100 C'''.ar=عندما تصبح درجة الحرارة أكثر ارتفاعاً من تلك التي قمت بضبطها، ستتلقى إشعاراً. ويمكنك ضبط ۰ درجة مئوية أو ۲ - ۱۰۰ درجة مئوية. +'''Available settings: 0 or 2-100 C'''.be=Калі тэмпература перавысіць зададзеную, вы атрымаеце апавяшчэнне. Вы можаце задаць 0 °C або 2-100 °C. +'''Available settings: 0 or 2-100 C'''.sr-ba=Kada je temperatura viša od postavljene, primit ćete obavještenje. Možete postaviti 0°C ili raspon između 2°C i 100°C. +'''Available settings: 0 or 2-100 C'''.bg=Когато е по-горещо от зададената температура, ще получите уведомление. Може да зададете 0°C или 2 – 100°C. +'''Available settings: 0 or 2-100 C'''.ca=Quan la temperatura sigui superior a l'establerta, rebràs una notificació. Pots establir 0 °C o 2-100 °C. +'''Available settings: 0 or 2-100 C'''.zh-cn=当温度超过设置的温度时,您将收到通知。您可以设置 0°C 或 2-100°C。 +'''Available settings: 0 or 2-100 C'''.zh-hk=當溫度超過您設定的溫度時,您會收到通知。您可設定 0°C 或 2-100°C。 +'''Available settings: 0 or 2-100 C'''.zh-tw=溫度超過設定的熱度時,將傳送通知給您。您可設定 0°C 或 2 至 100°C 間的數值。 +'''Available settings: 0 or 2-100 C'''.hr=Kada je temperatura viša od postavljene, primit ćete obavijest. Možete postaviti 0 °C ili raspon između 2 °C i 100 °C. +'''Available settings: 0 or 2-100 C'''.cs=Když bude teplota vyšší než nastavená, budete upozorněni. Můžete nastavit teplotu 0 °C nebo 2-100 °C. +'''Available settings: 0 or 2-100 C'''.da=Du får en meddelelse, når det er varmere end den temperatur, du har angivet. Du kan angive 0 °C eller 2-100 °C. +'''Available settings: 0 or 2-100 C'''.nl=Als het warmer is dan de temperatuur die u hebt ingesteld, krijgt u een melding. U kunt 0°C of 2-100°C instellen. +'''Available settings: 0 or 2-100 C'''.et=Kui on kuumem, kui teie määratud tempreatuur, saate teavituse. Saate määrata 0 °C või 2 kuni 100 °C. +'''Available settings: 0 or 2-100 C'''.fi=Kun lämpötila ylittää asettamasi arvon, saat ilmoituksen. Voit asettaa arvoksi 0 °C tai 2–100 °C. +'''Available settings: 0 or 2-100 C'''.fr=Lorsqu'il fait plus chaud que la température que vous avez définie, vous recevez une notification. Vous pouvez régler la température sur 0 °C ou entre 2 et 100 °C. +'''Available settings: 0 or 2-100 C'''.fr-ca=Lorsqu'il fait plus chaud que la température que vous avez définie, vous recevez une notification. Vous pouvez régler la température sur 0 °C ou entre 2 et 100 °C. +'''Available settings: 0 or 2-100 C'''.de=Wenn die von Ihnen festgelegte Temperatur überschritten wird, erhalten Sie ein Benachrichtigung. Sie können 0°C oder einen Wert zwischen 2 und 100°C festlegen. +'''Available settings: 0 or 2-100 C'''.el=Όταν κάνει περισσότερη ζεστή από τη θερμοκρασία που έχετε ορίσει, θα λάβετε μια ειδοποίηση. Μπορείτε να ρυθμίσετε 0°C ή 2-100°C. +'''Available settings: 0 or 2-100 C'''.iw=כאשר הטמפרטורה גבוהה מזו שציינת, תקבל התראה. באפשרותך להגדיר 0°C או 2-100°C. +'''Available settings: 0 or 2-100 C'''.hi-in=जब यह आपके द्वारा सेट किए गए तापमान से अधिक गर्म होता है, तो आपको एक सूचना प्राप्त होगी। आप 0°C या 2-100°C सेट कर सकते हैं। +'''Available settings: 0 or 2-100 C'''.hu=Amikor melegebb van a beállított hőmérsékletnél, jelentést kap. 0 °C-ot vagy 2 és 100 °C közötti értéket adhat meg. +'''Available settings: 0 or 2-100 C'''.is=Þegar það er heitara en hitastigið sem þú stillir færðu tilkynningu. Hægt er að velja 0 °C eða 2–100 °C. +'''Available settings: 0 or 2-100 C'''.in=Saat suhu melebihi angka yang ditetapkan, Anda akan menerima notifikasi. Anda dapat menetapkan 0°C atau 2-100°C. +'''Available settings: 0 or 2-100 C'''.it=Quando la temperatura è superiore rispetto a quella impostata, si riceve una notifica. Potete impostare una temperatura di 0 o 2-100 °C. +'''Available settings: 0 or 2-100 C'''.ja=設定した温度より熱くなると、通知を受信します。0°Cまたは2~100°Cを設定できます。 +'''Available settings: 0 or 2-100 C'''.ko=설정한 온도보다 뜨거우면 알림을 받아요. 온도는 0°C 또는 2 - 100°C 사이로 설정할 수 있어요. +'''Available settings: 0 or 2-100 C'''.lv=Kad kļūs karstāks par jūsu iestatīto temperatūru, jūs saņemsit paziņojumu. Jūs varat iestatīt 0 °C vai 2-100 °C. +'''Available settings: 0 or 2-100 C'''.lt=Kai temperatūra bus aukštesnė nei nustatėte, gausite pranešimą. Galite nustatyti 0 °C arba 2–100 °C. +'''Available settings: 0 or 2-100 C'''.ms=Apabila suhu lebih panas daripada yang ditetapkan, anda akan menerima pemberitahuan. Anda boleh menetapkan 0°C atau 2-100°C. +'''Available settings: 0 or 2-100 C'''.no=Når det er varmere enn temperaturen du har angitt, får du et varsel. Du kan angi 0 °C eller 2–100 °C. +'''Available settings: 0 or 2-100 C'''.pl=Otrzymasz powiadomienie, gdy temperatura przekroczy ustawioną przez Ciebie wartość. Możesz ustawić 0°C lub 2–100°C. +'''Available settings: 0 or 2-100 C'''.pt=Quando estiver mais quente do que a temperatura que definir, receberá uma notificação. Pode definir 0 °C ou 2-100 °C. +'''Available settings: 0 or 2-100 C'''.ro=Atunci când temperatura o depășește pe cea setată, veți primi o notificare. Puteți seta 0 °C sau 2-100 °C. +'''Available settings: 0 or 2-100 C'''.ru=Если фактическая температура превысит заданную, вам поступит уведомление. Можно установить значение 0°C или 2–100°C. +'''Available settings: 0 or 2-100 C'''.sr=Kada je toplije od temperature koju ste podesili, dobićete obaveštenje. Možete da podesite 0°C ili 2–100°C. +'''Available settings: 0 or 2-100 C'''.sk=Keď prekročí nastavenú teplotu, dostanete oznámenie. Môžete nastaviť teplotu 0 °C alebo 2 až 100 °C. +'''Available settings: 0 or 2-100 C'''.sl=Ko je temperatura višja od nastavljene, boste prejeli obvestilo nastavite lahko 0 °C ali 2–100 °C. +'''Available settings: 0 or 2-100 C'''.es=Recibirás una notificación cuando la temperatura sea superior a la que establezcas. Puedes establecer 0 °C o 2-100 °C. +'''Available settings: 0 or 2-100 C'''.sv=När det är varmare än temperaturen som du anger får du en avisering. Du kan ange 0 °C eller 2–100 °C. +'''Available settings: 0 or 2-100 C'''.th=เมื่ออุณหภูมิร้อนขึ้นกว่าที่คุณตั้งค่าไว้ คุณจะได้รับการแจ้งเตือน คุณสามารถตั้งค่า 0°C หรือ 2-100°C ได้ +'''Available settings: 0 or 2-100 C'''.tr=Ortam, ayarladığınız değerden daha sıcak olduğunda bildirim alırsınız. 0 °C veya 2-100 °C arasında bir değeri ayarlayabilirsiniz. +'''Available settings: 0 or 2-100 C'''.uk=Якщо температура перевищить установлену, ви отримаєте сповіщення. Доступні значення: 0°C та 2–100°C. +'''Available settings: 0 or 2-100 C'''.vi=Khi nhiệt độ nóng hơn mức bạn đã đặt, bạn sẽ nhận được thông báo. Bạn có thể đặt 0°C hoặc 2-100°C. +'''Sound notifications status'''.en=Sound notifications +'''Sound notifications status'''.en-gb=Sound notifications +'''Sound notifications status'''.en-us=Sound notifications +'''Sound notifications status'''.en-ca=Sound notifications +'''Sound notifications status'''.sq=Njoftimet zanore +'''Sound notifications status'''.ar=إشعارات الصوت +'''Sound notifications status'''.be=Гукавыя апавяшчэнні +'''Sound notifications status'''.sr-ba=Zvučna obavještenja +'''Sound notifications status'''.bg=Звукови уведомления +'''Sound notifications status'''.ca=Notificacions de so +'''Sound notifications status'''.zh-cn=声音通知 +'''Sound notifications status'''.zh-hk=聲音通知 +'''Sound notifications status'''.zh-tw=音效通知 +'''Sound notifications status'''.hr=Zvučne obavijesti +'''Sound notifications status'''.cs=Zvuková oznámení +'''Sound notifications status'''.da=Lydmeddelelser +'''Sound notifications status'''.nl=Geluid meldingen +'''Sound notifications status'''.et=Heliteavitused +'''Sound notifications status'''.fi=Ääni-ilmoitukset +'''Sound notifications status'''.fr=Notifications sonores +'''Sound notifications status'''.fr-ca=Notifications sonores +'''Sound notifications status'''.de=Tonbenachrichtigungen +'''Sound notifications status'''.el=Ειδοποιήσεις ήχου +'''Sound notifications status'''.iw=התראות צליל +'''Sound notifications status'''.hi-in=ध्वनि सूचनाएँ +'''Sound notifications status'''.hu=Hangos értesítések +'''Sound notifications status'''.is=Hljóðviðvaranir +'''Sound notifications status'''.in=Notifikasi suara +'''Sound notifications status'''.it=Notifiche audio +'''Sound notifications status'''.ja=通知音 +'''Sound notifications status'''.ko=소리 알림 +'''Sound notifications status'''.lv=Skaņas paziņojumi +'''Sound notifications status'''.lt=Garso pranešimai +'''Sound notifications status'''.ms=Pemberitahuan bunyi +'''Sound notifications status'''.no=Lydvarsler +'''Sound notifications status'''.pl=Powiadomienia dźwiękowe +'''Sound notifications status'''.pt=Notificações de som +'''Sound notifications status'''.ro=Notificări sonore +'''Sound notifications status'''.ru=Звуковые уведомления +'''Sound notifications status'''.sr=Zvučna obaveštenja +'''Sound notifications status'''.sk=Zvukové oznámenia +'''Sound notifications status'''.sl=Zvočna obvestila +'''Sound notifications status'''.es=Notificaciones de sonido +'''Sound notifications status'''.sv=Ljudaviseringar +'''Sound notifications status'''.th=การแจ้งเตือนเสียง +'''Sound notifications status'''.tr=Sesli bildirimler +'''Sound notifications status'''.uk=Звукові сповіщення +'''Sound notifications status'''.vi=Thông báo âm thanh +'''24 hours'''.en=24 hours +'''24 hours'''.en-gb=24 hours +'''24 hours'''.en-us=24 hours +'''24 hours'''.en-ca=24 hours +'''24 hours'''.sq=24 orë +'''24 hours'''.ar=٢٤ ساعة +'''24 hours'''.be=24 гадзіны +'''24 hours'''.sr-ba=24 sata +'''24 hours'''.bg=24 часа +'''24 hours'''.ca=24 hores +'''24 hours'''.zh-cn=24 小时 +'''24 hours'''.zh-hk=24 小時 +'''24 hours'''.zh-tw=24 小時 +'''24 hours'''.hr=24 sata +'''24 hours'''.cs=24 hodin +'''24 hours'''.da=24 timer +'''24 hours'''.nl=24 uur +'''24 hours'''.et=24 tundi +'''24 hours'''.fi=24 tuntia +'''24 hours'''.fr=24 heures +'''24 hours'''.fr-ca=24 heures +'''24 hours'''.de=24 Stunden +'''24 hours'''.el=24 ώρες +'''24 hours'''.iw=24 שעות +'''24 hours'''.hi-in=24 घंटे +'''24 hours'''.hu=24 óra +'''24 hours'''.is=Sólarhringur +'''24 hours'''.in=24 jam +'''24 hours'''.it=24 ore +'''24 hours'''.ja=24時間 +'''24 hours'''.ko=24시간 +'''24 hours'''.lv=24 stundas +'''24 hours'''.lt=24 val. +'''24 hours'''.ms=24 jam +'''24 hours'''.no=24 timer +'''24 hours'''.pl=24 godziny +'''24 hours'''.pt=24 horas +'''24 hours'''.ro=24 de ore +'''24 hours'''.ru=24 часа +'''24 hours'''.sr=24 sata +'''24 hours'''.sk=24 hodín +'''24 hours'''.sl=24 ur +'''24 hours'''.es=24 horas +'''24 hours'''.sv=24 timmar +'''24 hours'''.th=24 ชั่วโมง +'''24 hours'''.tr=24 saat +'''24 hours'''.uk=24 години +'''24 hours'''.vi=24 giờ +'''Overheat temperature threshold'''.en=Overheat temperature threshold +'''Overheat temperature threshold'''.en-gb=Overheat temperature threshold +'''Overheat temperature threshold'''.en-us=Overheat temperature threshold +'''Overheat temperature threshold'''.en-ca=Overheat temperature threshold +'''Overheat temperature threshold'''.sq=Pragu i temp. për mbinxehje +'''Overheat temperature threshold'''.ar=حد درجة الحرارة المرتفعة +'''Overheat temperature threshold'''.be=Парог тэмпературы перагрэву +'''Overheat temperature threshold'''.sr-ba=Prag temperature pregrijavanja +'''Overheat temperature threshold'''.bg=Праг на температура на прегряване +'''Overheat temperature threshold'''.ca=Llindar de temperatura excessiva +'''Overheat temperature threshold'''.zh-cn=过热温度阈值 +'''Overheat temperature threshold'''.zh-hk=過熱溫度閾值 +'''Overheat temperature threshold'''.zh-tw=溫度過熱臨界值 +'''Overheat temperature threshold'''.hr=Prag temperature pregrijavanja +'''Overheat temperature threshold'''.cs=Prahová hodn. teploty přehřátí +'''Overheat temperature threshold'''.da=Tærskelværdi for overophedning +'''Overheat temperature threshold'''.nl=Grens temperatuur oververhitting +'''Overheat temperature threshold'''.et=Ülekuumenemise temperat. lävi +'''Overheat temperature threshold'''.fi=Ylikuumenemislämpötilan kynnysarvo +'''Overheat temperature threshold'''.fr=Seuil de surchauffe +'''Overheat temperature threshold'''.fr-ca=Seuil de surchauffe +'''Overheat temperature threshold'''.de=Überhitzungstemperatur-Grenzwert +'''Overheat temperature threshold'''.el=Όριο θερμοκρασίας υπερθέρμανσης +'''Overheat temperature threshold'''.iw=סף טמפרטורה של התחממות יתר +'''Overheat temperature threshold'''.hi-in=बहुत गर्म तापमान थ्रेसहोल्ड +'''Overheat temperature threshold'''.hu=Túlmelegedési küszöbhőmérséklet +'''Overheat temperature threshold'''.is=Viðmiðunmörk fyrir hitast. ofh. +'''Overheat temperature threshold'''.in=Ambang batas kelebihan suhu +'''Overheat temperature threshold'''.it=Soglia di surriscaldamento +'''Overheat temperature threshold'''.ja=高温閾値 +'''Overheat temperature threshold'''.ko=과열 온도 기준 +'''Overheat temperature threshold'''.lv=Pārkaršanas temp. slieksnis +'''Overheat temperature threshold'''.lt=Perkaitimo temperat. slenkstis +'''Overheat temperature threshold'''.ms=Ambang suhu terlampau panas +'''Overheat temperature threshold'''.no=Terskel for overtemperatur +'''Overheat temperature threshold'''.pl=Próg temperatury przegrzania +'''Overheat temperature threshold'''.pt=Limite de temp. sobreaquecimento +'''Overheat temperature threshold'''.ro=Prag temperatură supraîncălzire +'''Overheat temperature threshold'''.ru=Порог температуры перегрева +'''Overheat temperature threshold'''.sr=Granična vredn. temp. pregrevanja +'''Overheat temperature threshold'''.sk=Prah teploty prehriatia +'''Overheat temperature threshold'''.sl=Temperaturni prag pregrevanja +'''Overheat temperature threshold'''.es=Umbral de exceso de temperatura +'''Overheat temperature threshold'''.sv=Tröskel för överhettningstemp. +'''Overheat temperature threshold'''.th=ขอบเขตอุณหภูมิร้อนจัด +'''Overheat temperature threshold'''.tr=Aşırı ısınma sıcaklık eşiği +'''Overheat temperature threshold'''.uk=Поріг температури перегріву +'''Overheat temperature threshold'''.vi=Ngưỡng nhiệt độ quá nóng +'''Medium'''.en=Medium +'''Medium'''.en-gb=Medium +'''Medium'''.en-us=Medium +'''Medium'''.en-ca=Medium +'''Medium'''.sq=Mesatare +'''Medium'''.ar=متوسطة +'''Medium'''.be=Сярэдняя +'''Medium'''.sr-ba=Umjereno +'''Medium'''.bg=Средна +'''Medium'''.ca=Mitjana +'''Medium'''.zh-cn=中 +'''Medium'''.zh-hk=中 +'''Medium'''.zh-tw=中 +'''Medium'''.hr=Srednja +'''Medium'''.cs=Střední +'''Medium'''.da=Middel +'''Medium'''.nl=Gemiddeld +'''Medium'''.et=Keskmine +'''Medium'''.fi=Normaali +'''Medium'''.fr=Moyenne +'''Medium'''.fr-ca=Moyenne +'''Medium'''.de=Mittel +'''Medium'''.el=Μεσαία +'''Medium'''.iw=בינונית +'''Medium'''.hi-in=मध्‍यम +'''Medium'''.hu=Közepes +'''Medium'''.is=Miðlungs +'''Medium'''.in=Sedang +'''Medium'''.it=Media +'''Medium'''.ja=中 +'''Medium'''.ko=보통 +'''Medium'''.lv=Vidējs +'''Medium'''.lt=Vidutinis +'''Medium'''.ms=Sederhana +'''Medium'''.no=Middels +'''Medium'''.pl=Średnia +'''Medium'''.pt=Média +'''Medium'''.ro=Medie +'''Medium'''.ru=Средняя +'''Medium'''.sr=Srednja +'''Medium'''.sk=Stredná +'''Medium'''.sl=Srednje +'''Medium'''.es=Media +'''Medium'''.sv=Medel +'''Medium'''.th=ปานกลาง +'''Medium'''.tr=Orta +'''Medium'''.uk=Середній +'''Medium'''.vi=Trung bình +'''Advanced settings'''.en=Advanced settings +'''Advanced settings'''.en-gb=Advanced settings +'''Advanced settings'''.en-us=Advanced settings +'''Advanced settings'''.en-ca=Advanced settings +'''Advanced settings'''.en-ph=Advanced settings +'''Advanced settings'''.sq=Cilësime të avancuara +'''Advanced settings'''.ar=الضبط المتقدم +'''Advanced settings'''.be=Дадатковыя налады +'''Advanced settings'''.sr-ba=Napredne postavke +'''Advanced settings'''.bg=Разширени настройки +'''Advanced settings'''.ca=Ajustaments avançats +'''Advanced settings'''.zh-cn=高级设置 +'''Advanced settings'''.zh-hk=進階設定 +'''Advanced settings'''.zh-tw=進階設定 +'''Advanced settings'''.hr=Napredne postavke +'''Advanced settings'''.cs=Rozšířené nastavení +'''Advanced settings'''.da=Avancerede indstillinger +'''Advanced settings'''.nl=Geavanceerde instellingen +'''Advanced settings'''.et=Täpsemad seaded +'''Advanced settings'''.fi=Lisäasetukset +'''Advanced settings'''.fr=Paramètres avancés +'''Advanced settings'''.fr-ca=Paramètres avancés +'''Advanced settings'''.de=Erweiterte Einstellungen +'''Advanced settings'''.el=Σύνθετες ρυθμίσεις +'''Advanced settings'''.iw=הגדרות מתקדמות +'''Advanced settings'''.hi-in=उन्नत सेटिंग्स +'''Advanced settings'''.hu=Speciális beállítások +'''Advanced settings'''.is=Ítarlegar stillingar +'''Advanced settings'''.in=Pengaturan lanjutan +'''Advanced settings'''.it=Impostazioni avanzate +'''Advanced settings'''.ja=詳細設定 +'''Advanced settings'''.ko=고급 설정 +'''Advanced settings'''.lv=Papildu iestatījumi +'''Advanced settings'''.lt=Papildomi nustatymai +'''Advanced settings'''.ms=Aturan lanjutan +'''Advanced settings'''.no=Avanserte innstillinger +'''Advanced settings'''.pl=Ustawienia zaawansowane +'''Advanced settings'''.pt=Definições avançadas +'''Advanced settings'''.ro=Setări avansate +'''Advanced settings'''.ru=Дополнительные параметры +'''Advanced settings'''.sr=Napredna podešavanja +'''Advanced settings'''.sk=Rozšírené nastavenia +'''Advanced settings'''.sl=Napredne nastavitve +'''Advanced settings'''.es=Ajustes avanzados +'''Advanced settings'''.sv=Avancerade inställningar +'''Advanced settings'''.th=การตั้งค่าขั้นสูง +'''Advanced settings'''.tr=Gelişmiş ayarlar +'''Advanced settings'''.uk=Додаткові налаштування +'''Advanced settings'''.vi=Cài đặt nâng cao +'''Temperature report interval'''.en=Temperature report interval +'''Temperature report interval'''.en-gb=Temperature report interval +'''Temperature report interval'''.en-us=Temperature report interval +'''Temperature report interval'''.en-ca=Temperature report interval +'''Temperature report interval'''.sq=Intervali i raportit për temp. +'''Temperature report interval'''.ar=الفاصل الزمني لتقرير درجة الحرارة +'''Temperature report interval'''.be=Інтэрвал справаздач аб тэмпер +'''Temperature report interval'''.sr-ba=Interval izvješt. o temperaturi +'''Temperature report interval'''.bg=Интервал за отчитане на температ. +'''Temperature report interval'''.ca=Interval d'informe de temperatura +'''Temperature report interval'''.zh-cn=温度报告间隔 +'''Temperature report interval'''.zh-hk=溫度報告時間間隔 +'''Temperature report interval'''.zh-tw=溫度報告時間間隔 +'''Temperature report interval'''.hr=Interval izvješća o temperaturi +'''Temperature report interval'''.cs=Interval hlášení teploty +'''Temperature report interval'''.da=Interval for temperaturrapport +'''Temperature report interval'''.nl=Interval temperatuurrapport +'''Temperature report interval'''.et=Temperatuurist teavitamise välp +'''Temperature report interval'''.fi=Lämpötilaraportin aikaväli +'''Temperature report interval'''.fr=Intervalle rapport de température +'''Temperature report interval'''.fr-ca=Intervalle rapport de température +'''Temperature report interval'''.de=Temperaturberichtsintervall +'''Temperature report interval'''.el=Διάστημα αναφοράς θερμοκρασίας +'''Temperature report interval'''.iw=מרווח דוח טמפרטורה +'''Temperature report interval'''.hi-in=तापमान रिपोर्ट अंतराल +'''Temperature report interval'''.hu=Hőmérsékleti jelentési időköze +'''Temperature report interval'''.is=Tími á milli hitastigsskráninga +'''Temperature report interval'''.in=Interval laporan suhu +'''Temperature report interval'''.it=Intervallo report temperatura +'''Temperature report interval'''.ja=温度レポートの間隔 +'''Temperature report interval'''.ko=온도 알림 간격 +'''Temperature report interval'''.lv=Temperatūras ziņojuma intervāls +'''Temperature report interval'''.lt=Temperatūros praneš. intervalas +'''Temperature report interval'''.ms=Selang laporan suhu +'''Temperature report interval'''.no=Temperaturrapportintervall +'''Temperature report interval'''.pl=Interwał raportów o temperat. +'''Temperature report interval'''.pt=Intervalo do relatório temperatura +'''Temperature report interval'''.ro=Interval raportare temperatură +'''Temperature report interval'''.ru=Интервал отчета о температуре +'''Temperature report interval'''.sr=Interval izveštaja o temperaturi +'''Temperature report interval'''.sk=Interval hlásenia teploty +'''Temperature report interval'''.sl=Interval poročil o temperaturi +'''Temperature report interval'''.es=Intervalo de informe temperatura +'''Temperature report interval'''.sv=Intervall för temperaturrapport +'''Temperature report interval'''.th=ช่วงเวลารายงานอุณหภูมิ +'''Temperature report interval'''.tr=Sıcaklık raporlama aralığı +'''Temperature report interval'''.uk=Інтервал звіту про температуру +'''Temperature report interval'''.vi=Chu kỳ báo cáo nhiệt độ +'''Available settings: 1-100 C'''.en=Choose how much the temperature must differ from the previously reported temperature to send a new temperature report. You can enter a value from 1 to 100. The value you enter will be multiplied by 0.1. For example, if you enter 20, a report will be sent whenever the temperature changes by 2°C or more. +'''Available settings: 1-100 C'''.en-gb=Choose how much the temperature must differ from the previously reported temperature to send a new temperature report. You can enter a value from 1 to 100. The value you enter will be multiplied by 0.1. For example, if you enter 20, a report will be sent whenever the temperature changes by 2°C or more. +'''Available settings: 1-100 C'''.en-us=Choose how much the temperature must differ from the previously reported temperature to send a new temperature report. You can enter a value from 1 to 100. The value you enter will be multiplied by 0.1. For example, if you enter 20, a report will be sent whenever the temperature changes by 2°C or more. +'''Available settings: 1-100 C'''.en-ca=Choose how much the temperature must differ from the previously reported temperature to send a new temperature report. You can enter a value from 1 to 100. The value you enter will be multiplied by 0.1. For example, if you enter 20, a report will be sent whenever the temperature changes by 2°C or more. +'''Available settings: 1-100 C'''.sq=Zgjidh sa duhet të ndryshojë temperatura nga temperatura e raportuar më parë, që të dërgohet një raport i ri për temperaturën. Mund të futësh një vlerë nga 1 në 100. Vlera që fut do të shumëzohet me 0.1. Për shembull, në qoftë se fut 20, raporti do të dërgohet sa herë që temperatura ndryshon me 2°C ose më shumë. +'''Available settings: 1-100 C'''.ar=اختر القيمة التي يجب أن تختلف بها درجة الحرارة عن درجة الحرارة السابق الإبلاغ عنها لإرسال تقرير درجة الحرارة الجديد. ويمكنك إدخال قيمة من ۱ إلى ۱۰۰. وسيتم ضرب القيمة التي تقوم بإدخالها في ۰,۱. وعلى سبيل المثال، إذا قمت بإدخال ۲۰، سيتم إرسال تقرير عندما تتغير درجة الحرارة بـ ۲ درجة مئوية أو أكثر. +'''Available settings: 1-100 C'''.be=Выберыце, наколькі тэмпература павінна адрознівацца ад пазначанай у мінулай справаздачы для адпраўкі новай справаздачы. Вы можаце ўвесці значэнне ад 1 да 100. Уведзенае значэнне будзе памножана на 0,1. Напрыклад, калі ўвесці 20, справаздачы будуць адпраўляцца ў выпадку змянення тэмпературы на 2 °C або больш. +'''Available settings: 1-100 C'''.sr-ba=Izaberite koliko se temperatura mora razlikovati od prethodno prijavljene temperature u svrhu slanja novog izvještaja o temperaturi. Možete unijeti vrijednost od 1 do 100. Vrijednost koju unesete pomnožit će se s 0,1. Na primjer, ako unesete 20, izvještaj će se poslati kad god se temperatura promijeni za 2°C ili više. +'''Available settings: 1-100 C'''.bg=Изберете колко температурата трябва да се различава от отчетената по-рано температура, за да се изпрати нов отчет за температурата. Може да въведете стойност от 1 до 100. Стойността, която въведете, ще се умножи по 0,1. Например, ако въведете 20, всеки път ще се изпраща отчет, когато температурата се промени с 2°C или повече. +'''Available settings: 1-100 C'''.ca=Tria quant ha de diferir la temperatura respecte a la temperatura notificada anterior per enviar un nou informe de temperatura. Pots introduir un valor entre 1 i 100. El valor que introdueixis es multiplicarà per 0,1. Per exemple, si introdueixes 20, s'enviarà un informe quan la temperatura canviï 2 °C o més. +'''Available settings: 1-100 C'''.zh-cn=选择温度必须与以前报告的温度相差多少才能发送新的温度报告。您可以输入 1 到 100 之间的值。您输入的值将乘以 0.1。例如,如果输入 20,则每当温度变化超过 2°C 或更高时,就会发送报告。 +'''Available settings: 1-100 C'''.zh-hk=選擇當前溫度與之前報告的溫度必須相差多少才發送新的溫度報告。您可以輸入從 1 至 100 的值。您輸入的值將乘以 0.1。例如,若您輸入 20,則會在溫度變化 2°C 或以上時發送報告。 +'''Available settings: 1-100 C'''.zh-tw=請選擇目前溫度需與先前回報溫度有多大差異,才會傳送新的溫度報告。您可輸入 1 至 100 的數值。將以輸入的數值乘以 0.1 進行計算。舉例來說,如輸入 20,則只要溫差超過 2°C 以上,隨即自動傳送報告。 +'''Available settings: 1-100 C'''.hr=Odaberite koliko se temperatura mora razlikovati od prethodno prijavljene temperature u svrhu slanja novog izvješća o temperaturi. Možete unijeti vrijednost od 1 do 100. Vrijednost koju unesete pomnožit će se s 0,1. Na primjer, ako unesete 20, izvješće će se poslati kada se temperatura promijeni za 2 °C ili više. +'''Available settings: 1-100 C'''.cs=Zvolte, o kolik se musí teplota lišit od předchozí nahlášené teploty, aby byla zaslána nová zpráva o teplotě. Můžete zadat hodnotu od 1 do 100. Zadaná hodnota bude vynásobena koeficientem 0,1. Například když zadáte hodnotu 20, zpráva bude zaslána vždy, když se teplota změní o 2 °C nebo více. +'''Available settings: 1-100 C'''.da=Vælg, hvor meget temperaturen skal afvige fra den tidligere rapporterede temperatur, før der skal sendes en ny temperaturrapport. Du kan angive en værdi fra 1 til 100. Den værdi, du angiver, bliver ganget med 0,1. Så hvis du f.eks. angiver 20, så sendes der en rapport, når temperaturen varierer med 2 °C eller mere. +'''Available settings: 1-100 C'''.nl=Kies hoeveel de temperatuur moet verschillen van de eerder gerapporteerde temperatuur om een nieuw temperatuurrapport te sturen. U kunt een waarde tussen 1 en 100 invoeren. De waarde die u invoert, wordt vermenigvuldigd met 0,1. Als u bijvoorbeeld 20 invoert, wordt er een rapport verzonden wanneer de temperatuur 2°C of meer verschilt. +'''Available settings: 1-100 C'''.et=Valige, kui palju peab temperatuur erinema varasemalt teatatud temperatuurist, et saata uus temperatuuri aruanne. Saate sisestada väärtuse vahemikus 1 kuni 100. Sisestatud väärtus korrutatakse väärtusega 0,1. Näiteks, kui sisestate 20, saadetakse teade, kui temperatuur muutub 2 °C või rohkem. +'''Available settings: 1-100 C'''.fi=Valitse, kuinka paljon lämpötilan on poikettava viimeksi ilmoitetusta lämpötilasta, jotta lähetetään uusi lämpötilaraportti. Voit antaa arvoksi 1–100. Antamasi arvo kerrotaan 0,1:llä. Jos siis annat arvoksi esimerkiksi 20, raportti lähetetään, kun lämpötila muuttuu vähintään 2 °C. +'''Available settings: 1-100 C'''.fr=Déterminez de combien la température doit différer par rapport à la température signalée précédemment pour envoyer un rapport de nouvelle température. Vous pouvez entrer une valeur comprise entre 1 et 100. La valeur que vous entrez est multipliée par 0,1. Par exemple, si vous entrez 20, un rapport est envoyé lorsque la température varie d'au moins 2 °C. +'''Available settings: 1-100 C'''.fr-ca=Déterminez de combien la température doit différer par rapport à la température signalée précédemment pour envoyer un rapport de nouvelle température. Vous pouvez saisir une valeur comprise entre 1 et 100. La valeur que vous saisissez est multipliée par 0,1. Par exemple, si vous saisissez 20, un rapport est envoyé lorsque la température varie d'au moins 2 °C. +'''Available settings: 1-100 C'''.de=Wählen Sie aus, wie hoch der Unterschied zur zuvor gemeldeten Temperatur sein muss, damit ein neuer Temperaturbericht gesendet wird. Sie können einen Wert zwischen 1 und 100 eingeben. Der von Ihnen eingegebene Wert wird mit 0,1 multipliziert. Wenn Sie beispielsweise 20 eingeben, wird ein Bericht gesendet, wenn sich die Temperatur um mindestens 2°C ändert. +'''Available settings: 1-100 C'''.el=Επιλέξτε πόσο πρέπει να διαφέρει η θερμοκρασία από τη θερμοκρασία που αναφέρθηκε προηγουμένως για να στείλετε μια νέα αναφορά θερμοκρασίας. Μπορείτε να εισαγάγετε μια τιμή από 1 έως 100. Η τιμή που θα εισάγετε θα πολλαπλασιαστεί επί 0,1. Για παράδειγμα, εάν εισαγάγετε 20, μια αναφορά θα αποστέλλεται κάθε φορά που η θερμοκρασία αλλάζει κατά 2°C ή περισσότερο. +'''Available settings: 1-100 C'''.iw=בחר בכמה על הטמפרטורה לחרוג מהטמפרטורה שדווחה לאחרונה כדי שיישלח דוח טמפרטורה. ניתן להזין ערך בין 1 ל-100. הערך שתזין יוכפל פי 0.1. לדוגמה, אם תזין 20, יישלח דוח בכל פעם שהטמפרטורה משתנה ב-2°C או יותר. +'''Available settings: 1-100 C'''.hi-in=चुनें कि नई तापमान रिपोर्ट भेजने के लिए, तापमान पिछली बार रिपोर्ट किए तापमान से कितना भिन्न होना चाहिए। आप 1 से 100 तक का कोई मान प्रविष्ट कर सकते हैं। आपके द्वारा प्रविष्ट किए गए मान का 0.1 से गुणा किया जाएगा। जैसे कि, अगर आप 20 प्रविष्ट करते हैं, तो जब भी 2°C या उससे ज्यादा बदलता है, तब एक रिपोर्ट भेजी जाएगी। +'''Available settings: 1-100 C'''.hu=Válassza ki, hogy mennyivel kell eltérnie a hőmérsékletnek a korábban jelentettől ahhoz, hogy a rendszer új hőmérsékleti jelentést küldjön. 1 és 100 közötti értéket adhat meg. A megadott értéket a rendszer beszorozza 0,1-gyel. Ha például a 20 értéket adja meg, a hőmérséklet legalább 2 °C-os változásakor készül jelentés. +'''Available settings: 1-100 C'''.is=Veldu hversu mikil frávik mega vera í mælingum hitastigs miðað við fyrri skráningar á hitastigi til að ný hitastigsskýrsla verði send. Þú getur fært inn gildi frá 1 til 100. Gildið sem þú færir inn verður margfaldað með 0,1. Ef þú t.d. færir inn 20 verður send skýrsla í hvert sinn sem hitastigið breytist um 2 °C eða meira. +'''Available settings: 1-100 C'''.in=Pilih besar perbedaan suhu dari laporan suhu sebelumnya untuk mengirimkan laporan suhu yang baru. Anda dapat memasukkan angka dari 1 hingga 100. Angka yang dimasukkan akan dikalikan dengan 0,1. Contoh, jika Anda memasukkan angka 20, laporan akan dikirimkan setiap kali suhu berubah sebesar 2°C atau lebih. +'''Available settings: 1-100 C'''.it=Decidete di quanto deve differire la temperatura rispetto a quella segnalata in precedenza, per inviare un nuovo report sulla temperatura. Potete inserire un valore da 1 a 100, che verrà moltiplicato per 0,1. Se, per esempio, inserite 20, verrà inviato un report qualora la temperatura cambiasse di almeno 2 °C. +'''Available settings: 1-100 C'''.ja=前回のレポート時から温度が何度変化したら新しい温度レポートを送信するかを選択してください。1~100の値を入力できます。入力した値に0.1を掛けた値が求める温度になります。例えば、20と入力すると、温度が2°C以上変化したときにレポートが送信されます。 +'''Available settings: 1-100 C'''.ko=이전에 알린 온도보다 몇 도 높아졌을 때 알림을 받을지 선택해 주세요. 값은 1 - 100 사이로 입력할 수 있어요. 입력한 값에 0.1을 곱한 수만큼의 온도 변화에 따라 알림을 받아요. 예를 들어, 20을 입력했다면 온도가 2°C 이상 높아지면 알림을 받아요. +'''Available settings: 1-100 C'''.lv=Izvēlieties, cik lielai ir jābūt temperatūras atšķirībai no iepriekš uzrādītās temperatūras, lai jums tiktu nosūtīts jauns temperatūras ziņojums. Jūs varat ievadīt vērtību no 1 līdz 100. Ievadītā vērtība tiks reizināta ar 0,1. Piemēram, ja ievadīsit 20, ziņojums tiks nosūtīts ikreiz, kad temperatūra mainīsies par vismaz 2 °C. +'''Available settings: 1-100 C'''.lt=Pasirinkite, koks temperatūros skirtumas turi būti nuo anksčiau nurodytos temperatūros, kad būtų siunčiama nauja temperatūros ataskaita. Galite įvesti reikšmę nuo 1 iki 100. Jūsų įvesta reikšmė bus padauginta iš 0,1. Pavyzdžiui, jei įvesite 20, ataskaita bus siunčiama kaskart temperatūrai pasikeitus 2 °C ar daugiau. +'''Available settings: 1-100 C'''.ms=Pilih jumlah perbezaan suhu daripada laporan suhu sebelumnya untuk menghantar laporan suhu terbaru. Anda boleh masukkan nilai daripada 1 hingga 100. Nilai yang anda masukkan akan didarabkan dengan 0.1. Contohnya, jika anda memasukkan 20, laporan akan dihantar setiap kali suhu berubah sebanyak 2°C atau lebih. +'''Available settings: 1-100 C'''.no=Velg hvor mye temperaturen må avvike fra tidligere rapportert temperatur for å sende en ny temperaturrapport. Du kan angi en verdi fra 1 til 100. Verdien du angir, blir ganget med 0,1. Hvis du for eksempel angir 20, sendes en rapport når temperaturen endres med 2 °C eller mer. +'''Available settings: 1-100 C'''.pl=Wybierz, jak bardzo temperatura musi różnić się od poprzednio zarejestrowanej, aby wysłać nowy raport na temat temperatury. Możesz wprowadzić wartość od 1 do 100. Wprowadzona wartość zostanie pomnożona przez 0,1. Na przykład: jeśli wprowadzisz wartość 20, raport zostanie wysłany, gdy temperatura zmieni się o 2 stopnie lub więcej. +'''Available settings: 1-100 C'''.pt=Escolha a diferença de temperatura que tem de existir em relação à temperatura anteriormente reportada, para que seja enviado um novo relatório de temperatura. Pode introduzir um valor de 1 a 100. O valor introduzido será multiplicado por 0,1. Por exemplo, se introduzir 20, será enviado um relatório sempre que a temperatura se alterar em 2 °C ou mais. +'''Available settings: 1-100 C'''.ro=Alegeți cu cât trebuie să difere temperatura față de temperatura raportată anterior pentru a se trimite un nou raport de temperatură. Puteți introduce o valoare de la 1 la 100. Valoarea introdusă va fi înmulțită cu 0,1. De exemplu, dacă introduceți 20, va fi trimis un raport de fiecare dată când temperatura s-a modificat cu 2 °C sau mai mult. +'''Available settings: 1-100 C'''.ru=Укажите, при какой разнице между фактическим и ранее зарегистрированным значением температуры будет отправляться новый отчет о температуре. Можно ввести значение от 1 до 100. Указанное значение будет умножено на 0,1. Например, при вводе значения 20 отчет будет отправляться каждый раз, когда температура изменится на 2°C или более. +'''Available settings: 1-100 C'''.sr=Odaberite koliko temperatura mora da se razlikuje od prethodno prijavljene temperature da bi se poslala nova prijava temperature. Možete da unesete vrednost od 1 do 100. Vrednost koju unesete će biti pomnožena sa 0,1. Na primer, ako unesete 20, prijava će se poslati svaki put kada se temperatura promeni za 2°C ili više. +'''Available settings: 1-100 C'''.sk=Zvoľte, o koľko sa musí teplota líšiť od predchádzajúcej nahlásenej teploty, aby sa odoslala nová správa o teplote. Môžete zadať hodnotu od 1 do 100. Zadaná hodnota bude vynásobená koeficientom 0,1. Ak zadáte napríklad hodnotu 20, správa bude odoslaná vždy, keď sa teplota zmení o 2 °C alebo viac. +'''Available settings: 1-100 C'''.sl=Izberite, kolikšna mora biti temperaturna razlika glede na prejšnjo sporočeno temperaturo, da se bo poslalo novo poročilo o temperaturi. Vnesete lahko vrednost od 1 do 100. Vnesena vrednost bo pomnožena z 0,1. Če na primer vnesete 20, bo poročilo poslano, ko se temperatura spremeni za 2 °C ali več. +'''Available settings: 1-100 C'''.es=Elige qué diferencia de temperatura debe producirse con respecto a la temperatura comunicada anteriormente para enviar un nuevo informe de temperatura. Puedes introducir un valor de entre 1 y 100. El valor que introduzcas se multiplicará por 0,1. Por ejemplo, si introduces 20, se enviará un informe cuando la temperatura cambie en 2 °C o más. +'''Available settings: 1-100 C'''.sv=Välj hur mycket temperaturen måste skilja sig från den tidigare rapporterade temperaturen för att en ny temperaturrapport ska skickas. Du kan välja ett värde mellan 1 och 100. Värdet du anges multipliceras med 0,1. Om du t.ex. anger 20 skickas en rapport när temperaturen ändras med 2 °C eller mer. +'''Available settings: 1-100 C'''.th=เลือกว่าอุณหภูมิจะต้องแตกต่างจากอุณหภูมิที่รายงานก่อนหน้าเท่าใดจึงจะส่งรายงานอุณหภูมิใหม่ คุณสามารถใส่ค่าได้จาก 1 ถึง 100 ค่าที่คุณใส่จะถูกคูณด้วย 0.1 เช่น หากคุณใส่ 20 รายงานจะถูกส่งเมื่ออุณหภูมิเปลี่ยนไปอย่างน้อย 2°C +'''Available settings: 1-100 C'''.tr=Yeni bir sıcaklık raporu göndermek için önceden bildirilen sıcaklığa göre kaç derece farklılık olması gerektiğini seçin. 1-100 arasında bir değer girebilirsiniz. Girdiğiniz değer, 0,1 ile çarpılır. Örneğin, 20 değerini girerseniz sıcaklık 2 °C veya daha fazla değiştiğinde rapor gönderilir. +'''Available settings: 1-100 C'''.uk=Виберіть, наскільки температура має відрізнятися від раніше записаної для надсилання сповіщення про температуру. Можна ввести значення від 1 до 100, яке потім буде помножено на 0,1. Наприклад, якщо ввести 20, сповіщення надсилатиметься кожного разу, коли температура змінюватиметься на 2°C чи більше. +'''Available settings: 1-100 C'''.vi=Chọn mức nhiệt độ khác biệt với nhiệt độ đã báo cáo trước đó để gửi báo cáo nhiệt độ mới. Bạn có thể nhập một giá trị từ 1 đến 100. Giá trị bạn nhập sẽ được nhân với 0,1. Ví dụ, nếu bạn nhập 20, báo cáo sẽ được gửi mỗi khi nhiệt độ thay đổi từ 2°C trở lên. +'''To check smoke detection state'''.en=Checking the smoke detection state +'''To check smoke detection state'''.en-uk=Checking the smoke detection state +'''To check smoke detection state'''.en-us=Checking the smoke detection state +'''To check smoke detection state'''.en-ca=Checking the smoke detection state +'''To check smoke detection state'''.sq=Kontroll i statusit të pikasjes së tymit +'''To check smoke detection state'''.ar=التحقق من حالة اكتشاف الدخان +'''To check smoke detection state'''.be=Праверка стану выяўлення дыму +'''To check smoke detection state'''.sr-ba=Provjera stanja prepoznavanja dima +'''To check smoke detection state'''.bg=Проверка състоянието на откриване на дим +'''To check smoke detection state'''.ca=Comprovant l'estat de detecció de fums +'''To check smoke detection state'''.zh-cn=检查烟雾检测状态 +'''To check smoke detection state'''.zh-hk=檢查煙霧偵測器狀態 +'''To check smoke detection state'''.zh-tw=查看煙霧偵測狀態 +'''To check smoke detection state'''.hr=Provjera stanja prepoznavanja dima +'''To check smoke detection state'''.cs=Kontrola stavu detekce kouře +'''To check smoke detection state'''.da=Tjekker tilstand for registrering af røg +'''To check smoke detection state'''.nl=Status van de rookdetector controleren +'''To check smoke detection state'''.et=Suitsu tuvastamise oleku kontrollimine +'''To check smoke detection state'''.fi=Tarkistetaan savun tunnistuksen tilaa +'''To check smoke detection state'''.fr=Vérification état de détection de la fumée +'''To check smoke detection state'''.fr-ca=Vérification état de détection de la fumée +'''To check smoke detection state'''.de=Überprüfen des Raucherkennungsstatus +'''To check smoke detection state'''.el=Έλεγχος της κατάστασης ανίχνευσης καπνού +'''To check smoke detection state'''.iw=בודק את מצב זיהוי העשן +'''To check smoke detection state'''.hi-in=धुआँ पहचान की स्थिति जांचना +'''To check smoke detection state'''.hu=Füstérzékelés állapotának ellenőrzése +'''To check smoke detection state'''.is=Staða reykgreiningar athuguð +'''To check smoke detection state'''.in=Memeriksa status deteksi asap +'''To check smoke detection state'''.it=Verifica stato del rilevamento di fumo +'''To check smoke detection state'''.ja=煙の検出状況を確認 +'''To check smoke detection state'''.ko=연기 감지 상태 확인하기 +'''To check smoke detection state'''.lv=Dūmu noteikšanas stāvokļa pārbaude +'''To check smoke detection state'''.lt=Tikrinama dūmų detektoriaus būsena +'''To check smoke detection state'''.ms=Menyemak keadaan pengesanan asap +'''To check smoke detection state'''.no=Sjekker røykvarslerstatusen +'''To check smoke detection state'''.pl=Sprawdzanie stanu wykrywania dymu +'''To check smoke detection state'''.pt=Verificar o estado de detecção de fumo +'''To check smoke detection state'''.ro=Verificarea stării de detectare a fumului +'''To check smoke detection state'''.ru=Проверка сост. процесса обнаружения дыма +'''To check smoke detection state'''.sr=Proveravanje statusa detekcije dima +'''To check smoke detection state'''.sk=Kontrola stavu detekcie dymu +'''To check smoke detection state'''.sl=Preverjanje stanja zaznavanja dima +'''To check smoke detection state'''.es=Consultar estado de detección de humo +'''To check smoke detection state'''.sv=Kontrollerar brandvarnarens tillstånd +'''To check smoke detection state'''.th=การตรวจสอบสถานะการตรวจจับควัน +'''To check smoke detection state'''.tr=Duman algılama durumu kontrol ediliyor +'''To check smoke detection state'''.uk=Перевірка стану датчика диму +'''To check smoke detection state'''.vi=Kiểm tra trạng thái phát hiện khói +'''5 minutes'''.en=5 minutes +'''5 minutes'''.en-gb=5 minutes +'''5 minutes'''.en-us=5 minutes +'''5 minutes'''.en-ca=5 minutes +'''5 minutes'''.sq=5 minuta +'''5 minutes'''.ar=٥ دقائق +'''5 minutes'''.be=5 хвілін +'''5 minutes'''.sr-ba=5 minuta +'''5 minutes'''.bg=5 минути +'''5 minutes'''.ca=5 minuts +'''5 minutes'''.zh-cn=5 分钟 +'''5 minutes'''.zh-hk=5 分鐘 +'''5 minutes'''.zh-tw=5 分鐘 +'''5 minutes'''.hr=5 minuta +'''5 minutes'''.cs=5 minut +'''5 minutes'''.da=5 minutter +'''5 minutes'''.nl=5 minuten +'''5 minutes'''.et=5 minutit +'''5 minutes'''.fi=5 minuuttia +'''5 minutes'''.fr=5 minutes +'''5 minutes'''.fr-ca=5 minutes +'''5 minutes'''.de=5 Minuten +'''5 minutes'''.el=5 λεπτά +'''5 minutes'''.iw=5 דקות +'''5 minutes'''.hi-in=5 मिनट +'''5 minutes'''.hu=5 perc +'''5 minutes'''.is=5 mínútur +'''5 minutes'''.in=5 menit +'''5 minutes'''.it=5 minuti +'''5 minutes'''.ja=5分 +'''5 minutes'''.ko=5분 +'''5 minutes'''.lv=5 minūtes +'''5 minutes'''.lt=5 minutės +'''5 minutes'''.ms=5 minit +'''5 minutes'''.no=5 minutter +'''5 minutes'''.pl=5 minut +'''5 minutes'''.pt=5 minutos +'''5 minutes'''.ro=5 minute +'''5 minutes'''.ru=5 минут +'''5 minutes'''.sr=5 minuta +'''5 minutes'''.sk=5 minút +'''5 minutes'''.sl=5 minut +'''5 minutes'''.es=5 minutos +'''5 minutes'''.sv=5 minuter +'''5 minutes'''.th=5 นาที +'''5 minutes'''.tr=5 dakika +'''5 minutes'''.uk=5 хвилин +'''5 minutes'''.vi=5 phút +'''Exceeding temperature threshold'''.en=Temperature threshold exceeded +'''Exceeding temperature threshold'''.en-gb=Temperature threshold exceeded +'''Exceeding temperature threshold'''.en-us=Temperature threshold exceeded +'''Exceeding temperature threshold'''.en-ca=Temperature threshold exceeded +'''Exceeding temperature threshold'''.sq=U tejkalua caku i temperaturës +'''Exceeding temperature threshold'''.ar=تجاوز حد درجة الحرارة +'''Exceeding temperature threshold'''.be=Тэмпературны парог перавышаны +'''Exceeding temperature threshold'''.sr-ba=Prag temperature je prekoračen +'''Exceeding temperature threshold'''.bg=Температурният праг е надвишен +'''Exceeding temperature threshold'''.ca=S'ha excedit el llindar de temperatura +'''Exceeding temperature threshold'''.zh-cn=超过温度阈值 +'''Exceeding temperature threshold'''.zh-hk=超過溫度閾值 +'''Exceeding temperature threshold'''.zh-tw=超過溫度臨界值 +'''Exceeding temperature threshold'''.hr=Prag temperature premašen +'''Exceeding temperature threshold'''.cs=Prahová hodnota teploty překročena +'''Exceeding temperature threshold'''.da=Tærskelværdi for temp. overskredet +'''Exceeding temperature threshold'''.nl=Grens temperatuur overschreden +'''Exceeding temperature threshold'''.et=Temperatuuri lävi on ületatud +'''Exceeding temperature threshold'''.fi=Lämpötilan kynnysarvo ylitetty +'''Exceeding temperature threshold'''.fr=Seuil de température dépassé +'''Exceeding temperature threshold'''.fr-ca=Seuil de température dépassé +'''Exceeding temperature threshold'''.de=Temperaturgrenzwert überschritten +'''Exceeding temperature threshold'''.el=Υπέρβαση ορίου θερμοκρασίας +'''Exceeding temperature threshold'''.iw=סף הטמפרטורה נחצה +'''Exceeding temperature threshold'''.hi-in=तापमान थ्रेसहोल्ड बढ़ गया +'''Exceeding temperature threshold'''.hu=Küszöbhőmérséklet túllépve +'''Exceeding temperature threshold'''.is=Hitastig yfir viðmiðunarmörkum +'''Exceeding temperature threshold'''.in=Ambang batas suhu terlampaui +'''Exceeding temperature threshold'''.it=Soglia di surriscaldamento superata +'''Exceeding temperature threshold'''.ja=温度の閾値を超過 +'''Exceeding temperature threshold'''.ko=온도 기준 초과됨 +'''Exceeding temperature threshold'''.lv=Temperatūras slieksnis ir pārsniegts +'''Exceeding temperature threshold'''.lt=Viršytas temperatūros slenkstis +'''Exceeding temperature threshold'''.ms=Ambang suhu telah dilebihi +'''Exceeding temperature threshold'''.no=Temperaturterskel overskredet +'''Exceeding temperature threshold'''.pl=Przekroczono próg temperatury +'''Exceeding temperature threshold'''.pt=Limite de temperatura excedido +'''Exceeding temperature threshold'''.ro=Prag temperatură depășit +'''Exceeding temperature threshold'''.ru=Превышение температурного порога +'''Exceeding temperature threshold'''.sr=Granična vrednost temp. je premašena +'''Exceeding temperature threshold'''.sk=Prekročenie prahu teploty +'''Exceeding temperature threshold'''.sl=Temperaturni prag je prekoračen +'''Exceeding temperature threshold'''.es=Umbral de temperatura superado +'''Exceeding temperature threshold'''.sv=Temperaturtröskeln överskreds +'''Exceeding temperature threshold'''.th=เกินขอบเขตอุณหภูมิแล้ว +'''Exceeding temperature threshold'''.tr=Sıcaklık eşiği aşıldı +'''Exceeding temperature threshold'''.uk=Перевищено температурний поріг +'''Exceeding temperature threshold'''.vi=Đã vượt ngưỡng nhiệt độ +'''30 minutes'''.en=30 minutes +'''30 minutes'''.en-gb=30 minutes +'''30 minutes'''.en-us=30 minutes +'''30 minutes'''.en-ca=30 minutes +'''30 minutes'''.en-ph=30 minutes +'''30 minutes'''.sq=30 minuta +'''30 minutes'''.ar=٣٠ دقيقة +'''30 minutes'''.be=30 хвілін +'''30 minutes'''.sr-ba=30 minuta +'''30 minutes'''.bg=30 минути +'''30 minutes'''.ca=30 minuts +'''30 minutes'''.zh-cn=30 分钟 +'''30 minutes'''.zh-hk=30 分鐘 +'''30 minutes'''.zh-tw=30 分鐘 +'''30 minutes'''.hr=30 minuta +'''30 minutes'''.cs=30 minut +'''30 minutes'''.da=30 minutter +'''30 minutes'''.nl=30 minuten +'''30 minutes'''.et=30 minutit +'''30 minutes'''.fi=30 minuuttia +'''30 minutes'''.fr=30 minutes +'''30 minutes'''.fr-ca=30 minutes +'''30 minutes'''.de=30 Minuten +'''30 minutes'''.el=30 λεπτά +'''30 minutes'''.iw=30 דקות +'''30 minutes'''.hi-in=30 मिनट +'''30 minutes'''.hu=30 perc +'''30 minutes'''.is=30 mínútur +'''30 minutes'''.in=30 menit +'''30 minutes'''.it=30 minuti +'''30 minutes'''.ja=30分 +'''30 minutes'''.ko=30분 +'''30 minutes'''.lv=30 minūtes +'''30 minutes'''.lt=30 minučių +'''30 minutes'''.ms=30 minit +'''30 minutes'''.no=30 minutter +'''30 minutes'''.pl=30 minut +'''30 minutes'''.pt=30 minutos +'''30 minutes'''.ro=30 de minute +'''30 minutes'''.ru=30 минут +'''30 minutes'''.sr=30 minuta +'''30 minutes'''.sk=30 minút +'''30 minutes'''.sl=30 min +'''30 minutes'''.es=30 minutos +'''30 minutes'''.sv=30 minuter +'''30 minutes'''.th=30 นาที +'''30 minutes'''.tr=30 dakika +'''30 minutes'''.uk=30 хвилин +'''30 minutes'''.vi=30 phút +'''Instructions'''.en=Getting started +'''Instructions'''.en-gb=Getting started +'''Instructions'''.en-us=Getting started +'''Instructions'''.en-ca=Getting started +'''Instructions'''.en-ph=Getting started +'''Instructions'''.sq=Për të filluar +'''Instructions'''.ar=بدء الاستخدام +'''Instructions'''.be=Уводзіны +'''Instructions'''.sr-ba=Prvi koraci +'''Instructions'''.bg=Начално запознаване +'''Instructions'''.ca=Començar +'''Instructions'''.zh-cn=入门 +'''Instructions'''.zh-hk=快速入門 +'''Instructions'''.zh-tw=開始使用 +'''Instructions'''.hr=Početak rada +'''Instructions'''.cs=Začínáme +'''Instructions'''.da=Kom godt i gang +'''Instructions'''.nl=Aan de slag +'''Instructions'''.et=Alustamine +'''Instructions'''.fi=Käytön aloittaminen +'''Instructions'''.fr=Démarrer +'''Instructions'''.fr-ca=Démarrer +'''Instructions'''.de=Erste Schritte +'''Instructions'''.el=Έναρξη +'''Instructions'''.iw=תחילת העבודה +'''Instructions'''.hi-in=प्रारंभ करना +'''Instructions'''.hu=Első lépések +'''Instructions'''.is=Hafist handa +'''Instructions'''.in=Mulai +'''Instructions'''.it=Introduzione +'''Instructions'''.ja=はじめに +'''Instructions'''.ko=빅스비와 대화하기 +'''Instructions'''.lv=Darba sākšana +'''Instructions'''.lt=Darbo pradžia +'''Instructions'''.ms=Bermula +'''Instructions'''.no=Komme i gang +'''Instructions'''.pl=Pierwsze kroki +'''Instructions'''.pt=Introdução +'''Instructions'''.ro=Primii pași +'''Instructions'''.ru=Введение +'''Instructions'''.sr=Prvi koraci +'''Instructions'''.sk=Začíname +'''Instructions'''.sl=Vodnik za začetek +'''Instructions'''.es=Primeros pasos +'''Instructions'''.sv=Komma igång +'''Instructions'''.th=เริ่มต้น +'''Instructions'''.tr=Başlarken +'''Instructions'''.uk=Початок роботи +'''Instructions'''.vi=Bắt đầu sử dụng +'''High'''.en=High +'''High'''.en-gb=High +'''High'''.en-us=High +'''High'''.en-ca=High +'''High'''.sq=E lartë +'''High'''.ar=عالية +'''High'''.be=Высокая +'''High'''.sr-ba=Visoko +'''High'''.bg=Висока +'''High'''.ca=Alta +'''High'''.zh-cn=高 +'''High'''.zh-hk=高 +'''High'''.zh-tw=高 +'''High'''.hr=Visoka +'''High'''.cs=Vysoká +'''High'''.da=Høj +'''High'''.nl=Hoog +'''High'''.et=Kõrge +'''High'''.fi=Suuri +'''High'''.fr=Élevée +'''High'''.fr-ca=Élevée +'''High'''.de=Hoch +'''High'''.el=Υψηλή +'''High'''.iw=גבוהה +'''High'''.hi-in=उच्च +'''High'''.hu=Magas +'''High'''.is=Mikið +'''High'''.in=Tinggi +'''High'''.it=Alta +'''High'''.ja=高 +'''High'''.ko=높음 +'''High'''.lv=Augsts +'''High'''.lt=Didelis +'''High'''.ms=Tinggi +'''High'''.no=Høy +'''High'''.pl=Wysoka +'''High'''.pt=Alta +'''High'''.ro=Ridicată +'''High'''.ru=Высокая +'''High'''.sr=Visoka +'''High'''.sk=Vysoká +'''High'''.sl=Visoko +'''High'''.es=Alta +'''High'''.sv=Högt +'''High'''.th=สูง +'''High'''.tr=Yüksek +'''High'''.uk=Високий +'''High'''.vi=Cao +'''None'''.en=None +'''None'''.en-gb=None +'''None'''.en-us=None +'''None'''.en-ca=None +'''None'''.en-ph=None +'''None'''.sq=Asnjë +'''None'''.ar=بلا +'''None'''.be=Няма +'''None'''.sr-ba=Nema +'''None'''.bg=Няма +'''None'''.ca=Cap +'''None'''.zh-cn=无 +'''None'''.zh-hk=無 +'''None'''.zh-tw=無 +'''None'''.hr=Ništa +'''None'''.cs=Žádná +'''None'''.da=Ingen +'''None'''.nl=Geen +'''None'''.et=Puudub +'''None'''.fi=Ei mitään +'''None'''.fr=Aucune +'''None'''.fr-ca=Aucune +'''None'''.de=Keine +'''None'''.el=Κανένα +'''None'''.iw=ללא +'''None'''.hi-in=कुछ भी नहीं +'''None'''.hu=Egyik sem +'''None'''.is=Ekkert +'''None'''.in=Tidak ada +'''None'''.it=Nessuna +'''None'''.ja=なし +'''None'''.ko=설정 안 함 +'''None'''.lv=Nav +'''None'''.lt=Nėra +'''None'''.ms=Tiada +'''None'''.no=Ingen +'''None'''.pl=Brak +'''None'''.pt=Nenhum +'''None'''.ro=Niciuna +'''None'''.ru=Нет +'''None'''.sr=Ništa +'''None'''.sk=Žiadne +'''None'''.sl=Brez +'''None'''.es=Ninguno +'''None'''.sv=Inget +'''None'''.th=ไม่มี +'''None'''.tr=Hiçbiri +'''None'''.uk=Немає +'''None'''.vi=Không có +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.en=Check the manual that came with your Fibaro Smoke Sensor for information about advanced settings. If you don't make any changes below, the default settings will be used. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.en-gb=Check the manual that came with your Fibaro Smoke Sensor for information about advanced settings. If you don't make any changes below, the default settings will be used. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.en-us=Check the manual that came with your Fibaro Smoke Sensor for information about advanced settings. If you don't make any changes below, the default settings will be used. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.en-ca=Check the manual that came with your Fibaro Smoke Sensor for information about advanced settings. If you don't make any changes below, the default settings will be used. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.sq=Shiko në manualin që të erdhi me Sensorin e Tymit Fibaro për informacion rreth cilësimeve të avancuara. Në qoftë se nuk bën ndryshime më poshtë, do të përdoren cilësimet e parazgjedhura. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ar=راجع الدليل المرفق مع مستشعر الدخان Fibaro الخاص بك للحصول على معلومات حول الضبط المتقدم. إذا لم تقم بإجراء أي من التغييرات التالية، فسيتم استخدام الضبط الافتراضي. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.be=Інфармацыю аб дадатковых наладах вы знойдзеце ў дапаможніку, які пастаўляўся з датчыкам дыму Fibaro. Калі вы не зменіце нічога ніжэй, будуць выкарыстоўвацца стандартныя налады. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.sr-ba=Provjerite uputstvo koji ste dobili zajedno sa senzorom dima kompanije Fibaro za informacije o naprednim postavkama. Ako ne izvršite promjene u nastavku, koristit će se zadane postavke. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.bg=Прегледайте ръководството, приложено към сензора за дим Fibaro, за информация относно разширените настройки. Ако не направите промени по-долу, ще бъдат използвани настройките по подразбиране. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ca=Consulta el manual inclòs amb el sensor de fum Fibaro per obtenir informació sobre els ajustaments avançats. Si no fas canvis a continuació, s'utilitzaran els ajustaments predeterminats. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.zh-cn=有关高级设置的信息,请查看 Fibaro 烟雾传感器随附的手册。如果未在下面进行任何更改,将使用默认设置。 +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.zh-hk=查看 Fibaro 煙霧偵測器隨附的手冊,瞭解關於進階設定的資訊。若您沒有進行以下任何變更,將使用預設設定。 +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.zh-tw=請查看 Fibaro 煙霧偵測器隨附的使用說明書,瞭解進階設定相關資訊。如未於下方進行任何變更,則會使用預設設定。 +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.hr=Provjerite priručnik koji ste dobili uz senzor dima tvrtke Fibaro za informacije o naprednim postavkama. Ako ne izvršite promjene u nastavku, upotrebljavat će se zadane postavke. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.cs=Informace o pokročilém nastavení najdete v návodu k použití detektoru kouře Fibaro. Pokud neprovedete níže žádné změny, budou použita výchozí nastavení. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.da=Tjek manualen, der fulgte med til din Fibaro-røgsensor, for at få oplysninger om avancerede indstillinger. Hvis du ikke foretager nogen ændringer nedenfor, anvendes standardindstillingerne. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.nl=Bekijk de handleiding die u bij uw Fibaro-rooksensor hebt ontvangen voor informatie over geavanceerde instellingen. Als u hieronder geen wijzigingen invoert, worden de standaardinstellingen gebruikt. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.et=Vaadake oma Fibaro suitsuanduriga kaasasolevat kasutusjuhendit, et saada teavet täpsemate seadete kohta. Kui te ei tee all muudatusi, kasutatakse vaikeseadeid. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.fi=Saat tietoja lisäasetuksista Fibaro-savutunnistimen mukana toimitetusta oppaasta. Jos et tee alla mainittuja muutoksia, käytetään oletusasetuksia. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.fr=Consultez le manuel fourni avec votre détecteur de fumée Fibaro pour plus d'informations sur les paramètres avancés. Si vous n'apportez aucune modification ci-dessous, les paramètres par défaut seront utilisés. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.fr-ca=Consultez le manuel fourni avec votre détecteur de fumée Fibaro pour plus d'informations sur les paramètres avancés. Si vous n'apportez aucune modification ci-dessous, les paramètres par défaut seront utilisés. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.de=In der Bedienungsanleitung Ihres Fibaro-Rauchmelders finden Sie Informationen zu den erweiterten Einstellungen. Wenn Sie unten keine Änderungen vornehmen, werden die Standardeinstellungen verwendet. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.el=Ανατρέξτε στο εγχειρίδιο που συνοδεύει τον αισθητήρα καπνού Fibaro για πληροφορίες σχετικά με τις σύνθετες ρυθμίσεις. Εάν δεν κάνετε αλλαγές παρακάτω, θα χρησιμοποιηθούν οι προεπιλεγμένες ρυθμίσεις. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.iw=עיין במדריך שהגיע עם גלאי העשן של Fibaro לקבלת מידע על הגדרות מתקדמות. אם לא תצבע שום שינויים להלן, המכשיר ישתמש בהגדרות ברירת המחדל. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.hi-in=उन्नत सेटिंग्स के बारे में जानकारी के लिए, अपने Fibaro स्मोक सेंसर के साथ आए मैनुअल की जांच करें। अगर आप नीचे कोई बदलाव नहीं करते हैं, तो डिफॉल्ट सेटिंग्स का उपयोग किया जाएगा। +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.hu=A speciális beállításokról a Fibaro füstérzékelőhöz kapott kézikönyvből tájékozódhat. Ha nem végez módosításokat alább, a rendszer az alapértelmezett értékeket fogja használni. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.is=Lestu handbókina sem fylgdi Fibaro-reykskynjaranum til að fá nánari upplýsingar um ítarlegar stillingar. Ef þú gerir engar breytingar hér að neðan verða sjálfgefnar stillingar notaðar. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.in=Periksa manual yang disertakan dengan Fibaro Smoke Sensor untuk melihat informasi mengenai pengaturan lanjutan. Jika Anda tidak melakukan perubahan di bawah ini, maka pengaturan default akan digunakan. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.it=Per informazioni sulle impostazioni avanzate, controllate il manuale in dotazione con il rilevatore di fumo Fibaro. Se non apportate le modifiche di seguito, verranno applicate le impostazioni predefinite. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ja=詳細設定については、Fibaro煙センサーに付属のマニュアルをご確認ください。以下の変更を行わないと、初期設定が使用されます。 +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ko=Fibaro 연기 센서 사용 설명서에서 고급 설정에 관한 정보를 확인할 수 있어요. 아래에서 설정을 변경하지 않으면 기본 설정으로 사용하게 돼요. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.lv=Informāciju par papildu iestatījumiem skatiet Fibaro dūmu sensora rokasgrāmatā. Ja neveiksit nekādas izmaiņas, tiks izmantoti noklusējuma iestatījumi. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.lt=Informacijos apie išplėstinius nustatymus ieškokite prie „Fibaro“ dūmų jutiklio pridėtame vadove. Jei toliau neatliksite jokių pakeitimų, bus naudojami numatytieji nustatymai. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ms=Semak manual yang datang bersama Penderia Asap Fibaro anda untuk maklumat tentang aturan lanjutan. Jika anda tidak melakukan apa-apa perubahan di bawah, aturan lalai akan digunakan. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.no=Se i håndboken som fulgte med Fibaro Smoke Sensor for informasjon om avanserte innstillinger. Hvis du ikke gjør noen endringer nedenfor, brukes standardinnstillingene. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.pl=Zajrzyj do instrukcji dostarczonej wraz z czujnikiem dymu Fibaro, aby uzyskać informacje na temat ustawień zaawansowanych. Jeśli nie wprowadzisz żadnych zmian, będą używane ustawienia fabryczne. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.pt=Consulte o manual fornecido com o seu Sensor de Fumo Fibaro, para obter informações sobre as definições avançadas. Se não fizer nenhuma alteração em baixo, serão utilizadas as predefinições. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ro=Consultați manualul care a venit împreună cu senzorul de fum Fibaro pentru a afla informații despre setările avansate. Dacă nu efectuați nicio modificare mai jos, vor fi utilizate setările implicite. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.ru=Информация о расширенных настройках приведена в руководстве, прилагаемом к датчику дыма Fibaro. Если вы не внесете изменения ниже, будут применены настройки по умолчанию. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.sr=Informacije o naprednim podešavanjima potražite u priručniku koji ste dobili uz Fibaro senzor dima. Ako u nastavku ne unesete promene, koristiće se podrazumevana podešavanja. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.sk=Informácie o rozšírených nastaveniach nájdete v návode na použitie senzora dymu Fibaro. Ak nižšie nevykonáte žiadne zmeny, použijú sa predvolené nastavenia. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.sl=Za informacije o dodatnih nastavitvah preverite priročnik, ki je bil priložen senzorju dima Fibaro. Če spodaj ne opravite nobene spremembe, bodo uporabljene privzete nastavitve. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.es=Consulta el manual que viene con el sensor de humo Fibaro para obtener información sobre los ajustes avanzados. Si no realizas ningún cambio a continuación, se usarán los ajustes predeterminados. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.sv=Läs handboken som medföljde Fibaro-brandvarnaren om du vill ha information om avancerade inställningar. Om du inte ändrar något nedan används standardinställningarna. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.th=ตรวจสอบคู่มือที่มาพร้อมกับเซ็นเซอร์ควัน Fibaro ของคุณเพื่อดูข้อมูลเกี่ยวกับการตั้งค่าขั้นสูง หากคุณไม่ดำเนินการเปลี่ยนแปลงใดๆ ด้านล่าง ระบบจะใช้การตั้งค่าเริ่มต้น +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.tr=Gelişmiş ayarlarla ilgili bilgi için Fibaro Duman Sensörünüzle birlikte verilen kılavuzu kontrol edin. Aşağıda herhangi bir değişiklik yapmazsanız varsayılan ayarlar kullanılır. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.uk=Відомості про додаткові налаштування наведено в посібнику датчика диму Fibaro. Якщо ви не внесете жодних змін, буде використано налаштування за замовчуванням. +'''Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings'''.vi=Xem sách hướng dẫn kèm theo Cảm biến khói Fibaro của bạn để biết thêm thông tin về các cài đặt nâng cao. Nếu bạn không thực hiện thay đổi nào dưới đây, cài đặt mặc định sẽ được sử dụng. +'''Notifications'''.en=Notifications +'''Notifications'''.en-gb=Notifications +'''Notifications'''.en-us=Notifications +'''Notifications'''.en-ca=Notifications +'''Notifications'''.en-ph=Notifications +'''Notifications'''.sq=Njoftimet +'''Notifications'''.ar=الإشعارات +'''Notifications'''.be=Апавяшчэнні +'''Notifications'''.sr-ba=Obavještenja +'''Notifications'''.bg=Уведомления +'''Notifications'''.ca=Notificacions +'''Notifications'''.zh-cn=通知 +'''Notifications'''.zh-hk=通知 +'''Notifications'''.zh-tw=通知 +'''Notifications'''.hr=Obavijesti +'''Notifications'''.cs=Oznámení +'''Notifications'''.da=Meddelelser +'''Notifications'''.nl=Meldingen +'''Notifications'''.et=Teavitused +'''Notifications'''.fi=Ilmoitukset +'''Notifications'''.fr=Notifications +'''Notifications'''.fr-ca=Notifications +'''Notifications'''.de=Benachrichtigungen +'''Notifications'''.el=Ειδοποιήσεις +'''Notifications'''.iw=התראות +'''Notifications'''.hi-in=सूचनाएँ +'''Notifications'''.hu=Értesítések +'''Notifications'''.is=Tilkynningar +'''Notifications'''.in=Notifikasi +'''Notifications'''.it=Notifiche +'''Notifications'''.ja=通知 +'''Notifications'''.ko=알림 +'''Notifications'''.lv=Paziņojumi +'''Notifications'''.lt=Pranešimai +'''Notifications'''.ms=Pemberitahuan +'''Notifications'''.no=Varsler +'''Notifications'''.pl=Powiadomienia +'''Notifications'''.pt=Notificações +'''Notifications'''.ro=Notificări +'''Notifications'''.ru=Уведомления +'''Notifications'''.sr=Obaveštenja +'''Notifications'''.sk=Oznámenia +'''Notifications'''.sl=Obvestila +'''Notifications'''.es=Notificaciones +'''Notifications'''.sv=Aviseringar +'''Notifications'''.th=การแจ้งเตือน +'''Notifications'''.tr=Bildirimler +'''Notifications'''.uk=Сповіщення +'''Notifications'''.vi=Thông báo +'''Temperature report hysteresis'''.en=Temperature report hysteresis +'''Temperature report hysteresis'''.en-gb=Temperature report hysteresis +'''Temperature report hysteresis'''.en-us=Temperature report hysteresis +'''Temperature report hysteresis'''.en-ca=Temperature report hysteresis +'''Temperature report hysteresis'''.sq=Histereza e raportit për temperaturën +'''Temperature report hysteresis'''.ar=تخلف تقرير درجة الحرارة +'''Temperature report hysteresis'''.be=Гістэрэзіс справаздач аб тэмпературы +'''Temperature report hysteresis'''.sr-ba=Histereza izvještaja o temperaturi +'''Temperature report hysteresis'''.bg=Хистерезис за отчитане на температурата +'''Temperature report hysteresis'''.ca=Histèresi de l'informe de temperatura +'''Temperature report hysteresis'''.zh-cn=温度报告迟滞 +'''Temperature report hysteresis'''.zh-hk=溫度報告滯後 +'''Temperature report hysteresis'''.zh-tw=溫度報告磁滯 +'''Temperature report hysteresis'''.hr=Histereza izvješća o temperaturi +'''Temperature report hysteresis'''.cs=Hystereze hlášení teploty +'''Temperature report hysteresis'''.da=Hysterese for temperaturrapport +'''Temperature report hysteresis'''.nl=Hysterese temperatuurrapport +'''Temperature report hysteresis'''.et=Temperatuurist teavitamise hüsterees +'''Temperature report hysteresis'''.fi=Lämpötilaraportin hystereesi +'''Temperature report hysteresis'''.fr=Hystérèse du rapport de température +'''Temperature report hysteresis'''.fr-ca=Hystérèse du rapport de température +'''Temperature report hysteresis'''.de=Temperaturberichtshysterese +'''Temperature report hysteresis'''.el=Υστέρηση αναφοράς θερμοκρασίας +'''Temperature report hysteresis'''.iw=חשל דוח טמפרטורה +'''Temperature report hysteresis'''.hi-in=तापमान रिपोर्ट हिस्टैरिसीस +'''Temperature report hysteresis'''.hu=Hőmérsékleti jelentési hiszterézise +'''Temperature report hysteresis'''.is=Segulheldni hitastigsskráninga +'''Temperature report hysteresis'''.in=Histeresis laporan suhu +'''Temperature report hysteresis'''.it=Isteresi report sulla temperatura +'''Temperature report hysteresis'''.ja=温度レポートヒステリシス +'''Temperature report hysteresis'''.ko=온도 알림 이력 현상 +'''Temperature report hysteresis'''.lv=Temperatūras ziņojuma histerēze +'''Temperature report hysteresis'''.lt=Temperatūros pranešimo histerezė +'''Temperature report hysteresis'''.ms=Histeresis laporan suhu +'''Temperature report hysteresis'''.no=Temperaturrapporthysterese +'''Temperature report hysteresis'''.pl=Histereza raportów o temperaturze +'''Temperature report hysteresis'''.pt=Histerese do relatório de temperatura +'''Temperature report hysteresis'''.ro=Histerezis raportare temperatură +'''Temperature report hysteresis'''.ru=Задержка отчета о температуре +'''Temperature report hysteresis'''.sr=Histereza izveštaja o temperaturi +'''Temperature report hysteresis'''.sk=Hysteréza hlásenia teploty +'''Temperature report hysteresis'''.sl=Histereza poročil o temperaturi +'''Temperature report hysteresis'''.es=Histéresis de informe de temperatura +'''Temperature report hysteresis'''.sv=Hysteres för temperaturrapport +'''Temperature report hysteresis'''.th=ฮิสเทอรีซิสของรายงานอุณหภูมิ +'''Temperature report hysteresis'''.tr=Sıcaklık raporlama gecikmesi +'''Temperature report hysteresis'''.uk=Час надсилання сповіщень про температуру +'''Temperature report hysteresis'''.vi=Độ trễ báo cáo nhiệt độ +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.en=Press and hold the B button for at least 3 seconds. When the indicator glows white, release the B button. The indicator will start changing colours in sequence. Press the B button when the indicator turns green. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.en-gb=Press and hold the B button for at least 3 seconds. When the indicator glows white, release the B button. The indicator will start changing colours in sequence. Press the B button when the indicator turns green. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.en-us=Press and hold the B button for at least 3 seconds. When the indicator glows white, release the B button. The indicator will start changing colors in sequence. Press the B button when the indicator turns green. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.en-ca=Press and hold the B button for at least 3 seconds. When the indicator glows white, release the B button. The indicator will start changing colours in sequence. Press the B button when the indicator turns green. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.sq=Shtyp dhe mbaj butonin B për së paku 3 sekonda. Kur treguesi të ndizet i bardhë, liroje butonin B. Treguesi do të fillojë të ndryshojë ngjyrat në sekuencë. Shtyp butonin B kur treguesi të bëhet i gjelbër. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ar=اضغط مع الاستمرار على الزر B لمدة ۳ ثوانٍ على الأقل. عندما يضيء المؤشر باللون الأبيض، قم بتحرير الزر B. سيبدأ تغيير ألوان المؤشر بالتتابع. اضغط على الزر B عندما يتحول لون المؤشر إلى الأخضر. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.be=Націсніце кнопку B і ўтрымлівайце яе мінімум 3 секунды. Калі індыкатар засвеціцца белым, адпусціце кнопку B. Колеры індыкатара пачнуць мяняцца ў пэўнай паслядоўнасці. Націсніце кнопку B, калі колер індыкатара зробіцца зялёным. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.sr-ba=Pritisnite i zadržite dugme B najmanje tri sekunde. Kada indikator zasvijetli bijelom bojom, otpustite dugme B. Indikator će uzastopno početi mijenjati boje. Pritisnite dugme B kada indikator postane zelen. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.bg=Натиснете и задръжте бутона B за поне 3 секунди. Когато индикаторът свети бяло, пуснете бутона B. Индикаторът ще започне да променя цветовете последователно. Натиснете бутона B, когато индикаторът стане зелен. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ca=Mantén premut el botó B almenys 3 segons. Quan l'indicador brilli amb el colo blanc, deixa anar al botó B. L'indicador començarà a canviar de color en seqüència. Prem el botó B quan l'indicador es torni verd. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.zh-cn=长按 B 按钮至少 3 秒钟。当指示灯呈白色亮起时,松开 B 按钮。指示灯将开始按顺序更改颜色。指示灯变为绿色时,按 B 按钮。 +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.zh-hk=按住 B 按鍵至少 3 秒。當指示燈發出白光時,鬆開 B 按鍵。指示燈將依次變更顏色。當指示燈變成綠色時,按下 B 按鍵。 +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.zh-tw=請長按 B 按鈕至少 3 秒。指示燈亮白色時,放開 B 按鈕。指示燈色彩隨即會依序變化。指示燈轉為綠色時,請按下 B 按鈕。 +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.hr=Pritisnite i držite gumb B najmanje 3 sekunde. Kada pokazatelj zasvijetli bijelom bojom, otpustite gumb B. Pokazatelj će početi uzastopno mijenjati boje. Pritisnite gumb B kada pokazatelj pozeleni. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.cs=Stiskněte a podržte tlačítko B alespoň na 3 sekundy. Když indikátor svítí bíle, pusťte tlačítko B. Indikátor začne postupně měnit barvy. Až začne indikátor svítit zeleně, pusťte tlačítko B. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.da=Tryk og hold på B-knappen i mindst 3 sekunder. Når indikatoren lyser hvidt, skal du slippe B-knappen. Indikatoren begynder at skifte farve i sekvens. Tryk på B-knappen, når indikatoren bliver grøn. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.nl=Houd de knop B tenminste 3 seconden ingedrukt. Laat de knop B los als de indicator wit begint te branden. De indicator verandert in volgorde van kleur. Druk op de knop B als de indicator groen wordt. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.et=Vajutage ja hoidke nuppu B vähemalt 3 sekundit. Kui indikaator põleb valgelt, vabastage nupp B. Indikaator hakkab järgemööda värve muutma. Vajutage nuppu B, kui indikaator põleb roheliselt. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.fi=Pidä B-painiketta painettuna vähintään 3 sekunnin ajan. Vapauta B-painike, kun ilmaisin palaa valkoisena. Ilmaisin alkaa vaihtaa väriä järjestyksessä. Paina B-painiketta, kun ilmaisin muuttuu vihreäksi. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.fr=Maintenez le bouton B appuyé pendant au moins 3 secondes. Lorsque l'indicateur s'allume en blanc, relâchez le bouton B. L'indicateur commencera à changer de couleur progressivement. Appuyez sur le bouton B lorsque l'indicateur passe au vert. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.fr-ca=Pressez le bouton B pendant au moins 3 secondes. Lorsque l'indicateur s'allume en blanc, relâchez le bouton B. L'indicateur commencera à changer de couleur progressivement. Pressez le bouton B lorsque l'indicateur passe au vert. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.de=Halten Sie die B-Taste mindestens 3 Sekunden gedrückt. Wenn die Anzeige weiß leuchtet, lassen Sie die B-Taste los. Die Anzeige ändert die Farbe der Reihe nach. Drücken Sie die B-Taste, wenn die Anzeige grün leuchtet. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.el=Πιέστε παρατεταμένα το κουμπί B για τουλάχιστον 3 δευτερόλεπτα. Όταν η ένδειξη ανάψει με λευκό χρώμα, αποδεσμεύστε το κουμπί B. Η ένδειξη θα αρχίσει να αλλάζει χρώματα διαδοχικά. Πιέστε το κουμπί B μόλις η ένδειξη γίνει πράσινη. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.iw=לחץ על הלחצן B והחזק לפחות 3 שניות. כשהמחוון זוהר בלבן, שחרר את לחצן B. המחוון יתחיל לשנות צבעים ברצף. לחץ על הלחצן B כשהמחוון נעשה ירוק. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.hi-in=B बटन को कम से कम 3 सेकंड तक दबाकर रखें। जब संकेतक सफेद चमकने लगे, तो B बटन को छोड़ दें। संकेतक क्रम में रंग बदलना प्रारंभ कर देगा। जब संकेतक हरा हो जाए, तो B बटन दबाएँ। +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.hu=Tartsa nyomva a B gombot legalább 3 másodpercig. Amikor a jelzőfény fehérre vált, engedje el a B gombot. A jelzőfény sorban színt vált. Nyomja meg a B gombot, amikor a jelzőfény zöldre vált. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.is=Haltu B-hnappinum inni í minnst 3 sekúndur. Þegar gaumljósið logar í hvítu skaltu sleppa B-hnappinum. Liturinn á gaumljósinu byrjar að breytast í tiltekinni röð. Ýttu á B-hnappinn þegar ljósið verður grænt. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.in=Tekan dan tahan tombol B minimal 3 detik. Saat indikator menyala berwarna putih, lepaskan tombol B. Indikator akan mulai berubah warna secara berurutan. Tekan tombol B saat indikator berwarna hijau. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.it=Tenete premuto il pulsante B per almeno 3 secondi. Quando lʹindicatore lampeggia con luce bianca, rilasciate il pulsante B. Lʹindicatore inizierà a cambiare colore in sequenza. Quando diventa verde, premete il pulsante B. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ja=Bボタンを3秒以上長押ししてください。インジケーターが白く光ったら、Bボタンを離してください。インジケーターの色が順に変化します。インジケーターが緑になったら、Bボタンを離してください。 +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ko=3초 이상 B 버튼을 길게 누르세요. 표시등이 흰색으로 반짝이면 B 버튼에서 손을 떼세요. 표시등의 색상이 순서대로 바뀌다가 초록색이 켜지면 B 버튼을 누르세요. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.lv=Nospiediet un vismaz 3 sekundes turiet pogu B. Kad indikators iedegas baltā krāsā, atlaidiet pogu B. Indikators sāks mainīt krāsas pēc kārtas. Kad indikators kļūst zaļš, nospiediet pogu B. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.lt=Paspauskite ir bent 3 sek. palaikykite mygtuką B. Kai indikatorius pradės šviesti baltai, mygtuką B atleiskite. Indikatoriaus spalvos keisis paeiliui. Kai indikatorius švies žaliai, paspauskite mygtuką B. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ms=Tekan dan tahan butang B untuk sekurang-kurangnya 3 saat. Apabila penunjuk bercahaya putih, lepaskan butang B. Penunjuk akan mula berubah warna mengikut turutan. Tekan butang B apabila penunjuk bertukar hijau. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.no=Trykk på og hold B-knappen i minst 3 sekunder. Når indikatoren lyser hvitt, slipper du B-knappen. Indikatoren begynner å endre farge i rekkefølge. Trykk på B-knappen når indikatoren lyser grønt. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.pl=Naciśnij przycisk B i przytrzymaj go co najmniej 3 sekundy. Gdy wskaźnik zaświeci na biało, zwolnij przycisk B. Wskaźnik zacznie zmieniać kolor. Naciśnij przycisk B, kiedy wskaźnik będzie zielony. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.pt=Prima sem soltar o botão B durante pelo menos 3 segundos. Quando o indicador brilhar a branco, solte o botão B. O indicador começará a mudar de cor em sequência. Prima o botão B quando o indicador ficar verde. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ro=Mențineți apăsat butonul B timp de cel puțin 3 secunde. Atunci când indicatorul se aprinde în culoarea albă, eliberați butonul B. Indicatorul va începe să schimbe culorile secvențial. Apăsați butonul B atunci când indicatorul devine verde. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.ru=Нажмите и удерживайте кнопку B не менее 3 секунд. Когда индикатор загорится белым, отпустите кнопку. Индикатор начнет последовательно менять цвета. Нажмите кнопку B, когда индикатор загорится зеленым. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.sr=Pritisnite i zadržite dugme B na najmanje 3 sekunde. Kada lampica zasvetli belom bojom, otpustite dugme B. Lampica će početi da menja boje redom. Pritisnite dugme B kada lampica postane zelena. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.sk=Stlačte tlačidlo B a podržte ho stlačené aspoň na 3 sekundy. Keď sa indikátor rozsvieti na bielo, uvoľnite tlačidlo B. Indikátor začne postupne meniť farby. Keď sa indikátor zmení na zelený, stlačte tlačidlo B. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.sl=Pritisnite in vsaj 3 sekunde držite gumb B. Ko indikator zasveti belo, spustite gumb B. Indikator bo začel spreminjati barve v zaporedju. Ko indikator zasveti zeleno, pritisnite gumb B. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.es=Mantén pulsado el botón B durante al menos 3 segundos. Cuando el indicador brille de color blanco, suelta el botón B. El indicador empezará a cambiar de color en una secuencia. Pulsa el botón B cuando el indicador cambie a verde. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.sv=Håll ned B-knappen i minst tre sekunder. Släpp upp den när indikatorn blir vit. Indikatorns färg ändras i en följd. Tryck på B-knappen när indikatorn blir grön. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.th=กดค้างที่ปุ่ม B นานอย่างน้อย 3 วินาที เมื่อตัวระบุติดสว่างเป็นสีขาว ให้ปล่อยปุ่ม B ตัวระบุจะเริ่มเปลี่ยนสีตามลำดับ กดปุ่ม B เมื่อตัวระบุเปลี่ยนเป็นสีเขียว +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.tr=B tuşunu en az 3 saniye basılı tutun. Gösterge beyaz renkte yandığında, B tuşunu bırakın. Gösterge sırayla farklı renklerde yanmaya başlar. Gösterge yeşil renkte yandığında B tuşuna basın. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.uk=Утримуйте кнопку B принаймні 3 секунди та відпустіть її, коли індикатор почне світитися білим. Після цього індикатор послідовно змінюватиме кольори. Натисніть кнопку B, коли колір індикатора стане зеленим. +'''Enter the menu by press and hold B-button for 3 seconds. Once indicator glows WHITE, release the B-button. Visual indicator will start changing colours in sequence. Press B-button briefly when visual indicator glows GREEN'''.vi=Nhấn và giữ phím B trong ít nhất 3 giây. Khi chỉ báo hiện màu trắng, hãy thả phím B. Chỉ báo sẽ bắt đầu lần lượt thay đổi màu. Hãy nhấn phím B khi chỉ báo chuyển màu xanh lá. +'''Casing opened'''.en=Case opened +'''Casing opened'''.en-gb=Case opened +'''Casing opened'''.en-us=Case opened +'''Casing opened'''.en-ca=Case opened +'''Casing opened'''.sq=Kutia hapur +'''Casing opened'''.ar=تم فتح الحالة +'''Casing opened'''.be=Корпус адчынены +'''Casing opened'''.sr-ba=Omot je otvoren +'''Casing opened'''.bg=Кутията е отворена +'''Casing opened'''.ca=S'ha obert la funda +'''Casing opened'''.zh-cn=充电盒已打开 +'''Casing opened'''.zh-hk=已打開外殼 +'''Casing opened'''.zh-tw=開蓋 +'''Casing opened'''.hr=Poklopac otvoren +'''Casing opened'''.cs=Pouzdro otevřeno +'''Casing opened'''.da=Kabinet åbnet +'''Casing opened'''.nl=Hoes geopend +'''Casing opened'''.et=Ümbris avatud +'''Casing opened'''.fi=Kotelo avattu +'''Casing opened'''.fr=Boîtier ouvert +'''Casing opened'''.fr-ca=Boitier ouvert +'''Casing opened'''.de=Gehäuse geöffnet +'''Casing opened'''.el=Άνοιξε υπόθεση +'''Casing opened'''.iw=הנרתיק פתוח +'''Casing opened'''.hi-in=केस खुल गया +'''Casing opened'''.hu=Ház kinyitva +'''Casing opened'''.is=Hulstur opnað +'''Casing opened'''.in=Casing dibuka +'''Casing opened'''.it=Case aperto +'''Casing opened'''.ja=ケースが開いている +'''Casing opened'''.ko=케이스 열림 +'''Casing opened'''.lv=Atvērts vāks +'''Casing opened'''.lt=Dėklas atidarytas +'''Casing opened'''.ms=Bekas dibuka +'''Casing opened'''.no=Deksel åpnet +'''Casing opened'''.pl=Obudowa otwarta +'''Casing opened'''.pt=Caixa aberta +'''Casing opened'''.ro=Carcasă deschisă +'''Casing opened'''.ru=Корпус открыт +'''Casing opened'''.sr=Slučaj je otvoren +'''Casing opened'''.sk=Puzdro otvorené +'''Casing opened'''.sl=Ohišje je odprto +'''Casing opened'''.es=Cubierta abierta +'''Casing opened'''.sv=Höljet har öppnats +'''Casing opened'''.th=เปิดฝาปิดแล้ว +'''Casing opened'''.tr=Muhafaza açıldı +'''Casing opened'''.uk=Корпус відкрито +'''Casing opened'''.vi=Đã mở nắp +'''Reports inactive'''.en=No reports +'''Reports inactive'''.en-gb=No reports +'''Reports inactive'''.en-us=No reports +'''Reports inactive'''.en-ca=No reports +'''Reports inactive'''.sq=Nuk ka raporte +'''Reports inactive'''.ar=لا توجد تقارير +'''Reports inactive'''.be=Няма справаздач +'''Reports inactive'''.sr-ba=Nema izvještaja +'''Reports inactive'''.bg=Няма отчети +'''Reports inactive'''.ca=Sense informes +'''Reports inactive'''.zh-cn=没有报告 +'''Reports inactive'''.zh-hk=無報告 +'''Reports inactive'''.zh-tw=無報告 +'''Reports inactive'''.hr=Nema izvješća +'''Reports inactive'''.cs=Žádné zprávy +'''Reports inactive'''.da=Ingen rapporter +'''Reports inactive'''.nl=Geen rapporten +'''Reports inactive'''.et=Aruandeid pole +'''Reports inactive'''.fi=Raportteja ei ole +'''Reports inactive'''.fr=Aucun rapport +'''Reports inactive'''.fr-ca=Aucun rapport +'''Reports inactive'''.de=Keine Berichte +'''Reports inactive'''.el=Δεν υπάρχουν αναφορές +'''Reports inactive'''.iw=אין דוחות +'''Reports inactive'''.hi-in=कोई रिपोर्ट्स नहीं +'''Reports inactive'''.hu=Nincsenek jelentések +'''Reports inactive'''.is=Engar skýrslur +'''Reports inactive'''.in=Tidak ada laporan +'''Reports inactive'''.it=Nessun report +'''Reports inactive'''.ja=レポートなし +'''Reports inactive'''.ko=알림 받지 않음 +'''Reports inactive'''.lv=Nav ziņojumu +'''Reports inactive'''.lt=Ataskaitų nėra +'''Reports inactive'''.ms=Tiada laporan +'''Reports inactive'''.no=Ingen rapporter +'''Reports inactive'''.pl=Brak raportów +'''Reports inactive'''.pt=Sem relatórios +'''Reports inactive'''.ro=Niciun raport +'''Reports inactive'''.ru=Нет отчетов +'''Reports inactive'''.sr=Nema izveštaja +'''Reports inactive'''.sk=Žiadne správy +'''Reports inactive'''.sl=Ni poročil +'''Reports inactive'''.es=No hay informes +'''Reports inactive'''.sv=Inga rapporter +'''Reports inactive'''.th=ไม่มีรายงาน +'''Reports inactive'''.tr=Rapor yok +'''Reports inactive'''.uk=Немає сповіщень +'''Reports inactive'''.vi=Không có báo cáo +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.en=After installing, press the B button on your Fibaro Smoke Sensor to update the device's status and configuration. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.en-gb=After installing, press the B button on your Fibaro Smoke Sensor to update the device's status and configuration. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.en-us=After installing, press the B button on your Fibaro Smoke Sensor to update the device's status and configuration. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.en-ca=After installing, press the B button on your Fibaro Smoke Sensor to update the device's status and configuration. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.sq=Pas instalimit, shtyp butonin B te Sensori i tymit Fibaro për të përditësuar statusin dhe konfigurimin e pajisjes. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ar=بعد التثبيت، اضغط على الزر B على مستشعر الدخان Fibaro الخاص بك لتحديث حالة الجهاز والإعداد. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.be=Пасля ўсталявання націсніце кнопку B на датчыку дыму Fibaro, каб абнавіць стан і канфігурацыю прылады. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.sr-ba=Nakon instalacije pritisnite dugme B na senzoru dima kompanije Fibaro za ažuriranje statusa i konfiguraciju uređaja. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.bg=След инсталирането натиснете бутона B върху сензора за дим Fibaro, за да актуализирате състоянието и конфигурацията на устройството. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ca=Després de la instal·lació, prem el botó B del sensor de fum Fibaro per actualitzar l'estat i la configuració del dispositiu. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.zh-cn=安装后,按 Fibaro 烟雾传感器上的 B 按钮来更新设备的状态和配置。 +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.zh-hk=安裝後,請按下 Fibaro 煙霧偵測器上的 B 按鍵以更新裝置的狀態與配置。 +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.zh-tw=安裝後,請按下 Fibaro 煙霧偵測器上的 B 按鈕來更新裝置狀態與設定。 +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.hr=Nakon instalacije pritisnite gumb B na senzoru dima tvrtke Fibaro za aktualizaciju statusa i konfiguracije uređaja. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.cs=Po nainstalování stiskněte tlačítko B na detektoru kouře Fibaro, abyste aktualizovali stav a konfiguraci zařízení. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.da=Efter installationen skal du trykke på B-knappen på din Fibaro-røgsensor for at opdatere enhedens status og konfiguration. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.nl=Druk na installeren op de knop B op uw Fibaro-rooksensor om de status en configuratie van het apparaat bij te werken. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.et=Vajutage oma Fibaro suitsuanduril pärast installimist nuppu B, et värskendada seadme olekut ja konfiguratsiooni. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.fi=Päivitä laitteen tila ja kokoonpano painamalla Fibaro-savutunnistimen B-painiketta asennuksen jälkeen. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.fr=Après l'installation, appuyez sur le bouton B de votre détecteur de fumée Fibaro pour mettre à jour le statut et la configuration de l'appareil. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.fr-ca=Après l'installation, pressez le bouton B de votre détecteur de fumée Fibaro pour mettre à jour le statut et la configuration de l'appareil. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.de=Drücken Sie nach der Installation auf die B-Taste Ihres Fibaro-Rauchmelders, um den Status und die Konfiguration des Geräts zu aktualisieren. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.el=Μετά την εγκατάσταση, πιέστε το κουμπί B στον αισθητήρα καπνού Fibaro για να ενημερώσετε την κατάσταση και τη διαμόρφωση της συσκευής. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.iw=לאחר ההתקנה, לחץ על המקש B על חיישן העשן של Fibaro כדי לעדכן את הסטטוס והתצורה של המכשיר. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.hi-in=स्थापित करने के बाद, डिवाइस की स्थिति और कॉन्फिगरेशन अपडेट करने के लिए अपने Fibaro स्मोक सेंसर पर B बटन दबाएँ। +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.hu=A telepítés után nyomja meg a B gombot a Fibaro füstérzékelőn az eszköz állapotának és konfigurációjának frissítéséhez. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.is=Eftir uppsetningu skaltu ýta á B-hnappinn á Fibaro-reykskynjaranum þínum til að uppfæra stöðu og grunnstillingar tækisins. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.in=Setelah menginstal, tekan tombol B di Fibaro Smoke Sensor untuk memperbarui status dan konfigurasi perangkat. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.it=Dopo lʹinstallazione, premete il pulsante B sul sensore di fumo Fibaro per aggiornare lo stato e la configurazione del dispositivo. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ja=設置した後、Fibaro煙センサーのBボタンを押してデバイスのステータスと設定を更新してください。 +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ko=설치한 후에 Fibaro 연기 센서의 B 버튼을 누르면 디바이스 상태 및 설정이 업데이트돼요. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.lv=Pēc instalēšanas nospiediet Fibaro dūmu sensora pogu B, lai atjauninātu ierīces statusu un konfigurāciju. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.lt=Įdiegę paspauskite ƒ„Fibaro“ dūmų jutiklio mygtuką B, kad atnaujintumėte įrenginio būseną ir konfigūraciją. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ms=Selepas pemasangan, tekan butang B pada Penderia Asap Fibaro anda untuk mengemas kini status dan penatarajahan peranti. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.no=Etter installasjon trykker du på B-knappen på Fibaro Smoke Sensor for å oppdatere enhetens status og konfigurasjon. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.pl=Po zakończeniu instalacji naciśnij przycisk B na czujniku dymu Fibaro, aby zaktualizować stan i konfigurację urządzenia. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.pt=Após a instalação, prima o botão B do seu Sensor de Fumo Fibaro para actualizar o estado e a configuração do dispositivo. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ro=După instalare, apăsați butonul B de pe senzorul de fum Fibaro pentru a actualiza starea și configurația dispozitivului. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.ru=После установки нажмите кнопку B на датчике дыма Fibaro, чтобы обновить статус и конфигурацию устройства. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.sr=Nakon instaliranja pritisnite dugme B na FIbaro senzoru za dim kako biste ažurirali status i konfiguraciju uređaja. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.sk=Po nainštalovaní stlačte tlačidlo B na senzore dymu Fibaro, aby sa aktualizoval stav a konfigurácia zariadenia. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.sl=Po namestitvi pritisnite gumb B na senzorju dima Fibaro, da posodobite stanje in konfiguracijo naprave. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.es=Una vez instalado, pulsa el botón B en tu sensor de humo Fibaro para actualizar el estado y la configuración del dispositivo. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.sv=Tryck efter installationen på B-knappen på din Fibaro-brandvarnare för att uppdatera enhetens status och konfiguration. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.th=หลังการติดตั้ง ให้กดปุ่ม B บนเซ็นเซอร์ควัน Fibaro ของคุณเพื่ออัพเดทสถานะและการกำหนดค่าของอุปกรณ์ +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.tr=Yükleme işleminden sonra, cihazın durumunu ve yapılandırmasını güncellemek için Fibaro Duman Sensörünüzde B tuşuna basın. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.uk=Після встановлення натисніть кнопку B на датчику диму Fibaro, щоб оновити стан і конфігурацію пристрою. +'''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.vi=Sau khi cài đặt, nhấn phím B trên Cảm biến khói Fibaro của bạn để cập nhật trạng thái và cấu hình của thiết bị. +'''1 hour'''.en=1 hour +'''1 hour'''.en-gb=1 hour +'''1 hour'''.en-us=1 hour +'''1 hour'''.en-ca=1 hour +'''1 hour'''.en-ph=1 hour +'''1 hour'''.sq=1 orë +'''1 hour'''.ar=ساعة واحدة +'''1 hour'''.be=1 гадзіна +'''1 hour'''.sr-ba=Jedan sat +'''1 hour'''.bg=1 час +'''1 hour'''.ca=1 hora +'''1 hour'''.zh-cn=1 小时 +'''1 hour'''.zh-hk=1 小時 +'''1 hour'''.zh-tw=1 小時 +'''1 hour'''.hr=1 sat +'''1 hour'''.cs=1 hodina +'''1 hour'''.da=1 time +'''1 hour'''.nl=1 uur +'''1 hour'''.et=1 tund +'''1 hour'''.fi=1 tunti +'''1 hour'''.fr=1 heure +'''1 hour'''.fr-ca=1 heure +'''1 hour'''.de=1 Stunde +'''1 hour'''.el=1 ώρα +'''1 hour'''.iw=שעה אחת +'''1 hour'''.hi-in=1 घंटा +'''1 hour'''.hu=1 óra +'''1 hour'''.is=1 klukkustund +'''1 hour'''.in=1 jam +'''1 hour'''.it=1 ora +'''1 hour'''.ja=1時間 +'''1 hour'''.ko=1시간 +'''1 hour'''.lv=1 stunda +'''1 hour'''.lt=1 val. +'''1 hour'''.ms=1 jam +'''1 hour'''.no=1 time +'''1 hour'''.pl=1 godzina +'''1 hour'''.pt=1 hora +'''1 hour'''.ro=1 oră +'''1 hour'''.ru=1 час +'''1 hour'''.sr=Jedan sat +'''1 hour'''.sk=1 hodina +'''1 hour'''.sl=1 h +'''1 hour'''.es=1 hora +'''1 hour'''.sv=En timme +'''1 hour'''.th=1 ชั่วโมง +'''1 hour'''.tr=1 saat +'''1 hour'''.uk=1 година +'''1 hour'''.vi=1 giờ +'''Visual indicator notifications status'''.en=Visual indicator notifications +'''Visual indicator notifications status'''.en-gb=Visual indicator notifications +'''Visual indicator notifications status'''.en-us=Visual indicator notifications +'''Visual indicator notifications status'''.en-ca=Visual indicator notifications +'''Visual indicator notifications status'''.sq=Njoftime me tregues vizual +'''Visual indicator notifications status'''.ar=إشعارات المؤشر المرئي +'''Visual indicator notifications status'''.be=Візуальныя апавяшчэнні індыкатара +'''Visual indicator notifications status'''.sr-ba=Obavještenja vizuelnog indikatora +'''Visual indicator notifications status'''.bg=Уведомления чрез визуални индикатори +'''Visual indicator notifications status'''.ca=Notificacions d'indicador visual +'''Visual indicator notifications status'''.zh-cn=视觉指示器通知 +'''Visual indicator notifications status'''.zh-hk=視覺指示燈通知 +'''Visual indicator notifications status'''.zh-tw=視覺顯示通知 +'''Visual indicator notifications status'''.hr=Obavijesti vizualnog pokazatelja +'''Visual indicator notifications status'''.cs=Vizuální indikátor oznámení +'''Visual indicator notifications status'''.da=Visuelle indikatormeddelelser +'''Visual indicator notifications status'''.nl=Meldingen visuele indicator +'''Visual indicator notifications status'''.et=Visuaalse indikaatori teavitused +'''Visual indicator notifications status'''.fi=Visuaalisen ilmaisimen ilmoitukset +'''Visual indicator notifications status'''.fr=Notifications par indicateur visuel +'''Visual indicator notifications status'''.fr-ca=Notifications par indicateur visuel +'''Visual indicator notifications status'''.de=Optische Anzeigebenachrichtigungen +'''Visual indicator notifications status'''.el=Ειδοποιήσεις οπτικής ένδειξης +'''Visual indicator notifications status'''.iw=התראות מחוון חזותי +'''Visual indicator notifications status'''.hi-in=विजुअल संकेतक सूचनाएँ +'''Visual indicator notifications status'''.hu=Vizuális értesítések +'''Visual indicator notifications status'''.is=Sjónrænar tilkynningar +'''Visual indicator notifications status'''.in=Notifikasi indikator visual +'''Visual indicator notifications status'''.it=Indicatore notifiche visive +'''Visual indicator notifications status'''.ja=ビジュアルインジケーター通知 +'''Visual indicator notifications status'''.ko=시각적 표시 알림 +'''Visual indicator notifications status'''.lv=Vizuālie indikatora ziņojumi +'''Visual indicator notifications status'''.lt=Vaizdo indikatoriaus pranešimai +'''Visual indicator notifications status'''.ms=Pemberitahuan penunjuk visual +'''Visual indicator notifications status'''.no=Visuelle indikatorvarsler +'''Visual indicator notifications status'''.pl=Powiadomienia wizualne +'''Visual indicator notifications status'''.pt=Notificações de indicador visual +'''Visual indicator notifications status'''.ro=Notificări indicator vizual +'''Visual indicator notifications status'''.ru=Уведомления визуального индикатора +'''Visual indicator notifications status'''.sr=Vizuelna indikatorska obaveštenja +'''Visual indicator notifications status'''.sk=Vizuálne indikačné oznámenia +'''Visual indicator notifications status'''.sl=Obvestila vidnega indikatorja +'''Visual indicator notifications status'''.es=Notificaciones de indicador visual +'''Visual indicator notifications status'''.sv=Visuella indikatoraviseringar +'''Visual indicator notifications status'''.th=การแจ้งเตือนตัวระบุการมองเห็น +'''Visual indicator notifications status'''.tr=Görsel gösterge bildirimleri +'''Visual indicator notifications status'''.uk=Візуальні сповіщення на індикаторі +'''Visual indicator notifications status'''.vi=Thông báo chỉ báo trực quan +'''All'''.en=All +'''All'''.en-gb=All +'''All'''.en-us=All +'''All'''.en-ca=All +'''All'''.en-ph=All +'''All'''.sq=Të gjitha +'''All'''.ar=الكل +'''All'''.be=Усе +'''All'''.sr-ba=Sve +'''All'''.bg=Всички +'''All'''.ca=Tot +'''All'''.zh-cn=全部 +'''All'''.zh-hk=全部 +'''All'''.zh-tw=全部 +'''All'''.hr=Sve +'''All'''.cs=Vše +'''All'''.da=Alt +'''All'''.nl=Alle +'''All'''.et=Kõik +'''All'''.fi=Kaikki +'''All'''.fr=Tout +'''All'''.fr-ca=Tout +'''All'''.de=Alle +'''All'''.el=Όλα +'''All'''.iw=הכל +'''All'''.hi-in=सभी +'''All'''.hu=Mind +'''All'''.is=Allt +'''All'''.in=Semua +'''All'''.it=Tutti/e +'''All'''.ja=全て +'''All'''.ko=전체 +'''All'''.lv=Visi +'''All'''.lt=Visi +'''All'''.ms=Semua +'''All'''.no=Alle +'''All'''.pl=Wszystkie +'''All'''.pt=Todas +'''All'''.ro=Toate +'''All'''.ru=Все +'''All'''.sr=Sve +'''All'''.sk=Všetky +'''All'''.sl=Vse +'''All'''.es=Todo +'''All'''.sv=Allt +'''All'''.th=ทั้งหมด +'''All'''.tr=Tümü +'''All'''.uk=Усі +'''All'''.vi=Tất cả +# End of Device Preferences diff --git a/devicetypes/smartthings/zwave-siren.src/i18n/messages.properties b/devicetypes/smartthings/zwave-siren.src/i18n/messages.properties new file mode 100644 index 00000000000..c7d3a81e2fe --- /dev/null +++ b/devicetypes/smartthings/zwave-siren.src/i18n/messages.properties @@ -0,0 +1,306 @@ +# Copyright 2020 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. + +# Device Preferences +'''Enter alarm length'''.en=Enter alarm length +'''Enter alarm length'''.en-gb=Enter alarm length +'''Enter alarm length'''.en-us=Enter alarm length +'''Enter alarm length'''.en-ca=Enter alarm length +'''Enter alarm length'''.sq=Fut kohëzgjatjen e alarmit +'''Enter alarm length'''.ar=إدخال مدة نغمة المنبه +'''Enter alarm length'''.be=Увядзіце працягласць сігнала +'''Enter alarm length'''.sr-ba=Unesi dužinu alarma +'''Enter alarm length'''.bg=Въвеждане на продължителност на алармата +'''Enter alarm length'''.ca=Introduir longitud d'alarma +'''Enter alarm length'''.zh-cn=输入闹钟长度 +'''Enter alarm length'''.zh-hk=輸入警報長度 +'''Enter alarm length'''.zh-tw=輸入警報時間長度 +'''Enter alarm length'''.hr=Unesite trajanje alarma +'''Enter alarm length'''.cs=Zadejte délku alarmu +'''Enter alarm length'''.da=Angiv varighed af alarm +'''Enter alarm length'''.nl=Alarmduur invoeren +'''Enter alarm length'''.et=Sisestage märguande pikkus +'''Enter alarm length'''.fi=Anna hälytyksen pituus +'''Enter alarm length'''.fr=Entrer la durée de l'alarme +'''Enter alarm length'''.fr-ca=Saisir la durée de l'alarme +'''Enter alarm length'''.de=Alarmlänge eingeben +'''Enter alarm length'''.el=Εισαγάγετε διάρκεια συναγερμού +'''Enter alarm length'''.iw=הזן אורך התראה +'''Enter alarm length'''.hi-in=अलार्म की लंबाई प्रविष्ट करें +'''Enter alarm length'''.hu=Adja meg a riasztás hosszát +'''Enter alarm length'''.is=Slá inn lengd viðvörunar +'''Enter alarm length'''.in=Masukkan durasi alarm +'''Enter alarm length'''.it=Inserisci durata allarme +'''Enter alarm length'''.ja=アラームの長さを入力 +'''Enter alarm length'''.ko=알람 길이를 입력하세요 +'''Enter alarm length'''.lv=Ievadiet signāla ilgumu +'''Enter alarm length'''.lt=Įveskite signalo trukmę +'''Enter alarm length'''.ms=Masukkan panjang penggera +'''Enter alarm length'''.no=Angi alarmlengde +'''Enter alarm length'''.pl=Wprowadź długość alarmu +'''Enter alarm length'''.pt=Introduzir duração do alarme +'''Enter alarm length'''.ro=Introducere durată alarmă +'''Enter alarm length'''.ru=Укажите продолжительность сигнала +'''Enter alarm length'''.sr=Unesite dužinu trajanja alarma +'''Enter alarm length'''.sk=Zadajte dĺžku alarmu +'''Enter alarm length'''.sl=Vnesite dolžino alarma +'''Enter alarm length'''.es=Introduce la duración de la alarma +'''Enter alarm length'''.sv=Ange larmlängden +'''Enter alarm length'''.th=ใส่ระยะเวลาปลุก +'''Enter alarm length'''.tr=Alarm uzunluğu girin +'''Enter alarm length'''.uk=Уведіть тривалість сигналу +'''Enter alarm length'''.vi=Nhập độ dài chuông báo +'''Alarm length'''.en=Alarm length +'''Alarm length'''.en-gb=Alarm length +'''Alarm length'''.en-us=Alarm length +'''Alarm length'''.en-ca=Alarm length +'''Alarm length'''.sq=Kohëzgjatja e alarmit +'''Alarm length'''.ar=مدة نغمة المنبه +'''Alarm length'''.be=Працягласць сігналу +'''Alarm length'''.sr-ba=Dužina alarma +'''Alarm length'''.bg=Продължителност на алармата +'''Alarm length'''.ca=Longitud d'alarma +'''Alarm length'''.zh-cn=闹钟长度 +'''Alarm length'''.zh-hk=警報長度 +'''Alarm length'''.zh-tw=警報時間長度 +'''Alarm length'''.hr=Trajanje alarma +'''Alarm length'''.cs=Délka alarmu +'''Alarm length'''.da=Varighed af alarm +'''Alarm length'''.nl=Alarmduur +'''Alarm length'''.et=Märguande pikkus +'''Alarm length'''.fi=Hälytyksen pituus +'''Alarm length'''.fr=Durée de l'alarme +'''Alarm length'''.fr-ca=Durée de l'alarme +'''Alarm length'''.de=Alarmlänge +'''Alarm length'''.el=Διάρκεια συναγερμού +'''Alarm length'''.iw=אורך התראה +'''Alarm length'''.hi-in=अलार्म की लंबाई +'''Alarm length'''.hu=Riasztás hossza +'''Alarm length'''.is=Lengd viðvörunar +'''Alarm length'''.in=Durasi alarm +'''Alarm length'''.it=Lunghezza allarme +'''Alarm length'''.ja=アラームの長さ +'''Alarm length'''.ko=알람 길이 +'''Alarm length'''.lv=Signāla ilgums +'''Alarm length'''.lt=Signalo ilgis +'''Alarm length'''.ms=Panjang penggera +'''Alarm length'''.no=Alarmlengde +'''Alarm length'''.pl=Długość alarmu +'''Alarm length'''.pt=Duração do alarme +'''Alarm length'''.ro=Lungime alarmă +'''Alarm length'''.ru=Продолжительность сигнала +'''Alarm length'''.sr=Dužina trajanja alarma +'''Alarm length'''.sk=Dĺžka alarmu +'''Alarm length'''.sl=Dolžina alarma +'''Alarm length'''.es=Duración de la alarma +'''Alarm length'''.sv=Larmlängd +'''Alarm length'''.th=ระยะเวลาเตือน +'''Alarm length'''.tr=Alarm uzunluğu +'''Alarm length'''.uk=Тривалість сигналу +'''Alarm length'''.vi=Độ dài chuông báo +'''This setting only applies to Yale sirens.'''.en=This setting only applies to Yale sirens. +'''This setting only applies to Yale sirens.'''.en-gb=This setting only applies to Yale sirens. +'''This setting only applies to Yale sirens.'''.en-us=This setting only applies to Yale sirens. +'''This setting only applies to Yale sirens.'''.en-ca=This setting only applies to Yale sirens. +'''This setting only applies to Yale sirens.'''.sq=Ky cilësim vlen vetëm për sirenat Yale. +'''This setting only applies to Yale sirens.'''.ar=ينطبق هذا الضبط على صفارات إنذار‬ Yale فقط. +'''This setting only applies to Yale sirens.'''.be=Гэта налада прымяняецца толькі да сірэн Yale. +'''This setting only applies to Yale sirens.'''.sr-ba=Ova postavka se primjenjuje isključivo na sirene kompanije Yale. +'''This setting only applies to Yale sirens.'''.bg=Тази настройка важи само за сирени от Yale. +'''This setting only applies to Yale sirens.'''.ca=Aquest ajustament només s'aplica a les sirenes Yale. +'''This setting only applies to Yale sirens.'''.zh-cn=此设置仅适用于 Yale 报警器。 +'''This setting only applies to Yale sirens.'''.zh-hk=此設定僅適用於 Yale 警報器。 +'''This setting only applies to Yale sirens.'''.zh-tw=此設定僅適用於 Yale 警報。 +'''This setting only applies to Yale sirens.'''.hr=Ova se postavka primjenjuje isključivo na sirene tvrtke Yale. +'''This setting only applies to Yale sirens.'''.cs=Toto nastavení platí pouze pro sirény Yale. +'''This setting only applies to Yale sirens.'''.da=Denne indstilling gælder kun for Yale-sirener. +'''This setting only applies to Yale sirens.'''.nl=Deze instelling geldt alleen voor Yale-alarmen. +'''This setting only applies to Yale sirens.'''.et=See seadistus kehtib ainult Yale’i sireenide puhul. +'''This setting only applies to Yale sirens.'''.fi=Tämä asetus koskee vain Yale-sireenejä. +'''This setting only applies to Yale sirens.'''.fr=Ce paramètre s'applique uniquement aux sirènes Yale. +'''This setting only applies to Yale sirens.'''.fr-ca=Ce paramètre s'applique uniquement aux sirènes Yale. +'''This setting only applies to Yale sirens.'''.de=Diese Einstellung wird nur auf Yale-Sirenen angewendet. +'''This setting only applies to Yale sirens.'''.el=Αυτή η ρύθμιση ισχύει μόνο για τους συναγερμούς Yale. +'''This setting only applies to Yale sirens.'''.iw=הגדרה זו חלה על אזעקות של Yale בלבד. +'''This setting only applies to Yale sirens.'''.hi-in=यह सेटिंग केवल येल सायरन्स पर लागू होती है। +'''This setting only applies to Yale sirens.'''.hu=Ez a beállítás csak a Yale szirénákra vonatkozik. +'''This setting only applies to Yale sirens.'''.is=Þessi stilling á aðeins við um Yale-sírenur. +'''This setting only applies to Yale sirens.'''.in=Pengaturan ini hanya berlaku bagi sirine Yale. +'''This setting only applies to Yale sirens.'''.it=Questa impostazione si applica solo alle sirene Yale. +'''This setting only applies to Yale sirens.'''.ja=この設定はYaleサイレンにのみ適用されます。 +'''This setting only applies to Yale sirens.'''.ko=이 설정은 Yale 사이렌에만 적용돼요. +'''This setting only applies to Yale sirens.'''.lv=Šis iestatījums attiecas tikai uz Yale sirēnām. +'''This setting only applies to Yale sirens.'''.lt=Šis nustatymas taikomas tik „Yale“ signalizacijoms. +'''This setting only applies to Yale sirens.'''.ms=Aturan ini hanya terpakai kepada siren Yale. +'''This setting only applies to Yale sirens.'''.no=Denne innstillingen gjelder bare Yale-sirener. +'''This setting only applies to Yale sirens.'''.pl=To ustawienie dotyczy tylko syren Yale. +'''This setting only applies to Yale sirens.'''.pt=Esta definição apenas se aplica às sirenes Yale. +'''This setting only applies to Yale sirens.'''.ro=Setarea se va aplica doar sirenelor Yale. +'''This setting only applies to Yale sirens.'''.ru=Этот параметр применяется только к сиренам Yale. +'''This setting only applies to Yale sirens.'''.sr=Ovo podešavanje se odnosi samo na Yale sirene. +'''This setting only applies to Yale sirens.'''.sk=Toto nastavenie sa vzťahuje iba na sirény Yale. +'''This setting only applies to Yale sirens.'''.sl=Ta nastavitev velja samo za sirene Yale. +'''This setting only applies to Yale sirens.'''.es=Este ajuste solo se aplica a las sirenas Yale. +'''This setting only applies to Yale sirens.'''.sv=Denna inställning gäller bara Yale-sirener. +'''This setting only applies to Yale sirens.'''.th=การตั้งค่านี้ใช้ได้กับไซเรนของ Yale เท่านั้น +'''This setting only applies to Yale sirens.'''.tr=Bu ayar sadece Yale sirenleri için geçerlidir. +'''This setting only applies to Yale sirens.'''.uk=Цей параметр стосується лише сирен Yale. +'''This setting only applies to Yale sirens.'''.vi=Cài đặt này chỉ áp dụng lên còi Yale. +'''Alarm LED flash'''.en=Alarm LED flash +'''Alarm LED flash'''.en-gb=Alarm LED flash +'''Alarm LED flash'''.en-us=Alarm LED flash +'''Alarm LED flash'''.en-ca=Alarm LED flash +'''Alarm LED flash'''.sq=Flash-i LED i alarmit +'''Alarm LED flash'''.ar=منبه ذو وميض LED +'''Alarm LED flash'''.be=Мігценне індыкатара сігналу +'''Alarm LED flash'''.sr-ba=LED blic alarma +'''Alarm LED flash'''.bg=Аларма LED светкавица +'''Alarm LED flash'''.ca=Flaix LED d'alarma +'''Alarm LED flash'''.zh-cn=闹钟 LED 闪烁 +'''Alarm LED flash'''.zh-hk=警報 LED 閃爍 +'''Alarm LED flash'''.zh-tw=LED 閃燈警報 +'''Alarm LED flash'''.hr=LED bljeskalica alarma +'''Alarm LED flash'''.cs=Blikání LED při alarmu +'''Alarm LED flash'''.da=Alarm-LED blinker +'''Alarm LED flash'''.nl=Alarm-LED met flits +'''Alarm LED flash'''.et=Märguande LED-i vilkumine +'''Alarm LED flash'''.fi=Hälytyksen merkkivalon välähdys +'''Alarm LED flash'''.fr=Alarme avec flash LED +'''Alarm LED flash'''.fr-ca=Alarme avec flash DEL +'''Alarm LED flash'''.de=Alarm-LED-Blitz +'''Alarm LED flash'''.el=Αναβοσβ. το LED του συναγερμού +'''Alarm LED flash'''.iw=הבהוב בנורית התראה +'''Alarm LED flash'''.hi-in=अलार्म LED फ्लैश +'''Alarm LED flash'''.hu=Riasztó LED-jének villogtatása +'''Alarm LED flash'''.is=Blikkandi LED-ljós með viðvörun +'''Alarm LED flash'''.in=Cahaya LED alarm +'''Alarm LED flash'''.it=Flash LED di allarme +'''Alarm LED flash'''.ja=アラームLEDフラッシュ +'''Alarm LED flash'''.ko=LED 불빛 알람 +'''Alarm LED flash'''.lv=Mirgojošs brīdinājuma LED +'''Alarm LED flash'''.lt=Signalo LED mirksėjimas +'''Alarm LED flash'''.ms=Kelip LED penggera +'''Alarm LED flash'''.no=LED-blink for alarm +'''Alarm LED flash'''.pl=Dioda LED alarmu +'''Alarm LED flash'''.pt=Flash de LED do alarme +'''Alarm LED flash'''.ro=Alarmă cu LED +'''Alarm LED flash'''.ru=Мигание во время сигнала +'''Alarm LED flash'''.sr=LED blic alarma +'''Alarm LED flash'''.sk=Blikanie poplachovej LED diódy +'''Alarm LED flash'''.sl=Bliskavica LED ob alarmu +'''Alarm LED flash'''.es=Flash LED de la alarma +'''Alarm LED flash'''.sv=Blinkande larmlysdiod +'''Alarm LED flash'''.th=กะพริบไฟ LED เตือน +'''Alarm LED flash'''.tr=Alarm LED'inin yanıp sönmesi +'''Alarm LED flash'''.uk=Блимання під час сигналу +'''Alarm LED flash'''.vi=Đèn flash LED chuông báo +'''Comfort LED (x10 sec)'''.en=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.en-gb=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.en-us=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.en-ca=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.en-ph=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.sq=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.ar=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.be=Comfort LED (×١٠‬ ثوانٍ) +'''Comfort LED (x10 sec)'''.sr-ba=Կոմֆորտ լուսադիոդ (x10 վ) +'''Comfort LED (x10 sec)'''.bg=আৰামদায়ক LED (x10 ছেকেণ্ড) +'''Comfort LED (x10 sec)'''.ca=আৰামদায়ক LED (x10 ছেকেণ্ড) +'''Comfort LED (x10 sec)'''.zh-cn=Komfort LED (x10 san) +'''Comfort LED (x10 sec)'''.zh-hk=Komfort LED (x10 san) +'''Comfort LED (x10 sec)'''.zh-tw=LED erosoa (×10 s) +'''Comfort LED (x10 sec)'''.hr=LED erosoa (×10 s) +'''Comfort LED (x10 sec)'''.cs=Comfort LED (x10 секунд) +'''Comfort LED (x10 sec)'''.da=Comfort LED (x10 секунд) +'''Comfort LED (x10 sec)'''.nl=স্বস্তিজনক LED (x10 সেকেন্ড) +'''Comfort LED (x10 sec)'''.et=স্বস্তিজনক LED (x10 সেকেন্ড) +'''Comfort LED (x10 sec)'''.fi=Bljes. LED lamp. (puta 10 sek.) +'''Comfort LED (x10 sec)'''.fr=Bljeskanje LED lampice (puta 10 sekundi) +'''Comfort LED (x10 sec)'''.fr-ca=Комфортен светодиод (x10 сек) +'''Comfort LED (x10 sec)'''.de=LED de comoditat (x10 s) +'''Comfort LED (x10 sec)'''.el=舒适 LED (x10 秒) +'''Comfort LED (x10 sec)'''.iw=舒適型 LED (x10 秒) +'''Comfort LED (x10 sec)'''.hi-in=舒適型 LED (x10 秒) +'''Comfort LED (x10 sec)'''.hu=舒適 LED (x10 秒) +'''Comfort LED (x10 sec)'''.is=舒適 LED (x10 秒) +'''Comfort LED (x10 sec)'''.in=Bljesk. LED lamp. (x10 sekundi) +'''Comfort LED (x10 sec)'''.it=Komfortní LED (x10 s) +'''Comfort LED (x10 sec)'''.ja=Komfortní LED (x10 s) +'''Comfort LED (x10 sec)'''.ko=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.lv=LED راحتی (هر ۱۰ ثانیه) +'''Comfort LED (x10 sec)'''.lt=LED راحتی (هر ۱۰ ثانیه) +'''Comfort LED (x10 sec)'''.ms=Comfort LED (x10 sec) +'''Comfort LED (x10 sec)'''.no=LED de confort (x10 segundos) +'''Comfort LED (x10 sec)'''.pl=კომფორტული LED (x10 წმ) +'''Comfort LED (x10 sec)'''.pt=კომფორტული LED (x10 წმ) +'''Comfort LED (x10 sec)'''.ro=LED άνεσης (x10 δευτ.) +'''Comfort LED (x10 sec)'''.ru=LED άνεσης (x10 δευτ.) +'''Comfort LED (x10 sec)'''.sr=અનૂકૂળ LED (x10 સે) +'''Comfort LED (x10 sec)'''.sk=נורת LED לנוחות (x10 שניות) +'''Comfort LED (x10 sec)'''.sl=कम्फर्ट LED (x10 सेकंड) +'''Comfort LED (x10 sec)'''.es=कम्फर्ट LED (x10 सेकंड) +'''Comfort LED (x10 sec)'''.sv=Komfort LED (x10 mp) +'''Comfort LED (x10 sec)'''.th=LED compoird (x10 soic) +'''Comfort LED (x10 sec)'''.tr=Comfort LED (da 10 sec) +'''Comfort LED (x10 sec)'''.uk=Comfort LED (da 10 sec) +'''Comfort LED (x10 sec)'''.vi=ಆರಾಮದಾಯಕ LED (x10 ಸೆಕೆ) +'''Tamper alert'''.en=Tamper alert +'''Tamper alert'''.en-gb=Tamper alert +'''Tamper alert'''.en-us=Tamper alert +'''Tamper alert'''.en-ca=Tamper alert +'''Tamper alert'''.en-ph=Tamper alert +'''Tamper alert'''.sq=Sinjalizim për prekje +'''Tamper alert'''.ar=تنبيه بالعبث +'''Tamper alert'''.be=Абвестка аб незаконным доступе +'''Tamper alert'''.sr-ba=Upozorenje o izmjeni +'''Tamper alert'''.bg=Известие за подправяне +'''Tamper alert'''.ca=Modificar avís +'''Tamper alert'''.zh-cn=异常提醒 +'''Tamper alert'''.zh-hk=異常提示 +'''Tamper alert'''.zh-tw=異常警報 +'''Tamper alert'''.hr=Promijeni upozorenje +'''Tamper alert'''.cs=Upozornění na manipulaci +'''Tamper alert'''.da=Ændringsvarsel +'''Tamper alert'''.nl=Melding geknoeid +'''Tamper alert'''.et=Manipuleerimise märguanne +'''Tamper alert'''.fi=Peukalointihälytys +'''Tamper alert'''.fr=Alerte d'altération +'''Tamper alert'''.fr-ca=Alerte d'altération +'''Tamper alert'''.de=Modifikationswarnung +'''Tamper alert'''.el=Ειδοποίηση τροποποίησης +'''Tamper alert'''.iw=התראת טיפול לא מורשה +'''Tamper alert'''.hi-in=छेड़छाड़ सतर्क +'''Tamper alert'''.hu=Manipulálási riasztás +'''Tamper alert'''.is=Viðvörun vegna fikts +'''Tamper alert'''.in=Peringatan gangguan +'''Tamper alert'''.it=Avviso di manomissione +'''Tamper alert'''.ja=改ざん通知 +'''Tamper alert'''.ko=비정상 조작 감지 경고 +'''Tamper alert'''.lv=Brīdinājums par iejaukšanos +'''Tamper alert'''.lt=Įsilaužimo įspėjimas +'''Tamper alert'''.ms=Amaran usikan +'''Tamper alert'''.no=Manipuleringsvarsel +'''Tamper alert'''.pl=Alert modyfikacji +'''Tamper alert'''.pt=Alerta de manipulação +'''Tamper alert'''.ro=Alertă interferențe +'''Tamper alert'''.ru=Оповещение о неисправности +'''Tamper alert'''.sr=Upozorenje na modifikovanje +'''Tamper alert'''.sk=Upozornenie na manipuláciu +'''Tamper alert'''.sl=Opozorilo o posegu +'''Tamper alert'''.es=Alerta de manipulación +'''Tamper alert'''.sv=Manipuleringsavisering +'''Tamper alert'''.th=การเตือนการดัดแปลง +'''Tamper alert'''.tr=Kurcalama uyarısı +'''Tamper alert'''.uk=Сповіщення про несправність +'''Tamper alert'''.vi=Cảnh báo nhiễu +# End of Device Preferences diff --git a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy index 35aa75e425f..27426889e90 100644 --- a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy +++ b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy @@ -79,13 +79,13 @@ metadata { // Yale siren only preferences { - input name: "alarmLength", type: "number", title: "Alarm length", description: "This setting does not apply to all devices", range: "1..10" + input name: "alarmLength", type: "number", title: "Alarm length", description: "Enter alarm length", range: "1..10" // defaultValue: 10 - input name: "alarmLEDflash", type: "bool", title: "Alarm LED flash", description: "This setting does not apply to all devices" + input name: "alarmLEDflash", type: "bool", title: "Alarm LED flash", description: "This setting only applies to Yale sirens." // defaultValue: false - input name: "comfortLED", type: "number", title: "Comfort LED (x10 sec.)", description: "This setting does not apply to all devices", range: "0..25" + input name: "comfortLED", type: "number", title: "Comfort LED (x10 sec.)", description: "This setting only applies to Yale sirens.", range: "0..25" // defaultValue: 0 - input name: "tamper", type: "bool", title: "Tamper alert", description: "This setting does not apply to all devices" + input name: "tamper", type: "bool", title: "Tamper alert", description: "This setting only applies to Yale sirens." // defaultValue: false } From dd8eec5073e9eddccffb3633c83877d3ab0a2ed1 Mon Sep 17 00:00:00 2001 From: Ajax Online <56229160+Ajax-online@users.noreply.github.com> Date: Fri, 4 Sep 2020 12:39:42 +0100 Subject: [PATCH 055/422] DevWs for Ajaxonline Ltd containing containing ZigBee RGBW Bulb (#42713) * DevWs for Ajaxonline Ltd containing containing ZigBee RGBW Bulb * Update zigbee-rgbw-bulb.groovy I fixed spacing (4 spaces -> 1 tab) Co-authored-by: Konrad K <33450498+KKlimczukS@users.noreply.github.com> --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 9b2ce34265e..24fc4c54c6f 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -96,6 +96,7 @@ metadata { // Ajax Online fingerprint manufacturer: "Ajaxonline", model: "AJ-RGBCCT 5 in 1", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" + fingerprint manufacturer: "Ajax online Ltd", model: "AJ_ZB30_GU10", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" // Raw Description: 0B 0104 010D 01 08 0000 0003 0004 0005 0006 0008 0300 1000 00 } // UI tile definitions From d51a6a97e024fe8f645f29db07f1fb09843fb57a Mon Sep 17 00:00:00 2001 From: greens Date: Fri, 4 Sep 2020 12:55:10 -0700 Subject: [PATCH 056/422] ONEAPP-33322 Update range for tempOffset preference --- devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy | 2 +- .../ezex-temp-humidity-sensor.groovy | 2 +- .../smartthings/smartsense-button.src/smartsense-button.groovy | 2 +- .../smartsense-garage-door-multi.groovy | 2 +- .../smartsense-garage-door-sensor-button.groovy | 2 +- .../smartsense-motion-sensor.groovy | 2 +- .../smartsense-multi-sensor.src/smartsense-multi-sensor.groovy | 2 +- .../smartthings/smartsense-multi.src/smartsense-multi.groovy | 2 +- .../smartsense-temp-humidity-sensor.groovy | 2 +- .../smartsense-virtual-open-closed.groovy | 2 +- .../tyco-door-window-sensor.src/tyco-door-window-sensor.groovy | 2 +- .../zigbee-motion-temp-humidity-sensor.groovy | 2 +- .../zwave-door-temp-sensor.src/zwave-door-temp-sensor.groovy | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy index 1e37af84c0f..d8ff0f971e9 100644 --- a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy +++ b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy @@ -49,7 +49,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "-100..100", displayDuringSetup: false input "interval", "number", title: "Report Interval", description: "How often the device should report in minutes", range: "1..120", defaultValue: 10, displayDuringSetup: false input "resetMinMax", "bool", title: "Reset Humidity min and max", required: false, displayDuringSetup: false } diff --git a/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy b/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy index 1b7fb7f29fa..6a1be540d2c 100755 --- a/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/ezex-temp-humidity-sensor.src/ezex-temp-humidity-sensor.groovy @@ -28,7 +28,7 @@ metadata { preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false input "humidityOffset", "number", title: "Humidity offset", description: "Enter a percentage to adjust the humidity.", range: "*..*", displayDuringSetup: false } diff --git a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy index e4cfdb55d1f..209c08ad376 100755 --- a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy +++ b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy @@ -43,7 +43,7 @@ metadata { ]) } section { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } } diff --git a/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy b/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy index 04ce5baff1c..e33f187f81e 100644 --- a/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy +++ b/devicetypes/smartthings/smartsense-garage-door-multi.src/smartsense-garage-door-multi.groovy @@ -71,7 +71,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } } diff --git a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy index 50226cce856..4521db28a53 100644 --- a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy +++ b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy @@ -88,7 +88,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } } diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index cfcad896320..e5d41b088db 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -59,7 +59,7 @@ metadata { ]) } section { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } } 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 f17aa367b46..0965e848582 100755 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -64,7 +64,7 @@ metadata { ]) } section { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } section { input("garageSensor", "enum", title: "Use on garage door", description: "", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false) diff --git a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy index 5393df351f2..efd0494c6c1 100644 --- a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy +++ b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy @@ -43,7 +43,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } tiles(scale: 2) { 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 b7dc41ea645..b600c95c1ef 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 @@ -45,7 +45,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false input "humidityOffset", "number", title: "Humidity offset", description: "Enter a percentage to adjust the humidity.", range: "*..*", displayDuringSetup: false } diff --git a/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy b/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy index c4607775b7a..47352273166 100644 --- a/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy +++ b/devicetypes/smartthings/smartsense-virtual-open-closed.src/smartsense-virtual-open-closed.groovy @@ -45,7 +45,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } tiles { diff --git a/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy b/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy index c43fe82be2b..8e5d5c5410b 100644 --- a/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy +++ b/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy @@ -33,7 +33,7 @@ metadata { } preferences { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } tiles(scale: 2) { diff --git a/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy b/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy index 62a78293fb1..11d6e967b64 100644 --- a/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/zigbee-motion-temp-humidity-sensor.groovy @@ -44,7 +44,7 @@ metadata { ]) } section { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false input "humidityOffset", "number", title: "Humidity offset", description: "Enter a percentage to adjust the humidity.", range: "*..*", displayDuringSetup: false } } diff --git a/devicetypes/smartthings/zwave-door-temp-sensor.src/zwave-door-temp-sensor.groovy b/devicetypes/smartthings/zwave-door-temp-sensor.src/zwave-door-temp-sensor.groovy index 72ee0e9f021..79f12250846 100644 --- a/devicetypes/smartthings/zwave-door-temp-sensor.src/zwave-door-temp-sensor.groovy +++ b/devicetypes/smartthings/zwave-door-temp-sensor.src/zwave-door-temp-sensor.groovy @@ -32,7 +32,7 @@ metadata { preferences { section { - input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "*..*", displayDuringSetup: false + input "tempOffset", "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false } } From a8bc1e6fc174314fd08ac8d5bf23c912820af10a Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 8 Sep 2020 19:49:15 +0200 Subject: [PATCH 057/422] ICP-11574 - Query device for current status in response to WakeUpNotification (#42710) --- .../zwave-basic-smoke-alarm.groovy | 4 ++-- .../zwave-smoke-alarm.src/zwave-smoke-alarm.groovy | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy b/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy index 3d4dd97e472..5f7d1db7632 100644 --- a/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy +++ b/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy @@ -195,12 +195,12 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, res zwave.notificationV3.notificationGet(notificationType: 0x01).format(), zwave.batteryV1.batteryGet().format(), zwave.wakeUpV1.wakeUpNoMoreInformation().format() - ]), 2000 ) + ], 2000)) } else { results << response(delayBetween([ zwave.notificationV3.notificationGet(notificationType: 0x01).format(), zwave.wakeUpV1.wakeUpNoMoreInformation().format() - ]), 2000 ) + ], 2000)) } } diff --git a/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy b/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy index ab720ea56c3..e09023afd4f 100644 --- a/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy +++ b/devicetypes/smartthings/zwave-smoke-alarm.src/zwave-smoke-alarm.groovy @@ -195,12 +195,12 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, res zwave.notificationV3.notificationGet(notificationType: 0x01).format(), zwave.batteryV1.batteryGet().format(), zwave.wakeUpV1.wakeUpNoMoreInformation().format() - ]), 2000 ) + ], 2000)) } else { results << response(delayBetween([ zwave.notificationV3.notificationGet(notificationType: 0x01).format(), zwave.wakeUpV1.wakeUpNoMoreInformation().format() - ]), 2000 ) + ], 2000)) } } From 47afaebe5c076a77910eed8d3be97f7b551eb63a Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 8 Sep 2020 21:04:28 +0200 Subject: [PATCH 058/422] WWST-5667_2 - check if device is SOMFY, code refactoring. (#42726) --- .../zigbee-window-shade.groovy | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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 e8f4e28e40a..0ec1b5207dc 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -273,7 +273,7 @@ private List readDeviceBindingTable() { } def shouldInvertLiftPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() || isSomfyGlydea() || isSomfySonesse() + return isIkeaKadrilj() || isIkeaFyrtur() || isSomfy() } def reportsBatteryPercentage() { @@ -288,11 +288,6 @@ def isIkeaFyrtur() { device.getDataValue("model") == "FYRTUR block-out roller blind" } -def isSomfyGlydea() { - device.getDataValue("model") == "Glydea Somfy" -} - -def isSomfySonesse() { - // the default model name can be changed by the user from the Somfy Set&Go bluetooth app. - device.getDataValue("model") == "Roller" +def isSomfy() { + device.getDataValue("manufacturer") == "SOMFY" } \ No newline at end of file From 7c9e809dbd29844fd83a84ff889f37aebb9f1fd0 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 8 Sep 2020 13:05:56 -0700 Subject: [PATCH 059/422] CHAD-5235 Hotfix en-uk is not a valid language code. this was due to manual editing on my part --- .../fibaro-smoke-sensor.src/i18n/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties b/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties index 0f31fa75b74..f4577247cf5 100644 --- a/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties @@ -639,7 +639,7 @@ '''Available settings: 1-100 C'''.uk=Виберіть, наскільки температура має відрізнятися від раніше записаної для надсилання сповіщення про температуру. Можна ввести значення від 1 до 100, яке потім буде помножено на 0,1. Наприклад, якщо ввести 20, сповіщення надсилатиметься кожного разу, коли температура змінюватиметься на 2°C чи більше. '''Available settings: 1-100 C'''.vi=Chọn mức nhiệt độ khác biệt với nhiệt độ đã báo cáo trước đó để gửi báo cáo nhiệt độ mới. Bạn có thể nhập một giá trị từ 1 đến 100. Giá trị bạn nhập sẽ được nhân với 0,1. Ví dụ, nếu bạn nhập 20, báo cáo sẽ được gửi mỗi khi nhiệt độ thay đổi từ 2°C trở lên. '''To check smoke detection state'''.en=Checking the smoke detection state -'''To check smoke detection state'''.en-uk=Checking the smoke detection state +'''To check smoke detection state'''.en-gb=Checking the smoke detection state '''To check smoke detection state'''.en-us=Checking the smoke detection state '''To check smoke detection state'''.en-ca=Checking the smoke detection state '''To check smoke detection state'''.sq=Kontroll i statusit të pikasjes së tymit From c49f27c15b44c8e514c4680b107bd0590e2a3618 Mon Sep 17 00:00:00 2001 From: greens Date: Thu, 10 Sep 2020 11:00:36 -0700 Subject: [PATCH 060/422] CHAD-5235 CHAD-5236 Spot fixes for translations Programmatic default values are overwritten by "Tap to set" in DM if they have not yet been set and "tap to set" is not translated. Fixes missing period that was preventing the application of translations. --- .../fibaro-smoke-sensor.groovy | 12 ++++++------ .../smartthings/zwave-siren.src/zwave-siren.groovy | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy b/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy index cc4e9ef70d2..d5c08dc86a7 100644 --- a/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy +++ b/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy @@ -55,25 +55,25 @@ metadata { title: "To check smoke detection state", displayDuringSetup: true, type: "paragraph", element: "paragraph" input description: "Please consult Fibaro Smoke Sensor operating manual for advanced setting options. You can skip this configuration to use default settings", title: "Advanced settings", displayDuringSetup: true, type: "paragraph", element: "paragraph" - input "smokeSensorSensitivity", "enum", title: "Smoke sensor sensitivity", options: ["High", "Medium", "Low"], defaultValue: "${smokeSensorSensitivity}", displayDuringSetup: true + input "smokeSensorSensitivity", "enum", title: "Smoke sensor sensitivity", options: ["High", "Medium", "Low"], defaultValue: "Medium", displayDuringSetup: true input "zwaveNotificationStatus", "enum", title: "Notifications", options: ["None", "Casing opened", "Exceeding temperature threshold", "Lack of Z-Wave range", "All"], // defaultValue: "${zwaveNotificationStatus}", displayDuringSetup: true //Setting the default to casing opened so it can work in SmartThings mobile app. defaultValue: "Casing opened", displayDuringSetup: true input "visualIndicatorNotificationStatus", "enum", title: "Visual indicator notifications status", options: ["None", "Casing opened", "Exceeding temperature threshold", "Lack of Z-Wave range", "All"], - defaultValue: "${visualIndicatorNotificationStatus}", displayDuringSetup: true + defaultValue: "None", displayDuringSetup: true input "soundNotificationStatus", "enum", title: "Sound notifications status", options: ["None", "Casing opened", "Exceeding temperature threshold", "Lack of Z-Wave range", "All"], - defaultValue: "${soundNotificationStatus}", displayDuringSetup: true + defaultValue: "None", displayDuringSetup: true input "temperatureReportInterval", "enum", title: "Temperature report interval", - options: ["Reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${temperatureReportInterval}", displayDuringSetup: true + options: ["Reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "30 minutes", displayDuringSetup: true input "temperatureReportHysteresis", "number", title: "Temperature report hysteresis", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true input "temperatureThreshold", "number", title: "Overheat temperature threshold", description: "Available settings: 0 or 2-100 C", range: "0..100", displayDuringSetup: true input "excessTemperatureSignalingInterval", "enum", title: "Excess temperature signaling interval", - options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${excessTemperatureSignalingInterval}", displayDuringSetup: true + options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "30 minutes", displayDuringSetup: true input "lackOfZwaveRangeIndicationInterval", "enum", title: "Lack of Z-Wave range indication interval", - options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${lackOfZwaveRangeIndicationInterval}", displayDuringSetup: true + options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "6 hours", displayDuringSetup: true } tiles (scale: 2){ multiAttributeTile(name:"smoke", type: "lighting", width: 6, height: 4){ diff --git a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy index 27426889e90..1f95814ecaf 100644 --- a/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy +++ b/devicetypes/smartthings/zwave-siren.src/zwave-siren.groovy @@ -83,7 +83,7 @@ metadata { // defaultValue: 10 input name: "alarmLEDflash", type: "bool", title: "Alarm LED flash", description: "This setting only applies to Yale sirens." // defaultValue: false - input name: "comfortLED", type: "number", title: "Comfort LED (x10 sec.)", description: "This setting only applies to Yale sirens.", range: "0..25" + input name: "comfortLED", type: "number", title: "Comfort LED (x10 sec)", description: "This setting only applies to Yale sirens.", range: "0..25" // defaultValue: 0 input name: "tamper", type: "bool", title: "Tamper alert", description: "This setting only applies to Yale sirens." // defaultValue: false From ef649f714bce2adeafa2719aa18e3362ec85f67c Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 10 Sep 2020 22:42:54 +0200 Subject: [PATCH 061/422] [WWST-4667] Qubino 3 Phase Meter integration (#43205) * New DTHes to support Qubino 3 Phase Meter * Added raw description, changed child device DTH other minor fixes --- .../qubino-3-phase-meter.groovy | 195 ++++++++++++++++++ .../child-energy-meter.groovy | 50 +++++ 2 files changed, 245 insertions(+) create mode 100644 devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy create mode 100644 devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy diff --git a/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy b/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy new file mode 100644 index 00000000000..fef00c54bd2 --- /dev/null +++ b/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy @@ -0,0 +1,195 @@ +/** + * Copyright 2020 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: "Qubino 3 Phase Meter", namespace: "qubino", author: "SmartThings", ocfDeviceType: "x.com.st.d.energymeter", mcdSync: true) { + capability "Energy Meter" + capability "Power Meter" + capability "Configuration" + capability "Sensor" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0159", prod: "0007", model: "0054", deviceJoinName: "Qubino Energy Monitor" //Qubino 3 Phase Meter + // zw:L type:3103 mfr:0159 prod:0007 model:0054 ver:1.00 zwv:4.61 lib:03 cc:5E,55,86,73,56,98,9F,72,5A,70,60,85,8E,59,32,6C,7A epc:4 + } + + // tile definitions + tiles(scale: 2) { + multiAttributeTile(name:"power", type: "generic", width: 6, height: 4){ + tileAttribute("device.power", key: "PRIMARY_CONTROL") { + attributeState("default", label:'${currentValue} W') + } + tileAttribute("device.energy", key: "SECONDARY_CONTROL") { + attributeState("default", label:'${currentValue} kWh') + } + } + standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat",width: 2, height: 2) { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" + } + standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat",width: 2, height: 2) { + state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" + } + + main (["power","energy"]) + details(["power","energy","refresh", "configure"]) + } +} + +def installed() { + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + + state.numberOfMeters = 3 + + if (!childDevices) { + addChildMeters(state.numberOfMeters) + } + + response(refresh()) +} + +def updated() { + response(refresh()) +} + +def ping() { + refresh() +} + +def parse(String description) { + def result = null + def cmd = zwave.parse(description) + if (cmd) { + result = createEvent(zwaveEvent(cmd)) + } + log.debug "Parse returned ${result?.descriptionText}" + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(versions) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + log.debug "Multichannel command ${cmd}" + (ep ? " from endpoint $ep" : "") + if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { // Supervision encapsulated Message + // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header + cmd.parameter = cmd.parameter.drop(2) + // Updated Command Class/Command now with the remaining bytes + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, endpoint = null) { + handleMeterReport(cmd, endpoint) +} + +private handleMeterReport(cmd, endpoint) { + def event = createMeterEventMap(cmd) + if (endpoint && endpoint > 1) { + String childDni = "${device.deviceNetworkId}:${endpoint - 1}" + def child = childDevices.find { it.deviceNetworkId == childDni } + child?.sendEvent(event) + } else { + createEvent(event) + } +} + +private createMeterEventMap(cmd) { + def eventMap = [:] + if (cmd.scale == 0) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + } else if (cmd.scale == 2) { + eventMap.name = "power" + eventMap.value = Math.round(Math.abs(cmd.scaledMeterValue)) + eventMap.unit = "W" + } + eventMap +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + // Handles all Z-Wave commands we aren't interested in + log.warn "Not handled Z-Wave command: ${cmd}" + [:] +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +def refresh() { + delayBetween([ + encap(zwave.meterV3.meterGet(scale: 0)), + encap(zwave.meterV3.meterGet(scale: 2)) + ]) +} + +def configure() { + log.debug "configure() has been called" + encap(zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: 1800)) // Report energy consumption every 30 minutes +} + +private addChildMeters(numberOfMeters) { + for (def endpoint : 1..numberOfMeters) { + try { + String childDni = "${device.deviceNetworkId}:$endpoint" + def componentLabel = device.displayName + " ${endpoint}" + addChildDevice("smartthings", "Child Energy Meter", childDni, device.getHub().getId(), [ + completedSetup : true, + label : componentLabel, + isComponent : true, + componentName : "endpointMeter$endpoint", + componentLabel : "Endpoint Meter $endpoint" + ]) + } catch (Exception e) { + log.warn "Exception: ${e}" + } + } +} + +private getMeterId(deviceNetworkId) { + def split = deviceNetworkId?.split(":") + return (split.length > 1) ? split[1] as Integer : null +} + +private childRefresh(deviceNetworkId) { + def meterId = getMeterId(deviceNetworkId) + if (switchId != null) { + sendHubCommand delayBetween([ + encap(zwave.meterV3.meterGet(scale: 0), meterId), + encap(zwave.meterV3.meterGet(scale: 2), meterId) + ]) + } +} \ No newline at end of file diff --git a/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy b/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy new file mode 100644 index 00000000000..a182b0c19a8 --- /dev/null +++ b/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy @@ -0,0 +1,50 @@ +/** + * Copyright 2020 SRPOL + * + * 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: "Child Energy Meter", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Actuator" + capability "Sensor" + capability "Health Check" + } + + tiles(scale: 2) { + valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) { + state "default", label:'${currentValue} W' + } + valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) { + state "default", label:'${currentValue} kWh' + } + standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" + } + + main(["switch"]) + details(["power","energy","refresh"]) + } +} + +def refresh() { + parent.childRefresh(device.deviceNetworkId) +} + +def ping() { + refresh() +} + +def installed() { + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [hubHardwareId: device.hub.hardwareID]) +} \ No newline at end of file From 94093140bd1d79bd930cd38a528bf3114b83b34c Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Fri, 11 Sep 2020 19:38:18 +0200 Subject: [PATCH 062/422] ICP-13360 Qubino Dimmer: Addded querying the device about meter values in basic report handling (#42729) --- devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index 8cea8b62461..6d3b34f82f3 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -332,6 +332,10 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { log.debug "BasicReport: ${cmd}" + if(isDINDimmer()) { + sendHubCommand(encap(zwave.meterV2.meterGet(scale: 0))) + sendHubCommand(encap(zwave.meterV2.meterGet(scale: 2))) + } dimmerEvents(cmd) } From 0f87987bc5e5194ab244d90dbfcbb66b1399901c Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:01:17 +0200 Subject: [PATCH 063/422] [WWST-6748] Fingerprint for Yale Fingerprint Lock YMF40 (#43641) --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 323f6dd7867..bdb80053f75 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -45,6 +45,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0101", manufacturer:"Kwikset", model:"Smartcode", deviceJoinName: "Kwikset Door Lock" //Kwikset Smartcode Lock fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0009, 0020, 0101, 0B05, FC00", outClusters: "000A, 0019", manufacturer: "Schlage", model: "BE468", deviceJoinName: "Schlage Door Lock" //Schlage Connect Smart Deadbolt fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020,0B05", outClusters: "000A,0019", manufacturer: "Yale", model: "YDD-D4F0 TSDB", deviceJoinName: "Lockwood Door Lock" //Lockwood Smart Lock + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 } tiles(scale: 2) { From b3b937b60c63963983a3ee2aee07b5b1ccfbadaf Mon Sep 17 00:00:00 2001 From: Vinay Rao <3150991+workingmonk@users.noreply.github.com> Date: Mon, 14 Sep 2020 16:06:46 -0700 Subject: [PATCH 064/422] C2C-1064 Update Ecobee notify text --- .../smartthings/ecobee-connect.src/ecobee-connect.groovy | 4 ++-- .../smartthings/ecobee-connect.src/i18n/ar-AE.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/bg-BG.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/ca-ES.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/cs-CZ.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/da-DK.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/de-DE.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/el-GR.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/en-GB.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/es-ES.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/es-MX.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/es-US.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/et-EE.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/fi-FI.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/fr-CA.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/fr-FR.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/hr-HR.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/hu-HU.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/it-IT.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/ko-KR.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/nl-NL.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/no-NO.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/pl-PL.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/pt-BR.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/pt-PT.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/ro-RO.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/ru-RU.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/sk-SK.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/sl-SI.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/sq-AL.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/sr-RS.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/sv-SE.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/th-TH.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/tr-TR.properties | 2 +- .../smartthings/ecobee-connect.src/i18n/zh-CN.properties | 2 +- 35 files changed, 36 insertions(+), 36 deletions(-) diff --git a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy index 284a1fda5dc..2a3a78cb1d9 100644 --- a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy +++ b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy @@ -599,7 +599,7 @@ def poll() { // No need to keep trying to poll if authToken is null if (!state.authToken) { log.info "poll failed due to authToken=null" - def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials." + def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials." sendPushAndFeeds(notificationMessage) markChildrenOffline(true) unschedule() @@ -958,7 +958,7 @@ def toQueryString(Map m) { boolean refreshAuthToken() { log.debug "refreshing auth token" - def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials." + def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials." def isSuccess = false if(!state.refreshToken) { diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties index 9d52078730c..555db6a9e05 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=يتعذر إنشاء الاتصال! '''Click 'Done' to return to the menu.'''=انقر فوق ”تم“ للعودة إلى القائمة. '''is connected to SmartThings'''={{deviceName}} متصل بـ SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=تم قطع اتصال {{deviceName}} بـ SmartThings، لأن بيانات اعتماد الوصول قد تغيرت أو فُقدت. يُرجى الانتقال إلى التطبيق الذكي Ecobee (Connect)‎ وإعادة إدخال بيانات اعتماد تسجيل الدخول إلى حسابك. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=تم قطع اتصال {{deviceName}} بـ SmartThings، لأن بيانات اعتماد الوصول قد تغيرت أو فُقدت. يُرجى الانتقال إلى التطبيق الذكي Ecobee (Connect)‎ وإعادة إدخال بيانات اعتماد تسجيل الدخول إلى حسابك. '''Your Ecobee thermostat '''=ثرموستات Ecobee '''Select your ecobee devices'''=تحديد أجهزة ecobee لديك '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=انقر أدناه لإضافة أجهزة الثرموستات المتوفرة في حساب ecobee لديك أو إزالتها. سيتم توصيل أجهزة الثرموستات المحددة بـ SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties b/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties index f90497fbe5c..19f0cf6bdd4 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Връзката не може да се осъществи! '''Click 'Done' to return to the menu.'''=Щракнете върху Done (Готово), за да се върнете към менюто. '''is connected to SmartThings'''=е свързан към SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=е прекъснат от SmartThings, тъй като идентификационните данни за достъп са променени или изгубени. Отидете в Ecobee (Connect) SmartApp и въведете отново идентификационните си данни за влизане в акаунта. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=е прекъснат от SmartThings, тъй като идентификационните данни за достъп са променени или изгубени. Отидете в Ecobee (Connect) SmartApp и въведете отново идентификационните си данни за влизане в акаунта. '''Your Ecobee thermostat '''=Вашият термостат Ecobee '''Select your ecobee devices'''=Избор на ecobee устройства '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Докоснете по-долу, за да добавите или премахнете термостатите, налични във вашия ecobee акаунт. Избраните термостати ще се свържат със SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties index 7b18c7b0285..e6364de3c01 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=¡No se ha podido establecer la conexión! '''Click 'Done' to return to the menu.'''=Haga clic en “Done” (Hecho) para volver al menú. '''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. '''Your Ecobee thermostat '''=Su termostato Ecobee '''Select your ecobee devices'''=Selecciona os teus dispositivos de ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toca a continuación para engadir ou eliminar termóstatos dispoñibles na túa conta de ecobee. Os termóstatos seleccionados conectaranse a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties b/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties index 139e6b24ccc..da3e40a8f84 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Připojení nelze navázat! '''Click 'Done' to return to the menu.'''=Klepnutím na tlačítko „Done“ (Hotovo) se vrátíte do menu. '''is connected to SmartThings'''=je připojen ke SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=byl odpojen od systému SmartThings, protože přístupové přihlašovací údaje byly změněny nebo ztraceny. Přejděte do Ecobee (Connect) SmartApp a znovu zadejte své přihlašovací údaje k účtu. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=byl odpojen od systému SmartThings, protože přístupové přihlašovací údaje byly změněny nebo ztraceny. Přejděte do Ecobee (Connect) SmartApp a znovu zadejte své přihlašovací údaje k účtu. '''Your Ecobee thermostat '''=Termostat Ecobee '''Select your ecobee devices'''=Vyberte zařízení ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Klepnutím na následující tlačítko přidáte nebo odeberete termostaty dostupné na účtu ecobee. Vybrané termostaty budou připojeny k aplikaci SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties b/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties index 40929b5a3c1..ab837fde6b3 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Der kunne ikke oprettes forbindelse! '''Click 'Done' to return to the menu.'''=Klik på “Done” (Udført) for at vende tilbage til menuen. '''is connected to SmartThings'''=er forbundet med SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=er koblet fra SmartThings, fordi adgangslegitimationsoplysningerne er ændret eller gået tabt. Gå til Ecobee (Connect (Forbind)) SmartApp, og indtast dine kontologinoplysninger igen. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=er koblet fra SmartThings, fordi adgangslegitimationsoplysningerne er ændret eller gået tabt. Gå til Ecobee (Connect (Forbind)) SmartApp, og indtast dine kontologinoplysninger igen. '''Your Ecobee thermostat '''=Din Ecobee-termostat '''Select your ecobee devices'''=Vælg dine ecobee-enheder '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tryk herunder for at tilføje eller fjerne termostater, der er tilgængelige på din ecobee-konto. De valgte termostater bliver forbundet til SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties index f788e199d87..a5909eef5b5 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Es konnte keine Verbindung hergestellt werden! '''Click 'Done' to return to the menu.'''=Klicken Sie auf „Done“ (OK), um zum Menü zurückzukehren. '''is connected to SmartThings'''=ist mit SmartThings verbunden -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=ist von SmartThings getrennt, da die Zugangsdaten für den Zugriff geändert wurden oder verloren gingen. Wechseln Sie zur ecobee (Connect)-SmartApp und geben Sie Ihre Kontozugangsdaten erneut ein. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=ist von SmartThings getrennt, da die Zugangsdaten für den Zugriff geändert wurden oder verloren gingen. Wechseln Sie zur ecobee (Connect)-SmartApp und geben Sie Ihre Kontozugangsdaten erneut ein. '''Your Ecobee thermostat '''=Ihr ecobee-Thermostat '''Select your ecobee devices'''=Wählen Sie Ihre ecobee-Geräte aus '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tippen Sie unten, um Thermostate hinzuzufügen oder zu entfernen, die in Ihrem ecobee-Konto verfügbar sind. Ausgewählte Thermostate werden mit SmartThings verbunden. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties index e5599a76b08..abe17d298d5 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Δεν ήταν δυνατή η δημιουργία σύνδεσης! '''Click 'Done' to return to the menu.'''=Κάντε κλικ στο "Done" (Τέλος) για να επιστρέψετε στο μενού. '''is connected to SmartThings'''=συνδέθηκε στο SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=αποσυνδέθηκε από το SmartThings, επειδή τα διαπιστευτήρια πρόσβασης άλλαξαν ή έχουν χαθεί. Μεταβείτε στην εφαρμογή Ecobee (Connect) SmartApp και καταχωρήστε ξανά τα διαπιστευτήρια σύνδεσης για το λογαριασμό σας. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=αποσυνδέθηκε από το SmartThings, επειδή τα διαπιστευτήρια πρόσβασης άλλαξαν ή έχουν χαθεί. Μεταβείτε στην εφαρμογή Ecobee (Connect) SmartApp και καταχωρήστε ξανά τα διαπιστευτήρια σύνδεσης για το λογαριασμό σας. '''Your Ecobee thermostat '''=Θερμοστάτης Ecobee '''Select your ecobee devices'''=Επιλέξτε τις συσκευές σας ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Πατήστε παρακάτω για να προσθέσετε ή να καταργήσετε τους θερμοστάτες που είναι διαθέσιμοι στο λογαριασμό σας ecobee. Οι επιλεγμένοι θερμοστάτες θα συνδεθούν στο SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties b/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties index 578e3831110..2e434f86057 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties @@ -16,5 +16,5 @@ '''The connection could not be established!'''=The connection could not be established! '''Click 'Done' to return to the menu.'''=Click ’Done’ to return to the menu. '''is connected to SmartThings'''=is connected to SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials. '''Your Ecobee thermostat '''=Your Ecobee thermostat diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties b/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties index 30b402c6698..387a54218c0 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=¡No se ha podido establecer la conexión! '''Click 'Done' to return to the menu.'''=Haga clic en “Done” (Hecho) para volver al menú. '''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. '''Your Ecobee thermostat '''=Su termostato Ecobee '''Select your ecobee devices'''=Selecciona tus dispositivos ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Pulsa a continuación para añadir o eliminar los termostatos disponibles en tu cuenta de ecobee. Los termostatos seleccionados se conectarán a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties b/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties index 4778727afab..14b3027e7f1 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=¡No se ha podido establecer la conexión! '''Click 'Done' to return to the menu.'''=Haga clic en “Done” (Hecho) para volver al menú. '''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. '''Your Ecobee thermostat '''=Su termostato Ecobee '''Select your ecobee devices'''=Seleccione sus dispositivos ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Pulse a continuación para añadir o eliminar los termostatos disponibles en su cuenta de ecobee. Los dispositivos seleccionados se conectarán a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties b/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties index 761224ed09f..bf026c87491 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties @@ -16,5 +16,5 @@ '''The connection could not be established!'''=¡No fue posible establecer la conexión! '''Click 'Done' to return to the menu.'''=Haga clic en 'Done' ('Listo') para volver al menú. '''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=no está conectado a SmartThings debido a que la credencial de acceso se cambió o se perdió. Vaya a la SmartApp de Ecobee (Connect) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=no está conectado a SmartThings debido a que la credencial de acceso se cambió o se perdió. Vaya a la SmartApp de Ecobee (Connect) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. '''Your Ecobee thermostat '''=Su termostato Ecobee diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties index c09c662bc23..6a70d1e52ac 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Ühenduse loomine nurjus! '''Click 'Done' to return to the menu.'''=Klõpsake valikut Valmis, et naasta menüüsse. '''is connected to SmartThings'''=on ühendatud teenusega SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=on teenusest SmartThings lahti ühendatud, kuna juurdepääsu volitus muutus või kadus. Avage rakendus Ecobee (Connect) SmartApp ja sisestage uuesti oma konto sisselogimisandmed. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=on teenusest SmartThings lahti ühendatud, kuna juurdepääsu volitus muutus või kadus. Avage rakendus Ecobee (Connect) SmartApp ja sisestage uuesti oma konto sisselogimisandmed. '''Your Ecobee thermostat '''=Teie Ecobee termostaat '''Select your ecobee devices'''=Valige oma ecobee seadmed '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toksake allpool, et lisada või eemaldada ecobee konto all olevaid termostaate. Valitud termostaadid ühendatakse teenusega SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties b/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties index ccf273cd528..cd9eb4b092c 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Yhteyden muodostaminen epäonnistui! '''Click 'Done' to return to the menu.'''=Palaa valikkoon napsauttamalla Done (Valmis). '''is connected to SmartThings'''=on yhdistetty SmartThingsiin -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=ei enää ole yhteydessä SmartThingsiin, sillä käyttötunnukset ovat muuttuneet tai kadonneet. Siirry Ecobee (Connect) SmartAppiin ja anna tilisi kirjautumistiedot uudelleen. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=ei enää ole yhteydessä SmartThingsiin, sillä käyttötunnukset ovat muuttuneet tai kadonneet. Siirry Ecobee (Connect) SmartAppiin ja anna tilisi kirjautumistiedot uudelleen. '''Your Ecobee thermostat '''=Ecobee-termostaattisi '''Select your ecobee devices'''=Valitse ecobee-laitteet '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Napauttamalla alla voit lisätä tai poistaa ecobee-tililläsi käytettävissä olevat termostaatit. Valitut termostaatit muodostavat yhteyden SmartThingsiin. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties b/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties index a1dd9f11883..c11220a9114 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=La connexion n'a pas pu être établie ! '''Click 'Done' to return to the menu.'''=Cliquez sur Done (Terminé) pour revenir au menu. '''is connected to SmartThings'''=est connecté à SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=est déconnecté de SmartThings, car les identifiants d'accès ont été modifiés ou perdus. Accédez à la SmartApp Ecobee (Connect) et saisissez à nouveau les informations de connexion à votre compte. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=est déconnecté de SmartThings, car les identifiants d'accès ont été modifiés ou perdus. Accédez à la SmartApp Ecobee (Connect) et saisissez à nouveau les informations de connexion à votre compte. '''Your Ecobee thermostat '''=Votre thermostat Ecobee '''Select your ecobee devices'''=Sélectionnez vos appareils ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Touchez ci-dessous pour ajouter ou retirer des thermostats disponibles dans votre compte ecobee. Les thermostats sélectionnés se connecteront à SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties index 1f29952af11..2b065d62c9e 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=La connexion n'a pas pu être établie ! '''Click 'Done' to return to the menu.'''=Cliquez sur Done (Terminé) pour revenir au menu. '''is connected to SmartThings'''=est connecté à SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=est déconnecté de SmartThings, car les identifiants d'accès ont été modifiés ou perdus. Accédez à la SmartApp Ecobee (Connect) et saisissez à nouveau les informations de connexion à votre compte. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=est déconnecté de SmartThings, car les identifiants d'accès ont été modifiés ou perdus. Accédez à la SmartApp Ecobee (Connect) et saisissez à nouveau les informations de connexion à votre compte. '''Your Ecobee thermostat '''=Votre thermostat Ecobee '''Select your ecobee devices'''=Sélectionnez vos appareils ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Appuyez ci-dessous pour ajouter ou supprimer les thermostats disponibles dans votre compte ecobee. Les thermostats sélectionnés se connecteront à SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties index 565e9864057..a65bd0e6ea8 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Veza se nije uspostavila! '''Click 'Done' to return to the menu.'''=Kliknite „Done” (Gotovo) za vraćanje na izbornik. '''is connected to SmartThings'''=povezan je s uslugom SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=nije povezan s uslugom SmartThings jer su se pristupni podaci promijenili ili izgubili. Idite na Ecobee (Connect) SmartApp i ponovno unesite podatke za prijavu na račun. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=nije povezan s uslugom SmartThings jer su se pristupni podaci promijenili ili izgubili. Idite na Ecobee (Connect) SmartApp i ponovno unesite podatke za prijavu na račun. '''Your Ecobee thermostat '''=Termostat Ecobee '''Select your ecobee devices'''=Odaberite uređaje ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Dodirnite u nastavku da biste dodali ili uklonili termostate dostupne na računu za ecobee. Odabrani termostati povezat će se s uslugom SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties b/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties index f301b38bd5a..4527593558c 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Nem sikerült kapcsolatot létesíteni! '''Click 'Done' to return to the menu.'''=A menühöz való visszatéréshez kattintson a „Done” (Kész) gombra. '''is connected to SmartThings'''=kapcsolódott a SmartThings rendszerhez -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=le lett választva a SmartThings rendszerről, mert megváltoztak vagy elvesztek a hozzáférési hitelesítő adatok. Adja meg újra a fiókja bejelentkezési hitelesítő adatait a Ecobee (Connect) SmartApp segítségével. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=le lett választva a SmartThings rendszerről, mert megváltoztak vagy elvesztek a hozzáférési hitelesítő adatok. Adja meg újra a fiókja bejelentkezési hitelesítő adatait a Ecobee (Connect) SmartApp segítségével. '''Your Ecobee thermostat '''=Az Ön Ecobee termosztátja '''Select your ecobee devices'''=Az ecobee eszközök kiválasztása '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Az alábbi lehetőség megérintésével veheti fel, illetve távolíthatja el az ecobee-fiókjában rendelkezésre álló termosztátokat. A kiválasztott eszközök csatlakozni fognak a SmartThings szolgáltatáshoz. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties b/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties index 9fc8b269626..1b7f518acd8 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Non è stato possibile stabilire la connessione. '''Click 'Done' to return to the menu.'''=Fate clic su “Done” (Fatto) per tornare al menu. '''is connected to SmartThings'''=connesso a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=disconnesso da SmartThings. Le credenziali di accesso sono state modificate o sono andate perse. Andate alla SmartApp di Ecobee (Connect) e inserite nuovamente le credenziali di accesso all'account. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=disconnesso da SmartThings. Le credenziali di accesso sono state modificate o sono andate perse. Andate alla SmartApp di Ecobee (Connect) e inserite nuovamente le credenziali di accesso all'account. '''Your Ecobee thermostat '''=Il termostato Ecobee '''Select your ecobee devices'''=Selezionate i dispositivi ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toccate di seguito per aggiungere o rimuovere i termostati disponibili nell’account ecobee. I termostati selezionati si connetteranno a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties index 19bc45b5788..0dd465aff0f 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=연결을 실행할 수 없습니다! '''Click 'Done' to return to the menu.'''=메뉴로 돌아가려면 [완료]를 클릭하세요. '''is connected to SmartThings'''=이(가) SmartThings에 연결되었습니다 -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=이(가) SmartThings에서 연결 해제되었습니다. 로그인 정보가 변경되었거나 유실되었습니다. Ecobee (연결) 스마트앱에서 계정의 로그인 정보를 다시 입력하세요. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=이(가) SmartThings에서 연결 해제되었습니다. 로그인 정보가 변경되었거나 유실되었습니다. Ecobee (연결) 스마트앱에서 계정의 로그인 정보를 다시 입력하세요. '''Your Ecobee thermostat '''=Ecobee 온도조절기 '''Select your ecobee devices'''=ecobee 기기 선택 '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=ecobee 계정에서 사용할 수 있는 온도조절기를 추가하거나 삭제하려면 아래를 누르세요. 선택된 온도조절기를 SmartThings에 연결합니다. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties b/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties index 85a2b38539a..173ef4e7e3c 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Er kan geen verbinding worden gemaakt. '''Click 'Done' to return to the menu.'''=Klik op Done (Gereed) om terug te gaan naar het menu. '''is connected to SmartThings'''=is verbonden met SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=-verbinding met SmartThings is verbroken, omdat de inloggegevens zijn gewijzigd of verloren zijn gegaan. Ga naar de Ecobee (Connect) SmartApp en voer de inloggegevens voor uw account opnieuw in. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=-verbinding met SmartThings is verbroken, omdat de inloggegevens zijn gewijzigd of verloren zijn gegaan. Ga naar de Ecobee (Connect) SmartApp en voer de inloggegevens voor uw account opnieuw in. '''Your Ecobee thermostat '''=Uw Ecobee-thermostaat '''Select your ecobee devices'''=Selecteer uw ecobee-apparaten '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tik hieronder om thermostaten die beschikbaar zijn in uw ecobee-account, toe te voegen of te verwijderen. Geselecteerde thermostaten maken verbinding met SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties b/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties index 53ea6bb3a55..b38993c2ba0 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Kunne ikke opprette tilkoblingen! '''Click 'Done' to return to the menu.'''=Klikk på Done (Ferdig) for å gå tilbake til menyen. '''is connected to SmartThings'''=er koblet til SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=er koblet fra SmartThings fordi tilgangsinformasjonen ble endret eller mistet. Gå til Ecobee (Connect) SmartApp, og angi påloggingsinformasjonen for kontoen på nytt. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=er koblet fra SmartThings fordi tilgangsinformasjonen ble endret eller mistet. Gå til Ecobee (Connect) SmartApp, og angi påloggingsinformasjonen for kontoen på nytt. '''Your Ecobee thermostat '''=Ecobee-termostaten '''Select your ecobee devices'''=Velg ecobee-enhetene dine '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Trykk nedenfor for å legge til eller fjerne termostater som er tilgjengelige i ecobee-kontoen din. Valgte termostater blir koblet til SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties b/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties index be9eddb0dcd..b5c48ef48ac 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Nie można ustanowić połączenia. '''Click 'Done' to return to the menu.'''=Kliknij opcję „Done” (Gotowe), aby powrócić do menu. '''is connected to SmartThings'''=jest połączony ze SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=jest odłączony od SmartThings, ponieważ poświadczenie dostępu zostało zmienione lub utracone. Przejdź do aplikacji Ecobee (Connect) SmartApp i wprowadź ponownie poświadczenia logowania konta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=jest odłączony od SmartThings, ponieważ poświadczenie dostępu zostało zmienione lub utracone. Przejdź do aplikacji Ecobee (Connect) SmartApp i wprowadź ponownie poświadczenia logowania konta. '''Your Ecobee thermostat '''=Termostat Ecobee '''Select your ecobee devices'''=Wybierz urządzenia ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Dotknij poniżej, aby dodać lub usunąć termostaty dostępne na koncie ecobee. Wybrane termostaty zostaną połączone ze SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties index 5005e056077..cedf11f9b31 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Não foi possível estabelecer a conexão! '''Click 'Done' to return to the menu.'''=Clique em 'Done' (Concluído) para retornar ao menu. '''is connected to SmartThings'''=está conectado ao SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=foi desconectado do SmartThings, pois a credencial de acesso foi alterada ou perdida. Vá para Ecobee (Connect) SmartApp e insira novamente suas credenciais de acesso à conta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=foi desconectado do SmartThings, pois a credencial de acesso foi alterada ou perdida. Vá para Ecobee (Connect) SmartApp e insira novamente suas credenciais de acesso à conta. '''Your Ecobee thermostat '''=Seu termostato Ecobee '''Select your ecobee devices'''=Selecionar seus aparelhos ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toque abaixo para adicionar ou remover os termostatos disponíveis na sua conta ecobee. Os termostatos selecionados serão conectados ao SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties b/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties index ca8626b1583..d70f944e1e2 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Não foi possível estabelecer a ligação! '''Click 'Done' to return to the menu.'''=Clique em "Done" (Concluir) para regressar ao menu. '''is connected to SmartThings'''=está ligado ao SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=foi desligado do SmartThings, porque a credencial de acesso foi alterada ou perdida. Vá para Ecobee (Connect) SmartApp e introduza novamente as suas credenciais de início de sessão na conta. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=foi desligado do SmartThings, porque a credencial de acesso foi alterada ou perdida. Vá para Ecobee (Connect) SmartApp e introduza novamente as suas credenciais de início de sessão na conta. '''Your Ecobee thermostat '''=O seu termóstato Ecobee '''Select your ecobee devices'''=Seleccionar os seus dispositivos ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toque abaixo para adicionar ou remover termóstatos disponíveis na sua conta ecobee. Os termóstatos seleccionados irão ligar-se ao SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties index c115c1be832..d552f704983 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Nu a putut fi stabilită conexiunea! '''Click 'Done' to return to the menu.'''=Faceți clic pe „Done” (Efectuat) pentru a reveni la meniu. '''is connected to SmartThings'''=este conectat la SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=este deconectat de la SmartThings, deoarece acreditările de acces au fost schimbate sau pierdute. Accesați aplicația Ecobee (Connect) SmartApp și reintroduceți acreditările de conectare la cont. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=este deconectat de la SmartThings, deoarece acreditările de acces au fost schimbate sau pierdute. Accesați aplicația Ecobee (Connect) SmartApp și reintroduceți acreditările de conectare la cont. '''Your Ecobee thermostat '''=Termostatul dvs. Ecobee '''Select your ecobee devices'''=Selectați dispozitivele dvs. ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Atingeți mai jos pentru a adăuga sau elimina termostate disponibile în contul dvs. ecobee. Termostatele selectate se vor conecta la SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties index a076a18d699..bfa4b083b0e 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Не удалось установить соединение! '''Click 'Done' to return to the menu.'''=Чтобы вернуться в меню, нажмите «Готово». '''is connected to SmartThings'''=подключено к SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=отключено от SmartThings, поскольку данные для доступа были изменены или потеряны. Перейдите в Ecobee (Подключить) SmartApp и повторно введите регистрационные данные своей учетной записи. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=отключено от SmartThings, поскольку данные для доступа были изменены или потеряны. Перейдите в Ecobee (Подключить) SmartApp и повторно введите регистрационные данные своей учетной записи. '''Your Ecobee thermostat '''=Ваш термостат Ecobee '''Select your ecobee devices'''=Выберите свои устройства ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Чтобы добавить или удалить доступные термостаты в учетной записи ecobee, коснитесь ниже. Выбранные термостаты будут подключены к SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties index 99f1060e66b..e0e9fb1ced3 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Nepodarilo sa nadviazať spojenie. '''Click 'Done' to return to the menu.'''=Kliknutím na tlačidlo Done (Hotovo) sa vráťte do menu. '''is connected to SmartThings'''=je pripojený k systému SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=je odpojený od systému SmartThings, pretože prístupové poverenia boli zmenené alebo stratené. Prejdite do aplikácie Ecobee (Connect) SmartApp a znova zadajte prihlasovacie poverenia pre konto. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=je odpojený od systému SmartThings, pretože prístupové poverenia boli zmenené alebo stratené. Prejdite do aplikácie Ecobee (Connect) SmartApp a znova zadajte prihlasovacie poverenia pre konto. '''Your Ecobee thermostat '''=Váš termostat Ecobee '''Select your ecobee devices'''=Vyberte svoje zariadenia ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Ťuknutím nižšie môžete pridať alebo odstrániť termostaty dostupné vo vašom konte ecobee. Vybraté termostaty sa pripoja k systému SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties index 6e1004ad402..c4b71c54066 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Povezave ni bilo mogoče vzpostaviti! '''Click 'Done' to return to the menu.'''=Kliknite »Done« (Končano), da se vrnete v meni. '''is connected to SmartThings'''=je povezan s storitvijo SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=ni povezan s storitvijo SmartThings, ker so bile poverilnice za dostop spremenjene ali izgubljene. Pojdite v aplikacijo Ecobee (Connect) SmartApp in znova vnesite poverilnice za prijavo v račun. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=ni povezan s storitvijo SmartThings, ker so bile poverilnice za dostop spremenjene ali izgubljene. Pojdite v aplikacijo Ecobee (Connect) SmartApp in znova vnesite poverilnice za prijavo v račun. '''Your Ecobee thermostat '''=Vaš termostat Ecobee '''Select your ecobee devices'''=Izberite naprave ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Pritisnite spodaj, da dodate ali odstranite termostate, ki so na voljo v vašem računu ecobee. Izbrani termostati se bodo povezali s storitvijo SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties index 5dc3421e195..d92f42c06bf 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Lidhja nuk u vendos dot! '''Click 'Done' to return to the menu.'''=Kliko mbi ‘Done’ (U krye) për t’u kthyer në meny. '''is connected to SmartThings'''=është lidhur me SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=është shkëputur nga SmartThings, sepse kredenciali i aksesit ka ndryshuar ose ka humbur. Shko te Ecobee (Connect) SmartApp dhe futi sërish kredencialet e logimit në llogari. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=është shkëputur nga SmartThings, sepse kredenciali i aksesit ka ndryshuar ose ka humbur. Shko te Ecobee (Connect) SmartApp dhe futi sërish kredencialet e logimit në llogari. '''Your Ecobee thermostat '''=Termostati yt Ecobee '''Select your ecobee devices'''=Përzgjidh pajisjet e tua ecobee '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Trokit më poshtë për të shtuar ose hequr termostate që gjenden në llogarinë tënde ecobee. Termostatet e përzgjedhura do të lidhen me SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties index c2f13d6ae0d..2ed4545e50c 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Veza nije uspostavljena! '''Click 'Done' to return to the menu.'''=Kliknite na „Done” (Gotovo) da biste se vratili na meni. '''is connected to SmartThings'''=je povezan na SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=je prekinuo vezu sa aplikacijom SmartThings zato što su akreditivi za pristup promenjeni ili izgubljeni. Idite na aplikaciju Ecobee (Connect) SmartApp i ponovo unesite akreditive za prijavljivanje na nalog. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=je prekinuo vezu sa aplikacijom SmartThings zato što su akreditivi za pristup promenjeni ili izgubljeni. Idite na aplikaciju Ecobee (Connect) SmartApp i ponovo unesite akreditive za prijavljivanje na nalog. '''Your Ecobee thermostat '''=Vaš Ecobee termostat '''Select your ecobee devices'''=Izaberite ecobee uređaje '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Kucnite ispod da biste dodali ili uklonili dostupne termostate na ecobee nalogu. Izabrani termostati će se povezati na SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties index 67081695813..ea957914439 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Det gick inte att upprätta anslutningen! '''Click 'Done' to return to the menu.'''=Klicka på Done (Klart) för att återgå till menyn. '''is connected to SmartThings'''=är ansluten till SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=är frånkopplad från SmartThings, eftersom inloggningsuppgifterna har ändrats eller gått förlorade. Starta Ecobee (Connect) SmartApp och ange kontots inloggningsuppgifter igen. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=är frånkopplad från SmartThings, eftersom inloggningsuppgifterna har ändrats eller gått förlorade. Starta Ecobee (Connect) SmartApp och ange kontots inloggningsuppgifter igen. '''Your Ecobee thermostat '''=Din Ecobee-termostat '''Select your ecobee devices'''=Välj dina ecobee-enheter '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tryck nedan om du vill lägga till eller ta bort termostater som är tillgängliga i ditt ecobee-konto. De valda termostaterna ansluter till SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties b/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties index 068b69896c7..57a2f36f35b 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=ไม่สามารถสร้างการเชื่อมต่อได้! '''Click 'Done' to return to the menu.'''=คลิก 'เสร็จสิ้น' เพื่อกลับไปยังเมนู '''is connected to SmartThings'''={{deviceName}} เชื่อมต่อกับ SmartThings แล้ว -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''={{deviceName}} ถูกตัดการเชื่อมต่อจาก SmartThings เนื่องจากข้อมูลการเข้าถึงถูกเปลี่ยนแปลงหรือหายไป กรุณาไปที่ Ecobee (การเชื่อมต่อ) SmartApp และใส่ข้อมูลยืนยันตัวตนการเข้าสู่บัญชีผู้ใช้ของคุณอีกครั้ง +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''={{deviceName}} ถูกตัดการเชื่อมต่อจาก SmartThings เนื่องจากข้อมูลการเข้าถึงถูกเปลี่ยนแปลงหรือหายไป กรุณาไปที่ Ecobee (การเชื่อมต่อ) SmartApp และใส่ข้อมูลยืนยันตัวตนการเข้าสู่บัญชีผู้ใช้ของคุณอีกครั้ง '''Your Ecobee thermostat '''=ตัวควบคุมอุณหภูมิ Ecobee ของคุณ '''Select your ecobee devices'''=เลือกอุปกรณ์ ecobee ของคุณ '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=แตะด้านล่างเพื่อเพิ่มหรือลบตัวควบคุมอุณหภูมิที่พร้อมใช้งานในบัญชี ecobee ของคุณ ตัวควบคุมอุณหภูมิที่เลือกจะเชื่อมต่อกับ SmartThings diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties index c5b80f32dd6..59f4e905ff3 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=Bağlantı kurulamadı! '''Click 'Done' to return to the menu.'''=Menüye dönmek için 'Bitti' öğesine tıklayın. '''is connected to SmartThings'''={{cihazİsmi}} SmartThings'e bağlandı -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=Erişim kimlik doğruları değiştirildiğinden veya kaybolduğundan {{cihazİsmi}} ile SmartThings arasındaki bağlantı kesildi. Lütfen Ecobee (Connect) SmartApp'e gidin ve hesabınızın oturum açma kimlik bilgilerini tekrar girin. +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=Erişim kimlik doğruları değiştirildiğinden veya kaybolduğundan {{cihazİsmi}} ile SmartThings arasındaki bağlantı kesildi. Lütfen Ecobee (Connect) SmartApp'e gidin ve hesabınızın oturum açma kimlik bilgilerini tekrar girin. '''Your Ecobee thermostat '''=Ecobee termostatınız '''Select your ecobee devices'''=ecobee Cihazlarınızı seçin '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=ecobee hesabınızdaki kullanılabilir termostatları eklemek veya kaldırmak için aşağıya dokunun. Seçilen termostatlar SmartThings'e bağlanır. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties b/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties index 9694b09b9de..a927eb82b41 100644 --- a/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties +++ b/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties @@ -16,7 +16,7 @@ '''The connection could not be established!'''=无法建立连接! '''Click 'Done' to return to the menu.'''=单击“完成”返回菜单。 '''is connected to SmartThings'''=已连接至 SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) SmartApp and re-enter your account login credentials.'''=已从 SmartThings 断开,因为访问凭据已更改或丢失。请转到 Ecobee (连接) SmartApp,然后重新输入您的帐户登录凭据。 +'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=已从 SmartThings 断开,因为访问凭据已更改或丢失。请转到 Ecobee (连接) SmartApp,然后重新输入您的帐户登录凭据。 '''Your Ecobee thermostat '''=您的 Ecobee 恒温器 '''Select your ecobee devices'''=选择您的 ecobee 设备 '''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=点击下方可添加或删除 ecobee 帐户中的恒温器。选定的恒温器将连接到 SmartThings。 From 1d7f3cbb1807b4fbfd8022f66805cea596ed6e86 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 15 Sep 2020 10:01:44 -0700 Subject: [PATCH 065/422] CHAD-5382 Prepare Z-Wave Battery Thermostat for Local Execution (#43642) * CHAD-5382 Prepare Z-Wave Battery Thermostat for Local Execution Adds the Humidity Measurement capability that was being populated but not declared Sets the hubcore version required for local thermostat execution. Removes unused "currentSetpoint" attribute that was cruft from battery-less DTH --- .../zwave-battery-thermostat.groovy | 103 +++++++----------- 1 file changed, 42 insertions(+), 61 deletions(-) diff --git a/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy b/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy index 8b3c57747df..b800ff6684b 100644 --- a/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy +++ b/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy @@ -12,7 +12,9 @@ * */ metadata { - definition (name: "Z-Wave Battery Thermostat", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.thermostat", genericHandler: "Z-Wave") { + definition (name: "Z-Wave Battery Thermostat", namespace: "smartthings", author: "SmartThings", + ocfDeviceType: "oic.d.thermostat", genericHandler: "Z-Wave", runLocally: true, + executeCommandsLocally: false, minHubCoreVersion: '000.033.0001') { capability "Actuator" capability "Temperature Measurement" capability "Thermostat Heating Setpoint" @@ -20,12 +22,13 @@ metadata { capability "Thermostat Operating State" capability "Thermostat Mode" capability "Thermostat Fan Mode" + capability "Relative Humidity Measurement" capability "Configuration" capability "Refresh" capability "Sensor" capability "Health Check" capability "Battery" - + attribute "thermostatFanState", "string" command "switchMode" @@ -115,9 +118,9 @@ metadata { def installed() { // Configure device - def cmds = [new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())] + def cmds = [zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId])] sendHubCommand(cmds) - runIn(3, "initialize", [overwrite: true]) // Allow configure command to be sent and acknowledged before proceeding + runIn(3, "initialize", [overwrite: true, forceForLocallyExecuting: true]) // Allow configure command to be sent and acknowledged before proceeding } def updated() { @@ -128,11 +131,14 @@ def initialize() { // Device-Watch simply pings if no device events received for 24hrs sendEvent(name: "checkInterval", value: 60 * 60 * 24, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) unschedule() + sendHubCommand([ + zwave.thermostatModeV2.thermostatModeSupportedGet(), + zwave.thermostatFanModeV3.thermostatFanModeSupportedGet() + ]) pollDevice() } def configure() { - def cmds = [] /* Configuration of reporting values. Bitmask based on: 1 TEMPERATURE (CC_SENSOR_MULTILEVEL) @@ -150,7 +156,7 @@ def configure() { 16384 MECH STATUS 32768 SCP STATUS */ - cmds << zwave.configurationV1.configurationSet(parameterNumber: 23, size: 2, scaledConfigurationValue: 8319).format() + response(zwave.configurationV1.configurationSet(parameterNumber: 23, size: 2, scaledConfigurationValue: 8319)) } def parse(String description) @@ -180,11 +186,9 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpo switch (cmd.setpointType) { case 1: sendEvent(name: "heatingSetpoint", value: setpoint, unit: unit, displayed: false) - updateThermostatSetpoint("heatingSetpoint", setpoint) break; case 2: sendEvent(name: "coolingSetpoint", value: setpoint, unit: unit, displayed: false) - updateThermostatSetpoint("coolingSetpoint", setpoint) break; default: log.debug "unknown setpointType $cmd.setpointType" @@ -204,7 +208,6 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelR map.value = getTempInLocalScale(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C") map.unit = getTemperatureScale() map.name = "temperature" - updateThermostatSetpoint(null, null) } else if (cmd.sensorType == 5) { map.value = cmd.scaledSensorValue map.unit = "%" @@ -239,7 +242,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev1.Thermosta break } // Makes sure we have the correct thermostat mode - sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())) + sendHubCommand(zwave.thermostatModeV2.thermostatModeGet()) createEvent(map) } @@ -260,7 +263,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanSt } def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) { - def map = [name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes]] + def map = [name: "thermostatMode"] switch (cmd.mode) { case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF: map.value = "off" @@ -278,12 +281,11 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeRepor map.value = "auto" break } - updateThermostatSetpoint(null, null) createEvent(map) } def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) { - def map = [name: "thermostatFanMode", data:[supportedThermostatFanModes: state.supportedFanModes]] + def map = [name: "thermostatFanMode"] switch (cmd.fanMode) { case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW: map.value = "auto" @@ -349,21 +351,20 @@ def refresh() { if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) { state.refreshTriggeredAt = timeNow // use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved - runIn(2, "pollDevice", [overwrite: true]) + runIn(2, "pollDevice", [overwrite: true, forceForLocallyExecuting: true]) } } def pollDevice() { def cmds = [] - cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()) - cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format()) - cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format()) - cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format()) - cmds << new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format()) // current temperature - cmds << new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()) - cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()) - cmds << new physicalgraph.device.HubAction(zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()) - cmds << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format()) + cmds << zwave.thermostatModeV2.thermostatModeGet() + cmds << zwave.thermostatFanModeV3.thermostatFanModeGet() + cmds << zwave.sensorMultilevelV2.sensorMultilevelGet(sensorType: 1) // current temperature + cmds << zwave.sensorMultilevelV2.sensorMultilevelGet(sensorType: 5) // current relative humidity + cmds << zwave.thermostatOperatingStateV1.thermostatOperatingStateGet() + cmds << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1) + cmds << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2) + cmds << zwave.batteryV1.batteryGet() sendHubCommand(cmds, 1200) } @@ -405,11 +406,11 @@ def alterSetpoint(raise, setpoint) { unit: getTemperatureScale(), eventType: "ENTITY_UPDATE", displayed: false) } if (data.targetHeatingSetpoint && data.targetCoolingSetpoint) { - runIn(5, "updateHeatingSetpoint", [data: data, overwrite: true]) + runIn(5, "updateHeatingSetpoint", [data: data, overwrite: true, forceForLocallyExecuting: true]) } else if (setpoint == "heatingSetpoint" && data.targetHeatingSetpoint) { - runIn(5, "updateHeatingSetpoint", [data: data, overwrite: true]) + runIn(5, "updateHeatingSetpoint", [data: data, overwrite: true, forceForLocallyExecuting: true]) } else if (setpoint == "coolingSetpoint" && data.targetCoolingSetpoint) { - runIn(5, "updateCoolingSetpoint", [data: data, overwrite: true]) + runIn(5, "updateCoolingSetpoint", [data: data, overwrite: true, forceForLocallyExecuting: true]) } } @@ -422,7 +423,7 @@ def updateCoolingSetpoint(data) { } def enforceSetpointLimits(setpoint, data) { - def locationScale = getTemperatureScale() + def locationScale = getTemperatureScale() def minSetpoint = (setpoint == "heatingSetpoint") ? getTempInDeviceScale(40, "F") : getTempInDeviceScale(50, "F") def maxSetpoint = (setpoint == "heatingSetpoint") ? getTempInDeviceScale(90, "F") : getTempInDeviceScale(99, "F") def deadband = (state.scale == 1) ? 3 : 2 // 3°F, 2°C @@ -437,7 +438,7 @@ def enforceSetpointLimits(setpoint, data) { } // Enforce 3 degrees F deadband between setpoints if (setpoint == "heatingSetpoint") { - heatingSetpoint = targetValue + heatingSetpoint = targetValue coolingSetpoint = (heatingSetpoint + deadband > getTempInDeviceScale(data.coolingSetpoint, locationScale)) ? heatingSetpoint + deadband : null } if (setpoint == "coolingSetpoint") { @@ -450,14 +451,14 @@ def enforceSetpointLimits(setpoint, data) { def setHeatingSetpoint(degrees) { if (degrees) { state.heatingSetpoint = degrees.toDouble() - runIn(2, "updateSetpoints", [overwrite: true]) + runIn(2, "updateSetpoints", [overwrite: true, forceForLocallyExecuting: true]) } } def setCoolingSetpoint(degrees) { if (degrees) { state.coolingSetpoint = degrees.toDouble() - runIn(2, "updateSetpoints", [overwrite: true]) + runIn(2, "updateSetpoints", [overwrite: true, forceForLocallyExecuting: true]) } } @@ -497,33 +498,13 @@ def updateSetpoints(data) { sendHubCommand(cmds, 1000) } -// thermostatSetpoint is not displayed by any tile as it can't be predictable calculated due to -// the device's quirkiness but it is defined by the capability so it must be set, set it to the most likely value -def updateThermostatSetpoint(setpoint, value) { - def scale = getTemperatureScale() - def heatingSetpoint = (setpoint == "heatingSetpoint") ? value : getTempInLocalScale("heatingSetpoint") - def coolingSetpoint = (setpoint == "coolingSetpoint") ? value : getTempInLocalScale("coolingSetpoint") - def mode = device.currentValue("thermostatMode") - def thermostatSetpoint = heatingSetpoint // corresponds to (mode == "heat" || mode == "emergency heat") - if (mode == "cool") { - thermostatSetpoint = coolingSetpoint - } else if (mode == "auto" || mode == "off") { - // Set thermostatSetpoint to the setpoint closest to the current temperature - def currentTemperature = getTempInLocalScale("temperature") - if (currentTemperature > (heatingSetpoint + coolingSetpoint)/2) { - thermostatSetpoint = coolingSetpoint - } - } - sendEvent(name: "thermostatSetpoint", value: thermostatSetpoint, unit: getTemperatureScale()) -} - /** * PING is used by Device-Watch in attempt to reach the Device * */ def ping() { log.debug "ping() called" // Just get Operating State there's no need to flood more commands - sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format())) + sendHubCommand(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet()) } def switchMode() { @@ -533,7 +514,7 @@ def switchMode() { if (supportedModes && supportedModes.size() && supportedModes[0].size() > 1) { def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] } def nextMode = next(currentMode) - runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true]) + runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true, forceForLocallyExecuting: true]) } else { log.warn "supportedModes not defined" getSupportedModes() @@ -545,7 +526,7 @@ def switchToMode(nextMode) { // Old version of supportedModes was as string, make sure it gets updated if (supportedModes && supportedModes.size() && supportedModes[0].size() > 1) { if (supportedModes.contains(nextMode)) { - runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true]) + runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true, forceForLocallyExecuting: true]) } else { log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}") } @@ -557,7 +538,7 @@ def switchToMode(nextMode) { def getSupportedModes() { def cmds = [] - cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format()) + cmds << zwave.thermostatModeV2.thermostatModeSupportedGet() sendHubCommand(cmds) } @@ -568,7 +549,7 @@ def switchFanMode() { if (supportedFanModes && supportedFanModes.size() && supportedFanModes[0].size() > 1) { def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] } def nextMode = next(currentMode) - runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true]) + runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true, forceForLocallyExecuting: true]) } else { log.warn "supportedFanModes not defined" getSupportedFanModes() @@ -580,7 +561,7 @@ def switchToFanMode(nextMode) { // Old version of supportedFanModes was as string, make sure it gets updated if (supportedFanModes && supportedFanModes.size() && supportedFanModes[0].size() > 1) { if (supportedFanModes.contains(nextMode)) { - runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true]) + runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true, forceForLocallyExecuting: true]) } else { log.debug("FanMode $nextMode is not supported by ${device.displayName}") } @@ -591,7 +572,7 @@ def switchToFanMode(nextMode) { } def getSupportedFanModes() { - def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())] + def cmds = [zwave.thermostatFanModeV3.thermostatFanModeSupportedGet()] sendHubCommand(cmds) } @@ -608,8 +589,8 @@ def setThermostatMode(String value) { } def setGetThermostatMode(data) { - def cmds = [new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format()), - new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())] + def cmds = [zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]), + zwave.thermostatModeV2.thermostatModeGet()] sendHubCommand(cmds) } @@ -624,8 +605,8 @@ def setThermostatFanMode(String value) { } def setGetThermostatFanMode(data) { - def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format()), - new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())] + def cmds = [zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]), + zwave.thermostatFanModeV3.thermostatFanModeGet()] sendHubCommand(cmds) } From ff3e4e7aea2f9810c37281bb0affe8deecd1d47f Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Tue, 15 Sep 2020 19:03:25 +0200 Subject: [PATCH 066/422] [ICP-13584; ICP-13551] Fingerprint change for Somfy Glydea, pause() method tweak (#43795) --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 0ec1b5207dc..ea37642a2e6 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -33,7 +33,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.8", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "KG0001", deviceJoinName: "Window Treatment" //Smart Curtain Motor(BCM300D) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "DY0010", deviceJoinName: "Window Treatment" //Smart Curtain Motor(DT82TV) - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Somfy", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Curtain", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack } @@ -191,7 +191,13 @@ def setLevel(data, rate = null) { def pause() { log.info "pause()" - zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) + def currentShadeStatus = device.currentValue("windowShade") + + if (currentShadeStatus == "open" || currentShadeStatus == "closed") { + sendEvent(name: "windowShade", value: currentShadeStatus) + } else { + zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) + } } def presetPosition() { From 29ed65bf0761d8ef85be6ab2e472cc7c31d9ee85 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 15 Sep 2020 19:56:06 +0200 Subject: [PATCH 067/422] WWST-6945 - support for Somfy Situo 4 Zigbee Pure (#43628) * WWST-6945 - support for Somfy Situo 4 Zigbee Pure --- .../ikea-button.src/ikea-button.groovy | 79 +++++++++++++++---- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index 56665bc1a2a..756465aecdd 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -31,6 +31,7 @@ metadata { fingerprint inClusters: "0000, 0001, 0003, 0009, 0102, 1000, FC7C", outClusters: "0003, 0004, 0006, 0008, 0019, 0102, 1000", manufacturer:"IKEA of Sweden", model: "TRADFRI on/off switch", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_On/Off_Switch" //IKEA TRÅDFRI On/Off switch fingerprint manufacturer: "IKEA of Sweden", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote fingerprint manufacturer: "KE", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote + fingerprint manufacturer: "SOMFY", model: "Situo 4 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 } tiles { @@ -70,6 +71,13 @@ private getOPENCLOSE_BUTTONS() { DOWN:2] } +private getOPENCLOSESTOP_BUTTONS() { + [UP:1, + STOP:2, + DOWN:3 + ] +} + private channelNumber(String dni) { dni.split(":")[-1] as Integer } @@ -90,13 +98,21 @@ private getIkeaOnOffSwitchNames() { ] } -private getIkeaOpenCloseRemoteNames() { +private getOpenCloseRemoteNames() { [ "Up", // Up button "Down" // Down button ] } +private getOpenCloseStopRemoteNames() { + [ + "Up", // Up button + "Stop", // Stop button + "Down" // Down button + ] +} + private getButtonLabel(buttonNum) { def label = "Button ${buttonNum}" @@ -105,7 +121,9 @@ private getButtonLabel(buttonNum) { } else if (isIkeaOnOffSwitch()) { label = ikeaOnOffSwitchNames[buttonNum - 1] } else if (isIkeaOpenCloseRemote()) { - label = ikeaOpenCloseRemoteNames[buttonNum - 1] + label = openCloseRemoteNames[buttonNum - 1] + } else if (isSomfySituo()) { + label = openCloseStopRemoteNames[buttonNum - 1] } return label @@ -122,7 +140,7 @@ private void createChildButtonDevices(numberOfButtons) { for (i in 1..numberOfButtons) { log.debug "Creating child $i" - def supportedButtons = ((isIkeaRemoteControl() && i == REMOTE_BUTTONS.MIDDLE) || isIkeaOpenCloseRemote()) ? ["pushed"] : ["pushed", "held"] + def supportedButtons = ((isIkeaRemoteControl() && i == REMOTE_BUTTONS.MIDDLE) || isIkeaOpenCloseRemote() || isSomfySituo()) ? ["pushed"] : ["pushed", "held"] def child = addChildDevice("Child Button", "${device.deviceNetworkId}:${i}", device.hubId, [completedSetup: true, label: getButtonName(i), isComponent: true, componentName: "button$i", componentLabel: getButtonLabel(i)]) @@ -140,13 +158,15 @@ def installed() { numberOfButtons = 5 } else if (isIkeaOnOffSwitch() || isIkeaOpenCloseRemote()) { numberOfButtons = 2 + } else if (isSomfySituo()) { + numberOfButtons = 3 } if (numberOfButtons > 1) { createChildButtonDevices(numberOfButtons) } - def supportedButtons = isIkeaOpenCloseRemote() ? ["pushed"] : ["pushed", "held"] + def supportedButtons = isIkeaOpenCloseRemote() || isSomfySituo() ? ["pushed"] : ["pushed", "held"] sendEvent(name: "supportedButtonValues", value: supportedButtons.encodeAsJSON(), displayed: false) sendEvent(name: "numberOfButtons", value: numberOfButtons, displayed: false) numberOfButtons.times { @@ -170,11 +190,19 @@ def updated() { def configure() { log.debug "Configuring device ${device.getDataValue("model")}" - def cmds = zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, DataType.UINT8, 30, 21600, 0x01) + - zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21) + - zigbee.addBinding(zigbee.ONOFF_CLUSTER) + - readDeviceBindingTable() // Need to read the binding table to see what group it's using + def cmds = [] + + if (isSomfySituo()) { + cmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, ["destEndpoint":0xE8]) + + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, DataType.UINT8, 30, 21600, 0x01, ["destEndpoint":0xE8]) + + zigbee.addBinding(CLUSTER_WINDOW_COVERING) + } else { + cmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21) + + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, DataType.UINT8, 30, 21600, 0x01) + + zigbee.addBinding(zigbee.ONOFF_CLUSTER) + } + cmds += readDeviceBindingTable() // Need to read the binding table to see what group it's using cmds } @@ -188,7 +216,11 @@ def parse(String description) { if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) { def descMap = zigbee.parseDescriptionAsMap(description) if (descMap.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.attrInt == 0x0021) { - event = getBatteryEvent(zigbee.convertHexToInt(descMap.value)) + def batteryValue = zigbee.convertHexToInt(descMap.value) + if (!isIkea()) { + batteryValue = batteryValue / 2 + } + event = getBatteryEvent(batteryValue) } else if (descMap.clusterInt == CLUSTER_SCENES || descMap.clusterInt == zigbee.ONOFF_CLUSTER || descMap.clusterInt == zigbee.LEVEL_CONTROL_CLUSTER || @@ -252,11 +284,11 @@ private Map getButtonEvent(Map descMap) { 0x07: { [state: "", buttonNumber: 0] }], (CLUSTER_SCENES): [0x07: { it == "00" - ? [state: "pushed", buttonNumber: REMOTE_BUTTONS.RIGHT] - : [state: "pushed", buttonNumber: REMOTE_BUTTONS.LEFT] }, + ? [state: "pushed", buttonNumber: REMOTE_BUTTONS.RIGHT] + : [state: "pushed", buttonNumber: REMOTE_BUTTONS.LEFT] }, 0x08: { it == "00" - ? [state: "held", buttonNumber: REMOTE_BUTTONS.RIGHT] - : [state: "held", buttonNumber: REMOTE_BUTTONS.LEFT] }, + ? [state: "held", buttonNumber: REMOTE_BUTTONS.RIGHT] + : [state: "held", buttonNumber: REMOTE_BUTTONS.LEFT] }, 0x09: { [state: "", buttonNumber: 0] }] ] @@ -293,6 +325,17 @@ private Map getButtonEvent(Map descMap) { buttonNumber = OPENCLOSE_BUTTONS.DOWN } } + } else if (isSomfySituo()){ + if (descMap.clusterInt == CLUSTER_WINDOW_COVERING) { + buttonState = "pushed" + if (descMap.commandInt == 0x00) { + buttonNumber = OPENCLOSESTOP_BUTTONS.UP + } else if (descMap.commandInt == 0x01) { + buttonNumber = OPENCLOSESTOP_BUTTONS.DOWN + } else if (descMap.commandInt == 0x02) { + buttonNumber = OPENCLOSESTOP_BUTTONS.STOP + } + } } if (buttonNumber != 0) { @@ -318,6 +361,14 @@ private boolean isIkeaOpenCloseRemote() { device.getDataValue("model") == "TRADFRI open/close remote" } +private boolean isIkea() { + isIkeaRemoteControl() || isIkeaOnOffSwitch() || isIkeaOpenCloseRemote() +} + +private boolean isSomfySituo() { + device.getDataValue("model") == "Situo 4 Zigbee" +} + private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) @@ -339,4 +390,4 @@ private List addHubToGroup(Integer groupAddr) { private List readDeviceBindingTable() { ["zdo mgmt-bind 0x${device.deviceNetworkId} 0", "delay 200"] -} +} \ No newline at end of file From 6857c6ec4a19033d41865a05a98faea5d5de9798 Mon Sep 17 00:00:00 2001 From: ZWozniakS <48519140+ZWozniakS@users.noreply.github.com> Date: Wed, 16 Sep 2020 20:16:39 +0200 Subject: [PATCH 068/422] [ICP-13278] Changed minimum dimmer level range in settings for Aeotec Nano Dimmer (#43964) Co-authored-by: Zuzanna Wozniak/Home IoT Development (IoT) /SRPOL/Engineer/Samsung Electronics --- .../zwave-metering-dimmer.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index 945ab48af3c..74ad008234b 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -107,8 +107,8 @@ metadata { description: "This may need to be adjusted for bulbs that are not dimming properly.", name: "minDimmingLevel", type: "number", - range: "0..99", - defaultValue: 0 + range: "1..99", + defaultValue: 1 ) } } @@ -304,10 +304,10 @@ def normalizeLevel(level) { def getAeotecNanoDimmerConfigurationCommands() { def result = [] - Integer minDimmingLevel = (settings.minDimmingLevel as Integer) ?: 0 // default value (parameter 131) for Aeotec Nano Dimmer + Integer minDimmingLevel = (settings.minDimmingLevel as Integer) ?: 1 // default value (parameter 131) for Aeotec Nano Dimmer if (!state.minDimmingLevel) { - state.minDimmingLevel = 0 // default value (parameter 131) for Aeotec Nano Dimmer + state.minDimmingLevel = 1 // default value (parameter 131) for Aeotec Nano Dimmer } if (!state.configured || (minDimmingLevel != state.minDimmingLevel)) { From 6d3318000ed7959d44f968210268f05d7bc72f95 Mon Sep 17 00:00:00 2001 From: ADUROSMART ERIA <52692745+adurosmart@users.noreply.github.com> Date: Fri, 18 Sep 2020 04:21:57 +0800 Subject: [PATCH 069/422] update smartsense-motion-sensor.groovy (#44143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update smartsense-motion-sensor.groovy,Optimize that deviceJoinName,change the vid to generic-motion-2 Co-authored-by: Andy Yi --- .../smartsense-motion-sensor.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index e5d41b088db..fc0b1948794 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -41,8 +41,8 @@ metadata { fingerprint inClusters: "0000,0001,0003,0020,0402,0500", outClusters: "0019", manufacturer: "Samjin", model: "motion", deviceJoinName: "Motion Sensor" // This is the only ST sensor that shouldn't use SmartThings-smartthings-SmartSense_Motion_Sensor fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "Ecolink", model: "PIRZB1-ECO", deviceJoinName: "Ecolink Motion Sensor" //Ecolink Motion Detector //AduroSmart - fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "ADUROLIGHT", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Motion Sensor V2.0 - fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "AduroSmart Eria", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Motion Sensor V2.1 + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "ADUROLIGHT", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.0 + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "AduroSmart Eria", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.1 } simulator { From 83a00a70fe220210bf260939260fb9879514ce76 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Thu, 17 Sep 2020 13:38:57 -0700 Subject: [PATCH 070/422] ICP-11574 Manually set smoke alarm wakeup interval to 4 hours (#44128) * ICP-11574 Manually set smoke alarm wakeup interval to 4 hours * don't set wakeupinterval for first alert, since there is legacy behavior --- .../zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy b/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy index 5f7d1db7632..d6e4ff5a592 100644 --- a/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy +++ b/devicetypes/smartthings/zwave-basic-smoke-alarm.src/zwave-basic-smoke-alarm.groovy @@ -252,5 +252,6 @@ def initialPoll() { // check initial battery and smoke sensor state request << zwave.batteryV1.batteryGet() request << zwave.sensorBinaryV2.sensorBinaryGet(sensorType: zwave.sensorBinaryV2.SENSOR_TYPE_SMOKE) + if (zwaveInfo.mfr != "0138") request << zwave.wakeUpV1.wakeUpIntervalSet(seconds: 4*60*60, nodeid: zwaveHubNodeId) commands(request, 500) + ["delay 6000", command(zwave.wakeUpV1.wakeUpNoMoreInformation())] } From 1f9576d8753614c876fb17df7b0924ffe8b7da2d Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Thu, 17 Sep 2020 17:30:56 -0700 Subject: [PATCH 071/422] Z-Wave Basic Window Shade fix + Calibration (#44034) * Z-Wave Basic Window Shade fix + Calibration - Open() and Close() buttons resolved - Calibration Parameter setting included in preference * Fix of requested issues - Return pair name to "Aeotec Window Treatment" - Fixed spacing of lines 61 - 73 - Removed else block on line 171 * Fixed Requested issues - Fixed indentation to use tabs. (line 165) - Fixed range instead of the use of text. (line 69) * Update Part 3 - Nano Shutter - used calibrationTime rather than int Time. (Line 164) - Condensed preference input for "calibrationTime" - Added note on top of calibrationTime to indicate it is specific to Nano Shutter and the function its used in. * Update Nano Shutter Part 4 - Added case "open": on line 117 case "close": on line 121 --- .../zwave-basic-window-shade.groovy | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy b/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy index eb670ddf29b..7276b9a52f8 100644 --- a/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy +++ b/devicetypes/smartthings/zwave-basic-window-shade.src/zwave-basic-window-shade.groovy @@ -58,11 +58,22 @@ metadata { defaultValue: false, displayDuringSetup: false ) + + //This setting for calibrationTime is specific to Aeotec Nano Shutter and operates under def updated() - Line 159 + input("calibrationTime", "number", + title: "Open/Close timing", + description: "Set the motor's open/close time", + defaultValue: false, + displayDuringSetup: false, + range: "5..255", + default: 10 + ) } } } def parse(String description) { + log.debug "parse() - description: $description" def result = [] if (description.startsWith("Err")) { result = createEvent(descriptionText:description, isStateChange:true) @@ -101,11 +112,14 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { } def setButton(button) { + log.debug "button: $button" switch(button) { case "open": + case "statelessCurtainPowerButton_open_button": open() break case "close": + case "statelessCurtainPowerButton_close_button": close() break default: @@ -147,13 +161,20 @@ def installed() { def updated() { sendHubCommand(pause()) state.reverseDirection = reverseDirection ? reverseDirection : false + + if (calibrationTime >= 5 && calibrationTime <= 255) { + response([ + secure(zwave.configurationV1.configurationSet(parameterNumber: 35, size: 1, scaledConfigurationValue: calibrationTime)), + ]) + } + } def configure() { log.debug "Configure..." response([ - secure(zwave.configurationV1.configurationSet(parameterNumber: 80, size: 1, scaledConfigurationValue: 1)), - secure(zwave.configurationV1.configurationSet(parameterNumber: 85, size: 1, scaledConfigurationValue: 1)) + secure(zwave.configurationV1.configurationSet(parameterNumber: 80, size: 1, scaledConfigurationValue: 1)), + secure(zwave.configurationV1.configurationSet(parameterNumber: 85, size: 1, scaledConfigurationValue: 1)) ]) } @@ -171,4 +192,4 @@ private getOpenValue() { private getCloseValue() { !state.reverseDirection ? 0xFF : 0x00 -} \ No newline at end of file +} From 2a21becb8a9963751822117756d6a2a37e6ce888 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Mon, 21 Sep 2020 16:28:06 +0800 Subject: [PATCH 072/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Switch (#41408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 啦啦 王 --- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 84b8add3add..c5407525d73 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -53,7 +53,6 @@ metadata { fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, FFFF", outClusters: "0019", manufacturer: "MEGAMAN", model: "BSZTM005", deviceJoinName: "INGENIUM Switch" //INGENIUM ZB Mains Switching Module // Innr - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 220", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 222", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 224", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug @@ -84,7 +83,8 @@ metadata { // SONOFF fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "SONOFF", model: "BASICZBR3", deviceJoinName: "SONOFF Outlet", ocfDeviceType: "oic.d.smartplug" //SONOFF Basic (R3 Zigbee) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "SONOFF", model: "S31 Lite zb", deviceJoinName: "S31 Outlet", ocfDeviceType: "oic.d.smartplug" //S31 Lite zb - + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "1000", manufacturer: "SONOFF", model: "01MINIZB", deviceJoinName: "SONOFF 01MINIZB" //01MINIZB + // Terncy fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0019", manufacturer: "", model: "TERNCY-LS01", deviceJoinName: "Terncy Switch" //Terncy Smart Light Socket From 7b8bda8ec1010a53fd44f25c5b14210ac3467508 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 21 Sep 2020 12:08:55 -0700 Subject: [PATCH 073/422] ONEAPP-33321 Update Korean translations (#44467) --- .../ezex-temp-humidity-sensor.src/i18n/messages.properties | 2 +- .../smartsense-button.src/i18n/messages.properties | 2 +- .../i18n/messages.properties | 2 +- .../i18n/messages.properties | 2 +- .../smartsense-moisture-sensor.src/i18n/messages.properties | 2 +- .../smartsense-motion-sensor.src/i18n/messages.properties | 2 +- .../smartsense-multi-sensor.src/i18n/messages.properties | 6 +++--- .../smartsense-multi.src/i18n/messages.properties | 2 +- .../i18n/messages.properties | 2 +- .../i18n/messages.properties | 2 +- .../i18n/messages.properties | 2 +- .../tyco-door-window-sensor.src/i18n/messages.properties | 2 +- .../i18n/messages.properties | 2 +- .../zwave-door-temp-sensor.src/i18n/messages.properties | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/devicetypes/smartthings/ezex-temp-humidity-sensor.src/i18n/messages.properties b/devicetypes/smartthings/ezex-temp-humidity-sensor.src/i18n/messages.properties index 3d3ada2df22..df90df538c9 100755 --- a/devicetypes/smartthings/ezex-temp-humidity-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/ezex-temp-humidity-sensor.src/i18n/messages.properties @@ -47,7 +47,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-button.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-button.src/i18n/messages.properties index 1c0687ef854..8aa53ce0472 100755 --- a/devicetypes/smartthings/smartsense-button.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-button.src/i18n/messages.properties @@ -49,7 +49,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-garage-door-multi.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-garage-door-multi.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/smartsense-garage-door-multi.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-garage-door-multi.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties index 7a1fb48717e..ad39b4c7e21 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties @@ -61,7 +61,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties index b5c0813a80f..8c25257baf0 100755 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties @@ -47,7 +47,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties index 512baa0fc47..fa7397b5c2c 100755 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties @@ -71,7 +71,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. @@ -216,7 +216,7 @@ '''No'''.in=Tidak '''No'''.it=No '''No'''.ja=いいえ -'''No'''.ko=아니요 +'''No'''.ko=아니요(문열림 센서) '''No'''.lv=Nē '''No'''.lt=Ne '''No'''.ms=Tidak @@ -265,7 +265,7 @@ '''Yes'''.in=Ya '''Yes'''.it=Sì '''Yes'''.ja=はい -'''Yes'''.ko=예 +'''Yes'''.ko=예(3축 가속도 센서) '''Yes'''.lv=Jā '''Yes'''.lt=Taip '''Yes'''.ms=Ya diff --git a/devicetypes/smartthings/smartsense-multi.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-multi.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/smartsense-multi.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-multi.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-open-closed-sensor.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/i18n/messages.properties index 1013c99ac42..92e733232ce 100755 --- a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/i18n/messages.properties @@ -46,7 +46,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/smartsense-virtual-open-closed.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-virtual-open-closed.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/smartsense-virtual-open-closed.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-virtual-open-closed.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/tyco-door-window-sensor.src/i18n/messages.properties b/devicetypes/smartthings/tyco-door-window-sensor.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/tyco-door-window-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/tyco-door-window-sensor.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/i18n/messages.properties index e561095c75a..f02619ca619 100644 --- a/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/zigbee-motion-temp-humidity-sensor.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. diff --git a/devicetypes/smartthings/zwave-door-temp-sensor.src/i18n/messages.properties b/devicetypes/smartthings/zwave-door-temp-sensor.src/i18n/messages.properties index 082f59e22fa..1a64327c7c5 100644 --- a/devicetypes/smartthings/zwave-door-temp-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/zwave-door-temp-sensor.src/i18n/messages.properties @@ -43,7 +43,7 @@ '''Select how many degrees to adjust the temperature.'''.in=Pilih berapa derajat suhu akan disesuaikan. '''Select how many degrees to adjust the temperature.'''.it=Selezionate il numero di gradi per regolare la temperatura. '''Select how many degrees to adjust the temperature.'''.ja=温度を調整する度数を選択してください。 -'''Select how many degrees to adjust the temperature.'''.ko=온도를 얼마나 조절할 지 선택해 주세요. +'''Select how many degrees to adjust the temperature.'''.ko=측정 온도가 지속적으로 맞지 않을 경우, 온도를 보정해 주세요. '''Select how many degrees to adjust the temperature.'''.lv=Izvēlieties, par cik grādiem regulēt temperatūru. '''Select how many degrees to adjust the temperature.'''.lt=Pasirinkite, keliais laipsniais pakoreguoti temperatūrą. '''Select how many degrees to adjust the temperature.'''.ms=Pilih tahap darjah untuk melaraskan suhu. From a7e6c38f99c3c857a825787c29cad3c08a82a5da Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Tue, 22 Sep 2020 19:21:10 +0200 Subject: [PATCH 074/422] ICP-13664, ICP-13665 Deleted old fingerprint for Inovelli Dimmer LZW31-SN (#45024) --- .../zwave-metering-dimmer.src/zwave-metering-dimmer.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index 74ad008234b..e0b876a7b8b 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -40,7 +40,6 @@ metadata { fingerprint mfr:"0086", prod:"0203", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //AU //Aeotec Nano Dimmer fingerprint mfr:"014F", prod:"5044", model:"3533", deviceJoinName: "GoControl Dimmer Switch" //GoControl Plug-in Dimmer fingerprint mfr:"0159", prod:"0001", model:"0055", deviceJoinName: "Qubino Dimmer Switch" //Qubino Mini Dimmer ZMNHHD1 - fingerprint mfr:"031E", prod:"0001", model:"0001", deviceJoinName: "Inovelli Dimmer Switch" //Inovelli Dimmer LZW31-SN } simulator { From 36bd1fe60143496a642e008b752ebc9168c324da Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 23 Sep 2020 19:52:06 +0200 Subject: [PATCH 075/422] WWST-7022 Added fingerprint for POPP Strike Lock Control (#45045) --- .../zwave-lock-without-codes.groovy | 2 ++ 1 file changed, 2 insertions(+) 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 index 963bfb03712..964f9376f74 100644 --- 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 @@ -28,6 +28,8 @@ metadata { fingerprint mfr: "0090", prod: "0003", model: "0446", deviceJoinName: "Kwikset Door Lock" //99140 //Kwikset Convert Deadbolt Door Lock fingerprint mfr: "033F", prod: "0001", model: "0001", deviceJoinName: "August Door Lock" //August Smart Lock Pro fingerprint mfr: "021D", prod: "0003", model: "0001", deviceJoinName: "Alfred Door Lock" // DB2 //Alfred Smart Home Touchscreen Deadbolt + //zw:Fs type:4001 mfr:0154 prod:0005 model:0001 ver:1.05 zwv:4.38 lib:03 cc:7A,73,80,5A,98 sec:5E,86,72,30,71,70,59,85,62 + fingerprint mfr: "0154", prod: "0005", model: "0001", deviceJoinName: "POPP Door Lock" // POPP Strike Lock Control POPE012501 } simulator { From d8fa5589e625c7f14cfc6d9de3bc72d74a0296c8 Mon Sep 17 00:00:00 2001 From: Taejun Park Date: Fri, 25 Sep 2020 03:37:22 +0900 Subject: [PATCH 076/422] [CHAD-5501][zigbee multi switch] Need Exception handling for eZEX multi switch (#44306) * Exception handling for eZEX * Change the logic --- .../zigbee-multi-switch.groovy | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 8f4f7cd159c..a77587676f8 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -107,25 +107,19 @@ def parse(String description) { Map eventMap = zigbee.getEvent(description) Map eventDescMap = zigbee.parseDescriptionAsMap(description) - if (!eventMap && eventDescMap) { - eventMap = [:] - if (eventDescMap?.clusterId == zigbee.ONOFF_CLUSTER) { - eventMap[name] = "switch" - eventMap[value] = eventDescMap?.value - } - } - if (eventMap) { - if (eventDescMap?.sourceEndpoint == "01" || eventDescMap?.endpoint == "01") { - sendEvent(eventMap) - } else { - def childDevice = childDevices.find { - it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.sourceEndpoint}" || it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.endpoint}" - } - if (childDevice) { - childDevice.sendEvent(eventMap) + if (eventDescMap && eventDescMap?.attrId == "0000") {//0x0000 : OnOff attributeId + if (eventDescMap?.sourceEndpoint == "01" || eventDescMap?.endpoint == "01") { + sendEvent(eventMap) } else { - log.debug "Child device: $device.deviceNetworkId:${eventDescMap.sourceEndpoint} was not found" + def childDevice = childDevices.find { + it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.sourceEndpoint}" || it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.endpoint}" + } + if (childDevice) { + childDevice.sendEvent(eventMap) + } else { + log.debug "Child device: $device.deviceNetworkId:${eventDescMap.sourceEndpoint} was not found" + } } } } From 411707d981bd7cc7bca5abd3293ad255c4bdf294 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 29 Sep 2020 19:14:51 +0200 Subject: [PATCH 077/422] ICP-13694 - removes supporting the Battery capability (#45566) * ICP-13694 - removes supporting the Battery capability * ICP-13694 - removes supporting the Battery capability --- .../zigbee-window-shade.groovy | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) 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 ea37642a2e6..705c2520288 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -18,7 +18,6 @@ import physicalgraph.zigbee.zcl.DataType metadata { definition(name: "ZigBee Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind", mnmn: "SmartThings", vid: "generic-shade") { capability "Actuator" - capability "Battery" capability "Configuration" capability "Refresh" capability "Window Shade" @@ -80,7 +79,6 @@ private getCOMMAND_GOTO_LIFT_PERCENTAGE() { 0x05 } private getATTRIBUTE_POSITION_LIFT() { 0x0008 } private getATTRIBUTE_CURRENT_LEVEL() { 0x0000 } private getCOMMAND_MOVE_LEVEL_ONOFF() { 0x04 } -private getBATTERY_PERCENTAGE_REMAINING() { 0x0021 } private List collectAttributes(Map descMap) { List descMaps = new ArrayList() @@ -117,9 +115,6 @@ def parse(String description) { def valueInt = Math.round((zigbee.convertHexToInt(descMap.value)) / 255 * 100) levelEventHandler(valueInt) - } else if (reportsBatteryPercentage() && descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && zigbee.convertHexToInt(descMap?.attrId) == BATTERY_PERCENTAGE_REMAINING && descMap.value) { - def batteryLevel = zigbee.convertHexToInt(descMap.value) - batteryPercentageEventHandler(batteryLevel) } } } @@ -152,13 +147,6 @@ def updateFinalState() { } } -def batteryPercentageEventHandler(batteryLevel) { - if (batteryLevel != null) { - batteryLevel = Math.min(100, Math.max(0, batteryLevel)) - sendEvent([name: "battery", value: batteryLevel, unit: "%", descriptionText: "{{ device.displayName }} battery was {{ value }}%"]) - } -} - def supportsLiftPercentage() { device.getDataValue("manufacturer") != "Feibit Co.Ltd" } @@ -243,17 +231,9 @@ def configure() { cmds += readDeviceBindingTable() } - if (reportsBatteryPercentage()) { - cmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_PERCENTAGE_REMAINING, DataType.UINT8, 30, 21600, 0x01) - } - return refresh() + cmds } -def usesLocalGroupBinding() { - isIkeaKadrilj() || isIkeaFyrtur() -} - private def parseBindingTableMessage(description) { Integer groupAddr = getGroupAddrFromBindingTable(description) if (groupAddr) { @@ -279,19 +259,7 @@ private List readDeviceBindingTable() { } def shouldInvertLiftPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() || isSomfy() -} - -def reportsBatteryPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() -} - -def isIkeaKadrilj() { - device.getDataValue("model") == "KADRILJ roller blind" -} - -def isIkeaFyrtur() { - device.getDataValue("model") == "FYRTUR block-out roller blind" + return isSomfy() } def isSomfy() { From 6cd4ee11d9110228ef23c1436117c5e89e078749 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 29 Sep 2020 19:15:19 +0200 Subject: [PATCH 078/422] reverts innr fingerprint (#45829) --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index c5407525d73..c4c0ae9061f 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -53,6 +53,7 @@ metadata { fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, FFFF", outClusters: "0019", manufacturer: "MEGAMAN", model: "BSZTM005", deviceJoinName: "INGENIUM Switch" //INGENIUM ZB Mains Switching Module // Innr + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 220", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 222", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "innr", model: "SP 224", deviceJoinName: "Innr Outlet", ocfDeviceType: "oic.d.smartplug" //Innr Smart Plug From 36d37c1b431eb81c2722202c66dd07d38f3bd3dd Mon Sep 17 00:00:00 2001 From: Steven Green Date: Wed, 30 Sep 2020 13:40:27 -0700 Subject: [PATCH 079/422] CHAD-5290 Move Schlage BR469ZP fingerprint (#45988) Because this device reports door lock operation events prior to notification events, lock code events are generated without the necessary user code data. This was causing user-code based automations to not fire. Since this device executes locally, it would have required a high-risk fix in hubcore. Rather than removing the device entirely, we are moving it to a DTH where user codes would not be accessible for the purposes of creating automations. --- .../zwave-lock-without-codes.src/zwave-lock-without-codes.groovy | 1 + devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) 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 index 964f9376f74..8e338181460 100644 --- 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 @@ -30,6 +30,7 @@ metadata { fingerprint mfr: "021D", prod: "0003", model: "0001", deviceJoinName: "Alfred Door Lock" // DB2 //Alfred Smart Home Touchscreen Deadbolt //zw:Fs type:4001 mfr:0154 prod:0005 model:0001 ver:1.05 zwv:4.38 lib:03 cc:7A,73,80,5A,98 sec:5E,86,72,30,71,70,59,85,62 fingerprint mfr: "0154", prod: "0005", model: "0001", deviceJoinName: "POPP Door Lock" // POPP Strike Lock Control POPE012501 + fingerprint mfr: "003B", prod: "0001", model: "0469", deviceJoinName: "Schlage Door Lock" //BE469ZP //Schlage Connect Smart Deadbolt Door Lock } simulator { diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index 3a17e4763cb..33faffb9fc1 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -47,7 +47,6 @@ metadata { fingerprint mfr:"003B", prod:"6341", model:"5044", deviceJoinName: "Schlage Door Lock" //Schlage Touchscreen Deadbolt Door Lock fingerprint mfr:"003B", prod:"634B", model:"504C", deviceJoinName: "Schlage Door Lock" //Schlage Connected Keypad Lever Door Lock fingerprint mfr:"003B", prod:"0001", model:"0468", deviceJoinName: "Schlage Door Lock" //BE468ZP //Schlage Connect Smart Deadbolt Door Lock - fingerprint mfr:"003B", prod:"0001", model:"0469", deviceJoinName: "Schlage Door Lock" //BE469ZP //Schlage Connect Smart Deadbolt Door Lock fingerprint mfr:"003B", prod:"0004", model:"2109", deviceJoinName: "Schlage Door Lock" //Schlage Keypad Deadbolt JBE109 fingerprint mfr:"003B", prod:"0004", model:"6109", deviceJoinName: "Schlage Door Lock" //Schlage Keypad Lever JFE109 // Yale From 0a63ebf5aea810a8905f2451a66eea04b2654fd6 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Thu, 1 Oct 2020 22:37:54 +0200 Subject: [PATCH 080/422] ICP-13566/ICP-13565 Change metadata for Qubino 1D (#45982) --- .../qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index b6b799ee961..d0567b6b544 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -25,7 +25,7 @@ metadata { fingerprint mfr: "0159", prod: "0002", model: "0051", deviceJoinName: "Qubino Switch 1" //Qubino Flush 2 Relay fingerprint mfr: "0159", prod: "0002", model: "0052", deviceJoinName: "Qubino Switch" //Qubino Flush 1 Relay - fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Switch" //Qubino Flush 1D Relay + fingerprint mfr: "0159", prod: "0002", model: "0053", deviceJoinName: "Qubino Switch", mnmn: "SmartThings", vid: "generic-switch" //Qubino Flush 1D Relay } tiles(scale: 2) { From 4c5479fe4c83a838ed6a0eb620263b6a37851961 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 2 Oct 2020 01:07:38 +0200 Subject: [PATCH 081/422] [WWST-6516] New DTHes to cover Fibaro Walli Roller Shutter functionality (#30258) * New DTHes to cover Fibaro Walli Roller Shutter functionality * Added Child Venetian Blind DTH and changed namespace * Further fixes for bugs discovered during work with similar device * Ongoing work Fibaro Walli Roller Shutter * Added Window Shade Level capability --- .../fibaro-walli-roller-shutter-driver.groovy | 531 +++++++++++++++ ...ibaro-walli-roller-shutter-venetian.groovy | 617 ++++++++++++++++++ .../fibaro-walli-roller-shutter.groovy | 554 ++++++++++++++++ .../child-venetian-blind.groovy | 33 + 4 files changed, 1735 insertions(+) create mode 100644 devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy create mode 100644 devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy create mode 100644 devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy create mode 100644 devicetypes/smartthings/child-venetian-blind.src/child-venetian-blind.groovy diff --git a/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy b/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy new file mode 100644 index 00000000000..21ae9e826f3 --- /dev/null +++ b/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy @@ -0,0 +1,531 @@ +/** + * Copyright 2020 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. + * + */ + +import java.lang.Math + +metadata { + definition (name: "Fibaro Walli Roller Shutter Driver", namespace: "fibargroup", author: "SmartThings", ocfDeviceType: "oic.d.blind", mnmn: "SmartThings", vid: "SmartThings-smartthings-Fibaro_Roller_Shutter") { + capability "Window Shade" + capability "Window Shade Level" + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Health Check" + capability "Configuration" + + capability "Switch Level" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" + } + } + standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc" + } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + valueTile("shadeLevel", "device.level", width: 4, height: 1) { + state "level", label: 'Shade is ${currentValue}% up', defaultState: true + } + controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { + state "level", action:"switch level.setLevel" + } + + main "windowShade" + details(["windowShade", "contPause", "shadeLevel", "levelSliderControl", "refresh"]) + } + + preferences { + // Preferences template begin + parameterMap.each { + input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") + + switch(it.type) { + case "boolRange": + input( + name: it.key + "Boolean", type: "bool", title: "Enable", + description: "If you disable this option, it will overwrite setting below.", + defaultValue: it.defaultValue != it.disableValue, required: false + ) + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + case "boolean": + input( + type: "paragraph", element: "paragraph", + description: "Option enabled: ${it.activeDescription}\n Option disabled: ${it.inactiveDescription}" + ) + input( + name: it.key, type: "boolean", + title: "Enable", defaultValue: it.defaultValue == it.activeOption, required: false + ) + break + case "enum": + input( + name: it.key, title: "Select", type: "enum", + options: it.values, defaultValue: it.defaultValue, required: false + ) + break + case "range": + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + } + } + // Preferences template end + } +} + +def installed() { + state.calibrationStatus = "notStarted" + sendEvent(name: "checkInterval", value: 2 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // Preferences template begin + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + if (it.type == "boolRange" && getPreferenceValue(it) == it.disableValue) { + state.currentPreferencesState."$it.key".status = "disablePending" + } else { + def preferenceName = it.key + "Boolean" + settings."$preferenceName" = true + state.currentPreferencesState."$it.key".status = "synced" + } + } + // Preferences template end + sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) +} + +def updated() { + // Preferences template begin + parameterMap.each { + if (isPreferenceChanged(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + if (it.type == "boolRange") { + def preferenceName = it.key + "Boolean" + if (notNullCheck(settings."$preferenceName")) { + if (!settings."$preferenceName") { + state.currentPreferencesState."$it.key".status = "disablePending" + } else if (state.currentPreferencesState."$it.key".status == "disabled") { + state.currentPreferencesState."$it.key".status = "syncPending" + } + } else { + state.currentPreferencesState."$it.key".status = "syncPending" + } + } + } else if (!state.currentPreferencesState."$it.key".value) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + syncConfiguration() + // Preferences template end +} + +private syncConfiguration() { + def commands = [] + parameterMap.each { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } else if (state.currentPreferencesState."$it.key".status == "disablePending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + } + sendHubCommand(commands) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + // Preferences template begin + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find( {it.parameterNumber == cmd.parameterNumber} ) + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + if (settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + handleConfigurationChange(cmd) + } else if (preference.type == "boolRange") { + if (state.currentPreferencesState."$key".status == "disablePending" && preferenceValue == preference.disableValue) { + state.currentPreferencesState."$key".status = "disabled" + } else { + runIn(5, "syncConfiguration", [overwrite: true]) + } + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } + // Preferences template end + handleConfigurationChange(cmd) +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + case "boolean": + return String.valueOf(preference.optionActive == integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + switch (preference.type) { + case "boolean": + return settings."$parameterKey" ? preference.optionActive : preference.optionInactive + case "boolRange": + def parameterKeyBoolean = parameterKey + "Boolean" + return !notNullCheck(settings."$parameterKeyBoolean") || settings."$parameterKeyBoolean" ? settings."$parameterKey" : preference.disableValue + case "range": + return settings."$parameterKey" + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isPreferenceChanged(preference) { + if (notNullCheck(settings."$preference.key")) { + def value = state.currentPreferencesState."$preference.key" + switch (preference.type) { + case "boolRange": + def boolName = preference.key + "Boolean" + if (state.currentPreferencesState."$preference.key".status == "disabled") { + return settings."$boolName" + } else { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" || !settings."$boolName" + } + default: + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } + } else { + return false + } +} + +private notNullCheck(value) { + return value != null +} + +def handleConfigurationChange(confgurationReport) { + switch (confgurationReport.parameterNumber) { + case 151: //Operating mode + switch(confgurationReport.scaledConfigurationValue) { + case 1: // "Roller blind (with positioning)" + log.info "Changing device type to Fibaro Walli Roller Shutter" + setDeviceType("Fibaro Walli Roller Shutter") + break + case 2: // "Venetian blind (with positioning)" + log.info "Changing device type to Fibaro Walli Roller Shutter Venetian" + setDeviceType("Fibaro Walli Roller Shutter Venetian") + break + case 5: // "Roller blind with built-in driver" + case 6: // "Roller blind with built-in driver (impulse)" + log.info "Device is already configured as Fibaro Walli Roller Shutter Driver" + break + } + break + default: + log.info "Parameter no. ${confgurationReport.parameterNumber} has no specific handler" + break + } +} + +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 close() { + setShadeLevel(0x64) +} + +def open() { + setShadeLevel(0x00) +} + +def pause() { + encap(zwave.switchMultilevelV3.switchMultilevelStopLevelChange()) +} + +def setLevel(level) { + setShadeLevel(level) +} + +def setShadeLevel(level) { + log.debug "Setting shade level: ${level}" + state.isManualCommand = false + def currentLevel = Integer.parseInt(device.currentState("shadeLevel").value) + state.blindsLastCommand = currentLevel > level ? "opening" : "closing" + state.shadeTarget = level + encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 1) +} + +def refresh() { + sendHubCommand([ + encap(zwave.switchMultilevelV3.switchMultilevelGet()) + ]) +} + +def ping() { + refresh() +} + +def configure() { + def configurationCommands = [] + configurationCommands += encap(zwave.associationV1.associationSet(groupingIdentifier: 2, nodeId: [zwaveHubNodeId])) + configurationCommands += encap(zwave.associationV1.associationSet(groupingIdentifier: 3, nodeId: [zwaveHubNodeId])) + + delayBetween(configurationCommands) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand() + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } else { + log.warn "unable to extract secure command from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd, ep = null) { + if (cmd.value != 0xFE && ep != 2) { + shadeEvent(cmd.value) + } else { + log.warn "Something went wrong with calibration, position of blind is unknown" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { + if (ep != 2) { + shadeEvent(cmd.value) + } +} + +private shadeEvent(value) { + def shadeValue + if (!value) { + shadeValue = "open" + } else if (value == 0x63) { + shadeValue = "closed" + } else { + shadeValue = "partially open" + } + [ + createEvent(name: "windowShade", value: shadeValue, isStateChange: true, descriptionText: "Window blinds is ${shadeValue}"), + createEvent(name: "level", value: value != 0x63 ? value : 100), + createEvent(name: "shadeLevel", value: value != 0x63 ? value : 100) + ] +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { + def toReturn = [] + def eventMap = [:] + def additionalShadeEvent = [:] + if (cmd.meterType == 0x01) { + if (cmd.scale == 0x00) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + toReturn += createEvent(eventMap) + } else if (cmd.scale == 0x02) { + eventMap.name = "power" + eventMap.value = Math.round(cmd.scaledMeterValue) + eventMap.unit = "W" + toReturn += createEvent(eventMap) + if (cmd.scaledMeterValue) { + additionalShadeEvent.name = "windowShade" + additionalShadeEvent.value = state.blindsLastCommand + toReturn += createEvent(additionalShadeEvent) + if (!state.isManualCommand) { + sendEvent(name: "level", value: state.shadeTarget) + sendEvent(name: "shadeLevel", value: state.shadeTarget) + } + } else { + toReturn += response(encap(zwave.switchMultilevelV3.switchMultilevelGet(), 1)) + } + } + } + toReturn +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStartLevelChange cmd) { + state.isManualCommand = true + state.blindsLastCommand = cmd.upDown ? "opening" : "closing" +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, ep = null) { + log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private getParameterMap() {[ + [ + name: "LED frame - colour when moving", key: "ledFrame-ColourWhenMoving", type: "enum", + parameterNumber: 11, size: 1, defaultValue: 1, + values: [ + 0: "LED disabled", + 1: "White", + 2: "Red", + 3: "Green", + 4: "Blue", + 5: "Yellow", + 6: "Cyan", + 7: "Magenta" + ], + description: "This setting defines the LED colour when the motor is running." + ], + [ + name: "LED frame - colour when not moving", key: "ledFrame-ColourWhenNotMoving", type: "enum", + parameterNumber: 12, size: 1, defaultValue: 0, + values: [ + 0: "LED disabled", + 1: "White", + 2: "Red", + 3: "Green", + 4: "Blue", + 5: "Yellow", + 6: "Cyan", + 7: "Magenta" + ], + description: "This setting defines the LED colour when the motor isn't running." + ], + [ + name: "LED frame - brightness", key: "ledFrame-Brightness", type: "boolRange", + parameterNumber: 13, size: 1, defaultValue: 100, + range: "1..100", disableValue: 0, + description: "This setting allows to adjust the LED frame brightness." + ], + [ + name: "Operating mode", key: "operatingMode", type: "enum", + parameterNumber: 151, size: 1, defaultValue: 1, + values: [ + 1: "Roller blind (with positioning)", + 2: "Venetian blind (with positioning)", + 5: "Roller blind with built-in driver", + 6: "Roller blind with built-in driver (impulse)" + ], + description: "This setting allows adjusting operation according to the connected device." + ], + [ + name: "Delay motor stop after reaching end switch", key: "delayMotorStopAfterReachingEndSwitch", type: "range", + parameterNumber: 154, size: 2, defaultValue: 10, + range: "1..255", + description: "The setting determines the time after which the motor will be stopped after end switch contacts are closed." + ], + [ + name: "Motor operation detection", key: "motorOperationDetection", type: "range", + parameterNumber: 155, size: 2, defaultValue: 10, + range: "1..255", + description: "Power threshold interpreted as reaching a limit switch." + ], + [ + name: "Time of up movement", key: "timeOfUpMovement", type: "range", + parameterNumber: 156, size: 4, defaultValue: 6000, + range: "1..90000", + description: "This setting determines the time needed for roller blinds to reach the top. [100 = 1s]" + ], + [ + name: "Time of down movement", key: "timeOfDownMovement", type: "range", + parameterNumber: 157, size: 4, defaultValue: 6000, + range: "1..90000", + description: "This setting determines time needed for roller blinds to reach the bottom. [100 = 1s]" + ], + [ + name: "Buttons orientation", key: "buttonsOrientation", type: "boolean", + parameterNumber: 24, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "default (1st button UP, 2nd button DOWN)", + optionActive: 1, activeDescription: "reversed (1st button DOWN, 2nd button UP)", + description: "This setting allows reversing the operation of the buttons." + ], + [ + name: "Outputs orientation", key: "outputsOrientation", type: "boolean", + parameterNumber: 25, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "(Q1 - UP, Q2 - DOWN)LED disabled", + optionActive: 1, activeDescription: "reversed (Q1 - DOWN, Q2 - UP)", + description: "This setting allows reversing the operation of Q1 and Q2 without changing the wiring (e.g. in case of invalid motor connection)." + ], + [ + name: "Power reports - include self-consumption", key: "powerReports-IncludeSelf-Consumption", type: "boolean", + parameterNumber: 60, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "Self-consumption not included", + optionActive: 1, activeDescription: "Self-consumption included", + description: "This setting determines whether the power measurements should include power consumed by the device itself." + ], + [ + name: "Power reports - on change", key: "powerReports-OnChange", type: "boolRange", + parameterNumber: 61, size: 2, defaultValue: 15, + range: "1..500", disableValue: 0, + description: "This setting defines minimal change (from the last reported) in measured power that results in sending new report. For loads under 50W the setting is irrelevant, report are sent every 5W change." + ], + [ + name: "Power reports - periodic", key: "powerReports-Periodic", type: "boolRange", + parameterNumber: 62, size: 2, defaultValue: 3600, + range: "30..32400", disableValue: 0, + description: "This setting defines reporting interval for measured power." + ], + [ + name: "Energy reports - on change", key: "energyReports-OnChange", type: "boolRange", + parameterNumber: 65, size: 2, defaultValue: 10, + range: "1..500", disableValue: 0, + description: "This setting defines minimal change (from the last reported) in measured energy that results in sending new report." + ], + [ + name: "Energy reports - periodic", key: "energyReports-Periodic", type: "boolRange", + parameterNumber: 66, size: 2, defaultValue: 3600, + range: "30..32400", disableValue: 0, + description: "This setting defines reporting interval for measured energy." + ] +]} \ No newline at end of file diff --git a/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy b/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy new file mode 100644 index 00000000000..7cfbf097669 --- /dev/null +++ b/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy @@ -0,0 +1,617 @@ +/** + * Copyright 2020 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. + * + */ + +import java.lang.Math + +metadata { + definition (name: "Fibaro Walli Roller Shutter Venetian", namespace: "fibargroup", author: "SmartThings", ocfDeviceType: "oic.d.blind", mnmn: "SmartThings", vid: "SmartThings-smartthings-Fibaro_Roller_Shutter_Venetian", mcdSync: true) { + capability "Window Shade" + capability "Window Shade Level" + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Health Check" + capability "Configuration" + + capability "Switch Level" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" + } + } + standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc" + } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + valueTile("shadeLevel", "device.level", width: 4, height: 1) { + state "level", label: 'Shade is ${currentValue}% up', defaultState: true + } + controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { + state "level", action:"switch level.setLevel" + } + + main "windowShade" + details(["windowShade", "contPause", "shadeLevel", "levelSliderControl", "refresh"]) + } + + preferences { + // Preferences template begin + parameterMap.each { + input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") + + switch(it.type) { + case "boolRange": + input( + name: it.key + "Boolean", type: "bool", title: "Enable", + description: "If you disable this option, it will overwrite setting below.", + defaultValue: it.defaultValue != it.disableValue, required: false + ) + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + case "boolean": + input( + type: "paragraph", element: "paragraph", + description: "Option enabled: ${it.activeDescription}\n Option disabled: ${it.inactiveDescription}" + ) + input( + name: it.key, type: "boolean", + title: "Enable", defaultValue: it.defaultValue == it.activeOption, required: false + ) + break + case "enum": + input( + name: it.key, title: "Select", type: "enum", + options: it.values, defaultValue: it.defaultValue, required: false + ) + break + case "range": + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + } + } + // Preferences template end + } +} + +def installed() { + state.calibrationStatus = "notStarted" + sendEvent(name: "checkInterval", value: 2 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // Preferences template begin + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + if (it.type == "boolRange" && getPreferenceValue(it) == it.disableValue) { + state.currentPreferencesState."$it.key".status = "disablePending" + } else { + def preferenceName = it.key + "Boolean" + settings."$preferenceName" = true + state.currentPreferencesState."$it.key".status = "synced" + } + } + // Preferences template end + state.timeOfVenetianMovement = 150 + sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) +} + +def updated() { + // Preferences template begin + parameterMap.each { + if (isPreferenceChanged(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + if (it.type == "boolRange") { + def preferenceName = it.key + "Boolean" + if (notNullCheck(settings."$preferenceName")) { + if (!settings."$preferenceName") { + state.currentPreferencesState."$it.key".status = "disablePending" + } else if (state.currentPreferencesState."$it.key".status == "disabled") { + state.currentPreferencesState."$it.key".status = "syncPending" + } + } else { + state.currentPreferencesState."$it.key".status = "syncPending" + } + statusOverrideIfNeeded(it.key) + } + } else if (!state.currentPreferencesState."$it.key".value) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + syncConfiguration() + // Preferences template end + addVenetianBlind() +} + +private syncConfiguration() { + def commands = [] + parameterMap.each { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } else if (state.currentPreferencesState."$it.key".status == "disablePending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + } + sendHubCommand(commands) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + // Preferences template begin + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find( {it.parameterNumber == cmd.parameterNumber} ) + if (preference) { + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + if (settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + handleConfigurationChange(cmd) + } else if (preference.type == "boolRange") { + if (state.currentPreferencesState."$key".status == "disablePending" && preferenceValue == preference.disableValue) { + state.currentPreferencesState."$key".status = "disabled" + } else { + runIn(5, "syncConfiguration", [overwrite: true]) + } + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } + } else { + handleConfigurationChange(cmd) + } + // Preferences template end +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + case "boolean": + return String.valueOf(preference.optionActive == integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + switch (preference.type) { + case "boolean": + return settings."$parameterKey" ? preference.optionActive : preference.optionInactive + case "boolRange": + def parameterKeyBoolean = parameterKey + "Boolean" + return !notNullCheck(settings."$parameterKeyBoolean") || settings."$parameterKeyBoolean" ? settings."$parameterKey" : preference.disableValue + case "range": + return settings."$parameterKey" + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isPreferenceChanged(preference) { + if (notNullCheck(settings."$preference.key")) { + def value = state.currentPreferencesState."$preference.key" + switch (preference.type) { + case "boolRange": + def boolName = preference.key + "Boolean" + if (state.currentPreferencesState."$preference.key".status == "disabled") { + return settings."$boolName" + } else { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" || !settings."$boolName" + } + default: + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } + } else { + return false + } +} + +private notNullCheck(value) { + return value != null +} + +private statusOverrideIfNeeded(preferenceKey) { + switch (preferenceKey) { + case "forceCalibration": + if (state.calibrationStatus == "done") { + state.currentPreferencesState."$preferenceKey".status = "synced" + } + break + } +} + +def handleConfigurationChange(confgurationReport) { + switch (confgurationReport.parameterNumber) { + case 150: // Calibrating + switch(confgurationReport.scaledConfigurationValue) { + case 0: // "Device is not calibrated" + state.calibrationStatus = "notStarted" + break + case 1: // "Device is calibrated" + state.calibrationStatus = "done" + state.currentPreferencesState.forceCalibration.status = "synced" + break + case 2: // "Force Calibration" + state.calibrationStatus = state.calibrationStatus == "notStarted" ? "pending" : state.calibrationStatus + break + } + log.info "Calibration ${state.calibrationStatus}" + break + case 151: //Operating mode + switch(confgurationReport.scaledConfigurationValue) { + case 1: // "Roller blind (with positioning)" + log.info "Changing device type to Fibaro Walli Roller Shutter" + setDeviceType("Fibaro Walli Roller Shutter") + break + case 2: // "Venetian blind (with positioning)" + log.info "Device is already configured as Fibaro Roller Shutter Venetian" + break + case 5: // "Roller blind with built-in driver" + case 6: // "Roller blind with built-in driver (impulse)" + log.info "Changing device type to Fibaro Walli Roller Shutter Driver" + setDeviceType("Fibaro Walli Roller Shutter Driver") + break + } + break + case 152: // Venetian Blinds - time of full turn of the slats + state.timeOfVenetianMovement = confgurationReport.scaledConfigurationValue + break + default: + log.info "Parameter no. ${confgurationReport.parameterNumber} has no specific handler" + break + } +} + +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 close() { + setShadeLevel(0x64) +} + +def open() { + setShadeLevel(0x00) +} + +def pause() { + encap(zwave.switchMultilevelV3.switchMultilevelStopLevelChange()) +} + +def setLevel(level) { + setShadeLevel(level) +} + +def setShadeLevel(level) { + log.debug "Setting shade level: ${level}" + state.isManualCommand = false + def currentLevel = Integer.parseInt(device.currentState("shadeLevel").value) + state.blindsLastCommand = currentLevel > level ? "opening" : "closing" + state.shadeTarget = level + encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 1) +} + +def setSlats(childDni, level) { + state.isManualCommand = false + def time = (int) (state.timeOfVenetianMovement * 1.1) + sendHubCommand([ + encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 2), + "delay ${time}", + encap(zwave.switchMultilevelV3.switchMultilevelGet(), 2) + ]) +} + +def refresh() { + sendHubCommand([ + encap(zwave.switchMultilevelV3.switchMultilevelGet(), 1), + "delay 500", + encap(zwave.switchMultilevelV3.switchMultilevelGet(), 2) + ]) +} + +def ping() { + refresh() +} + +def configure() { + encap(zwave.configurationV1.configurationGet(parameterNumber: 152)) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand() + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } else { + log.warn "unable to extract secure command from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd, ep = null) { + if (cmd.value != 0xFE) { + if (ep != 2) { + shadeEvent(cmd.value) + } else { + String childDni = "${device.deviceNetworkId}:1" + def child = childDevices.find { it.deviceNetworkId == childDni } + child?.sendEvent(name: "level", value: cmd.value != 0x63 ? cmd.value : 100) + } + } else { + log.warn "Something went wrong with calibration, position of blind is unknown" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { + if (ep != 2) { + shadeEvent(cmd.value) + } +} + +private shadeEvent(value) { + def shadeValue + if (!value) { + shadeValue = "open" + } else if (value == 0x63) { + shadeValue = "closed" + } else { + shadeValue = "partially open" + } + [ + createEvent(name: "windowShade", value: shadeValue, isStateChange: true, descriptionText: "Window blinds is ${shadeValue}"), + createEvent(name: "level", value: value != 0x63 ? value : 100), + createEvent(name: "shadeLevel", value: value != 0x63 ? value : 100) + ] +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { + def toReturn = [] + def eventMap = [:] + def additionalShadeEvent = [:] + if (cmd.meterType == 0x01) { + if (cmd.scale == 0x00) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + toReturn += createEvent(eventMap) + } else if (cmd.scale == 0x02) { + eventMap.name = "power" + eventMap.value = Math.round(cmd.scaledMeterValue) + eventMap.unit = "W" + toReturn += createEvent(eventMap) + if (cmd.scaledMeterValue) { + additionalShadeEvent.name = "windowShade" + additionalShadeEvent.value = state.blindsLastCommand + toReturn += createEvent(additionalShadeEvent) + if (!state.isManualCommand) { + sendEvent(name: "level", value: state.shadeTarget) + sendEvent(name: "shadeLevel", value: state.shadeTarget) + } + } else { + toReturn += response(encap(zwave.switchMultilevelV3.switchMultilevelGet(), 1)) + } + } + } + toReturn +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStartLevelChange cmd) { + state.isManualCommand = true + state.blindsLastCommand = cmd.upDown ? "opening" : "closing" +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelSet cmd) { + state.isManualCommand = true + def time = (int) (state.timeOfVenetianMovement * 1.1) + response([ + "delay ${time}", + encap(zwave.switchMultilevelV3.switchMultilevelGet(), 2) + ]) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, ep = null) { + log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private addVenetianBlind() { + if (!childDevices) { + try { + String childDni = "${device.deviceNetworkId}:1" + def componentLabel = "Fibaro Roller Shutter Venetian Blind" + def child = addChildDevice("Child Venetian Blind", childDni, device.getHub().getId(), [ + completedSetup: true, + label : componentLabel, + isComponent : true, + componentName : "venetianBlind", + componentLabel: "Venetian Blind" + ]) + } catch(Exception e) { + log.debug "Exception: ${e}" + } + } +} + +private getParameterMap() {[ + [ + name: "LED frame - colour when moving", key: "ledFrame-ColourWhenMoving", type: "enum", + parameterNumber: 11, size: 1, defaultValue: 1, + values: [ + 0: "LED disabled", + 1: "White", + 2: "Red", + 3: "Green", + 4: "Blue", + 5: "Yellow", + 6: "Cyan", + 7: "Magenta" + ], + description: "This setting defines the LED colour when the motor is running." + ], + [ + name: "LED frame - colour when not moving", key: "ledFrame-ColourWhenNotMoving", type: "enum", + parameterNumber: 12, size: 1, defaultValue: 0, + values: [ + 0: "LED disabled", + 1: "White", + 2: "Red", + 3: "Green", + 4: "Blue", + 5: "Yellow", + 6: "Cyan", + 7: "Magenta" + ], + description: "This setting defines the LED colour when the motor isn't running." + ], + [ + name: "LED frame - brightness", key: "ledFrame-Brightness", type: "boolRange", + parameterNumber: 13, size: 1, defaultValue: 100, + range: "1..100", disableValue: 0, + description: "This setting allows to adjust the LED frame brightness." + ], + [ + name: "Force calibration", key: "forceCalibration", type: "boolean", + parameterNumber: 150, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "Blinds are not calibrated.", + optionActive: 2, activeDescription: "Blinds calibration process starts.", + description: "This setting allows triggering blinds calibration process." + ], + [ + name: "Operating mode", key: "operatingMode", type: "enum", + parameterNumber: 151, size: 1, defaultValue: 1, + values: [ + 1: "Roller blind (with positioning)", + 2: "Venetian blind (with positioning)", + 5: "Roller blind with built-in driver", + 6: "Roller blind with built-in driver (impulse)" + ], + description: "This setting allows adjusting operation according to the connected device." + ], + [ + name: "Venetian blind - time of full turn of the slats", key: "venetianBlind-TimeOfFullTurnOfTheSlats", type: "range", + parameterNumber: 152, size: 4, defaultValue: 150, + range: "0..65535", + description: "The setting determines time of full turn cycle of the slats. [100 = 1s]" + ], + [ + name: "Set slats back to previous position", key: "setSlatsBackToPreviousPosition", type: "enum", + parameterNumber: 153, size: 1, defaultValue: 1, + values: [ + 0: "slats return to previously set position only in case of the main controller operation", + 1: "slats return to previously set position in case of the main controller operation, momentary switch operation, or when the limit switch is reached", + 2: "slats return to previously set position in case of the main controller operation, momentary switch operation, when the limit switch is reached or after receiving the Switch Multilevel Stop control frame" + ], + description: "The setting determines slats positioning in various situations." + ], + [ + name: "Delay motor stop after reaching end switch", key: "delayMotorStopAfterReachingEndSwitch", type: "range", + parameterNumber: 154, size: 2, defaultValue: 10, + range: "1..255", + description: "The setting determines the time after which the motor will be stopped after end switch contacts are closed." + ], + [ + name: "Motor operation detection", key: "motorOperationDetection", type: "range", + parameterNumber: 155, size: 2, defaultValue: 10, + range: "1..255", + description: "Power threshold interpreted as reaching a limit switch." + ], + [ + name: "Buttons orientation", key: "buttonsOrientation", type: "boolean", + parameterNumber: 24, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "default (1st button UP, 2nd button DOWN)", + optionActive: 1, activeDescription: "reversed (1st button DOWN, 2nd button UP)", + description: "This setting allows reversing the operation of the buttons." + ], + [ + name: "Outputs orientation", key: "outputsOrientation", type: "boolean", + parameterNumber: 25, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "(Q1 - UP, Q2 - DOWN)LED disabled", + optionActive: 1, activeDescription: "reversed (Q1 - DOWN, Q2 - UP)", + description: "This setting allows reversing the operation of Q1 and Q2 without changing the wiring (e.g. in case of invalid motor connection)." + ], + [ + name: "Power reports - include self-consumption", key: "powerReports-IncludeSelf-Consumption", type: "boolean", + parameterNumber: 60, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "Self-consumption not included", + optionActive: 1, activeDescription: "Self-consumption included", + description: "This setting determines whether the power measurements should include power consumed by the device itself." + ], + [ + name: "Power reports - on change", key: "powerReports-OnChange", type: "boolRange", + parameterNumber: 61, size: 2, defaultValue: 15, + range: "1..500", disableValue: 0, + description: "This setting defines minimal change (from the last reported) in measured power that results in sending new report. For loads under 50W the setting is irrelevant, report are sent every 5W change." + ], + [ + name: "Power reports - periodic", key: "powerReports-Periodic", type: "boolRange", + parameterNumber: 62, size: 2, defaultValue: 3600, + range: "30..32400", disableValue: 0, + description: "This setting defines reporting interval for measured power." + ], + [ + name: "Energy reports - on change", key: "energyReports-OnChange", type: "boolRange", + parameterNumber: 65, size: 2, defaultValue: 10, + range: "1..500", disableValue: 0, + description: "This setting defines minimal change (from the last reported) in measured energy that results in sending new report." + ], + [ + name: "Energy reports - periodic", key: "energyReports-Periodic", type: "boolRange", + parameterNumber: 66, size: 2, defaultValue: 3600, + range: "30..32400", disableValue: 0, + description: "This setting defines reporting interval for measured energy." + ] +]} \ No newline at end of file diff --git a/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy b/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy new file mode 100644 index 00000000000..3cb91b3a7ff --- /dev/null +++ b/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy @@ -0,0 +1,554 @@ +/** + * Copyright 2020 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. + * + */ + +import java.lang.Math + +metadata { + definition (name: "Fibaro Walli Roller Shutter", namespace: "fibargroup", author: "SmartThings", ocfDeviceType: "oic.d.blind", mnmn: "SmartThings", vid: "SmartThings-smartthings-Fibaro_Roller_Shutter") { + capability "Window Shade" + capability "Window Shade Level" + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Health Check" + capability "Configuration" + + capability "Switch Level" + + fingerprint mfr: "010F", prod: "1D01", model: "1000", deviceJoinName: "Fibaro Window Treatment" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" + } + } + standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc" + } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + valueTile("shadeLevel", "device.level", width: 4, height: 1) { + state "level", label: 'Shade is ${currentValue}% up', defaultState: true + } + controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { + state "level", action:"switch level.setLevel" + } + + main "windowShade" + details(["windowShade", "contPause", "shadeLevel", "levelSliderControl", "refresh"]) + } + + preferences { + // Preferences template begin + parameterMap.each { + input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") + + switch(it.type) { + case "boolRange": + input( + name: it.key + "Boolean", type: "bool", title: "Enable", + description: "If you disable this option, it will overwrite setting below.", + defaultValue: it.defaultValue != it.disableValue, required: false + ) + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + case "boolean": + input( + type: "paragraph", element: "paragraph", + description: "Option enabled: ${it.activeDescription}\n Option disabled: ${it.inactiveDescription}" + ) + input( + name: it.key, type: "boolean", + title: "Enable", defaultValue: it.defaultValue == it.activeOption, required: false + ) + break + case "enum": + input( + name: it.key, title: "Select", type: "enum", + options: it.values, defaultValue: it.defaultValue, required: false + ) + break + case "range": + input( + name: it.key, type: "number", title: "Set value (range ${it.range})", + defaultValue: it.defaultValue, range: it.range, required: false + ) + break + } + } + // Preferences template end + } +} + +def installed() { + state.calibrationStatus = "notStarted" + sendEvent(name: "checkInterval", value: 2 * 60 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // Preferences template begin + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + if (it.type == "boolRange" && getPreferenceValue(it) == it.disableValue) { + state.currentPreferencesState."$it.key".status = "disablePending" + } else { + def preferenceName = it.key + "Boolean" + settings."$preferenceName" = true + state.currentPreferencesState."$it.key".status = "synced" + } + } + // Preferences template end + sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) +} + +def updated() { + // Preferences template begin + parameterMap.each { + if (isPreferenceChanged(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + if (it.type == "boolRange") { + def preferenceName = it.key + "Boolean" + if (notNullCheck(settings."$preferenceName")) { + if (!settings."$preferenceName") { + state.currentPreferencesState."$it.key".status = "disablePending" + } else if (state.currentPreferencesState."$it.key".status == "disabled") { + state.currentPreferencesState."$it.key".status = "syncPending" + } + } else { + state.currentPreferencesState."$it.key".status = "syncPending" + } + statusOverrideIfNeeded(it.key) + } + } else if (!state.currentPreferencesState."$it.key".value) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + syncConfiguration() + // Preferences template end +} + +private syncConfiguration() { + def commands = [] + parameterMap.each { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } else if (state.currentPreferencesState."$it.key".status == "disablePending") { + commands += encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: it.disableValue, parameterNumber: it.parameterNumber, size: it.size)) + commands += encap(zwave.configurationV2.configurationGet(parameterNumber: it.parameterNumber)) + } + } + sendHubCommand(commands) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + // Preferences template begin + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find( {it.parameterNumber == cmd.parameterNumber} ) + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + if (settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + handleConfigurationChange(cmd) + } else if (preference.type == "boolRange") { + if (state.currentPreferencesState."$key".status == "disablePending" && preferenceValue == preference.disableValue) { + state.currentPreferencesState."$key".status = "disabled" + } else { + runIn(5, "syncConfiguration", [overwrite: true]) + } + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } + // Preferences template end + handleConfigurationChange(cmd) +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + case "boolean": + return String.valueOf(preference.optionActive == integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + switch (preference.type) { + case "boolean": + return settings."$parameterKey" ? preference.optionActive : preference.optionInactive + case "boolRange": + def parameterKeyBoolean = parameterKey + "Boolean" + return !notNullCheck(settings."$parameterKeyBoolean") || settings."$parameterKeyBoolean" ? settings."$parameterKey" : preference.disableValue + case "range": + return settings."$parameterKey" + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isPreferenceChanged(preference) { + if (notNullCheck(settings."$preference.key")) { + def value = state.currentPreferencesState."$preference.key" + switch (preference.type) { + case "boolRange": + def boolName = preference.key + "Boolean" + if (state.currentPreferencesState."$preference.key".status == "disabled") { + return settings."$boolName" + } else { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" || !settings."$boolName" + } + default: + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } + } else { + return false + } +} + +private notNullCheck(value) { + return value != null +} + +private statusOverrideIfNeeded(preferenceKey) { + switch (preferenceKey) { + case "forceCalibration": + if (state.calibrationStatus == "done") { + state.currentPreferencesState."$preferenceKey".status = "synced" + } + break + } +} + +def handleConfigurationChange(confgurationReport) { + switch (confgurationReport.parameterNumber) { + case 150: // Calibrating + switch(confgurationReport.scaledConfigurationValue) { + case 0: // "Device is not calibrated" + state.calibrationStatus = "notStarted" + break + case 1: // "Device is calibrated" + state.calibrationStatus = "done" + state.currentPreferencesState.forceCalibration.status = "synced" + break + case 2: // "Force Calibration" + state.calibrationStatus = state.calibrationStatus == "notStarted" ? "pending" : state.calibrationStatus + break + } + log.info "Calibration ${state.calibrationStatus}" + break + case 151: //Operating mode + switch(confgurationReport.scaledConfigurationValue) { + case 1: // "Roller blind (with positioning)" + log.info "Device is already configured as Roller Blind" + break + case 2: // "Venetian blind (with positioning)" + log.info "Changing device type to Fibaro Walli Roller Shutter Venetian" + setDeviceType("Fibaro Walli Roller Shutter Venetian") + break + case 5: // "Roller blind with built-in driver" + case 6: // "Roller blind with built-in driver (impulse)" + log.info "Changing device type to Fibaro Walli Roller Shutter Driver" + setDeviceType("Fibaro Walli Roller Shutter Driver") + break + } + break + default: + log.info "Parameter no. ${confgurationReport.parameterNumber} has no specific handler" + break + } +} + +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 close() { + setShadeLevel(0x64) +} + +def open() { + setShadeLevel(0x00) +} + +def pause() { + encap(zwave.switchMultilevelV3.switchMultilevelStopLevelChange()) +} + +def setLevel(level) { + setShadeLevel(level) +} + +def setShadeLevel(level) { + log.debug "Setting shade level: ${level}" + state.isManualCommand = false + def currentLevel = Integer.parseInt(device.currentState("shadeLevel").value) + state.blindsLastCommand = currentLevel > level ? "opening" : "closing" + state.shadeTarget = level + encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 1) +} + +def refresh() { + sendHubCommand([ + encap(zwave.switchMultilevelV3.switchMultilevelGet()) + ]) +} + +def ping() { + refresh() +} + +def configure() { + def configurationCommands = [] + configurationCommands += encap(zwave.associationV1.associationSet(groupingIdentifier: 2, nodeId: [zwaveHubNodeId])) + configurationCommands += encap(zwave.associationV1.associationSet(groupingIdentifier: 3, nodeId: [zwaveHubNodeId])) + + delayBetween(configurationCommands) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand() + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } else { + log.warn "unable to extract secure command from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd, ep = null) { + if (cmd.value != 0xFE && ep != 2) { + shadeEvent(cmd.value) + } else { + log.warn "Something went wrong with calibration, position of blind is unknown" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { + if (ep != 2) { + shadeEvent(cmd.value) + } +} + +private shadeEvent(value) { + def shadeValue + if (!value) { + shadeValue = "open" + } else if (value == 0x63) { + shadeValue = "closed" + } else { + shadeValue = "partially open" + } + [ + createEvent(name: "windowShade", value: shadeValue, isStateChange: true, descriptionText: "Window blinds is ${shadeValue}"), + createEvent(name: "level", value: value != 0x63 ? value : 100), + createEvent(name: "shadeLevel", value: value != 0x63 ? value : 100) + ] +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { + def toReturn = [] + def eventMap = [:] + def additionalShadeEvent = [:] + if (cmd.meterType == 0x01) { + if (cmd.scale == 0x00) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + toReturn += createEvent(eventMap) + } else if (cmd.scale == 0x02) { + eventMap.name = "power" + eventMap.value = Math.round(cmd.scaledMeterValue) + eventMap.unit = "W" + toReturn += createEvent(eventMap) + if (cmd.scaledMeterValue) { + additionalShadeEvent.name = "windowShade" + additionalShadeEvent.value = state.blindsLastCommand + toReturn += createEvent(additionalShadeEvent) + if (!state.isManualCommand) { + sendEvent(name: "level", value: state.shadeTarget) + sendEvent(name: "shadeLevel", value: state.shadeTarget) + } + } else { + toReturn += response(encap(zwave.switchMultilevelV3.switchMultilevelGet(), 1)) + } + } + } + toReturn +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStartLevelChange cmd) { + state.isManualCommand = true + state.blindsLastCommand = cmd.upDown ? "opening" : "closing" +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, ep = null) { + log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") +} + +private encap(cmd, endpoint = null) { + if (cmd) { + if (endpoint) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private getParameterMap() {[ + [ + name: "LED frame - colour when moving", key: "ledFrame-ColourWhenMoving", type: "enum", + parameterNumber: 11, size: 1, defaultValue: 1, + values: [ + 0: "LED disabled", + 1: "White", + 2: "Red", + 3: "Green", + 4: "Blue", + 5: "Yellow", + 6: "Cyan", + 7: "Magenta" + ], + description: "This setting defines the LED colour when the motor is running." + ], + [ + name: "LED frame - colour when not moving", key: "ledFrame-ColourWhenNotMoving", type: "enum", + parameterNumber: 12, size: 1, defaultValue: 0, + values: [ + 0: "LED disabled", + 1: "White", + 2: "Red", + 3: "Green", + 4: "Blue", + 5: "Yellow", + 6: "Cyan", + 7: "Magenta" + ], + description: "This setting defines the LED colour when the motor isn't running." + ], + [ + name: "LED frame - brightness", key: "ledFrame-Brightness", type: "boolRange", + parameterNumber: 13, size: 1, defaultValue: 100, + range: "1..100", disableValue: 0, + description: "This setting allows to adjust the LED frame brightness." + ], + [ + name: "Force calibration", key: "forceCalibration", type: "boolean", + parameterNumber: 150, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "Blinds are not calibrated.", + optionActive: 2, activeDescription: "Blinds calibration process starts.", + description: "This setting allows triggering blinds calibration process." + ], + [ + name: "Operating mode", key: "operatingMode", type: "enum", + parameterNumber: 151, size: 1, defaultValue: 1, + values: [ + 1: "Roller blind (with positioning)", + 2: "Venetian blind (with positioning)", + 5: "Roller blind with built-in driver", + 6: "Roller blind with built-in driver (impulse)" + ], + description: "This setting allows adjusting operation according to the connected device." + ], + [ + name: "Delay motor stop after reaching end switch", key: "delayMotorStopAfterReachingEndSwitch", type: "range", + parameterNumber: 154, size: 2, defaultValue: 10, + range: "1..255", + description: "The setting determines the time after which the motor will be stopped after end switch contacts are closed." + ], + [ + name: "Motor operation detection", key: "motorOperationDetection", type: "range", + parameterNumber: 155, size: 2, defaultValue: 10, + range: "1..255", + description: "Power threshold interpreted as reaching a limit switch." + ], + [ + name: "Buttons orientation", key: "buttonsOrientation", type: "boolean", + parameterNumber: 24, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "default (1st button UP, 2nd button DOWN)", + optionActive: 1, activeDescription: "reversed (1st button DOWN, 2nd button UP)", + description: "This setting allows reversing the operation of the buttons." + ], + [ + name: "Outputs orientation", key: "outputsOrientation", type: "boolean", + parameterNumber: 25, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "(Q1 - UP, Q2 - DOWN)LED disabled", + optionActive: 1, activeDescription: "reversed (Q1 - DOWN, Q2 - UP)", + description: "This setting allows reversing the operation of Q1 and Q2 without changing the wiring (e.g. in case of invalid motor connection)." + ], + [ + name: "Power reports - include self-consumption", key: "powerReports-IncludeSelf-Consumption", type: "boolean", + parameterNumber: 60, size: 1, defaultValue: 0, + optionInactive: 0, inactiveDescription: "Self-consumption not included", + optionActive: 1, activeDescription: "Self-consumption included", + description: "This setting determines whether the power measurements should include power consumed by the device itself." + ], + [ + name: "Power reports - on change", key: "powerReports-OnChange", type: "boolRange", + parameterNumber: 61, size: 2, defaultValue: 15, + range: "1..500", disableValue: 0, + description: "This setting defines minimal change (from the last reported) in measured power that results in sending new report. For loads under 50W the setting is irrelevant, report are sent every 5W change." + ], + [ + name: "Power reports - periodic", key: "powerReports-Periodic", type: "boolRange", + parameterNumber: 62, size: 2, defaultValue: 3600, + range: "30..32400", disableValue: 0, + description: "This setting defines reporting interval for measured power." + ], + [ + name: "Energy reports - on change", key: "energyReports-OnChange", type: "boolRange", + parameterNumber: 65, size: 2, defaultValue: 10, + range: "1..500", disableValue: 0, + description: "This setting defines minimal change (from the last reported) in measured energy that results in sending new report." + ], + [ + name: "Energy reports - periodic", key: "energyReports-Periodic", type: "boolRange", + parameterNumber: 66, size: 2, defaultValue: 3600, + range: "30..32400", disableValue: 0, + description: "This setting defines reporting interval for measured energy." + ] +]} \ No newline at end of file diff --git a/devicetypes/smartthings/child-venetian-blind.src/child-venetian-blind.groovy b/devicetypes/smartthings/child-venetian-blind.src/child-venetian-blind.groovy new file mode 100644 index 00000000000..35860d6b558 --- /dev/null +++ b/devicetypes/smartthings/child-venetian-blind.src/child-venetian-blind.groovy @@ -0,0 +1,33 @@ +/** + * Copyright 2020 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: "Child Venetian Blind", namespace: "smartthings", author: "SmartThings") { + capability "Switch Level" + capability "Actuator" + capability "Sensor" + } + + tiles(scale: 2) { + valueTile("slatsLevel", "device.level", width: 4, height: 1) { + state "level", label: 'Slats covers in ${currentValue}% ', defaultState: true + } + + main "slatsLevel" + details(["slatsLevel"]) + } +} + +def setLevel(level) { + parent.setSlats(device.deviceNetworkId, level) +} \ No newline at end of file From b4c0a6185cab2cd17f6f878611d88a4c0af5c282 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 2 Oct 2020 19:29:44 +0200 Subject: [PATCH 082/422] [ICP-13618] Qubino Flush 2 Relay - energy usage is not reported (#43446) * Added daily energy usage report * Changes in getEnergyUsage method * Moved to querying energy usage after power report --- .../qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index d0567b6b544..2f2a66ca2df 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -81,6 +81,7 @@ def installed() { if (!childDevices && state.numberOfSwitches > 1) { addChildSwitches(state.numberOfSwitches) } + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) // Preferences template begin state.currentPreferencesState = [:] @@ -255,11 +256,15 @@ private changeSwitch(endpoint, cmd) { def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { log.debug "Meter ${cmd}" + (ep ? " from endpoint $ep" : "") if (ep == 1) { - createEvent(createMeterEventMap(cmd)) + [ + createEvent(createMeterEventMap(cmd)), + response(encap(zwave.meterV3.meterGet(scale: 0x00), 1)) + ] } else if (ep) { String childDni = "${device.deviceNetworkId}:$ep" def child = childDevices.find { it.deviceNetworkId == childDni } child?.sendEvent(createMeterEventMap(cmd)) + response(encap(zwave.meterV3.meterGet(scale: 0x00), ep)) } } From 7e7e4196ed051a6ba2803e1b65329f0f3cc01a2b Mon Sep 17 00:00:00 2001 From: "ingvar.marstorp" Date: Mon, 5 Oct 2020 10:40:32 -0700 Subject: [PATCH 083/422] C2C-1027 Ecobee Capability Migration in Groovy integration Adding discrete thermostat capabilities to prepare migration to st-schema integration --- .../ecobee-thermostat.src/ecobee-thermostat.groovy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index 7e83727452b..a121f454438 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -26,6 +26,12 @@ metadata { capability "Relative Humidity Measurement" capability "Health Check" + // New discrete thermostat capabilities, added to replace "Thermostat" and prepare for migration to st-schema + capability "Thermostat Cooling Setpoint" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Fan Mode" + command "generateEvent" command "resumeProgram" command "switchMode" From 3fc2ba2722e06302e745cf36a535b5ebb5d46548 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 5 Oct 2020 14:05:19 -0700 Subject: [PATCH 084/422] CHAD-5488 Update Keen Home Smart Vent (#45127) * CHAD-5488 Update Keen Home Smart Vent includes a workaround for a bug introduced in newer vent FWs adds the draft atmospheric pressure measurement capability instead of custom attributes if the vent detects it is obstructed, it will attempt to clear its obstruction automatically * update obstruction detection * fixes and update to obstruction detection * fixup * rework configuration flow * fixup * remove obstruction handling --- .../keen-home-smart-vent.groovy | 503 +++--------------- 1 file changed, 76 insertions(+), 427 deletions(-) diff --git a/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy b/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy index bce06c5dcad..69df0f40310 100644 --- a/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy +++ b/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy @@ -1,9 +1,11 @@ +import physicalgraph.zigbee.zcl.DataType + // keen home smart vent // http://www.keenhome.io // SmartThings Device Handler v1.0.0 metadata { - definition (name: "Keen Home Smart Vent", namespace: "Keen Home", author: "Keen Home") { + definition (name: "Keen Home Smart Vent", namespace: "Keen Home", author: "Keen Home", ocfDeviceType: "x.com.st.d.vent") { capability "Switch Level" capability "Switch" capability "Configuration" @@ -12,16 +14,9 @@ metadata { capability "Temperature Measurement" capability "Battery" capability "Health Check" + capability "Atmospheric Pressure Measurement" - command "getLevel" - command "getOnOff" - command "getPressure" - command "getBattery" - command "getTemperature" - command "setZigBeeIdTile" - command "clearObstruction" - - fingerprint endpoint: "1", profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0006,0008,0020,0402,0403,0B05,FC01,FC02", outClusters: "0019", deviceJoinName: "Keen Home Vent" + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0006,0008,0020,0402,0403,0B05,FC01,FC02", outClusters: "0019", deviceJoinName: "Keen Home Vent" } // simulator metadata @@ -40,8 +35,6 @@ metadata { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { state "on", action: "switch.off", icon: "st.vents.vent-open-text", backgroundColor: "#00a0dc" state "off", action: "switch.on", icon: "st.vents.vent-closed", backgroundColor: "#ffffff" - state "obstructed", action: "clearObstruction", icon: "st.vents.vent-closed", backgroundColor: "#e86d13" - state "clearing", action: "", icon: "st.vents.vent-closed", backgroundColor: "#ffffff" } controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false) { state "level", action:"switch level.setLevel" @@ -51,423 +44,113 @@ metadata { } valueTile("temperature", "device.temperature", inactiveLabel: false) { state "temperature", label:'${currentValue}°', - backgroundColors:[ - [value: 31, 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"] - ] + 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"] + ] } valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") { state "battery", label: 'Battery \n${currentValue}%', backgroundColor:"#ffffff" } - valueTile("zigbeeId", "device.zigbeeId", inactiveLabel: true, decoration: "flat") { - state "serial", label:'${currentValue}', backgroundColor:"#ffffff" - } main "switch" details(["switch","refresh","temperature","levelSliderControl","battery"]) } } -/**** PARSE METHODS ****/ +def getPRESSURE_MEASUREMENT_CLUSTER() {0x0403} +def getMFG_CODE() {0x115B} + def parse(String description) { log.debug "description: $description" - - Map map = [:] - if (description?.startsWith('catchall:')) { - map = parseCatchAllMessage(description) - } - else if (description?.startsWith('read attr -')) { - map = parseReportAttributeMessage(description) - } - else if (description?.startsWith('temperature: ') || description?.startsWith('humidity: ')) { - map = parseCustomMessage(description) - } - else if (description?.startsWith('on/off: ')) { - map = parseOnOffMessage(description) - } - - log.debug "Parse returned $map" - return map ? createEvent(map) : null -} - -private Map parseCatchAllMessage(String description) { - log.debug "parseCatchAllMessage" - - def cluster = zigbee.parse(description) - log.debug "cluster: ${cluster}" - if (shouldProcessMessage(cluster)) { - log.debug "processing message" - switch(cluster.clusterId) { - case 0x0001: - return makeBatteryResult(cluster.data.last()) - break - - case 0x0402: - // temp is last 2 data values. reverse to swap endian - String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() - def value = convertTemperatureHex(temp) - return makeTemperatureResult(value) - break - - case 0x0006: - return makeOnOffResult(cluster.data[-1]) - break + def event = zigbee.getEvent(description) + if (!event) { + Map descMap = zigbee.parseDescriptionAsMap(description) + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.attrInt == 0x0021) { + event = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) + } else if (descMap?.clusterInt == PRESSURE_MEASUREMENT_CLUSTER && descMap.attrInt == 0x0020) { + // manufacturer-specific attribute + event = getPressureResult(Integer.parseInt(descMap.value, 16)) + } + } else if (event.name == "level") { + if (event.value > 0 && device.currentValue("switch") == "off") { + sendEvent([name: "switch", value: "on"]) } } - return [:] -} - -private boolean shouldProcessMessage(cluster) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - if (cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e)) { - return false - } - - return true -} - -private Map parseReportAttributeMessage(String description) { - log.debug "parseReportAttributeMessage" - - Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param -> - def nameAndValue = param.split(":") - map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] - } - log.debug "Desc Map: $descMap" - - if (descMap.cluster == "0006" && descMap.attrId == "0000") { - return makeOnOffResult(Int.parseInt(descMap.value)); - } - else if (descMap.cluster == "0008" && descMap.attrId == "0000") { - return makeLevelResult(descMap.value) - } - else if (descMap.cluster == "0402" && descMap.attrId == "0000") { - def value = convertTemperatureHex(descMap.value) - return makeTemperatureResult(value) - } - else if (descMap.cluster == "0001" && descMap.attrId == "0021") { - return makeBatteryResult(Integer.parseInt(descMap.value, 16)) - } - else if (descMap.cluster == "0403" && descMap.attrId == "0020") { - return makePressureResult(Integer.parseInt(descMap.value, 16)) - } - else if (descMap.cluster == "0000" && descMap.attrId == "0006") { - return makeSerialResult(new String(descMap.value.decodeHex())) - } - - // shouldn't get here - return [:] -} - -private Map parseCustomMessage(String description) { - Map resultMap = [:] - if (description?.startsWith('temperature: ')) { - def tempData = description.split(": ")[1].split(" ") - def scale = (tempData.length > 1) ? tempData[1] : "C" - def value = Double.parseDouble(tempData[0]) - resultMap = makeTemperatureResult(convertTemperature(value, scale)) - } - return resultMap -} - -private Map parseOnOffMessage(String description) { - Map resultMap = [:] - if (description?.startsWith('on/off: ')) { - def value = Integer.parseInt(description - "on/off: ") - resultMap = makeOnOffResult(value) - } - return resultMap -} - -private Map makeOnOffResult(rawValue) { - log.debug "makeOnOffResult: ${rawValue}" - def linkText = getLinkText(device) - def value = rawValue == 1 ? "on" : "off" - return [ - name: "switch", - value: value, - descriptionText: "${linkText} is ${value}" - ] + log.debug "parsed event: $event" + createEvent(event) } -private Map makeLevelResult(rawValue) { - def linkText = getLinkText(device) - def value = Integer.parseInt(rawValue, 16) - def rangeMax = 254 +def getBatteryPercentageResult(rawValue) { + // reports raw percentage, not 2x + def result = [:] - // catch obstruction level - if (value == 255) { - log.debug "${linkText} is obstructed" - // Just return here. Once the vent is power cycled - // it will go back to the previous level before obstruction. - // Therefore, no need to update level on the display. - return [ - name: "switch", - value: "obstructed", - descriptionText: "${linkText} is obstructed. Please power cycle." - ] + if (0 <= rawValue && rawValue <= 100) { + result.name = 'battery' + result.translatable = true + result.descriptionText = "${device.displayName} battery was ${rawValue}%" + result.value = Math.round(rawValue) } - value = Math.floor(value / rangeMax * 100) - - return [ - name: "level", - value: value, - descriptionText: "${linkText} level is ${value}%" - ] -} - -private Map makePressureResult(rawValue) { - log.debug 'makePressureResut' - def linkText = getLinkText(device) - - def pascals = rawValue / 10 - def result = [ - name: 'pressure', - descriptionText: "${linkText} pressure is ${pascals}Pa", - value: pascals - ] - return result } -private Map makeBatteryResult(rawValue) { - // log.debug 'makeBatteryResult' - def linkText = getLinkText(device) - - // log.debug - [ - name: 'battery', - value: rawValue, - descriptionText: "${linkText} battery is at ${rawValue}%" - ] -} - -private Map makeTemperatureResult(value) { - // log.debug 'makeTemperatureResult' - def linkText = getLinkText(device) - - // log.debug "tempOffset: ${tempOffset}" - if (tempOffset) { - def offset = tempOffset as int - // log.debug "offset: ${offset}" - def v = value as int - // log.debug "v: ${v}" - value = v + offset - // log.debug "value: ${value}" - } - - return [ - name: 'temperature', - value: "" + value, - descriptionText: "${linkText} is ${value}°${temperatureScale}", - unit: temperatureScale - ] -} - -/**** HELPER METHODS ****/ -private def convertTemperatureHex(value) { - // log.debug "convertTemperatureHex(${value})" - def celsius = Integer.parseInt(value, 16).shortValue() / 100 - // log.debug "celsius: ${celsius}" - - return convertTemperature(celsius, "C") -} - -private def convertTemperature(value, scale = "C") { - if(getTemperatureScale() == scale){ - return Math.round(value * 100) / 100 - } else { - if (scale == "C") { - // Celsius to Fahrenheit - return Math.round(celsiusToFahrenheit(value) * 100) /100 - } - // Fahrenheit to Celsius - return Math.round(fahrenheitToCelsius(value) * 100) /100 - } -} - -private def makeSerialResult(serial) { - log.debug "makeSerialResult: " + serial - - def linkText = getLinkText(device) - sendEvent([ - name: "serial", - value: serial, - descriptionText: "${linkText} has serial ${serial}" ]) - return [ - name: "serial", - value: serial, - descriptionText: "${linkText} has serial ${serial}" ] -} - -// takes a level from 0 to 100 and translates it to a ZigBee move to level with on/off command -private def makeLevelCommand(level) { - def rangeMax = 254 - def scaledLevel = Math.round(level * rangeMax / 100) - log.debug "scaled level for ${level}%: ${scaledLevel}" - - // convert to hex string and pad to two digits - def hexLevel = new BigInteger(scaledLevel.toString()).toString(16).padLeft(2, '0') - - "st cmd 0x${device.deviceNetworkId} 1 8 4 {${hexLevel} 0000}" +def getPressureResult(rawValue) { + def kpa = rawValue / (10 * 1000) // reports are in deciPascals + return [name: "atmosphericPressure", value: kpa, unit: "kPa"] } /**** COMMAND METHODS ****/ def on() { - def linkText = getLinkText(device) - log.debug "open ${linkText}" - - // only change the state if the vent is not obstructed - if (device.currentValue("switch") == "obstructed") { - log.error("cannot open because ${linkText} is obstructed") - return + def cmds = [] + def currentLevel = device.currentValue("level") + if (currentLevel != null) { + currentLevel = currentLevel as int } - - sendEvent(makeOnOffResult(1)) - "st cmd 0x${device.deviceNetworkId} 1 6 1 {}" + def levelToSet = currentLevel ? currentLevel : 100 + cmds << zigbee.setLevel(levelToSet) } def off() { - def linkText = getLinkText(device) - log.debug "close ${linkText}" - - // only change the state if the vent is not obstructed - if (device.currentValue("switch") == "obstructed") { - log.error("cannot close because ${linkText} is obstructed") - return - } - - sendEvent(makeOnOffResult(0)) - "st cmd 0x${device.deviceNetworkId} 1 6 0 {}" -} - -def clearObstruction() { - def linkText = getLinkText(device) - log.debug "attempting to clear ${linkText} obstruction" - - sendEvent([ - name: "switch", - value: "clearing", - descriptionText: "${linkText} is clearing obstruction" - ]) - - // send a move command to ensure level attribute gets reset for old, buggy firmware - // then send a reset to factory defaults - // finally re-configure to ensure reports and binding is still properly set after the rtfd - [ - makeLevelCommand(device.currentValue("level")), "delay 500", - "st cmd 0x${device.deviceNetworkId} 1 0 0 {}", "delay 5000" - ] + configure() + zigbee.off() } def setLevel(value, rate = null) { log.debug "setting level: ${value}" - def linkText = getLinkText(device) - - // only change the level if the vent is not obstructed - def currentState = device.currentValue("switch") - - if (currentState == "obstructed") { - log.error("cannot set level because ${linkText} is obstructed") - return - } - - sendEvent(name: "level", value: value) - if (value > 0) { - sendEvent(name: "switch", value: "on", descriptionText: "${linkText} is on by setting a level") - } - else { - sendEvent(name: "switch", value: "off", descriptionText: "${linkText} is off by setting level to 0") - } - - makeLevelCommand(value) -} - -def getOnOff() { - log.debug "getOnOff()" - - // disallow on/off updates while vent is obstructed - if (device.currentValue("switch") == "obstructed") { - log.error("cannot update open/close status because ${getLinkText(device)} is obstructed") - return [] - } - - ["st rattr 0x${device.deviceNetworkId} 1 0x0006 0"] -} - -def getPressure() { - log.debug "getPressure()" - - // using a Keen Home specific attribute in the pressure measurement cluster - [ - "zcl mfg-code 0x115B", "delay 200", - "zcl global read 0x0403 0x20", "delay 200", - "send 0x${device.deviceNetworkId} 1 1", "delay 200" - ] -} - -def getLevel() { - log.debug "getLevel()" - - // disallow level updates while vent is obstructed - if (device.currentValue("switch") == "obstructed") { - log.error("cannot update level status because ${getLinkText(device)} is obstructed") - return [] - } - - ["st rattr 0x${device.deviceNetworkId} 1 0x0008 0x0000"] -} - -def getTemperature() { - log.debug "getTemperature()" - - ["st rattr 0x${device.deviceNetworkId} 1 0x0402 0"] -} - -def getBattery() { - log.debug "getBattery()" - - ["st rattr 0x${device.deviceNetworkId} 1 0x0001 0x0021"] -} - -def setZigBeeIdTile() { - log.debug "setZigBeeIdTile() - ${device.zigbeeId}" - - def linkText = getLinkText(device) - - sendEvent([ - name: "zigbeeId", - value: device.zigbeeId, - descriptionText: "${linkText} has zigbeeId ${device.zigbeeId}" ]) - return [ - name: "zigbeeId", - value: device.zigbeeId, - descriptionText: "${linkText} has zigbeeId ${device.zigbeeId}" ] + def cmds = [] + cmds << zigbee.setLevel(value) + cmds << "delay 1000" + cmds << zigbee.levelRefresh() + cmds } def refresh() { - getOnOff() + - getLevel() + - getTemperature() + - getPressure() + - getBattery() + zigbee.onOffRefresh() + + zigbee.levelRefresh() + + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + + zigbee.readAttribute(PRESSURE_MEASUREMENT_CLUSTER, 0x0020, [mfgCode: MFG_CODE]) + + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } /** * PING is used by Device-Watch in attempt to reach the Device * */ def ping() { - return refresh() + zigbee.levelRefresh() } def configure() { @@ -477,47 +160,13 @@ def configure() { // enrolls with default periodic reporting until newer 5 min interval is confirmed sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - // get ZigBee ID by hidden tile because that's the only way we can do it - setZigBeeIdTile() - - def configCmds = [ - // bind reporting clusters to hub - //commenting out switch cluster bind as using wrapper onOffConfig of zigbee class - //"zdo bind 0x${device.deviceNetworkId} 1 1 0x0006 {${device.zigbeeId}} {}", "delay 500", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x0008 {${device.zigbeeId}} {}", "delay 500", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x0402 {${device.zigbeeId}} {}", "delay 500", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x0403 {${device.zigbeeId}} {}", "delay 500", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x0001 {${device.zigbeeId}} {}", "delay 500" - - // configure report commands - // zcl global send-me-a-report [cluster] [attr] [type] [min-interval] [max-interval] [min-change] - - // report with these parameters is preconfigured in firmware, can be overridden here - // vent on/off state - type: boolean, change: 1 - // "zcl global send-me-a-report 6 0 0x10 5 60 {01}", "delay 200", - // "send 0x${device.deviceNetworkId} 1 1", "delay 1500", - - // report with these parameters is preconfigured in firmware, can be overridden here - // vent level - type: int8u, change: 1 - // "zcl global send-me-a-report 8 0 0x20 5 60 {01}", "delay 200", - // "send 0x${device.deviceNetworkId} 1 1", "delay 1500", - - // report with these parameters is preconfigured in firmware, can be overridden here - // temperature - type: int16s, change: 0xA = 10 = 0.1C - // "zcl global send-me-a-report 0x0402 0 0x29 60 60 {0A00}", "delay 200", - // "send 0x${device.deviceNetworkId} 1 1", "delay 1500", - - // report with these parameters is preconfigured in firmware, can be overridden here - // keen home custom pressure (tenths of Pascals) - type: int32u, change: 1 = 0.1Pa - // "zcl mfg-code 0x115B", "delay 200", - // "zcl global send-me-a-report 0x0403 0x20 0x22 60 60 {010000}", "delay 200", - // "send 0x${device.deviceNetworkId} 1 1", "delay 1500", - - // report with these parameters is preconfigured in firmware, can be overridden here - // battery - type: int8u, change: 1 - // "zcl global send-me-a-report 1 0x21 0x20 60 3600 {01}", "delay 200", - // "send 0x${device.deviceNetworkId} 1 1", "delay 1500", + def cmds = [ + zigbee.temperatureConfig(30, 300) + + zigbee.onOffConfig() + + zigbee.addBinding(zigbee.LEVEL_CONTROL_CLUSTER) + + zigbee.addBinding(PRESSURE_MEASUREMENT_CLUSTER) + + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 600, 21600, 0x01) // battery precentage ] - return configCmds + zigbee.onOffConfig() + refresh() + return refresh() + delayBetween(cmds) } From 13d998effbb9987a2c3b6c4d19f75c93e3245fb8 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 5 Oct 2020 17:30:10 -0700 Subject: [PATCH 085/422] CHAD-5555 Update z-wave device multichannel to work in oneapp (#46568) * CHAD-5555 Update z-wave device multichannel to work in oneapp Because of its variable number of components, the child devices cannot be added as components. * add updated method to convert legacy implementations --- .../zwave-device-multichannel.groovy | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 740042d126e..a017e88a776 100644 --- a/devicetypes/smartthings/zwave-device-multichannel.src/zwave-device-multichannel.groovy +++ b/devicetypes/smartthings/zwave-device-multichannel.src/zwave-device-multichannel.groovy @@ -99,8 +99,7 @@ def installed() { try { String dni = "${device.deviceNetworkId}-ep${num}" addChildDevice(typeName, dni, device.hub.id, - [completedSetup: true, label: "${device.displayName} ${componentLabel}", - isComponent: true, componentName: "ch${num}", componentLabel: "${componentLabel}"]) + [completedSetup: true, label: "${device.displayName} ${componentLabel}", isComponent: false]) // enabledEndpoints << num.toString() log.debug "Endpoint $num ($desc) added as $componentLabel" } catch (e) { @@ -435,3 +434,9 @@ private encap(cmd, endpoint) { private encapWithDelay(commands, endpoint, delay=200) { delayBetween(commands.collect{ encap(it, endpoint) }, delay) } + +def updated() { + childDevices.each { + if (it.device.isComponent) { it.save([isComponent: false, componentLabel: null, componentName: null]) } + } +} From e868a83e8990ff882bc1a5b3f7b548f6ea934840 Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Tue, 6 Oct 2020 14:31:43 -0700 Subject: [PATCH 086/422] Recessed Door Sensor 7 device type update (#46572) * Update def configure() Lines 136 - 140 - configure Recessed Door Sensor 7 upon pairing to enable Binary Sensor Report - fixes issue where Open/Close status does not update when paired using S2 Authentication * Update state check Line 137 changed to zwaveInfo.mfr == "0371" && zwaveInfo.model == "00BB" --- .../zwave-door-window-sensor.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 566decd9772..1e73e6afc22 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 @@ -133,7 +133,11 @@ def updated() { } def configure() { - // currently supported devices do not require initial configuration + //Recessed Door Sensor 7 - Enable Binary Sensor Report for S2 Authenticated + if (zwaveInfo.mfr == "0371" || zwaveInfo.model == "00BB") { + result << response(command(zwave.configurationV1.configurationSet(parameterNumber: 1, size: 1, scaledConfigurationValue: 1))) + result + } } def sensorValueEvent(value) { From c0981b2b3cd6612bf71b51f73ca220c71069ec06 Mon Sep 17 00:00:00 2001 From: dwd-kwon <59678391+dwd-kwon@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:50:25 +0900 Subject: [PATCH 087/422] DevWs for DAWON DNS containing containing Zigbee Metering Plug and 2 more (#43376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for DAWON DNS containing containing Zigbee Metering Plug and 2 more * 1. Changed deviceJoinName(ST Dawon Outlet -> Dawon Outlet). 2. Changed the structure of getEnergyDiv(). 3. Added isDawonOutlet() . * re-coding * Changed deviceJoinName. * re-coding * Changed deviceJoinName * original Public DTH * add * master DTH * Update zigbee-metering-plug.groovy Add Dawon Outlet * Update zigbee-switch.groovy remove empty line * Update zigbee-multi-switch.groovy remove empty line * Update zigbee-metering-plug.groovy remove command "reset" * Update zigbee-metering-plug.groovy remove empty line * Update zigbee-switch.groovy remove empty line * Update zigbee-switch.groovy * Update zigbee-metering-plug.groovy remove reset Co-authored-by: 남수 허 --- .../zigbee-metering-plug.groovy | 18 ++++++++++++++++-- .../zigbee-multi-switch.groovy | 6 +++++- .../zigbee-switch.src/zigbee-switch.groovy | 4 +++- 3 files changed, 24 insertions(+), 4 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy diff --git a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy old mode 100755 new mode 100644 index a680e7c4900..e2e81d2b622 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -33,6 +33,10 @@ metadata { fingerprint manufacturer: "DAWON_DNS", model: "PM-B430-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug PM-B430-ZB (10A), raw description: 01 0104 0051 01 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 fingerprint manufacturer: "DAWON_DNS", model: "PM-B530-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug PM-B530-ZB (16A), raw description: 01 0104 0051 01 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 fingerprint manufacturer: "DAWON_DNS", model: "PM-C140-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet PM-C140-ZB, raw description: 01 0104 0051 01 0A 0000 0002 0003 0004 0006 0019 0702 0B04 0008 0009 0A 0000 0002 0003 0004 0006 0019 0702 0B04 0008 0009 + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-B540-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "ST-B550-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C150-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet + fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C250-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet } tiles(scale: 2){ @@ -162,9 +166,19 @@ private int getPowerDiv() { } private int getEnergyDiv() { - isSengledOutlet() ? 10000 : 100 + if (isDawonOutlet()) { + 1000 + } else if (isSengledOutlet()) { + 10000 + } else { + 100 + } } private boolean isSengledOutlet() { device.getDataValue("model") == "E1C-NB7" -} \ No newline at end of file +} + +private boolean isDawonOutlet() { + device.getDataValue("manufacturer") == "DAWON_DNS" +} diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index a77587676f8..e192b1ef805 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -48,6 +48,10 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S240R-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S240R-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S340-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S340-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S340R-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S340R-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S250-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S250-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S350-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S350-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S250-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch ST-S250-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S350-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch ST-S350-ZB // eWeLink // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 @@ -245,7 +249,7 @@ private getChildCount() { return 5 } else if (device.getDataValue("model") == "E220-KR6N0Z0-HA") { return 6 - } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB") { + } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB" || device.getDataValue("model") == "PM-S350-ZB" || device.getDataValue("model") == "ST-S350-ZB") { return 3 } else { return 2 diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index c4c0ae9061f..e53187e617c 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -96,6 +96,8 @@ metadata { // Dawon fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0006, 0019, 0002, 0009", manufacturer: "DAWON_DNS", model: "PM-S140-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S140-ZB fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0006, 0019, 0002, 0009", manufacturer: "DAWON_DNS", model: "PM-S140R-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S140R-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S150-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S150-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S150-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch ST-S150-ZB } // simulator metadata @@ -163,4 +165,4 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() -} \ No newline at end of file +} From 773b916e33fdba5a6dcf8434e97222c058387e05 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Wed, 7 Oct 2020 20:31:51 +0200 Subject: [PATCH 088/422] WWST-6752 Fingerprint for Yale Fingerprint Lock YDF40 (#43948) --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index bdb80053f75..0cecae3e178 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -46,6 +46,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0009, 0020, 0101, 0B05, FC00", outClusters: "000A, 0019", manufacturer: "Schlage", model: "BE468", deviceJoinName: "Schlage Door Lock" //Schlage Connect Smart Deadbolt fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020,0B05", outClusters: "000A,0019", manufacturer: "Yale", model: "YDD-D4F0 TSDB", deviceJoinName: "Lockwood Door Lock" //Lockwood Smart Lock fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000202", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YDF40 } tiles(scale: 2) { From 59fa8cde563999ea9707546f0602027e49d77e2f Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 8 Oct 2020 20:16:24 +0200 Subject: [PATCH 089/422] ICP-13708 Added proper fingerprint for Yale YMF40 (#46734) --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 0cecae3e178..cc413f1fe5b 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -45,8 +45,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0101", manufacturer:"Kwikset", model:"Smartcode", deviceJoinName: "Kwikset Door Lock" //Kwikset Smartcode Lock fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0009, 0020, 0101, 0B05, FC00", outClusters: "000A, 0019", manufacturer: "Schlage", model: "BE468", deviceJoinName: "Schlage Door Lock" //Schlage Connect Smart Deadbolt fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020,0B05", outClusters: "000A,0019", manufacturer: "Yale", model: "YDD-D4F0 TSDB", deviceJoinName: "Lockwood Door Lock" //Lockwood Smart Lock - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Locks (YDF30/40, YMF30/40) with old firmware (v.9.0) fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000202", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YDF40 + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000001", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 } tiles(scale: 2) { From 9299107aaa10e1acb11ca00883266f94521e923b Mon Sep 17 00:00:00 2001 From: dwd-kwon <59678391+dwd-kwon@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:50:25 +0900 Subject: [PATCH 090/422] DevWs for DAWON DNS containing containing Zigbee Metering Plug and 2 more (#43376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for DAWON DNS containing containing Zigbee Metering Plug and 2 more * 1. Changed deviceJoinName(ST Dawon Outlet -> Dawon Outlet). 2. Changed the structure of getEnergyDiv(). 3. Added isDawonOutlet() . * re-coding * Changed deviceJoinName. * re-coding * Changed deviceJoinName * original Public DTH * add * master DTH * Update zigbee-metering-plug.groovy Add Dawon Outlet * Update zigbee-switch.groovy remove empty line * Update zigbee-multi-switch.groovy remove empty line * Update zigbee-metering-plug.groovy remove command "reset" * Update zigbee-metering-plug.groovy remove empty line * Update zigbee-switch.groovy remove empty line * Update zigbee-switch.groovy * Update zigbee-metering-plug.groovy remove reset Co-authored-by: 남수 허 --- .../zigbee-metering-plug.groovy | 18 ++++++++++++++++-- .../zigbee-multi-switch.groovy | 6 +++++- .../zigbee-switch.src/zigbee-switch.groovy | 4 +++- 3 files changed, 24 insertions(+), 4 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy diff --git a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy old mode 100755 new mode 100644 index a680e7c4900..e2e81d2b622 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -33,6 +33,10 @@ metadata { fingerprint manufacturer: "DAWON_DNS", model: "PM-B430-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug PM-B430-ZB (10A), raw description: 01 0104 0051 01 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 fingerprint manufacturer: "DAWON_DNS", model: "PM-B530-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug PM-B530-ZB (16A), raw description: 01 0104 0051 01 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 fingerprint manufacturer: "DAWON_DNS", model: "PM-C140-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet PM-C140-ZB, raw description: 01 0104 0051 01 0A 0000 0002 0003 0004 0006 0019 0702 0B04 0008 0009 0A 0000 0002 0003 0004 0006 0019 0702 0B04 0008 0009 + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-B540-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "ST-B550-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C150-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet + fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C250-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet } tiles(scale: 2){ @@ -162,9 +166,19 @@ private int getPowerDiv() { } private int getEnergyDiv() { - isSengledOutlet() ? 10000 : 100 + if (isDawonOutlet()) { + 1000 + } else if (isSengledOutlet()) { + 10000 + } else { + 100 + } } private boolean isSengledOutlet() { device.getDataValue("model") == "E1C-NB7" -} \ No newline at end of file +} + +private boolean isDawonOutlet() { + device.getDataValue("manufacturer") == "DAWON_DNS" +} diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index a77587676f8..e192b1ef805 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -48,6 +48,10 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S240R-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S240R-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S340-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S340-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S340R-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S340R-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S250-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S250-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S350-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S350-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S250-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch ST-S250-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002,0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S350-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch ST-S350-ZB // eWeLink // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 @@ -245,7 +249,7 @@ private getChildCount() { return 5 } else if (device.getDataValue("model") == "E220-KR6N0Z0-HA") { return 6 - } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB") { + } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB" || device.getDataValue("model") == "PM-S350-ZB" || device.getDataValue("model") == "ST-S350-ZB") { return 3 } else { return 2 diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index c4c0ae9061f..e53187e617c 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -96,6 +96,8 @@ metadata { // Dawon fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0006, 0019, 0002, 0009", manufacturer: "DAWON_DNS", model: "PM-S140-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S140-ZB fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0006, 0019, 0002, 0009", manufacturer: "DAWON_DNS", model: "PM-S140R-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S140R-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S150-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S150-ZB + fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S150-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch ST-S150-ZB } // simulator metadata @@ -163,4 +165,4 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() -} \ No newline at end of file +} From 82d9d94048f1298b3d71910fc42232bf844138ed Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Fri, 9 Oct 2020 11:50:55 -0700 Subject: [PATCH 091/422] ICP-12798 Update Doorbell Siren 6 to support Sound/Volume control (#44305) * Update aeotec-doorbell-siren-6.groovy - Added setting/preference option to configure volume/sound via Multichannel Cmd Encap (Sound Switch Configuration SET) * Update code format Format fixes - zwave command classes use hex values for their input - device preferences and def variables changed to have initial lowercase letters - open braces are now on same line as if statements - closing braces are now on the same line as following else statements - fixed minor formatting issues Code change - separated multichannel encap as mcEncap() and encap() to handle Multichannel Cmd Encap (Sound Switch Configuration Set) separately * Quick update Remove 0x79: 1 from secureencapsulation and encapsulation. * Condense private mcEncap() Condensed code to use response(cmd) for MC Cmd Encap (Sound Switch Config SET). * just a comment edit Non-essential commit, changed commend on line 336 * added return to line 336 - Add return in front of line 336 * Final update for minor changes - Changed steps in description on line 57 for better user flow. * Fixed child device status update - Fixed issue where child devices were not updating. Button press and tamper child devices now change states. - Edited Line 162 -164 to set state.lastTriggeredSound when a new sound is playing from a different endpoint. --- .../aeotec-doorbell-siren-6.groovy | 129 ++++++++++++++---- 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy index 5a0e53ee343..74666828740 100644 --- a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy +++ b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy @@ -23,10 +23,10 @@ metadata { fingerprint mfr: "0371", prod: "0003", model: "00A2", deviceJoinName: "Aeotec Doorbell", ocfDeviceType: "x.com.st.d.doorbell" //EU //Aeotec Doorbell 6 fingerprint mfr: "0371", prod: "0103", model: "00A2", deviceJoinName: "Aeotec Doorbell", ocfDeviceType: "x.com.st.d.doorbell" //US //Aeotec Doorbell 6 + fingerprint mfr: "0371", prod: "0203", model: "00A2", deviceJoinName: "Aeotec Doorbell", ocfDeviceType: "x.com.st.d.doorbell" //AU //Aeotec Doorbell 6 fingerprint mfr: "0371", prod: "0003", model: "00A4", deviceJoinName: "Aeotec Siren", ocfDeviceType: "x.com.st.d.siren" //EU //Aeotec Siren 6 fingerprint mfr: "0371", prod: "0103", model: "00A4", deviceJoinName: "Aeotec Siren", ocfDeviceType: "x.com.st.d.siren" //US //Aeotec Siren 6 fingerprint mfr: "0371", prod: "0203", model: "00A4", deviceJoinName: "Aeotec Siren", ocfDeviceType: "x.com.st.d.siren" //AU //Aeotec Siren 6 - fingerprint mfr: "0371", prod: "0203", model: "00A2", deviceJoinName: "Aeotec Doorbell", ocfDeviceType: "x.com.st.d.doorbell" //AU //Aeotec Doorbell 6 } tiles { @@ -50,13 +50,56 @@ metadata { main "alarm" details(["alarm", "off", "tamper", "refresh"]) } + + preferences { + section { + input(title: "Control Sound and Volume", + description: "Follow these steps to adjust sound/volume: 1. Set Endpoint, 2. Set Volume, 3. Set Sound, 4. Toggle button to ON, 5. Wait five seconds before new configuration or use, 6. Close setting page to refresh button or toggle button OFF", + displayDuringSetup: false, + type: "paragraph", + element: "paragraph") + + //Endpoint variable + input(title: "Endpoint Explanation", + description: "Determines which endpoint to control. 1 = Browse, 2 = Tamper, 3 = Button one, 4 = Button two, 5 = Button three, 6 = Environment, 7 = Security, 8 = Emergency", + displayDuringSetup: false, + type: "paragraph", + element: "paragraph") + input("sirenDoorbellEndpoint", "number", + title: "1. Endpoint", + default: 2, + range: "1..8", + displayDuringSetup: false) + + //Volume level variable + input("sirenDoorbellVolume", "number", + title: "2. Volume set in %", + default: 30, + range: "0..100", + displayDuringSetup: false) + + //SoundID variable + input("sirenDoorbellSound", "number", + title: "3. Sound #", + default: 17, + range: "1..30", + displayDuringSetup: false) + + //Will not send sound/volume to reduce z-wave traffic until (SirenDoorbellSend == true) + //SirenDoorbellSend will toggle back to false when settings page is closed. + input("sirenDoorbellSend", "bool", + title: "4. Send sound and volume configuration", + default: false, + displayDuringSetup: false) + } + } } private getNumberOfSounds() { def numberOfSounds = [ - "0003" : 8, //Aeotec Doorbell/Siren EU - "0103" : 8, //Aeotec Doorbell/Siren US - "0203" : 8 //Aeotec Doorbell/Siren AU + "0003" : 8, //Aeotec Doorbell/Siren EU + "0103" : 8, //Aeotec Doorbell/Siren US + "0203" : 8 //Aeotec Doorbell/Siren AU ] return numberOfSounds[zwaveInfo.prod] ?: 1 } @@ -66,10 +109,23 @@ def installed() { sendEvent(name: "alarm", value: "off", isStateChange: true, displayed: false) sendEvent(name: "chime", value: "off", isStateChange: true, displayed: false) sendEvent(name: "tamper", value: "clear", isStateChange: true, displayed: false) + soundControl(2, 30, 17) //adjust the tamper volume to be lower than default when initially paired. } def updated() { initialize() + //keep Z-Wave traffic low, requires bool button in setting to trigger. + if (sirenDoorbellSend == true) { + soundControl(sirenDoorbellEndpoint, sirenDoorbellVolume, sirenDoorbellSound) + } +} + +def soundControl(endpoint, volume, sound) { + if (endpoint && volume && sound) { + mcEncap(zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint, commandClass:0x79, command:0x05, parameter: [volume,sound])) + } else { + log.debug "endpoint, volume, or sound settings is null" + } } def initialize() { @@ -89,7 +145,8 @@ def parse(String description) { return result } -def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + log.debug ""+cmd if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { // Supervision encapsulated Message // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header cmd.parameter = cmd.parameter.drop(2) @@ -98,15 +155,21 @@ def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd.command = cmd.parameter[1] cmd.parameter = cmd.parameter.drop(2) } - def encapsulatedCommand = cmd.encapsulatedCommand() + + def encapsulatedCommand = cmd.encapsulatedCommand([0x60: 3]) def endpoint = cmd.sourceEndPoint + + if (cmd.commandClass == 0x71) { + state.lastTriggeredSound = endpoint //notification cc determines sound is triggered + } + if (endpoint == state.lastTriggeredSound && encapsulatedCommand != null) { - zwaveEvent(encapsulatedCommand) + return zwaveEvent(encapsulatedCommand) } } def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { - def securedEncapsulatedCommand = cmd.securedEncapsulatedCommand() + def securedEncapsulatedCommand = cmd.securedEncapsulatedCommand([0x60: 3]) if (securedEncapsulatedCommand) { zwaveEvent(securedEncapsulatedCommand) } else { @@ -148,7 +211,7 @@ def chime() { def ping() { def cmds = [ - encap(zwave.basicV1.basicGet()) + encap(zwave.basicV1.basicGet()) ] sendHubCommand(cmds) } @@ -171,11 +234,11 @@ private addChildren(numberOfSounds) { String childDni = "${device.deviceNetworkId}:$endpoint" addChildDevice("Aeotec Doorbell Siren Child", childDni, device.getHub().getId(), [ - completedSetup: true, - label : "$device.displayName Sound $endpoint", - isComponent : true, - componentName : "sound$endpoint", - componentLabel: "Sound $endpoint" + completedSetup: true, + label : "$device.displayName Sound $endpoint", + isComponent : true, + componentName : "sound$endpoint", + componentLabel: "Sound $endpoint" ]) } catch (Exception e) { log.debug "Excep: ${e} " @@ -183,19 +246,6 @@ private addChildren(numberOfSounds) { } } -private encap(cmd, endpoint = null) { - if (cmd) { - if (endpoint && endpoint > 1) { - cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) - } - if (zwaveInfo.zw.contains("s")) { - zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() - } else { - cmd.format() - } - } -} - def channelNumber(String dni) { dni[-1] as Integer } @@ -276,3 +326,28 @@ def keepChildrenOnline() { child?.sendEvent(name: "alarm", value: "off") } } + +private encap(cmd, endpoint = null) { + if (cmd) { + log.debug "encap: "+cmd + if (endpoint && endpoint > 1) { + cmd = zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd) + } + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } + } +} + +private mcEncap(cmd) { + if (cmd) { + if (zwaveInfo.zw.contains("s")) { + device.updateSetting("sirenDoorbellSend", [value:"false",type:"bool"]) //reset preference toggle button when leaving setting page + return response(zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()) //used to process Sound Switch Configuration SET + } else { + cmd.format() + } + } +} From c110f4551a2ab6f8fa39dce3d497f0b320842bf4 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Tue, 13 Oct 2020 07:58:24 -0700 Subject: [PATCH 092/422] CHAD-5552, CHAD-5553, CHAD-5554 Update legacy MCD devices to properly work as MCD (#47247) * CHAD-5552, CHAD-5553, CHAD-5554 Update legacy MCD devices to properly work as MCD * Logic updates --- .../inovelli-2-channel-smart-plug.groovy | 24 ++++++++++++++++++- .../aeon-key-fob.src/aeon-key-fob.groovy | 4 ++-- .../zooz-power-strip.groovy | 2 +- 3 files changed, 26 insertions(+), 4 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 5d60c505c1f..f20d7636a7e 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 @@ -209,7 +209,9 @@ def ping() { def installed() { logging("installed()", 1) command(zwave.manufacturerSpecificV1.manufacturerSpecificGet()) - createChildDevices() + if (!childDevices) { // Clicking "Update" from the Graph IDE calls installed(), so protect against trying to recreate children. + createChildDevices() + } } def updated() { logging("updated()", 1) @@ -226,6 +228,26 @@ def updated() { } sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) sendEvent(name: "needUpdate", value: device.currentValue("needUpdate"), displayed: false, isStateChange: true) + + migrate() +} +def migrate() { + log.info "Migrating to MCD DTH" + + childDevices.each { + def i = it.deviceNetworkId[-1] + + log.info "Migrating child ${i} from ${it.componentName} to outlet${i}" + + it.save([deviceNetworkId: "${device.deviceNetworkId}:${i}", + label: "${device.displayName} Outlet ${i}", + isComponent: true, + componentName: "outlet$i", + componentLabel: "Outlet $i"]) + it.setDeviceType("smartthings", "Child Switch Health") + } + + setDeviceType("erocm123", "Inovelli 2-Channel Smart Plug MCD") } 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) 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 b2b0e9e2c29..5c54b5e3e51 100644 --- a/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy +++ b/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy @@ -13,7 +13,7 @@ import groovy.json.JsonOutput * */ metadata { - definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, ocfDeviceType: "x.com.st.d.remotecontroller") { + definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, ocfDeviceType: "x.com.st.d.remotecontroller", mnmn: "SmartThings", vid: "generic-4-button", mcdSync: true) { capability "Actuator" capability "Button" capability "Holdable Button" @@ -169,7 +169,7 @@ def installed() { initialize() Integer buttons = (device.currentState("numberOfButtons").value).toBigInteger() - if (buttons > 1) { + if (buttons > 1 && !childDevices) { // Clicking "Update" from the Graph IDE calls installed(), so protect against trying to recreate children. createChildDevices() } } 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 34dbe6a37aa..b757d703424 100644 --- a/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy +++ b/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy @@ -20,7 +20,7 @@ * */ metadata { - definition (name: "Zooz Power Strip", namespace: "smartthings", author: "SmartThings") { + definition (name: "Zooz Power Strip", namespace: "smartthings", author: "SmartThings", mcdSync: true) { capability "Switch" capability "Refresh" capability "Actuator" From 260ac60b29351947b5738818832c16560d2c074c Mon Sep 17 00:00:00 2001 From: habhomegit <42719976+habhomegit@users.noreply.github.com> Date: Tue, 13 Oct 2020 11:26:29 -0500 Subject: [PATCH 093/422] DevWs for HAB Home Intelligence containing containing iblinds v3 (#42736) * DevWs for HAB Home Intelligence containing containing iblinds v3 * Bring v3 code in to other DTH and protect with device model check * Remove extra space. Ooops! * Fix exceptions * Add protection against exceptions when configuring Co-authored-by: Eric Barnett Co-authored-by: Donald Kirker --- .../iblinds-zwave.src/iblinds-zwave.groovy | 148 ++++++++++++++++-- 1 file changed, 132 insertions(+), 16 deletions(-) diff --git a/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy b/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy index 24276d671fe..cfc352d7fdc 100644 --- a/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy +++ b/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy @@ -16,14 +16,13 @@ import groovy.json.JsonOutput metadata { definition (name: "iblinds Z-Wave", namespace: "iblinds", author: "HABHomeIntel", ocfDeviceType: "oic.d.blind", mnmn: "SmartThings", vid: "generic-shade-3") { + capability "Window Shade" + capability "Window Shade Preset" capability "Switch Level" - capability "Switch" capability "Battery" capability "Refresh" capability "Actuator" capability "Health Check" - capability "Window Shade" - capability "Window Shade Preset" command "stop" @@ -75,8 +74,20 @@ metadata { } preferences { + // V3 configuration + input title: "V3 iBlinds Device Config", description: "Configuration options for newer V3 iBlinds devices", type: "paragraph", element: "paragraph", displayDuringSetup: false + input name: "NVM_TightLevel", type: "number", title: "Close Interval", defaultValue: 22, description: "Used for Large and Heavy blinds to set the close interval. A smaller value will make the blinds close tighter", required: true, displayDuringSetup: true + input name: "NVM_Direction", type: "bool", title: "Reverse", description: "Reverse Blind Direction", defaultValue: false + input name: "NVM_Target_Value", type: "number", title: "Default ON Value", defaultValue: 50, range: "1..100", description: "Used to set the default ON level when manual push button is pushed", required: true, displayDuringSetup:false + input name: "NVM_Device_Reset_Support", type: "bool", title: "Disable Reset Button", description: "Used for situations where the top motor buttons are being pushed accidentally via a tight installation space, etc.", defaultValue: false + input name: "Speed_Parameter", type: "number", title: "Open/Close Speed (seconds)", defaultValue: 0, range:"0..100", description: "To slow down the blinds, increase the value", required: true, displayDuringSetup: false + + input title: "", description: "", type: "paragraph", element: "paragraph", displayDuringSetup: false + + // V2 configuration + input title: "V2 iBlinds Device Config", description: "Configuration options for older V2 iBlinds devices", type: "paragraph", element: "paragraph", displayDuringSetup: false input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..99", required: false, displayDuringSetup: false - input "reverse", "bool", title: "Reverse", description: "Reverse Blind Direction",defaultValue: false, required: false , displayDuringSetup: false + input "reverse", "bool", title: "Reverse", description: "Reverse Blind Direction", defaultValue: false, required: false , displayDuringSetup: false } main(["windowShade"]) @@ -89,10 +100,12 @@ def parse(String description) { //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) // TODO: Workaround manual parsing of v4 multilevel report def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { result = zwaveEvent(cmd) } - log.debug "Parsed '$description' to ${result.inspect()}" + log.debug "Parsed '$description' to ${result?.inspect()}" + return result } @@ -105,16 +118,40 @@ def getCheckInterval() { def installed() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close"]), displayed: false) - response(refresh()) + + storeParamState() + + response(initialize() + refresh()) } def updated() { + def cmds = [] + if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } - if (!device.latestState("battery")) { - response(zwave.batteryV1.batteryGet()) + + cmds += configureParams() + storeParamState() + cmds += initialize() + + response(cmds) +} + +def initialize() { + def cmds = [] + + if (isV3Device()) { + // Set up lifeline association + cmds << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId).format() } + + // Schedule daily battery check + unschedule() + runIn(15, getBattery) + schedule("2020-01-01T12:01:00.000-0600", getBattery) + + cmds } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { @@ -131,6 +168,7 @@ def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelR private handleLevelReport(physicalgraph.zwave.Command cmd) { def level = cmd.value as Integer + def result = [] log.debug "handleLevelReport($level)" @@ -166,19 +204,22 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { } def open() { + Integer level = isV3Device() ? (NVM_Target_Value ?: 50) : 50 // Blinds fully open at 50%, NVM_Target_Value can't be 0% log.debug "open()" - // Blinds fully open at 50% + sendEvent(name: "windowShade", value: "open") - sendEvent(name: "level", value: 50, unit: "%", displayed: true) - zwave.switchMultilevelV3.switchMultilevelSet(value: 50).format() + sendEvent(name: "level", value: level, unit: "%", displayed: true) + + zwave.switchMultilevelV3.switchMultilevelSet(value: level).format() } def close() { log.debug "close()" - Integer level = reverse ? 99 : 0 + Integer level = isV3Device() ? 0 : (reverse ? 99 : 0) sendEvent(name: "windowShade", value: "closed") sendEvent(name: "level", value: 0, unit: "%", displayed: true) + zwave.switchMultilevelV3.switchMultilevelSet(value: level).format() } @@ -192,8 +233,8 @@ def setLevel(value, duration = null) { if (level > 99) level = 99 Integer tiltLevel = level as Integer // we will use this value to decide what level is sent to device (reverse or not reversed) - // Check to see if user wants blinds to operate in reverse direction - if (reverse) { + // For older devices, check to see if user wants blinds to operate in reverse direction + if (!isV3Device() && reverse) { tiltLevel = 99 - level } @@ -211,11 +252,10 @@ def setLevel(value, duration = null) { //log.debug "Level - ${level}% & Tilt Level - ${tiltLevel}%" sendEvent(name: "level", value: level, descriptionText: descriptionText) zwave.switchMultilevelV3.switchMultilevelSet(value: tiltLevel).format() - } def presetPosition() { - setLevel(preset ?: state.preset ?: 50) + isV3Device() ? open() : setLevel(preset ?: 50) } def pause() { @@ -239,3 +279,79 @@ def refresh() { zwave.batteryV1.batteryGet().format() ], 1500) } + +def configureParams() { + def cmds = [] + + if (isV3Device()) { + /* + Parameter No. Size Parameter Name Desc. + 1 1 NVM_TightLevel Auto Calibration tightness + 2 1 NVM_Direction Reverse the direction of iblinds + 3 1 NVM_Target_Report Not used **** + 4 1 NVM_Target_Value Default on position + 5 1 NVM_Device_Reset_Support Turns off the reset button + 6 1 Speed_Parameter Speed + */ + + log.debug "Configuration Started" + + // If paramater value has changed then add zwave configration command to cmds + + if (NVM_TightLevel != null && state.param1 != NVM_TightLevel) { + cmds << zwave.configurationV1.configurationSet(parameterNumber: 1, size: 1, configurationValue: [NVM_TightLevel.toInteger()]).format() + } + if (NVM_Direction != null && state.param2 != NVM_Direction) { + def NVM_Direction_Val = boolToInteger(NVM_Direction) + + cmds << zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, configurationValue: [NVM_Direction_Val.toInteger()]).format() + } + if (state.param3 != 0) { + cmds << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, configurationValue: [0]).format() + } + if (NVM_Target_Value != null && state.param4 != NVM_Target_Value) { + cmds << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, configurationValue: [NVM_Target_Value.toInteger()]).format() + } + if (NVM_Device_Reset_Support != null && state.param5 != NVM_Device_Reset_Support) { + def NVM_Device_Reset_Val = boolToInteger(NVM_Device_Reset_Support) + + cmds << zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, configurationValue: [NVM_Device_Reset_Val.toInteger()]).format() + } + if (Speed_Parameter != null && state.param6 != Speed_Parameter) { + cmds << zwave.configurationV1.configurationSet(parameterNumber: 6, size: 1, configurationValue: [Speed_Parameter.toInteger()]).format() + } + + log.debug "Configuration Complete" + } + + delayBetween(cmds, 500) +} + +private storeParamState() { + if (isV3Device()) { + log.debug "Storing Paramater Values" + + state.param1 = NVM_TightLevel + state.param2 = NVM_Direction + state.param3 = 0 // Not used at the moment + state.param4 = NVM_Target_Value + state.param5 = NVM_Device_Reset_Support + state.param6 = Speed_Parameter + } +} + +def boolToInteger(boolValue) { + boolValue ? 1 : 0 +} + +def getBattery() { + log.debug "get battery level" + // Use sendHubCommand to get battery level + def cmd = [] + cmd << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format()) + sendHubCommand(cmd) +} + +def isV3Device() { + zwaveInfo.mfr == "0287" && zwaveInfo.prod == "0004" && zwaveInfo.model == "0071" +} From f17331260227df9f4094b6aed5b191fade62d2b6 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 14 Oct 2020 20:28:18 +0200 Subject: [PATCH 094/422] ICP-13708 Fixed typo in module name (#47261) --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index cc413f1fe5b..705d444e569 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -47,7 +47,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020,0B05", outClusters: "000A,0019", manufacturer: "Yale", model: "YDD-D4F0 TSDB", deviceJoinName: "Lockwood Door Lock" //Lockwood Smart Lock fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Locks (YDF30/40, YMF30/40) with old firmware (v.9.0) fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000202", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YDF40 - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000001", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "0700000001", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 } tiles(scale: 2) { From 192376f847b8fa2eaa38088951ef27ec5f840934 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 16 Oct 2020 23:18:44 +0200 Subject: [PATCH 095/422] [ICP-13411] Qubino Flush Shutter - energy usage is not reported (#42222) * Schedule energy usage poll once a day * Moved to querying energy usage after power report --- .../qubino-flush-shutter.src/qubino-flush-shutter.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy index 22cd56f40f1..13845b2c378 100644 --- a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -368,7 +368,11 @@ def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) events += createEvent([name: "windowShade", value: state.blindsLastCommand]) events += createEvent([name: "shadeLevel", value: state.shadeTarget, displayed: false]) } else { - events += response(encap(zwave.switchMultilevelV3.switchMultilevelGet())) + events += response([ + encap(zwave.switchMultilevelV3.switchMultilevelGet()), + "delay 500", + encap(zwave.meterV3.meterGet(scale: 0x00)) + ]) } } } From 4c52d5c15c8f923dafb0e6fb5eba99b1b4803f26 Mon Sep 17 00:00:00 2001 From: Konrad Klimczuk Date: Tue, 20 Oct 2020 14:30:39 +0200 Subject: [PATCH 096/422] WWST-7143 - changes deviceJoinName to WAFER. --- .../zigbee-white-color-temperature-bulb.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index fe9fc667764..616a107b4d5 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -35,7 +35,7 @@ metadata { fingerprint profileId: "0104", deviceId: "010C", inClusters: "0006, 0008, 0300", deviceJoinName: "Light" //Generic Color Temperature Light // ABL - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "Wafer", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010C", manufacturer: "AduroSmart Eria", model: "AD-ColorTemperature3001", deviceJoinName: "Eria Light" //Eria ZigBee Color Temperature Bulb From ea51a76cc40d800adba897734839db32b51f4792 Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 21 Oct 2020 15:03:18 -0700 Subject: [PATCH 097/422] CHAD-5656 Change reporting interval for zigbee rgbw bulb A reporting interval of 0 for the level value was causing the bulb to send us erroneous intermediary reports that were breaking some automations. --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 24fc4c54c6f..a54e8bb5a2a 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -223,8 +223,8 @@ def configure() { cmds += refresh() + // OnOff, level minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity - zigbee.onOffConfig(0, 300) + - zigbee.levelConfig(0, 300) + zigbee.onOffConfig() + + zigbee.levelConfig() cmds } From 78d5664d11323601566e2fe98c3ccba004c4d368 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Thu, 22 Oct 2020 00:05:49 +0200 Subject: [PATCH 098/422] WWST-6892, WWST-6896, WWST-6898, WWST-6904 - New DTH for Viconics/Schneider Room Controllers (#45873) * WWST-6898 - New DTH for Viconics Schneider Room Controller * Support for capabilities: occupancy, fan mode. Support for Viconics VT8650, Schneider Electric: SE8350 and SE8650. Reading more attributes after thermostat mode and fan speed changes. Changes in configuration. * Fixes spacing * Renames child device to Child Thermostat Setpoints * Renames child device to Child Thermostat Setpoints * Fixes processing occupancy reports. * small refactoring * code refactoring * reverted capability Thermostat * Removed configuration from child dth. * Configure setpoint values initially. Round incoming setpoint values to nearest half. Code refactoring. * Remove blank line * fix * fix * Change max reporting interval to 3600 seconds. Clean up the code. * Code refactoring. Removed unnecessary configuration commands. * Fix, code refactoring. * Fix * Fix * Removes unnecessary value from thermostat mode map. Fixes temperature reporting. --- .../child-thermostat-setpoints.groovy | 43 ++ .../viconics-schneider-room-controller.groovy | 546 ++++++++++++++++++ 2 files changed, 589 insertions(+) create mode 100644 devicetypes/smartthings/child-thermostat-setpoints.src/child-thermostat-setpoints.groovy create mode 100644 devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy diff --git a/devicetypes/smartthings/child-thermostat-setpoints.src/child-thermostat-setpoints.groovy b/devicetypes/smartthings/child-thermostat-setpoints.src/child-thermostat-setpoints.groovy new file mode 100644 index 00000000000..b8838d6cf34 --- /dev/null +++ b/devicetypes/smartthings/child-thermostat-setpoints.src/child-thermostat-setpoints.groovy @@ -0,0 +1,43 @@ +/** + * Child Thermostat Setpoints + * + * Copyright 2020 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: "Child Thermostat Setpoints", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.thermostat") { + capability "Actuator" + capability "Health Check" + capability "Refresh" + capability "Thermostat Cooling Setpoint" + capability "Thermostat Heating Setpoint" + } +} + +def setCoolingSetpoint(setpoint) { + log.debug "setCoolingSetpoint: ${setpoint}" + parent.setChildCoolingSetpoint(device.deviceNetworkId, setpoint) +} + +def setHeatingSetpoint(setpoint) { + log.debug "setHeatingSetpoint: ${setpoint}" + parent.setChildHeatingSetpoint(device.deviceNetworkId, setpoint) +} + +def ping() { + refresh() +} + +def refresh() { + parent.refreshChild() +} \ No newline at end of file diff --git a/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy b/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy new file mode 100644 index 00000000000..479e2ea4985 --- /dev/null +++ b/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy @@ -0,0 +1,546 @@ +/** + * Viconics/Schneider Room Controller + * + * Copyright 2020 + * + * 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. + * + */ +import groovy.json.JsonOutput +import physicalgraph.zigbee.zcl.DataType +metadata { + definition(name: "Viconics Schneider Room Controller", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.thermostat", mcdSync: true) { + + capability "Actuator" + capability "Sensor" + capability "Occupancy Sensor" + capability "Temperature Measurement" + capability "Relative Humidity Measurement" + capability "Thermostat Mode" + capability "Fan Speed" + capability "Thermostat Fan Mode" + capability "Thermostat Cooling Setpoint" + capability "Thermostat Heating Setpoint" + capability "Thermostat Operating State" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + // Viconics VT8350 Low Voltage Fan Coil Controller and Zone Controller + // Raw Description 0A 0104 0301 00 0A 0201 0202 0405 0402 0406 0204 0000 0004 0003 0005 0B 0201 0202 0405 0402 0406 0204 0000 0004 0003 0005 0500 + fingerprint manufacturer: "Viconics", model: "254-143", deviceJoinName: "Viconics Room Controller", mnmn: "SmartThings", vid: "SmartThings-smartthings-Viconics_Schneider_Room_Controller_Fan" + + // Viconics VT8650 Heat Pump and Indoor Air Quality Controller + // Raw Description 0A 0104 0301 00 09 0201 0405 0402 0406 0204 0000 0004 0003 0005 0A 0201 0405 0402 0406 0204 0000 0004 0003 0005 0500 + fingerprint manufacturer: "Viconics", model: "254-162", deviceJoinName: "Viconics Room Controller", mnmn: "SmartThings", vid: "SmartThings-smartthings-Viconics_Schneider_Room_Controller" + //fingerprint profileId: "0104", inClusters: "0000,0003,0201,0204,0405", outClusters: "0402,0405", manufacturer: "Viconics", model: "254-162", deviceJoinName: "VT8650xx" + + // Schneider Electric SE8350 Low Voltage Fan Coil Unit (FCU) and Zone Control + // Raw Description 0A 0104 0301 00 0A 0201 0202 0405 0402 0406 0204 0000 0004 0003 0005 0B 0201 0202 0405 0402 0406 0204 0000 0004 0003 0005 0500 + fingerprint manufacturer: "Schneider Electric", model: "254-145", deviceJoinName: "Schneider Electric Room Controller", vid: "SmartThings-smartthings-Viconics_Schneider_Room_Controller_Fan" + + // Schneider Electric SE8650 Roof Top Unit Controller + // Raw Description 0A 0104 0301 00 09 0201 0405 0402 0406 0204 0000 0004 0003 0005 0A 0201 0405 0402 0406 0204 0000 0004 0003 0005 0500 + fingerprint manufacturer: "Schneider Electric", model: "254-163", deviceJoinName: "Schneider Electric Room Controller", vid: "SmartThings-smartthings-Viconics_Schneider_Room_Controller" + } +} + +private getTHERMOSTAT_CLUSTER() { 0x0201 } +private getTHERMOSTAT_UI_CONFIGURATION_CLUSTER() { 0x0204 } +private getTEMPERATURE_DISPLAY_MODE() {0x0000} +private getLOCAL_TEMPERATURE() {0x0000} +private getCOOLING_SETPOINT() { 0x0011 } +private getHEATING_SETPOINT() { 0x0012 } +private getCOOLING_SETPOINT_UNOCCUPIED() { 0x0013 } +private getHEATING_SETPOINT_UNOCCUPIED() { 0x0014 } +private getOCCUPANCY() { 0x002 } +private getCUSTOM_OCCUPANCY() {0x0650} +private getCUSTOM_EFFECTIVE_OCCUPANCY() { 0x0c50 } +private getCUSTOM_HUMIDITY() { 0x07a6 } +private getCUSTOM_THERMOSTAT_MODE() { 0x0687 } +private getCUSTOM_FAN_SPEED() { 0x0688 } +private getCUSTOM_FAN_MODE() { 0x0698 } +private getCUSTOM_THERMOSTAT_OPERATING_STATE() { 0x06BF } +private getUNOCCUPIED_SETPOINT_CHILD_DEVICE_ID() {1} +private getTHERMOSTAT_MODE_OFF() { 0x00 } +private getTHERMOSTAT_MODE_AUTO() { 0x01 } +private getTHERMOSTAT_MODE_COOL() { 0x02 } +private getTHERMOSTAT_MODE_HEAT() { 0x03 } +private getCUSTOM_FAN_MODE_ON() { 0x00 } +private getCUSTOM_FAN_MODE_AUTO() { 0x01 } +private getCUSTOM_FAN_MODE_CIRCULATE() { 0x02 } +private getOPERATING_STATE_IDLE() { 0x00 } +private getOPERATING_STATE_COOLING() { 0x01 } +private getOPERATING_STATE_HEATING() { 0x02 } +private getOCCUPANCY_OCCUPIED() { 0x00 } +private getOCCUPANCY_UNOCCUPIED() { 0x01 } + +private getFAN_MODE_MAP() { + [ + (CUSTOM_FAN_MODE_ON):"on", + (CUSTOM_FAN_MODE_AUTO):"auto", + (CUSTOM_FAN_MODE_CIRCULATE):"circulate" + ] +} + +private getTHERMOSTAT_FAN_MODE_ATTRIBUTE_ID_MAP() { + [ + "on": (CUSTOM_FAN_MODE_ON), + "auto": (CUSTOM_FAN_MODE_AUTO), + "circulate": (CUSTOM_FAN_MODE_CIRCULATE) + ] +} + +private getTHERMOSTAT_MODE_MAP() { + [ + (THERMOSTAT_MODE_OFF):"off", + (THERMOSTAT_MODE_AUTO):"auto", + (THERMOSTAT_MODE_COOL):"cool", + (THERMOSTAT_MODE_HEAT):"heat" + ] +} + +private getTHERMOSTAT_OPERATING_STATE_MAP() { + [ + (OPERATING_STATE_IDLE):"idle", + (OPERATING_STATE_COOLING):"cooling", + (OPERATING_STATE_HEATING):"heating" + ] +} + +private getEFFECTIVE_OCCUPANCY_MAP() { + [ + (OCCUPANCY_OCCUPIED):"occupied", + (OCCUPANCY_UNOCCUPIED):"unoccupied" + ] +} + +def installed() { + log.debug "installed" + sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + if (isViconicsVT8350() || isSchneiderSE8350()) { + state.supportedFanModes = ["on", "auto"] + } else { + state.supportedFanModes = ["on", "auto", "circulate"] + } + state.supportedThermostatModes = ["off", "auto", "cool", "heat"] + + sendEvent(name: "supportedThermostatFanModes", value: JsonOutput.toJson(state.supportedFanModes), displayed: false) + sendEvent(name: "supportedThermostatModes", value: JsonOutput.toJson(state.supportedThermostatModes), displayed: false) + sendEvent(name: "coolingSetpointRange", value: coolingSetpointRange, displayed: false) + sendEvent(name: "heatingSetpointRange", value: heatingSetpointRange, displayed: false) +} + +private void createChildThermostat() { + log.debug "Creating child thermostat to handle unoccupied cooling/heating setpoints" + def label = "Unoccupied setpoints" + def childName = "${device.displayName} ${label}" + + def child = addChildDevice("Child Thermostat Setpoints", "${device.deviceNetworkId}:1", device.hubId, + [completedSetup: true, label: childName, isComponent: true, componentName: "childSetpoints", componentLabel: label] + ) + + child.sendEvent(name: "coolingSetpoint", value: 20.0, unit: "C") + child.sendEvent(name: "heatingSetpoint", value: 21.0, unit: "C") + log.debug "child.inspect() ${child}" +} + +def parse(String description) { + def result = [] + def eventMap = [:] + def descMap = zigbee.parseDescriptionAsMap(description) + + if (descMap.clusterInt == THERMOSTAT_CLUSTER && descMap.attrInt != null) { + switch (descMap.attrInt) { + case OCCUPANCY: + case CUSTOM_EFFECTIVE_OCCUPANCY: + log.debug "${descMap.attrInt == OCCUPANCY ? "OCCUPANCY" : "EFFECTIVE OCCUPANCY"}, descMap.value: ${descMap.value}, descMap.attrInt: ${descMap.attrInt}" + eventMap.name = "occupancy" + eventMap.value = EFFECTIVE_OCCUPANCY_MAP[Integer.parseInt(descMap.value, 16)] + break + case COOLING_SETPOINT: + log.debug "COOLING SETPOINT OCCUPIED, descMap.value: ${descMap.value}" + eventMap.name = "coolingSetpoint" + eventMap.value = getTemperature(descMap.value, true) + eventMap.unit = temperatureScale + break + case HEATING_SETPOINT: + log.debug "HEATING SETPOINT OCCUPIED, descMap.value: ${descMap.value}" + eventMap.name = "heatingSetpoint" + eventMap.value = getTemperature(descMap.value, true) + eventMap.unit = temperatureScale + break + case COOLING_SETPOINT_UNOCCUPIED: + log.debug "COOLING SETPOINT UNOCCUPIED, descMap.value: ${descMap.value}" + def childEvent = [:] + childEvent.name = "coolingSetpoint" + childEvent.value = getTemperature(descMap.value, true) + childEvent.unit = temperatureScale + sendEventToChild(UNOCCUPIED_SETPOINT_CHILD_DEVICE_ID, childEvent) + break + case HEATING_SETPOINT_UNOCCUPIED: + log.debug "HEATING SETPOINT UNOCCUPIED, descMap.value: ${descMap.value}" + def childEvent = [:] + childEvent.name = "heatingSetpoint" + childEvent.value = getTemperature(descMap.value, true) + childEvent.unit = temperatureScale + sendEventToChild(UNOCCUPIED_SETPOINT_CHILD_DEVICE_ID, childEvent) + break + case LOCAL_TEMPERATURE: + log.debug "LOCAL TEMPERATURE, descMap.value: ${descMap.value}" + eventMap.name = "temperature" + eventMap.value = getTemperature(descMap.value) + eventMap.unit = temperatureScale + break + case CUSTOM_HUMIDITY: + log.debug "CUSTOM HUMIDITY, descMap.value: ${descMap.value}" + eventMap.name = "humidity" + eventMap.value = Integer.parseInt(descMap.value, 16) + eventMap.unit = "%" + break + case CUSTOM_FAN_MODE: + log.debug "CUSTOM FAN MODE, descMap.value: ${descMap.value}" + if (isViconicsVT8650() || isSchneiderSE8650()) { + eventMap.name = "thermostatFanMode" + eventMap.value = FAN_MODE_MAP[Integer.parseInt(descMap.value, 16)] + eventMap.data = [supportedThermostatFanModes: state.supportedFanModes] + } + break + case CUSTOM_FAN_SPEED: + // VT8350 reports fan speed 3 as AUTO + log.debug "CUSTOM FAN SPEED, descMap.value: ${descMap.value}" + // the device reports values of range 0-3 (0 is LOW) + def sliderValue = Integer.parseInt(descMap.value, 16) + 1 + if (sliderValue < 4) { + eventMap.name = "fanSpeed" + eventMap.value = sliderValue + result << createEvent([name:"thermostatFanMode", value: "on", data: [supportedThermostatFanModes: state.supportedFanModes]]) + } else { + result << createEvent([name:"thermostatFanMode", value: "auto", data:[supportedThermostatFanModes: state.supportedFanModes]]) + } + break + case CUSTOM_THERMOSTAT_MODE: + log.debug "CUSTOM THERMOSTAT MODE, descMap.value: ${descMap.value}" + eventMap.name = "thermostatMode" + eventMap.value = THERMOSTAT_MODE_MAP[Integer.parseInt(descMap.value, 16)] + eventMap.data = [supportedThermostatModes: state.supportedThermostatModes] + break + case CUSTOM_THERMOSTAT_OPERATING_STATE: + log.debug "CUSTOM THERMOSTAT OPERATING STATE, descMap.value: ${descMap.value}" + eventMap.name = "thermostatOperatingState" + eventMap.value = THERMOSTAT_OPERATING_STATE_MAP[Integer.parseInt(descMap.value, 16)] + break + default: + log.debug "UNHANDLED ATTRIBUTE, descMap.inspect(): ${descMap.inspect()}" + } + } + result << createEvent(eventMap) + //log.debug "Description ${description} parsed to ${result}" + result +} + +private sendEventToChild(childNumber, event) { + def child = childDevices?.find { getChildId(it.deviceNetworkId) == childNumber } + + if (child) { + log.debug "Sending ${event.name} event to $child.displayName" + child?.sendEvent(event) + } else { + log.debug "Child device $childNumber not found!" + } +} + +def setCoolingSetpoint(degrees) { + setSetpoint(degrees, COOLING_SETPOINT) +} + +def setHeatingSetpoint(degrees) { + setSetpoint(degrees, HEATING_SETPOINT) +} + +def setChildCoolingSetpoint(deviceNetworkId, degrees) { + log.debug "deviceNetworkId: ${deviceNetworkId} degrees: ${degrees}" + def childId = getChildId(deviceNetworkId) + if (childId != null) { + setSetpoint(degrees, COOLING_SETPOINT_UNOCCUPIED) + } +} + +def setChildHeatingSetpoint(deviceNetworkId, degrees) { + log.debug "deviceNetworkId: ${deviceNetworkId} degrees: ${degrees}" + + def childId = getChildId(deviceNetworkId) + if (childId != null) { + setSetpoint(degrees, HEATING_SETPOINT_UNOCCUPIED) + } +} + +def getChildId(deviceNetworkId) { + def split = deviceNetworkId?.split(":") + (split.length > 1) ? split[1] as Integer : null +} + +def setSetpoint(degrees, setpointAttr) { + log.debug "degrees: ${degrees}, setpointAttr: ${setpointAttr}" + if (degrees != null && setpointAttr != null) { + log.debug "temperatureScale: ${temperatureScale}" + def celsius = (temperatureScale == "C") ? degrees : fahrenheitToCelsius(degrees) + celsius = (celsius as Double).round(2) + + delayBetween([ + zigbee.writeAttribute(THERMOSTAT_CLUSTER, setpointAttr, DataType.INT16, zigbee.convertToHexString(celsius * 100)), + zigbee.readAttribute(THERMOSTAT_CLUSTER, setpointAttr) + ], 500) + } +} + +def setThermostatFanMode(mode) { + if (state.supportedFanModes?.contains(mode)) { + if (isViconicsVT8350() || isSchneiderSE8350()) { + switch (mode) { + case "on": + setFanSpeed(1) + break + case "auto": + setFanSpeed(4) + break + } + } else if (isViconicsVT8650() || isSchneiderSE8650()) { + getThermostatFanModeCommands(THERMOSTAT_FAN_MODE_ATTRIBUTE_ID_MAP[mode]) + } + } else { + log.debug "Unsupported fan mode $mode" + } +} + +def fanOn() { + getThermostatFanModeCommands(CUSTOM_FAN_MODE_ON) +} + +def fanAuto() { + getThermostatFanModeCommands(CUSTOM_FAN_MODE_AUTO) +} + +def fanCirculate() { + getThermostatFanModeCommands(CUSTOM_FAN_MODE_CIRCULATE) +} + +def getThermostatFanModeCommands(mode) { + if (mode) { + delayBetween([ + zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE, DataType.ENUM8, mode), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE) + ], 500) + } +} + +def setFanSpeed(speed) { + log.debug "setFanSpeed: ${speed}" + + if (speed == 0 || speed >= 4) { //if by any chance user selects 0 or a value higher than 3, it fan will be set to AUTO + speed = 3 + } else { + speed = speed - 1 + } + delayBetween([ + zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_SPEED, DataType.ENUM8, speed), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_SPEED), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_OPERATING_STATE) + ], 500) +} + +def setThermostatMode(mode) { + log.debug "set mode $mode (supported ${state.supportedThermostatModes})" + if (state.supportedThermostatModes?.contains(mode)) { + switch (mode) { + case "auto": + auto() + break + case "cool": + cool() + break + case "heat": + heat() + break + case "off": + off() + break + } + } else { + log.debug "Unsupported mode $mode" + } +} + +def auto() { + getThermostatModeCommands(THERMOSTAT_MODE_AUTO) +} + +def cool() { + getThermostatModeCommands(THERMOSTAT_MODE_COOL) +} + +def heat() { + getThermostatModeCommands(THERMOSTAT_MODE_HEAT) +} + +def off() { + getThermostatModeCommands(THERMOSTAT_MODE_OFF) +} + +def getThermostatModeCommands(mode) { + if (mode) { + delayBetween([ + zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE, DataType.ENUM8, mode), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_OPERATING_STATE) + ], 500) + } +} + +def ping() { + log.debug "ping" + zigbee.readAttribute(THERMOSTAT_CLUSTER, LOCAL_TEMPERATURE) +} + +def refresh() { + log.debug "refresh" + getRefreshCommands() +} + +def getRefreshCommands() { + def refreshCommands = [] + + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_HUMIDITY) + + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, LOCAL_TEMPERATURE) + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, COOLING_SETPOINT) + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, HEATING_SETPOINT) + + if (supportsFanSpeed()) { + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_SPEED) + } + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE) + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE) // formerly THERMOSTAT MODE: 0x001C + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_OPERATING_STATE) + + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, OCCUPANCY) + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_OCCUPANCY) + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_EFFECTIVE_OCCUPANCY) + refreshCommands += zigbee.readAttribute(THERMOSTAT_UI_CONFIGURATION_CLUSTER, TEMPERATURE_DISPLAY_MODE) + + refreshCommands += refreshChild() + + refreshCommands +} + +def refreshChild() { + log.debug "refresh child device" + def refreshCommands = [] + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, COOLING_SETPOINT_UNOCCUPIED) + refreshCommands += zigbee.readAttribute(THERMOSTAT_CLUSTER, HEATING_SETPOINT_UNOCCUPIED) + + refreshCommands +} + +def configure() { + log.debug "Configuration" + + configureChild() + + def configurationCommands = [] + + // set initial values + configurationCommands += setSetpoint(initialCoolingSetpoint, COOLING_SETPOINT) + configurationCommands += setSetpoint(initialHeatingSetpoint, HEATING_SETPOINT) + configurationCommands += setSetpoint(initialCoolingSetpoint, COOLING_SETPOINT_UNOCCUPIED) + configurationCommands += setSetpoint(initialHeatingSetpoint, HEATING_SETPOINT_UNOCCUPIED) + + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE, DataType.ENUM8, 1, 3600, 1) //formerly THERMOSTAT MODE: 0x001C + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, LOCAL_TEMPERATURE, DataType.INT16, 10, 3600, 10) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, COOLING_SETPOINT, DataType.INT16, 1, 3600, 10) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, HEATING_SETPOINT, DataType.INT16, 1, 3600, 10) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, COOLING_SETPOINT_UNOCCUPIED, DataType.INT16, 1, 3600, 10) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, HEATING_SETPOINT_UNOCCUPIED, DataType.INT16, 1, 3600, 10) + //configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, 0x0A58, 0x10, 1, 300, 1) //GFan + if (supportsFanSpeed()) { + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_FAN_SPEED, DataType.ENUM8, 1, 3600, 1) + } + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE, DataType.ENUM8, 1, 3600, 1) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_OPERATING_STATE, DataType.ENUM8, 1, 3600, 1) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, OCCUPANCY, DataType.ENUM8, 1, 3600, null) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_OCCUPANCY, DataType.ENUM8, 1, 3600, null) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_EFFECTIVE_OCCUPANCY, DataType.ENUM8, 1, 3600, null) + configurationCommands += zigbee.configureReporting(THERMOSTAT_UI_CONFIGURATION_CLUSTER, TEMPERATURE_DISPLAY_MODE, DataType.ENUM8, 1, 3600, 1) + configurationCommands += zigbee.configureReporting(THERMOSTAT_CLUSTER, CUSTOM_HUMIDITY, DataType.UINT16, 1, 3600, 10) + + delayBetween(getRefreshCommands() + configurationCommands) +} + +def configureChild() { + if (!childDevices) { + createChildThermostat() + } +} + +def getCoolingSetpointRange() { + (getTemperatureScale() == "C") ? [12, 37.5] : [54, 100] +} +def getHeatingSetpointRange() { + (getTemperatureScale() == "C") ? [4.5, 32] : [40, 90] +} + +def getInitialCoolingSetpoint() { + (getTemperatureScale() == "C") ? 28 : 86 +} + +def getInitialHeatingSetpoint() { + (getTemperatureScale() == "C") ? 20 : 68 +} + +def getTemperature(value, roundValue = false) { + if (value != null) { + def celsius = Integer.parseInt(value, 16) / 100 + if(roundValue) { + celsius = roundToTheNearestHalf(celsius) + } + + if (temperatureScale == "C") { + celsius + } else { + celsiusToFahrenheit(celsius) + } + } +} + +def roundToTheNearestHalf(value) { + Math.round(value * 2) / 2 +} + +def supportsFanSpeed() { + isViconicsVT8350() || isSchneiderSE8350() +} + +def isViconicsVT8350() { + device.getDataValue("model") == "254-143" // Viconics VT8350 Low Voltage Fan Coil Controller and Zone Controller +} + +def isViconicsVT8650() { + device.getDataValue("model") == "254-162" // Viconics VT8650 Heat Pump and Indoor Air Quality Controller +} + +def isSchneiderSE8350() { + device.getDataValue("model") == "254-145" // SE8350 Low Voltage Fan Coil Unit (FCU) and Zone Control +} + +def isSchneiderSE8650() { + device.getDataValue("model") == "254-163" // SE8650 Roof Top Unit Controller +} \ No newline at end of file From 72041b5f95d3531d6bac793c47b06a69f861e212 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Thu, 22 Oct 2020 17:52:20 -0700 Subject: [PATCH 099/422] BUG-979 Update DTH vid for Aeon Key Fob to metadata that has the main component visible in the device details page Also update the Inovelli 2-Channel Smart Plug MCD DTH to protect createChildDevices so that changing to that DTH doesn't get interrupted with an exception --- .../inovelli-2-channel-smart-plug-mcd.groovy | 4 +++- devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/devicetypes/erocm123/inovelli-2-channel-smart-plug-mcd.src/inovelli-2-channel-smart-plug-mcd.groovy b/devicetypes/erocm123/inovelli-2-channel-smart-plug-mcd.src/inovelli-2-channel-smart-plug-mcd.groovy index 2d4336e47c5..6b9b243fa48 100644 --- a/devicetypes/erocm123/inovelli-2-channel-smart-plug-mcd.src/inovelli-2-channel-smart-plug-mcd.groovy +++ b/devicetypes/erocm123/inovelli-2-channel-smart-plug-mcd.src/inovelli-2-channel-smart-plug-mcd.groovy @@ -237,7 +237,9 @@ def installed() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - createChildDevices() + if (!childDevices) { + createChildDevices() + } response(refresh()) } 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 5c54b5e3e51..a7097e1c635 100644 --- a/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy +++ b/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy @@ -13,7 +13,7 @@ import groovy.json.JsonOutput * */ metadata { - definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, ocfDeviceType: "x.com.st.d.remotecontroller", mnmn: "SmartThings", vid: "generic-4-button", mcdSync: true) { + definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, ocfDeviceType: "x.com.st.d.remotecontroller", mnmn: "SmartThings", vid: "generic-4-button-alt", mcdSync: true) { capability "Actuator" capability "Button" capability "Holdable Button" From 9df93fe9108c550f589aa07b26e903634fd6c7ed Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Mon, 26 Oct 2020 22:47:20 +0100 Subject: [PATCH 100/422] [WWST-7126] Fingerprint for Somfy Situo 1 (#47897) * [WWST7126] Fingerprint for Somfy Situo 1 * shortened checking if is somfy situo --- devicetypes/smartthings/ikea-button.src/ikea-button.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index 756465aecdd..934ce563bb8 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -32,6 +32,7 @@ metadata { fingerprint manufacturer: "IKEA of Sweden", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote fingerprint manufacturer: "KE", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote fingerprint manufacturer: "SOMFY", model: "Situo 4 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 + fingerprint manufacturer: "SOMFY", model: "Situo 1 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 } tiles { @@ -366,7 +367,7 @@ private boolean isIkea() { } private boolean isSomfySituo() { - device.getDataValue("model") == "Situo 4 Zigbee" + device.getDataValue("manufacturer") == "SOMFY" } private Integer getGroupAddrFromBindingTable(description) { From 1d52117269031f9a5e50d55c770a34c50421ac8a Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 27 Oct 2020 10:46:43 -0600 Subject: [PATCH 101/422] Add Tradfri signal repeater model with caps (#48433) * Add Tradfri signal repeater model with caps * Update zigbee-range-extender.groovy --- .../zigbee-range-extender.src/zigbee-range-extender.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-range-extender.src/zigbee-range-extender.groovy b/devicetypes/smartthings/zigbee-range-extender.src/zigbee-range-extender.groovy index 3981275654d..47bcbfbb5cb 100644 --- a/devicetypes/smartthings/zigbee-range-extender.src/zigbee-range-extender.groovy +++ b/devicetypes/smartthings/zigbee-range-extender.src/zigbee-range-extender.groovy @@ -17,6 +17,7 @@ metadata { capability "Health Check" fingerprint profileId: "0104", inClusters: "0000, 0003, 0009, 0B05, 1000, FC7C", outClusters: "0019, 0020, 1000", manufacturer: "IKEA of Sweden", model: "TRADFRI signal repeater", deviceJoinName: "IKEA Repeater/Extender" //TRÅDFRI Signal Repeater + fingerprint profileId: "0104", inClusters: "0000, 0003, 0009, 0B05, 1000", outClusters: "0019, 0020, 1000", manufacturer: "IKEA of Sweden", model: "TRADFRI Signal Repeater", deviceJoinName: "IKEA Repeater/Extender" //TRÅDFRI Signal Repeater fingerprint profileId: "0104", inClusters: "0000, 0003", outClusters: "0019", manufacturer: "Smartenit, Inc", model: "ZB3RE", deviceJoinName: "Smartenit Repeater/Extender" //Smartenit Range Extender fingerprint profileId: "0104", inClusters: "0000, 0003, DC00, FC01", manufacturer: "Rooms Beautiful", model: "R001", deviceJoinName: "Rooms Beautiful Repeater/Extender" //Range Extender } From da3ea42159f5fa5acbbddaf32da8e84cbe6aad56 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 27 Oct 2020 10:22:32 -0700 Subject: [PATCH 102/422] BUG-1052 Do not display level events for fan controllers Level events were showing up in history, but this is not preferred. --- .../zwave-fan-controller.src/zwave-fan-controller.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-fan-controller.src/zwave-fan-controller.groovy b/devicetypes/smartthings/zwave-fan-controller.src/zwave-fan-controller.groovy index c040aa06620..f84972dfe05 100644 --- a/devicetypes/smartthings/zwave-fan-controller.src/zwave-fan-controller.groovy +++ b/devicetypes/smartthings/zwave-fan-controller.src/zwave-fan-controller.groovy @@ -120,7 +120,7 @@ def fanEvents(physicalgraph.zwave.Command cmd) { if (0 <= rawLevel && rawLevel <= 100) { def value = (rawLevel ? "on" : "off") result << createEvent(name: "switch", value: value) - result << createEvent(name: "level", value: rawLevel == 99 ? 100 : rawLevel) + result << createEvent(name: "level", value: rawLevel == 99 ? 100 : rawLevel, displayed: false) def fanLevel = 0 From 36208eae958552b6205d64ffc9423a26547ef5b5 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 27 Oct 2020 18:37:16 +0100 Subject: [PATCH 103/422] ICP-13718 - ignore redundant misinterpreted UP events (#48489) --- devicetypes/smartthings/ikea-button.src/ikea-button.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index 934ce563bb8..df6ef22e976 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -326,7 +326,9 @@ private Map getButtonEvent(Map descMap) { buttonNumber = OPENCLOSE_BUTTONS.DOWN } } - } else if (isSomfySituo()){ + } else if (isSomfySituo() && descMap.data?.size() == 0){ + // Somfy Situo Remotes query their shades directly after "My"(stop) button is pressed (that's intended behavior) + // descMap contains 'data':['00', '00'] in such cases, so we have to ignore those redundant misinterpreted UP events if (descMap.clusterInt == CLUSTER_WINDOW_COVERING) { buttonState = "pushed" if (descMap.commandInt == 0x00) { From c878abf4fe4c53a2a69e77f835cc94c070985c1c Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 27 Oct 2020 18:38:50 +0100 Subject: [PATCH 104/422] [ICP-13446] Fixed powerMeter, handling on/off from device (#47977) * Fixed powerMeter, handling on/off from device * Fixes, added method to define endpoint * Space fix --- .../qubino-flush-2-relay.groovy | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 2f2a66ca2df..0415fdd04d5 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -81,7 +81,6 @@ def installed() { if (!childDevices && state.numberOfSwitches > 1) { addChildSwitches(state.numberOfSwitches) } - sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) // Preferences template begin state.currentPreferencesState = [:] @@ -94,8 +93,9 @@ def installed() { } // Preferences template end response([ - refresh((1..state.numberOfSwitches).toList()) - ]) + refresh((1..state.numberOfSwitches).toList()), + addToAssociationGroupIfNeeded() + ].flatten()) } def updated() { @@ -129,6 +129,14 @@ def excludeParameterFromSync(preference){ return exclude } +def addToAssociationGroupIfNeeded() { + def cmds = [] + if (zwaveInfo?.model?.equals("0052")) { + cmds += encap(zwave.associationV2.associationSet(groupingIdentifier: 2, nodeId: [zwaveHubNodeId])) + } + cmds +} + private syncConfiguration() { def commands = [] parameterMap.each { @@ -237,14 +245,33 @@ def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) changeSwitch(ep, cmd) } +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, ep = null) { + log.debug "Basic ${cmd}" + (ep ? " from endpoint $ep" : "") + [ + changeSwitch(ep, cmd), + response([ + "delay 2000", + encap(zwave.meterV3.meterGet(scale: 2), endpoint) + ]) + ] +} + def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { log.debug "Binary ${cmd}" + (ep ? " from endpoint $ep" : "") changeSwitch(ep, cmd) } +def defaultEndpoint() { + if (zwaveInfo?.model?.equals("0052") || zwaveInfo?.model?.equals("0053")) { + return null + } else { + return 1 + } +} + private changeSwitch(endpoint, cmd) { def value = cmd.value ? "on" : "off" - if (endpoint == 1) { + if (endpoint == defaultEndpoint()) { createEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") } else if (endpoint) { String childDni = "${device.deviceNetworkId}:$endpoint" @@ -255,10 +282,10 @@ private changeSwitch(endpoint, cmd) { def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { log.debug "Meter ${cmd}" + (ep ? " from endpoint $ep" : "") - if (ep == 1) { + if (ep == defaultEndpoint()) { [ createEvent(createMeterEventMap(cmd)), - response(encap(zwave.meterV3.meterGet(scale: 0x00), 1)) + cmd.scale == 2 ? response(encap(zwave.meterV3.meterGet(scale: 0x00), ep)) : null ] } else if (ep) { String childDni = "${device.deviceNetworkId}:$ep" @@ -328,10 +355,10 @@ def childOnOff(deviceNetworkId, value) { if (switchId != null) sendHubCommand onOffCmd(value, switchId) } -private onOffCmd(value, endpoint = 1) { +private onOffCmd(value, endpoint = defaultEndpoint()) { delayBetween([ encap(zwave.basicV1.basicSet(value: value), endpoint), - encap(zwave.basicV1.basicGet(), endpoint), + encap(zwave.basicV1.basicGet(), endpoint) ]) } From a98c1216f2ebf870415271cbd572ec52a421ffc4 Mon Sep 17 00:00:00 2001 From: Juan Pablo Risso Date: Thu, 29 Oct 2020 13:45:47 -0400 Subject: [PATCH 105/422] C2C-1115 - Clean up Rachio post migration --- .../rachio-iro2-controller.groovy | 634 -------- .../rachio-iro2-zone.groovy | 167 -- .../rachio-connect.src/rachio-connect.groovy | 1360 ----------------- 3 files changed, 2161 deletions(-) delete mode 100644 devicetypes/rachio/rachio-iro2-controller.src/rachio-iro2-controller.groovy delete mode 100644 devicetypes/rachio/rachio-iro2-zone.src/rachio-iro2-zone.groovy delete mode 100644 smartapps/rachio/rachio-connect.src/rachio-connect.groovy diff --git a/devicetypes/rachio/rachio-iro2-controller.src/rachio-iro2-controller.groovy b/devicetypes/rachio/rachio-iro2-controller.src/rachio-iro2-controller.groovy deleted file mode 100644 index ce10d780430..00000000000 --- a/devicetypes/rachio/rachio-iro2-controller.src/rachio-iro2-controller.groovy +++ /dev/null @@ -1,634 +0,0 @@ -/** - * Rachio Sprinkler Controller Device Handler - * - * Copyright\u00A9 2017, 2018 Franz Garsombke - * Written by Anthony Santilli (@tonesto7) - * - * 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. - * - */ - -import java.text.SimpleDateFormat - -def devVer() { return "2.0.0" } - -metadata { - definition (name: "Rachio Sprinkler Controller", namespace: "rachio", author: "Rachio") { - capability "Refresh" - capability "Switch" - capability "Actuator" - capability "Valve" - capability "Sensor" - capability "Health Check" - - attribute "hardwareModel", "string" - attribute "hardwareDesc", "string" - attribute "activeZoneCnt", "number" - attribute "controllerOn", "string" - - attribute "rainDelay","number" - attribute "watering", "string" - - //current_schedule data - attribute "scheduleType", "string" - attribute "curZoneRunStatus", "string" - - attribute "curZoneName", "string" - attribute "curZoneNumber", "number" - attribute "curZoneDuration", "number" - attribute "curZoneStartDate", "string" - attribute "curZoneIsCycling", "string" - attribute "curZoneCycleCount", "number" - attribute "curZoneWaterTime", "number" - attribute "rainDelayStr", "string" - attribute "standbyMode", "string" - - attribute "lastUpdatedDt", "string" - - command "stopWatering" - command "setRainDelay", ["number"] - - command "doSetRainDelay" - command "decreaseRainDelay" - command "increaseRainDelay" - command "setZoneWaterTime", ["number"] - command "decZoneWaterTime" - command "incZoneWaterTime" - command "runAllZones" - command "standbyOn" - command "standbyOff" - //command "pauseScheduleRun" - - command "open" - command "close" - //command "pause" - } - - simulator { - // TODO: define status and reply messages here - } - - tiles (scale: 2){ - multiAttributeTile(name: "valveTile", type: "generic", width: 6, height: 4) { - tileAttribute("device.watering", key: "PRIMARY_CONTROL" ) { - attributeState "off", label: 'Off', action: "runAllZones", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"on" - attributeState "offline", label: 'Offline', icon: "st.valves.water.closed", backgroundColor: "#cccccc" - attributeState "standby", label: 'Standby Mode', icon: "st.valves.water.closed", backgroundColor: "#cccccc" - attributeState "on", label: 'Watering', action: "close", icon: "st.valves.water.open", backgroundColor: "#00a0dc", nextState: "off" - } - tileAttribute("device.curZoneRunStatus", key: "SECONDARY_CONTROL") { - attributeState("default", label:'${currentValue}') - } - } - standardTile("hardwareModel", "device.hardwareModel", inactiveLabel: false, width: 2, height: 2, decoration: "flat") { - state "default", icon: "" - state "8ZoneV1", icon: "https://s3-us-west-2.amazonaws.com/rachio-media/smartthings/8zone_v1.png" - state "16ZoneV1", icon: "https://s3-us-west-2.amazonaws.com/rachio-media/smartthings/8zone_v1.png" - state "8ZoneV2", icon: "https://raw.githubusercontent.com/tonesto7/rachio-manager/master/images/rachio_gen2.png" - state "16ZoneV2", icon: "https://raw.githubusercontent.com/tonesto7/rachio-manager/master/images/rachio_gen2.png" - state "8ZoneV3", icon: "https://raw.githubusercontent.com/tonesto7/rachio-manager/master/images/rachio_gen3.png" - state "16ZoneV3", icon: "https://raw.githubusercontent.com/tonesto7/rachio-manager/master/images/rachio_gen3.png" - } - valueTile("hardwareDesc", "device.hardwareDesc", inactiveLabel: false, width: 4, height: 1, decoration: "flat") { - state "default", label: 'Model:\n${currentValue}' - } - valueTile("activeZoneCnt", "device.activeZoneCnt", inactiveLabel: true, width: 4, height: 1, decoration: "flat") { - state "default", label: 'Active Zones:\n${currentValue}' - } - valueTile("controllerOn", "device.controllerOn", inactiveLabel: true, width: 2, height: 1, decoration: "flat") { - state "default", label: 'Online Status:\n${currentValue}' - } - valueTile("controllerRunStatus", "device.controllerRunStatus", inactiveLabel: true, width: 4, height: 2, decoration: "flat") { - state "default", label: '${currentValue}' - } - valueTile("blank", "device.blank", width: 2, height: 1, decoration: "flat") { - state("default", label: '') - } - standardTile("switch", "device.switch", inactiveLabel: false, decoration: "flat") { - state "off", icon: "st.switch.off" - state "on", action: "stopWatering", icon: "st.switch.on" - } - valueTile("pauseScheduleRun", "device.scheduleTypeBtnDesc", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { - state "default", label: '${currentValue}', action: "pauseScheduleRun" - } - - // Rain Delay Control - standardTile("leftButtonControl", "device.rainDelay", inactiveLabel: false, decoration: "flat") { - state "default", action:"decreaseRainDelay", icon:"st.thermostat.thermostat-left" - } - valueTile("rainDelay", "device.rainDelay", width: 2, height: 1, decoration: "flat") { - state "default", label:'Rain Delay:\n${currentValue} Days' - } - standardTile("rightButtonControl", "device.rainDelay", inactiveLabel: false, decoration: "flat") { - state "default", action:"increaseRainDelay", icon:"st.thermostat.thermostat-right" - } - valueTile("applyRainDelay", "device.rainDelayStr", width: 2, height: 1, inactiveLabel: false, decoration: "flat") { - state "default", label: '${currentValue}', action:'doSetRainDelay' - } - - //zone Water time control - valueTile("lastWateredDesc", "device.lastWateredDesc", width: 4, height: 1, decoration: "flat", wordWrap: true) { - state("default", label: 'Last Watered:\n${currentValue}') - } - standardTile("leftZoneTimeButton", "device.curZoneWaterTime", inactiveLabel: false, decoration: "flat") { - state "default", action:"decZoneWaterTime", icon:"st.thermostat.thermostat-left" - } - valueTile("curZoneWaterTime", "device.curZoneWaterTime", width: 2, height: 1, decoration: "flat") { - state "default", label:'Manual Zone Time:\n${currentValue} Minutes' - } - standardTile("rightZoneTimeButton", "device.curZoneWaterTime", inactiveLabel: false, decoration: "flat") { - state "default", action:"incZoneWaterTime", icon:"st.thermostat.thermostat-right" - } - valueTile("runAllZonesTile", "device.curZoneWaterTime", inactiveLabel: false, width: 2 , height: 1, decoration: "flat") { - state("default", label: 'Run All Zones\n${currentValue} Minutes', action:'runAllZones') - } - standardTile("standbyMode", "device.standbyMode", decoration: "flat", wordWrap: true, width: 2, height: 2) { - state "on", label:'Turn Standby Off', action:"standbyOff", nextState: "false", icon: "http://cdn.device-icons.smartthings.com/sonos/play-icon@2x.png" - state "off", label:'Turn Standby On', action:"standbyOn", nextState: "true", icon: "http://cdn.device-icons.smartthings.com/sonos/pause-icon@2x.png" - } - standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" - } - } - main "valveTile" - details(["valveTile", "hardwareModel", "hardwareDesc", "activeZoneCnt", "curZoneIsCyclingTile", "leftButtonControl", "rainDelay", "rightButtonControl", "applyRainDelay", - "leftZoneTimeButton", "curZoneWaterTime", "rightZoneTimeButton", "runAllZonesTile", "lastUpdatedDt", "standbyMode", "refresh"]) -} - -def getAppImg(imgName) { - return "https://raw.githubusercontent.com/tonesto7/rachio-manager/master/images/$imgName" -} - -// parse events into attributes -def parse(String description) { - log.debug "Parsing '${description}'" -} - -def initialize() { - sendEvent(name: "DeviceWatch-Enroll", value: groovy.json.JsonOutput.toJson(["protocol":"cloud", "scheme":"untracked"]), displayed: false) - - verifyDataAttr() -} - -def verifyDataAttr() { - updateDataValue("HealthEnrolled", "true") - updateDataValue("manufacturer", "Rachio") -// getDevGeneration is not defined in the connect app... -// def gen = state.deviceId ? parent?.getDevGeneration(state.deviceId) : null -// updateDataValue("model", "${device.name}${gen ? " ($gen)" : ""}") -} - -void installed() { - initialize() - state.isInstalled = true - sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) -} - -void updated() { - initialize() -} - -// NOP implementation of ping as health check only calls this for tracked devices -// But as capability defines this method it's implemented to avoid MissingMethodException -def ping() { - log.info "unexpected ping call from health check" -} - -def generateEvent(Map results) { - if (!state.swVersion || state.swVersion != devVer()) { - initialize() - state.swVersion = devVer() - } - //log.warn "---------------START OF API RESULTS DATA----------------" - if (results) { - // log.debug results - state.deviceId = device.deviceNetworkId - state.pauseInStandby = (results.pauseInStandby == true) - hardwareModelEvent(results.data?.model) - activeZoneCntEvent(results.data?.zones) - controllerOnEvent(results.data?.on) - - if (results.status == "ONLINE") { - state.inStandby = results.standby - sendEvent(name: 'standbyMode', value: (results.standby?.toString() == "true" ? "on": "off"), displayed: true) - sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false) - if (results.standby == true && results.pauseInStandby == true) { - markStandby() - } else { - isWateringEvent(results.schedData?.status, results.schedData?.zoneId) - } - lastUpdatedEvent() - } else { - markOffLine() - } - if (!device.currentValue("curZoneWaterTime")) { - setZoneWaterTime(parent?.settings?.defaultZoneTime.toInteger()) - } - scheduleDataEvent(results.schedData, results.data.zones, results.rainDelay) - rainDelayValEvent(results.rainDelay) - } -} - -def getDurationDesc(long secondsCnt) { - int seconds = secondsCnt %60 - secondsCnt -= seconds - long minutesCnt = secondsCnt / 60 - long minutes = minutesCnt % 60 - minutesCnt -= minutes - long hoursCnt = minutesCnt / 60 - return "${minutes} min ${(seconds >= 0 && seconds < 10) ? "0${seconds}" : "${seconds}"} sec" -} - -def getDurationMinDesc(long secondsCnt) { - int seconds = secondsCnt %60 - secondsCnt -= seconds - long minutesCnt = secondsCnt / 60 - long minutes = minutesCnt % 60 - minutesCnt -= minutes - long hoursCnt = minutesCnt / 60 - return "${minutes}" -} - -def lastUpdatedEvent() { - state.lastUpdatedDt = formatDt(new Date())?.toString() - sendEvent(name: 'lastUpdatedDt', value: state.lastUpdatedDt, displayed: false) -} - -def markOffLine() { - log.debug("Watering is set to (Offline)") - sendEvent(name: 'watering', value: "offline", displayed: true) - sendEvent(name: 'valve', value: "closed", displayed: false) - sendEvent(name: 'switch', value: "off", displayed: false) - sendEvent(name: 'curZoneRunStatus', value: "Device is Offline", displayed: false) - sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) -} - -def markStandby() { - log.debug("Watering set to (Standby Mode)") - sendEvent(name: 'watering', value: "standby", displayed: true) - sendEvent(name: 'valve', value: "closed", displayed: false) - sendEvent(name: 'switch', value: "off", displayed: false) - sendEvent(name: 'curZoneRunStatus', value: "Device in Standby Mode", displayed: false) -} - -def isWateringEvent(status, zoneId) { - //log.trace "isWateringEvent..." - def curState = device.currentValue("watering") - def isOn = (status == "PROCESSING") - def newState = isOn ? "on" : "off" - parent?.setWateringDeviceState(device.deviceNetworkId, isOn) - if(curState != newState) { - log.debug("UPDATED: Watering (${newState}) | Previous: (${curState})") - sendEvent(name: 'watering', value: newState, displayed: true) - sendEvent(name: 'switch', value: newState, displayed: false) - sendEvent(name: 'valve', value: (isOn ? "open" : "closed"), displayed: false) - if(curState != null) { - parent?.handleWateringSched(device.deviceNetworkId, isOn) - } - } -} - -def hardwareModelEvent(val) { - def newModel = null // Should these be assigned a defalt value e.g. 'Unknow' ? - def newDesc = null - switch(val) { - case "GENERATION1_8ZONE": - newModel = "8ZoneV1" - newDesc = "8-Zone (Gen 1)" - break - case "GENERATION1_16ZONE": - newModel = "16ZoneV1" - newDesc = "16-Zone (Gen 1)" - break - case "GENERATION2_8ZONE": - newModel = "8ZoneV2" - newDesc = "8-Zone (Gen 2)" - break - case "GENERATION2_16ZONE": - newModel = "16ZoneV2" - newDesc = "16-Zone (Gen 2)" - break - case "GENERATION3_8ZONE": - newModel = "8ZoneV3" - newDesc = "8-Zone (Gen 3)" - break - case "GENERATION3_16ZONE": - newModel = "16ZoneV3" - newDesc = "16-Zone (Gen 3)" - break - } - log.debug "Controller Model ${newModel}" - sendEvent(name: 'hardwareModel', value: newModel, displayed: true) - - log.debug "UPDATED: Controller Description ${newDesc}" - sendEvent(name: 'hardwareDesc', value: newDesc, displayed: true) -} - -def activeZoneCntEvent(zData) { - def zoneCnt = 0 - if (zData) { - zData.each { z -> if(z?.enabled.toString() == "true") { zoneCnt = zoneCnt+1 } } - } - log.debug "Active Zone Count ${zoneCnt}" - sendEvent(name: 'activeZoneCnt', value: zoneCnt, displayed: true) -} - -def controllerOnEvent(val) { - log.debug "Controller On Status ${newState}" - sendEvent(name: 'controllerOn', value: newState, displayed: true) -} - -def lastWateredDateEvent(val, dur) { - def newState = "${epochToDt(val)}" - def newDesc = "${epochToDt(val)}\nDuration: ${getDurationDesc(dur?.toLong())}" - log.debug "Last Watered Date ${newState}" - sendEvent(name: 'lastWateredDt', value: newState, displayed: true) - sendEvent(name: 'lastWateredDesc', value: newDesc, displayed: false) -} - -def rainDelayValEvent(val) { - def newState = val ? val : 0 - log.debug("Rain Delay Value ${newState}") - sendEvent(name: 'rainDelay', value: newState, displayed: true) - setRainDelayString(newState) -} - -def setZoneWaterTime(timeVal) { - def newVal = timeVal ? timeVal.toInteger() : parent?.settings?.defaultZoneTime.toInteger() - log.debug("Manual Zone Water Time (${newVal})") - sendEvent(name: 'curZoneWaterTime', value: newVal, displayed: true) -} - -def scheduleDataEvent(sData, zData, rainDelay) { - //log.trace "scheduleDataEvent($data)..." - state.schedData = sData - state.zoneData = zData - state.rainData = rainDelay - //def curSchedTypeBtnDesc = (!curSchedType || curSchedType in ["off", "manual"]) ? "Pause Disabled" : "Pause Schedule" - state.curSchedType = !sData?.type ? "Off" : sData?.type?.toString().capitalize() - state.curScheduleId = !sData?.scheduleId ? null : sData?.scheduleId - state.curScheduleRuleId = !sData?.scheduleRuleId ? null : sData?.scheduleRuleId - def zoneData = sData && zData ? getZoneData(zData, sData?.zoneId) : null - def zoneId = !zoneData ? null : sData?.zoneId - def zoneName = !zoneData ? null : zoneData?.name - def zoneNum = !zoneData ? null : zoneData?.zoneNumber - - def zoneStartDate = sData?.zoneStartDate ? sData?.zoneStartDate : null - def zoneDuration = sData?.zoneDuration ? sData?.zoneDuration : null - - def timeDiff = sData?.zoneStartDate ? GetTimeValDiff(sData?.zoneStartDate.toLong()) : 0 - def elapsedDuration = sData?.zoneStartDate ? getDurationMinDesc(Math.round(timeDiff)) : 0 - def wateringDuration = zoneDuration ? getDurationMinDesc(zoneDuration) : 0 - def zoneRunStatus = ((!zoneStartDate && !zoneDuration) || !zoneId ) ? "Status: Idle" : "${zoneName}: (${elapsedDuration} of ${wateringDuration} Minutes)" - - def zoneCycleCount = !sData?.totalCycleCount ? 0 : sData?.totalCycleCount - def zoneIsCycling = !sData?.cycling ? false : sData?.cycling - def wateringVal = device.currentValue("watering") - log.debug("ScheduleType ${state.curSchedType}") - sendEvent(name: 'scheduleType', value: state.curSchedType, displayed: true) - if(!state.inStandby && wateringVal != "offline" && isStateChange(device, "curZoneRunStatus", zoneRunStatus)) { - log.debug("UPDATED: ZoneRunStatus (${zoneRunStatus})") - sendEvent(name: 'curZoneRunStatus', value: zoneRunStatus, displayed: false) - } - log.debug("Active Zone Duration (${zoneDuration})") - sendEvent(name: 'curZoneDuration', value: zoneDuration?.toString(), displayed: true) - - log.debug("Current Zone Name (${zoneName})") - sendEvent(name: 'curZoneName', value: zoneName?.toString(), displayed: true) - - log.debug("Active Zone Number (${zoneNum})") - sendEvent(name: 'curZoneNumber', value: zoneNum, displayed: true) - log.debug("Zone Cycle Count (${zoneCycleCount})") - sendEvent(name: 'curZoneCycleCount', value: zoneCycleCount, displayed: true) - - sendEvent(name: 'curZoneIsCycling', value: zoneIsCycling?.toString().capitalize(), displayed: true) - - log.debug("Zone StartDate (${(zoneStartDate ? epochToDt(zoneStartDate).toString() : "Not Active")})") - sendEvent(name: 'curZoneStartDate', value: (zoneStartDate ? epochToDt(zoneStartDate).toString() : "Not Active"), displayed: true) -} - -def getZoneData(zData, zId) { - if (zData && zId) { - return zData.find { it?.id == zId } - } -} - -def incZoneWaterTime() { - // log.debug("Decrease Zone Runtime"); - def value = device.latestValue('curZoneWaterTime') - setZoneWaterTime(value + 1) -} - -def decZoneWaterTime() { - // log.debug("Increase Zone Runtime"); - def value = device.latestValue('curZoneWaterTime') - setZoneWaterTime(value - 1) -} - -def setRainDelayString( rainDelay) { - def rainDelayStr = "No Rain Delay"; - if( rainDelay > 0) { - rainDelayStr = "Rain Delayed"; - } - sendEvent(name: "rainDelayStr", value: rainDelayStr) -} - -def doSetRainDelay() { - def value = device.latestValue('rainDelay') - log.debug "Set Rain Delay ${value}" - if (parent?.setRainDelay(this, state.deviceId, value)) { - setRainDelayString(value) - } else { - markOffLine() - } - -} - -def updateRainDelay(value) { - log.debug "Update ${value}" - if (value > 7) { - value = 7; - } else if (value < 0) { - value = 0 - } - sendEvent(name: "rainDelayStr", value: "Set New Rain Delay") - sendEvent(name: 'rainDelay', value: value, displayed: true) -} - -def increaseRainDelay() { - log.debug "Increase Rain Delay" - def value = device.latestValue('rainDelay') - updateRainDelay(value + 1) -} - -def decreaseRainDelay() { - log.debug "Decrease Rain Delay" - def value = device.latestValue('rainDelay') - updateRainDelay(value - 1) -} - -def refresh() { - //log.trace "refresh..." - parent?.poll(this) -} - -def isCmdOk2Run() { - //log.trace "isCmdOk2Run..." - if (device.currentValue("DeviceWatch-DeviceStatus") == "online") { - if (!(state.pauseInStandby && state.inStandby)) { - return true - } - log.warn "Skipping the request... Because the controller is unable to send commands while it is in standby mode!!!" - } else { - log.warn "Skipping the request... Because the zone is unable to send commands while it's in an Offline State." - } - return false -} - -def runAllZones() { - log.trace "runAllZones..." - if (isCmdOk2Run()) { - def waterTime = device.latestValue('curZoneWaterTime') - log.debug "Sending Run All Zones for (${waterTime} Minutes)" - if (!parent?.runAllZones(this, state.deviceId, waterTime)) { - markOffLine() - } - } -} - -def pauseScheduleRun() { - log.trace "pauseScheduleRun... NOT AVAILABLE YET!!!" - if (state.curSchedType == "automatic") { - parent?.pauseScheduleRun(this) - } -} - -def standbyOn() { - log.trace "standbyOn..." - if (device.currentValue("watering") == "offline") { - log.debug "Device is currently Offline... Ignoring..." - } else if (device.currentValue("standbyMode") == "on") { - log.debug "Device is Already in Standby... Ignoring..." - } else { - if (parent?.standbyOn(this, state.deviceId)) { - sendEvent(name: 'standbyMode', value: "on", displayed: true) - } - } -} - -def standbyOff() { - log.trace "standbyOff..." - def inStandby = device.currentValue("standbyMode") == "on" ? true : false - if (device.currentValue("watering") == "offline") { - log.debug "Device is currently Offline... Ignoring..." - } else if (device.currentValue("standbyMode") == "on") { - if (parent?.standbyOff(this, state.deviceId)) { - sendEvent(name: 'standbyMode', value: "off", displayed: true) - } - } else { - log.debug "Device is Already out of Standby... Ignoring..." - } -} - -def on() { - log.trace "on..." - if (isCmdOk2Run()) { - if (device.currentValue("switch") == "off") { - open() - } else { - log.debug "Switch is Already ON... Ignoring..." - } - } -} - -def off() { - log.trace "off..." - if (device.currentValue("switch") == "on") { - close() - } else { - log.debug "Switch is Already OFF... Ignoring..." - } -} - -def open() { - log.debug "open command is not currently supported by the controller device..." -} - -def close() { - log.trace "close()..." - if (device.currentValue("valve") == "open") { - if (parent?.off(this, state.deviceId)) { - sendEvent(name:'watering', value: "off", displayed: true) - sendEvent(name:'switch', value: "off", displayed: false) - sendEvent(name:'valve', value: "closed", displayed: false) - } else { - log.trace "close(). marking offline" - markOffLine() - } - } else { - log.debug "Close command Ignored... The Valve is Already Closed" - } -} - -// To be used directly by smart apps -def stopWatering() { - log.trace "stopWatering" - close() -} - -def setRainDelay(rainDelay) { - sendEvent("name":"rainDelay", "value": value) - parent?.setRainDelay(this, value) -} - -def getDtNow() { - def now = new Date() - return formatDt(now, false) -} - -def epochToDt(val) { - return formatDt(new Date(val)) -} - -def formatDt(dt, mdy = true) { - def formatVal = mdy ? "MMM d, yyyy - h:mm:ss a" : "E MMM dd HH:mm:ss z yyyy" - def tf = new SimpleDateFormat(formatVal) - if (location?.timeZone) { - tf.setTimeZone(location?.timeZone) - } - return tf.format(dt) -} - -//Returns time differences is seconds -def GetTimeValDiff(timeVal) { - try { - def start = new Date(timeVal).getTime() - def now = new Date().getTime() - def diff = (int) (long) (now - start) / 1000 - return diff - } - catch (ex) { - log.error "GetTimeValDiff Exception: ${ex}" - return 1000 - } -} - -def getTimeDiffSeconds(strtDate, stpDate=null) { - if((strtDate && !stpDate) || (strtDate && stpDate)) { - def now = new Date() - def stopVal = stpDate ? stpDate.toString() : formatDt(now, false) - def start = Date.parse("E MMM dd HH:mm:ss z yyyy", strtDate).getTime() - def stop = Date.parse("E MMM dd HH:mm:ss z yyyy", stopVal).getTime() - def diff = (int) (long) (stop - start) / 1000 - return diff - } else { - return null - } -} diff --git a/devicetypes/rachio/rachio-iro2-zone.src/rachio-iro2-zone.groovy b/devicetypes/rachio/rachio-iro2-zone.src/rachio-iro2-zone.groovy deleted file mode 100644 index 3464acb15e0..00000000000 --- a/devicetypes/rachio/rachio-iro2-zone.src/rachio-iro2-zone.groovy +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Rachio IRO2 Zone Device Handler - * - * Copyright\u00A9 2017, 2018 Franz Garsombke - * Written by Anthony Santilli (@tonesto7) - * - * 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: "Rachio Zone", namespace: "rachio", author: "Rachio") { - capability "Refresh" - capability "Switch" - capability "Actuator" - capability "Valve" - capability "Sensor" - capability "Health Check" - } - - simulator { - // TODO: define status and reply messages here - } - - tiles (scale: 2){ - multiAttributeTile(name: "valveTile", type: "generic", width: 6, height: 4) { - tileAttribute("device.switch", key: "PRIMARY_CONTROL" ) { - attributeState "off", label: 'Off', action: "open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"on" - attributeState "on", label: 'Watering', action: "close", icon: "st.valves.water.open", backgroundColor: "#00a0dc", nextState: "off" - } - } - } - main "valveTile" - details(["valveTile"]) -} - -// parse events into attributes -def parse(String description) { - log.debug "Parsing '${description}'" -} - -def initialize() { - sendEvent(name: "DeviceWatch-Enroll", value: groovy.json.JsonOutput.toJson(["protocol":"cloud", "scheme":"untracked"]), displayed: false) -} - -void installed() { - state.isInstalled = true - initialize() - sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) -} - -void updated() { - initialize() -} - -// NOP implementation of ping as health check only calls this for tracked devices -// But as capability defines this method it's implemented to avoid MissingMethodException -def ping() { - log.info "unexpected ping call from health check" -} - -def generateEvent(Map results) { - if (results) { - if (!results.data?.enabled) { - sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) - return - } - // log.debug results - if (results.status == "ONLINE") { - sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false) - } else { - markOffLine() - } - } -} - -def refresh() { - parent?.poll(this) -} - -def on() { - log.trace "zone on..." - if (isCmdOk2Run()) { - if (device.currentValue("switch") == "off") { - open() - } else { - log.debug "Zone is Already ON... Ignoring.." - } - } -} - -def off() { - log.trace "zone off..." - if (device.currentValue("switch") == "on") { - close() - } else { - log.debug "Zone is Already OFF... Ignoring..." - } -} - -def open() { - log.trace "Zone open()..." - if (isCmdOk2Run()) { - if (device.currentValue("valve") == "closed") { - startZone() - } else { - log.debug "Valve is Already Open... Ignoring..." - } - } -} - -def close() { - log.trace "Zone close()..." - if (device.currentValue("valve") == "open") { - if (parent?.off(this, state.deviceId)) { - log.info "Zone was Stopped Successfully..." - sendEvent(name:'switch', value: "off", displayed: false) - sendEvent(name:'valve', value: "closed", displayed: false) - } - } else { - log.debug "Valve is Already Closed... Ignoring..." - } -} - -def markOffLine() { - log.trace "Watering (Offline)" - sendEvent(name: 'valve', value: "closed", displayed: false) - sendEvent(name: 'switch', value: "off", displayed: false) - sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) -} - -def startZone() { - log.trace "startZone()..." - if (isCmdOk2Run()) { - def zoneNum = device.latestValue('zoneNumber') - def waterTime = 10; - log.debug("Starting Watering for Zone (${zoneNum}) for (${waterTime}) Minutes") - if (parent?.startZone(this, state.deviceId, zoneNum, waterTime)) { - log.debug "runThisZone was Sent Successfully" - sendEvent(name:'switch', value: "on", displayed: false) - sendEvent(name:'valve', value: "open", displayed: false) - } else { - markOffLine() - } - } -} - -// To be used directly by smart apps -def stopWatering() { - log.trace "stopWatering" - close() -} - -def isCmdOk2Run() { - //log.trace "isCmdOk2Run..." - if (device.currentValue("DeviceWatch-DeviceStatus") == "offline") { - log.warn "Skipping the request... Because the zone is unable to send commands while it's in an Offline State." - return false - } - return true -} diff --git a/smartapps/rachio/rachio-connect.src/rachio-connect.groovy b/smartapps/rachio/rachio-connect.src/rachio-connect.groovy deleted file mode 100644 index 087cd7bb0c1..00000000000 --- a/smartapps/rachio/rachio-connect.src/rachio-connect.groovy +++ /dev/null @@ -1,1360 +0,0 @@ -/** - * Rachio (Connect) Smart App - * - * Copyright\u00A9 2017, 2018 Franz Garsombke - * Written by Anthony Santilli (@tonesto7) - * - * 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. - * - */ - -import groovy.json.* -import java.text.SimpleDateFormat - -definition( - name: "Rachio (Connect)", - namespace: "rachio", - author: "Rachio", - description: "Connect your Rachio Sprinklers to SmartThings.", - category: "Green Living", - iconUrl: "https://s3-us-west-2.amazonaws.com/rachio-media/smartthings/Rachio-logo-100px.png", - iconX2Url: "https://s3-us-west-2.amazonaws.com/rachio-media/smartthings/Rachio-logo-200px.png", - iconX3Url: "https://s3-us-west-2.amazonaws.com/rachio-media/smartthings/Rachio-logo-300px.png", - singleInstance: true, - oauth: true, - usesThirdPartyAuthentication: true, - pausable: false -) - -{ - appSetting "clientId" - appSetting "clientSecret" - appSetting "serverUrl" - appSetting "apiUrl" - appSetting "appUrl" -} - -preferences { - page(name: "startPage") - page(name: "authPage") - page(name: "devicePage") - page(name: "devMigrationPage") - page(name: "supportPage") -} - -mappings { - path("/oauth/initialize") { action: [GET: "init"] } - path("/oauth/callback") { action: [ GET: "callback" ] } - path("/rachioReceiver") { action: [ POST: "rachioReceiveHandler" ] } -} - -def appVer() { return "2.0.0" } - -def appInfoSect() { - section() { - paragraph "Rachio (Connect)\n" + - "Copyright\u00A9 2017, 2018 Rachio, Inc.\n" + - "Version: ${appVer()}", - image: "https://s3-us-west-2.amazonaws.com/rachio-media/smartthings/Rachio-logo-100px.png" - } -} - -def startPage() { - if(atomicState.authToken) { - if(!settings.controllers && settings.sprinklers) { - devMigrationPage() - } else { - devicePage() - } - } else { - authPage() - } -} - -// Begin OAuth stuff -//Section2: page-related methods --------------------------------------------------------------------------------------- -def authPage() { - //log.debug "authPage()" - getAccessToken() - - def description = null - def uninstallAllowed = false - def oauthTokenProvided = false - //This is 3rd party cloud accessToken - if(atomicState.authToken) { - getRachioDeviceData(true) - def usrName = atomicState.userName ?: "" - description = usrName ? "You are signed in as $usrName" : "You are connected." - uninstallAllowed = true - oauthTokenProvided = true - } else { - description = "Login to Rachio..." - } - - //redirectUrl to be called back for code exchange - def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state?.accessToken}&apiServerUrl=${shardUrl}&client_name=smartthings" - - if (!oauthTokenProvided) { - log.debug "No Rachio AuthToken Found... Please Login..." - } - def authPara = !oauthTokenProvided ? "Tap to Login into Rachio service and authorize SmartThings access" : "Tap Next to setup your sprinklers." - return dynamicPage(name: "authPage", title: "Auth Page", nextPage: (oauthTokenProvided ? "devicePage" : null), uninstall: uninstallAllowed) { - appInfoSect() - section() { - paragraph authPara - href url: redirectUrl, style: "embedded", required: (!oauthTokenProvided), state: (oauthTokenProvided ? "complete" : ""), title: "Rachio", description: description - } - if(uninstallAllowed) { removeSect() } - } -} - -def removeSect() { - remove("Remove this App and Devices!", "WARNING!!!", "Last Chance to Stop!\nThis action is not reversible\n\nThis App and All Devices will be removed") -} - -def devMigrationPage() { - return dynamicPage(name: "devMigrationPage", title: "Migration Page", nextPage: "devicePage", install: false, uninstall: false) { - section() { - paragraph "This SmartApp was updated to support multiple controllers.\nYour previous controller and zone selections are being migrated to the new data structure.", required: true, state: null - } - section() { - log.debug "Migrating Controller and Zone Selections to New Data Structure..." - List devs = [] - String id = settings.sprinklers - devs.push(id as String) - app.updateSetting("controllers", [type: "enum", value: devs]) - log.debug "Controllers: ${settings.controllers}" - if(settings.selectedZones) { - List zones = settings.selectedZones?.collect { it as String } - if(zones) { - app.updateSetting("${id}_zones", [type: "enum", value: zones]) - } - log.debug "Controller($id) Zones: ${settings."${id}_zones"}" - } - paragraph "Setting Migration Complete...\n\nTap Next to Proceed to Device Configuration", state: "complete" - } - } -} - -// This method is called after "auth" page is done with Oauth2 authorization, then page "deviceList" with content of devicePage() -def devicePage() { - //log.trace "devicePage()..." - if(!atomicState.authToken) { - log.debug "No accesstoken" - return - } - // Step 1: get (list) of available devices associated with the login account. - def devData = getRachioDeviceData() - def devices = getDeviceInputEnum(devData) - // log.debug "rachioDeviceList(): ${devices}" - - //step2: render the page for user to select which device - return dynamicPage(name: "devicePage", title: "${(atomicState.authToken && atomicState.selectedDevices) ? "Select" : "Manage"} Your Devices", install: true, uninstall: true) { - appInfoSect() - section("Controller Configuration:"){ - input "controllers", "enum", title: "Select your controllers", description: "Tap to Select", required: true, multiple: true, options: devices, submitOnChange: true, image: (atomicState.modelInfo ? atomicState.modelInfo.img : "") - atomicState.controllerIds = settings.controllers - } - if(settings.controllers) { - updateHwInfoMap(devData?.devices) - devices?.sort { it?.value }?.each { cont-> - if(cont?.key in settings.controllers) { - section("${cont?.value} Zones:"){ - if(settings."${cont?.key}_zones") { - def dData = devData?.devices?.find { it?.id == cont?.key } - if(dData) { devDesc(dData) } - } - def zoneData = zoneSelections(devData, cont?.key) - input "${cont?.key}_zones", "enum", title: "Select your zones", description: "Tap to Select", required: true, multiple: true, options: zoneData, submitOnChange: true - } - } - } - section("Preferences:") { - input(name: "pauseInStandby", title: "Disable Actions while in Standby?", type: "bool", defaultValue: true, multiple: false, submitOnChange: true, - description: "Allow your device to be disabled in SmartThings when you place your controller in Standby Mode...") - paragraph "Select the Duration time to be used for manual Zone Runs (This can be changed under each zones device page)" - input(name: "defaultZoneTime", title: "Default Zone Runtime (Minutes)", type: "number", description: "Tap to Modify", required: false, defaultValue: 10, submitOnChange: true) - } - } - section() { - href "supportPage", title: "Rachio Support", description: "" - href "authPage", title: "Manage Login", description: "" - } - removeSect() - } -} - -void settingUpdate(name, value, type=null) { - log.trace "settingUpdate($name, $value, $type)..." - if(name && type) { - app.updateSetting("$name", [type: "$type", value: value]) - } - else if (name && type == null){ app.updateSetting(name.toString(), value) } -} - -void settingRemove(name) { - log.trace "settingRemove($name)..." - if(name) { - app.deleteSetting("$name") - } -} - -void appCleanup() { - log.trace "appCleanup()" - def stateItems = ["deviceId", "selectedDevice", "selectedZones", "inStandbyMode", "webhookId", "isWateringMap", "inStandbyModeMap"] - def setItems = ["sprinklers", "selectedZones"] - stateItems?.each { if(state.containsKey(it as String)) {state.remove(it)} } - setItems?.each { if(settings.containsKey(it as String)) {settingRemove(it)} } -} - -def devDesc(dev) { - if(dev) { - def zoneCnt = dev?.zones?.findAll { it?.id in settings."${dev?.id}_zones" }?.size() ?: 0 - def str = "${atomicState.installed ? "Installed" : "Installing"} Device:\n${atomicState.modelInfo[dev?.id]?.desc}\n" + - "\n($zoneCnt) Zone(s) ${atomicState.installed ? "are selected" : "will be installed"}" - paragraph str, state: "complete", image: (atomicState.modelInfo[dev?.id]?.img ?: "") - } -} - -def supportPage() { - return dynamicPage(name: "supportPage", title: "Rachio Support", install: false, uninstall: false) { - section() { - href url: getSupportUrl(), style:"embedded", title:"Rachio Support (Web)", description:"", state: "complete", - image: "http://rachio-media.s3.amazonaws.com/images/icons/icon-support.png" - href url: getCommunityUrl(), style:"embedded", title:"Rachio Community (Web)", description:"", state: "complete", - image: "http://d33v4339jhl8k0.cloudfront.net/docs/assets/5355b85be4b0d020874de960/images/58333550903360645bfa6cf8/file-Er3y7doeam.png" - } - } -} - -def zoneSelections(devData, devId=null) { - //log.debug "zoneSelections: $devData" - def res = [:] - if(!devData) { return res } - devData?.devices.sort {it?.name}.each { dev -> - if(dev?.id == devId) { - dev?.zones?.sort {it?.zoneNumber }.each { zone -> - def str = (zone?.enabled == true) ? "" : " (Disabled)" - //log.debug "zoneId: $zone.id" - def adni = [zone?.id].join('.') - res[adni] = "${zone?.name}$str" - } - } - } - return res -} - -// This was added to handle missing oauth on the smartapp and notifying the user of why it failed. -def getAccessToken() { - try { - if(!atomicState.accessToken) { - atomicState.accessToken = createAccessToken() - } - } - catch (ex) { - log.warn "Error: OAuth is not Enabled for the Rachio (Connect) application!!!. Please click remove and Enable Oauth under the SmartApp App Settings in the IDE..." - } -} - -//1. redirect SmartApp to prompt user to input his/her credentials on 3rd party cloud service -def init() { - //log.debug "init()" - def stcid = getClientId() - //log.debug "Rachio OAuth Client ID: ${stcid}" - - def oauthParams = [ - response_type: "code", - client_id: stcid, - redirect_uri: callbackUrl, - client_name: "smartthings" - ] - - def loc = "${appEndpoint}/oauth?${toQueryString(oauthParams)}" - //log.debug "OAuth Callback URL: ${loc}" - redirect(location: loc) - -} - -/* 2.0 Obtain authorization_code, access_token, refresh_token to be used with API calls - 2.1 get authorization_code from 3rd party cloud service - 2.2 use authorization_code to get access_token, refresh_token, and expire from 3rd party cloud service -*/ -def callback() { - //log.debug "callback()>> params.code ${params.code}" - def appKey = !appSettings?.clientId ? "smartthings" : appSettings.clientId - def tokenParams = [ - headers: ["Authorization": "Basic $appKey", "Content-Type": "application/x-www-form-urlencoded"], - uri: "${apiEndpoint}/1/oauth/token_2_0", - body: [ - grant_type:'authorization_code', - code:params.code, - redirect_uri: callbackUrl, - client_id : getClientId(), - client_secret: getClientSecret() - ] - ] - - try { - httpPost(tokenParams) { resp -> - atomicState.authToken = resp?.data.access_token.toString() - atomicState.refreshToken = resp?.data.refresh_token.toString() - atomicState.authTokenExpiresIn = resp?.data.expires_in.toString() - //log.debug "Response: ${resp?.data}" //Hiding from Release - } - } catch (groovyx.net.http.HttpResponseException e) { - log.error "Error: ${e?.statusCode}" - log.debug "Response headers: ${e?.response?.allHeaders}" - log.debug "Data: ${e?.response?.data}" - } - - if (atomicState.authToken) { - success() - } else { - fail() - } -} - -def success() { - def message = """ -

Your Rachio Account is now connected to SmartThings!

-

Click 'Done' to finish setup.

- """ - connectionStatus(message) -} - -def fail() { - def message = """ -

The connection could not be established!

-

Click 'Done' to return to the menu.

- """ - connectionStatus(message) -} - -// End OAuth Stuff - -def connectionStatus(message, redirectUrl = null) { - def redirectHtml = "" - if (redirectUrl) { - redirectHtml = """ - - """ - } - - def html = """ - - - - - Withings Connection - - ${redirectHtml} - - -
- - connected device icon - SmartThings logo - ${message} -
- - - """ - render contentType: 'text/html', data: html -} - -def revokeRachioToken() { - def params = [ - method: 'POST', - uri: "${apiEndpoint}/1/oauth/revoke", - headers: ["Content-Type": "application/x-www-form-urlencoded"], - body: [ - "client_id":getClientId(), - "token": atomicState.authToken, - "client_secret":getClientSecret() - ] - ] - //log.debug("revokeRachioToken params: $params) - try { - httpPost(params) { resp -> - if (resp.status == 200) { - atomicState.authToken = null - log.warn "Your Rachio Token has been revoked successfully..." - return true - } - } - } - catch (ex) { - log.error "revokeRachioToken Exception: ${ex}" - return false - } -} - -def getRachioDeviceData(noData=false) { - //log.trace "getRachioDevicesData($noData)..." - - //Step1: GET account info "userId" - atomicState.userId = getUserId(); - if (!atomicState.userId) { - log.error "No user Id found exiting" - return - } - def userInfo = getUserInfo(atomicState.userId) - //log.debug "userInfo: ${userInfo}" - atomicState.userName = userInfo?.username - - if (!noData) { - return userInfo - } -} - -def getDeviceInputEnum(data) { - //Step3: Obtain device information for a location - def devices = [:] - if(!data) { return devices } - data?.devices.sort { it?.name }.each { sid -> - //log.debug "systemId: ${sid.id}" - def dni = sid?.id - devices[dni] = sid?.name - //log.debug "Found sprinkler with dni(locationId.gatewayId.systemId.zoneId): $dni and displayname: ${devices[dni]}" - } - // log.debug "getRachioDevicesData() >> sprinklers: $devices" - return devices -} - -def zoneMap(data, onlySelected=false) { - def zoneMap = [:] - if(data) { - data?.sort { it?.zoneNumber }.each { zn -> - if(onlySelected && !zn?.id in selDevs) { return } - def zdni = [zn?.id].join('.') - zoneMap[zdni] = zn?.name - } - } - return zoneMap -} - -def getUserInfo(userId) { - //log.trace "getUserInfo ${userId}" - return _httpGet("person/${userId}"); -} - -def getUserId() { - //log.trace "getUserId()" - def res = _httpGet("person/info"); - if (res) { - return res?.id; - } - return null -} - -void updateHwInfoMap(devdata) { - def result = [:] - if(devdata && settings.controllers) { - def results = null - results = devdata?.findAll { it?.id in settings.controllers } - results?.each { dev -> - result[dev?.id] = getHardwareInfo(dev?.model) - } - } - atomicState.modelInfo = result -} - -def getHardwareInfo(val) { - switch(val) { - case "GENERATION1_8ZONE": - return [model: "8ZoneV1", desc: "8-Zone (Gen 1)", img: getAppImg("rachio_gen1.png"), gen: "Gen1"] - case "GENERATION1_16ZONE": - return [model: "16ZoneV1", desc: "16-Zone (Gen 1)", img: getAppImg("rachio_gen1.png"), gen: "Gen1"] - case "GENERATION2_8ZONE": - return [model: "8ZoneV2", desc: "8-Zone (Gen 2)", img: getAppImg("rachio_gen2.png"), gen: "Gen2"] - case "GENERATION2_16ZONE": - return [model: "16ZoneV2", desc: "16-Zone (Gen 2)", img: getAppImg("rachio_gen2.png"), gen: "Gen2"] - case "GENERATION3_8ZONE": - return [model: "8ZoneV3", desc: "8-Zone (Gen 3)", img: getAppImg("rachio_gen3.png"), gen: "Gen3"] - case "GENERATION3_16ZONE": - return [model: "16ZoneV3", desc: "16-Zone (Gen 3)", img: getAppImg("rachio_gen3.png"), gen: "Gen3"] - } - return [desc: null, model: null, img: "", gen: null] -} - -def getAppImg(imgName) { - return "https://raw.githubusercontent.com/tonesto7/rachio-manager/master/images/$imgName" -} - -def _httpGet(subUri) { - //log.debug "_httpGet($subUri)" - try { - def params = [ - uri: "${apiEndpoint}/1/public/${subUri}", - headers: ["Authorization": "Bearer ${atomicState.authToken}"] - ] - httpGet(params) { resp -> - if(resp.status == 200) { - return resp?.data - } else { - //refresh the auth token - if (resp?.status == 500 && resp?.data?.status?.code == 14) { - log.debug "Currently not Refreshing your authToken!" - // refreshAuthToken() - } else { - log.error "Authentication error, invalid authentication method, lack of credentials, etc." - } - return null - } - } - } catch (groovyx.net.http.HttpResponseException ex) { - if (ex?.response) { - log.error "httpGet() Response Exception | Status: ${ex?.response?.status} | Data: ${ex?.response?.data}" - } else { - log.error "httpGet() Response Exception | Status: ${ex}" - } - } catch (ex) { - log.error "_httpGet exception: ${ex.message}" - } -} - -def getDisplayName(iroName, zname) { - if(zname) { - return "${iroName}:${zname}" - } else { - return "Rachio" - } -} - -//Section3: installed, updated, initialize methods -def installed() { - log.trace "Installed with settings: ${settings}" - // initialize will be called by the updated method - atomicState.installed = true -} - -def updated() { - log.debug "Updated with settings: ${settings}" - unschedule() - unsubscribe() - initialize() -} - -def initialize() { - log.trace "initialized..." - scheduler() - subscribe(app, onAppTouch) - updateDevZoneStates() //Creates the selectedDevices maps in state - runIn(2, "initStep2", [overwrite: true]) - sendActivityFeeds("is connected to SmartThings") - atomicState.timeSendPush = null -} - -void initStep2() { - addRemoveDevices() - appCleanup() - runIn(3, "initStep3", [overwrite: true]) -} - -void initStep3() { - initWebhooks() - poll() -} - -def uninstalled() { - log.trace "uninstalled() called... removing smartapp and devices" - unschedule() - - //Remove any existing webhooks before uninstall... - removeAllWebhooks() - if(addRemoveDevices(true)) { - //Revokes Smartthings endpoint token... - revokeAccessToken() - //Revokes Rachio Auth Token - if(atomicState.authToken) { - revokeRachioToken() - atomicState.authToken = null - } - } -} - -def onAppTouch(event) { - updated() -} - -def scheduler() { - runEvery15Minutes("heartbeat") -} - -def heartbeat() { - log.trace "heartbeat 15 minute keep alive poll()..." - poll() -} - -void initWebhooks() { - settings.controllers?.each { c-> - if(c) { - initWebhook(c) - // log.debug "webhooks($c): ${getWebhookIdsForDev(c)}" - } - } -} - -//Subscribes to the selected controllers API events that will be used to trigger a poll -def initWebhook(controlId) { - //log.trace "initWebhook..." - def result = false - def whId = atomicState.webhookIds ?: [:] - def cmdType = whId[controlId] == null ? "post" : "put" - def apiWebhookUrl = "${rootApiUrl()}/notification/webhook" - def endpointUrl = apiServerUrl("/api/token/${atomicState.accessToken}/smartapps/installations/${app.id}/rachioReceiver") - def bodyData - if(!whId[controlId]) { - bodyData = new JsonOutput().toJson([device:[id: controlId], externalId: app.id, url: endpointUrl, eventTypes: webhookEvtTypes()]) - } else { - bodyData = new JsonOutput().toJson([id: whId[controlId], externalId: app.id, url: endpointUrl, eventTypes: webhookEvtTypes()]) - } - try { - if(webhookHttp(apiWebhookUrl, bodyData, cmdType, controlId)) { - log.debug "Successfully ${cmdType == "post" ? "Created" : "Updated"} API Webhook Subscription for Controller (${controlId})!!!" - result = true - } - } catch(ex) { - log.error "initWebhook Exception: ${ex.message} | Data sent: ${bodyData}" - } - return result -} - -//This isn't used for anything other than to return the webhooks for the device -def getWebhookIdsForDev(devId) { - if(!devId) { return null } - def data = _httpGet("notification/${devId}/webhook") - def res = null - if(data) { res = data?.findAll { it?.externalId == app.id }?.collect { it?.id } } - return res -} - -void removeWebhookByDevId(devId) { - def webhookIds = atomicState.webhookIds - if(webhookIds && webhookIds[devId] != null) { - if(webhookHttp("${rootApiUrl()}/notification/webhook/${webhookIds[devId]}", "", "delete")) { - log.warn "Removed API Webhook Subscription for (${webhookIds[devId]})" - } - } -} - -//Removes the webhook subscription for the device. -void removeAllWebhooks() { - def webhookIds = atomicState.webhookIds - if(settings.controllers && webhookIds) { - settings.controllers?.each { c-> - if(c) { - if(webhookHttp("${rootApiUrl()}/notification/webhook/${webhookIds[c]}", "", "delete")) { - log.warn "Removed API Webhook Subscription for (${webhookIds[c]})" - } - } - } - } -} - -//Returns the available event types to subscribe to. -def webhookEvtTypes() { - def typeIds = [] - def okTypes = ["DEVICE_STATUS_EVENT", "ZONE_STATUS_EVENT"] //, "WEATHER_INTELLIGENCE_EVENT", "RAIN_DELAY_EVENT"] - def data = _httpGet("notification/webhook_event_type") - if(data) { - typeIds = data?.findAll { it?.name in okTypes }.collect { ["id":it?.id?.toString()] } - } - return typeIds -} - -//Handles the http requests for the webhook methods -def webhookHttp(url, jsonBody, type=null, ctrlId) { - //log.trace "webhookHttp($url, $jsonBody, $type)" - def returnStatus = false - def response = null - def cmdParams = [ - uri: url, - headers: ["Authorization": "Bearer ${atomicState.authToken}", "Content-Type": "application/json"], - body: jsonBody - ] - try { - if(type == "post") { - httpPost(cmdParams) { resp -> - response = resp - } - } - else if(type == "put") { - httpPut(cmdParams) { resp -> - response = resp - } - } - else if(type == "delete") { - httpDelete(cmdParams) { resp -> - response = resp - } - } - if(response) { - //log.debug "response.status: ${response?.status} | data: ${response?.data}" - if(response?.status in [200, 201, 204]) { - returnStatus = true - if(type in ["put", "post"]) { - def whIds = atomicState.webhookIds ?: [:] - whIds[ctrlId] = response?.data?.id - atomicState.webhookIds = whIds - } else if(type == "delete") { - def whIds = atomicState.webhookIds ?: [:] - whIds?.remove(ctrlId) - atomicState.webhookIds = whIds - } - } else { - //refresh the auth token - if (response?.status == 401) { - log.debug "Refreshing your authToken!" - // refreshAuthToken() - } else { - log.error "Authentication error, invalid authentication method, lack of credentials, etc." - } - } - } else { return returnStatus } - } catch(Exception e) { - log.error "webhookHttp Exception Error: ", e - } - return returnStatus -} - -def getDeviceIds() { - return settings.controllers ?: null -} - -def getZoneIds(devId) { - return settings."${devId}_zones" ?: null -} - -def getZoneData(userId, zoneId) { - return _httpGet("person/${userId}/${zoneId}") -} - -void updateDevZoneStates() { - def devMap = [:] - def userInfo = getUserInfo(atomicState.userId) - userInfo?.devices?.each { dev -> - if(dev?.id in settings.controllers) { - devMap[dev?.id] = [:] - devMap[dev?.id]["name"] = dev?.name - def zoneMap = [:] - dev?.zones?.each { zone -> - if(zone?.id in settings."${dev?.id}_zones") { - zoneMap[zone?.id] = [:] - zoneMap[zone?.id] = zone?.name - } - } - devMap[dev?.id]["zones"] = zoneMap - } - } - atomicState.selectedDevices = devMap -} - -def getDeviceInfo(devId) { - //log.trace "getDeviceInfo..." - return _httpGet("device/${devId}") -} - -def getCurSchedule(devId) { - //log.trace "getCurSchedule..." - return _httpGet("device/${devId}/current_schedule") -} - -def getDeviceData(devId) { - //log.trace "getDeviceData($devId)..." - return _httpGet("device_with_current_schedule/${devId}") -} - -def rootApiUrl() { return "https://api.rach.io/1/public" } - -def cleanupObjects(id){ - if(settings."${id}_zones") { settingRemove("${id}_zones") } - def whIds = atomicState.webhookIds - if(whIds && whIds[id]) { removeWebhookByDevId(id) } -} - -def isWatering() { - def i = atomicState.isWateringMap?.findAll { it?.value == true } - return (i?.size() > 0) -} - -def removeWateringItem(id) { - def i = atomicState.isWateringMap ?: [:] - if(id && i[id] != null) { i?.remove(id) } - atomicState.isWateringMap = i -} - -def removeStandbyItem(id) { - def i = atomicState.inStandbyModeMap ?: [:] - if(id && i[id] != null) { i?.remove(id) } - atomicState.inStandbyModeMap = i -} - -def updateWateringItem(id, val) { - def i = atomicState.isWateringMap ?: [:] - if(id && i != null) { i[id] = val } - atomicState.isWateringMap = i -} - -def updateStandbyItem(String id, Boolean val) { - def i = atomicState.inStandbyModeMap ?: [:] - if(id && i != null) { i[id] = val } - atomicState.inStandbyModeMap = i -} - -def addRemoveDevices(uninst=false) { - try { - def delete = [] - if(uninst == false) { - def devsInUse = [] - def selectedDevices = atomicState.selectedDevices - selectedDevices?.each { contDev -> - //Check if the discovered sprinklers are already initiated with corresponding device types. - def d = getChildDevice(contDev?.key) - if(!d) { - d = addChildDevice(app.namespace, getChildContName(), contDev?.key, null, [label: getDeviceLabelStr(contDev?.value?.name)]) - d.completedSetup = true - log.debug "Controller Device Created: (${d?.displayName}) with id: [${contDev?.key}]" - } else { - //log.debug "found ${d?.displayName} with dni: ${dni?.key} already exists" - } - devsInUse += contDev.key - contDev?.value?.zones?.each { zoneDni -> - //Check if the discovered sprinklers are already initiated with corresponding device types. - def d2 = getChildDevice(zoneDni?.key) - if(!d2) { - d2 = addChildDevice(app.namespace, getChildZoneName(), zoneDni?.key, null, [label: getDeviceLabelStr(zoneDni?.value)]) - d2.completedSetup = true - log.debug "Zone Device Created: (${d2?.displayName}) with id: [${zoneDni?.key}]" - } else { - //log.debug "found ${d2?.displayName} with dni: ${zoneDni?.key} already exists" - } - devsInUse += zoneDni?.key - } - } - //log.debug "devicesInUse: ${devsInUse}" - delete = app.getChildDevices(true).findAll { !(it?.deviceNetworkId in devsInUse) } - } else { - atomicState.selectedDevices = [] - delete = app.getChildDevices(true) - } - if(delete?.size() > 0) { - log.warn "Device Delete: ${delete} | Removing (${delete?.size()}) Devices..." - delete?.each { - cleanupObjects(it?.deviceNetworkId) - deleteChildDevice(it?.deviceNetworkId, true) - log.warn "Deleted the Device: ${it?.displayName}" - } - } - return true - } catch (physicalgraph.exception.ConflictException ex) { - log.warn "Error: Can't Delete App because Devices are still in use in other Apps, Routines, or Rules. Please double check before trying again." - } catch (ex) { - log.error "addRemoveDevices Exception: ${ex}" - return false - } -} - -def getDeviceLabelStr(name) { - return "Rachio - ${name}" -} - -def getTimeSinceInSeconds(time) { - if(!time) { return 10000 } - return (int) (now() - time)/1000 -} - -// This is the endpoint the webhook sends the events to... -def rachioReceiveHandler() { - def reqData = request.JSON - if(reqData?.size() || reqData == [:]) { - // log.trace "eventDatas: ${reqData?.summary}" - log.trace "Rachio Device Event | Summary: (${reqData?.summary}) | Requesting Latest Data from API | DeviceID: ${reqData?.deviceId}" - if(reqData?.deviceId) { - def dev = getChildDevice(reqData?.deviceId) - poll(dev, "api") - } else { poll() } - } -} - -//Section4: polling device info methods -void poll(child=null, type=null) { - def lastPollSec = getTimeSinceInSeconds(atomicState.lastPollDt) - if(child && !type) { type = "device" } - log.debug "${app.label} -- Polling API for Latest Data -- Last Update was ($lastPollSec seconds ago)${type ? " | Reason: [$type]" : ""}" - if(lastPollSec < 9) { - runIn(10, "poll", [overwrite: true]) - //log.warn "Delaying poll... It's too soon to request new data" - return - } - def selectedDevices = atomicState.selectedDevices - def ctrlCnt = 0 - def zoneCnt = 0 - // Loop over each controller and poll its device data - selectedDevices?.each { cont-> - // Get controllers data from the cloud - // If null is returned should devices be marked offline?? - def devData = getDeviceData(cont?.key) - def cDev = getChildDevice(cont?.key) - if(cDev) { - ctrlCnt = ctrlCnt+1 - // Update Controller data - pollChild(cDev, devData) - // Loop and update each zone connected to the controller - cont?.value?.zones?.each { zone-> - zoneCnt = zoneCnt+1 - def zDev = getChildDevice(zone?.key) - if(zDev) { pollChild(zDev, devData) } - } - } - } - log.debug "Updated (${ctrlCnt}) Controllers and (${zoneCnt}) Zone devices..." - atomicState.lastPollDt = now() -} - -def pollChild(child, devData) { - if (pollChildren(child, devData)){ - //generate event for each (child) device type identified by different dni - } -} - -def pollChildren(childDev, devData) { - //log.trace "updating child device (${child?.label})" // | ${child?.device?.deviceNetworkId})" - try { - if(childDev && devData) { - String dni = childDev.device?.deviceNetworkId - String devLabel = childDev.label - def schedData = devData.currentSchedule - def devStatus = devData - def rainDelay = getCurrentRainDelay(devStatus) - def status = devStatus?.status - if(!childDev.getDataValue("HealthEnrolled")) { childDev.updated() } - Boolean pauseInStandby = settings.pauseInStandby == false ? false : true - Boolean inStandby = devData.on.toString() != "true" ? true : false - Boolean schedRunning = (schedData?.status == "PROCESSING") ? true : false - def data = [] - Map selectedDevices = atomicState.selectedDevices ?: [:] - selectedDevices?.each { contDev -> - if(dni == contDev?.key) { - updateStandbyItem(dni, inStandby) - // log.debug "schedRunning: ${schedRunning} | isWatering: ${isWatering()}" - if (isWatering() && !schedRunning) { - handleWateringSched(dni, false) - } - def newLabel = getDeviceLabelStr(devData?.name).toString() - if(devLabel != newLabel) { - childDev?.label = newLabel - log.debug "Controller Label has changed from (${devLabel}) to [${newLabel}]" - } - data = [data: devData, schedData: schedData, rainDelay: rainDelay, status: status, standby: inStandby, pauseInStandby: pauseInStandby] - } else { - contDev?.value?.zones?.each { zone -> - if (dni == zone?.key) { - def zoneData = findZoneData(zone?.key, devData) - def newLabel = getDeviceLabelStr(zone?.value).toString() - if(devLabel != newLabel) { - childDev?.label = newLabel - log.debug "Zone Label has changed from (${devLabel}) to [${newLabel}]" - } - data = [data: zoneData, schedData: schedData, devId: contDev?.key, status: status, standby: inStandby, pauseInStandby: pauseInStandby] - } - } - } - } - if (data) { - childDev.generateEvent(data) - } - } else { - log.warn "pollChildren cannot update children because it is missing the required parameters..." - // Should devices be marked offline here as we can't update them? - } - } catch(Exception ex) { - log.error "exception polling children:", ex - } - return result -} - -void setWateringDeviceState(devId, val) { - // log.trace "setWateringDeviceState($devId, $val)" - updateWateringItem(devId, val) -} - -void handleWateringSched(devId, val=false) { - // log.trace "handleWateringSched($devId, $val)" - if(val == true) { - log.trace "Watering is Active... Scheduling poll for every 1 minute" - // Unschedule first to make sure there's only one scheduled runEvery1Minute as poll is polling all devices - unschedule("poll") - runEvery1Minute("poll") - } else { - log.trace "Watering has finished... 1 minute Poll has been unscheduled" - unschedule("poll") - runIn(60, "poll") // This is here just to make sure that the schedule actually stopped and that the data is really current. - } - updateWateringItem(devId, val) -} - -def findZoneData(devId, devData) { - if(!devId || !devData) { return null } - if(devData?.zones) { return devData?.zones.find { it?.id == devId } } - return null -} - -def setValue(child, deviceId, newValue) { - def jsonRequestBody = '{"value":'+ newValue+'}' - def result = sendJson(child, jsonRequestBody, deviceId) - return result -} - -def sendJson(subUri, jsonBody, deviceId, standbyCmd=false) { - //log.trace "Sending: ${jsonBody}" - def returnStatus = false - def cmdParams = [ - uri: "${apiEndpoint}/1/public/${subUri}", - headers: ["Authorization": "Bearer ${atomicState.authToken}", "Content-Type": "application/json"], - body: jsonBody - ] - - try{ - if(!standbyCmd && settings.pauseInStandby == true && deviceId && atomicState.inStandbyModeMap[deviceId] == true) { - log.debug "Skipping this command while controller is in 'Standby Mode'..." - return true - } - - httpPut(cmdParams) { resp -> - returnStatus = resp - if(resp.status == 201 || resp.status == 204) { - returnStatus = true - //runIn(4, "poll", [overwrite: true]) - } else { - //refresh the auth token - if (resp.status == 401) { - log.debug "Refreshing your authToken!" - // refreshAuthToken() - } else { - log.error "Authentication error, invalid authentication method, lack of credentials, etc." - } - } - - } - } catch(Exception e) { - log.error "sendJson Exception Error: ${e}" - } - return returnStatus -} - -def refreshAuthToken() { - log.debug "refreshAuthToken()" - def appKey = "refreshToken" - - def notificationMessage = "Rachio is disconnected from SmartThings, because the access credential changed or was lost. " + - "Please go to the Rachio SmartApp and re-enter your account login credentials." - - def refreshParams = [ - method: 'POST', - headers: ["Authorization": "Basic $appKey"], - uri: "${apiEndpoint}/uri", - body: [grant_type:'refresh_token', refresh_token:"${atomicState.refreshToken}"], - ] - - try { - httpPost(refreshParams) { resp -> - if(resp?.status == 200) { - log.debug "refreshAuthToken()>> Response: ${resp?.data}" - if (resp?.data) { - atomicState.refreshToken = resp?.data?.refresh_token?.toString() - atomicState.authToken = resp?.data?.access_token?.toString() - } - } else { - sendPushAndFeeds(notificationMessage) - } - } - } catch (groovyx.net.http.HttpResponseException e) { - log.error "refreshAuthToken() >> Error: e.statusCode ${e.statusCode}" - def reAttemptPeriod = 300 - if (e.statusCode != 401) { - runIn(reAttemptPeriod, "refreshAuthToken") - } else if (e.statusCode == 401) { //refresh token is expired - sendPushAndFeeds(notificationMessage) - } - } -} - -//Section6: helper methods --------------------------------------------------------------------------------------------- - -def toJson(Map m) { - return new org.codehaus.groovy.grails.web.json.JSONObject(m).toString() -} - -def toQueryString(Map m) { - return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&") -} - -def epochToDt(val) { - return formatDt(new Date(val)) -} - -def formatDt(dt) { - def tf = new SimpleDateFormat("MMM d, yyyy - h:mm:ss a") - if(location?.timeZone) { tf?.setTimeZone(location?.timeZone) } - else { - log.warn "SmartThings TimeZone is not found or is not set... Please Try to open your ST location and Press Save..." - return null - } - return tf.format(dt) -} - -def getDurationDesc(long secondsCnt) { - int seconds = secondsCnt %60 - secondsCnt -= seconds - long minutesCnt = secondsCnt / 60 - long minutes = minutesCnt % 60 - minutesCnt -= minutes - long hoursCnt = minutesCnt / 60 - - return "${minutes} min ${(seconds >= 0 && seconds < 10) ? "0${seconds}" : "${seconds}"} sec" -} - -//Returns time differences is seconds -def GetTimeValDiff(timeVal) { - try { - def start = new Date(timeVal).getTime() - def now = new Date().getTime() - def diff = (int) (long) (now - start) / 1000 - //log.debug "diff: $diff" - return diff - } catch (ex) { - log.error "GetTimeValDiff Exception: ${ex}" - return 1000 - } -} - -def getChildContName() { return "Rachio Sprinkler Controller" } -def getChildZoneName() { return "Rachio Zone" } -def getServerUrl() { return "https://graph.api.smartthings.com" } -def getShardUrl() { return getApiServerUrl() } -def getCallbackUrl() { return "https://graph.api.smartthings.com/oauth/callback" } -def getAppEndpoint() { return "https://app.rach.io" } -def getApiEndpoint() { return "https://api.rach.io" } -def getClientId() { return appSettings.clientId ?: "smartthings" } -def getClientSecret() { return appSettings.clientSecret ?: "b10c4f90-7952-4b35-a505-ab8ca3c80e41" } -def getSupportUrl() { return "http://support.rachio.com/" } -def getCommunityUrl() { return "http://community.rachio.com/" } - -def debugEventFromParent(child, message) { - child.sendEvent("name":"debugEventFromParent", "value":message, "description":message, displayed: true, isStateChange: true) -} - -//send both push notification and mobile activity feeds -def sendPushAndFeeds(notificationMessage){ - def timeNow = now() - def timeSendPush = atomicState.timeSendPush - if (!timeSendPush || (timeNow - timeSendPush > 86400000)) { - sendPush("Rachio " + notificationMessage) - sendActivityFeeds(notificationMessage) - atomicState.timeSendPush = timeNow - } - atomicState.authToken = null -} - -def sendActivityFeeds(notificationMessage) { - def devices = app.getChildDevices(true) - devices.each { child -> - //update(child) - child.generateActivityFeedsEvent(notificationMessage) - } -} - -def standbyOn(child, deviceId) { - log.debug "standbyOn() command received from ${child?.device?.displayName}" - if(deviceId) { - def jsonData = new JsonBuilder("id":deviceId) - def res = sendJson("device/off", jsonData.toString(), deviceId, true) - // poll() - // child?.log("${child?.device.displayName} Standby OFF (Result: $res)") - return res - } -} - -def standbyOff(child, deviceId) { - log.debug "standbyOff() command received from ${child?.device?.displayName}" - if(deviceId) { - def jsonData = new JsonBuilder("id":deviceId) - def res = sendJson("device/on", jsonData.toString(), deviceId, true) - // // poll() - // child?.log("${child?.device.displayName} Standby OFF (Result: $res)") - return res - } -} - -def on(child, deviceId) { - log.trace "App on()..." -} - -def off(child, deviceId) { - log.trace "Received off() command from (${child?.device?.displayName})..." - // child?.log("Stop Watering - Received from (${child?.device.displayName})") - if(deviceId) { - def jsonData = new JsonBuilder("id":deviceId) - def res = sendJson("device/stop_water", jsonData.toString(), deviceId) - // poll() - return res - } - return false -} - -def setRainDelay(child, deviceId, delayVal) { - if (delayVal != null) { - def secondsPerDay = 24*60*60; - def duration = delayVal * secondsPerDay; - def jsonData = new JsonBuilder("id":child?.device?.deviceNetworkId, "duration":duration) - - return sendJson("device/rain_delay", jsonData?.toString(), deviceId) - } -} - -def isWatering(devId) { - //log.debug "isWatering()..." - def res = _httpGet("device/${devId}/current_schedule"); - def result = (res && res?.status) ? true : false - return result -} - -def getDeviceStatus(devId) { - return _httpGet("device/${devId}") -} - -def getControlLblById(id) { - def dev = getChildDevice(id) - return dev ? dev?.displayName : null -} - -def getCurrentRainDelay(res) { - //log.debug("getCurrentRainDelay($devId)...") - // convert to configured rain delay to days. - //def ret = (res?.rainDelayExpirationDate || res?.rainDelayStartDate) ? (res?.rainDelayExpirationDate - res?.rainDelayStartDate)/(26*60*60*1000) : 0 - def value = 0 - def rainDelayStartDate = res?.rainDelayStartDate ?: (new Date().getTime()) - if(res?.rainDelayExpirationDate && (rainDelayStartDate < res.rainDelayExpirationDate)) { - value = (res.rainDelayExpirationDate - rainDelayStartDate)/(26*60*60*1000) - value = (long) Math.floor(value + 0.5d) - } - return value -} - -def startZone(child, deviceId, zoneNum, mins) { - def res = false - def ctrlLbl = getControlLblById(deviceId) - log.trace "Starting to Water on ${ctrlLbl ? "$ctrlLbl: " : ""}(ZoneName: ${child?.device.displayName} | ZoneNumber: ${zoneNum} | RunDuration: ${mins})" - //child?.log("Starting to water on (ZoneName: ${child?.device.displayName} | ZoneNumber: ${zoneNum} | RunDuration: ${mins})...") - def zoneId = child?.device?.deviceNetworkId - if (zoneId && zoneNum && mins) { - def duration = mins.toInteger() * 60 - def jsonData = new JsonBuilder("id":zoneId, "duration":duration) - //log.debug "startZone jsonData: ${jsonData}" - res = sendJson("zone/start", jsonData?.toString(), deviceId) - } else { log.error "startZone Missing ZoneId or duration... ${zoneId} | ${mins}" } - return res -} - -def runAllZones(child, deviceId, mins) { - log.trace "runAllZones(ZoneName: ${child.device.displayName}, Duration: ${mins})..." - //child?.log("runAllZones(ZoneName: ${child?.device?.displayName} | Duration: ${mins})") - def selectedDevices = atomicState.selectedDevices ?: [:] - if (selectedDevices[deviceId]?.zones && mins) { - def zoneData = [] - def sortNum = 1 - def duration = mins.toInteger() * 60 - selectedDevices[deviceId].zones.each { z -> - def d = getChildDevice(z.key) - if (d?.device.currentValue("watering") != "disabled") { - zoneData << ["id":z.key, "duration":duration, "sortOrder": sortNum++] - } - } - def jsonData = new JsonBuilder("zones":zoneData) - //child?.log("runAllZones jsonData: ${jsonData}") - return sendJson("zone/start_multiple", jsonData?.toString(), deviceId) - } else { - log.error "runAllZones Missing ZoneIds or Duration... ${selectedDevices[deviceId]?.zones} | ${mins}" - } - return false -} - -def pauseScheduleRun(child) { - log.trace "pauseScheduleRun..." - def schedData = getCurSchedule(atomicState.deviceId) - def schedRuleData = getScheduleRuleInfo(schedData?.scheduleRuleId) - child?.log "schedRuleData: $schedRuleData" - child?.log "Schedule Started on: ${epochToDt(schedRuleData?.startDate)} | Total Duration: ${getDurationDesc(schedRuleData?.totalDuration.toLong())}" - - if(schedRuleData) { - def zones = schedRuleData?.zones?.sort { a , b -> a.sortOrder <=> b.sortOrder } - zones?.each { zn -> - child?.log "Zone#: ${zn?.zoneNumber} | Zone Duration: ${getDurationDesc(zn?.duration.toLong())} | Order#: ${zn?.sortOrder}" - if(zn?.zoneId == schedData?.zoneId) { - def zoneRunTime = "Elapsed Runtime: ${getDurationDesc(GetTimeValDiff(schedData?.zoneStartDate.toLong()))}" - child?.log "Zone Started: ${epochToDt(schedData?.zoneStartDate)} | ${zoneRunTime} | Cycle Count: ${schedData?.cycleCount} | Cycling: ${schedData?.cycling}" - } - } - } -} - -//Required by child devices -def getZones(device) { - log.trace "getZones(${device.label})..." - def res = _httpGet("device/${device?.deviceNetworkId}") - return !res ? null : res?.zones -} - -def getScheduleRuleInfo(schedId) { - def res = _httpGet("schedulerule/${schedId}") - return res -} From 69477b2412928c0251a7abe8afb18c1801beefa7 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 3 Nov 2020 19:26:35 +0100 Subject: [PATCH 106/422] Force metadata for device (#49092) --- .../aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy index 74666828740..8abe105510c 100644 --- a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy +++ b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy @@ -14,7 +14,7 @@ * */ metadata { - definition(name: "Aeotec Doorbell Siren 6", namespace: "smartthings", author: "SmartThings", mcdSync: true) { + definition(name: "Aeotec Doorbell Siren 6", namespace: "smartthings", author: "SmartThings", mcdSync: true, mnmn: "SmartThings", vid: "SmartThings-smartthings-Aeotec_Doorbell_Siren_6") { capability "Actuator" capability "Health Check" capability "Tamper Alert" From a51e2b48f0003677c4b6a1379d4d4f7453344b3e Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Tue, 3 Nov 2020 10:27:46 -0800 Subject: [PATCH 107/422] CHAD-5315 Update SmartWeather Station Tile DTH (#38387) * CHAD-5315 Update SmartWeather Station Tile DTH * Update capabilities * General logic updates, and better support for PWS forecast and conditions * Update city name logic to account for i18n * Update polling logic -- poll once every 3 hours, `update()` reschedules --- .../capability.stsmartweather.windSpeed.json | 10 +- .../smartweather-station-tile.groovy | 214 ++++++++++-------- 2 files changed, 119 insertions(+), 105 deletions(-) diff --git a/devicetypes/smartthings/smartweather-station-tile.src/capability.stsmartweather.windSpeed.json b/devicetypes/smartthings/smartweather-station-tile.src/capability.stsmartweather.windSpeed.json index 9994d651c48..04ccc66da41 100644 --- a/devicetypes/smartthings/smartweather-station-tile.src/capability.stsmartweather.windSpeed.json +++ b/devicetypes/smartthings/smartweather-station-tile.src/capability.stsmartweather.windSpeed.json @@ -6,18 +6,18 @@ "type": "object", "properties": { "value": { - "type": "string" + "title": "PositiveNumber", + "type": "number", + "minimum": 0 }, "unit": { "type": "string", - "enum": ["KPH", "MPH"], - "default": "KPH" + "enum": ["KPH", "MPH"] } }, "additionalProperties": false, "required": [ - "value", - "unit" + "value", "unit" ] } } diff --git a/devicetypes/smartthings/smartweather-station-tile.src/smartweather-station-tile.groovy b/devicetypes/smartthings/smartweather-station-tile.src/smartweather-station-tile.groovy index 77c3dfbb558..427833743c1 100644 --- a/devicetypes/smartthings/smartweather-station-tile.src/smartweather-station-tile.groovy +++ b/devicetypes/smartthings/smartweather-station-tile.src/smartweather-station-tile.groovy @@ -22,8 +22,8 @@ metadata { capability "Temperature Measurement" capability "Relative Humidity Measurement" capability "Ultraviolet Index" - //capability "Wind Speed" // Not in production yet - capability "stsmartweather.windSpeed" // "Wind Speed" only supports m/s unit, however we want to create both events + capability "Wind Speed" + capability "stsmartweather.windSpeed" capability "stsmartweather.windDirection" capability "stsmartweather.apparentTemperature" capability "stsmartweather.astronomicalData" @@ -34,29 +34,6 @@ metadata { capability "stsmartweather.weatherSummary" capability "Sensor" capability "Refresh" - - // While we have created a custom capability for these attributes, - // they will remain to support any custom DataManagement based SmartApps using them. - attribute "localSunrise", "string" - attribute "localSunset", "string" - attribute "city", "string" - attribute "timeZoneOffset", "string" - attribute "weather", "string" - attribute "wind", "string" - attribute "windVector", "string" - attribute "weatherIcon", "string" - attribute "forecastIcon", "string" - attribute "feelsLike", "string" - attribute "percentPrecip", "string" - attribute "alert", "string" - attribute "alertKeys", "string" - attribute "sunriseDate", "string" - attribute "sunsetDate", "string" - attribute "lastUpdate", "string" - attribute "uvDescription", "string" - attribute "forecastToday", "string" - attribute "forecastTonight", "string" - attribute "forecastTomorrow", "string" } preferences { @@ -209,12 +186,18 @@ def parse(String description) { } def installed() { + schedulePoll() poll() - runEvery30Minutes(poll) +} + +def schedulePoll() { + unschedule() + runEvery3Hours("poll") } def updated() { - poll + schedulePoll() + poll() } def uninstalled() { @@ -253,13 +236,16 @@ def pollUsingZipCode(String zipCode) { send(name: "feelsLike", value: obs.temperatureFeelsLike, unit: tempUnits) send(name: "humidity", value: obs.relativeHumidity, unit: "%") - send(name: "weather", value: obs.wxPhraseShort) + send(name: "weather", value: obs.wxPhraseLong) send(name: "weatherIcon", value: obs.iconCode, displayed: false) + send(name: "wind", value: obs.windSpeed, unit: windUnits) + send(name: "windspeed", value: new BigDecimal(convertWindSpeed(obs.windSpeed, tempUnits == "F" ? "imperial" : "metric", "metric") / 3.6).setScale(2, BigDecimal.ROUND_HALF_UP), unit: "m/s") send(name: "windVector", value: "${obs.windDirectionCardinal} ${obs.windSpeed} ${windUnits}") + log.trace "Getting location info" - def loc = getTwcLocation(zipCode).location - def cityValue = "${loc.city}, ${loc.adminDistrictCode} ${loc.countryCode}" + def loc = getTwcLocation(zipCode)?.location + def cityValue = createCityName(loc) ?: zipCode // I don't think we'll ever hit a point where we can't build a city name... But just in case... if (cityValue != device.currentValue("city")) { send(name: "city", value: cityValue, isStateChange: true) } @@ -275,7 +261,7 @@ def pollUsingZipCode(String zipCode) { def sunsetDate = dtf.parse(obs.sunsetTimeLocal) def tf = new java.text.SimpleDateFormat("h:mm a") - tf.setTimeZone(TimeZone.getTimeZone(loc.ianaTimeZone)) + tf.setTimeZone(TimeZone.getTimeZone(loc?.ianaTimeZone)) def localSunrise = "${tf.format(sunriseDate)}" def localSunset = "${tf.format(sunsetDate)}" @@ -287,22 +273,26 @@ def pollUsingZipCode(String zipCode) { // Forecast def f = getTwcForecast(zipCode) if (f) { - def icon = f.daypart[0].iconCode[0] ?: f.daypart[0].iconCode[1] - def precip = f.daypart[0].precipChance[0] ?: f.daypart[0].precipChance[1] + def icon = f.daypart[0].iconCode[0] != null ? f.daypart[0].iconCode[0] : f.daypart[0].iconCode[1] + def precip = f.daypart[0].precipChance[0] != null ? f.daypart[0].precipChance[0] : f.daypart[0].precipChance[1] def narrative = f.daypart[0].narrative send(name: "percentPrecip", value: precip, unit: "%") send(name: "forecastIcon", value: icon, displayed: false) - send(name: "forecastToday", value: narrative[0] ?: "-") - send(name: "forecastTonight", value: narrative[1] ?: "-") - send(name: "forecastTomorrow", value: narrative[2] ?: "-") - } - else { + send(name: "forecastToday", value: narrative[0] ?: "n/a") + send(name: "forecastTonight", value: narrative[1] ?: "n/a") + send(name: "forecastTomorrow", value: narrative[2] ?: "n/a") + } else { log.warn "Forecast not found" + send(name: "percentPrecip", value: 0, unit: "%", descriptionText: "Chance of precipitation could not be found") + send(name: "forecastIcon", value: "", displayed: false) + send(name: "forecastToday", value: "n/a", descriptionText: "Today's forecast could not be found") + send(name: "forecastTonight", value: "n/a", descriptionText: "Tonight's forecast could not be found") + send(name: "forecastTomorrow", value: "n/a", descriptionText: "Tomorrow's forecast could not be found") } // Alerts - def alerts = getTwcAlerts("${loc.latitude},${loc.longitude}") + def alerts = getTwcAlerts("${loc?.latitude},${loc?.longitude}") if (alerts) { alerts.each {alert -> def msg = alert.headlineText @@ -314,12 +304,10 @@ def pollUsingZipCode(String zipCode) { } send(name: "alert", value: msg, descriptionText: msg) } - } - else { + } else { send(name: "alert", value: "No current alerts", descriptionText: msg) } - } - else { + } else { log.warn "No response from TWC API" } @@ -339,33 +327,76 @@ def pollUsingPwsId(String stationId) { if (obsWrapper && obsWrapper.observations && obsWrapper.observations.size()) { def obs = obsWrapper.observations[0] def dataScale = obs.imperial ? 'imperial' : 'metric' + send(name: "temperature", value: convertTemperature(obs[dataScale].temp, dataScale, tempUnits), unit: tempUnits) send(name: "feelsLike", value: convertTemperature(obs[dataScale].windChill, dataScale, tempUnits), unit: tempUnits) send(name: "humidity", value: obs.humidity, unit: "%") - send(name: "weather", value: "n/a") - send(name: "weatherIcon", value: null, displayed: false) - send(name: "wind", value: convertWindSpeed(obs[dataScale].windSpeed, dataScale, tempUnits), unit: windUnits) - send(name: "windVector", value: "${obs.winddir}° ${convertWindSpeed(obs[dataScale].windSpeed, dataScale, tempUnits)} ${windUnits}") - def cityValue = obs.neighborhood + + def windSpeed = convertWindSpeed(obs[dataScale].windSpeed, dataScale, tempUnits) + send(name: "wind", value: windSpeed, unit: windUnits) + send(name: "windspeed", value: new BigDecimal(convertWindSpeed(obs[dataScale].windSpeed, dataScale, "metric") / 3.6).setScale(2, BigDecimal.ROUND_HALF_UP), unit: "m/s") + send(name: "windVector", value: "${obs.winddir}° ${windSpeed} ${windUnits}") + + def loc = getTwcLocation("${obs.lat},${obs.lon}")?.location + def cityValue = createCityName(loc) ?: "${obs.neighborhood}, ${obs.country}" if (cityValue != device.currentValue("city")) { send(name: "city", value: cityValue, isStateChange: true) } send(name: "ultravioletIndex", value: obs.uv) - send(name: "uvDescription", value: "n/a") - send(name: "localSunrise", value: "n/a", descriptionText: "Sunrise is not supported when using PWS") - send(name: "localSunset", value: "n/a", descriptionText: "Sunset is not supported when using PWS") - send(name: "illuminance", value: null) + def cond = getTwcConditions("${obs.lat},${obs.lon}") + if (cond) { + send(name: "weather", value: cond.wxPhraseLong) + send(name: "weatherIcon", value: cond.iconCode, displayed: false) + send(name: "uvDescription", value: cond.uvDescription) + + def dtf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + def sunriseDate = dtf.parse(cond.sunriseTimeLocal) + log.debug "'${cond.sunriseTimeLocal}'" + + def sunsetDate = dtf.parse(cond.sunsetTimeLocal) + def tf = new java.text.SimpleDateFormat("h:mm a") + tf.setTimeZone(TimeZone.getTimeZone(loc?.ianaTimeZone)) + + def localSunrise = "${tf.format(sunriseDate)}" + def localSunset = "${tf.format(sunsetDate)}" + send(name: "localSunrise", value: localSunrise, descriptionText: "Sunrise today is at $localSunrise") + send(name: "localSunset", value: localSunset, descriptionText: "Sunset today at is $localSunset") - // Forecast not supported - send(name: "percentPrecip", value: "n/a", unit: "%") - send(name: "forecastIcon", value: null, displayed: false) - send(name: "forecastToday", value: "n/a") - send(name: "forecastTonight", value: "n/a") - send(name: "forecastTomorrow", value: "n/a") - log.warn "Forecast not supported when using PWS" + send(name: "illuminance", value: estimateLux(cond, sunriseDate, sunsetDate)) + } else { + log.warn "Conditions not found" + send(name: "weather", value: "n/a", descriptionText: "Weather summary could not be found") + send(name: "weatherIcon", value: "", displayed: false) + send(name: "uvDescription", value: "n/a") + + send(name: "localSunrise", value: "n/a", descriptionText: "Sunrise time could not be found") + send(name: "localSunset", value: "n/a", descriptionText: "Sunset time could not be found") + send(name: "illuminance", value: 0, descriptionText: "Illuminance could not be found") + } + + // Forecast + def f = getTwcForecast("${obs.lat},${obs.lon}") + if (f) { + def icon = f.daypart[0].iconCode[0] != null ? f.daypart[0].iconCode[0] : f.daypart[0].iconCode[1] + def precip = f.daypart[0].precipChance[0] != null ? f.daypart[0].precipChance[0] : f.daypart[0].precipChance[1] + def narrative = f.daypart[0].narrative + + send(name: "percentPrecip", value: precip, unit: "%") + send(name: "forecastIcon", value: icon, displayed: false) + send(name: "forecastToday", value: narrative[0] ?: "n/a") + send(name: "forecastTonight", value: narrative[1] ?: "n/a") + send(name: "forecastTomorrow", value: narrative[2] ?: "n/a") + } else { + log.warn "Forecast not found" + send(name: "percentPrecip", value: 0, unit: "%", descriptionText: "Chance of precipitation could not be found") + send(name: "forecastIcon", value: "", displayed: false) + send(name: "forecastToday", value: "n/a", descriptionText: "Today's forecast could not be found") + send(name: "forecastTonight", value: "n/a", descriptionText: "Tonight's forecast could not be found") + send(name: "forecastTomorrow", value: "n/a", descriptionText: "Tomorrow's forecast could not be found") + } // Alerts def alerts = getTwcAlerts("${obs.lat},${obs.lon}") @@ -380,12 +411,10 @@ def pollUsingPwsId(String stationId) { } send(name: "alert", value: msg, descriptionText: msg) } - } - else { + } else { send(name: "alert", value: "No current alerts", descriptionText: msg) } - } - else { + } else { log.warn "No response from TWC API" } @@ -431,49 +460,16 @@ private localDate(timeZone) { df.format(new Date()) } -// Create the new custom capability event if needed, -// but also send a legacy custom event for any DM-backed SmartApps using them. private send(Map map) { - def eventConversion = [ - "localSunrise": "stsmartweather.astronomicalData.localSunrise", - "localSunset": "stsmartweather.astronomicalData.localSunset", - "city": "stsmartweather.astronomicalData.city", - "timeZoneOffset": "stsmartweather.astronomicalData.timeZoneOffset", - "weather": "stsmartweather.weatherSummary.weather", - "wind": "stsmartweather.windSpeed.wind", - "windVector": "stsmartweather.windDirection.windVector", - "weatherIcon": "stsmartweather.weatherSummary.weatherIcon", - "forecastIcon": "stsmartweather.weatherForecast.forecastIcon", - "feelsLike": "stsmartweather.apparentTemperature.feelsLike", - "percentPrecip": "stsmartweather.precipitation.percentPrecip", - "alert": "stsmartweather.weatherAlert.alert", - "alertKeys": "stsmartweather.weatherAlert.alertKeys", - "sunriseDate": "stsmartweather.astronomicalData.sunriseDate", - "sunsetDate": "stsmartweather.astronomicalData.sunsetDate", - "lastUpdate": "stsmartweather.smartWeather.lastUpdate", - "uvDescription": "stsmartweather.ultravioletDescription.uvDescription", - "forecastToday": "stsmartweather.weatherForecast.forecastToday", - "forecastTonight": "stsmartweather.weatherForecast.forecastTonight", - "forecastTomorrow": "stsmartweather.weatherForecast.forecastTomorrow" - ] - //log.trace "WUSTATION: event: $map" sendEvent(map) - if (map.name && eventConversion.containsKey(map.name)) { - def newMap = map.clone() - newMap.name = eventConversion[map.name] - - //log.trace "WUSTATION: NEW event: $newMap" - sendEvent(newMap) - } } private estimateLux(obs, sunriseDate, sunsetDate) { def lux = 0 if (obs.dayOrNight == 'N') { lux = 10 - } - else { + } else { //day switch(obs.iconCode) { case 4: @@ -499,7 +495,7 @@ private estimateLux(obs, sunriseDate, sunsetDate) { def beforeSunset = sunsetDate.time - now def oneHour = 1000 * 60 * 60 - if(afterSunrise < oneHour) { + if (afterSunrise < oneHour) { //dawn lux = (long)(lux * (afterSunrise/oneHour)) } else if (beforeSunset < oneHour) { @@ -539,7 +535,25 @@ private convertWindSpeed(value, fromScale, toScale) { return value } if (ts == 'imperial') { - return value * 1.608 + return value / 1.609 + } + return value * 1.609 +} + +private createCityName(location) { + def cityName = null + + if (location) { + cityName = location.city + ", " + + if (location.adminDistrictCode) { + cityName += location.adminDistrictCode + cityName += " " + cityName += location.countryCode ?: location.country + } else { + cityName += location.country + } } - return value / 1.608 + + cityName } From c64f28711870da16ce446ee7e9538afe7f27d45f Mon Sep 17 00:00:00 2001 From: dwd-kwon <59678391+dwd-kwon@users.noreply.github.com> Date: Wed, 4 Nov 2020 18:14:03 +0900 Subject: [PATCH 108/422] DevWs for DAWON DNS containing containing SmartSense S1 Sensor (#47733) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for DAWON DNS containing containing SmartSense S1 Sensor * Add to fingerprint * delete dawon-zigbee-signal-interlock.groovy Co-authored-by: 남수 허 --- .../smartsense-open-closed-sensor.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index 9f127bd884e..698174ae1ae 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -41,6 +41,8 @@ metadata { fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "CSW_ADUROLIGHT", deviceJoinName: "ERIA Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Contact Sensor V2.1 fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "ADUROLIGHT", model: "CSW_ADUROLIGHT", deviceJoinName: "ERIA Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Contact Sensor V2.0 fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Sercomm Corp.", model: "SZ-DWS04", deviceJoinName: "Sercomm Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact" //Sercomm Door Window Sensor + //Dawon + fingerprint inClusters: "0000, 0003, 0006, 0500", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "SS-B100-ZB", deviceJoinName: "Dawon Signal Interlock", mnmn: "0AIg", vid: "dawon-zigbee-signal-interlock2" } simulator { @@ -235,4 +237,4 @@ private Boolean isEcolink() { private Boolean isBoschRadionMultiSensor() { device.getDataValue("manufacturer") == "Bosch" && device.getDataValue("model") == "RFMS-ZBMS" -} +} \ No newline at end of file From 3fd3e8053c60fefc8ccb64dff68dcad90a803a20 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 4 Nov 2020 19:47:24 +0100 Subject: [PATCH 109/422] [ICP-13795] Honeywell Home T6 Pro - device specific metadata (#49013) * Specified metadata for Honeywell T6 Pro * Updated Sensor Multilevel CC version in initial poll --- .../zwave-battery-thermostat.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy b/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy index b800ff6684b..61b91aff63d 100644 --- a/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy +++ b/devicetypes/smartthings/zwave-battery-thermostat.src/zwave-battery-thermostat.groovy @@ -41,7 +41,7 @@ metadata { fingerprint inClusters: "0x43,0x40,0x44,0x31,0x80", deviceJoinName: "Thermostat" fingerprint mfr: "014F", prod: "5442", model: "5431", deviceJoinName: "Linear Thermostat" //Linear Z-Wave Thermostat fingerprint mfr: "014F", prod: "5442", model: "5436", deviceJoinName: "GoControl Thermostat" //GoControl Z-Wave Thermostat - fingerprint mfr: "0039", prod: "0011", model: "0008", deviceJoinName: "Honeywell Thermostat" //Honeywell T6 Pro Z-Wave Thermostat + fingerprint mfr: "0039", prod: "0011", model: "0008", deviceJoinName: "Honeywell Thermostat", mnmn: "SmartThings", vid: "honeywell-t6-pro" //Honeywell T6 Pro Z-Wave Thermostat } tiles { @@ -359,8 +359,8 @@ def pollDevice() { def cmds = [] cmds << zwave.thermostatModeV2.thermostatModeGet() cmds << zwave.thermostatFanModeV3.thermostatFanModeGet() - cmds << zwave.sensorMultilevelV2.sensorMultilevelGet(sensorType: 1) // current temperature - cmds << zwave.sensorMultilevelV2.sensorMultilevelGet(sensorType: 5) // current relative humidity + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1) // current temperature + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 5) // current relative humidity cmds << zwave.thermostatOperatingStateV1.thermostatOperatingStateGet() cmds << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1) cmds << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2) From 21b8c1d7bc59ab3e10ded06793862604d7f2f865 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Thu, 5 Nov 2020 20:41:06 +0100 Subject: [PATCH 110/422] ICP-13656 - Query the device for power meter values in response to dimmer reports. Code refactoring. (#49278) --- .../qubino-dimmer.src/qubino-dimmer.groovy | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index 6d3b34f82f3..bcfbe74cbc2 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -332,10 +332,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { log.debug "BasicReport: ${cmd}" - if(isDINDimmer()) { - sendHubCommand(encap(zwave.meterV2.meterGet(scale: 0))) - sendHubCommand(encap(zwave.meterV2.meterGet(scale: 2))) - } dimmerEvents(cmd) } @@ -389,6 +385,12 @@ private dimmerEvents(physicalgraph.zwave.Command cmd, ep = null) { if (cmdValue && cmdValue <= 100) { result << createEvent(name: "level", value: cmdValue == 99 ? 100 : cmdValue) } + + if(supportsPowerMeter()){ + log.debug "query device for power meter values" + sendHubCommand(encapCommands(getPowerMeterCommands())) + } + return result } @@ -452,11 +454,6 @@ def on() { zwave.switchMultilevelV3.switchMultilevelGet() ] - if(supportsPowerMeter()){ - commands << zwave.meterV2.meterGet(scale: 0) - commands << zwave.meterV2.meterGet(scale: 2) - } - encapCommands(commands, 3000) } @@ -466,11 +463,6 @@ def off() { zwave.switchMultilevelV3.switchMultilevelGet() ] - if(supportsPowerMeter()){ - commands << zwave.meterV2.meterGet(scale: 0) - commands << zwave.meterV2.meterGet(scale: 2) - } - encapCommands(commands, 3000) } @@ -493,11 +485,6 @@ def setLevel(value, duration = null) { commands << zwave.switchMultilevelV3.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration) commands << zwave.switchMultilevelV3.switchMultilevelGet() - if(supportsPowerMeter()){ - commands << zwave.meterV2.meterGet(scale: 0) - commands << zwave.meterV2.meterGet(scale: 2) - } - encapCommands(commands, getStatusDelay) } @@ -516,9 +503,17 @@ def refresh() { def getRefreshCommands() { def commands = [] + commands << zwave.basicV1.basicGet() + commands += getPowerMeterCommands() + + commands +} + +def getPowerMeterCommands() { + def commands = [] - if(isFlushDimmer() || isDINDimmer()) { + if(supportsPowerMeter()) { commands << zwave.meterV2.meterGet(scale: 0) commands << zwave.meterV2.meterGet(scale: 2) } From ccbde80bd5d368de0efd2e2acede52d915d7bbbf Mon Sep 17 00:00:00 2001 From: rboy1 <3846367+rboy1@users.noreply.github.com> Date: Mon, 9 Nov 2020 12:08:34 -0500 Subject: [PATCH 111/422] Fix reporting battery value Battery event reporting template was incorrect --- .../zigbee-non-holdable-button.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy index 45972f054b1..deb2611dc7b 100755 --- a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy +++ b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2016 SmartThings + * Copyright 2016 SmartThings, contributions by RBoy Apps * * 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 @@ -124,7 +124,7 @@ private Map getBatteryResult(rawValue) { if (!(rawValue == 0 || rawValue == 255)) { result.name = 'battery' result.translatable = true - result.descriptionText = "${ device.displayName } battery was ${ value }%" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" def minVolts = 2.1 def maxVolts = 3.0 From c61cef60efe102b598bb92b1abb9e5309b867144 Mon Sep 17 00:00:00 2001 From: "ingvar.marstorp" Date: Tue, 10 Nov 2020 20:28:29 -0800 Subject: [PATCH 112/422] C2C-1027 Ecobee Capability Migration in Groovy integration Adding missed thermostat capability "Thermostat Operating State" (id: thermostatOperatingState) --- .../smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index a121f454438..3aaa542b3d4 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -31,6 +31,7 @@ metadata { capability "Thermostat Heating Setpoint" capability "Thermostat Mode" capability "Thermostat Fan Mode" + capability "Thermostat Operating State" command "generateEvent" command "resumeProgram" From 7cf62a71d88ce2071c25e8662f7f60fe11ad5577 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Thu, 12 Nov 2020 18:34:19 +0900 Subject: [PATCH 113/422] Update zigbee-white-color-temperature-bulb.groovy (#49686) --- .../zigbee-white-color-temperature-bulb.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 616a107b4d5..9bc8bad500b 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -36,6 +36,7 @@ metadata { // ABL fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010C", manufacturer: "AduroSmart Eria", model: "AD-ColorTemperature3001", deviceJoinName: "Eria Light" //Eria ZigBee Color Temperature Bulb From d887797fa2df890aa6efe8d455ed0e31338c3cec Mon Sep 17 00:00:00 2001 From: Steven Green Date: Thu, 12 Nov 2020 14:47:01 -0800 Subject: [PATCH 114/422] CHAD-5726 Update Thermostat DTHs to discrete capabilities (#49771) * CHAD-5726 Update Thermostat DTHs to discrete capabilities This is the first step towards transitioning away from the deprecated monolithic Thermostat capability. * remove fidure custom attribute and re-map operating states --- .../centralite-thermostat.groovy | 4 ++++ .../ct100-thermostat.src/ct100-thermostat.groovy | 5 +++++ .../fibaro-heat-controller.groovy | 1 + .../fidure-thermostat.src/fidure-thermostat.groovy | 13 +++++++------ .../zwave-radiator-thermostat.groovy | 1 + .../zwave-thermostat.src/zwave-thermostat.groovy | 5 +++++ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy b/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy index 4954b690cf5..dc8e1c8a821 100644 --- a/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy +++ b/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy @@ -20,6 +20,10 @@ metadata { capability "Actuator" capability "Temperature Measurement" capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Cooling Setpoint" + capability "Thermostat Mode" + capability "Thermostat Fan Mode" capability "Configuration" capability "Refresh" capability "Sensor" diff --git a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy index c5638956b54..cfb29ecdabe 100644 --- a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy +++ b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy @@ -5,6 +5,11 @@ metadata { capability "Temperature Measurement" capability "Relative Humidity Measurement" capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Cooling Setpoint" + capability "Thermostat Operating State" + capability "Thermostat Mode" + capability "Thermostat Fan Mode" capability "Battery" capability "Refresh" capability "Sensor" diff --git a/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy b/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy index c535e6ab08d..3822c837b42 100644 --- a/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy +++ b/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy @@ -20,6 +20,7 @@ metadata { capability "Thermostat Heating Setpoint" capability "Health Check" capability "Thermostat" + capability "Thermostat Mode" capability "Temperature Measurement" command "setThermostatSetpointUp" diff --git a/devicetypes/smartthings/fidure-thermostat.src/fidure-thermostat.groovy b/devicetypes/smartthings/fidure-thermostat.src/fidure-thermostat.groovy index 338ebcb6337..1b5dff47f62 100644 --- a/devicetypes/smartthings/fidure-thermostat.src/fidure-thermostat.groovy +++ b/devicetypes/smartthings/fidure-thermostat.src/fidure-thermostat.groovy @@ -12,6 +12,11 @@ metadata { capability "Actuator" capability "Temperature Measurement" capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Cooling Setpoint" + capability "Thermostat Operating State" + capability "Thermostat Mode" + capability "Thermostat Fan Mode" capability "Configuration" capability "Refresh" capability "Sensor" @@ -41,8 +46,6 @@ metadata { attribute "lastTimeSync", "string" - attribute "thermostatOperatingState", "string" - fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0201,0204,0B05", outClusters: "000A, 0019", deviceJoinName: "Fidure Thermostat" fingerprint manufacturer: "Fidure", model: "A1732R3" , deviceJoinName: "Fidure Thermostat"// same clusters as above @@ -94,9 +97,7 @@ metadata { } standardTile("hvacStatus", "thermostatOperatingState", inactiveLabel: false, decoration: "flat") { - state "Resting", label: 'Resting' - state "Heating", icon:"st.thermostat.heating" - state "Cooling", icon:"st.thermostat.cooling" + state "thermostatOperatingState", label:'${currentValue}' } @@ -496,7 +497,7 @@ def Program() { def getThermostatOperatingState(value) { - String[] m = [ "heating", "cooling", "fan", "Heat2", "Cool2", "Fan2", "Fan3"] + String[] m = [ "heating", "cooling", "fan only", "heating", "cooling", "fan only", "fan only"] String desc = 'idle' value = Integer.parseInt(''+value, 16) diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index 7e4ee20830b..788f16cd5f2 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -20,6 +20,7 @@ metadata { capability "Thermostat Heating Setpoint" capability "Health Check" capability "Thermostat" + capability "Thermostat Mode" capability "Temperature Measurement" capability "Configuration" diff --git a/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy b/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy index ce19cc3cc43..b697b460310 100644 --- a/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy +++ b/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy @@ -16,6 +16,11 @@ metadata { capability "Actuator" capability "Temperature Measurement" capability "Thermostat" + capability "Thermostat Heating Setpoint" + capability "Thermostat Cooling Setpoint" + capability "Thermostat Operating State" + capability "Thermostat Mode" + capability "Thermostat Fan Mode" capability "Refresh" capability "Sensor" capability "Health Check" From 783461197b3eab190c8a8029ff7926fb246abb55 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Fri, 13 Nov 2020 21:02:56 +0100 Subject: [PATCH 115/422] ICP-13361 Added min dimming level preference (#46726) * ICP-13361 Added minimum dimming level preference and adjusting dimming values from reports for DIN dimmer when this preference is set * changed dimming level value calculation * The range is not limited on UI when minimum dimming level preference is set * changed setting minimum dimming value preference * removed unnecessary comment --- .../qubino-dimmer.src/qubino-dimmer.groovy | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index bcfbe74cbc2..76d8f38c715 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -158,6 +158,8 @@ def excludeParameterFromSync(preference){ if (isDINDimmer() || isFlushDimmer010V()) { exclude = true } + } else if (preference.key == "minimumDimmingValue"){ + exclude = true } if (exclude) { @@ -232,13 +234,6 @@ def configure() { commands << zwave.associationV1.associationSet(groupingIdentifier:6, nodeId:[zwaveHubNodeId]) commands << zwave.multiChannelV3.multiChannelEndPointGet() commands += getRefreshCommands() - - // 1% is default Minimum dimming value for dimmers, - // when device is set to 1% - it turns off and device does not send any level reports - // Minimum dimming value has to be set to 2%, so the device's internal range would be 2-100% - // Still, for users it will relatively be 1-100% on the UI and device will report it. - // Parameter no. 60 – Minimum dimming value - commands << zwave.configurationV2.configurationSet(scaledConfigurationValue: 2, parameterNumber: 60, size: 1) commands += getReadConfigurationFromTheDeviceCommands() encapCommands(commands) @@ -394,6 +389,14 @@ private dimmerEvents(physicalgraph.zwave.Command cmd, ep = null) { return result } +Integer adjustValueToRange(value){ + if(value == 0){ + return 0 + } + def minDimmingLvlPref = settings.minimumDimmingValue ?: parameterMap.find({it.key == 'minimumDimmingValue'}).defaultValue + return Math.max(value, minDimmingLvlPref) +} + def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd, ep = null) { log.info "SensorMultilevelReport: ${cmd}, endpoint: ${ep}" def result = [] @@ -482,7 +485,9 @@ def setLevel(value, duration = null) { getStatusDelay = duration < 128 ? (duration * 1000) + 2000 : (Math.round(duration / 60) * 60 * 1000) + 2000 } - commands << zwave.switchMultilevelV3.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration) + def adjustedLevel = adjustValueToRange(level) + + commands << zwave.switchMultilevelV3.switchMultilevelSet(value: adjustedLevel, dimmingDuration: dimmingDuration) commands << zwave.switchMultilevelV3.switchMultilevelGet() encapCommands(commands, getStatusDelay) @@ -619,6 +624,16 @@ private getParameterMap() {[ optionActive: 1, activeDescription: " Flush Dimmer 0-10V module does not save the state after a power failure, it returns to off position", description: "Set whether the device stores or does not store the last output level in the event of a power outage." ], + [ + name : "Minimum dimming value", + key : "minimumDimmingValue", + type : "range", + parameterNumber: 60, + size : 1, + defaultValue : 1, + range : "1..98", + description : "Select minimum dimming value for this device. When the switch type is selected as Bi-stable, it is not possible to dim the value between min and max." + ], [ name: "Dimming time (soft on/off)", key: "dimmingTime(SoftOn/Off)", type: "range", parameterNumber: 65, size: 2, defaultValue: 100, From bf658c2bf2e6b3d88586785e26260bb9e24f9641 Mon Sep 17 00:00:00 2001 From: GatorSystem <69823174+GatorSystem@users.noreply.github.com> Date: Mon, 16 Nov 2020 08:12:28 -0600 Subject: [PATCH 116/422] DevWs for GatorSystem LLC containing containing GatorSystem Motion Sensor (#46565) * DevWs for GatorSystem LLC containing containing GatorSystem Motion Sensor * GS HomeWatcher DTH * Update gatorsystem-homewatcher.groovy * Update gatorsystem-homewatcher.groovy * Update gatorsystem-homewatcher.groovy I used GEDIT to align the code first and then copied it back to the edit window at Github. It looked aligned both in Gedit and in the Github window. So I'm trying to commit the aligned code again here. * Update gatorsystem-homewatcher.groovy I guess I figured it out; the misalignment was caused by tabs. I replaced all tabs with spaces. * Update gatorsystem-homewatcher.groovy I tested the code with a device and made changes to make the code work better. * Update gatorsystem-homewatcher.groovy Revised per Konrad's comments. * Update gatorsystem-homewatcher.groovy Made small changes to logged information. * Update gatorsystem-homewatcher.groovy Deleted a line break. * Update gatorsystem-homewatcher.groovy Revised per Konrad's comments. * Update gatorsystem-homewatcher.groovy Minor revisions to log information. * Update gatorsystem-homewatcher.groovy Revised per Conrad's comments. Co-authored-by: Ying Zhao --- .../gatorsystem-homewatcher.groovy | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 devicetypes/gs/gatorsystem-homewatcher.src/gatorsystem-homewatcher.groovy diff --git a/devicetypes/gs/gatorsystem-homewatcher.src/gatorsystem-homewatcher.groovy b/devicetypes/gs/gatorsystem-homewatcher.src/gatorsystem-homewatcher.groovy new file mode 100644 index 00000000000..567d8ca2de7 --- /dev/null +++ b/devicetypes/gs/gatorsystem-homewatcher.src/gatorsystem-homewatcher.groovy @@ -0,0 +1,127 @@ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +metadata { + definition (name: "GatorSystem HomeWatcher", namespace: "GS", author: "GS_coder000") { + capability "Motion Sensor" + capability "Battery" + capability "Contact Sensor" + capability "Presence Sensor" + + // Raw Description 08 0104 0402 00 02 0000 0500 01 0502 + fingerprint manufacturer: "GatorSystem", model: "GSHW01", deviceJoinName: "GatorSystem Multipurpose Sensor" + } +} + +def parse(String description) { + log.debug "${device.displayName} description: $description" + Map map = [:] + if (description?.startsWith('catchall:')) { //raw commands that smartthings does not or cannot interpret + map = zigbee.parseDescriptionAsMap(description) + } else if (description?.startsWith('read attr -')) { + map = zigbee.parseDescriptionAsMap(description) + } else if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } + log.debug "Parse returned map $map" + if (map != null) { + createEvent(map) + } +} + +private Map parseIasMessage(String description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + Map resultMap = [:] + def oneMinute = 60 + def twoMinutes = 120 + def resultOccupied = '0x8000' + def resultOpenned = '0x4000' + def resultClosed = '0x2000' + def resultBatteryNew = '0x1000' + def resultBatteryOut = '0x0800' + if (zs.isAlarm1Set()) { + runIn(twoMinutes, stopMotion) + resultMap = getMotionResult('active') + } else if (zs.isBatterySet()) { + resultMap = getBatteryResult(10) + } else if (description.contains(resultOccupied)) { + runIn(oneMinute, resetOccupancy) + resultMap = getMotionResult('occupied') + } else if (description.contains(resultOpenned)) { + resultMap = getMotionResult('openned') + } else if (description.contains(resultClosed)) { + resultMap = getMotionResult('closed') + } else if (description.contains(resultBatteryNew)) { + resultMap = getBatteryResult(100) + } else if (description.contains(resultBatteryOut)) { + resultMap = getBatteryResult(0) + } +} + +private Map getBatteryResult(rawValue) { + log.debug "Battery rawValue = ${rawValue}" + createEvent(name:"battery", value:rawValue) +} + +private Map getMotionResult(value) { + if (value == 'active') { + log.debug 'detected intrusion' + String descriptionText = "{{ device.displayName }} detected intrusion" + createEvent( + name: 'motion', + value: value, + descriptionText: descriptionText, + translatable: false + ) + } else if (value == 'occupied') { + log.debug 'detected occupancy' + String descriptionText = "{{ device.displayName }} detected occupancy" + createEvent( + name: 'presence', + value: "present", + descriptionText: descriptionText, + translatable: false + ) + } else if (value == 'openned') { + log.debug 'detected window openned' + String descriptionText = "{{ device.displayName }} detected window openned" + createEvent( + name: 'contact', + value: "open", + descriptionText: descriptionText, + translatable: false + ) + } else if (value == 'closed') { + log.debug 'detected window closed' + String descriptionText = "{{ device.displayName }} detected window closed" + createEvent( + name: 'contact', + value: "closed", + descriptionText: descriptionText, + translatable: false + ) + } +} + +def installed() { + initialize() +} + +def initialize() { + sendEvent(name:"motion", value:"inactive") + sendEvent(name:"battery", value:"100") + sendEvent(name:"presence", value:"not present") + sendEvent(name:"contact", value:"closed") +} + +def stopMotion() { + if (device.currentState('motion')?.value == "active") { + sendEvent(name:"motion", value:"inactive", isStateChange: true) + log.debug "${device.displayName} reset to monitoring after 120 seconds" + } +} + +def resetOccupancy() { + if (device.currentState('presence')?.value == "present") { + sendEvent(name:"presence", value:"not present", isStateChange: true) + log.debug "${device.displayName} reset to sensing after 60 seconds" + } +} From 3e81c589515fad6d35fe27ff55f97cda9c4df402 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Mon, 16 Nov 2020 16:50:56 +0100 Subject: [PATCH 117/422] DevWs for frient containing containing SmartSense Temp/Humidity Sensor (#49853) * DevWs for frient containing containing SmartSense Temp/Humidity Sensor * fixed encoding issue * fixing encoding issues * fixed encoding issue * fixed manufacturer name issue * fixed indentation * Fix indentation formatting * Random indentation fix * Fix various indentations * Fix identation Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../smartsense-temp-humidity-sensor.groovy | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 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 b600c95c1ef..2cca97187ed 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 @@ -25,7 +25,7 @@ metadata { capability "Health Check" capability "Sensor" - + fingerprint profileId: "0104", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003", manufacturer: "CentraLite", model: "3310-S", deviceJoinName: "Multipurpose Sensor" fingerprint profileId: "0104", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003", manufacturer: "CentraLite", model: "3310-G", deviceJoinName: "Centralite Multipurpose Sensor" //Centralite Temp & Humidity Sensor fingerprint profileId: "0104", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003", manufacturer: "CentraLite", model: "3310", deviceJoinName: "Multipurpose Sensor" @@ -33,7 +33,7 @@ metadata { fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0003,0402", manufacturer: "HEIMAN", model: "888a434f3cfc47f29ec4a3a03e9fc442", deviceJoinName: "Orvibo Multipurpose Sensor" //Orvibo Temperature & Humidity Sensor fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0009, 0402", manufacturer: "HEIMAN", model: "HT-EM", deviceJoinName: "HEIMAN Multipurpose Sensor" //HEIMAN Temperature & Humidity Sensor fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0B05", manufacturer: "HEIMAN", model: "HT-EF-3.0", deviceJoinName: "HEIMAN Multipurpose Sensor" //HEIMAN Temperature & Humidity Sensor - + fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0003,0020,0402,0405", outClusters: "0003,000A,0019", manufacturer: "frient A/S", model :"HMSZB-110", deviceJoinName: "frient Multipurpose Sensor" // frient Humidity Sensor } simulator { @@ -88,10 +88,10 @@ def parse(String description) { 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)) + map = getBatteryPercentageResult(Integer.parseInt(descMap.value,16)) } else { map = getBatteryResult(Integer.parseInt(descMap.value, 16)) - } + } } else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) { if (descMap.data[0] == "00") { log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap" @@ -135,11 +135,11 @@ private Map getBatteryResult(rawValue) { log.debug 'Battery' def linkText = getLinkText(device) - def result = [:] + def result = [:] def volts = rawValue / 10 if (!(rawValue == 0 || rawValue == 255)) { - def minVolts = 2.1 + def minVolts = isFrientSensor() ? 2.3 : 2.1 def maxVolts = 3.0 def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) @@ -168,13 +168,17 @@ def refresh() { if (manufacturer == "Heiman"|| manufacturer == "HEIMAN") { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, [destEndpoint: 0x01])+ - zigbee.readAttribute(0x0402, 0x0000, [destEndpoint: 0x01])+ - zigbee.readAttribute(0x0405, 0x0000, [destEndpoint: 0x02]) + zigbee.readAttribute(0x0402, 0x0000, [destEndpoint: 0x01])+ + zigbee.readAttribute(0x0405, 0x0000, [destEndpoint: 0x02]) + } else if (isFrientSensor()) { + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)+ + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)+ + zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000) } 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) + zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) } } @@ -190,14 +194,23 @@ def configure() { def manufacturer = device.getDataValue("manufacturer") if (manufacturer == "Heiman"|| 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]) + 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 if (isFrientSensor()) { + return refresh() + + zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000, DataType.UINT16, 60, 600, 1*100) + + zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 60, 600, 0xA) + + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020, DataType.UINT8, 30, 21600, 0x1) } 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) + 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) } } + +private Boolean isFrientSensor() { + device.getDataValue("manufacturer") == "frient A/S" +} From 052abf4a3d239538d08dd6506b4ad714c14079b4 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Mon, 16 Nov 2020 17:01:46 +0100 Subject: [PATCH 118/422] DevWs for frient containing containing SmartSense Motion Sensor (#49364) * DevWs for frient containing containing SmartSense Motion Sensor * Update smartsense-motion-sensor.groovy changed runLocally to true * Harmonize deviceJoinName * Fix indentation formatting * Fixed indentations for fingerprints Changed spaces to tabs and aligned them * Fix identation Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../smartsense-motion-sensor.groovy | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index fc0b1948794..2557342635a 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -40,9 +40,10 @@ metadata { fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "Bosch", model: "RFDL-ZB-MS", deviceJoinName: "Bosch Motion Sensor" //Bosch Motion Sensor fingerprint inClusters: "0000,0001,0003,0020,0402,0500", outClusters: "0019", manufacturer: "Samjin", model: "motion", deviceJoinName: "Motion Sensor" // This is the only ST sensor that shouldn't use SmartThings-smartthings-SmartSense_Motion_Sensor fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "Ecolink", model: "PIRZB1-ECO", deviceJoinName: "Ecolink Motion Sensor" //Ecolink Motion Detector - //AduroSmart - fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "ADUROLIGHT", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.0 - fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "AduroSmart Eria", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.1 + //AduroSmart + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "ADUROLIGHT", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.0 + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "AduroSmart Eria", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.1 + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"MOSZB-140", deviceJoinName: "frient Motion Sensor" } simulator { @@ -133,7 +134,7 @@ def parse(String description) { map = getBatteryResult(Integer.parseInt(battMap.value, 16)) } } - } else if (descMap?.clusterInt == 0x0500 && descMap.attrInt == 0x0002 && descMap.commandInt != 0x07) { + } else if (descMap?.clusterInt == 0x0500 && descMap.attrInt == 0x0002 && descMap.commandInt != 0x07 && descMap.value != null) { def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16)) map = translateZoneStatus(zs) } else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) { @@ -212,6 +213,12 @@ private Map getBatteryResult(rawValue) { def pct = Math.round((rawValue - minValue) * 100 / (maxValue - minValue)) pct = pct > 0 ? pct : 1 result.value = Math.min(100, pct) + } else if (isFrientSensor()) { + def minValue = 23 + def maxValue = 30 + def pct = Math.round((rawValue - minValue) * 100 / (maxValue - minValue)) + pct = pct > 0 ? pct : 1 + result.value = Math.min(100, pct) } else { // Centralite def useOldBatt = shouldUseOldBatteryReporting() def minVolts = useOldBatt ? 2.1 : 2.4 @@ -307,10 +314,17 @@ def configure() { // battery minReport 30 seconds, maxReportTime 6 hrs by default if (device.getDataValue("manufacturer") == "Samjin") { configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) + } else if (isFrientSensor()) { + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020, DataType.UINT8, 30, 21600, 0x1, powerEndpoint()) } else { configCmds += zigbee.batteryConfig() } - configCmds += zigbee.temperatureConfig(30, 300) + + if (isFrientSensor()) { + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 300, 0x64, temperatureEndpoint()) + } else { + configCmds += zigbee.temperatureConfig(30, 300) + } configCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) configCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr) @@ -335,3 +349,23 @@ private shouldUseOldBatteryReporting() { return isFwVersionLess // If f/w version is less than 1.15.7 then do NOT smooth battery reports and use the old reporting } + +private Boolean isFrientSensor() { + device.getDataValue("manufacturer") == "frient A/S" +} + +private Map temperatureEndpoint() { + if (isFrientSensor()) { + [destEndpoint: 0x26] + } else { + [:] + } +} + +private Map powerEndpoint() { + if (isFrientSensor()) { + [destEndpoint: 0x23] + } else { + [:] + } +} From 73968b82534eccd930ea90bc39a0ac0601914b90 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Mon, 16 Nov 2020 17:18:58 +0100 Subject: [PATCH 119/422] DevWs for frient containing containing SmartSense Open/Closed Sensor (#49363) * DevWs for frient containing containing SmartSense Open/Closed Sensor * Update smartsense-open-closed-sensor.groovy changed runLocally to true * Fix fingerprints As requested * Fixed indentation * Fixed indentation * Fix indentation Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../smartsense-open-closed-sensor.groovy | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index 698174ae1ae..971ff96e33d 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -43,6 +43,7 @@ metadata { fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Sercomm Corp.", model: "SZ-DWS04", deviceJoinName: "Sercomm Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact" //Sercomm Door Window Sensor //Dawon fingerprint inClusters: "0000, 0003, 0006, 0500", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "SS-B100-ZB", deviceJoinName: "Dawon Signal Interlock", mnmn: "0AIg", vid: "dawon-zigbee-signal-interlock2" + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"WISZB-120", deviceJoinName: "frient Open/Closed Sensor" } simulator { @@ -161,7 +162,7 @@ private Map getBatteryResult(rawValue) { def volts = rawValue / 10 if (!(rawValue == 0 || rawValue == 255)) { - def minVolts = 2.1 + def minVolts = isFrientSensor() ? 2.3 : 2.1 def maxVolts = 3.0 def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) @@ -216,6 +217,8 @@ def configure() { cmds += configureEcolink() } else if (isBoschRadionMultiSensor()) { cmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, IAS_ZONE_TYPE_ATTRIBUTE) + } else if (isFrientSensor()) { + cmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 60 * 30, 0x64, temperatureEndpoint()) } // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity // battery minReport 30 seconds, maxReportTime 6 hrs by default @@ -237,4 +240,16 @@ private Boolean isEcolink() { private Boolean isBoschRadionMultiSensor() { device.getDataValue("manufacturer") == "Bosch" && device.getDataValue("model") == "RFMS-ZBMS" -} \ No newline at end of file +} + +private Boolean isFrientSensor() { + device.getDataValue("manufacturer") == "frient A/S" +} + +private Map temperatureEndpoint() { + if (isFrientSensor()) { + [destEndpoint: 0x26] + } else { + [:] + } +} From ca671d8ae0b3e24011419c6133aa0cf98d5109eb Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Mon, 16 Nov 2020 19:22:10 +0100 Subject: [PATCH 120/422] [ICP-13810, ICP-13809] Qubino: Fix for powerMeter and energyCompsumption (#49845) --- .../qubino-flush-2-relay.groovy | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 0415fdd04d5..033eba2b593 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -132,7 +132,9 @@ def excludeParameterFromSync(preference){ def addToAssociationGroupIfNeeded() { def cmds = [] if (zwaveInfo?.model?.equals("0052")) { - cmds += encap(zwave.associationV2.associationSet(groupingIdentifier: 2, nodeId: [zwaveHubNodeId])) + //Hub automatically adds device to multiChannelAssosciationGroup and this needs to be removed + cmds += encap(zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1, nodeId:[])) + cmds += encap(zwave.associationV2.associationSet(groupingIdentifier: 1, nodeId: [zwaveHubNodeId])) } cmds } @@ -245,17 +247,6 @@ def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) changeSwitch(ep, cmd) } -def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, ep = null) { - log.debug "Basic ${cmd}" + (ep ? " from endpoint $ep" : "") - [ - changeSwitch(ep, cmd), - response([ - "delay 2000", - encap(zwave.meterV3.meterGet(scale: 2), endpoint) - ]) - ] -} - def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { log.debug "Binary ${cmd}" + (ep ? " from endpoint $ep" : "") changeSwitch(ep, cmd) From f63661cc124f201028f56a7d39b47954f81bcc67 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Mon, 16 Nov 2020 19:32:51 +0100 Subject: [PATCH 121/422] WWST-7147 Moved qubino mini dimmer fingerprint to qubino dimmer DTH (#49935) --- devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy | 4 ++++ .../zwave-metering-dimmer.src/zwave-metering-dimmer.groovy | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index 76d8f38c715..adb86d4b3ce 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -34,6 +34,10 @@ metadata { // Qubino Flush Dimmer 0-10V - ZMNHVD // Raw Description: zw:L type:1100 mfr:0159 prod:0001 model:0053 ver:2.04 zwv:4.34 lib:03 cc:5E,86,5A,72,73,27,25,26,85,8E,59,70 ccOut:20,26 role:05 ff:9C00 ui:9C00 fingerprint mfr: "0159", prod: "0001", model: "0053", deviceJoinName: "Qubino Dimmer", mnmn: "SmartThings", vid:"generic-dimmer" + + //Qubino Mini Dimmer + // Raw Description: zw:Ls type:1101 mfr:0159 prod:0001 model:0055 ver:20.02 zwv:5.03 lib:03 cc:5E,6C,55,98,9F sec:86,25,26,85,59,72,5A,70,32,71,73 + fingerprint mfr:"0159", prod:"0001", model:"0055", deviceJoinName: "Qubino Dimmer" } tiles(scale: 2) { diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index e0b876a7b8b..8a6853ee5fb 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -39,7 +39,6 @@ metadata { fingerprint mfr:"0086", prod:"0003", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //Aeotec Nano Dimmer fingerprint mfr:"0086", prod:"0203", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //AU //Aeotec Nano Dimmer fingerprint mfr:"014F", prod:"5044", model:"3533", deviceJoinName: "GoControl Dimmer Switch" //GoControl Plug-in Dimmer - fingerprint mfr:"0159", prod:"0001", model:"0055", deviceJoinName: "Qubino Dimmer Switch" //Qubino Mini Dimmer ZMNHHD1 } simulator { From 29f142e59682083fba7519ee5151022aff4c589e Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 16 Nov 2020 11:18:00 -0800 Subject: [PATCH 122/422] Frient cleanup (#50163) * removing some extra loc that were added to these DTHs * making whitespace consistent --- .../smartsense-motion-sensor.groovy | 20 ++--------------- .../smartsense-open-closed-sensor.groovy | 22 ++++++------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index 2557342635a..6d77e8b2f07 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -315,13 +315,13 @@ def configure() { if (device.getDataValue("manufacturer") == "Samjin") { configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) } else if (isFrientSensor()) { - configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020, DataType.UINT8, 30, 21600, 0x1, powerEndpoint()) + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020, DataType.UINT8, 30, 21600, 0x1, [destEndpoint: 0x23]) } else { configCmds += zigbee.batteryConfig() } if (isFrientSensor()) { - configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 300, 0x64, temperatureEndpoint()) + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 300, 0x64, [destEndpoint: 0x26]) } else { configCmds += zigbee.temperatureConfig(30, 300) } @@ -353,19 +353,3 @@ private shouldUseOldBatteryReporting() { private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "frient A/S" } - -private Map temperatureEndpoint() { - if (isFrientSensor()) { - [destEndpoint: 0x26] - } else { - [:] - } -} - -private Map powerEndpoint() { - if (isFrientSensor()) { - [destEndpoint: 0x23] - } else { - [:] - } -} diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index 971ff96e33d..4743f7911b1 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -22,7 +22,7 @@ metadata { capability "Configuration" capability "Contact Sensor" capability "Refresh" - capability "Temperature Measurement" + capability "Temperature Measurement" capability "Health Check" capability "Sensor" @@ -37,12 +37,12 @@ metadata { fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05,FC01,FC02", outClusters: "0003,0019", manufacturer: "iMagic by GreatStar", model: "1116-S", deviceJoinName: "Iris Open/Closed Sensor" //Iris Contact Sensor fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Bosch", model: "RFMS-ZBMS", deviceJoinName: "Bosch Open/Closed Sensor" //Bosch multi-sensor fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Megaman", model: "MS601/z1", deviceJoinName: "INGENIUM Open/Closed Sensor" //INGENIUM ZB Magnetic ON/OFF Sensor - //AduroSmart - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "CSW_ADUROLIGHT", deviceJoinName: "ERIA Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Contact Sensor V2.1 - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "ADUROLIGHT", model: "CSW_ADUROLIGHT", deviceJoinName: "ERIA Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Contact Sensor V2.0 + //AduroSmart + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "CSW_ADUROLIGHT", deviceJoinName: "ERIA Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Contact Sensor V2.1 + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "ADUROLIGHT", model: "CSW_ADUROLIGHT", deviceJoinName: "ERIA Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-3" //ERIA Contact Sensor V2.0 fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "Sercomm Corp.", model: "SZ-DWS04", deviceJoinName: "Sercomm Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact" //Sercomm Door Window Sensor - //Dawon - fingerprint inClusters: "0000, 0003, 0006, 0500", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "SS-B100-ZB", deviceJoinName: "Dawon Signal Interlock", mnmn: "0AIg", vid: "dawon-zigbee-signal-interlock2" + //Dawon + fingerprint inClusters: "0000, 0003, 0006, 0500", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "SS-B100-ZB", deviceJoinName: "Dawon Signal Interlock", mnmn: "0AIg", vid: "dawon-zigbee-signal-interlock2" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"WISZB-120", deviceJoinName: "frient Open/Closed Sensor" } @@ -218,7 +218,7 @@ def configure() { } else if (isBoschRadionMultiSensor()) { cmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, IAS_ZONE_TYPE_ATTRIBUTE) } else if (isFrientSensor()) { - cmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 60 * 30, 0x64, temperatureEndpoint()) + cmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 60 * 30, 0x64, [destEndpoint: 0x26]) } // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity // battery minReport 30 seconds, maxReportTime 6 hrs by default @@ -245,11 +245,3 @@ private Boolean isBoschRadionMultiSensor() { private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "frient A/S" } - -private Map temperatureEndpoint() { - if (isFrientSensor()) { - [destEndpoint: 0x26] - } else { - [:] - } -} From 0da890bb286a44a83d0434824b0dc059746afe4b Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Tue, 17 Nov 2020 12:00:45 +0100 Subject: [PATCH 123/422] DevWs for frient containing containing Zigbee Smoke Sensor (#49599) * DevWs for frient containing containing Zigbee Smoke Sensor * Fix identation Possible fix encoding causing large diff * Harmonize deviceJoinName * Update zigbee-smoke-sensor.groovy Fix line end coding issues * Use constants where possible * Use constants for battery attributes * Clean up power configuration cluster Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-smoke-sensor.groovy | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) mode change 100755 => 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 old mode 100755 new mode 100644 index 111dfe830ec..8a29be42aa2 --- a/devicetypes/smartthings/zigbee-smoke-sensor.src/zigbee-smoke-sensor.groovy +++ b/devicetypes/smartthings/zigbee-smoke-sensor.src/zigbee-smoke-sensor.groovy @@ -32,7 +32,7 @@ metadata { fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502,0009", outClusters: "0019", manufacturer: "HEIMAN", model: "98293058552c49f38ad0748541ee96ba", deviceJoinName: "Orvibo Smoke Detector" //欧瑞博 烟雾报警器(SF21) fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-EM", deviceJoinName: "HEIMAN Smoke Detector" //HEIMAN Smoke Sensor (HS1SA-E) fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500,0502,0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "SmokeSensor-N-3.0", deviceJoinName: "HEIMAN Smoke Detector" //HEIMAN Smoke Sensor (HS3SA) - + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500,0502", outClusters: "000A,0019", manufacturer: "frient A/S", model :"SMSZB-120", deviceJoinName: "frient Smoke Detector" // frient Intelligent Smoke Alarm } tiles { @@ -56,6 +56,9 @@ metadata { } } +def getBATTERY_VOLTAGE_ATTR() { 0x0020 } +def getBATTERY_PERCENT_ATTR() { 0x0021 } + def installed(){ log.debug "installed" @@ -86,7 +89,11 @@ 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)) + if (descMap.attrInt == BATTERY_VOLTAGE_ATTR) { + map = getBatteryResult(Integer.parseInt(descMap.value, 16)) + } else { + map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) + } } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS) { def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16)) map = translateZoneStatus(zs) @@ -103,6 +110,29 @@ private Map translateZoneStatus(ZoneStatus zs) { return getDetectedResult(zs.isAlarm1Set() || zs.isAlarm2Set()) } +private Map getBatteryResult(rawValue) { + log.debug "Battery rawValue = ${rawValue}" + def linkText = getLinkText(device) + + def result = [:] + + def volts = rawValue / 10 + + if (!(rawValue == 0 || rawValue == 255)) { + result.name = 'battery' + result.translatable = true + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" + + def minValue = 23 + def maxValue = 30 + def pct = Math.round((rawValue - minValue) * 100 / (maxValue - minValue)) + pct = pct > 0 ? pct : 1 + result.value = Math.min(100, pct) + } + + return result +} + private Map getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] @@ -129,7 +159,8 @@ def getDetectedResult(value) { def refresh() { log.debug "Refreshing Values" def refreshCmds = [] - refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + + def batteryAttr = isFrientSensor() ? BATTERY_VOLTAGE_ATTR : BATTERY_PERCENT_ATTR + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) return refreshCmds } @@ -148,6 +179,14 @@ def configure() { Integer minReportTime = 0 Integer maxReportTime = 180 Integer reportableChange = null - return refresh() + zigbee.enrollResponse() + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 1200, 0x10) + - zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, minReportTime, maxReportTime, reportableChange) + Integer batteryAttr = isFrientSensor() ? BATTERY_VOLTAGE_ATTR : BATTERY_PERCENT_ATTR + Integer batteryReportChange = isFrientSensor() ? 0x1 : 0x10 + return refresh() + + zigbee.enrollResponse() + + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, batteryAttr, DataType.UINT8, 30, 1200, batteryReportChange) + + zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, minReportTime, maxReportTime, reportableChange) +} + +private Boolean isFrientSensor() { + device.getDataValue("manufacturer") == "frient A/S" } From c32aae0bfcf54c15a9f84a5efe8798099f263743 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Tue, 17 Nov 2020 19:33:15 +0100 Subject: [PATCH 124/422] DevWs for frient containing containing SmartSense Moisture Sensor (#49598) * DevWs for frient containing containing SmartSense Moisture Sensor * Fix indentation Possibly encoding issue * Harmonize deviceJoinName * Try to fix line endings * Update smartsense-moisture-sensor.groovy Try to fix line endings * Inline temperature endpoint and use constants Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../smartsense-moisture-sensor.groovy | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy 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 100755 new mode 100644 index e57cd991447..dc5da7afaf6 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -34,6 +34,7 @@ metadata { fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor", mnmn: "SmartThings", vid: "smartthings-water-leak-3315S-STSWTR" fingerprint inClusters: "0000,0001,0003,0020,0402,0500", outClusters: "0019", manufacturer: "Samjin", model: "water", deviceJoinName: "Water Leak Sensor", mnmn: "SmartThings", vid: "smartthings-water-leak-IM6001" fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "Sercomm Corp.", model: "SZ-WTD03", deviceJoinName: "Sercomm Water Leak Sensor" //Sercomm Water Leak Detector + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500,0502", outClusters: "000A,0019", manufacturer: "frient A/S", model :"FLSZB-110", deviceJoinName: "frient Water Leak Sensor" // frient Water Leak Detector } simulator { @@ -84,6 +85,9 @@ metadata { } } +def getBATTERY_VOLTAGE_ATTR() { 0x0020 } +def getBATTERY_PERCENT_ATTR() { 0x0021 } + private List collectAttributes(Map descMap) { List descMaps = new ArrayList() @@ -111,13 +115,13 @@ def parse(String description) { List descMaps = collectAttributes(descMap) if (device.getDataValue("manufacturer") == "Samjin") { - def battMap = descMaps.find { it.attrInt == 0x0021 } + def battMap = descMaps.find { it.attrInt == BATTERY_PERCENT_ATTR } if (battMap) { map = getBatteryPercentageResult(Integer.parseInt(battMap.value, 16)) } } else { - def battMap = descMaps.find { it.attrInt == 0x0020 } + def battMap = descMaps.find { it.attrInt == BATTERY_VOLTAGE_ATTR } if (battMap) { map = getBatteryResult(Integer.parseInt(battMap.value, 16)) @@ -192,7 +196,7 @@ private Map getBatteryResult(rawValue) { def pct = batteryMap[volts] result.value = pct } else { - def minVolts = 2.1 + def minVolts = isFrientSensor() ? 2.3 : 2.1 def maxVolts = 3.0 def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) @@ -247,9 +251,9 @@ def refresh() { def refreshCmds = [] if (device.getDataValue("manufacturer") == "Samjin") { - refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_PERCENT_ATTR) } else { - refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_VOLTAGE_ATTR) } refreshCmds += zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + @@ -269,11 +273,20 @@ def configure() { // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity // battery minReport 30 seconds, maxReportTime 6 hrs by default if (device.getDataValue("manufacturer") == "Samjin") { - configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 21600, 0x10) + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_PERCENT_ATTR, DataType.UINT8, 30, 21600, 0x10) } else { configCmds += zigbee.batteryConfig() } - configCmds += zigbee.temperatureConfig(30, 300) + + if (isFrientSensor()) { + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 60, 600, 0x64, [destEndpoint: 0x26]) + } else { + configCmds += zigbee.temperatureConfig(30, 300) + } return refresh() + configCmds + refresh() // send refresh cmds as part of config } + +private Boolean isFrientSensor() { + device.getDataValue("manufacturer") == "frient A/S" +} From 9899ddaceac2950eacca1d3a2d017eca07780e7e Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Thu, 19 Nov 2020 11:15:13 +0100 Subject: [PATCH 125/422] DevWs for frient containing containing Zigbee Metering Plug (#50266) * DevWs for frient containing containing Zigbee Metering Plug * Update zigbee-metering-plug.groovy trying to fix the diff problem * Update zigbee-metering-plug.groovy Another try - we only changed 4 lines (40 - 43) * Try to fix CRLF * Inserting CRLF Original file contains CRLF * Adding fingerprints for frient miniplugs * Fix duplicate fingerprint Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 4 ++++ 1 file changed, 4 insertions(+) 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 e2e81d2b622..d7ae96afb45 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -37,6 +37,10 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "ST-B550-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C150-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C250-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet + fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-131", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 + fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-132", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 + fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-134", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 + fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SMRZB-143", deviceJoinName: "frient Outlet" // frient smart cable, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 } tiles(scale: 2){ From 01948d12a1eb71cdedb025c1d84c59a4890bf081 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Fri, 20 Nov 2020 08:35:27 +0100 Subject: [PATCH 126/422] ICP-13534, ICP-13535, ICP-13538 - Fix for setting zwave associations. Remove redundant polling. (#50157) * ICP-13534, ICP-13535, ICP-13538 - Fix for setting zwave associations. Remove redundant polling (device reports it itself when its state is changed). * ICP-13534, ICP-13535, ICP-13538 - removed unnecessary multiChannelEndPointGet command. --- .../qubino-dimmer.src/qubino-dimmer.groovy | 37 ++----------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index adb86d4b3ce..aa3f3b1df93 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -230,13 +230,9 @@ def configure() { Group 5: Multilevel sensor report (external temperature sensor report). */ - commands << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]) - commands << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:[zwaveHubNodeId]) - commands << zwave.associationV1.associationSet(groupingIdentifier:3, nodeId:[zwaveHubNodeId]) - commands << zwave.associationV1.associationSet(groupingIdentifier:4, nodeId:[zwaveHubNodeId]) - commands << zwave.associationV1.associationSet(groupingIdentifier:5, nodeId:[zwaveHubNodeId]) - commands << zwave.associationV1.associationSet(groupingIdentifier:6, nodeId:[zwaveHubNodeId]) - commands << zwave.multiChannelV3.multiChannelEndPointGet() + commands << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier:1, nodeId:[]) + commands << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]) + commands << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1) commands += getRefreshCommands() commands += getReadConfigurationFromTheDeviceCommands() @@ -334,29 +330,11 @@ def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) dimmerEvents(cmd) } -def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, ep = null) { - log.debug "BasicSet: ${cmd}" - def input1SwitchType = Integer.parseInt(state.currentPreferencesState.input1SwitchType.value) - - if(input1SwitchType == INPUT_TYPE_POTENTIOMETER) { - log.debug "BasicSet: ${cmd} / INPUT_TYPE_POTENTfIOMETER" - sendHubCommand(encap(zwave.switchMultilevelV3.switchMultilevelGet())) - } else if (input1SwitchType == INPUT_TYPE_BI_STABLE_SWITCH) { - log.debug "BasicSet: ${cmd} / INPUT_TYPE_BI_STABLE_SWITCH" - dimmerEvents(cmd) - } -} - def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd, ep = null) { log.debug "SwitchMultilevelReport: ${cmd}" dimmerEvents(cmd) } -def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelSet cmd, ep = null) { - log.debug "SwitchMultilevelSet: ${cmd}" - dimmerEvents(cmd) -} - def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { log.debug "MeterReport: ${cmd}" handleMeterReport(cmd) @@ -385,11 +363,6 @@ private dimmerEvents(physicalgraph.zwave.Command cmd, ep = null) { result << createEvent(name: "level", value: cmdValue == 99 ? 100 : cmdValue) } - if(supportsPowerMeter()){ - log.debug "query device for power meter values" - sendHubCommand(encapCommands(getPowerMeterCommands())) - } - return result } @@ -458,7 +431,6 @@ def createChildDevice(childDthNamespace, childDthName, childDni, childComponentL def on() { def commands = [ zwave.switchMultilevelV3.switchMultilevelSet(value: 0xFF, dimmingDuration: 0x00), - zwave.switchMultilevelV3.switchMultilevelGet() ] encapCommands(commands, 3000) @@ -467,7 +439,6 @@ def on() { def off() { def commands = [ zwave.switchMultilevelV3.switchMultilevelSet(value: 0x00, dimmingDuration: 0x00), - zwave.switchMultilevelV3.switchMultilevelGet() ] encapCommands(commands, 3000) @@ -490,9 +461,7 @@ def setLevel(value, duration = null) { } def adjustedLevel = adjustValueToRange(level) - commands << zwave.switchMultilevelV3.switchMultilevelSet(value: adjustedLevel, dimmingDuration: dimmingDuration) - commands << zwave.switchMultilevelV3.switchMultilevelGet() encapCommands(commands, getStatusDelay) } From 90037a472971864a00b58f46c07fd9f2730641b7 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 20 Nov 2020 20:02:33 +0100 Subject: [PATCH 127/422] [ICP-13712] Qubino 3 Phase Meter - additional poll for endpoints (#46481) * Added additional poll for endpoints * fixup! Added additional poll for endpoints * Removed unnecessary zwaveEvent() call * Removed call of event's descriptionText as it sometimes isn't defined leading to an exception to be thrown * Removed unnecessary endpoints polls, added proper configuration --- .../qubino-3-phase-meter.groovy | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy b/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy index fef00c54bd2..e61f2982096 100644 --- a/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy +++ b/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy @@ -70,9 +70,9 @@ def parse(String description) { def result = null def cmd = zwave.parse(description) if (cmd) { - result = createEvent(zwaveEvent(cmd)) + result = zwaveEvent(cmd) } - log.debug "Parse returned ${result?.descriptionText}" + log.debug "Parse returned ${result}" return result } @@ -158,7 +158,15 @@ def refresh() { def configure() { log.debug "configure() has been called" - encap(zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: 1800)) // Report energy consumption every 30 minutes + def configCmds = [] + configCmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: 1800)) // Report energy consumption every 30 minutes + configCmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 10)) // Report every 10% power usage change on root endpoint + + for (int endpoint : [2, 3, 4]) { + configCmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 10), endpoint) // Report every 10% power usage change on each endpoint + } + + configCmds } private addChildMeters(numberOfMeters) { @@ -185,11 +193,21 @@ private getMeterId(deviceNetworkId) { } private childRefresh(deviceNetworkId) { - def meterId = getMeterId(deviceNetworkId) - if (switchId != null) { + def meterId = getMeterId(deviceNetworkId) + 1 + if (meterId != null) { sendHubCommand delayBetween([ encap(zwave.meterV3.meterGet(scale: 0), meterId), encap(zwave.meterV3.meterGet(scale: 2), meterId) ]) } +} + +private pollEndpoints() { + def cmds = [] + def meterId + childDevices.each { + meterId = getMeterId(it.deviceNetworkId) + 1 + cmds += encap(zwave.meterV3.meterGet(scale: 2), meterId) + } + cmds } \ No newline at end of file From 34f35cca26acfb9fca4c7019eb9388e50e2811d6 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 20 Nov 2020 20:38:13 +0100 Subject: [PATCH 128/422] [WWST-6946] Somfy Glydea Ultra - fingerprint update and tweaks (#50407) * New fingerprint for Somfy Glydea Ultra, alongside with fix for timeout * Removed call to undefined method * Moved state.invalidSameLevelEvent to correct block --- .../zigbee-window-shade.groovy | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) 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 705c2520288..2d8dcf69cfd 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -32,7 +32,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.8", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "KG0001", deviceJoinName: "Window Treatment" //Smart Curtain Motor(BCM300D) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "DY0010", deviceJoinName: "Window Treatment" //Smart Curtain Motor(DT82TV) - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Curtain", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Ultra Curtain", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack } @@ -122,9 +122,10 @@ def parse(String description) { def levelEventHandler(currentLevel) { def lastLevel = device.currentValue("level") log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" - if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports + if ((lastLevel == "undefined" || currentLevel == lastLevel) && state.invalidSameLevelEvent) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { + state.invalidSameLevelEvent = true sendEvent(name: "level", value: currentLevel) if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") @@ -163,6 +164,12 @@ def open() { def setLevel(data, rate = null) { log.info "setLevel()" + def currentLevel = device.currentValue("level") + + if (isSomfy() && Math.abs(data - currentLevel) <= GLYDEA_MOVE_THRESHOLD) { + state.invalidSameLevelEvent = false + } + def cmd if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { @@ -211,6 +218,7 @@ def refresh() { } def installed() { + state.invalidSameLevelEvent = true sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) } @@ -227,10 +235,6 @@ def configure() { cmds = zigbee.levelConfig() } - if (usesLocalGroupBinding()) { - cmds += readDeviceBindingTable() - } - return refresh() + cmds } @@ -264,4 +268,6 @@ def shouldInvertLiftPercentage() { def isSomfy() { device.getDataValue("manufacturer") == "SOMFY" -} \ No newline at end of file +} + +private getGLYDEA_MOVE_THRESHOLD() { 3 } From cd87fb949dffdcb6d7956ac1c4cd734378161fd7 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 23 Nov 2020 18:45:28 +0100 Subject: [PATCH 129/422] [ICP-13816] Qubino Flush 2 Relay - making sure energy usage is polled (#49675) * Switched from response() to sendHubCommand() * Wrapped response() in list * Re-arranged meterReport handler --- .../qubino-flush-2-relay.groovy | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 033eba2b593..ba05096bd2c 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -272,18 +272,24 @@ private changeSwitch(endpoint, cmd) { } def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, ep = null) { + def result = [] + log.debug "Meter ${cmd}" + (ep ? " from endpoint $ep" : "") + if (ep == defaultEndpoint()) { - [ - createEvent(createMeterEventMap(cmd)), - cmd.scale == 2 ? response(encap(zwave.meterV3.meterGet(scale: 0x00), ep)) : null - ] + result << createEvent(createMeterEventMap(cmd)) } else if (ep) { String childDni = "${device.deviceNetworkId}:$ep" def child = childDevices.find { it.deviceNetworkId == childDni } + child?.sendEvent(createMeterEventMap(cmd)) - response(encap(zwave.meterV3.meterGet(scale: 0x00), ep)) } + // Query energy when we receive power reports + if (cmd.scale == 2) { + result << response(encap(zwave.meterV3.meterGet(scale: 0x00), ep)) + } + + result } private createMeterEventMap(cmd) { From 17135e094c968586fc5c25b9811a5a88f1f9d59f Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:15:35 +0100 Subject: [PATCH 130/422] ICP-11537 - add vid (custom UI Metadata) to Aeotec Nano Dimmer fingerprints (#50742) --- .../zwave-metering-dimmer.src/zwave-metering-dimmer.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index 8a6853ee5fb..ab85c1248ed 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -35,8 +35,8 @@ metadata { fingerprint mfr:"0086", prod:"0003", model:"001B", deviceJoinName: "Aeotec Dimmer Switch" //Aeotec Micro Smart Dimmer 2E fingerprint mfr:"0086", prod:"0103", model:"0063", deviceJoinName: "Aeotec Dimmer Switch" //US //Aeotec Smart Dimmer 6 fingerprint mfr:"0086", prod:"0003", model:"0063", deviceJoinName: "Aeotec Dimmer Switch" //EU //Aeotec Smart Dimmer 6 - fingerprint mfr:"0086", prod:"0103", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //Aeotec Nano Dimmer - fingerprint mfr:"0086", prod:"0003", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //Aeotec Nano Dimmer + fingerprint mfr:"0086", prod:"0103", model:"006F", deviceJoinName: "Aeotec Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Aeotec_Nano_Dimmer" //Aeotec Nano Dimmer + fingerprint mfr:"0086", prod:"0003", model:"006F", deviceJoinName: "Aeotec Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Aeotec_Nano_Dimmer" //Aeotec Nano Dimmer fingerprint mfr:"0086", prod:"0203", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //AU //Aeotec Nano Dimmer fingerprint mfr:"014F", prod:"5044", model:"3533", deviceJoinName: "GoControl Dimmer Switch" //GoControl Plug-in Dimmer } From 18317cd5cd8fb2b7255f96d132e26e11bd92bc26 Mon Sep 17 00:00:00 2001 From: Edwin Tan Date: Tue, 1 Dec 2020 04:49:59 +0800 Subject: [PATCH 131/422] DevWs for WYFY (Singapore) Private Limited containing containing WYFY TOUCH ZWAVE SWITCH (#47252) * DevWs for WYFY (Singapore) Private Limited containing containing WYFY TOUCH ZWAVE SWITCH * Added variants of same product * DevWs for WYFY (Singapore) Private Limited containing containing WYFY TOUCH ZWAVE SWITCH WYFY Touch (Z-Wave) Switch is a wall switch that controls different types of loads such as lights, ceiling fan and water storage heater. It comes in variants of 1-key, 2-key and 4-key. Each key can handle a load of up to 5 amperes. * Modifying 'DevWs for WYFY (Singapore) Private Limited containing containing WYFY TOUCH ZWAVE SWITCH' * Revised a comment in the code * Updated the label of the switch from "WYFY Touch S1" to "WYFY Touch 1" * Merged into existing DTH as suggested by PKacprowiczS * Updated based on comments from @KKlimczukS * Delete wyfy-touch-zwave-switch-child.groovy Deleted in pull request as requested by @KKlimczukS * Delete wyfy-touch-zwave-switch.groovy Deleted as requested by @KKlimczukS * Updated based on @PKacprowiczS comments * Updated based on PKacprowiczS comments * Modifying 'DevWs for WYFY (Singapore) Private Limited containing containing WYFY TOUCH ZWAVE SWITCH' * Updated based on new comments from reviewer * Updated * Delete child-switch.groovy * Create child-switch.groovy * Revert "Delete wyfy-touch-zwave-switch-child.groovy" This reverts commit 979e8e610d1c534154635fd94e05980c40cd26ea. * Update child-switch.groovy * Delete wyfy-touch-zwave-switch-child.groovy * Updated * Updated Co-authored-by: WYFY Singapore --- .../zwave-multi-metering-switch.groovy | 81 ++++++++++++++----- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy b/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy index 36a964cacf3..7c591ab529c 100644 --- a/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy @@ -30,6 +30,18 @@ metadata { fingerprint mfr: "0000", cc: "0x5E,0x25,0x27,0x32,0x81,0x71,0x60,0x8E,0x2C,0x2B,0x70,0x86,0x72,0x73,0x85,0x59,0x98,0x7A,0x5A", ccOut:"0x82", ui:"0x8700", deviceJoinName: "Aeotec Switch 1" //Aeotec Nano Switch 1 fingerprint mfr: "027A", prod: "A000", model: "A004", deviceJoinName: "Zooz Switch" //Zooz ZEN Power Strip fingerprint mfr: "027A", prod: "A000", model: "A003", deviceJoinName: "Zooz Switch" //Zooz Double Plug + // Raw Description zw:L type:1001 mfr:015F prod:3102 model:0201 ver:5.10 zwv:4.62 lib:03 cc:5E,85,59,8E,60,55,86,72,5A,73,25,27,70,2C,2B,5B,20,7A ccOut:5B,20,26 epc:1 + fingerprint mfr: "015F", prod: "3102", model: "0201", deviceJoinName: "WYFY Switch 1", mnmn: "SmartThings", vid: "generic-switch" //WYFY Touch 1-button Switch + // Raw Description zw:L type:1001 mfr:015F prod:3102 model:0202 ver:5.10 zwv:4.62 lib:03 cc:5E,85,59,8E,60,55,86,72,5A,73,25,27,70,2C,2B,5B,20,7A ccOut:5B,20,26 epc:2 + fingerprint mfr: "015F", prod: "3102", model: "0202", deviceJoinName: "WYFY Switch 1", mnmn: "SmartThings", vid: "generic-switch" //WYFY Touch 2-button Switch + // Raw Description zw:L type:1001 mfr:015F prod:3102 model:0204 ver:5.10 zwv:4.62 lib:03 cc:5E,85,59,8E,60,55,86,72,5A,73,25,27,70,2C,2B,5B,20,7A ccOut:5B,20,26 epc:4 + fingerprint mfr: "015F", prod: "3102", model: "0204", deviceJoinName: "WYFY Switch 1", mnmn: "SmartThings", vid: "generic-switch" //WYFY Touch 4-button Switch + // Raw Description zw:L type:1001 mfr:015F prod:3111 model:5102 ver:5.10 zwv:4.62 lib:03 cc:5E,85,59,8E,60,55,86,72,5A,73,25,27,70,2C,2B,5B,20,7A ccOut:5B,20,26 epc:1 + fingerprint mfr: "015F", prod: "3111", model: "5102", deviceJoinName: "WYFY Switch 1", mnmn: "SmartThings", vid: "generic-switch" //WYFY Touch 1-button Switch + // Raw Description zw:L type:1001 mfr:015F prod:3121 model:5102 ver:5.10 zwv:4.62 lib:03 cc:5E,85,59,8E,60,55,86,72,5A,73,25,27,70,2C,2B,5B,20,7A ccOut:5B,20,26 epc:2 + fingerprint mfr: "015F", prod: "3121", model: "5102", deviceJoinName: "WYFY Switch 1", mnmn: "SmartThings", vid: "generic-switch" //WYFY Touch 2-button Switch + // Raw Description zw:L type:1001 mfr:015F prod:3141 model:5102 ver:5.10 zwv:4.62 lib:03 cc:5E,85,59,8E,60,55,86,72,5A,73,25,27,70,2C,2B,5B,20,7A ccOut:5B,20,26 epc:4 + fingerprint mfr: "015F", prod: "3141", model: "5102", deviceJoinName: "WYFY Switch 1", mnmn: "SmartThings", vid: "generic-switch" //WYFY Touch 4-button Switch } tiles(scale: 2){ @@ -137,6 +149,11 @@ private lateConfigure() { encap(zwave.configurationV1.configurationSet(parameterNumber: 4, size: 4, scaledConfigurationValue: 600)) // enabling kWh energy reports every 10 minutes ] break + case "WYFY Touch": + cmds = [ + encap(zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, scaledConfigurationValue: 1)) // Remebers state before power failure + ] + break default: cmds = [encap(zwave.configurationV1.configurationSet(parameterNumber: 255, size: 1, scaledConfigurationValue: 0))] break @@ -181,7 +198,6 @@ def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cm private handleSwitchReport(endpoint, cmd) { def value = cmd.value ? "on" : "off" - if (isZoozZenStripV2()) { // device also sends reports without any endpoint specified, therefore all endpoints must be queried // sometimes it also reports 0.0 Wattage only until it's queried for it, then it starts reporting real values @@ -195,7 +211,7 @@ private changeSwitch(endpoint, value) { if (endpoint == 1) { createEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") } else { - String childDni = "${device.deviceNetworkId}:$endpoint" + String childDni = "${device.deviceNetworkId}:${endpoint}" def child = childDevices.find { it.deviceNetworkId == childDni } child?.sendEvent(name: "switch", value: value, isStateChange: true, descriptionText: "Switch ${endpoint} is ${value}") } @@ -231,8 +247,10 @@ private createMeterEventMap(cmd) { eventMap } +// This method handles unexpected commands def zwaveEvent(physicalgraph.zwave.Command cmd, ep) { - log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") + // Handles all Z-Wave commands we aren't interested in + log.warn "${device.displayName} - Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") } def on() { @@ -243,6 +261,9 @@ def off() { onOffCmd(0x00) } +// The Health Check capability uses the “checkInterval” attribute to determine the maximum number of seconds the device can go without generating new events. +// If the device hasn’t created any events within that amount of time, SmartThings executes the “ping()” command. +// If ping() does not generate any events, SmartThings marks the device as offline. def ping() { refresh() } @@ -252,20 +273,31 @@ def childOnOff(deviceNetworkId, value) { if (switchId != null) sendHubCommand onOffCmd(value, switchId) } -private onOffCmd(value, endpoint = 1) { - delayBetween([ - encap(zwave.basicV1.basicSet(value: value), endpoint), - encap(zwave.basicV1.basicGet(), endpoint), - "delay 3000", - encap(zwave.meterV3.meterGet(scale: 0), endpoint), - encap(zwave.meterV3.meterGet(scale: 2), endpoint) - ]) +def childOn(deviceNetworkId) { + childOnOff(deviceNetworkId, 0xFF) } -private refreshAll(includeMeterGet = true) { +def childOff(deviceNetworkId) { + childOnOff(deviceNetworkId, 0x00) +} - def endpoints = [1] +private onOffCmd(value, endpoint = 1) { + def cmds = [] + + cmds += encap(zwave.basicV1.basicSet(value: value), endpoint) + cmds += encap(zwave.basicV1.basicGet(), endpoint) + + if (deviceIncludesMeter()) { + cmds += "delay 3000" + cmds += encap(zwave.meterV3.meterGet(scale: 0), endpoint) + cmds += encap(zwave.meterV3.meterGet(scale: 2), endpoint) + } + + delayBetween(cmds) +} +private refreshAll(includeMeterGet = deviceIncludesMeter()) { + def endpoints = [1] childDevices.each { def switchId = getSwitchId(it.deviceNetworkId) if (switchId != null) { @@ -275,17 +307,15 @@ private refreshAll(includeMeterGet = true) { sendHubCommand refresh(endpoints,includeMeterGet) } -def childRefresh(deviceNetworkId, includeMeterGet = true) { +def childRefresh(deviceNetworkId, includeMeterGet = deviceIncludesMeter()) { def switchId = getSwitchId(deviceNetworkId) if (switchId != null) { sendHubCommand refresh([switchId],includeMeterGet) } } -def refresh(endpoints = [1], includeMeterGet = true) { - +def refresh(endpoints = [1], includeMeterGet = deviceIncludesMeter()) { def cmds = [] - endpoints.each { cmds << [encap(zwave.basicV1.basicGet(), it)] if (includeMeterGet) { @@ -293,7 +323,6 @@ def refresh(endpoints = [1], includeMeterGet = true) { cmds << encap(zwave.meterV3.meterGet(scale: 2), it) } } - delayBetween(cmds, 200) } @@ -339,11 +368,13 @@ private encap(cmd, endpoint = null) { } private addChildSwitches(numberOfSwitches) { + log.debug "${device.displayName} - Executing addChildSwitches()" for (def endpoint : 2..numberOfSwitches) { try { String childDni = "${device.deviceNetworkId}:$endpoint" def componentLabel = device.displayName[0..-2] + "${endpoint}" - addChildDevice("Child Metering Switch", childDni, device.getHub().getId(), [ + def childDthName = deviceIncludesMeter() ? "Child Metering Switch" : "Child Switch" + addChildDevice(childDthName, childDni, device.getHub().getId(), [ completedSetup : true, label : componentLabel, isComponent : false @@ -357,19 +388,31 @@ private addChildSwitches(numberOfSwitches) { def isAeotec() { getDeviceModel() == "Aeotec Nano Switch" } + def isZoozZenStripV2() { zwaveInfo.mfr.equals("027A") && zwaveInfo.model.equals("A004") } + def isZoozDoublePlug() { zwaveInfo.mfr.equals("027A") && zwaveInfo.model.equals("A003") } +def isWYFYTouch() { + getDeviceModel() == "WYFY Touch" +} + private getDeviceModel() { if ((zwaveInfo.mfr?.contains("0086") && zwaveInfo.model?.contains("0084")) || (getDataValue("mfr") == "86") && (getDataValue("model") == "84")) { "Aeotec Nano Switch" } else if(zwaveInfo.mfr?.contains("027A")) { "Zooz Switch" + } else if(zwaveInfo.mfr?.contains("015F")) { + "WYFY Touch" } else { "" } } + +private deviceIncludesMeter() { + return !isWYFYTouch() +} \ No newline at end of file From a5b78ceb1bfdab8840719be4e8e0ad1af13e9e23 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Tue, 1 Dec 2020 09:26:34 +0100 Subject: [PATCH 132/422] DevWs for frient containing containing SmartSense Motion Sensor (#51459) * DevWs for frient containing containing SmartSense Motion Sensor * Removing profileId and deviceId * Added raw description Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../smartsense-motion-sensor.src/smartsense-motion-sensor.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index 6d77e8b2f07..dcb414b2765 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -44,6 +44,7 @@ metadata { fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "ADUROLIGHT", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.0 fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "AduroSmart Eria", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.1 fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"MOSZB-140", deviceJoinName: "frient Motion Sensor" + fingerprint manufacturer: "frient A/S", model :"MOSZB-141", deviceJoinName: "frient Motion Sensor", mnmn: "SmartThingsCommunity", vid: "87753fce-8cd6-3b91-8bde-2483e564252d" // Raw description: 22 0104 0107 00 03 0000 0003 0406 00 } simulator { From 2de3ff48079551c3ec043a6246af495d9711c41e Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Tue, 1 Dec 2020 09:28:44 +0100 Subject: [PATCH 133/422] DevWs for frient containing containing SmartSense Open/Closed Sensor (#51460) * DevWs for frient containing containing SmartSense Open/Closed Sensor * Updating fingerprint * Added raw description Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../smartsense-open-closed-sensor.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index 4743f7911b1..1428c938e98 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -44,6 +44,7 @@ metadata { //Dawon fingerprint inClusters: "0000, 0003, 0006, 0500", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "SS-B100-ZB", deviceJoinName: "Dawon Signal Interlock", mnmn: "0AIg", vid: "dawon-zigbee-signal-interlock2" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"WISZB-120", deviceJoinName: "frient Open/Closed Sensor" + fingerprint manufacturer: "frient A/S", model :"WISZB-121", deviceJoinName: "frient Open/Closed Sensor", mnmn: "SmartThingsCommunity", vid: "aaca16c3-fade-3cb3-b742-e2237f4ffd76" // Raw description: 23 0104 0402 00 06 0000 0001 0003 000F 0020 0500 02 000A 0019 } simulator { From 781e6c1f182b5ec3e54ed0d6dc5cc6fbfa9839ec Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Tue, 1 Dec 2020 10:17:17 +0100 Subject: [PATCH 134/422] DevWs for frient containing containing Zigbee Metering Plug (#51389) * DevWs for frient containing containing Zigbee Metering Plug * fixing indentation * Fix large diff - encoding Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 1 + 1 file changed, 1 insertion(+) 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 d7ae96afb45..b09cb78ee70 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -40,6 +40,7 @@ metadata { fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-131", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-132", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-134", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 + fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-137", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SMRZB-143", deviceJoinName: "frient Outlet" // frient smart cable, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 } From 37e75c4bd31d768d6d9f80a75fe2356c656584bf Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Wed, 2 Dec 2020 21:35:37 +0100 Subject: [PATCH 135/422] ICP-11537 - add vid (custom UI Metadata) to Aeotec Nano Dimmer fingerprint (AU) (#51550) --- .../zwave-metering-dimmer.src/zwave-metering-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index ab85c1248ed..74a99e03a63 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -37,7 +37,7 @@ metadata { fingerprint mfr:"0086", prod:"0003", model:"0063", deviceJoinName: "Aeotec Dimmer Switch" //EU //Aeotec Smart Dimmer 6 fingerprint mfr:"0086", prod:"0103", model:"006F", deviceJoinName: "Aeotec Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Aeotec_Nano_Dimmer" //Aeotec Nano Dimmer fingerprint mfr:"0086", prod:"0003", model:"006F", deviceJoinName: "Aeotec Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Aeotec_Nano_Dimmer" //Aeotec Nano Dimmer - fingerprint mfr:"0086", prod:"0203", model:"006F", deviceJoinName: "Aeotec Dimmer Switch" //AU //Aeotec Nano Dimmer + fingerprint mfr:"0086", prod:"0203", model:"006F", deviceJoinName: "Aeotec Dimmer Switch", mnmn: "SmartThings", vid: "SmartThings-smartthings-Aeotec_Nano_Dimmer" //Aeotec Nano Dimmer, AU fingerprint mfr:"014F", prod:"5044", model:"3533", deviceJoinName: "GoControl Dimmer Switch" //GoControl Plug-in Dimmer } From 45f14c9001638bef0a4d9e8eb40c5c205713bc13 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Wed, 2 Dec 2020 22:49:44 +0100 Subject: [PATCH 136/422] WWST-7231 New fingerprint for Enbrighten, Plug-in Outdoor Smart Switch, 43100 (#51625) * ICP-11537 - add vid (custom UI Metadata) to Aeotec Nano Dimmer fingerprints * WWST-7231 - fingerprint for Enbrighten, Plug-in Outdoor Smart Switch, 43100 --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index e53187e617c..e73842429b9 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -98,6 +98,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0006, 0019, 0002, 0009", manufacturer: "DAWON_DNS", model: "PM-S140R-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S140R-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006", manufacturer: "DAWON_DNS", model: "PM-S150-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S150-ZB fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006", manufacturer: "DAWON_DNS", model: "ST-S150-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch ST-S150-ZB + + // Enbrighten/Jasco + fingerprint manufacturer: "Jasco Products", model: "43100", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Outdoor Smart Switch, 43100, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 } // simulator metadata From 751944950161fdc0e873de90f77040756a6bc32b Mon Sep 17 00:00:00 2001 From: Carter Swedal Date: Wed, 2 Dec 2020 16:00:18 -0600 Subject: [PATCH 137/422] BUG-1315: Remove checkInterval event from aeon key fob (#51554) https://smartthings.atlassian.net/browse/BUG-1315 --- devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 a7097e1c635..d68bad3f9bc 100644 --- a/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy +++ b/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy @@ -197,15 +197,14 @@ def initialize() { def buttons = 1 if (zwaveInfo && zwaveInfo.mfr == "0086" && zwaveInfo.prod == "0001" && zwaveInfo.model == "0026") { - sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) buttons = 1 // Only one button for Aeon Panic Button results << response(zwave.batteryV1.batteryGet().format()) } else { - // Device only goes OFFLINE when Hub is off - sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false) buttons = 4 // Default for Key Fob } + // These devices only go OFFLINE when Hub is off + sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false) sendEvent(name: "numberOfButtons", value: buttons, displayed: false) sendEvent(name: "supportedButtonValues", value: JsonOutput.toJson(["pushed", "held"]), displayed: false) From 9ac6d95d4b303dfe84bd24b03acd63850db0fb43 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Thu, 3 Dec 2020 20:25:29 +0100 Subject: [PATCH 138/422] [WWST-7209, WWST-7219, WWST-7235] Fingerprints for Enbrighten In-Wall Smart Switch Toggle 43084 and Plug-in Smart Switch 43094 and In-Wall Smart Outlet, 43102 (#51705) * WWST-7209 Fingerprint for Enbrighten In-Wall Smart Switch Toggle 43084 * WWST-7173 Fingerprint for Enbrighten Plug-in Smart Switch 43094 * WWST-7235 Fingerprint for Enbrighten In-Wall Smart Outlet 43102 --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index e73842429b9..06f7a1309fb 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -101,6 +101,9 @@ metadata { // Enbrighten/Jasco fingerprint manufacturer: "Jasco Products", model: "43100", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Outdoor Smart Switch, 43100, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43084", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch, Toggle, 43084, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43094", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Smart Switch, 43094, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Outlet, 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 } // simulator metadata From 2d1b296b22b27c7c95cc97c97051e5dbfe3bc6ae Mon Sep 17 00:00:00 2001 From: greens Date: Thu, 3 Dec 2020 14:26:03 -0800 Subject: [PATCH 139/422] Revert "CHAD-5290 Move Schlage BR469ZP fingerprint (#45988)" This reverts commit 36d37c1b431eb81c2722202c66dd07d38f3bd3dd. --- .../zwave-lock-without-codes.src/zwave-lock-without-codes.groovy | 1 - devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) 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 index 8e338181460..964f9376f74 100644 --- 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 @@ -30,7 +30,6 @@ metadata { fingerprint mfr: "021D", prod: "0003", model: "0001", deviceJoinName: "Alfred Door Lock" // DB2 //Alfred Smart Home Touchscreen Deadbolt //zw:Fs type:4001 mfr:0154 prod:0005 model:0001 ver:1.05 zwv:4.38 lib:03 cc:7A,73,80,5A,98 sec:5E,86,72,30,71,70,59,85,62 fingerprint mfr: "0154", prod: "0005", model: "0001", deviceJoinName: "POPP Door Lock" // POPP Strike Lock Control POPE012501 - fingerprint mfr: "003B", prod: "0001", model: "0469", deviceJoinName: "Schlage Door Lock" //BE469ZP //Schlage Connect Smart Deadbolt Door Lock } simulator { diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index 33faffb9fc1..3a17e4763cb 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -47,6 +47,7 @@ metadata { fingerprint mfr:"003B", prod:"6341", model:"5044", deviceJoinName: "Schlage Door Lock" //Schlage Touchscreen Deadbolt Door Lock fingerprint mfr:"003B", prod:"634B", model:"504C", deviceJoinName: "Schlage Door Lock" //Schlage Connected Keypad Lever Door Lock fingerprint mfr:"003B", prod:"0001", model:"0468", deviceJoinName: "Schlage Door Lock" //BE468ZP //Schlage Connect Smart Deadbolt Door Lock + fingerprint mfr:"003B", prod:"0001", model:"0469", deviceJoinName: "Schlage Door Lock" //BE469ZP //Schlage Connect Smart Deadbolt Door Lock fingerprint mfr:"003B", prod:"0004", model:"2109", deviceJoinName: "Schlage Door Lock" //Schlage Keypad Deadbolt JBE109 fingerprint mfr:"003B", prod:"0004", model:"6109", deviceJoinName: "Schlage Door Lock" //Schlage Keypad Lever JFE109 // Yale From dc369a4f3fc461594b28d322d3f9aeac996b6442 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 7 Dec 2020 20:02:10 +0100 Subject: [PATCH 140/422] Removed extra Thermostat Mode cap entry (#51976) --- .../zwave-radiator-thermostat.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index 788f16cd5f2..deedf7874de 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -14,7 +14,6 @@ */ 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" From 34b45e662f23b0030ee5560d6219afe771d85199 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 7 Dec 2020 12:07:12 -0800 Subject: [PATCH 141/422] ICP-5864 Add health check to original SmartSense Multi (#51473) * ICP-5864 Add health check to original SmartSense Multi * change interval to 12 minutes --- .../smartthings/smartsense-multi.src/smartsense-multi.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy index efd0494c6c1..f31d6c67b0c 100644 --- a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy +++ b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy @@ -20,6 +20,7 @@ metadata { capability "Temperature Measurement" capability "Sensor" capability "Battery" + capability "Health Check" fingerprint profileId: "FC01", deviceId: "0139", deviceJoinName: "Multipurpose Sensor" } @@ -80,6 +81,10 @@ metadata { } } +def updated() { + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) +} + def parse(String description) { def results From 601718f1ae313d80c7aab22969bb1b94aafd8a50 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 8 Dec 2020 20:00:58 +0100 Subject: [PATCH 142/422] ICP-13878 - skips rounding of Danfoss Ally Thermostat temperature values (#52021) * ICP-13878 - skips rounding of Danfoss Ally Thermostat temperature values. * ICP-13878 - rounds temperature values to one decimal place --- .../smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy b/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy index 21fb393b2da..ab3179b8ddf 100644 --- a/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy +++ b/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy @@ -372,7 +372,7 @@ def getTemperature(value) { if (value != null) { def celsius = Integer.parseInt(value, 16) / 100 if (temperatureScale == "C") { - return Math.round(celsius) + return celsius.toDouble().round(1) } else { return Math.round(celsiusToFahrenheit(celsius)) } From 0e06fdc1ac9385ed64870859b78ecbb5e716f2cb Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Tue, 8 Dec 2020 20:01:28 +0100 Subject: [PATCH 143/422] [WWST-7227, WWST-7213, WWST-7197] Fingerprints for Enbrighten dimmers (#52044) --- .../smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index 2cd9d152fb2..cc4d8d4aed5 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -126,6 +126,11 @@ metadata { // Wemo fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Light" //Wemo Bulb + + // Enbrighten/Jasco + fingerprint manufacturer: "Jasco Products", model: "43096", deviceJoinName: "Enbrighten Dimmer", ocfDeviceType: "oic.d.switch" //Enbrighten, Plug-in Smart Dimmer, 43096, Raw Description: 01 0104 0101 00 07 0000 0003 0004 0005 0006 0008 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43090", deviceJoinName: "Enbrighten Dimmer", ocfDeviceType: "oic.d.switch" //Enbrighten, In-Wall Smart Dimmer, Toggle. 43090, Raw Description: 01 0104 0101 00 07 0000 0003 0004 0005 0006 0008 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43080", deviceJoinName: "Enbrighten Dimmer", ocfDeviceType: "oic.d.switch" //Enbrighten, In-Wall Smart Dimmer, 43080, Raw Description: 01 0104 0101 00 07 0000 0003 0004 0005 0006 0008 0B05 02 000A 0019 } tiles(scale: 2) { From 98ed3df65a7d72b852ec03c83b7899d872f8f91f Mon Sep 17 00:00:00 2001 From: Carter Swedal Date: Wed, 2 Dec 2020 16:00:18 -0600 Subject: [PATCH 144/422] BUG-1315: Remove checkInterval event from aeon key fob (#51554) https://smartthings.atlassian.net/browse/BUG-1315 --- devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 a7097e1c635..d68bad3f9bc 100644 --- a/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy +++ b/devicetypes/smartthings/aeon-key-fob.src/aeon-key-fob.groovy @@ -197,15 +197,14 @@ def initialize() { def buttons = 1 if (zwaveInfo && zwaveInfo.mfr == "0086" && zwaveInfo.prod == "0001" && zwaveInfo.model == "0026") { - sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) buttons = 1 // Only one button for Aeon Panic Button results << response(zwave.batteryV1.batteryGet().format()) } else { - // Device only goes OFFLINE when Hub is off - sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false) buttons = 4 // Default for Key Fob } + // These devices only go OFFLINE when Hub is off + sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zwave", scheme:"untracked"]), displayed: false) sendEvent(name: "numberOfButtons", value: buttons, displayed: false) sendEvent(name: "supportedButtonValues", value: JsonOutput.toJson(["pushed", "held"]), displayed: false) From ee81f0d6a9b48e69f3c2957e3950c22a8a696d6a Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Tue, 8 Dec 2020 14:17:30 -0800 Subject: [PATCH 145/422] Disable threshold settings Found error with parameter setting used, Change Parameter 90 to Parameter 3 which is used for threshold settings on HEM Gen5. --- .../aeon-home-energy-meter.src/aeon-home-energy-meter.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy b/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy index 9cd3d420225..568740d34ff 100644 --- a/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy +++ b/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy @@ -163,7 +163,7 @@ def configure() { encap(zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 0)), // disable group 2... encap(zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0)), // disable group 3... encap(zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300)), // ...every 5 min - encap(zwave.configurationV1.configurationSet(parameterNumber: 90, size: 1, scaledConfigurationValue: 0)), // enabling automatic reports, disabled selective reporting... + encap(zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: 0)), // enabling automatic reports, disabled selective reporting... encap(zwave.configurationV1.configurationSet(parameterNumber: 13, size: 1, scaledConfigurationValue: 0)) //disable CRC16 encapsulation ], 500) else if (isQubinoSmartMeter()) From 9ee444aedba11817ebfb017d047aaaea770fd589 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Thu, 10 Dec 2020 21:09:18 +0100 Subject: [PATCH 146/422] ICP-13858 - fix for setting the thermostat mode to OFF (#52199) * ICP-13858 - fix for setting the thermostat mode to OFF * ICP-13858 - fixes setting the thermostat fan mode to ON * ICP-13858 - removed null checks (since the methods are called with proper parameters) --- .../viconics-schneider-room-controller.groovy | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy b/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy index 479e2ea4985..14da28dc0c8 100644 --- a/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy +++ b/devicetypes/smartthings/viconics-schneider-room-controller.src/viconics-schneider-room-controller.groovy @@ -334,12 +334,10 @@ def fanCirculate() { } def getThermostatFanModeCommands(mode) { - if (mode) { - delayBetween([ - zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE, DataType.ENUM8, mode), - zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE) - ], 500) - } + delayBetween([ + zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE, DataType.ENUM8, mode), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_FAN_MODE) + ], 500) } def setFanSpeed(speed) { @@ -397,13 +395,11 @@ def off() { } def getThermostatModeCommands(mode) { - if (mode) { - delayBetween([ - zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE, DataType.ENUM8, mode), - zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE), - zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_OPERATING_STATE) - ], 500) - } + delayBetween([ + zigbee.writeAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE, DataType.ENUM8, mode), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_MODE), + zigbee.readAttribute(THERMOSTAT_CLUSTER, CUSTOM_THERMOSTAT_OPERATING_STATE) + ], 500) } def ping() { From 2d84ba6759135b12bfb84a0b4d79509108cbadb8 Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Fri, 11 Dec 2020 13:27:14 -0800 Subject: [PATCH 147/422] Fix Smart Switch 7 US fingerprint (#49685) Model identifier is changed from "00AF" to "0017", the new model number for Series 700 Smart Switch 7 is ZWA023-A. Certified product ID can be found here: https://products.z-wavealliance.org/products/3844?selectedFrequencyId=-1 --- .../zwave-metering-switch.src/zwave-metering-switch.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d0ba1e97b0b..d9228c0e275 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -47,7 +47,7 @@ metadata { fingerprint mfr: "027A", prod: "0101", model: "000D", deviceJoinName: "Zooz Switch" //Zooz Power Switch fingerprint mfr: "0159", prod: "0002", model: "0054", deviceJoinName: "Qubino Outlet", ocfDeviceType: "oic.d.smartplug" //Qubino Smart Plug fingerprint mfr: "0371", prod: "0003", model: "00AF", deviceJoinName: "Aeotec Outlet", ocfDeviceType: "oic.d.smartplug" //EU //Aeotec Smart Switch 7 - fingerprint mfr: "0371", prod: "0103", model: "00AF", deviceJoinName: "Aeotec Outlet", ocfDeviceType: "oic.d.smartplug" //US //Aeotec Smart Switch 7 + fingerprint mfr: "0371", prod: "0103", model: "0017", deviceJoinName: "Aeotec Outlet", ocfDeviceType: "oic.d.smartplug" //US //Aeotec Smart Switch 7 fingerprint mfr: "0060", prod: "0004", model: "000B", deviceJoinName: "Everspring Outlet", ocfDeviceType: "oic.d.smartplug" //US //Everspring Smart Plug fingerprint mfr: "031E", prod: "0002", model: "0001", deviceJoinName: "Inovelli Switch" //US //Inovelli Switch Red Series fingerprint mfr: "0154", prod: "0003", model: "000A", deviceJoinName: "POPP Outlet", ocfDeviceType: "oic.d.smartplug" //EU //POPP Smart Outdoor Plug From 91a25add1e72c2d1d4aaf5d296a74780a4bba91f Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 15 Dec 2020 19:30:25 +0100 Subject: [PATCH 148/422] WWST-7192, WWST-7201, WWST-7205, WWST-7223 - new fingerprints for Enbrighten devices (#52364) * WWST-7223 - fingerprint for Enbrighten Plug-in Smart Switch With Energy Monitoring 43095 * WWST-7201 - fingerprint for Enbrighten In-Wall Smart Switch With Energy Monitoring 43078 * WWST-7192 - fingerprint for Enbrighten In-Wall Smart Switch 43076 * WWST-7205 - fingerprint for Enbrighten in-Wall Smart Dimmer With Energy Monitoring, 43082 * WWST-7205 - fixes spacing --- .../zigbee-dimmer-power.src/zigbee-dimmer-power.groovy | 3 +++ .../zigbee-switch-power.src/zigbee-switch-power.groovy | 4 ++++ .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 7 ++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy b/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy index 6c7bc321119..0502ab3dfd6 100644 --- a/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy +++ b/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy @@ -34,6 +34,9 @@ metadata { // Sengled fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-CIA19NAE26", deviceJoinName: "Sengled Light" //Sengled Element touch + + // Enbrighten/Jasco + fingerprint manufacturer: "Jasco Products", model: "43082", deviceJoinName: "Enbrighten Switch" //Enbrighten, in-Wall Smart Dimmer With Energy Monitoring 43082, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0702 0B05 02 000A 0019 } tiles(scale: 2) { diff --git a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy index 15563dc1217..0f60222d5ed 100644 --- a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy +++ b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy @@ -54,6 +54,10 @@ metadata { fingerprint profileId: "0104", deviceId: "0051", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04, 1000, 0702", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "AD-SmartPlug3001", deviceJoinName: "Eria Switch" //Eria Zigbee Smart Plug fingerprint profileId: "0104", deviceId: "010A", inClusters: "0000, 0003, 0004, 0005, 0006, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BPU3", deviceJoinName: "Eria Switch" //Eria Zigbee On/Off Plug fingerprint profileId: "0104", deviceId: "0101", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BDP3001", deviceJoinName: "Eria Switch" //Eria Zigbee Dimmable Plug + + // Enbrighten + fingerprint manufacturer: "Jasco Products", model: "43078", deviceJoinName: "Enbrighten Switch" //Enbrighten In-Wall Smart Switch With Energy Monitoring 43078, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43095", deviceJoinName: "Enbrighten Switch" //Enbrighten Plug-in Smart Switch With Energy Monitoring 43095, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 } tiles(scale: 2) { diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 06f7a1309fb..e52b94344e2 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -101,9 +101,10 @@ metadata { // Enbrighten/Jasco fingerprint manufacturer: "Jasco Products", model: "43100", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Outdoor Smart Switch, 43100, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 - fingerprint manufacturer: "Jasco Products", model: "43084", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch, Toggle, 43084, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 - fingerprint manufacturer: "Jasco Products", model: "43094", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Smart Switch, 43094, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 - fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Outlet, 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43084", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch Toggle, 43084, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43094", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Smart Switch 43094, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Outlet 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43076", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch 43076, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 } // simulator metadata From 37adf2f2cbe25d4bc6f3b86076185b79b9accc2b Mon Sep 17 00:00:00 2001 From: dwd-kwon <59678391+dwd-kwon@users.noreply.github.com> Date: Wed, 16 Dec 2020 03:48:40 +0900 Subject: [PATCH 149/422] Adding the device handler for Zigbee Metering Plug Power Consumption Report (#52774) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding the device handler for Zigbee Metering Plug Power Consumption Report * Update Tiles removal and Change 4 spaces to 1 tab. * Update * Update zigbee-metering-plug-power-consumption-report.groovy * Update zigbee-metering-plug-power-consumption-report.groovy * Adding the device handler for Zigbee Metering Plug Power Consumption Report * Update zigbee-metering-plug-power-consumption-report.groovy * Update zigbee-metering-plug.groovy Co-authored-by: 남수 허 --- ...ering-plug-power-consumption-report.groovy | 152 ++++++++++++++++++ .../zigbee-metering-plug.groovy | 19 +-- 2 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy diff --git a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy new file mode 100644 index 00000000000..f99892db596 --- /dev/null +++ b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy @@ -0,0 +1,152 @@ +/** + * 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. + * + */ +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition (name: "Zigbee Metering Plug Power Consumption Report", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.smartplug", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + capability "Energy Meter" + capability "Power Meter" + capability "Actuator" + capability "Switch" + capability "Refresh" + capability "Health Check" + capability "Sensor" + capability "Configuration" + capability "Power Consumption Report" + + fingerprint manufacturer: "DAWON_DNS", model: "PM-B430-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint manufacturer: "DAWON_DNS", model: "PM-B530-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint manufacturer: "DAWON_DNS", model: "PM-C140-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet + fingerprint manufacturer: "DAWON_DNS", model: "PM-B540-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint manufacturer: "DAWON_DNS", model: "ST-B550-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + fingerprint manufacturer: "DAWON_DNS", model: "PM-C150-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet + fingerprint manufacturer: "DAWON_DNS", model: "PM-C250-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet + fingerprint manufacturer: "DAWON_DNS", model: "PM-B440-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug + } +} + +def getATTRIBUTE_READING_INFO_SET() { 0x0000 } +def getATTRIBUTE_HISTORICAL_CONSUMPTION() { 0x0400 } + +def parse(String description) { + log.debug "description is $description" + def event = zigbee.getEvent(description) + def descMap = zigbee.parseDescriptionAsMap(description) + + if (event) { + log.info "event enter:$event" + if (event.name == "switch" && !descMap.isClusterSpecific && descMap.commandInt == 0x0B) { + log.info "Ignoring default response with desc map: $descMap" + return [:] + } else if (event.name== "power") { + event.value = event.value/getPowerDiv() + event.unit = "W" + } else if (event.name== "energy") { + event.value = event.value/getEnergyDiv() + event.unit = "kWh" + } + log.info "event outer:$event" + sendEvent(event) + } else { + List result = [] + log.debug "Desc Map: $descMap" + + List attrData = [[clusterInt: descMap.clusterInt ,attrInt: descMap.attrInt, value: descMap.value]] + descMap.additionalAttrs.each { + attrData << [clusterInt: descMap.clusterInt, attrInt: it.attrInt, value: it.value] + } + + attrData.each { + def map = [:] + 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)/getPowerDiv() + map.unit = "W" + } + 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)/getEnergyDiv() + map.unit = "kWh" + + def currentEnergy = map.value + def currentPowerConsumption = device.currentState("powerConsumption")?.value + Map previousMap = currentPowerConsumption ? new groovy.json.JsonSlurper().parseText(currentPowerConsumption) : [:] + def deltaEnergy = calculateDelta (currentEnergy, previousMap) + Map reportMap = [:] + reportMap["energy"] = currentEnergy + reportMap["deltaEnergy"] = deltaEnergy + sendEvent("name": "powerConsumption", "value": reportMap.encodeAsJSON(), displayed: false) + } + + if (map) { + result << createEvent(map) + } + log.debug "Parse returned $map" + } + return result + } +} + +def off() { + def cmds = zigbee.off() + return cmds +} + +def on() { + def cmds = zigbee.on() + return cmds +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return refresh() +} + +def refresh() { + log.debug "refresh" + zigbee.onOffRefresh() + + zigbee.electricMeasurementPowerRefresh() + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET) +} + +def configure() { + // this device will send instantaneous demand and current summation delivered every 1 minute + sendEvent(name: "checkInterval", value: 2 * 60 + 10 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + log.debug "Configuring Reporting" + return refresh() + + zigbee.onOffConfig() + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 1, 600, 1) + + zigbee.electricMeasurementPowerConfig(1, 600, 1) + + zigbee.simpleMeteringPowerConfig() +} + +private int getPowerDiv() { + 1 +} + +private int getEnergyDiv() { + 1000 +} + +BigDecimal calculateDelta (BigDecimal currentEnergy, Map previousMap) { + if (previousMap?.'energy' == null) { + return 0; + } + BigDecimal lastAcumulated = BigDecimal.valueOf(previousMap ['energy']); + return currentEnergy.subtract(lastAcumulated).max(BigDecimal.ZERO); +} 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 b09cb78ee70..c26d20ba3d0 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -30,13 +30,6 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B04", outClusters: "0003", manufacturer: "REXENSE", model: "HY0104", deviceJoinName: "HONYAR Outlet" //HONYAR Smart Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0009, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "E_Socket", deviceJoinName: "HEIMAN Outlet" //HEIMAN Smart Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04, 0702, FC82", outClusters: "0003, 000A, 0019", manufacturer: "sengled", model: "E1C-NB7", deviceJoinName: "Sengled Outlet" //Sengled Smart Plug with Energy Tracker - fingerprint manufacturer: "DAWON_DNS", model: "PM-B430-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug PM-B430-ZB (10A), raw description: 01 0104 0051 01 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 - fingerprint manufacturer: "DAWON_DNS", model: "PM-B530-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug PM-B530-ZB (16A), raw description: 01 0104 0051 01 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 07 0000, 0004, 0003, 0006, 0019, 0702, 0B04 - fingerprint manufacturer: "DAWON_DNS", model: "PM-C140-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet PM-C140-ZB, raw description: 01 0104 0051 01 0A 0000 0002 0003 0004 0006 0019 0702 0B04 0008 0009 0A 0000 0002 0003 0004 0006 0019 0702 0B04 0008 0009 - fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-B540-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug - fingerprint profileId: "0104", inClusters: "0000, 0002, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "ST-B550-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS Smart Plug - fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C150-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet - fingerprint profileId: "0104", inClusters: "0000, 0003, 0006, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "PM-C250-ZB", deviceJoinName: "Dawon Outlet" // DAWON DNS In-Wall Outlet fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-131", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-132", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-134", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 @@ -171,19 +164,9 @@ private int getPowerDiv() { } private int getEnergyDiv() { - if (isDawonOutlet()) { - 1000 - } else if (isSengledOutlet()) { - 10000 - } else { - 100 - } + isSengledOutlet() ? 10000 : 100 } private boolean isSengledOutlet() { device.getDataValue("model") == "E1C-NB7" } - -private boolean isDawonOutlet() { - device.getDataValue("manufacturer") == "DAWON_DNS" -} From 92ff78f2d0d08c7d583d16ed3ec308f31a8c973a Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Thu, 17 Dec 2020 09:05:42 +0100 Subject: [PATCH 150/422] DevWs for frient containing containing Zigbee Power Meter (#52604) * DevWs for frient containing containing Zigbee Power Meter * Update for comments * Add fingerprints * Revert tabs to spaces * Replace magic numbers with attribute names Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-power-meter.groovy | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy old mode 100755 new mode 100644 index 99b9b56c318..88be0594b85 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -21,7 +21,8 @@ metadata { capability "Configuration" fingerprint profileId: "0104", deviceId:"0053", inClusters: "0000, 0003, 0004, 0B04, 0702", outClusters: "0019", manufacturer: "", model: "E240-KR080Z0-HA", deviceJoinName: "Energy Monitor" //Smart Sub-meter(CT Type) - + fingerprint profileId: "0104", deviceId:"0007", inClusters: "0000,0003,0702", outClusters: "000A", manufacturer: "Develco", model: "ZHEMI101", deviceJoinName: "frient Energy Monitor" // frient External Meter Interface (develco) 02 0104 0007 00 03 0000 0003 0702 01 000A + fingerprint profileId: "0104", manufacturer: "Develco Products A/S", model: "EMIZB-132", deviceJoinName: "frient Energy Monitor" // frient Norwegian HAN (develco) 02 0104 0053 00 06 0000 0003 0020 0702 0704 0B04 03 0003 000A 0019 } // tile definitions @@ -55,14 +56,14 @@ def parse(String description) { def descMap = zigbee.parseDescriptionAsMap(description) log.debug "event : Desc Map: $descMap" if (descMap.clusterInt == 0x0B04 && descMap.attrInt == 0x050b) { - event.value = event.value/10 + event.value = event.value/activePowerDivisor event.unit = "W" } else { - event.value = event.value/1000 + event.value = event.value/powerDivisor event.unit = "W" } } else if (event.name == "energy") { - event.value = event.value/1000000 + event.value = event.value/(energyDivisor * 1000) event.unit = "kWh" } log.info "event outer:$event" @@ -72,29 +73,31 @@ def parse(String description) { def descMap = zigbee.parseDescriptionAsMap(description) log.debug "Desc Map: $descMap" - List attrData = [[clusterInt: descMap.clusterInt ,attrInt: descMap.attrInt, value: descMap.value]] + List attrData = [[clusterInt: descMap.clusterInt ,attrInt: descMap.attrInt, value: descMap.value, isValidForDataType: descMap.isValidForDataType]] descMap.additionalAttrs.each { - attrData << [clusterInt: descMap.clusterInt, attrInt: it.attrInt, value: it.value] + attrData << [clusterInt: descMap.clusterInt, attrInt: it.attrInt, value: it.value, isValidForDataType: it.isValidForDataType] } attrData.each { def map = [:] - if (it.clusterInt == 0x0702 && it.attrInt == 0x0400) { + if (it.isValidForDataType && (it.value != null)) { + if (it.clusterInt == 0x0702 && it.attrInt == 0x0400) { log.debug "meter" map.name = "power" - map.value = zigbee.convertHexToInt(it.value)/1000 + map.value = zigbee.convertHexToInt(it.value)/powerDivisor map.unit = "W" - } - if (it.clusterInt == 0x0B04 && it.attrInt == 0x050b) { + } + if (it.clusterInt == 0x0B04 && it.attrInt == 0x050b) { log.debug "meter" map.name = "power" - map.value = zigbee.convertHexToInt(it.value)/10 + map.value = zigbee.convertHexToInt(it.value)/activePowerDivisor map.unit = "W" - } - if (it.clusterInt == 0x0702 && it.attrInt == 0x0000) { - log.debug "energy" - map.name = "energy" - map.value = zigbee.convertHexToInt(it.value)/1000000 - map.unit = "kWh" + } + if (it.clusterInt == 0x0702 && it.attrInt == 0x0000) { + log.debug "energy" + map.name = "energy" + map.value = zigbee.convertHexToInt(it.value)/(energyDivisor * 1000) + map.unit = "kWh" + } } if (map) { @@ -129,3 +132,12 @@ def configure() { zigbee.simpleMeteringPowerConfig() + zigbee.electricMeasurementPowerConfig() } + +private getActivePowerDivisor() { 10 } +private getPowerDivisor() { isFrientSensor() ? 1 : 1000 } +private getEnergyDivisor() { isFrientSensor() ? 1 : 1000 } + +private Boolean isFrientSensor() { + device.getDataValue("manufacturer") == "Develco Products A/S" || + device.getDataValue("manufacturer") == "Develco" +} From 038af4db80d22a2075a9e3264d25d4ebb4ef7b14 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Thu, 17 Dec 2020 09:26:21 +0100 Subject: [PATCH 151/422] DevWs for frient containing containing Ozom Smart Siren (#52672) * DevWs for frient containing containing Ozom Smart Siren * Refactor code Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../ozom-smart-siren.groovy | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy index a7447d5790f..8125cf4ea25 100644 --- a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy +++ b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy @@ -27,6 +27,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0003,0500,0502", outClusters: "0000", manufacturer: "ClimaxTechnology", model: "SRAC_00.00.00.16TC", vid: "generic-siren-8", deviceJoinName: "Ozom Siren" // Ozom Siren - SRAC-23ZBS //Ozom Smart Siren fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0009,0500,0502", outClusters: "0003,0019", manufacturer: "Heiman", model: "WarningDevice", deviceJoinName: "HEIMAN Siren" //HEIMAN Smart Siren + fingerprint manufacturer: "frient A/S", model :"SIRZB-110", deviceJoinName: "frient Siren", mnmn: "SmartThingsCommunity", vid: "33d3bbac-144c-3a31-b022-0fc5c74240a3" // frient Smart Siren, 2B 0104 0403 00 05 0000 0003 0502 0500 0001 02 000A 0019 } tiles { @@ -57,6 +58,9 @@ private getMODE_BOTH() { "17" } private getMODE_OFF() { "00" } private getSTROBE_DUTY_CYCLE() { "40" } private getSTROBE_LEVEL() { "03" } +private getBASIC_DUTY_CYCLE() { "00" } +private getBASIC_LEVEL() { "00" } +private getFRIENT_MODE_SIREN() { "C1" } private getALARM_OFF() { 0x00 } private getALARM_SIREN() { 0x01 } @@ -167,16 +171,21 @@ def startCmd(cmd) { state.lastDuration = warningDuration def paramMode; - def paramDutyCycle = STROBE_DUTY_CYCLE; - def paramStrobeLevel = STROBE_LEVEL; + def paramDutyCycle; + def paramStrobeLevel; + if (cmd == ALARM_SIREN) { - paramMode = MODE_SIREN - paramDutyCycle = "00" - paramStrobeLevel = "00" + paramMode = isFrientSiren() ? FRIENT_MODE_SIREN : MODE_SIREN + paramDutyCycle = BASIC_DUTY_CYCLE + paramStrobeLevel = BASIC_LEVEL } else if (cmd == ALARM_STROBE) { - paramMode = MODE_STROBE + paramMode = isFrientSiren() ? FRIENT_MODE_SIREN : MODE_STROBE + paramDutyCycle = isFrientSiren() ? BASIC_DUTY_CYCLE : STROBE_DUTY_CYCLE + paramStrobeLevel = isFrientSiren() ? BASIC_LEVEL : STROBE_LEVEL } else if (cmd == ALARM_BOTH) { - paramMode = MODE_BOTH + paramMode = isFrientSiren() ? FRIENT_MODE_SIREN : MODE_BOTH + paramDutyCycle = isFrientSiren() ? BASIC_DUTY_CYCLE : STROBE_DUTY_CYCLE + paramStrobeLevel = isFrientSiren() ? BASIC_LEVEL : STROBE_LEVEL } zigbee.command(IAS_WD_CLUSTER, COMMAND_IAS_WD_START_WARNING, paramMode, DataType.pack(warningDuration, DataType.UINT16), paramDutyCycle, paramStrobeLevel) @@ -202,3 +211,7 @@ def off() { private isOzomSiren() { device.getDataValue("manufacturer") == "ClimaxTechnology" } + +private Boolean isFrientSiren() { + device.getDataValue("manufacturer") == "frient A/S" +} From bb5a3fad55a722e27fc11db4abd7ed060fe5240c Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Fri, 18 Dec 2020 20:19:06 +0100 Subject: [PATCH 152/422] WWST-7239 - new fingerprint for Jasco, In-Wall Smart Outlet With Energy Monitoring, 43132 (#53315) --- .../zigbee-switch-power.src/zigbee-switch-power.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy index 0f60222d5ed..1c38328b748 100644 --- a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy +++ b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy @@ -58,6 +58,7 @@ metadata { // Enbrighten fingerprint manufacturer: "Jasco Products", model: "43078", deviceJoinName: "Enbrighten Switch" //Enbrighten In-Wall Smart Switch With Energy Monitoring 43078, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43095", deviceJoinName: "Enbrighten Switch" //Enbrighten Plug-in Smart Switch With Energy Monitoring 43095, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43132", deviceJoinName: "Enbrighten Switch" //Enbrighten In-Wall Smart Outlet With Energy Monitoring 43132, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 } tiles(scale: 2) { From 28aade5f9fe36928c5ecdeb57621b5cbc77db9e7 Mon Sep 17 00:00:00 2001 From: PKacprowiczS Date: Mon, 21 Dec 2020 15:38:53 +0100 Subject: [PATCH 153/422] Added additional configuration for Qubino 2 Flush Relay --- .../qubino-flush-2-relay.groovy | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index ba05096bd2c..fdf6fcb03f0 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -20,6 +20,7 @@ metadata { capability "Actuator" capability "Sensor" capability "Health Check" + capability "Configuration" command "reset" @@ -129,6 +130,22 @@ def excludeParameterFromSync(preference){ return exclude } +def configure() { + def cmds = [] + + if (zwaveInfo?.model?.equals("0051")) { + // parameters 40 and 41 - power consumption reporting threshold for Q1 and Q2 loads (respectively) - 5 % + cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 5)) + cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 41, size: 1, scaledConfigurationValue: 5)) + // parameters 42 and 43 - power consumption reporting time threshold for Q1 and Q2 (respectively) - 5 minutes + // additionally, manual states that default value for below parameters is 0, which disables power reporting + cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: 300)) + cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 43, size: 2, scaledConfigurationValue: 300)) + } + + delayBetween(cmds, 500) +} + def addToAssociationGroupIfNeeded() { def cmds = [] if (zwaveInfo?.model?.equals("0052")) { From 3e90c816e77440973e1d8031f68773fc82f0e734 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Mon, 21 Dec 2020 09:48:37 -0800 Subject: [PATCH 154/422] CHAD-5384 Update GE Link to remove deprecated Polling capability (#52022) --- devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy | 5 ----- 1 file changed, 5 deletions(-) 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 a8ea17d7e62..86b154a4811 100644 --- a/devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy +++ b/devicetypes/smartthings/ge-link-bulb.src/ge-link-bulb.groovy @@ -50,7 +50,6 @@ metadata { capability "Sensor" capability "Switch" capability "Switch Level" - capability "Polling" capability "Light" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0019", manufacturer: "GE_Appliances", model: "ZLL Light", deviceJoinName: "GE Light" //GE Link Bulb @@ -98,10 +97,6 @@ def parse(String description) { } } -def poll() { - return zigbee.onOffRefresh() + zigbee.levelRefresh() -} - def updated() { state.dOnOff = "0000" From 0df6f436ebf37b8d74ca3c478febd549706e88fc Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Mon, 21 Dec 2020 21:27:23 +0100 Subject: [PATCH 155/422] ICP-13896 Fixed lack of energy meter reports (#53142) --- devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index aa3f3b1df93..95679f85517 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -327,6 +327,7 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { log.debug "BasicReport: ${cmd}" + sendHubCommand(encapCommands(getPowerMeterCommands())) dimmerEvents(cmd) } From 185451d2bcb2dcdbb949dc14b56bc3eb6c743bb6 Mon Sep 17 00:00:00 2001 From: Jack Kilby Date: Tue, 22 Dec 2020 15:43:25 +0800 Subject: [PATCH 156/422] Add Devicetype of RealilySwitch-Gen3 Zigbee Mode from Third Reality, Inc. --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index e52b94344e2..52df78c61a3 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -90,6 +90,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0019", manufacturer: "", model: "TERNCY-LS01", deviceJoinName: "Terncy Switch" //Terncy Smart Light Socket // Third Reality + fingerprint profileId: "0104", inClusters: "0000, 0006", outClusters: "0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS009Z", deviceJoinName: "RealitySwitch Switch" //RealitySwitch-Gen3 Zigbee Mode fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS008Z", deviceJoinName: "RealitySwitch Switch" //RealitySwitch Plus fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS007Z", deviceJoinName: "RealitySwitch Switch" //RealitySwitch From 4ee97c11a38a038218b5c5c5817d69c7dc0545f0 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Tue, 22 Dec 2020 10:14:57 -0800 Subject: [PATCH 157/422] CHAD-5976 Add explicit metadata for thermostats that are missing it (#53330) * CHAD-5976 Add explicit metadata for thermostats that are missing it The recent changes that added discrete thermostat capabilities to several devices previously using the monolithic capabilities caused issues with the fallback metadata being used for devices which previously did not have explicit metadata. The fallback metadata would see values for the duplicated attributes and create UI for both of them, leading to duplicated controls. Setting the devices to use explicit metadata should remedy this. * CHAD-5976 Add explicit metadata for thermostats that are missing it Discrete setpoint capabilities only sporadically have ui, but the monolithic ones work well. --- .../centralite-thermostat.src/centralite-thermostat.groovy | 3 ++- .../smartthings/ct100-thermostat.src/ct100-thermostat.groovy | 2 +- .../zwave-radiator-thermostat.groovy | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy b/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy index dc8e1c8a821..aae5023f5f3 100644 --- a/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy +++ b/devicetypes/smartthings/centralite-thermostat.src/centralite-thermostat.groovy @@ -16,7 +16,8 @@ * Date: 2013-12-02 */ metadata { - definition (name: "CentraLite Thermostat", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false) { + definition (name: "CentraLite Thermostat", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', + executeCommandsLocally: false, mnmn: "SmartThings", vid: "SmartThings-smartthings-Z-Wave_Thermostat") { capability "Actuator" capability "Temperature Measurement" capability "Thermostat" diff --git a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy index cfb29ecdabe..c14c466ca67 100644 --- a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy +++ b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy @@ -1,6 +1,6 @@ metadata { // Automatically generated. Make future change here. - definition (name: "CT100 Thermostat", namespace: "smartthings", author: "SmartThings") { + definition (name: "CT100 Thermostat", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "SmartThings-smartthings-Z-Wave_Thermostat") { capability "Actuator" capability "Temperature Measurement" capability "Relative Humidity Measurement" diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index deedf7874de..52ddcab095b 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -13,7 +13,8 @@ * */ metadata { - definition (name: "Z-Wave Radiator Thermostat", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.thermostat") { + definition (name: "Z-Wave Radiator Thermostat", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.thermostat", + mnmn: "SmartThings", vid: "SmartThings-smartthings-Fibaro_Heat_Controller") { capability "Refresh" capability "Battery" capability "Thermostat Heating Setpoint" From 69b1d3398493dbc7d3367288d0d4e3c08089a227 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 22 Dec 2020 19:28:58 +0100 Subject: [PATCH 158/422] [ICP-13928] Add basicSet to update status when manual action is preformed (#53557) * Add basicSet to update status when manual action is preformed * Space fix --- .../qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index ba05096bd2c..032e7bcbdf7 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -331,6 +331,11 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelR createEvent(map) } +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, ep = null) { + log.debug "Basic ${cmd}" + (ep ? " from endpoint $ep" : "") + changeSwitch(ep, cmd) +} + def zwaveEvent(physicalgraph.zwave.Command cmd, ep) { log.warn "Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "") } From 6c2f3e79f07e9ab1d303474032a0c4823702564e Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 23 Dec 2020 10:31:37 -0800 Subject: [PATCH 159/422] CHAD-5976 CT100 already had device-specific metadata --- .../smartthings/ct100-thermostat.src/ct100-thermostat.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy index c14c466ca67..cfb29ecdabe 100644 --- a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy +++ b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy @@ -1,6 +1,6 @@ metadata { // Automatically generated. Make future change here. - definition (name: "CT100 Thermostat", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "SmartThings-smartthings-Z-Wave_Thermostat") { + definition (name: "CT100 Thermostat", namespace: "smartthings", author: "SmartThings") { capability "Actuator" capability "Temperature Measurement" capability "Relative Humidity Measurement" From b963bb15ad4b17aba22d714e2f54b6603ea4a535 Mon Sep 17 00:00:00 2001 From: MGoralczykS Date: Mon, 4 Jan 2021 11:09:59 +0100 Subject: [PATCH 160/422] Add Leviton Switch to zigbee-switch --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 52df78c61a3..a127cab0c73 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -65,6 +65,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "000A", manufacturer: "HAI", model: "65A21-1", deviceJoinName: "Leviton Switch" //Leviton Wireless Load Control Module-30amp fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL15A", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Lumina RF Plug-In Appliance Module fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL15S", deviceJoinName: "Leviton Switch" //Leviton Lumina RF Switch + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG15S", deviceJoinName: "Leviton Switch" //Leviton Lumina RF Switch // Orvibo fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "095db3379e414477ba6c2f7e0c6aa026", deviceJoinName: "Orvibo Switch" //Orvibo Smart Switch From 973553a436fbe4eabb98f86d634d7344f5c3f9c4 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 5 Jan 2021 10:19:11 +0100 Subject: [PATCH 161/422] [WWST-7290] Add Levition Dimmer Switch to zgibee-dimmer (#54824) * Add Levition Dimmer Switch to zgibee-dimmer * Move fingerprint to leviton section * Fixes --- devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index cc4d8d4aed5..c5668eae63b 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -82,6 +82,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Plug-In Dimmer fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL1KD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Dimmer Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "ZSD07", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF 0-10V Dimming Wall Switch + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0301, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch" //Leviton Zigbee Plug-in DImmer DG3HL // LINKIND fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "lk", model: "ZBT-DIMLight-GLS0010", deviceJoinName: "Linkind Light" //Linkind Dimmable A19 Bulb From 961d3e9f14ebc5f6f1a1affe5847507e0eecc218 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Fri, 8 Jan 2021 12:00:17 -0800 Subject: [PATCH 162/422] GPD-433 Fibaro Smoke Sensor - Update preference range (#37638) * GPD-433 Fibaro Smoke Sensor - Update preference range The originally listed device preference range did not match the supported values. * fixup --- .../fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy b/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy index d5c08dc86a7..317e5cd37d2 100644 --- a/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy +++ b/devicetypes/smartthings/fibaro-smoke-sensor.src/fibaro-smoke-sensor.groovy @@ -69,7 +69,7 @@ metadata { input "temperatureReportInterval", "enum", title: "Temperature report interval", options: ["Reports inactive", "5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "30 minutes", displayDuringSetup: true input "temperatureReportHysteresis", "number", title: "Temperature report hysteresis", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true - input "temperatureThreshold", "number", title: "Overheat temperature threshold", description: "Available settings: 0 or 2-100 C", range: "0..100", displayDuringSetup: true + input "temperatureThreshold", "number", title: "Overheat temperature threshold", description: "Available settings: 1-100 C", range: "1..100", displayDuringSetup: true input "excessTemperatureSignalingInterval", "enum", title: "Excess temperature signaling interval", options: ["5 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "30 minutes", displayDuringSetup: true input "lackOfZwaveRangeIndicationInterval", "enum", title: "Lack of Z-Wave range indication interval", From e558caf91197b69323afd8932dfa0e0d612a2ed2 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Mon, 11 Jan 2021 19:56:28 +0100 Subject: [PATCH 163/422] WWST-7286, WWST-7298 - new fingerprints for Leviton devices; ICP-13939 - fix (#55425) * WWST-7286 - new fingerprint for Leviton Zigbee Dimmer DG6HD * WWST-7298 - new fingerprint for Leviton Zigbee Plug-In Switch DG15A * ICP-13939 - fixed ocf device type and device join name for Enbrighten In-Wall Smart Outlet 43102 --- devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy | 1 + devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index c5668eae63b..242f5e6d8be 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -83,6 +83,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL1KD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Dimmer Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "ZSD07", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF 0-10V Dimming Wall Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0301, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch" //Leviton Zigbee Plug-in DImmer DG3HL + fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 // LINKIND fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "lk", model: "ZBT-DIMLight-GLS0010", deviceJoinName: "Linkind Light" //Linkind Dimmable A19 Bulb diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index a127cab0c73..c006fe5a889 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -66,6 +66,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL15A", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Lumina RF Plug-In Appliance Module fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL15S", deviceJoinName: "Leviton Switch" //Leviton Lumina RF Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG15S", deviceJoinName: "Leviton Switch" //Leviton Lumina RF Switch + fingerprint manufacturer: "Leviton", model: "DG15A", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-In Switch DG15A, Raw Description: 01 0104 010A 00 06 0000 0003 0004 0005 0006 0B05 01 0019 // Orvibo fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "095db3379e414477ba6c2f7e0c6aa026", deviceJoinName: "Orvibo Switch" //Orvibo Smart Switch @@ -105,7 +106,7 @@ metadata { fingerprint manufacturer: "Jasco Products", model: "43100", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Outdoor Smart Switch, 43100, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43084", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch Toggle, 43084, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43094", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Smart Switch 43094, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 - fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Outlet 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Outlet", ocfDeviceType: "oic.d.smartplug" //Enbrighten, In-Wall Smart Outlet 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43076", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch 43076, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 } From 4eac557af9508aad16d03040ce90c3164dde7fa6 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Thu, 14 Jan 2021 21:55:31 +0800 Subject: [PATCH 164/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Multi Switch (#55741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 啦啦 王 --- .../zigbee-multi-switch.groovy | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index e192b1ef805..2a8915782a3 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -55,11 +55,27 @@ metadata { // eWeLink // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 - fingerprint manufacturer: "eWeLink", model: "ZB-SW02", deviceJoinName: "eWeLink Switch" //eWeLink 2 Gang Switch 1 + fingerprint manufacturer: "eWeLink", model: "ZB-SW02", deviceJoinName: "eWeLink Switch 1" //eWeLink 2 Gang Switch 1 // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 - fingerprint manufacturer: "eWeLink", model: "ZB-SW03", deviceJoinName: "eWeLink Switch" //eWeLink 3 Gang Switch 1 + fingerprint manufacturer: "eWeLink", model: "ZB-SW03", deviceJoinName: "eWeLink Switch 1" //eWeLink 3 Gang Switch 1 // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 - fingerprint manufacturer: "eWeLink", model: "ZB-SW04", deviceJoinName: "eWeLink Switch" //eWeLink 4 Gang Switch 1 + fingerprint manufacturer: "eWeLink", model: "ZB-SW04", deviceJoinName: "eWeLink Switch 1" //eWeLink 4 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "eWeLink", model: "ZB-SW05", deviceJoinName: "eWeLink Switch 1" //eWeLink 5 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "eWeLink", model: "ZB-SW06", deviceJoinName: "eWeLink Switch 1" //eWeLink 6 Gang Switch 1 + + // LELLKI + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "LELLKI", model: "JZ-ZB-002", deviceJoinName: "LELLKI Switch 1" //LELLKI 2 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "LELLKI", model: "JZ-ZB-003", deviceJoinName: "LELLKI Switch 1" //LELLKI 3 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "LELLKI", model: "JZ-ZB-004", deviceJoinName: "LELLKI Switch 1" //LELLKI 4 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "LELLKI", model: "JZ-ZB-005", deviceJoinName: "LELLKI Switch 1" //LELLKI 5 Gang Switch 1 + // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 + fingerprint manufacturer: "LELLKI", model: "JZ-ZB-006", deviceJoinName: "LELLKI Switch 1" //LELLKI 6 Gang Switch 1 } // simulator metadata simulator { @@ -241,17 +257,17 @@ private getChildCount() { return 3 } else if (device.getDataValue("model") == "E220-KR2N0Z0-HA") { return 2 - } else if (device.getDataValue("model") == "E220-KR3N0Z0-HA" || device.getDataValue("model") == "ZB-SW03") { + } else if (device.getDataValue("model") == "E220-KR3N0Z0-HA" || device.getDataValue("model") == "ZB-SW03" || device.getDataValue("model") == "JZ-ZB-003") { return 3 - } else if (device.getDataValue("model") == "E220-KR4N0Z0-HA" || device.getDataValue("model") == "ZB-SW04") { + } else if (device.getDataValue("model") == "E220-KR4N0Z0-HA" || device.getDataValue("model") == "ZB-SW04" || device.getDataValue("model") == "JZ-ZB-004") { return 4 - } else if (device.getDataValue("model") == "E220-KR5N0Z0-HA") { + } else if (device.getDataValue("model") == "E220-KR5N0Z0-HA" || device.getDataValue("model") == "ZB-SW05" || device.getDataValue("model") == "JZ-ZB-005") { return 5 - } else if (device.getDataValue("model") == "E220-KR6N0Z0-HA") { + } else if (device.getDataValue("model") == "E220-KR6N0Z0-HA" || device.getDataValue("model") == "ZB-SW06" || device.getDataValue("model") == "JZ-ZB-006") { return 6 } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB" || device.getDataValue("model") == "PM-S350-ZB" || device.getDataValue("model") == "ST-S350-ZB") { return 3 } else { return 2 } -} +} \ No newline at end of file From 17fe07ecab2b43c14d902cb50bf68a07ef782734 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 19 Jan 2021 19:18:04 +0100 Subject: [PATCH 165/422] [ICP-13872] Change power consumption reporting threshold for Qubino 1 Relay (#55813) * Change power consumption reporting threshold for Qubino 1 Relay * Add comment which explaining parameter --- .../qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 3818fd16a9f..85970314057 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -141,6 +141,9 @@ def configure() { // additionally, manual states that default value for below parameters is 0, which disables power reporting cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: 300)) cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 43, size: 2, scaledConfigurationValue: 300)) + } else if (zwaveInfo?.model?.equals("0052")) { + //parameter 40 - power reporting threshold for Q1 load - 75% + cmds += encap(zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 75)) } delayBetween(cmds, 500) From f8db14e463655751e20b008088cb0f46597d36ba Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Wed, 20 Jan 2021 15:29:26 +0100 Subject: [PATCH 166/422] DevWs for frient containing containing Zigbee Non-Holdable Button (#56135) * DevWs for frient containing containing Zigbee Non-Holdable Button * Clean up code * Match local identation Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-non-holdable-button.groovy | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) mode change 100755 => 100644 devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy diff --git a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy old mode 100755 new mode 100644 index deb2611dc7b..ba9af220756 --- a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy +++ b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy @@ -26,6 +26,7 @@ metadata { capability "Sensor" fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0500", outClusters: "0019", manufacturer: "HEIMAN", model: "SOS-EM", deviceJoinName: "HEIMAN Button" //HEIMAN Emergency Button + fingerprint manufacturer: "frient A/S", model: "MBTZB-110", deviceJoinName: "frient Button" // Frient Smart Button, 20 0104 0007 00 05 0000 0001 0003 000F 0020 04 0003 0006 000A 0019 } tiles(scale: 2) { @@ -51,6 +52,9 @@ def installed() { sendEvent(name: "numberOfButtons", value: 1, displayed: false) } +private getBINARY_INPUT_CLUSTER() { 0x000f } +private getATTRIBUTE_PRESENT_VALUE() { 0x0055 } + private List collectAttributes(Map descMap) { List descMaps = new ArrayList() @@ -86,6 +90,8 @@ def parse(String description) { map = translateZoneStatus(zs) } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) { map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value))) + } else if (isFrientButton() && descMap?.clusterInt == BINARY_INPUT_CLUSTER && descMap.attrInt == ATTRIBUTE_PRESENT_VALUE && descMap?.value == "01") { + map = getButtonResult('pushed') } } } @@ -108,7 +114,7 @@ private Map parseIasMessage(String description) { } private Map translateZoneStatus(ZoneStatus zs) { - if (zs.isAlarm1Set()) { + if (zs.isAlarm1Set() || (isFrientButton() && zs.isAlarm2Set()) { return getButtonResult('pushed') } } @@ -126,13 +132,17 @@ private Map getBatteryResult(rawValue) { result.translatable = true result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" - def minVolts = 2.1 - def maxVolts = 3.0 - def pct = (volts - minVolts) / (maxVolts - minVolts) - def roundedPct = Math.round(pct * 100) - if (roundedPct <= 0) - roundedPct = 1 - result.value = Math.min(100, roundedPct) + if (isFrientButton()) { + result.value = liIon3VTable.find { threshold, perc -> (threshold <= volts) }.value + } else { + def minVolts = 2.1 + def maxVolts = 3.0 + def pct = (volts - minVolts) / (maxVolts - minVolts) + def roundedPct = Math.round(pct * 100) + if (roundedPct <= 0) + roundedPct = 1 + result.value = Math.min(100, roundedPct) + } } return result @@ -192,5 +202,26 @@ def configure() { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.enrollResponse() + - zigbee.batteryConfig() + zigbee.batteryConfig() + + (isFrientButton() ? zigbee.configureReporting(BINARY_INPUT_CLUSTER, ATTRIBUTE_PRESENT_VALUE, DataType.BOOLEAN, 0, 600, null) : []) } + +private Boolean isFrientButton() { + device.getDataValue("manufacturer") == "frient A/S" +} + +// Capacity discharge curve for 3v Lithium Ion (voltage: remaining %) +private getLiIon3VTable() {[ + 2.9: 100, + 2.8: 80, + 2.75: 60, + 2.7: 50, + 2.65: 40, + 2.6: 30, + 2.5: 20, + 2.4: 15, + 2.2: 10, + 2.0: 1, + 1.9: 0, + 0.0: 0 +]} From 58ef27b50167c511ed29dcadb145483782d5c791 Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 20 Jan 2021 10:12:43 -0800 Subject: [PATCH 167/422] fixes missing paren --- .../zigbee-non-holdable-button.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy index ba9af220756..edfa4c064d4 100644 --- a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy +++ b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy @@ -114,7 +114,7 @@ private Map parseIasMessage(String description) { } private Map translateZoneStatus(ZoneStatus zs) { - if (zs.isAlarm1Set() || (isFrientButton() && zs.isAlarm2Set()) { + if (zs.isAlarm1Set() || (isFrientButton() && zs.isAlarm2Set())) { return getButtonResult('pushed') } } From 85e0103c5b09cd9619be5afe339d372c57d72844 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Wed, 20 Jan 2021 19:58:09 +0100 Subject: [PATCH 168/422] =?UTF-8?q?[ICP-13896]=20Qubino=20DIN=20Dimmer=20?= =?UTF-8?q?=E2=80=93=20Device=20does=20not=20report=20values=20of=20Energy?= =?UTF-8?q?=20Consumption=20(#56169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added power reports configuration and moved query for energy meter report * fix --- devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index 95679f85517..e67a02b7856 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -233,6 +233,10 @@ def configure() { commands << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier:1, nodeId:[]) commands << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]) commands << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1) + if (isDINDimmer()) { + //parameter 42 - power reporting time threshold + commands << zwave.configurationV1.configurationSet(parameterNumber: 42, size: 2, scaledConfigurationValue: 2 * 15 * 60 + 2 * 60) + } commands += getRefreshCommands() commands += getReadConfigurationFromTheDeviceCommands() @@ -327,7 +331,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, ep = null) { log.debug "BasicReport: ${cmd}" - sendHubCommand(encapCommands(getPowerMeterCommands())) dimmerEvents(cmd) } @@ -351,6 +354,9 @@ def handleMeterReport(cmd) { createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kVAh") } else if (cmd.scale == 2) { log.debug("createEvent power") + if (isDINDimmer()) { + sendHubCommand(encap(zwave.meterV3.meterGet(scale: 0x00))) + } createEvent(name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W") } } From e6e6085d37a5f09368969dd265395e03353c1bc7 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Thu, 21 Jan 2021 21:08:01 +0100 Subject: [PATCH 169/422] WWST-7309 - new fingerprint for POPP Smart Thermostat POPE701721 (#55698) * WWST-7309 - new fingerprint for Danfoss Ally Radiator thermostat (new firmware) * WWST-7309 - corrects deviceJoinName of POPP Smart Thermostat POPE701721 * WWST-7309 - handling POPP thermostat in conditional statements --- .../zigbee-thermostat.groovy | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy b/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy index ab3179b8ddf..98c51223f05 100644 --- a/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy +++ b/devicetypes/smartthings/zigbee-thermostat.src/zigbee-thermostat.groovy @@ -36,7 +36,8 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0020,0201,0202,0204,0B05", outClusters: "000A, 0019", manufacturer: "LUX", model: "KONOZ", deviceJoinName: "LUX Thermostat" //LUX KONOz Thermostat fingerprint profileId: "0104", inClusters: "0000,0003,0020,0201,0202,0405", outClusters: "0019, 0402", manufacturer: "Umbrela", model: "Thermostat", deviceJoinName: "Umbrela Thermostat" //Umbrela UTee - fingerprint manufacturer: "Danfoss", model: "eTRV0100", deviceJoinName: "Danfoss Thermostat", vid: "SmartThings-smartthings-Danfoss_Ally_Radiator_Thermostat" //Danfoss Ally Radiator thermostat, Raw Description 01 0104 0301 01 08 0000 0001 0003 000A 0020 0201 0204 0B05 02 0000 0019 //Danfoss Thermostat + fingerprint manufacturer: "Danfoss", model: "eTRV0100", deviceJoinName: "Danfoss Thermostat", vid: "SmartThings-smartthings-Danfoss_Ally_Radiator_Thermostat" //Danfoss Ally Radiator thermostat, Raw Description 01 0104 0301 01 08 0000 0001 0003 000A 0020 0201 0204 0B05 02 0000 0019 + fingerprint manufacturer: "D5X84YU", model: "eT093WRO", deviceJoinName: "POPP Thermostat", vid: "SmartThings-smartthings-Danfoss_Ally_Radiator_Thermostat" //POPP Smart Thermostat POPE701721, Raw Description 01 0104 0301 01 08 0000 0001 0003 000A 0020 0201 0204 0B05 02 0000 0019 } tiles { @@ -281,7 +282,7 @@ private parseAttrMessage(description) { def installed() { sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) - if (isDanfossAlly()) { + if (isDanfossAlly() || isPOPP()) { state.supportedThermostatModes = ["heat"] } else { state.supportedThermostatModes = ["off", "heat", "cool", "emergency heat"] @@ -309,7 +310,7 @@ def refresh() { } def getBatteryRemainingCommand() { - if (isDanfossAlly()) { + if (isDanfossAlly() || isPOPP()) { zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_PERCENTAGE_REMAINING) } else { zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_VOLTAGE) @@ -324,7 +325,7 @@ def configure() { def binding = zigbee.addBinding(THERMOSTAT_CLUSTER) + zigbee.addBinding(FAN_CONTROL_CLUSTER) def startValues = zigbee.writeAttribute(THERMOSTAT_CLUSTER, HEATING_SETPOINT, DataType.INT16, 0x07D0) - if (isDanfossAlly()) { + if (isDanfossAlly() || isPOPP()) { // setting Min/Max HeatSetPointLimits for Danfoss Ally - MinHeatSetpointLimit: 500 (0x01F4), MaxHeatSetpointLimit: 3500 (0x0DAC) startValues += zigbee.writeAttribute(THERMOSTAT_CLUSTER, MIN_HEAT_SETPOINT_LIMIT, DataType.INT16, 0x01F4) + zigbee.writeAttribute(THERMOSTAT_CLUSTER, MAX_HEAT_SETPOINT_LIMIT, DataType.INT16, 0x0DAC) @@ -360,7 +361,7 @@ def getBatteryPercentage(rawValue) { } def getVoltageRange() { - if (isDanfossAlly()) { + if (isDanfossAlly() || isPOPP()) { // Danfoss Ally's volage ranges: 2.4V - 0%, 3.2V - 100% (for some types of batteries it will be 3.4V - 100%) [minVolts: 2.4, maxVolts: 3.2] } else { @@ -504,12 +505,16 @@ private boolean isDanfossAlly() { device.getDataValue("model") == "eTRV0100" } +private boolean isPOPP() { + device.getDataValue("model") == "eT093WRO" +} + // TODO: Get these from the thermostat; for now they are set to match the UI metadata def getCoolingSetpointRange() { (getTemperatureScale() == "C") ? [10, 35] : [50, 95] } def getHeatingSetpointRange() { - if (isDanfossAlly()) { + if (isDanfossAlly() || isPOPP()) { (getTemperatureScale() == "C") ? [4, 35] : [39, 95] } else { (getTemperatureScale() == "C") ? [7.22, 32.22] : [45, 90] From 73089639e556475a287cc6c98c0c30d454e1e7ca Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Fri, 22 Jan 2021 13:49:28 +0100 Subject: [PATCH 170/422] =?UTF-8?q?[ICP-12754]=20Fix=20for:=20"Qubino=20Fl?= =?UTF-8?q?ush=20On=20Off=20Thermostat=202=20=E2=80=93=20Cannot=20set=20a?= =?UTF-8?q?=20heating=20temperature"=20(#56512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cast values to double * Cast minSetpointTemp to double --- .../qubino-flush-thermostat.src/qubino-flush-thermostat.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy index e836a7a42b6..95adc4ec67b 100644 --- a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy +++ b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy @@ -278,7 +278,7 @@ def setCoolingSetpoint(setpoint) { def updateSetpoint(setpoint, setpointType) { setpoint = temperatureScale == 'C' ? setpoint : fahrenheitToCelsius(setpoint) - setpoint = Math.max(Math.min(setpoint, maxSetpointTemperature), minSetpointTemperature) + setpoint = Math.max(Math.min(setpoint.doubleValue(), maxSetpointTemperature.doubleValue()), minSetpointTemperature.doubleValue()) [ secure(zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: setpoint, setpointType: setpointType, size: 2])), "delay 2000", From 3751571bbf110d9b3adfe08b33e1d452ffc618f4 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:29:40 +0100 Subject: [PATCH 171/422] [ICP-13654] Added specific UI metadata for Qubino dimmers (#52444) * ICP-13654 Added specific UI metadata for qubino dimmers * Added custom metadata for qubino flush dimmer 0-10V --- devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index e67a02b7856..807f7ce7ca7 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -12,7 +12,7 @@ * */ metadata { - definition(name: "Qubino Dimmer", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid:"generic-dimmer-power-energy", ocfDeviceType: "oic.d.switch", runLocally: false, executeCommandsLocally: false) { + definition(name: "Qubino Dimmer", namespace: "qubino", author: "SmartThings", mnmn: "SmartThings", vid:"qubino-dimmer-power-energy", ocfDeviceType: "oic.d.switch", runLocally: false, executeCommandsLocally: false) { capability "Actuator" capability "Configuration" capability "Energy Meter" @@ -33,7 +33,7 @@ metadata { // Qubino Flush Dimmer 0-10V - ZMNHVD // Raw Description: zw:L type:1100 mfr:0159 prod:0001 model:0053 ver:2.04 zwv:4.34 lib:03 cc:5E,86,5A,72,73,27,25,26,85,8E,59,70 ccOut:20,26 role:05 ff:9C00 ui:9C00 - fingerprint mfr: "0159", prod: "0001", model: "0053", deviceJoinName: "Qubino Dimmer", mnmn: "SmartThings", vid:"generic-dimmer" + fingerprint mfr: "0159", prod: "0001", model: "0053", deviceJoinName: "Qubino Dimmer", mnmn: "SmartThings", vid:"qubino-dimmer" //Qubino Mini Dimmer // Raw Description: zw:Ls type:1101 mfr:0159 prod:0001 model:0055 ver:20.02 zwv:5.03 lib:03 cc:5E,6C,55,98,9F sec:86,25,26,85,59,72,5A,70,32,71,73 From 6af51b736c2c80dcb92b83d19289b18ee986e476 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 26 Jan 2021 19:52:48 +0100 Subject: [PATCH 172/422] WWST-7201, WWST-7223, WWST-7239 moves Enbrighten, Jasco devices between DTHs (#56525) * WWST-7223, WWST-7239 moves Enbrighten, Jasco fingerprints between DTHs * WWST-7223, WWST-7239 adds support of Enbrighten,Jasco devices * WEST-7223, WEST-7239 fixes indentation * WWST-7239 corrects deviceJoinName * WWST-7201 - moves Enbrighten In-Wall Smart Switch With Energy Monitoring 43078 to zigbee-metering-plug DTH --- .../zigbee-metering-plug.groovy | 11 +++++++++-- .../zigbee-switch-power.groovy | 5 ----- 2 files changed, 9 insertions(+), 7 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 c26d20ba3d0..e42d58ea6e3 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -35,6 +35,9 @@ metadata { fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-134", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-137", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SMRZB-143", deviceJoinName: "frient Outlet" // frient smart cable, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 + fingerprint manufacturer: "Jasco Products", model: "43095", deviceJoinName: "Enbrighten Outlet" //Enbrighten Plug-in Smart Switch With Energy Monitoring 43095, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43132", deviceJoinName: "Jasco Outlet" //Enbrighten In-Wall Smart Outlet With Energy Monitoring 43132, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 + fingerprint manufacturer: "Jasco Products", model: "43078", deviceJoinName: "Enbrighten Switch", ocfDeviceType: "oic.d.switch" //Enbrighten In-Wall Smart Switch With Energy Monitoring 43078, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 } tiles(scale: 2){ @@ -160,13 +163,17 @@ def configure() { } private int getPowerDiv() { - isSengledOutlet() ? 10 : 1 + (isSengledOutlet() || isJascoProductsOutlet()) ? 10 : 1 } private int getEnergyDiv() { - isSengledOutlet() ? 10000 : 100 + (isSengledOutlet() || isJascoProductsOutlet()) ? 10000 : 100 } private boolean isSengledOutlet() { device.getDataValue("model") == "E1C-NB7" } + +private boolean isJascoProductsOutlet() { + device.getDataValue("manufacturer") == "Jasco Products" +} diff --git a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy index 1c38328b748..15563dc1217 100644 --- a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy +++ b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy @@ -54,11 +54,6 @@ metadata { fingerprint profileId: "0104", deviceId: "0051", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04, 1000, 0702", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "AD-SmartPlug3001", deviceJoinName: "Eria Switch" //Eria Zigbee Smart Plug fingerprint profileId: "0104", deviceId: "010A", inClusters: "0000, 0003, 0004, 0005, 0006, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BPU3", deviceJoinName: "Eria Switch" //Eria Zigbee On/Off Plug fingerprint profileId: "0104", deviceId: "0101", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BDP3001", deviceJoinName: "Eria Switch" //Eria Zigbee Dimmable Plug - - // Enbrighten - fingerprint manufacturer: "Jasco Products", model: "43078", deviceJoinName: "Enbrighten Switch" //Enbrighten In-Wall Smart Switch With Energy Monitoring 43078, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 - fingerprint manufacturer: "Jasco Products", model: "43095", deviceJoinName: "Enbrighten Switch" //Enbrighten Plug-in Smart Switch With Energy Monitoring 43095, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 - fingerprint manufacturer: "Jasco Products", model: "43132", deviceJoinName: "Enbrighten Switch" //Enbrighten In-Wall Smart Outlet With Energy Monitoring 43132, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 } tiles(scale: 2) { From 832297bdc85b3c7f424fe16cacb92693ea664cf7 Mon Sep 17 00:00:00 2001 From: Jack Kilby Date: Thu, 28 Jan 2021 07:44:39 +0800 Subject: [PATCH 173/422] Add ThirdReality Zigbee Door Sensor DeviceType. (#55831) * Add ThirdReality Zigbee Door Sensor DeviceType. * Merge ThirdReality DoorSensor into orvibo-contact-sensor DeviceHandler. * Modify ThirdReality Door Sensor Device Handler for Battery and other behaviors. Co-authored-by: jiang shanyang --- .../Orvibo-Contact-Sensor.groovy | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index 68b6e77aa96..29eb6e072dc 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -28,11 +28,13 @@ metadata { capability "Refresh" capability "Health Check" capability "Sensor" + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500", outClusters: "0003", manufacturer: "eWeLink", model: "DS01", deviceJoinName: "eWeLink Open/Closed Sensor" //eWeLink Door Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "ORVIBO", model: "e70f96b3773a4c9283c6862dbafb6a99", deviceJoinName: "Orvibo Open/Closed Sensor" fingerprint inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "Aurora", model: "WindowSensor51AU", deviceJoinName: "Aurora Open/Closed Sensor" //Aurora Smart Door/Window Sensor fingerprint manufacturer: "Aurora", model: "DoorSensor50AU", deviceJoinName: "Aurora Open/Closed Sensor" // Raw Description: 01 0104 0402 00 06 0000 0001 0003 0020 0500 0B05 01 0019 //Aurora Smart Door/Window Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "HEIMAN", model: "DoorSensor-N", deviceJoinName: "HEIMAN Open/Closed Sensor" //HEIMAN Door Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RDS17BZ", deviceJoinName: "ThirdReality Door Sensor" //ThirdReality Door Sensor } simulator { @@ -105,7 +107,13 @@ def parse(String description) { 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 manufacturer = getDataValue("manufacturer") + + if (manufacturer == "Third Reality, Inc") { + //ThirdReality Door Sensor do not set checkInterval for power-saving. + } else { + sendEvent(name: "checkInterval", value:20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + } } /** * PING is used by Device-Watch in attempt to reach the Device @@ -119,7 +127,7 @@ def refresh() { log.debug "Refreshing Battery and ZONE Status" def manufacturer = getDataValue("manufacturer") def refreshCmds = zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) - if (manufacturer == "ORVIBO" || manufacturer == "eWeLink" || manufacturer == "HEIMAN") { + if (manufacturer == "ORVIBO" || manufacturer == "eWeLink" || manufacturer == "HEIMAN" || manufacturer == "Third Reality, Inc") { refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } else { // this is actually just supposed to be for Aurora, but we'll make it the default as it's more widely supported refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) @@ -130,7 +138,9 @@ def refresh() { def configure() { def manufacturer = getDataValue("manufacturer") - if (manufacturer == "eWeLink") { + if (manufacturer == "Third Reality, Inc") { + //ThirdReality Door Sensor do not set checkInterval for power-saving. + } else 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"]) @@ -143,6 +153,8 @@ def configure() { cmds = zigbee.enrollResponse() + zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 30, 60 * 5, null) + zigbee.batteryConfig() } else if (manufacturer == "eWeLink" || manufacturer == "HEIMAN") { cmds = zigbee.enrollResponse() + zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 30, 60 * 5, null) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 600, 1) + } else if (manufacturer == "Third Reality, Inc") { + cmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } cmds += refresh() cmds @@ -151,11 +163,15 @@ def configure() { def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] - + def manufacturer = getDataValue("manufacturer") if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true + if (manufacturer == "Third Reality, Inc") { + result.value = Math.round(rawValue) + } else { result.value = Math.round(rawValue / 2) + } result.descriptionText = "${device.displayName} battery was ${result.value}%" } From 0afe0c48566ee938d6d074c63215216ae3813af6 Mon Sep 17 00:00:00 2001 From: Jack Kilby Date: Thu, 28 Jan 2021 07:45:33 +0800 Subject: [PATCH 174/422] Add ThirdReality Motion Sensor DeviceType. (#55830) * Add ThirdReality Motion Sensor DeviceType. * Merge ThirdReality Motion Sensor into existing zigbee-motion-detector device handler by add a fingerprint. * Change deviceJoinName as a brand-show one. * Modify Motion Sensor Device Handler for ThirdReality Motion Sensor. Co-authored-by: jiang shanyang --- .../zigbee-motion-detector.groovy | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy index 23c5d2d2d0d..3a52885a02d 100644 --- a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy +++ b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy @@ -25,10 +25,12 @@ metadata { capability "Refresh" capability "Health Check" capability "Sensor" + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500", outClusters: "0003", manufacturer: "eWeLink", model: "MS01", deviceJoinName: "eWeLink Motion Sensor" //eWeLink Motion Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "ORVIBO", model: "895a2d80097f4ae2b2d40500d5e03dcc", deviceJoinName: "Orvibo Motion Sensor" //Orvibo Motion Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "Megaman", model: "PS601/z1", deviceJoinName: "INGENIUM Motion Sensor" //INGENIUM ZB PIR Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001", outClusters: "0019", manufacturer: "HEIMAN", model: "PIRSensor-N", deviceJoinName: "HEIMAN Motion Sensor" //HEIMAN Motion Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RMS16BZ", deviceJoinName: "ThirdReality Motion Sensor" //ThirdReality Motion Sensor } simulator { status "active": "zone status 0x0001 -- extended status 0x00" @@ -118,16 +120,22 @@ def parseIasMessage(ZoneStatus zs) { } def supportsRestoreNotify() { - getDataValue("manufacturer") == "eWeLink" + return (getDataValue("manufacturer") == "eWeLink") || (getDataValue("manufacturer") == "Third Reality, Inc") } def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] + def manufacturer = getDataValue("manufacturer") + if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - result.value = Math.round(rawValue / 2) + if (manufacturer == "Third Reality, Inc") { + result.value = Math.round(rawValue) + } else { + result.value = Math.round(rawValue / 2) + } result.descriptionText = "${device.displayName} battery was ${result.value}%" } return result @@ -164,6 +172,8 @@ def configure() { if (manufacturer == "eWeLink") { sendEvent(name: "checkInterval", value:2 * 60 * 60 + 5 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) return zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 3600, 0x10) + refresh() + } else if (manufacturer == "Third Reality, Inc") { + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } 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() From 5805a1a59e87d731bba7f7911f5c1bb976b8f6ab Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 3 Feb 2021 10:55:31 -0800 Subject: [PATCH 175/422] updates sample translation text --- .../i18n/messages.properties | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/testing/simulated-device-preferences.src/i18n/messages.properties b/devicetypes/smartthings/testing/simulated-device-preferences.src/i18n/messages.properties index 7b63798460d..329850b9cc6 100644 --- a/devicetypes/smartthings/testing/simulated-device-preferences.src/i18n/messages.properties +++ b/devicetypes/smartthings/testing/simulated-device-preferences.src/i18n/messages.properties @@ -13,12 +13,12 @@ # under the License. # Korean (ko) # Device Preferences -'''Section 1 Title'''.fr=Section 1 Title (French) -'''Section 1 Description'''.fr=Section 1 Description (French) -'''Option 1 Value'''.fr=Option 1 Value (French) +'''Enum Types Description'''.fr=Enum Types Description (French) +'''Enum Description (key/value options)'''.fr=Enum Description (key/value options) (French) +'''Enum1 - Option A Value'''.fr=Enum1 - Option A Value (French) '''default password'''.fr=default password (French) -'''Section 1 Title'''.es=Section 1 Title (Spanish) -'''Section 1 Description'''.es=Section 1 Description (Spanish) -'''Option 1 Value'''.es=Option 1 Value (Spanish) +'''Enum Types Description'''.es=Enum Types Description (Spanish) +'''Enum Description (key/value options)'''.es=Enum Description (key/value options) (Spanish) +'''Enum1 - Option A Value'''.es=Enum1 - Option A Value (Spanish) '''default password'''.es=default password (Spanish) From 06c10c7b5b382997e1fd9a54095204dfc5736e88 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 9 Feb 2021 00:19:14 +0100 Subject: [PATCH 176/422] ICP-14001 - add ocfDeviceType to the fingerprint (#57588) --- devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index 242f5e6d8be..dd7b9b783d7 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -83,7 +83,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL1KD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Dimmer Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "ZSD07", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF 0-10V Dimming Wall Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0301, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch" //Leviton Zigbee Plug-in DImmer DG3HL - fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 + fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 // LINKIND fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "lk", model: "ZBT-DIMLight-GLS0010", deviceJoinName: "Linkind Light" //Linkind Dimmable A19 Bulb From 62da13c3627d141bbe2612669aa7f81eafbe9e00 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Tue, 9 Feb 2021 00:20:11 +0100 Subject: [PATCH 177/422] WWST-7205 - Enbrighten Smart Dimmer / new generic DTH to support dimmers that report both power [W] and energy [kWh] values. (#56598) * WWST-7205 - new generic DTH to support dimmers that report both power [W] and energy [kWh] values. * WWST-7205 - fixes indentations * WWST-7205 - fixes spacing * WWST-7205 - changes deviceJoinName * WWST-7205 - switches this DTH to local execution * WWST-7205 - adds minHubCoreVersion * WWST-7205 - fixes handling power and energy reports, fixes indentations * WWST-7205 - fixes handling power reports * WWST-7205 - changes ocfDeviceType to oic.d.switch --- .../zigbee-metering-dimmer.groovy | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy diff --git a/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy b/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy new file mode 100644 index 00000000000..0daad08df8f --- /dev/null +++ b/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy @@ -0,0 +1,142 @@ +/** + * Copyright 2021 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. + * + */ +import physicalgraph.zigbee.zcl.DataType +metadata { + definition (name: "ZigBee Metering Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch", mnmn: "SmartThings", vid:"generic-dimmer-power-energy", runLocally: true, executeCommandsLocally: true, genericHandler: "Zigbee", minHubCoreVersion: '000.019.00012') { + + capability "Actuator" + capability "Configuration" + capability "Refresh" + capability "Power Meter" + capability "Energy Meter" + capability "Switch" + capability "Switch Level" + capability "Health Check" + + // Enbrighten/Jasco + fingerprint manufacturer: "Jasco Products", model: "43082", deviceJoinName: "Enbrighten Dimmer Switch" //Enbrighten, in-Wall Smart Dimmer With Energy Monitoring 43082, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0702 0B05 02 000A 0019 + } +} + +def getATTRIBUTE_READING_INFO_SET() { 0x0000 } +def getATTRIBUTE_HISTORICAL_CONSUMPTION() { 0x0400 } + +// Parse incoming device messages to generate events +def parse(String description) { + log.debug "description is $description" + List result = [] + + def event = zigbee.getEvent(description) + if (event) { + log.info event + if (event.name == "power") { + def powerDiv = device.getDataValue("divisor") + powerDiv = powerDiv ? (powerDiv as int) : 1 + event.value = event.value/powerDiv + event.unit = "W" + } else if (event.name == "energy") { + def energyDiv = device.getDataValue("energyDivisor") + energyDiv = energyDiv ? (energyDiv as int) : 100 + event.value = event.value/energyDiv + event.unit = "kWh" + } + log.info "event: $event" + result << event + } else { + def descMap = zigbee.parseDescriptionAsMap(description) + log.debug "descMap: $descMap" + + List attrData = [[clusterInt: descMap.clusterInt ,attrInt: descMap.attrInt, value: descMap.value]] + descMap.additionalAttrs.each { + attrData << [clusterInt: descMap.clusterInt, attrInt: it.attrInt, value: it.value] + } + + attrData.each { + def map = [:] + if (it.value && it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_HISTORICAL_CONSUMPTION) { + log.debug "power" + map.name = "power" + def powerDiv = device.getDataValue("divisor") + powerDiv = powerDiv ? (powerDiv as int) : 1 + map.value = zigbee.convertHexToInt(it.value)/powerDiv + map.unit = "W" + } + else if (it.value && it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_READING_INFO_SET) { + log.debug "energy" + map.name = "energy" + def energyDiv = device.getDataValue("energyDivisor") + energyDiv = energyDiv ? (energyDiv as int) : 100 + map.value = zigbee.convertHexToInt(it.value)/energyDiv + map.unit = "kWh" + } + + if (map) { + result << createEvent(map) + } + } + } + log.debug "result: ${result}" + return result +} + +def off() { + zigbee.off() +} + +def on() { + zigbee.on() +} + +def setLevel(value, rate = null) { + zigbee.setLevel(value) + (value?.toInteger() > 0 ? zigbee.on() : []) +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + log.debug "ping" + return refresh() +} + +def refresh() { + log.debug "refresh" + zigbee.onOffRefresh() + + zigbee.levelRefresh() + + zigbee.simpleMeteringPowerRefresh() + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET) +} + +def configure() { + log.debug "Configuring Reporting and Bindings." + + if (isJascoProducts()) { + device.updateDataValue("divisor", "10") + device.updateDataValue("energyDivisor", "10000") + } else { + device.updateDataValue("divisor", "1") + device.updateDataValue("energyDivisor", "100") + } + + sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + return refresh() + + zigbee.onOffConfig() + + zigbee.levelConfig() + + zigbee.simpleMeteringPowerConfig() + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 1, 600, 1) +} + +private boolean isJascoProducts() { + device.getDataValue("manufacturer") == "Jasco Products" +} \ No newline at end of file From ffa0b5ed083c73f071ce90daf769f5b0354bc507 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 9 Feb 2021 14:41:28 +0100 Subject: [PATCH 178/422] [ICP-14010] Change icon for Leviton DG3HL (#57677) * Change icon for Leviton DG3HL * Shortening of fingerpring --- devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index dd7b9b783d7..6b0a8220917 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -82,7 +82,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Plug-In Dimmer fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL1KD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Dimmer Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "ZSD07", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF 0-10V Dimming Wall Switch - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0301, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch" //Leviton Zigbee Plug-in DImmer DG3HL + fingerprint manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-in DImmer DG3HL, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 01 0019 fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 // LINKIND From a4d8d1f7bee83c6762e907fb7b69553f4c0ad921 Mon Sep 17 00:00:00 2001 From: MGoralczykS <42434140+MGoralczykS@users.noreply.github.com> Date: Tue, 9 Feb 2021 19:33:24 +0100 Subject: [PATCH 179/422] [ICP-13928] Delete Qubino 1D Relay model from defaultEndpoint() (#57678) * Add basicSet to update status when manual action is preformed * Space fix * Delete Qubino 1D Relay model from defaultEndpoint() --- .../qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index 85970314057..a23cbf7f262 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -273,7 +273,7 @@ def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cm } def defaultEndpoint() { - if (zwaveInfo?.model?.equals("0052") || zwaveInfo?.model?.equals("0053")) { + if (zwaveInfo?.model?.equals("0052")) { return null } else { return 1 From 6acc17c9b82a7b0d12b33a1a848bd4d4b4ceadcb Mon Sep 17 00:00:00 2001 From: Jack Kilby Date: Thu, 11 Feb 2021 07:58:43 +0800 Subject: [PATCH 180/422] Add ThirdReality Zigbee WaterLeak Sensor DeviceHandler into SmartThings. (#55832) * Add ThirdReality Zigbee WaterLeak Sensor DeviceHandler into SmartThings. * Resolve conversion of "standardize tabs/spaces" and "parse apart from startsWith". * Add binding cmd in config procedure. * Use MACRO instead of hex strings and Optimize code structure of parse. * Define 0x8021 Bind cluster as MACRO. * change 0x0006 cluster as MACRO ONOFF_CLUSTER. * Solve a bug of conflict between On/Off and Wet/Dry. * Change "onoff_state" variable name into "buzzing_state". * Modify format. Co-authored-by: jiang shanyang --- .../thirdreality-waterleak-sensor.groovy | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy diff --git a/devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy b/devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy new file mode 100644 index 00000000000..5a931611382 --- /dev/null +++ b/devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy @@ -0,0 +1,116 @@ +/** + * ThirdReality WaterLeak Sensor + * + * Copyright 2021 THIRDREALITY + * + * 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: "ThirdReality WaterLeak Sensor", namespace: "smartthings", author: "THIRDREALITY", cstHandler: true) { + capability "Battery" + capability "Switch" + capability "Water Sensor" + capability "Refresh" + capability "Configuration" + + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0500", outClusters: "0006,0019", manufacturer:"Third Reality, Inc", model:"3RWS18BZ", deviceJoinName: "ThirdReality Water Leak Sensor" //ThirdReality WaterLeak Sensor + } + + simulator { + // When simulating, define status and reply messages here + } + + tiles { //Seems no use + // define your main and details tiles here + main("water") + details(["water", "battery", "switch"]) + } +} + +def getBIND_CLUSTER() {0x8021} + +// parse events into attributes +def parse(String description) { + log.trace "[parse] Parsing '${description}'" + def resMap = [:] + + if (description?.startsWith("zone status")) { + resMap = createEvent(name: "water", value: zigbee.parseZoneStatus(description).isAlarm1Set() ? "wet" : "dry") + if (getDataValue("buzzing_state") != "on") { + sendEvent(name: "switch", value: zigbee.parseZoneStatus(description).isAlarm1Set() ? "on":"off") + } + } else if (description?.startsWith("on/off")) { + resMap = zigbee.getEvent(description) + updateDataValue("buzzing_state", resMap.value) + sendEvent(resMap) + } else if (description?.startsWith("read attr") || description?.startsWith("catchall")) { + def descMap = zigbee.parseDescriptionAsMap(description) + log.trace "[parse] descMap: ${descMap}" + + if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap?.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS) { //Water: Zone Status + resMap = createEvent(name: "water", value: (descMap.value=="0000") ? "dry" : "wet") + } else if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER ) { //Battery: Power Config + if (descMap?.attrInt == 0x0021) { + resMap = createEvent(getBatteryPercentageResult(Integer.parseInt(descMap.value, 16))) + } else if (descMap?.attrInt == 0x0020) { + log.debug "[parse] Got Battery Voltage Value: 0x${descMap.value} * 100mV" + } + } else if (descMap?.clusterInt == zigbee.ONOFF_CLUSTER) { + if (descMap?.attrInt == 0x0000) { //Switch: On/Off + resMap = createEvent(name: "switch", value: (descMap.value=="00") ? "off" : "on") + } else if (descMap?.commandInt == 0x0B) { + log.trace "[parse] Cmd On/Off" + } + } else if (descMap?.clusterInt == BIND_CLUSTER) { //Bind Rsp + log.trace "[parse] got Bind Rsp" + } else { + log.warn "[WARN][parse] Unknown descMap: $descMap" + } + } else { + log.warn "[WARN][parse] Unknown description: $description" + } + + log.trace "[parse] return '${resMap}'" + return resMap +} + +// handle commands +def configure() { + log.trace "[configure]" + updateDataValue("buzzing_state", "off") + def enrollCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.readAttribute(0x0006, 0x0000) + return zigbee.addBinding(zigbee.IAS_ZONE_CLUSTER) + enrollCmds +} + +def refresh() { + log.trace "[refresh]" + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.readAttribute(0x0006, 0x0000) +} + +def on() { + log.trace "[on]" + zigbee.on() +} + +def off() { + log.trace "[off]" + zigbee.off() +} + +def getBatteryPercentageResult(rawValue) { + def result = [:] + if (0 <= rawValue && rawValue <= 200) { + result.name = 'battery' + result.translatable = true + result.value = Math.round(rawValue) + result.descriptionText = "${device.displayName} battery was ${result.value}%" + } + return result +} From c680784366abf71833586b2655e835820546dbd3 Mon Sep 17 00:00:00 2001 From: vision-raytseng <69944522+vision-raytseng@users.noreply.github.com> Date: Thu, 11 Feb 2021 15:31:54 +0800 Subject: [PATCH 181/422] DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision 4-in-1 sensor (#52125) * DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision 4-in-1 sensor * DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision 4-in-1 sensor * Modifying 'DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision 4-in-1 sensor' * Modifying 'DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision 4-in-1 sensor' * Modifying 'DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision 4-in-1 sensor' * Add Raw description in a comment after fingerprint. * Fixed. * Modified the format. * Add the Raw description comment in the fingerprint. * Fixed the indentations in parameter7options and parameter7enumMap. * Using tab to fix the indentations in parameter7options and parameter7enumMap. * Only include these command version that are parsed and handled. * Replace my conversion functions to scaledConfigurationValue in configuration set/report. * Refactored the configuration parameters and wake up info parameter. --- .../vision-4-in-1-motion-sensor.groovy | 377 ++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 devicetypes/vision-raytseng/vision-4-in-1-motion-sensor.src/vision-4-in-1-motion-sensor.groovy diff --git a/devicetypes/vision-raytseng/vision-4-in-1-motion-sensor.src/vision-4-in-1-motion-sensor.groovy b/devicetypes/vision-raytseng/vision-4-in-1-motion-sensor.src/vision-4-in-1-motion-sensor.groovy new file mode 100644 index 00000000000..e69ae2b90c3 --- /dev/null +++ b/devicetypes/vision-raytseng/vision-4-in-1-motion-sensor.src/vision-4-in-1-motion-sensor.groovy @@ -0,0 +1,377 @@ +/** + * Vision 4-in-1 Motion Sensor + * + * Author: Ray Tseng + */ +metadata { + definition (name: "Vision 4-in-1 Motion Sensor", namespace: "vision-raytseng", author: "Ray Tseng", vid: "generic-motion-8", ocfDeviceType: "x.com.st.d.sensor.motion") { + capability "Battery" + capability "Motion Sensor" + capability "Relative Humidity Measurement" + capability "Temperature Measurement" + capability "Illuminance Measurement" + capability "Tamper Alert" + capability "Health Check" + + fingerprint mfr:"0109", prod:"2021", model:"2112", deviceJoinName: "Vision Multipurpose Sensor" // Raw description: zw:Ss2a type:0701 mfr:0109 prod:2021 model:2112 ver:32.32 zwv:7.13 lib:03 cc:5E,22,98,9F,6C,55 sec:85,59,80,70,5A,7A,87,8E,72,71,73,31,86,84 + } + + preferences { + input title: "", description: "Vision 4-in-1 Motion Sensor", type: "paragraph", element: "paragraph", displayDuringSetup: true, required: true + parameterMap().each { + input name: it.name, + title: it.title, + description: it.description, + type: it.type, + options: (it.type == "enum")? it.options: null, + range: (it.type == "number")? it.options: null, + defaultValue: it.default, + required: true, displayDuringSetup: true + } + + input title: "", description: "Wake up settings", + type: "paragraph", element: "paragraph", displayDuringSetup: true, required: true + input name: wakeUpInfoMap.name, + title: wakeUpInfoMap.title, + description: wakeUpInfoMap.description, + type: wakeUpInfoMap.type, range: wakeUpInfoMap.range, + defaultValue: wakeUpInfoMap.default, + required: true, displayDuringSetup: true + } +} + +def installed() { + def cmds = [] + + parameterMap().each { + if (state."${it.name}" == null) { state."${it.name}" = [value: it.default, refresh: true] } + } + + if (state."${wakeUpInfoMap.name}" == null) { state."${wakeUpInfoMap.name}" = [value: wakeUpInfoMap.default, refresh: true] } + + cmds += configure() + if (cmds) { + cmds += ["delay 5000", zwave.wakeUpV2.wakeUpNoMoreInformation().format()] + } + + sendEvent(name: "motion", value: "inactive") + sendEvent(name: "tamper", value: "clear") + + response(cmds) +} + +def updated() { + parameterMap().each { + if (settings."${it.name}" != null && settings."${it.name}" != state."${it.name}".value) { + state."${it.name}".value = settings."${it.name}" + state."${it.name}".refresh = true + } + } + + if (settings."${wakeUpInfoMap.name}" != null && settings."${wakeUpInfoMap.name}" != state."${wakeUpInfoMap.name}".value) { + state."${wakeUpInfoMap.name}".value = settings."${wakeUpInfoMap.name}" + state."${wakeUpInfoMap.name}".refresh = true + } +} + +def configure() { + def cmds = [] + def value + + if (device?.currentValue("temperature") == null) { + def param = parameterMap().find { it.num == 1 } + if (param != null) { + value = param.enumMap.find { it.key == state."${param.name}".value }?.value + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01, scale: value?:0x00).format() + } + } + if (device?.currentValue("illuminance") == null) { + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x03, scale: 0x00).format() + } + if (device?.currentValue("humidity") == null) { + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x05, scale: 0x00).format() + } + if (canReportBattery() || device?.currentValue("battery") == null) { + cmds << zwave.batteryV1.batteryGet().format() + } + + for (param in parameterMap()) { + if (state."${param.name}".refresh == true) { + value = (param.type == "enum")? param.enumMap.find { it.key == state."${param.name}".value }?.value: state."${param.name}".value + if (value != null) { + cmds << zwave.configurationV2.configurationSet(parameterNumber: param.num, defaultValue: false, scaledConfigurationValue: value).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: param.num).format() + if (param.num == 1) { + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01, scale: value?:0x00).format() + } + } + } + } + + if (state."${wakeUpInfoMap.name}".refresh == true) { + cmds << zwave.wakeUpV2.wakeUpIntervalSet(nodeid: zwaveHubNodeId, seconds: hour2Second(state."${wakeUpInfoMap.name}".value)).format() + cmds << zwave.wakeUpV2.wakeUpIntervalGet().format() + } + + sendEvent(name: "checkInterval", value: (hour2Second(state."${wakeUpInfoMap.name}".value) + 2 * 60) * 2, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + + return cmds ? delayBetween(cmds, 500) : [] +} + +def parameterMap() {[ + [num: 1, + name: "TemperatureUnit", + title: "Temperature Unit [°C/°F]", + description: "", + type: "enum", + options: ["°C", "°F"], + enumMap: ["°C": 0, "°F": 1], + default: "°C", + size: 1 + ], + [num: 2, + name: "TempReportWhenChanged", + title: "Report when temperature difference is over the setting [unit is 0.1°C/°F]", + description: "", + type: "number", + options: "1..50", + enumMap: [], + default: 30, + size: 1], + [num: 3, + name: "HumiReportWhenChanged", + title: "Report when humidity difference is over the setting [%]", + description: "", + type: "number", + options: "1..50", + enumMap: [], + default: 20, + size: 1 + ], + [num: 4, + name: "LightReportWhenChanged", + title: "Report when illuminance difference is over the setting [%](1% is approximately equal to 4.5 lux)", + description: "", + type: "number", + options: "5..50", + enumMap: [], + default: 25, + size: 1 + ], + [num: 5, + name: "MotionRestoreTime", + title: "Motion inactive report time [Minutes] after active", + description: "", + type: "number", + options: "1..127", + enumMap: [], + default: 3, + size: 1 + ], + [num: 6, + name: "MotionSensitivity", + title: "Motion active sensitivity", + description: "", + type: "enum", + options: ["Highest", "Higher", "High", "Medium", "Low", "Lower", "Lowest"], + enumMap: ["Highest": 1, "Higher": 2, "High": 3, "Medium": 4, "Low": 5, "Lower": 6, "Lowest": 7], + default: "Medium", + size: 1 + ], + [num: 7, + name: "LedDispMode", + title: "LED display mode", + description: "", + type: "enum", + options: ["LED off when Temperature report/Motion active", + "LED blink when Temperature report/Motion active", + "LED blink when Motion active/LED off when Temperature report"], + enumMap: ["LED off when Temperature report/Motion active": 1, + "LED blink when Temperature report/Motion active": 2, + "LED blink when Motion active/LED off when Temperature report": 3], + default: "LED off when Temperature report/Motion active", + size: 1 + ], + [num: 8, + name: "RetryTimes", + title: "Motion notification retry times", + description: "", + type: "number", + options: "0..10", + enumMap: [], + default: 3, + size: 1 + ] + ] +} + +def getWakeUpInfoMap() { + [ + name: "wakeUpInterval", + title: "Wake up interval [Hours]", + description: "", + type: "number", + range : "1..4660", + default: 24 + ] +} + +private getCommandClassVersions() { + [ + 0x80: 1, + 0x70: 2, + 0X31: 5, + 0x71: 3, + 0x84: 2 + ] +} + +def parse(String description) { + def result = [] + def cmd = zwave.parse(description, commandClassVersions) + + if (cmd) { + result += zwaveEvent(cmd) + } else { + logDebug "Unable to parse description: ${description}" + } + + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + def cmds = [] + + cmds += configure() + if (cmds) { + cmds << "delay 5000" + } + cmds << zwave.wakeUpV2.wakeUpNoMoreInformation().format() + + return cmds ? response(cmds) : [] +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + if (cmd.nodeid == zwaveHubNodeId) { + if (state."${wakeUpInfoMap.name}".value == (cmd.seconds / 3600)) { + state."${wakeUpInfoMap.name}".refresh = false + } + } + [] +} + +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 + } + + state.lastBatteryReport = new Date().time + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def param = parameterMap().find { it.num == cmd.parameterNumber } + + if (param != null && param.size != null && param.size == cmd.size) { + def value = (param.type == "enum")? param.enumMap.find { it.value == cmd.scaledConfigurationValue }?.key: cmd.scaledConfigurationValue + if (value != null && value == state."${param.name}".value) { + state."${param.name}".refresh = false + } + } + [] +} + +def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + def result = [] + + if (cmd.notificationType == 0x07) { + if (cmd.eventParametersLength) { + cmd.eventParameter.each { + if (it == 0x03) { + result = createEvent(name: "tamper", value: "clear") + } else if( it == 0x08) { + result = createEvent(name: "motion", value: "inactive") + } + } + } else if (cmd.event == 0x03) { + result = createEvent(name: "tamper", value: "detected") + } else if (cmd.event == 0x08) { + result = createEvent(name: "motion", value: "active") + } + } + + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def map = [:] + + switch (cmd.sensorType) { + case 0x01: + map.name = "temperature" + map.value = cmd.scaledSensorValue + map.unit = cmd.scale == 0 ? "C": "F" + break + case 0x03: + map.name = "illuminance" + map.value = getLuxFromPercentage(cmd.scaledSensorValue) + map.unit = "lux" + break + case 0x05: + map.name = "humidity" + map.value = cmd.scaledSensorValue + map.unit = "%" + break + default: + map.descriptionText = cmd.toString() + break + } + + createEvent(map) +} + +def getBatteryReportIntervalSeconds() { + return 8 * 3600 +} + +def canReportBattery() { + def reportEveryMS = (getBatteryReportIntervalSeconds() * 1000) + + return (!state.lastBatteryReport || ((new Date().time) - state?.lastBatteryReport > reportEveryMS)) +} + +def hour2Second(hour) { + return hour * 3600 +} + +private getLuxFromPercentage(percentageValue) { + def multiplier = luxConversionData.find { + percentageValue >= it.min && percentageValue <= it.max + }?.multiplier ?: 5.312 + def luxValue = percentageValue * multiplier + Math.round(luxValue) +} + +private getLuxConversionData() {[ + [min: 0, max: 9.99, multiplier: 3.843], + [min: 10, max: 19.99, multiplier: 5.231], + [min: 20, max: 29.99, multiplier: 4.999], + [min: 30, max: 39.99, multiplier: 4.981], + [min: 40, max: 49.99, multiplier: 5.194], + [min: 50, max: 59.99, multiplier: 6.016], + [min: 60, max: 69.99, multiplier: 4.852], + [min: 70, max: 79.99, multiplier: 4.836], + [min: 80, max: 89.99, multiplier: 4.613], + [min: 90, max: 100, multiplier: 4.5] +]} + +def logDebug(msg) { + log.debug "${msg}" +} + From 0ebdfd7c6dc6a338e323e513e3fe2403988d02ca Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Tue, 16 Feb 2021 19:48:00 +0100 Subject: [PATCH 182/422] [ICP-14023] Everspring Radiator Thermostat - additional battery poll on device power up (#58285) * Additional battery poll on device power up * Added proper comment describing notification --- .../zwave-radiator-thermostat.groovy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index 52ddcab095b..f9375e14b5f 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -203,6 +203,12 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelR createEvent(name: "temperature", value: convertTemperatureIfNeeded(cmd.scaledSensorValue, deviceTemperatureScale, cmd.precision), unit: temperatureScale) } +def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + // Power Management - Power has been applied + if (cmd.notificationType == 0x08 && cmd.event == 0x01) + [response(zwave.batteryV1.batteryGet())] +} + def zwaveEvent(physicalgraph.zwave.Command cmd) { log.warn "Unhandled command: ${cmd}" [:] From fcf6d91ee5085d1d3e6be2e98f1787cefd577dcc Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Wed, 17 Feb 2021 20:52:32 +0100 Subject: [PATCH 183/422] [ICP-13745] Added maxCodes value for Yale Fingerprint Lock (#58363) --- .../smartthings/zigbee-lock.src/zigbee-lock.groovy | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 705d444e569..5d82295a219 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -100,6 +100,8 @@ private getDOORLOCK_ATTR_SEND_PIN_OTA() { 0x0032 } private getALARM_ATTR_ALARM_COUNT() { 0x0000 } private getALARM_CMD_ALARM() { 0x00 } +private getYALE_FINGERPRINT_MAX_CODES() { 0x1E } + /** * Called on app installed */ @@ -512,7 +514,7 @@ private def parseAttributeResponse(String description) { def maxCodeLength = Integer.parseInt(descMap.value, 16) responseMap = [name: "maxCodeLength", value: maxCodeLength, descriptionText: "Maximum PIN length is ${maxCodeLength}", displayed: false] } else if (clusterInt == CLUSTER_DOORLOCK && attrInt == DOORLOCK_ATTR_NUM_PIN_USERS && descMap.value) { - def maxCodes = Integer.parseInt(descMap.value, 16) + def maxCodes = isYaleFingerprintLock() ? YALE_FINGERPRINT_MAX_CODES : Integer.parseInt(descMap.value, 16) responseMap = [name: "maxCodes", value: maxCodes, descriptionText: "Maximum Number of user codes supported is ${maxCodes}", displayed: false] } else { log.trace "ZigBee DTH - parseAttributeResponse() - ignoring attribute response" @@ -1139,6 +1141,10 @@ def isYaleLock() { return "Yale" == device.getDataValue("manufacturer") } +def isYaleFingerprintLock() { + return "ASSA ABLOY iRevo" == device.getDataValue("manufacturer") && ("iZBModule01" || "c700000202" || "0700000001" == device.getDataValue("model")) +} + /** * Utility function to check for specific models of Yale Lock that don't report battery correctly * From a41e9d14139b012719d6c18e85f04145abab43e0 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:51:56 +0100 Subject: [PATCH 184/422] ICP-14033 - removes fingerprint: Enbrighten, in-Wall Smart Dimmer With Energy Monitoring 43082 (#58450) --- .../zigbee-dimmer-power.src/zigbee-dimmer-power.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy b/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy index 0502ab3dfd6..6c7bc321119 100644 --- a/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy +++ b/devicetypes/smartthings/zigbee-dimmer-power.src/zigbee-dimmer-power.groovy @@ -34,9 +34,6 @@ metadata { // Sengled fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-CIA19NAE26", deviceJoinName: "Sengled Light" //Sengled Element touch - - // Enbrighten/Jasco - fingerprint manufacturer: "Jasco Products", model: "43082", deviceJoinName: "Enbrighten Switch" //Enbrighten, in-Wall Smart Dimmer With Energy Monitoring 43082, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0702 0B05 02 000A 0019 } tiles(scale: 2) { From 5d7462e7e2332630db67e2e141a21b57b9ff61e4 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Thu, 18 Feb 2021 20:53:03 +0100 Subject: [PATCH 185/422] ICP-14035 - changes fingerprint/model to 'Sonesse 30 WF Roller' (#58518) --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2d8dcf69cfd..ee385f3c572 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -33,7 +33,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "KG0001", deviceJoinName: "Window Treatment" //Smart Curtain Motor(BCM300D) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "DY0010", deviceJoinName: "Window Treatment" //Smart Curtain Motor(DT82TV) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Ultra Curtain", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Sonesse 30 WF Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack } preferences { From 48cb6e8307db894586f326526269f55b6e6be54d Mon Sep 17 00:00:00 2001 From: Chris Baumler Date: Tue, 23 Feb 2021 17:02:28 -0600 Subject: [PATCH 186/422] Add firmware update capability to SmartSense motion sensor --- .../smartsense-motion-sensor.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index dcb414b2765..9f7746c193e 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -17,7 +17,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus import physicalgraph.zigbee.zcl.DataType metadata { - definition(name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, mnmn: "SmartThings", vid: "generic-motion", genericHandler: "Zigbee") { + definition(name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: true, mnmn: "SmartThings", vid: "generic-motion", genericHandler: "Zigbee") { capability "Motion Sensor" capability "Configuration" capability "Battery" @@ -25,6 +25,7 @@ metadata { capability "Refresh" capability "Health Check" capability "Sensor" + capability "Firmware Update" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S", deviceJoinName: "Motion Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Motion_Sensor" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Motion_Sensor" From a650b9d970b52ae8cb817a1747f113b7070a1d26 Mon Sep 17 00:00:00 2001 From: Jack Kilby Date: Fri, 26 Feb 2021 02:33:09 +0800 Subject: [PATCH 187/422] =?UTF-8?q?Delete=20ThirdReality=20Water=20Leak=20?= =?UTF-8?q?Sensor=20=E2=80=98Switch'=20func.=20for=20consumer=20confusion.?= =?UTF-8?q?=20(#58826)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Delete ‘Switch' func. for consumer confusion. * Merge Thirdreality WaterLeak Sensor into existed DeviceHandler. Co-authored-by: jiang shanyang --- .../orvibo-Moisture-Sensor.groovy | 51 +++++--- .../thirdreality-waterleak-sensor.groovy | 116 ------------------ 2 files changed, 36 insertions(+), 131 deletions(-) delete mode 100644 devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-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 index a08d137453d..f7addb7e9f6 100644 --- a/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy +++ b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy @@ -34,6 +34,7 @@ metadata { fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001, 0009", outClusters: "0019", manufacturer: "Heiman", model: "2f077707a13f4120846e0775df7e2efe", deviceJoinName: "Orvibo Water Leak Sensor" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001, 0009", outClusters: "0019", manufacturer: "HEIMAN", model: "da2edf1ded0d44e1815d06f45ce02029", deviceJoinName: "Orvibo Water Leak Sensor" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001", manufacturer: "HEIMAN", model: "WaterSensor-N", deviceJoinName: "HEIMAN Water Leak Sensor" //HEIMAN Water Leakage Sensor (HS3WL-E) + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0006,0019", manufacturer:"Third Reality, Inc", model:"3RWS18BZ", deviceJoinName: "ThirdReality Water Leak Sensor" //ThirdReality WaterLeak Sensor } simulator { @@ -103,26 +104,41 @@ def ping() { 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 manufacturer = getDataValue("manufacturer") + if (manufacturer == "Third Reality, Inc") { + return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + } else { + 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: 6 * 60 * 60 + 5 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + def manufacturer = getDataValue("manufacturer") + if (manufacturer != "Third Reality, Inc") { + sendEvent(name: "checkInterval", value: 6 * 60 * 60 + 5 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + } } def configure() { - sendEvent(name: "checkInterval", value: 6 * 60 * 60 + 5 * 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 manufacturer = getDataValue("manufacturer") + + if (manufacturer == "Third Reality, Inc") { + def enrollCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + return zigbee.addBinding(zigbee.IAS_ZONE_CLUSTER) + enrollCmds + } else { + sendEvent(name: "checkInterval", value: 6 * 60 * 60 + 5 * 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) { @@ -139,14 +155,19 @@ def getMoistureResult(description) { def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage" def result = [:] + def manufacturer = getDataValue("manufacturer") if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - result.value = Math.round(rawValue / 2) + if (manufacturer == "Third Reality, Inc") { + result.value = Math.round(rawValue) + } else { + 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 diff --git a/devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy b/devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy deleted file mode 100644 index 5a931611382..00000000000 --- a/devicetypes/smartthings/thirdreality-waterleak-sensor.src/thirdreality-waterleak-sensor.groovy +++ /dev/null @@ -1,116 +0,0 @@ -/** - * ThirdReality WaterLeak Sensor - * - * Copyright 2021 THIRDREALITY - * - * 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: "ThirdReality WaterLeak Sensor", namespace: "smartthings", author: "THIRDREALITY", cstHandler: true) { - capability "Battery" - capability "Switch" - capability "Water Sensor" - capability "Refresh" - capability "Configuration" - - fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0500", outClusters: "0006,0019", manufacturer:"Third Reality, Inc", model:"3RWS18BZ", deviceJoinName: "ThirdReality Water Leak Sensor" //ThirdReality WaterLeak Sensor - } - - simulator { - // When simulating, define status and reply messages here - } - - tiles { //Seems no use - // define your main and details tiles here - main("water") - details(["water", "battery", "switch"]) - } -} - -def getBIND_CLUSTER() {0x8021} - -// parse events into attributes -def parse(String description) { - log.trace "[parse] Parsing '${description}'" - def resMap = [:] - - if (description?.startsWith("zone status")) { - resMap = createEvent(name: "water", value: zigbee.parseZoneStatus(description).isAlarm1Set() ? "wet" : "dry") - if (getDataValue("buzzing_state") != "on") { - sendEvent(name: "switch", value: zigbee.parseZoneStatus(description).isAlarm1Set() ? "on":"off") - } - } else if (description?.startsWith("on/off")) { - resMap = zigbee.getEvent(description) - updateDataValue("buzzing_state", resMap.value) - sendEvent(resMap) - } else if (description?.startsWith("read attr") || description?.startsWith("catchall")) { - def descMap = zigbee.parseDescriptionAsMap(description) - log.trace "[parse] descMap: ${descMap}" - - if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap?.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS) { //Water: Zone Status - resMap = createEvent(name: "water", value: (descMap.value=="0000") ? "dry" : "wet") - } else if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER ) { //Battery: Power Config - if (descMap?.attrInt == 0x0021) { - resMap = createEvent(getBatteryPercentageResult(Integer.parseInt(descMap.value, 16))) - } else if (descMap?.attrInt == 0x0020) { - log.debug "[parse] Got Battery Voltage Value: 0x${descMap.value} * 100mV" - } - } else if (descMap?.clusterInt == zigbee.ONOFF_CLUSTER) { - if (descMap?.attrInt == 0x0000) { //Switch: On/Off - resMap = createEvent(name: "switch", value: (descMap.value=="00") ? "off" : "on") - } else if (descMap?.commandInt == 0x0B) { - log.trace "[parse] Cmd On/Off" - } - } else if (descMap?.clusterInt == BIND_CLUSTER) { //Bind Rsp - log.trace "[parse] got Bind Rsp" - } else { - log.warn "[WARN][parse] Unknown descMap: $descMap" - } - } else { - log.warn "[WARN][parse] Unknown description: $description" - } - - log.trace "[parse] return '${resMap}'" - return resMap -} - -// handle commands -def configure() { - log.trace "[configure]" - updateDataValue("buzzing_state", "off") - def enrollCmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.readAttribute(0x0006, 0x0000) - return zigbee.addBinding(zigbee.IAS_ZONE_CLUSTER) + enrollCmds -} - -def refresh() { - log.trace "[refresh]" - return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) + zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER,zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + zigbee.readAttribute(0x0006, 0x0000) -} - -def on() { - log.trace "[on]" - zigbee.on() -} - -def off() { - log.trace "[off]" - zigbee.off() -} - -def getBatteryPercentageResult(rawValue) { - def result = [:] - if (0 <= rawValue && rawValue <= 200) { - result.name = 'battery' - result.translatable = true - result.value = Math.round(rawValue) - result.descriptionText = "${device.displayName} battery was ${result.value}%" - } - return result -} From 83f370efd55894b920cae48a14198a3be68f4e7e Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Thu, 4 Mar 2021 23:57:22 -0800 Subject: [PATCH 188/422] Resolves Issue with Non-Secure Fixes issue where Sound/Volume cannot be configured if paired with No Security (S2_FAILED or No Security level) --- .../aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy index 8abe105510c..e4d2628b8cc 100644 --- a/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy +++ b/devicetypes/smartthings/aeotec-doorbell-siren-6.src/aeotec-doorbell-siren-6.groovy @@ -347,7 +347,7 @@ private mcEncap(cmd) { device.updateSetting("sirenDoorbellSend", [value:"false",type:"bool"]) //reset preference toggle button when leaving setting page return response(zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()) //used to process Sound Switch Configuration SET } else { - cmd.format() + return response(cmd.format()) //used to process Sound Switch Configuration SET when S2_FAILED or non-secure. } } } From ff5cf1e944d610d32fc23e5489c91fcc87289700 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Mon, 8 Mar 2021 21:32:46 +0100 Subject: [PATCH 189/422] ICP-13724, ICP-14062 - Somfy remotes / Fix for configuration, handling button events from all endpoints (#59763) * ICP-13724, ICP-14062 - fix for configuration (Somfy Situo 1 and Situo 4), handling button events from all endpoints (Situo 4), code refactoring. * ICP-14062 - updates vid for Somfy Situo 4 * ICP-14062 - updates vid for Somfy Situo 4 --- .../ikea-button.src/ikea-button.groovy | 77 +++++++++++++++---- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index df6ef22e976..0463afe45b8 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -31,7 +31,7 @@ metadata { fingerprint inClusters: "0000, 0001, 0003, 0009, 0102, 1000, FC7C", outClusters: "0003, 0004, 0006, 0008, 0019, 0102, 1000", manufacturer:"IKEA of Sweden", model: "TRADFRI on/off switch", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_On/Off_Switch" //IKEA TRÅDFRI On/Off switch fingerprint manufacturer: "IKEA of Sweden", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote fingerprint manufacturer: "KE", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote - fingerprint manufacturer: "SOMFY", model: "Situo 4 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 + fingerprint manufacturer: "SOMFY", model: "Situo 4 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_Situo4_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 fingerprint manufacturer: "SOMFY", model: "Situo 1 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 } @@ -72,10 +72,29 @@ private getOPENCLOSE_BUTTONS() { DOWN:2] } -private getOPENCLOSESTOP_BUTTONS() { - [UP:1, - STOP:2, - DOWN:3 +private getOPENCLOSESTOP_BUTTONS_ENDPOINTS() { + [ + 1: [UP:1, + STOP:2, + DOWN:3], + 2: [UP:4, + STOP:5, + DOWN:6], + 3: [UP:7, + STOP:8, + DOWN:9], + 4: [UP:10, + STOP:11, + DOWN:12] + ] +} + +private getBUTTON_NUMBER_ENDPOINT() { + [ + 1: 1, 2: 1, 3: 1, + 4: 2, 5: 2, 6: 2, + 7: 3, 8: 3, 9: 3, + 10:4, 11: 4, 12: 4 ] } @@ -123,8 +142,14 @@ private getButtonLabel(buttonNum) { label = ikeaOnOffSwitchNames[buttonNum - 1] } else if (isIkeaOpenCloseRemote()) { label = openCloseRemoteNames[buttonNum - 1] - } else if (isSomfySituo()) { - label = openCloseStopRemoteNames[buttonNum - 1] + } else if (isSomfy()) { + // UP, STOP, DOWN events in "Somfy Situo 4" come from 4 endpoints, so there are 12 child buttons + // endpoint 1: buttons 1-3, enpoint 2: buttons 4-6, endpoint 3: buttons 7-9, endpoint 4: buttons 10-12 + // Situo 1 reports from endpoint 1 only + def endpoint = BUTTON_NUMBER_ENDPOINT[buttonNum] + def buttonNameIdx = (buttonNum - 1)%3 + def buttonName = openCloseStopRemoteNames[buttonNameIdx] + label = "endpoint $endpoint $buttonName" } return label @@ -141,7 +166,7 @@ private void createChildButtonDevices(numberOfButtons) { for (i in 1..numberOfButtons) { log.debug "Creating child $i" - def supportedButtons = ((isIkeaRemoteControl() && i == REMOTE_BUTTONS.MIDDLE) || isIkeaOpenCloseRemote() || isSomfySituo()) ? ["pushed"] : ["pushed", "held"] + def supportedButtons = ((isIkeaRemoteControl() && i == REMOTE_BUTTONS.MIDDLE) || isIkeaOpenCloseRemote() || isSomfy()) ? ["pushed"] : ["pushed", "held"] def child = addChildDevice("Child Button", "${device.deviceNetworkId}:${i}", device.hubId, [completedSetup: true, label: getButtonName(i), isComponent: true, componentName: "button$i", componentLabel: getButtonLabel(i)]) @@ -159,15 +184,17 @@ def installed() { numberOfButtons = 5 } else if (isIkeaOnOffSwitch() || isIkeaOpenCloseRemote()) { numberOfButtons = 2 - } else if (isSomfySituo()) { + } else if (isSomfySituo1()) { numberOfButtons = 3 + } else if (isSomfySituo4()) { + numberOfButtons = 12 } if (numberOfButtons > 1) { createChildButtonDevices(numberOfButtons) } - def supportedButtons = isIkeaOpenCloseRemote() || isSomfySituo() ? ["pushed"] : ["pushed", "held"] + def supportedButtons = isIkeaOpenCloseRemote() || isSomfy() ? ["pushed"] : ["pushed", "held"] sendEvent(name: "supportedButtonValues", value: supportedButtons.encodeAsJSON(), displayed: false) sendEvent(name: "numberOfButtons", value: numberOfButtons, displayed: false) numberOfButtons.times { @@ -193,10 +220,17 @@ def configure() { def cmds = [] - if (isSomfySituo()) { + if (isSomfy()) { cmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, ["destEndpoint":0xE8]) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, DataType.UINT8, 30, 21600, 0x01, ["destEndpoint":0xE8]) + - zigbee.addBinding(CLUSTER_WINDOW_COVERING) + zigbee.removeBinding(zigbee.ONOFF_CLUSTER, device.zigbeeId, 0x01, device.hub.zigbeeEui, 0x01) + + zigbee.addBinding(CLUSTER_WINDOW_COVERING, ["destEndpoint":0x01]) + + if (isSomfySituo4()) { + cmds += zigbee.addBinding(CLUSTER_WINDOW_COVERING, ["destEndpoint":0x02]) + + zigbee.addBinding(CLUSTER_WINDOW_COVERING, ["destEndpoint":0x03]) + + zigbee.addBinding(CLUSTER_WINDOW_COVERING, ["destEndpoint":0x04]) + } } else { cmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x21, DataType.UINT8, 30, 21600, 0x01) + @@ -326,17 +360,18 @@ private Map getButtonEvent(Map descMap) { buttonNumber = OPENCLOSE_BUTTONS.DOWN } } - } else if (isSomfySituo() && descMap.data?.size() == 0){ + } else if (isSomfy() && descMap.data?.size() == 0){ // Somfy Situo Remotes query their shades directly after "My"(stop) button is pressed (that's intended behavior) // descMap contains 'data':['00', '00'] in such cases, so we have to ignore those redundant misinterpreted UP events if (descMap.clusterInt == CLUSTER_WINDOW_COVERING) { buttonState = "pushed" + def endpoint = Integer.parseInt(descMap.sourceEndpoint) if (descMap.commandInt == 0x00) { - buttonNumber = OPENCLOSESTOP_BUTTONS.UP + buttonNumber = OPENCLOSESTOP_BUTTONS_ENDPOINTS[endpoint].UP } else if (descMap.commandInt == 0x01) { - buttonNumber = OPENCLOSESTOP_BUTTONS.DOWN + buttonNumber = OPENCLOSESTOP_BUTTONS_ENDPOINTS[endpoint].DOWN } else if (descMap.commandInt == 0x02) { - buttonNumber = OPENCLOSESTOP_BUTTONS.STOP + buttonNumber = OPENCLOSESTOP_BUTTONS_ENDPOINTS[endpoint].STOP } } } @@ -368,10 +403,18 @@ private boolean isIkea() { isIkeaRemoteControl() || isIkeaOnOffSwitch() || isIkeaOpenCloseRemote() } -private boolean isSomfySituo() { +private boolean isSomfy() { device.getDataValue("manufacturer") == "SOMFY" } +private boolean isSomfySituo1() { + isSomfy() && device.getDataValue("model") == "Situo 1 Zigbee" +} + +private boolean isSomfySituo4() { + isSomfy() && device.getDataValue("model") == "Situo 4 Zigbee" +} + private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) From ecb221ae0beacb66435b45b5b842cc52d152efd6 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Tue, 9 Mar 2021 08:39:45 +0100 Subject: [PATCH 190/422] DevWs for frient containing containing Zigbee Metering Plug (#60110) * DevWs for frient containing containing Zigbee Metering Plug * Update zigbee-metering-plug.groovy Fix line ending encoding Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 e42d58ea6e3..64b6b97e3c8 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -167,7 +167,7 @@ private int getPowerDiv() { } private int getEnergyDiv() { - (isSengledOutlet() || isJascoProductsOutlet()) ? 10000 : 100 + (isSengledOutlet() || isJascoProductsOutlet()) ? 10000 : isFrientOutlet() ? 1000 : 100 } private boolean isSengledOutlet() { @@ -177,3 +177,7 @@ private boolean isSengledOutlet() { private boolean isJascoProductsOutlet() { device.getDataValue("manufacturer") == "Jasco Products" } + +private boolean isFrientOutlet() { + device.getDataValue("manufacturer") == "frient A/S" +} From 28b9070616b82025a214788b22d89411ac07548d Mon Sep 17 00:00:00 2001 From: jeremelau <38304013+JeremeLau@users.noreply.github.com> Date: Wed, 10 Mar 2021 15:45:58 +0800 Subject: [PATCH 191/422] DevWs for YookSmart containing containing ZigBee Window Shade Battery (#59056) * DevWs for YookSmart containing containing ZigBee Window Shade Battery * fix spacing and baterry report * fix 'invalidSameLevelEvent' thing * remove unnecessary code Co-authored-by: Bill Nie --- .../zigbee-window-shade-battery.groovy | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index 797ddd4f432..b47223f337b 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -27,8 +27,13 @@ metadata { command "pause" + // IKEA fingerprint manufacturer: "IKEA of Sweden", model: "KADRILJ roller blind", deviceJoinName: "IKEA Window Treatment" // raw description 01 0104 0202 00 09 0000 0001 0003 0004 0005 0020 0102 1000 FC7C 02 0019 1000 //IKEA KADRILJ Blinds fingerprint manufacturer: "IKEA of Sweden", model: "FYRTUR block-out roller blind", deviceJoinName: "IKEA Window Treatment" // raw description 01 0104 0202 01 09 0000 0001 0003 0004 0005 0020 0102 1000 FC7C 02 0019 1000 //IKEA FYRTUR Blinds + + // Yookee yooksmart + fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Yookee", model: "D10110", deviceJoinName: "Yookee Window Treatment" + fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "yooksmart", model: "D10110", deviceJoinName: "yooksmart Window Treatment" } preferences { @@ -87,6 +92,7 @@ private List collectAttributes(Map descMap) { if (descMap.additionalAttrs) { descMaps.addAll(descMap.additionalAttrs) } + return descMaps } @@ -184,7 +190,7 @@ def setLevel(data, rate = null) { } else { cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) } - cmd + return cmd } def pause() { @@ -236,6 +242,7 @@ def configure() { } if (reportsBatteryPercentage()) { + cmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_PERCENTAGE_REMAINING, DataType.UINT8, 30, 21600, 0x01) } @@ -271,7 +278,7 @@ private List readDeviceBindingTable() { } def supportsLiftPercentage() { - isIkeaKadrilj() || isIkeaFyrtur() + isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() } def shouldInvertLiftPercentage() { @@ -289,3 +296,7 @@ def isIkeaKadrilj() { def isIkeaFyrtur() { device.getDataValue("model") == "FYRTUR block-out roller blind" } + +def isYooksmartOrYookee() { + device.getDataValue("model") == "D10110" +} From 3756c080f766a5e6cc75e9410eb9cde1387809a0 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Thu, 11 Mar 2021 18:02:09 +0900 Subject: [PATCH 192/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#58898) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * * Remove : unnecessary space, simulator, debug message, offlinePingable: "1" * Change : private getter method instead of hardcoded numbers * * Change : adding space after if * * Remove : '/*occupancy*/ * Change : 0x18/*bitmap8*/ -> DataType.BITMAP8 * * Remove : redundant condition of zigbee.IAS_ZONE_CLUSTER * * Remove : empty line, tiles section * Change : opening bracket * Add : an empty line above to separate constants and this method * * Change : make readable code --- .../sihas-multipurpose-sensor.groovy | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy new file mode 100644 index 00000000000..0665334d685 --- /dev/null +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -0,0 +1,247 @@ +/* + * Copyright 2021 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. + */ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition (name: "SiHAS Multipurpose Sensor", namespace: "shinasys", author: "SHINA SYSTEM") { + capability "Motion Sensor" + capability "Configuration" + capability "Battery" + capability "Temperature Measurement" + capability "Illuminance Measurement" + capability "Relative Humidity Measurement" + capability "Refresh" + capability "Health Check" + capability "Sensor" + + fingerprint inClusters: "0000,0001,0003,0020,0400,0402,0405,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "USM-300Z", deviceJoinName: "SiHAS MultiPurpose Sensor", mnmn: "SmartThings", vid: "generic-motion-6" + fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" + fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "generic-humidity" + } + + preferences { + section { + input "tempOffset" , "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false + input "humidityOffset", "number", title: "Humidity offset" , description: "Enter a percentage to adjust the humidity.", range: "*..*", displayDuringSetup: false + } + } +} + +private getILLUMINANCE_MEASUREMENT_CLUSTER() { 0x0400 } +private getOCCUPANCY_SENSING_CLUSTER() { 0x0406 } +private getPOWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE() { 0x0020 } +private getTEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } +private getRALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } +private getILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } +private getOCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE() { 0x0000 } + +private List collectAttributes(Map descMap) { + List descMaps = new ArrayList() + descMaps.add(descMap) + if (descMap.additionalAttrs) { + descMaps.addAll(descMap.additionalAttrs) + } + return descMaps +} + +def parse(String description) { + Map map = zigbee.getEvent(description) + + if (!map) { + if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } else if (description?.startsWith('read attr')) { + Map descMap = zigbee.parseDescriptionAsMap(description) + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) { + List descMaps = collectAttributes(descMap) + def battMap = descMaps.find { it.attrInt == POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE } + if (battMap) { + map = getBatteryResult(Integer.parseInt(battMap.value, 16)) + } + } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap.commandInt != 0x07) { + def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 10)) + map = translateZoneStatus(zs) + } else if (descMap?.clusterInt == OCCUPANCY_SENSING_CLUSTER && descMap.attrInt == OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE && descMap?.value) { + map = getMotionResult(descMap.value == "01" ? "active" : "inactive") + } + } else if (description?.startsWith('illuminance:')) { //parse illuminance + map = parseCustomMessage(description) + } + } else if (map.name == "temperature") { + if (tempOffset) { + map.value = new BigDecimal((map.value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) + } + map.descriptionText = temperatureScale == 'C' ? "${device.displayName} temperature was ${map.value}°C" : "${device.displayName} temperature was ${map.value}°F" + map.translatable = true + } else if (map.name == "humidity") { + if (humidityOffset) { + map.value = (int) map.value + (int) humidityOffset + } + map.descriptionText = "${device.displayName} humidity was ${map.value}%" + map.translatable = true + } + + def result = map ? createEvent(map) : [:] + + if (description?.startsWith('enroll request')) { + List cmds = zigbee.enrollResponse() + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + return result +} + +private def parseCustomMessage(String description) { + return [ + name : description.split(": ")[0], + value : description.split(": ")[1], + translatable : true + ] +} + +private Map parseIasMessage(String description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + translateZoneStatus(zs) +} + +private Map translateZoneStatus(ZoneStatus zs) { + // Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion + return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') +} + +private Map getBatteryResult(rawValue) { + def linkText = getLinkText(device) + def result = [:] + def volts = rawValue / 10 + + if (!(rawValue == 0 || rawValue == 255)) { + result.name = 'battery' + result.translatable = true + def minVolts = 2.3 + def maxVolts = 3.2 + + // Get the current battery percentage as a multiplier 0 - 1 + def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0 + // Find the corresponding voltage from our range + curValVolts = curValVolts * (maxVolts - minVolts) + minVolts + // Round to the nearest 10th of a volt + curValVolts = Math.round(10 * curValVolts) / 10.0 + + // Only update the battery reading if we don't have a last reading, + // OR we have received the same reading twice in a row + // OR we don't currently have a battery reading + // OR the value we just received is at least 2 steps off from the last reported value + if (state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) { + def pct = (volts - minVolts) / (maxVolts - minVolts) + def roundedPct = Math.round(pct * 100) + if (roundedPct <= 0) + roundedPct = 1 + result.value = Math.min(100, roundedPct) + } else { + // Don't update as we want to smooth the battery values, but do report the last battery state for record keeping purposes + result.value = device.currentState("battery").value + } + + result.descriptionText = "${device.displayName} battery was ${result.value}%" + state.lastVolts = volts + } + return result +} + +private Map getMotionResult(value) { + String descriptionText = value == 'active' ? "${device.displayName} detected motion" : "${device.displayName} motion has stopped" + return [ + name : 'motion', + value : value, + descriptionText: descriptionText, + translatable : true + ] +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) +} + +def refresh() { + def refreshCmds = [] + + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) + + if (isUSM300() || isTSM300()) { + refreshCmds += zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) + refreshCmds += zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) + } + + if (isUSM300()) { + refreshCmds += zigbee.readAttribute(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) + } + + if (isUSM300() || isOSM300()) { + refreshCmds += zigbee.readAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE) + refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + refreshCmds += zigbee.enrollResponse() + } + + return refreshCmds +} + +def configure() { + def configCmds = [] + + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) + sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity + // battery minReport 30 seconds, maxReportTime 6 hrs by default + // humidity minReportTime 30 seconds, maxReportTime 60 min + // illuminance minReportTime 30 seconds, maxReportTime 60 min + // occupancy sensing minReportTime 10 seconds, maxReportTime 60 min + // ex) zigbee.configureReporting(0x0001, 0x0020, DataType.UINT8, 600, 21600, 0x01) + // This is for cluster 0x0001 (power cluster), attribute 0x0021 (battery level), whose type is UINT8, + // the minimum time between reports is 10 minutes (600 seconds) and the maximum time between reports is 6 hours (21600 seconds), + // and the amount of change needed to trigger a report is 1 unit (0x01). + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE, DataType.UINT8, 30, 21600, 0x01/*100mv*1*/) + + if (isUSM300() || isTSM300()) { + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 30, 300, 30/*30/100=0.3도*/) + configCmds += zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 30, 3600, 50/*50/100=0.5%*/) + } + + if (isUSM300()) { + configCmds += zigbee.configureReporting(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 30, 3600, 20/*20 lux*/) + } + + if (isUSM300() || isOSM300()) { + configCmds += zigbee.configureReporting(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE, DataType.BITMAP8, 1, 600, 1) + } + + return refresh() + configCmds +} + +private Boolean isUSM300() { + device.getDataValue("model") == "USM-300Z" +} + +private Boolean isTSM300() { + device.getDataValue("model") == "TSM-300Z" +} + +private Boolean isOSM300() { + device.getDataValue("model") == "OSM-300Z" +} \ No newline at end of file From 864a2f1f4023e9b1262297b37a8e306ff8be6e2b Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 15 Mar 2021 15:05:36 +0100 Subject: [PATCH 193/422] Added energysaveheat as supported value (#60111) --- .../zwave-radiator-thermostat.groovy | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index f9375e14b5f..db5a31c526d 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -170,6 +170,8 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeRepor map.value = "heat" break case 11: + map.value = "energysaveheat" + break case 15: map.value = "emergency heat" break @@ -222,11 +224,10 @@ def setThermostatMode(String mode) { modeValue = 1 break case "emergency heat": - if (isAeotecRadiatorThermostat()) { - modeValue = 15 - } else { - modeValue = 11 - } + modeValue = 15 + break + case "energysaveheat": + modeValue = 11 break case "off": modeValue = 0 @@ -328,7 +329,9 @@ private getMinHeatingSetpointTemperature() { } private getThermostatSupportedModes() { - if (isEverspringRadiatorThermostat() || isAeotecRadiatorThermostat()) { + if (isEverspringRadiatorThermostat()) { + ["off", "heat", "energysaveheat"] + } else if (isAeotecRadiatorThermostat()) { ["off", "heat", "emergency heat"] } else if (isPoppRadiatorThermostat()) { //that's just for looking fine in Classic ["heat"] From 67c429eb1e4d054492ae109e8febe8150885a299 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Tue, 16 Mar 2021 17:08:33 +0900 Subject: [PATCH 194/422] WWST-5862, added FP for the SAMSUMG LED (#60609) * Update zigbee-white-color-temperature-bulb.groovy * Update zigbee-white-color-temperature-bulb.groovy * Update zigbee-white-color-temperature-bulb.groovy --- .../zigbee-white-color-temperature-bulb.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 9bc8bad500b..042089e2e5c 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -38,6 +38,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" + // Samsung LED + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-001", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" //ITM CCT + // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010C", manufacturer: "AduroSmart Eria", model: "AD-ColorTemperature3001", deviceJoinName: "Eria Light" //Eria ZigBee Color Temperature Bulb From 6eb1d2adde0ab54ea3d4ee8042c070fdd7d24720 Mon Sep 17 00:00:00 2001 From: Konrad Klimczuk Date: Tue, 16 Mar 2021 10:19:46 +0100 Subject: [PATCH 195/422] ICP-14076, ICP-14077 - disables local execution --- .../zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy b/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy index 0daad08df8f..1c67c7b9fb3 100644 --- a/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy @@ -13,7 +13,7 @@ */ import physicalgraph.zigbee.zcl.DataType metadata { - definition (name: "ZigBee Metering Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch", mnmn: "SmartThings", vid:"generic-dimmer-power-energy", runLocally: true, executeCommandsLocally: true, genericHandler: "Zigbee", minHubCoreVersion: '000.019.00012') { + definition (name: "ZigBee Metering Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch", mnmn: "SmartThings", vid:"generic-dimmer-power-energy") { capability "Actuator" capability "Configuration" From 6dc266a15c4109a385848fbb632fb1d492394f34 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Thu, 18 Mar 2021 10:31:51 -0700 Subject: [PATCH 196/422] BUG-1910 Garage Door events being duplicated (#60782) * BUG-1910 Garage Door events being duplicated The Garage Door Control and Door Control capabilities are largely redundandant with each other. We should just be able to remove the Door Control capability. * Garage Door Control is the actual deprecated capability. --- .../smartsense-garage-door-sensor-button.groovy | 2 +- .../simulated-garage-door-opener.groovy | 3 +-- .../zwave-garage-door-opener.groovy | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy index 4521db28a53..525c32b4829 100644 --- a/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy +++ b/devicetypes/smartthings/smartsense-garage-door-sensor-button.src/smartsense-garage-door-sensor-button.groovy @@ -18,7 +18,7 @@ metadata { definition (name: "SmartSense Garage Door Sensor Button", namespace: "smartthings", author: "SmartThings") { capability "Three Axis" - capability "Garage Door Control" + capability "Door Control" capability "Contact Sensor" capability "Actuator" capability "Acceleration Sensor" diff --git a/devicetypes/smartthings/testing/simulated-garage-door-opener.src/simulated-garage-door-opener.groovy b/devicetypes/smartthings/testing/simulated-garage-door-opener.src/simulated-garage-door-opener.groovy index e711df18777..7a21730450b 100644 --- a/devicetypes/smartthings/testing/simulated-garage-door-opener.src/simulated-garage-door-opener.groovy +++ b/devicetypes/smartthings/testing/simulated-garage-door-opener.src/simulated-garage-door-opener.groovy @@ -16,8 +16,7 @@ metadata { definition (name: "Simulated Garage Door Opener", namespace: "smartthings/testing", author: "SmartThings") { capability "Actuator" - capability "Door Control" - capability "Garage Door Control" + capability "Door Control" capability "Contact Sensor" capability "Refresh" capability "Sensor" diff --git a/devicetypes/smartthings/zwave-garage-door-opener.src/zwave-garage-door-opener.groovy b/devicetypes/smartthings/zwave-garage-door-opener.src/zwave-garage-door-opener.groovy index f8fab8f0827..849a3778465 100644 --- a/devicetypes/smartthings/zwave-garage-door-opener.src/zwave-garage-door-opener.groovy +++ b/devicetypes/smartthings/zwave-garage-door-opener.src/zwave-garage-door-opener.groovy @@ -17,7 +17,6 @@ metadata { definition (name: "Z-Wave Garage Door Opener", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', executeCommandsLocally: false, ocfDeviceType: "oic.d.garagedoor") { capability "Actuator" capability "Door Control" - capability "Garage Door Control" capability "Health Check" capability "Contact Sensor" capability "Refresh" From e1a5d57c3df71b3c70f80bcbb132f23e1358cbe5 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Mon, 22 Mar 2021 10:51:55 -0700 Subject: [PATCH 197/422] GPD-1238 Text change for Zen Thermostat preference (#61323) --- .../zenwithin/zen-thermostat.src/zen-thermostat.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy b/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy index b79ec7ed0a3..1f18e6084c0 100644 --- a/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy +++ b/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy @@ -93,8 +93,9 @@ metadata { preferences { section { input("systemModes", "enum", - title: "Thermostat configured modes\nSelect the modes the thermostat has been configured for, as displayed on the thermostat", - description: "off, heat, cool", defaultValue: "3", required: true, multiple: false, + title: "Thermostat configured modes", + description: "Select the modes the thermostat has been configured for, as displayed on the thermostat", + defaultValue: "3", required: true, multiple: false, options:["1":"off, heat", "2":"off, cool", "3":"off, heat, cool", From 218f38f3a3a7a57c408d6f0809d0bc4fea6f2a48 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 22 Mar 2021 20:50:19 +0100 Subject: [PATCH 198/422] WWST-7366 Integrated Aeotec MultiSensor 7 (#61244) --- .../aeon-multisensor-6.groovy | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy index 80faf0a0e64..7ce6a1a69be 100644 --- a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy +++ b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy @@ -32,6 +32,9 @@ metadata { fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A,0x5A", deviceJoinName: "Aeon Multipurpose Sensor" fingerprint mfr: "0086", prod: "0102", model: "0064", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 6 fingerprint mfr: "0086", prod: "0202", model: "0064", deviceJoinName: "Aeotec Multipurpose Sensor" //AU //Aeotec MultiSensor 6 + fingerprint mfr: "0371", prod: "0002", model: "0018", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 7 (EU) + fingerprint mfr: "0371", prod: "0102", model: "0018", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 7 (US) + fingerprint mfr: "0371", prod: "0202", model: "0018", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 7 (AU) } simulator { @@ -188,6 +191,7 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) { result << response(configure()) } else { log.debug("Device has been configured sending >> wakeUpNoMoreInformation()") + if (isAeotecMultisensor7()) cmds << zwave.configurationV1.configurationGet(parameterNumber: 10).format() cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format() result << response(cmds) } @@ -317,15 +321,26 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm result << createEvent(name: "tamper", value: "clear") break case 3: + case 9: result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName was tampered") // Clear the tamper alert after 10s. This is a temporary fix for the tamper attribute until local execution handles it unschedule(clearTamper, [forceForLocallyExecuting: true]) runIn(10, clearTamper, [forceForLocallyExecuting: true]) break case 7: + case 8: result << motionEvent(1) break } + } else if (cmd.notificationType == 8) { + switch (cmd.event) { + case 2: + result << createEvent(name: "powerSource", value: "battery", displayed: false) + break + case 3: + result << createEvent(name: "powerSource", value: "dc", displayed: false) + break + } } else { log.warn "Need to handle this cmd.notificationType: ${cmd.notificationType}" result << createEvent(descriptionText: cmd.toString(), isStateChange: false) @@ -337,7 +352,10 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport log.debug "ConfigurationReport: $cmd" def result = [] def value - if (cmd.parameterNumber == 9) { + if (isAeotecMultisensor7() && cmd.parameterNumber == 10) { + value = cmd.scaledConfigurationValue ? "dc" : "battery" + result << createEvent(name: "powerSource", value: value, displayed: false) + } else if (cmd.parameterNumber == 9) { if (cmd.configurationValue[0] == 0) { value = "dc" if (!isConfigured()) { @@ -430,6 +448,7 @@ def configure() { //8. query configuration request << zwave.configurationV1.configurationGet(parameterNumber: 9) + request << zwave.configurationV1.configurationGet(parameterNumber: 10) request << zwave.configurationV1.configurationGet(parameterNumber: 101) request << zwave.configurationV1.configurationGet(parameterNumber: 102) request << zwave.configurationV1.configurationGet(parameterNumber: 111) @@ -542,3 +561,7 @@ def getReportTypesFromValue(value) { } reportList } + +private isAeotecMultisensor7() { + zwaveInfo.model.equals("0018") +} \ No newline at end of file From 86b31308b3852028e521dfafcd4dee07ce2e22ee Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Mon, 22 Mar 2021 20:53:26 +0100 Subject: [PATCH 199/422] ICP-14003 - moving Leviton (DG3HL, DG6HD) fingerprints to the zll-dimmer-bulb DTH * ICP-14003 - fixes configuration of Leviton devices, query Leviton devices after settting level. Code refactoring. * ICP-14003 - Code refactoring. * ICP-14003 - reverts changes in configuration * ICP-14003 - moving Leviton (DG3HL, DG6HD) fingerprints to the zll-dimmer-bulb DTH --- .../zigbee-dimmer.src/zigbee-dimmer.groovy | 30 +++++++++++++++---- .../zll-dimmer-bulb.groovy | 4 +++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index 6b0a8220917..79de2e21568 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -82,8 +82,6 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Plug-In Dimmer fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL1KD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF Dimmer Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "ZSD07", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Lumina RF 0-10V Dimming Wall Switch - fingerprint manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-in DImmer DG3HL, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 01 0019 - fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 // LINKIND fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05, 1000, FC82", outClusters: "000A, 0019", manufacturer: "lk", model: "ZBT-DIMLight-GLS0010", deviceJoinName: "Linkind Light" //Linkind Dimmable A19 Bulb @@ -174,7 +172,7 @@ def parse(String description) { } else { log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" } - } else if (device.getDataValue("manufacturer") == "sengled" && descMap && descMap.clusterInt == 0x0008 && descMap.attrInt == 0x0000) { + } else if (isSengled() && descMap && descMap.clusterInt == 0x0008 && descMap.attrInt == 0x0000) { // This is being done because the sengled element touch/classic incorrectly uses the value 0xFF for the max level. // Per the ZCL spec for the UINT8 data type 0xFF is an invalid value, and 0xFE should be the max. Here we // manually handle the invalid attribute value since it will be ignored by getEvent as an invalid value. @@ -204,8 +202,10 @@ def setLevel(value, rate = null) { def additionalCmds = [] if (device.getDataValue("model") == "iQBR30" && value.toInteger() > 0) { // Handle iQ bulb not following spec additionalCmds = zigbee.on() - } else if (device.getDataValue("manufacturer") == "MRVL") { // Handle marvel stack not reporting + } else if (isMRVL()) { // Handle marvel stack not reporting additionalCmds = refresh() + } else if (isLeviton()) { + additionalCmds = zigbee.levelRefresh() } zigbee.setLevel(value) + additionalCmds } @@ -221,13 +221,33 @@ def refresh() { } def installed() { - if (((device.getDataValue("manufacturer") == "MRVL") && (device.getDataValue("model") == "MZ100")) || (device.getDataValue("manufacturer") == "OSRAM SYLVANIA") || (device.getDataValue("manufacturer") == "OSRAM")) { + if ((isMRVL() && (device.getDataValue("model") == "MZ100")) || isOsram() || isOsramSylvania()) { if ((device.currentState("level")?.value == null) || (device.currentState("level")?.value == 0)) { sendEvent(name: "level", value: 100) } } } +def isLeviton() { + device.getDataValue("manufacturer") == "Leviton" +} + +def isMRVL() { + device.getDataValue("manufacturer") == "MRVL" +} + +def isOsram() { + device.getDataValue("manufacturer") == "OSRAM" +} + +def isOsramSylvania() { + device.getDataValue("manufacturer") == "OSRAM SYLVANIA" +} + +def isSengled() { + device.getDataValue("manufacturer") == "sengled" +} + def configure() { log.debug "Configuring Reporting and Bindings." // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) diff --git a/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy b/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy index 9a62821d25a..8e90fbedd01 100644 --- a/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy +++ b/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy @@ -52,6 +52,10 @@ metadata { fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0019", manufacturer: "innr", model: "RB 175 W", deviceJoinName: "Innr Light" //Innr Smart Bulb Warm Dimming fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0019", manufacturer: "innr", model: "RB 145", deviceJoinName: "Innr Light" //Innr Smart Candle White + // Leviton + fingerprint manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-in DImmer DG3HL, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 01 0019 + fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 + // OSRAM fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 W clear", deviceJoinName: "OSRAM Light" //OSRAM SMART+ LED Smart Connected Light fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 W clear - LIGHTIFY", deviceJoinName: "OSRAM Light" //OSRAM SMART+ LED Smart Connected Light From cf19f2c6b2450b02895ab52a78888664d5688701 Mon Sep 17 00:00:00 2001 From: natec007 Date: Tue, 23 Mar 2021 14:43:20 -0700 Subject: [PATCH 200/422] DevWs for Plaid Systems LLC containing containing Spruce Controller SST (#47339) * DevWs for Plaid Systems LLC containing containing Spruce Controller SST * Add Spruce Valve which is the required child device for Spruce Controller * Update code style, remove undefined commands, update fingerprint syntax, deprecate SmartApp scheduler compatibility and replace with settings that allow standard automation use * updated zigbee commands, constants, whitespace, tab spacing * Updated vid to remove a (temporary) duplicate entry in the json * Single presentation Spruce Controller update * Update Spruce Controller with patch fix for automations and some other fixes * update format, and health check * Update hextoint, remove health check from child * remove binding since included in reporting configuration --- .../spruce-controller.groovy | 1135 +++++++---------- .../spruce-valve.src/spruce-valve.groovy | 51 + 2 files changed, 507 insertions(+), 679 deletions(-) create mode 100644 devicetypes/plaidsystems/spruce-valve.src/spruce-valve.groovy diff --git a/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy b/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy index 5c49425e5c7..64c0a54dcd4 100644 --- a/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy +++ b/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy @@ -1,9 +1,5 @@ /** - * Spruce Controller V2_4 Big Tiles * - * Copyright 2015 Plaid Systems - * - * Author: NC - * Date: 2015-11 + * Copyright 2020 PlaidSystems * * 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: @@ -14,736 +10,517 @@ * 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. * - -----------V3 updates-11-2015------------ - -Start program button updated to signal schedule check in Scheduler - 11/17 alarm "0" -> 0 (ln 305) - */ + + Version v3.5 + * update zigbee ONOFF cluster + * update Health Check + * remove binding since reporting handles this + + Version v3.4 + * update presentation with 'patch' to rename 'valve' to 'Zone x' + * remove commands on, off + * add command setValveDuration + * update settings order and description + * fix controllerStatus -> status + + Version v3.3 + * change to remotecontrol with components + * health check -> ping + + Version v3.2 + * add zigbee constants + * update to zigbee commands + * tabs and trim whitespace + + Version v3.1 + * Change to work with standard ST automation options + * use standard switch since custom attributes still don't work in automations + * Add schedule minute times to settings + * Add split cycle to settings + * deprecate Spruce Scheduler compatibility + + Version v3.0 + * Update for new Samsung SmartThings app + * update vid with status, message, rainsensor + * maintain compatibility with Spruce Scheduler + * Requires Spruce Valve as child device + + Version v2.7 + * added Rain Sensor = Water Sensor Capability + * added Pump/Master + * add "Dimmer" to Spruce zone child for manual duration + +**/ + +import groovy.json.JsonOutput +import physicalgraph.zigbee.zcl.DataType + +//dth version +def getVERSION() {'v3.5 3-2021'} +def getDEBUG() {false} +def getHC_INTERVAL_MINS() {60} +//zigbee cluster, attribute, identifiers +def getALARMS_CLUSTER() {0x0009} +def getBINARY_INPUT_CLUSTER() {0x000F} +def getON_TIME_ATTRIBUTE() {0x4001} +def getOFF_WAIT_TIME_ATTRIBUTE() {0x4002} +def getOUT_OF_SERVICE_IDENTIFIER() {0x0051} +def getPRESENT_VALUE_IDENTIFIER() {0x0055} metadata { - definition (name: 'Spruce Controller', namespace: 'plaidsystems', author: 'Plaid Systems') { - capability 'Switch' - capability 'Configuration' - capability 'Refresh' - capability 'Actuator' - capability 'Valve' - - attribute 'switch', 'string' - attribute 'switch1', 'string' - attribute 'switch2', 'string' - attribute 'switch8', 'string' - attribute 'switch5', 'string' - attribute 'switch3', 'string' - attribute 'switch4', 'string' - attribute 'switch6', 'string' - attribute 'switch7', 'string' - attribute 'switch9', 'string' - attribute 'switch10', 'string' - attribute 'switch11', 'string' - attribute 'switch12', 'string' - attribute 'switch13', 'string' - attribute 'switch14', 'string' - attribute 'switch15', 'string' - attribute 'switch16', 'string' - attribute 'rainsensor', 'string' - attribute 'status', 'string' - attribute 'tileMessage', 'string' - attribute 'minutes', 'string' - attribute 'VALUE_UP', 'string' - attribute 'VALUE_DOWN', 'string' - - command 'levelUp' - command 'levelDown' - command 'programOn' - command 'programOff' - command 'programWait' - command 'programEnd' - - command 'on' - command 'off' - command 'zon' - command 'zoff' - command 'z1on' - command 'z1off' - command 'z2on' - command 'z2off' - command 'z3on' - command 'z3off' - command 'z4on' - command 'z4off' - command 'z5on' - command 'z5off' - command 'z6on' - command 'z6off' - command 'z7on' - command 'z7off' - command 'z8on' - command 'z8off' - command 'z9on' - command 'z9off' - command 'z10on' - command 'z10off' - command 'z11on' - command 'z11off' - command 'z12on' - command 'z12off' - command 'z13on' - command 'z13off' - command 'z14on' - command 'z14off' - command 'z15on' - command 'z15off' - command 'z16on' - command 'z16off' - - command 'config' - command 'refresh' - command 'rain' - command 'manual' - command 'manualTime' - command 'settingsMap' - command 'writeTime' - command 'writeType' - command 'notify' - command 'updated' - - //ST release - //fingerprint endpointId: '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18', profileId: '0104', deviceId: '0002', deviceVersion: '00', inClusters: '0000,0003,0004,0005,0006,000F', outClusters: '0003, 0019', manufacturer: 'PLAID SYSTEMS', model: 'PS-SPRZ16-01' + definition (name: "Spruce Controller", namespace: "plaidsystems", author: "Plaid Systems", mnmn: "SmartThingsCommunity", + ocfDeviceType: "x.com.st.d.remotecontroller", mcdSync: true, vid: "2914a12b-504f-344f-b910-54008ba9408f") { + + capability "Actuator" + capability "Switch" + capability "Sensor" + capability "Health Check" + capability "heartreturn55003.status" + capability "heartreturn55003.controllerState" + capability "heartreturn55003.rainSensor" + capability "heartreturn55003.valveDuration" + + capability "Configuration" + capability "Refresh" + + attribute "status", "string" + attribute "controllerState", "string" + attribute "rainSensor", "string" + attribute "valveDuration", "NUMBER" + + command "setStatus" + command "setRainSensor" + command "setControllerState" + command "setValveDuration" + //new release - fingerprint endpointId: "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18", profileId: "0104", deviceId: "0002", deviceVersion: "00", inClusters: "0000,0003,0004,0005,0006,0009,000A,000F", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01", deviceJoinName: "Spruce Irrigation" - + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01", zigbeeNodeType: "ROUTER", deviceJoinName: "Spruce Irrigation Controller" } - // simulator metadata - simulator { - // status messages - - // reply messages + preferences { + //general device settings + input title: "Device settings", displayDuringSetup: true, type: "paragraph", element: "paragraph", + description: "Settings for automatic operations and device touch buttons." + input "rainSensorEnable", "bool", title: "Rain Sensor Attached?", required: false, displayDuringSetup: true + input "touchButtonDuration", "integer", title: "Automatic turn off time when touch buttons are used on device? (minutes)", required: false, displayDuringSetup: true + input name: "pumpMasterZone", type: "enum", title: "Pump or Master zone", description: "This zone will turn on and off anytime another zone is turned on or off", required: false, + options: ["Zone 1", "Zone 2", "Zone 3", "Zone 4", "Zone 5", "Zone 6", "Zone 7", "Zone 8", "Zone 9", "Zone 10", "Zone 11", "Zone 12", "Zone 13", "Zone 14", "Zone 15", "Zone 16"] + + //break for ease of reading settings + input title: "", description: "", displayDuringSetup: true, type: "paragraph", element: "paragraph" + + //schedule specific settings + input title: "Schedule setup", displayDuringSetup: true, type: "paragraph", element: "paragraph", + description: "These settings only effect when the controller is switched to the on state." + input "splitCycle", "bool", title: "Cycle scheduled watering time to reduce runoff?", required: false, displayDuringSetup: true + input "valveDelay", "integer", title: "Delay between valves when a schedule runs? (seconds)", required: false, displayDuringSetup: true + + input title: "Schedule times", displayDuringSetup: true, type: "paragraph", element: "paragraph", + description: "Set the minutes for each zone to water anytime the controller is switched on." + input name: "z1Duration", type: "integer", title: "Zone 1 schedule minutes" + input name: "z2Duration", type: "integer", title: "Zone 2 schedule minutes" + input name: "z3Duration", type: "integer", title: "Zone 3 schedule minutes" + input name: "z4Duration", type: "integer", title: "Zone 4 schedule minutes" + input name: "z5Duration", type: "integer", title: "Zone 5 schedule minutes" + input name: "z6Duration", type: "integer", title: "Zone 6 schedule minutes" + input name: "z7Duration", type: "integer", title: "Zone 7 schedule minutes" + input name: "z8Duration", type: "integer", title: "Zone 8 schedule minutes" + input name: "z9Duration", type: "integer", title: "Zone 9 schedule minutes" + input name: "z10Duration", type: "integer", title: "Zone 10 schedule minutes" + input name: "z11Duration", type: "integer", title: "Zone 11 schedule minutes" + input name: "z12Duration", type: "integer", title: "Zone 12 schedule minutes" + input name: "z13Duration", type: "integer", title: "Zone 13 schedule minutes" + input name: "z14Duration", type: "integer", title: "Zone 14 schedule minutes" + input name: "z15Duration", type: "integer", title: "Zone 15 schedule minutes" + input name: "z16Duration", type: "integer", title: "Zone 16 schedule minutes" + + input title: "Version", description: VERSION, displayDuringSetup: true, type: "paragraph", element: "paragraph" } - - preferences { - input description: 'If you have a rain sensor wired to the rain sensor input on the Spruce controller, turn it on here.', displayDuringSetup: true, type: 'paragraph', element: 'paragraph', title: 'Rain Sensor' - input description: 'The SYNC SETTINGS button must be pressed after making a change to the Rain sensor:', displayDuringSetup: false, type: 'paragraph', element: 'paragraph', title: '' - input 'RainEnable', 'bool', title: 'Rain Sensor Attached?', required: false, displayDuringSetup: true - input description: 'Adjust manual water time with arrows on main tile. The time indicated in the first small tile indicates the time the zone will water when manually switched on.', displayDuringSetup: false, type: 'paragraph', element: 'paragraph', title: '' - } - - // UI tile definitions - tiles { - - multiAttributeTile(name:"switchall", type:"generic", width:6, height:4) { - tileAttribute('device.status', key: 'PRIMARY_CONTROL') { - attributeState 'schedule', label: 'Ready', icon: 'http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_top.png' - attributeState 'finished', label: 'Finished', icon: 'st.Outdoor.outdoor5', backgroundColor: '#46c2e8' - attributeState 'raintoday', label: 'Rain Today', icon: 'http://www.plaidsystems.com/smartthings/st_rain.png', backgroundColor: '#d65fe3' - attributeState 'rainy', label: 'Rain', icon: 'http://www.plaidsystems.com/smartthings/st_rain.png', backgroundColor: '#d65fe3' - attributeState 'raintom', label: 'Rain Tomorrow', icon: 'http://www.plaidsystems.com/smartthings/st_rain.png', backgroundColor: '#d65fe3' - attributeState 'donewweek', label: 'Finished', icon: 'st.Outdoor.outdoor5', backgroundColor: '#00A0DC' - attributeState 'skipping', label: 'Skip', icon: 'st.Outdoor.outdoor20', backgroundColor: '#46c2e8' - attributeState 'moisture', label: 'Ready', icon: 'st.Weather.weather2', backgroundColor: '#46c2e8' - attributeState 'pause', label: 'PAUSE', icon: 'st.contact.contact.open', backgroundColor: '#e86d13' - attributeState 'delayed', label: 'Delayed', icon: 'st.contact.contact.open', backgroundColor: '#e86d13' - attributeState 'active', label: 'Active', icon: 'st.Outdoor.outdoor12', backgroundColor: '#3DC72E' - attributeState 'season', label: 'Adjust', icon: 'st.Outdoor.outdoor17', backgroundColor: '#ffb900' - attributeState 'disable', label: 'Off', icon: 'st.secondary.off', backgroundColor: '#cccccc' - attributeState 'warning', label: 'Warning', icon: 'http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_top_yellow.png' - attributeState 'alarm', label: 'Alarm', icon: 'http://www.plaidsystems.com/smartthings/st_spruce_leaf_225_s_red.png', backgroundColor: '#e66565' - } - - tileAttribute("device.minutes", key: "VALUE_CONTROL") { - attributeState "VALUE_UP", action: "levelUp" - attributeState "VALUE_DOWN", action: "levelDown" - } - - tileAttribute("device.tileMessage", key: "SECONDARY_CONTROL") { - attributeState "tileMessage", label: '${currentValue}' - } - - } - valueTile('minutes', 'device.minutes'){ - state 'minutes', label: '${currentValue} min' - } - valueTile('dummy', 'device.minutes'){ - state 'minutes', label: '' - } - standardTile('switch', 'device.switch', width:2, height:2) { - state 'off', label: 'Start', action: 'programOn', icon: 'st.Outdoor.outdoor12', backgroundColor: '#a9a9a9' - state 'programOn', label: 'Wait', action: 'programOff', icon: 'st.contact.contact.open', backgroundColor: '#f6e10e' - state 'programWait', label: 'Wait', action: 'programEnd', icon: 'st.contact.contact.open', backgroundColor: '#f6e10e' - state 'on', label: 'Running', action: 'programEnd', icon: 'st.Outdoor.outdoor12', backgroundColor: '#3DC72E' - } - standardTile("rainsensor", "device.rainsensor", decoration: 'flat') { - state "rainSensoroff", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_on.png' - state "rainSensoron", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_on_blue_small.png' - state "disable", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_x_small.png' - state "enable", label: 'sensor', icon: 'http://www.plaidsystems.com/smartthings/st_drop_on.png' - } - standardTile('switch1', 'device.switch1', inactiveLabel: false) { - state 'z1off', label: '1', action: 'z1on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z1on', label: '1', action: 'z1off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch2', 'device.switch2', inactiveLabel: false) { - state 'z2off', label: '2', action: 'z2on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z2on', label: '2', action: 'z2off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch3', 'device.switch3', inactiveLabel: false) { - state 'z3off', label: '3', action: 'z3on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z3on', label: '3', action: 'z3off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch4', 'device.switch4', inactiveLabel: false) { - state 'z4off', label: '4', action: 'z4on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z4on', label: '4', action: 'z4off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch5', 'device.switch5', inactiveLabel: false) { - state 'z5off', label: '5', action: 'z5on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z5on', label: '5', action: 'z5off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch6', 'device.switch6', inactiveLabel: false) { - state 'z6off', label: '6', action: 'z6on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z6on', label: '6', action: 'z6off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch7', 'device.switch7', inactiveLabel: false) { - state 'z7off', label: '7', action: 'z7on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z7on', label: '7', action: 'z7off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch8', 'device.switch8', inactiveLabel: false) { - state 'z8off', label: '8', action: 'z8on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z8on', label: '8', action: 'z8off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch9', 'device.switch9', inactiveLabel: false) { - state 'z9off', label: '9', action: 'z9on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z9on', label: '9', action: 'z9off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch10', 'device.switch10', inactiveLabel: false) { - state 'z10off', label: '10', action: 'z10on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z10on', label: '10', action: 'z10off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch11', 'device.switch11', inactiveLabel: false) { - state 'z11off', label: '11', action: 'z11on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z11on', label: '11', action: 'z11off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch12', 'device.switch12', inactiveLabel: false) { - state 'z12off', label: '12', action: 'z12on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z12on', label: '12', action: 'z12off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch13', 'device.switch13', inactiveLabel: false) { - state 'z13off', label: '13', action: 'z13on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z13on', label: '13', action: 'z13off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch14', 'device.switch14', inactiveLabel: false) { - state 'z14off', label: '14', action: 'z14on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z14on', label: '14', action: 'z14off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch15', 'device.switch15', inactiveLabel: false) { - state 'z15off', label: '15', action: 'z15on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z15on', label: '15', action: 'z15off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('switch16', 'device.switch16', inactiveLabel: false) { - state 'z16off', label: '16', action: 'z16on', icon: 'st.valves.water.closed', backgroundColor: '#ffffff' - state 'z16on', label: '16', action: 'z16off', icon: 'st.valves.water.open', backgroundColor: '#00A0DC' - } - standardTile('refresh', 'device.switch', inactiveLabel: false, decoration: 'flat') { - state 'default', action: 'refresh', icon:'st.secondary.refresh'//-icon' - } - standardTile('configure', 'device.configure', inactiveLabel: false, decoration: 'flat') { - state 'configure', label:'', action:'configuration.configure', icon:'http://www.plaidsystems.com/smartthings/st_syncsettings.png'//sync_icon_small.png' - } - - main (['switchall']) - details(['switchall','minutes','rainsensor','switch1','switch2','switch3','switch4','switch','switch5','switch6','switch7','switch8','switch9','switch10','switch11','switch12','refresh','configure','switch13','switch14','switch15','switch16']) - } -} - -//used for schedule -def programOn(){ - sendEvent(name: 'switch', value: 'programOn', descriptionText: 'Program turned on') - } - -def programWait(){ - sendEvent(name: 'switch', value: 'programWait', descriptionText: "Initializing Schedule") - } - -def programEnd(){ - //sets switch to off and tells schedule switch is off/schedule complete with manaual - sendEvent(name: 'switch', value: 'off', descriptionText: 'Program manually turned off') - zoff() - } - -def programOff(){ - sendEvent(name: 'switch', value: 'off', descriptionText: 'Program turned off') - off() - } - -//set minutes -def levelUp(){ - def newvalue = 1 - if (device.latestValue('minutes') != null) newvalue = device.latestValue('minutes').toInteger()+1 - if (newvalue >= 60) newvalue = 60 - def value = newvalue.toString() - log.debug value - sendEvent(name: 'minutes', value: "${value}", descriptionText: "Manual Time set to ${value}", display: false) -} - -def levelDown(){ - def newvalue = device.latestValue('minutes').toInteger()-1 - if (newvalue <= 0) newvalue = 1 - def value = newvalue.toString() - log.debug value - sendEvent(name: 'minutes', value: "${value}", descriptionText: "Manual Time set to ${value}", display: false) } +//----------------------zigbee parse-------------------------------// + // Parse incoming device messages to generate events -def parse(String description) { - log.debug "Parse description ${description}" - def result = null - def map = [:] - if (description?.startsWith('read attr -')) { - def descMap = parseDescriptionAsMap(description) - //log.debug "Desc Map: $descMap" - //using 000F cluster instead of 0006 (switch) because ST does not differentiate between EPs and processes all as switch - if (descMap.cluster == '000F' && descMap.attrId == '0055') { - log.debug 'Zone' - map = getZone(descMap) - } - else if (descMap.cluster == '0009' && descMap.attrId == '0000') { - log.debug 'Alarm' - map = getAlarm(descMap) - } +def parse(String description) { + def result = [] + def endpoint, value, command + def map = zigbee.parseDescriptionAsMap(description) + if (DEBUG && !map.raw) log.debug "map ${map}" + + if (description.contains("on/off")) { + command = 1 + value = description[-1] } - else if (description?.startsWith('catchall: 0104 0009')){ - log.debug 'Sync settings to controller complete' - if (device.latestValue('status') != 'alarm'){ - def configEvt = createEvent(name: 'status', value: 'schedule', descriptionText: "Sync settings to controller complete") - def configMsg = createEvent(name: 'tileMessage', value: 'Sync settings to controller complete', descriptionText: "Sync settings to controller complete", displayed: false) - result = [configEvt, configMsg] - } - return result - } - - if (map) { - result = createEvent(map) - //configure after reboot - if (map.value == 'warning' || map.value == 'alarm'){ - def cmds = config() - def alarmEvt = createEvent(name: 'tileMessage', value: map.descriptionText, descriptionText: "${map.descriptionText}", displayed: false) - result = cmds?.collect { new physicalgraph.device.HubAction(it) } + createEvent(map) + alarmEvt - return result + else { + endpoint = ( map.sourceEndpoint == null ? zigbee.convertHexToInt(map.endpoint) : zigbee.convertHexToInt(map.sourceEndpoint) ) + value = ( map.sourceEndpoint == null ? zigbee.convertHexToInt(map.value) : null ) + command = (value != null ? commandType(endpoint, map.clusterInt) : null) + } + + if (DEBUG && command != null) log.debug "${command} >> endpoint ${endpoint} value ${value} cluster ${map.clusterInt}" + switch (command) { + case "alarm": + result.push(createEvent(name: "status", value: "alarm")) + break + case "schedule": + def scheduleValue = (value == 1 ? "on" : "off") + def scheduleState = device.latestValue("controllerState") + def scheduleStatus = device.latestValue("status") + + if (scheduleState == "pause") log.debug "pausing schedule" + else { + if (scheduleStatus != "off" && scheduleValue == "off") result.push(createEvent(name: "status", value: "Schedule ${scheduleValue}")) + result.push(createEvent(name: "controllerState", value: scheduleValue)) + result.push(createEvent(name: "switch", value: scheduleValue, displayed: false)) } - else if (map.name == 'rainsensor'){ - def rainEvt = createEvent(name: 'tileMessage', value: map.descriptionText, descriptionText: "${map.descriptionText}", displayed: false) - result = [createEvent(map), rainEvt] - return result - } + break + case "zone": + def onoff = (value == 1 ? "open" : "closed") + def child = childDevices.find{it.deviceNetworkId == "${device.deviceNetworkId}:${endpoint}"} + if (child) child.sendEvent(name: "valve", value: onoff) + + if (device.latestValue("controllerState") == "off") return setTouchButtonDuration() + break + case "rainsensor": + def rainSensor = (value == 1 ? "wet" : "dry") + if (!rainSensorEnable) rainSensor = "disabled" + result.push(createEvent(name: "rainSensor", value: rainSensor)) + break + case "refresh": + //log.debug "refresh command not used" + break + default: + //log.debug "not used command" + break } - if (map) log.debug "Parse returned ${map} ${result}" + return result } -def parseDescriptionAsMap(description) { - (description - 'read attr - ').split(',').inject([:]) { map, param -> - def nameAndValue = param.split(':') - map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] - } +def commandType(endpoint, cluster) { + if (cluster == 9) return "alarm" + else if (endpoint == 1) return "schedule" + else if (endpoint in 2..17) return "zone" + else if (endpoint == 18) return "rainsensor" + else if (endpoint == 19) return "refresh" } -def getZone(descMap){ - def map = [:] - - def EP = Integer.parseInt(descMap.endpoint.trim(), 16) - - String onoff - if(descMap.value == '00'){ - onoff = 'off' - } - else onoff = 'on' - - if (EP == 1){ - map.name = 'switch' - map.value = onoff - map.descriptionText = "${device.displayName} turned sprinkler program ${onoff}" - } - - else if (EP == 18) { - map.name = 'rainsensor' - log.debug "Rain enable: ${RainEnable}, sensor: ${onoff}" - map.value = 'rainSensor' + onoff - map.descriptionText = "${device.displayName} rain sensor is ${onoff}" - } - else { - EP -= 1 - map.name = 'switch' + EP - map.value = 'z' + EP + onoff - map.descriptionText = "${device.displayName} turned Zone $EP ${onoff}" - } - - map.isStateChange = true - map.displayed = true - return map -} - -def getAlarm(descMap){ - def map = [:] - map.name = 'status' - def alarmID = Integer.parseInt(descMap.value.trim(), 16) - log.debug "${alarmID}" - map.value = 'alarm' - map.displayed = true - map.isStateChange = true - if(alarmID <= 0){ - map.descriptionText = "${device.displayName} reboot, no other alarms" - map.value = 'warning' - //map.isStateChange = false - } - else map.descriptionText = "${device.displayName} reboot, reported zone ${alarmID - 1} error, please check zone is working correctly, press SYNC SETTINGS button to clear" - - return map -} - -//status notify and change status -def notify(String val, String txt){ - sendEvent(name: 'status', value: val, descriptionText: txt, isStateChange: true, display: false) - - //String txtShort = txt.take(100) - sendEvent(name: 'tileMessage', value: txt, descriptionText: "", isStateChange: true, display: false) -} - -def updated(){ - log.debug "updated" - -} +//--------------------end zigbee parse-------------------------------// -//prefrences - rain sensor, manual time -def rain() { - log.debug "Rain sensor: ${RainEnable}" - if (RainEnable) sendEvent(name: 'rainsensor', value: 'enable', descriptionText: "${device.displayName} rain sensor is enabled", isStateChange: true) - else sendEvent(name: 'rainsensor', value: 'disable', descriptionText: "${device.displayName} rain sensor is disabled", isStateChange: true) - - if (RainEnable) "st wattr 0x${device.deviceNetworkId} 18 0x0F 0x51 0x10 {01}" - else "st wattr 0x${device.deviceNetworkId} 18 0x0F 0x51 0x10 {00}" +def installed() { + createChildDevices() } -def manualTime(value){ - sendEvent(name: 'minutes', value: "${value}", descriptionText: "Manual Time set to ${value}", display: false) +def uninstalled() { + log.debug "uninstalled" + removeChildDevices() } -def manual(){ - def newManaul = 10 - if (device.latestValue('minutes')) newManaul = device.latestValue('minutes').toInteger() - log.debug "Manual Zone runtime ${newManaul} mins" - def manualTime = hex(newManaul) - - def sendCmds = [] - sendCmds.push("st wattr 0x${device.deviceNetworkId} 1 6 0x4002 0x21 {00${manualTime}}") - return sendCmds +def updated() { + log.debug "updated" + initialize() } -//write switch time settings map -def settingsMap(WriteTimes, attrType){ - log.debug WriteTimes - - def i = 1 - def runTime - def sendCmds = [] - while(i <= 17){ - - if (WriteTimes."${i}"){ - runTime = hex(Integer.parseInt(WriteTimes."${i}")) - log.debug "${i} : $runTime" - - if (attrType == 4001) sendCmds.push("st wattr 0x${device.deviceNetworkId} ${i} 0x06 0x4001 0x21 {00${runTime}}") - else sendCmds.push("st wattr 0x${device.deviceNetworkId} ${i} 0x06 0x4002 0x21 {00${runTime}}") - sendCmds.push("delay 500") - } - i++ - } - return sendCmds +def initialize() { + sendEvent(name: "switch", value: "off", displayed: false) + sendEvent(name: "controllerState", value: "off", displayed: false) + sendEvent(name: "status", value: "Initialize") + if (device.latestValue("valveDuration") == null) sendEvent(name: "valveDuration", value: 10) + + //update zigbee device settings + response(setDeviceSettings() + setTouchButtonDuration() + setRainSensor() + refresh()) } -//send switch time -def writeType(wEP, cycle){ - log.debug "wt ${wEP} ${cycle}" - "st wattr 0x${device.deviceNetworkId} ${wEP} 0x06 0x4001 0x21 {00" + hex(cycle) + "}" - } -//send switch off time -def writeTime(wEP, runTime){ - "st wattr 0x${device.deviceNetworkId} ${wEP} 0x06 0x4002 0x21 {00" + hex(runTime) + "}" - } +def createChildDevices() { + log.debug "create children" + def pumpMasterZone = (pumpMasterZone ? pumpMasterZone.replaceFirst("Zone ","").toInteger() : null) -//set reporting and binding -def configure() { - - sendEvent(name: 'status', value: 'schedule', descriptionText: "Syncing settings to controller") - sendEvent(name: 'minutes', value: "10", descriptionText: "Manual Time set to 10 mins", display: false) - sendEvent(name: 'tileMessage', value: 'Syncing settings to controller', descriptionText: 'Syncing settings to controller') - config() -} - -def config(){ - - String zigbeeId = swapEndianHex(device.hub.zigbeeId) - log.debug "Configuring Reporting and Bindings ${device.deviceNetworkId} ${device.zigbeeId}" - - def configCmds = [ - //program on/off - "zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x09 {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - //zones 1-8 - "zdo bind 0x${device.deviceNetworkId} 2 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 3 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 4 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 5 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 6 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 7 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 8 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 9 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - //zones 9-16 - "zdo bind 0x${device.deviceNetworkId} 10 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 11 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 12 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 13 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 14 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 15 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 16 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - "zdo bind 0x${device.deviceNetworkId} 17 1 0x0F {${device.zigbeeId}} {}", "delay 1000", - //rain sensor - "zdo bind 0x${device.deviceNetworkId} 18 1 0x0F {${device.zigbeeId}} {}", - - "zcl global send-me-a-report 6 0 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 1", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 1", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 2", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 3", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 4", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 5", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 6", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 7", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 8", "delay 500", - - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 9", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 10", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 11", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 12", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 13", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 14", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 15", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 16", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 17", "delay 500", - - "zcl global send-me-a-report 0x0F 0x55 0x10 1 0 {01}", "delay 500", - "send 0x${device.deviceNetworkId} 1 18", "delay 500", - - "zcl global send-me-a-report 0x09 0x00 0x21 1 0 {00}", "delay 500", - "send 0x${device.deviceNetworkId} 1 1", "delay 500" - ] - return configCmds + rain() -} + //create, rename, or remove child + for (i in 1..16) { + //endpoint is offset, zone number +1 + def endpoint = i + 1 -def refresh() { + def child = childDevices.find{it.deviceNetworkId == "${device.deviceNetworkId}:${endpoint}"} + //create child + if (!child) { + def childLabel = "Zone$i" + child = addChildDevice("Spruce Valve", "${device.deviceNetworkId}:${endpoint}", device.hubId, + [completedSetup: true, label: "${childLabel}", isComponent: true, componentName: "Zone$i", componentLabel: "Zone$i"]) + log.debug "${child}" + child.sendEvent(name: "valve", value: "closed", displayed: false) + } - log.debug "refresh pressed" - sendEvent(name: 'tileMessage', value: 'Refresh', descriptionText: 'Refresh') - - def refreshCmds = [ - - "st rattr 0x${device.deviceNetworkId} 1 0x0F 0x55", "delay 500", - - "st rattr 0x${device.deviceNetworkId} 2 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 3 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 4 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 5 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 6 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 7 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 8 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 9 0x0F 0x55", "delay 500", - - "st rattr 0x${device.deviceNetworkId} 10 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 11 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 12 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 13 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 14 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 15 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 16 0x0F 0x55", "delay 500", - "st rattr 0x${device.deviceNetworkId} 17 0x0F 0x55", "delay 500", - - "st rattr 0x${device.deviceNetworkId} 18 0x0F 0x51","delay 500", - - ] - - return refreshCmds -} - -private hex(value) { - new BigInteger(Math.round(value).toString()).toString(16) -} - -private String swapEndianHex(String hex) { - reverseArray(hex.decodeHex()).encodeHex() -} - -private byte[] reverseArray(byte[] array) { - int i = 0; - int j = array.length - 1; - byte tmp; - while (j > i) { - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - j--; - i++; - } - return array -} - -//on & off redefined for Alexa to start manual schedule -def on() { - log.debug 'Alexa on' - //schedule subscribes to programOn - sendEvent(name: 'switch', value: 'programOn', descriptionText: 'Alexa turned program on') -} -def off() { - log.debug 'Alexa off' - sendEvent(name: 'switch', value: 'off', descriptionText: 'Alexa turned program off') - zoff() -} + } -// Commands to device -//zones on - 8 -def zon() { - "st cmd 0x${device.deviceNetworkId} 1 6 1 {}" -} -def zoff() { - "st cmd 0x${device.deviceNetworkId} 1 6 0 {}" + state.oldLabel = device.label } -def z1on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 2 6 1 {}" -} -def z1off() { - "st cmd 0x${device.deviceNetworkId} 2 6 0 {}" -} -def z2on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 3 6 1 {}" -} -def z2off() { - "st cmd 0x${device.deviceNetworkId} 3 6 0 {}" -} -def z3on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 4 6 1 {}" -} -def z3off() { - "st cmd 0x${device.deviceNetworkId} 4 6 0 {}" -} -def z4on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 5 6 1 {}" + +def removeChildDevices() { + log.debug "remove all children" + + //get and delete children avoids duplicate children + def children = getChildDevices() + if (children != null) { + children.each{ + deleteChildDevice(it.deviceNetworkId) + } + } } -def z4off() { - "st cmd 0x${device.deviceNetworkId} 5 6 0 {}" + + +//----------------------------------commands--------------------------------------// + +def setStatus(status) { + if (DEBUG) log.debug "status ${status}" + sendEvent(name: "status", value: status, descriptionText: "Initialized") } -def z5on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 6 6 1 {}" + +def setRainSensor() { + if (DEBUG) log.debug "Rain sensor: ${rainSensorEnable}" + + if (rainSensorEnable) return zigbee.writeAttribute(BINARY_INPUT_CLUSTER, OUT_OF_SERVICE_IDENTIFIER, DataType.BOOLEAN, 1, [destEndpoint: 18]) + else return zigbee.writeAttribute(BINARY_INPUT_CLUSTER, OUT_OF_SERVICE_IDENTIFIER, DataType.BOOLEAN, 0, [destEndpoint: 18]) } -def z5off() { - "st cmd 0x${device.deviceNetworkId} 6 6 0 {}" + +def setValveDuration(duration) { + if (DEBUG) log.debug "Valve Duration set to: ${duration}" + + sendEvent(name: "valveDuration", value: duration, displayed: false) } -def z6on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 7 6 1 {}" + +//cahnge the device settings for automatically starting a pump or master zone, set the controller to split scheduled watering cycles, set a delay between scheduled valves +def setDeviceSettings() { + def pumpMasterZone = (pumpMasterZone ? pumpMasterZone.replaceFirst("Zone ","").toInteger() : null) + def splitCycle = (splitCycle == true ? 2 : 1) + def valveDelay = (valveDelay ? valveDelay.toInteger() : 0) + if (DEBUG) log.debug "Pump/Master: ${pumpMasterEndpoint} splitCycle: ${splitCycle} valveDelay: ${valveDelay}" + + def endpointMap = [:] + for (zone in 0..17) { + //setup zone, 1=single cycle, 2=split cycle, 4=pump/master + def zoneSetup = splitCycle + if (zone == pumpMasterZone) zoneSetup = 4 + else if (zone == 0) zoneSetup = valveDelay + + def endpoint = zone + 1 + endpointMap."${endpoint}" = "${zoneSetup}" + zone++ + } + + return settingsMap(endpointMap, ON_TIME_ATTRIBUTE) } -def z6off() { - "st cmd 0x${device.deviceNetworkId} 7 6 0 {}" + +//change the default time a zone will turn on when the buttons on the face of the controller are used +def setTouchButtonDuration() { + def touchButtonDuration = (touchButtonDuration ? touchButtonDuration.toInteger() : 10) + if (DEBUG) log.debug "touchButtonDuration ${touchButtonDuration} mins" + + def sendCmds = [] + sendCmds.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, touchButtonDuration, [destEndpoint: 1])) + return sendCmds } -def z7on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 8 6 1 {}" + +//controllerState +def setControllerState(state) { + if (DEBUG) log.debug "state ${state}" + sendEvent(name: "controllerState", value: state, descriptionText: "Initialized") + + switch(state) { + case "on": + if (!rainDelay()) { + sendEvent(name: "switch", value: "on", displayed: false) + sendEvent(name: "status", value: "initialize schedule", descriptionText: "initialize schedule") + startSchedule() + } + break + case "off": + sendEvent(name: "switch", value: "off", displayed: false) + scheduleOff() + break + case "pause": + pause() + break + case "resume": + resume() + break + } } -def z7off() { - "st cmd 0x${device.deviceNetworkId} 8 6 0 {}" + +//on & off from switch +def on() { + log.debug "switch on" + setControllerState("on") } -def z8on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 9 6 1 {}" + +def off() { + log.debug "switch off" + setControllerState("off") } -def z8off() { - "st cmd 0x${device.deviceNetworkId} 9 6 0 {}" + +def pause() { + log.debug "pause" + sendEvent(name: "switch", value: "off", displayed: false) + sendEvent(name: "status", value: "paused schedule", descriptionText: "pause on") + scheduleOff() } -//zones 9 - 16 -def z9on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 10 6 1 {}" +def resume() { + log.debug "resume" + sendEvent(name: "switch", value: "on", displayed: false) + sendEvent(name: "status", value: "resumed schedule", descriptionText: "resume on") + scheduleOn() } -def z9off() { - "st cmd 0x${device.deviceNetworkId} 10 6 0 {}" + +//set raindelay +def rainDelay() { + if (rainSensorEnable && device.latestValue("rainSensor") == "wet") { + sendEvent(name: "switch", value: "off", displayed: false) + sendEvent(name: "controllerState", value: "off") + sendEvent(name: "status", value: "rainy") + return true + } + return false } -def z10on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 11 6 1 {}" + +//set schedule +def noSchedule() { + sendEvent(name: "switch", value: "off", displayed: false) + sendEvent(name: "controllerState", value: "off") + sendEvent(name: "status", value: "Set schedule in settings") } -def z10off() { - "st cmd 0x${device.deviceNetworkId} 11 6 0 {}" + +//schedule on/off +def scheduleOn() { + zigbee.command(zigbee.ONOFF_CLUSTER, 1, "", [destEndpoint: 1]) } -def z11on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 12 6 1 {}" +def scheduleOff() { + zigbee.command(zigbee.ONOFF_CLUSTER, 0, "", [destEndpoint: 1]) } -def z11off() { - "st cmd 0x${device.deviceNetworkId} 12 6 0 {}" + +// Commands to zones/valves +def valveOn(valueMap) { + //get endpoint from deviceNetworkId + def endpoint = valueMap.dni.replaceFirst("${device.deviceNetworkId}:","").toInteger() + def duration = (device.latestValue("valveDuration").toInteger()) + + sendEvent(name: "status", value: "${valueMap.label} on for ${duration}min(s)", descriptionText: "Zone ${valueMap.label} on for ${duration}min(s)") + if (DEBUG) log.debug "state ${state.hasConfiguredHealthCheck} ${zigbee.ONOFF_CLUSTER}" + zoneOn(endpoint, duration) } -def z12on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 13 6 1 {}" + +def valveOff(valueMap) { + def endpoint = valueMap.dni.replaceFirst("${device.deviceNetworkId}:","").toInteger() + + sendEvent(name: "status", value: "${valueMap.label} turned off", descriptionText: "${valueMap.label} turned off") + + zoneOff(endpoint) } -def z12off() { - "st cmd 0x${device.deviceNetworkId} 13 6 0 {}" + +def zoneOn(endpoint, duration) { + //send duration from slider + return zoneDuration(duration) + zigbee.command(zigbee.ONOFF_CLUSTER, 1, "", [destEndpoint: endpoint]) } -def z13on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 14 6 1 {}" + +def zoneOff(endpoint) { + //reset touchButtonDuration to setting value + return zigbee.command(zigbee.ONOFF_CLUSTER, 0, "", [destEndpoint: endpoint]) + setTouchButtonDuration() } -def z13off() { - "st cmd 0x${device.deviceNetworkId} 14 6 0 {}" + +def zoneDuration(int duration) { + def sendCmds = [] + sendCmds.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, duration, [destEndpoint: 1])) + return sendCmds } -def z14on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 15 6 1 {}" + +//------------------end commands----------------------------------// + +//get times from settings and send to controller, then start schedule +def startSchedule() { + def startRun = false + def runTime, totalTime=0 + def scheduleTimes = [] + + for (i in 1..16) { + def endpoint = i + 1 + //if (settings."z${i}" && settings."z${i}Duration" != null) { + if (settings."z${i}Duration" != null) { + runTime = Integer.parseInt(settings."z${i}Duration") + totalTime += runTime + startRun = true + + scheduleTimes.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, runTime, [destEndpoint: endpoint])) + } + else { + scheduleTimes.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, 0, [destEndpoint: endpoint])) + } + } + if (!startRun || totalTime == 0) return noSchedule() + + //start after scheduleTimes are sent + scheduleTimes.push(zigbee.command(zigbee.ONOFF_CLUSTER, 1, "", [destEndpoint: 1])) + sendEvent(name: "status", value: "Scheduled for ${totalTime}min(s)", descriptionText: "Start schedule ending in ${totalTime} mins") + return scheduleTimes } -def z14off() { - "st cmd 0x${device.deviceNetworkId} 15 6 0 {}" + +//write switch time settings map +def settingsMap(WriteTimes, attrType) { + + def runTime + def sendCmds = [] + for (endpoint in 1..17) { + if (WriteTimes."${endpoint}") { + runTime = Integer.parseInt(WriteTimes."${endpoint}") + + if (attrType == ON_TIME_ATTRIBUTE) sendCmds.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, ON_TIME_ATTRIBUTE, DataType.UINT16, runTime, [destEndpoint: endpoint])) + else sendCmds.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, runTime, [destEndpoint: endpoint])) + } + } + return sendCmds } -def z15on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 16 6 1 {}" + +//send switch time +def writeType(endpoint, cycle) { + zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, ON_TIME_ATTRIBUTE, DataType.UINT16, cycle, [destEndpoint: endpoint]) } -def z15off() { - "st cmd 0x${device.deviceNetworkId} 16 6 0 {}" + +//send switch off time +def writeTime(endpoint, runTime) { + zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, runTime, [destEndpoint: endpoint]) } -def z16on() { - return manual() + "st cmd 0x${device.deviceNetworkId} 17 6 1 {}" + +//set reporting and binding +def configure() { + // Device-Watch checks every 1 hour + sendEvent(name: "checkInterval", value: HC_INTERVAL_MINS * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + if (DEBUG) log.debug "Configuring Reporting ${device.name} ${device.deviceNetworkId} ${device.hub.zigbeeId}" + + //setup reporting for 18 endpoints + def reportingCmds = [] + reportingCmds += zigbee.configureReporting(zigbee.ONOFF_CLUSTER, 0, DataType.BOOLEAN, 1, 0, 0x01, [destEndpoint: 1]) + reportingCmds += zigbee.configureReporting(ALARMS_CLUSTER, 0, DataType.UINT16, 1, 0, 0x00, [destEndpoint: 1]) + + for (endpoint in 1..18) { + reportingCmds += zigbee.configureReporting(BINARY_INPUT_CLUSTER, PRESENT_VALUE_IDENTIFIER, DataType.BOOLEAN, 1, 0, 0x01, [destEndpoint: endpoint]) + } + + return reportingCmds + setRainSensor() } -def z16off() { - "st cmd 0x${device.deviceNetworkId} 17 6 0 {}" + +//PING is used by Device-Watch in attempt to reach the Device +def ping() { + if (DEBUG) log.debug "device health ping" + return refresh() } +def refresh() { + if (DEBUG) log.debug "refresh" + + def refreshCmds = [] + for (endpoint in 1..17) { + refreshCmds += zigbee.readAttribute(BINARY_INPUT_CLUSTER, PRESENT_VALUE_IDENTIFIER, [destEndpoint: endpoint]) + } + refreshCmds += zigbee.readAttribute(BINARY_INPUT_CLUSTER, OUT_OF_SERVICE_IDENTIFIER, [destEndpoint: 18]) + + return refreshCmds +} diff --git a/devicetypes/plaidsystems/spruce-valve.src/spruce-valve.groovy b/devicetypes/plaidsystems/spruce-valve.src/spruce-valve.groovy new file mode 100644 index 00000000000..ab3f32d4a2d --- /dev/null +++ b/devicetypes/plaidsystems/spruce-valve.src/spruce-valve.groovy @@ -0,0 +1,51 @@ +/** + * Copyright Plaid Systems 2020 + * + * 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. + * + * +3-2021 + * remove parse + * cleanup space, comments + * remove Health Check, this is handled by parent + +11-2020 + * valveDuration slider capability added back to presentation + * tabs and trim whitespace + +**/ + +metadata { + definition (name: "Spruce Valve", namespace: "plaidsystems", author: "Plaid Systems", mnmn: "SmartThingsCommunity") { + capability "Actuator" + capability "Valve" + capability "Sensor" + } +} + +def installed() { + initialize() +} + +def updated() { + initialize() +} + +private initialize() { + sendEvent(name: "valve", value: "closed") +} + +def open() { + parent.valveOn(dni: device.deviceNetworkId, value: 'open', label: device.label) +} + +def close() { + parent.valveOff(dni: device.deviceNetworkId, value: 'closed', label: device.label) +} From 5455a0a4d8053643165f2e96b7c24fa497176d31 Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Wed, 24 Mar 2021 10:23:56 +0100 Subject: [PATCH 201/422] [WWST-7349] Added fingerprint for Leak Gopher water leak sensor (#61413) * Added fingerprint for Leak Gopher water leak sensor * fix --- .../zwave-water-sensor.groovy | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy b/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy index 3bef3d96056..86e57ec9995 100644 --- a/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy +++ b/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy @@ -31,6 +31,8 @@ metadata { fingerprint mfr: "0086", prod: "0002", model: "007A", deviceJoinName: "Aeotec Water Leak Sensor" //EU //Aeotec Water Sensor 6 fingerprint mfr: "0086", prod: "0202", model: "007A", deviceJoinName: "Aeotec Water Leak Sensor" //AU //Aeotec Water Sensor 6 fingerprint mfr: "000C", prod: "0201", model: "000A", deviceJoinName: "HomeSeer Water Leak Sensor" //HomeSeer LS100+ Water Sensor + //zw:Ss2 type:0701 mfr:0173 prod:4C47 model:4C44 ver:1.10 zwv:4.61 lib:03 cc:5E,55,98,9F sec:86,71,85,59,72,5A,6C,7A,84,80 + fingerprint mfr: "0173", prod: "4C47", model: "4C44", deviceJoinName: "Leak Gopher Water Leak Sensor" //Leak Intelligence Leak Gopher Z-Wave Leak Detector } simulator { @@ -58,8 +60,8 @@ metadata { } def initialize() { - if (isAeotec() || isNeoCoolcam() || isDome()) { - // 8 hour (+ 2 minutes) ping for Aeotec, NEO Coolcam, Dome + if (isAeotec() || isNeoCoolcam() || isDome() || isLeakGopher()) { + // 8 hour (+ 2 minutes) ping for Aeotec, NEO Coolcam, Dome, Leak Gopher sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } else { // 12 hours (+ 2 minutes) for other devices @@ -90,8 +92,8 @@ def configure() { // Tell sensor to send us battery information instead of USB power information commands << encap(zwave.configurationV1.configurationSet(parameterNumber: 0x5E, scaledConfigurationValue: 1, size: 1)) response(delayBetween(commands, 1000) + ["delay 20000", encap(zwave.wakeUpV1.wakeUpNoMoreInformation())]) - } else if (isNeoCoolcam() || isDome()) { - // wakeUpInterval set to 4 h for NEO Coolcam, Dome + } else if (isNeoCoolcam() || isDome() || isLeakGopher()) { + // wakeUpInterval set to 4 h for NEO Coolcam, Dome, Leak Gopher zwave.wakeUpV1.wakeUpIntervalSet(seconds: 4 * 3600, nodeid: zwaveHubNodeId).format() } } @@ -326,4 +328,8 @@ private isNeoCoolcam() { private isAeotec() { zwaveInfo.mfr == "0086" && zwaveInfo.model == "007A" +} + +private isLeakGopher() { + zwaveInfo.mfr == "0173" && zwaveInfo.model == "4C44" } \ No newline at end of file From 7a2f7ccc8563de548751fb9b87b4ca7d1a0ff5c5 Mon Sep 17 00:00:00 2001 From: MadiTang <32762617+MadiTang@users.noreply.github.com> Date: Fri, 26 Mar 2021 16:37:56 +0800 Subject: [PATCH 202/422] DevWs for Focalcrest containing containing ZigBee Switch (#61510) * DevWs for Focalcrest containing containing ZigBee Switch * change join name to Focalcrest Switch * fix spacing (use tabs instead of spaces / 4 spaces -> 1 tab) * Adds a line break Co-authored-by: madi tang Co-authored-by: Konrad K <33450498+KKlimczukS@users.noreply.github.com> --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index c006fe5a889..057a2fbd585 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -108,6 +108,9 @@ metadata { fingerprint manufacturer: "Jasco Products", model: "43094", deviceJoinName: "Enbrighten Switch" //Enbrighten, Plug-in Smart Switch 43094, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Outlet", ocfDeviceType: "oic.d.smartplug" //Enbrighten, In-Wall Smart Outlet 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43076", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch 43076, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 + + // Focalcrest + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "Focalcrest", model: "SRB01", deviceJoinName: "Focalcrest Switch" // In-Wall Relay Switch } // simulator metadata From 356f9622d6214dc343254d99b8e27a415ae85f96 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Fri, 2 Apr 2021 05:02:00 +0800 Subject: [PATCH 203/422] DevWs for www.easyiot.tech containing containing ZigBee Switch (#62113) Co-authored-by: jiang wegang --- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 057a2fbd585..75c937d5620 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -31,7 +31,10 @@ metadata { // eWeLink fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "eWeLink", model: "SA-003-Zigbee", deviceJoinName: "eWeLink Outlet", ocfDeviceType: "oic.d.smartplug" //eWeLink SmartPlug (SA-003) fingerprint profileId: "0104", inClusters: "0000,0003,0004,00005,0006", outClusters: "0000", manufacturer: "eWeLink", model: "ZB-SW01", deviceJoinName: "eWeLink Switch" - + + // LELLKI + fingerprint profileId: "0104", inClusters: "0000,0003,0004,00005,0006", outClusters: "0000", manufacturer: "LELLKI", model: "JZ-ZB-001", deviceJoinName: "LELLKI Switch" + // EZEX fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR1N0Z0-HA", deviceJoinName: "eZEX Switch" //EZEX Switch @@ -178,4 +181,4 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() -} +} \ No newline at end of file From 70f9e43c8200cea0d8affff99139afad4776bac6 Mon Sep 17 00:00:00 2001 From: "SmartThings, Inc" Date: Wed, 7 Apr 2021 05:15:19 -0700 Subject: [PATCH 204/422] DevWs for ID Lock containing containing ZigBee Lock (#62657) Co-authored-by: Company account - --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 5d82295a219..8f63826a068 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -48,6 +48,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Locks (YDF30/40, YMF30/40) with old firmware (v.9.0) fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000202", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YDF40 fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "0700000001", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0101", outClusters: "0000,0001,0003,0101", manufacturer: "Datek", model: "ID Lock 150", deviceJoinName: "ID Lock Door Lock" //ID Lock 150 Zigbee Module by Datek } tiles(scale: 2) { @@ -1219,4 +1220,4 @@ private boolean isMasterCode(codeID) { codeID = codeID.toInteger() } (codeID == 0) ? true : false -} +} \ No newline at end of file From a7c980340992ca37508d42a70f354b5fd7d30048 Mon Sep 17 00:00:00 2001 From: LanKuoWei <71474070+LanKuoWei@users.noreply.github.com> Date: Fri, 9 Apr 2021 04:36:44 +0800 Subject: [PATCH 205/422] DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision In-Wall 2Relays Switch (#52126) * DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision In-Wall 2Relays Switch * DevWs for Vision-Elec. Technology Co., Ltd. containing containing Vision In-Wall 2Relays Switch * Update vision-in-wall-2relays-switch.groovy Fix tile not used and getCommandClassVersions can be delete. * Modify by suggestions. * Delete vision-in-wall-2relays-switch.groovy file format have some issue that no correct mapping, will re-sent file upload! * Modify by suggestions. * Modify by suggestions. * Fix * Modify by suggestions and fix some issue. * Forgot no use "else". * Change encap() function, let will support send no multiChannelCmdEncap command, all send command can call encap() now. * Fix namespace issue. * Delete vision-in-wall-2relays-switch.groovy Delete the duplicate file, remove old DTH file, hold the newest DTH file. * Fix issue. * Use [zwaveHubNodeId] in multichannel association. * Change the multi Channel AssociationSet to the packet confirmed works, avoid occur Err-Code:34-501. --- .../vision-in-wall-2relays-switch.groovy | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 devicetypes/vision-kuowei/vision-in-wall-2relays-switch.src/vision-in-wall-2relays-switch.groovy diff --git a/devicetypes/vision-kuowei/vision-in-wall-2relays-switch.src/vision-in-wall-2relays-switch.groovy b/devicetypes/vision-kuowei/vision-in-wall-2relays-switch.src/vision-in-wall-2relays-switch.groovy new file mode 100644 index 00000000000..64e2594e1c2 --- /dev/null +++ b/devicetypes/vision-kuowei/vision-in-wall-2relays-switch.src/vision-in-wall-2relays-switch.groovy @@ -0,0 +1,266 @@ +/** + * Vision In-Wall 2Relays Switch (Models: ZL7435xx-5) + * + * Author: + * Lan, Kuo Wei + * + * Product Link: + * http://www.visionsecurity.com.tw/index.php?option=product&lang=en&task=pageinfo&id=335&belongid=334&index=0 +*/ +metadata { + definition( + name: "Vision In-Wall 2Relays Switch", + namespace: "Vision_KuoWei", + author: "Lan, Kuo Wei", + ocfDeviceType: "oic.d.switch", + deviceTypeId: "Switch", + mnmn: "0ALw", + vid: "812dd85f-ae1e-382e-9289-15cbc7c7fd6f" + ) { + capability "Health Check" + capability "Refresh" + capability "Switch" + capability "Configuration" + // This DTH uses 2 switch endpoints. Parent DTH controls endpoint 1 so please use '1' at the end of deviceJoinName + // Child device (isComponent : false) representing endpoint 2 will substitute 1 with 2 for easier identification. + fingerprint manufacturer: "0109", prod: "2017", model: "171B", deviceJoinName: "Vision 2Relays Switch 1" //zw:Ls type:1001 mfr:0109 prod:2017 model:171B ver:16.11 zwv:4.38 lib:03 cc:98 sec:5E,72,86,85,59,70,5A,7A,60,8E,73,27,25 epc:2 + fingerprint manufacturer: "0109", prod: "2017", model: "171C", deviceJoinName: "Vision 2Relays Switch 1" //zw:Ls type:1001 mfr:0109 prod:2017 model:171C ver:25.07 zwv:4.54 lib:03 cc:98 sec:5E,72,86,85,59,70,5A,7A,60,8E,73,27,25 epc:2 + } + + preferences { + parameterMap.each { + input (title: it.name, description: it.description, type: "paragraph", element: "paragraph") + switch(it.type) { + case "enum": + input(name: it.key, title: "Select", type: "enum", options: it.values, defaultValue: it.defaultValue, required: false) + break + } + } + } +} + +def installed() { + /* Child device check */ + if(!childDevices) { + def d = addChildDevice( + "smartthings", + "Z-Wave Binary Switch Endpoint", + "${device.deviceNetworkId}-child", + device.hubId, + [ + completedSetup: true, + label: "${device.displayName[0..-2]}2", + isComponent: false + ] + ) + } + // Preferences template + state.currentPreferencesState = [:] + parameterMap.each { + state.currentPreferencesState."$it.key" = [:] + state.currentPreferencesState."$it.key".value = getPreferenceValue(it) + def preferenceName = it.key + "Boolean" + settings."$preferenceName" = true + state.currentPreferencesState."$it.key".status = "synced" + } + firstCommand() +} + +def firstCommand(){ + def commands = [] + commands << encap(0, zwave.configurationV1.configurationGet(parameterNumber: 0x01)) + commands << "delay 300" + commands << encap(0, zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 0x01, nodeId: [0x01,0x01])) + sendHubCommand(commands, 100) +} + +def updated() { + parameterMap.each { + if (isPreferenceChanged(it)) { + log.debug "Preference ${it.key} has been updated from value: ${state.currentPreferencesState."$it.key".value} to ${settings."$it.key"}" + state.currentPreferencesState."$it.key".status = "syncPending" + } else if (!state.currentPreferencesState."$it.key".value) { + log.warn "Preference ${it.key} no. ${it.parameterNumber} has no value. Please check preference declaration for errors." + } + } + configure() +} + +def configure() { + // Device-Watch simply pings if no device events received for checkInterval duration of 32min = 2 * 15min + 2min lag time + sendEvent(name: "checkInterval", value: 30 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + def commands = [] + parameterMap.each { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands << encap(0, zwave.configurationV1.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands << "delay 300" + commands << encap(0, zwave.configurationV1.configurationGet(parameterNumber: it.parameterNumber)) + } + } + response(commands + refresh()) +} + +def parse(String description) { + def result = null + def cmd = zwave.parse(description) + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug("'$description' parsed to $result") + return createEvent(result) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, endpoint=null) { + (endpoint == 1) ? [name: "switch", value: cmd.value ? "on" : "off"] : [:] +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint=null) { + (endpoint == 1) ? [name: "switch", value: cmd.value ? "on" : "off"] : [:] +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + createEvent(descriptionText: cmd.toString()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { // Supervision encapsulated Message + cmd.parameter = cmd.parameter.drop(2) + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + def encapsulatedCommand = cmd.encapsulatedCommand([0x25: 1, 0x20: 1]) + if (cmd.sourceEndPoint == 1) { + zwaveEvent(encapsulatedCommand, 1) + } else { // sourceEndPoint == 2 + childDevices[0]?.handleZWave(encapsulatedCommand) + [:] + } +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, endpoint = null) { + if (endpoint == null) log.debug("$device.displayName: $cmd") + else log.debug("$device.displayName: $cmd endpoint: $endpoint") +} + +def on() { + // parent DTH controls endpoint 1 + def endpointNumber = 1 + delayBetween([ + encap(endpointNumber, zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF)), + encap(endpointNumber, zwave.switchBinaryV1.switchBinaryGet()) + ]) +} + +def off() { + def endpointNumber = 1 + delayBetween([ + encap(endpointNumber, zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00)), + encap(endpointNumber, zwave.switchBinaryV1.switchBinaryGet()) + ]) +} + +// PING is used by Device-Watch in attempt to reach the Device +def ping() { + refresh() +} + +def refresh() { + [encap(1, zwave.switchBinaryV1.switchBinaryGet()), encap(2, zwave.switchBinaryV1.switchBinaryGet())] +} + +// sendCommand is called by endpoint 2 child device handler +def sendCommand(endpointDevice, commands) { + def endpointNumber = 2 + def result + if (commands instanceof String) { + commands = commands.split(',') as List + } + result = commands.collect { encap(endpointNumber, it) } + sendHubCommand(result, 100) +} + +def encap(endpointNumber, cmd) { + if (endpointNumber == 0) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } + else if (cmd instanceof physicalgraph.zwave.Command) { + def cmdTemp = zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint: 0x01, destinationEndPoint: endpointNumber).encapsulate(cmd) + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmdTemp).format() + } else { + cmd.format() + } +} + +private getParameterMap() {[[ + name: "Input switch type (Default: Toggle Switch)", key: "inputSwitchType", type: "enum", + parameterNumber: 1, size: 1, defaultValue: 0, + values: [ + 0: "Toggle Switch", + 1: "Momentary Switch", + ], + description: "This item can select input switch type." + ] +]} + +private syncConfiguration() { + def commands = [] + parameterMap.each { + try { + if (state.currentPreferencesState."$it.key".status == "syncPending") { + commands << encap(0, zwave.configurationV1.configurationSet(scaledConfigurationValue: getCommandValue(it), parameterNumber: it.parameterNumber, size: it.size)) + commands << "delay 300" + commands << encap(0, zwave.configurationV1.configurationGet(parameterNumber: it.parameterNumber)) + } + } catch (e) { + log.warn "There's been an issue with preference: ${it.key}" + } + } + sendHubCommand(commands, 100) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + log.debug "Configuration report: ${cmd}" + def preference = parameterMap.find({it.parameterNumber == cmd.parameterNumber}) + def key = preference.key + def preferenceValue = getPreferenceValue(preference, cmd.scaledConfigurationValue) + if (settings."$key" == preferenceValue) { + state.currentPreferencesState."$key".value = settings."$key" + state.currentPreferencesState."$key".status = "synced" + } else { + state.currentPreferencesState."$key"?.status = "syncPending" + runIn(5, "syncConfiguration", [overwrite: true]) + } +} + +private getPreferenceValue(preference, value = "default") { + def integerValue = value == "default" ? preference.defaultValue : value.intValue() + switch (preference.type) { + case "enum": + return String.valueOf(integerValue) + default: + return integerValue + } +} + +private getCommandValue(preference) { + def parameterKey = preference.key + switch (preference.type) { + default: + return Integer.parseInt(settings."$parameterKey") + } +} + +private isPreferenceChanged(preference) { + if (settings."$preference.key" != null) { + return state.currentPreferencesState."$preference.key".value != settings."$preference.key" + } else { + return false + } +} \ No newline at end of file From 4a387b195e0198f771863e4fa882881f3ec5dd4b Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Tue, 13 Apr 2021 20:59:27 +0900 Subject: [PATCH 206/422] Update zigbee-rgbw-bulb.groovy (#62112) --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index a54e8bb5a2a..0f71f169eab 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -35,6 +35,9 @@ metadata { // Generic fingerprint fingerprint profileId: "0104", deviceId: "0102", inClusters: "0006, 0008, 0300", deviceJoinName: "Light" //Generic RGBW Light fingerprint profileId: "0104", deviceId: "010D", inClusters: "0006, 0008, 0300", deviceJoinName: "Light" //Generic RGBW Light + + // Samsung LED + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-002", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-002" //ITM RGBW // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010D", manufacturer: "AduroSmart Eria", model: "AD-RGBW3001", deviceJoinName: "Eria Light" //Eria ZigBee RGBW Bulb From e31b4edabc76aa61bc4f1adf03cb69f53dc7cd41 Mon Sep 17 00:00:00 2001 From: dwd-kwon <59678391+dwd-kwon@users.noreply.github.com> Date: Wed, 14 Apr 2021 02:43:08 +0900 Subject: [PATCH 207/422] Update zigbee-metering-plug-power-consumption-report.groovy (#61324) --- .../zigbee-metering-plug-power-consumption-report.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy index f99892db596..7b95c8e18e6 100644 --- a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy @@ -14,7 +14,7 @@ import physicalgraph.zigbee.zcl.DataType metadata { - definition (name: "Zigbee Metering Plug Power Consumption Report", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.smartplug", mnmn: "SmartThings", vid: "generic-switch-power-energy") { + definition (name: "Zigbee Metering Plug Power Consumption Report", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.smartplug", mnmn: "Dawon", vid: "STES-1-Dawon-Zigbee_Smart_Plug") { capability "Energy Meter" capability "Power Meter" capability "Actuator" From 048e1b7ff56b5ede86da694d836fe189aae4529a Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 14 Apr 2021 11:58:43 -0700 Subject: [PATCH 208/422] CHAD-6481 Incorrect temp reports for Zooz multisiren Zooz had reported incorrect temperature reports, and this should fix any inconsistencies between how this device reports vs how all other devices report. --- .../smartthings/zooz-multisiren.src/zooz-multisiren.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zooz-multisiren.src/zooz-multisiren.groovy b/devicetypes/smartthings/zooz-multisiren.src/zooz-multisiren.groovy index 7a4765a99d0..c3dc8aefe24 100644 --- a/devicetypes/smartthings/zooz-multisiren.src/zooz-multisiren.groovy +++ b/devicetypes/smartthings/zooz-multisiren.src/zooz-multisiren.groovy @@ -156,7 +156,8 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelR def events = [] if(cmd.sensorType == 1) { - events << createEvent([name: "temperature", value: convertTemperatureIfNeeded(cmd.scaledSensorValue, "C", cmd.precision), unit: getTemperatureScale()]) + def cmdScale = cmd.scale == 1 ? "F" : "C" + events << createEvent([name: "temperature", value: convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision), unit: getTemperatureScale()]) } else if(cmd.sensorType == 5) { events << createEvent([name: "humidity", value: cmd.scaledSensorValue]) } From 9949aa633a80f6ecf7b18b7b806ea84846399fac Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:19:44 -0700 Subject: [PATCH 209/422] MultiSensor 7 parameter settings update (#63423) * MultiSensor 7 parameter settings update Separated MS6 and MS7 configuration SET settings (byte size difference). Edited the available settings for 30 - 12 hours rather than 20 seconds to 24 hours. * Updated Configuration - Added EU Fingerprint for EU MS6 - Reduced coding in configuration and moved out similar settings outside of switch case. * Updated Configuration space to tabs Changed spaces to tabs. --- .../aeon-multisensor-6.groovy | 85 +++++++++++++------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy index 7ce6a1a69be..698020403a4 100644 --- a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy +++ b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy @@ -30,7 +30,8 @@ metadata { fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A", deviceJoinName: "Aeon Multipurpose Sensor" fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A,0x5A", deviceJoinName: "Aeon Multipurpose Sensor" - fingerprint mfr: "0086", prod: "0102", model: "0064", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 6 + fingerprint mfr: "0086", prod: "0002", model: "0064", deviceJoinName: "Aeotec Multipurpose Sensor" //EU //Aeotec MultiSensor 6 + fingerprint mfr: "0086", prod: "0102", model: "0064", deviceJoinName: "Aeotec Multipurpose Sensor" //US //Aeotec MultiSensor 6 fingerprint mfr: "0086", prod: "0202", model: "0064", deviceJoinName: "Aeotec Multipurpose Sensor" //AU //Aeotec MultiSensor 6 fingerprint mfr: "0371", prod: "0002", model: "0018", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 7 (EU) fingerprint mfr: "0371", prod: "0102", model: "0018", deviceJoinName: "Aeotec Multipurpose Sensor" //Aeotec MultiSensor 7 (US) @@ -74,12 +75,12 @@ metadata { preferences { input "motionDelayTime", "enum", title: "Motion Sensor Delay Time", - options: ["20 seconds", "40 seconds", "1 minute", "2 minutes", "3 minutes", "4 minutes"] + options: ["30 seconds", "40 seconds", "1 minute", "2 minutes", "3 minutes", "4 minutes"] input "motionSensitivity", "enum", title: "Motion Sensor Sensitivity", options: ["maximum", "normal", "minimum", "disabled"] input "reportInterval", "enum", title: "Report Interval", description: "How often the device should report in minutes", - options: ["8 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"] + options: ["1 minute", "2 minutes", "3 minutes", "4 minutes", "8 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours"] } tiles(scale: 2) { @@ -398,38 +399,66 @@ def ping() { def configure() { // This sensor joins as a secure device if you double-click the button to include it log.debug "${device.displayName} is configuring its settings" + def request = [] - + + //0. added as MSR wasn't getting detected upon pair. + request << zwave.manufacturerSpecificV2.manufacturerSpecificGet() //1. set association groups for hub - 2 groups are used to set battery refresh interval different than sensor report interval request << zwave.associationV1.associationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId) - request << zwave.associationV1.associationSet(groupingIdentifier: 2, nodeId: zwaveHubNodeId) //2. automatic report flags // param 101 -103 [4 bytes] 128: light sensor, 64 humidity, 32 temperature sensor, 15 ultraviolet sensor, 1 battery sensor // set value 241 (default for 101) to get all reports. Set value 0 for no reports (default for 102-103) //association group 1 - request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 240) + request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 1, scaledConfigurationValue: 240) //association group 2 - request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 1) - - //3. no-motion report x seconds after motion stops (default 20 secs) - request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 20) - - //4. motionSensitivity 3 levels: 3-normal, 5-maximum (default), 1-minimum, 0 - disabled - request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, - configurationValue: - motionSensitivity == "normal" ? [3] : - motionSensitivity == "minimum" ? [1] : - motionSensitivity == "disabled" ? [0] : [5]) - - //5. Parameters 111-113: report interval for association group 1-3 - //association group 1 - set in preferences, default 8 mins - request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) - - //association group 2 - report battery every 6 hours - request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6 * 60 * 60) - + request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 1, scaledConfigurationValue: 1) + + switch (state.MSR) { + case "0086-0002-0064": // MultiSensor 6 EU + case "0086-0102-0064": // MultiSensor 6 US + case "0086-0202-0064": // MultiSensor 6 AU + //3. no-motion report x seconds after motion stops (default 20 secs) + request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 20) + + //4. motionSensitivity 3 levels: 3-normal, 5-maximum (default), 1-minimum, 0 - disabled + request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, + configurationValue: + motionSensitivity == "normal" ? [3] : + motionSensitivity == "minimum" ? [1] : + motionSensitivity == "disabled" ? [0] : [5]) + + //5. Parameters 111-113: report interval for association group 1-3 + //association group 1 - set in preferences, default 8 mins + request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) + + //association group 2 - report battery every 6 hours + request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6 * 60 * 60) + break + case "0371-0002-0018": // MultiSensor 7 EU + case "0371-0102-0018": // MultiSensor 7 US + case "0371-0202-0018": // MultiSensor 7 AU + //3. no-motion report x seconds after motion stops (default 30 secs) + request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 30) + + //4. motionSensitivity 3 levels: 6-normal, 11-maximum (default), 1-minimum, 0 - disabled + request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, + configurationValue: + motionSensitivity == "normal" ? [6] : + motionSensitivity == "minimum" ? [1] : + motionSensitivity == "disabled" ? [0] : [11]) + + //5. Parameters 111-113: report interval for association group 1-3 + //association group 1 - set in preferences, default 8 mins + request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 2, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) + + //association group 2 - report battery every 6 hours + request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 2, scaledConfigurationValue: 6 * 60 * 60) + break + } + //6. report automatically ONLY on threshold change //From manual: //Enable/disable the selective reporting only when measurements reach a certain threshold or percentage set in 41-44. @@ -437,7 +466,7 @@ def configure() { //Note: If USB power, the Sensor will check the threshold every 10 seconds. If battery power, the Sensor will check the threshold //when it is waken up. request << zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 1) - + //7. query sensor data request << zwave.batteryV1.batteryGet() request << zwave.sensorBinaryV2.sensorBinaryGet(sensorType: 0x0C) //motion @@ -468,7 +497,7 @@ def configure() { private def getTimeOptionValueMap() { [ - "20 seconds": 20, + "30 seconds": 30, "40 seconds": 40, "1 minute" : 60, "2 minutes" : 2 * 60, @@ -564,4 +593,4 @@ def getReportTypesFromValue(value) { private isAeotecMultisensor7() { zwaveInfo.model.equals("0018") -} \ No newline at end of file +} From 98809b2b79b34d00b6f41f029282e74c6b5f240d Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Mon, 19 Apr 2021 10:03:53 -0700 Subject: [PATCH 210/422] BUG-2032 Send previous setpoint value if we don't intend to update the setpoint (#63570) The thermostat won't accept commands to change a setpoint that doesn't match the current mode. To appears the mobile client we need to send an event for the specified setpoint. So, we will just send the current value. --- .../zenwithin/zen-thermostat.src/zen-thermostat.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy b/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy index 1f18e6084c0..08723fce6eb 100644 --- a/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy +++ b/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy @@ -591,6 +591,8 @@ def setHeatingSetpoint(degrees) { state.heatingSetpoint = degrees.toDouble() // Use runIn to enable both setpoints to be changed if a routine/SA changes heating/cooling setpoint at the same time runIn(2, "updateSetpoints", [overwrite: true]) + } else { + sendEvent(name: "heatingSetpoint", value: device.currentValue("heatingSetpoint"), unit: getTemperatureScale()) } } @@ -600,6 +602,8 @@ def setCoolingSetpoint(degrees) { state.coolingSetpoint = degrees.toDouble() // Use runIn to enable both setpoints to be changed if a routine/SA changes heating/cooling setpoint at the same time runIn(2, "updateSetpoints", [overwrite: true]) + } else { + sendEvent(name: "coolingSetpoint", value: device.currentValue("coolingSetpoint"), unit: getTemperatureScale()) } } From 15d4ed8eeb95d84f27ac1ba3ffb7859ee2ae506a Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Mon, 19 Apr 2021 22:03:12 +0200 Subject: [PATCH 211/422] WWST-7560 - changes deviceJoinName to "Juno Connect" in the Juno fingerprint. (#63840) * WWST-7560 - changes deviceJoinName to "Juno Connect" in the Juno fingerprint. * WWST-7560 - changes deviceJoinName to "Juno Connect" in the "Samsung Electronics" fingerprint. --- .../zigbee-white-color-temperature-bulb.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 042089e2e5c..2c7140539bc 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -35,8 +35,8 @@ metadata { fingerprint profileId: "0104", deviceId: "010C", inClusters: "0006, 0008, 0300", deviceJoinName: "Light" //Generic Color Temperature Light // ABL - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-001", deviceJoinName: "WAFER", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "Juno Connect", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-001", deviceJoinName: "Juno Connect", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" // Samsung LED fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-001", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" //ITM CCT From 22934c0411a66a8e297a26cf7c0cca989c7ae329 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Wed, 21 Apr 2021 10:59:15 -0700 Subject: [PATCH 212/422] Stop running build on all branches (#64033) * Stop running build on all branches Build was running on _all_ branches that were not from a fork. Considering the number of direct branches opened via DevWS, this is not economical. * run builds on production as well --- .circleci/config.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a7c2a769a6f..3dbfd1df712 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,7 +37,14 @@ workflows: version: 2 deploy: jobs: - - build + - build: + filters: + branches: + only: + - master + - staging + - acceptance + - production - deploy-dev: requires: - build From d5d80997863733df162cc3ffdb461c823bac1dac Mon Sep 17 00:00:00 2001 From: NArlt <79513123+NArlt@users.noreply.github.com> Date: Thu, 22 Apr 2021 23:33:20 +0200 Subject: [PATCH 213/422] DevWs for TechniSat Digital GmbH containing containing TechniSat Single-Switch fixes (#63424) * DevWs for TechniSat Digital GmbH containing containing TechniSat Single-Switch * implement requested changes * update metadata according requested changes * removed custom command and changed format according review * removed obsolete code according to the review --- .../technisat-on-off-switch.groovy | 392 ++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy diff --git a/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy b/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy new file mode 100644 index 00000000000..f9e5bb6d036 --- /dev/null +++ b/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy @@ -0,0 +1,392 @@ +/** + * Copyright 2021 TechniSat + * + * 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. + * + */ +import groovy.json.JsonOutput + +metadata { + definition (name: "TechniSat On/Off switch", namespace: "TechniSat", author: "TechniSat", vid:"generic-switch-power-energy", + mnmn: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', + executeCommandsLocally: false) { + capability "Energy Meter" + capability "Switch" + capability "Power Meter" + capability "Refresh" + capability "Configuration" + capability "Health Check" + + fingerprint mfr: "0299", prod: "0002", model: "1A90", deviceJoinName: "TechniSat Switch" + } + + preferences { + parameterMap.each { + input(title: "Parameter ${it.paramZwaveNum}: ${it.title}", + description: it.descr, + type: "paragraph", + element: "paragraph") + if (it.enableSwitch) { + input(name: it.enableKey, + title: "Enable", + type: "bool", + required: false) + } + input(name: it.key, + title: it.paramName, + type: it.type, + options: it.values, + range: it.range, + required: false) + } + } +} +def installed() { + log.debug "installed()" + initStateConfig() + initialize() +} + +def updated() { + log.debug "updated()" + initialize() + syncConfig() +} + +def initialize() { + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +def getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x25: 1, // Switch Binary + 0x32: 3, // Meter + 0x56: 1, // Crc16Encap + 0x70: 2, // Configuration + 0x98: 1, // Security + ] +} + +def parse(String description) { + log.debug "parse() - description: "+description + def result = null + if (description != "updated") { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result = zwaveEvent(cmd) + log.debug("'$description' parsed to $result") + } else { + log.debug("Couldn't zwave.parse '$description'") + } + } + result +} + +def handleMeterReport(cmd) { + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kWh") + } else if (cmd.scale == 1) { + createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kVAh") + } else if (cmd.scale == 2) { + createEvent(name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W") + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + log.debug "v3 Meter report: "+cmd + handleMeterReport(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + log.debug "Basic report: "+cmd + def value = (cmd.value ? "on" : "off") + def evt = createEvent(name: "switch", value: value, type: "physical", descriptionText: "$device.displayName was turned $value") + if (evt.isStateChange) { + [evt, response(["delay 3000", meterGet(scale: 2).format()])] + } else { + evt + } +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + log.debug "Switch binary report: "+cmd + def value = (cmd.value ? "on" : "off") + createEvent(name: "switch", value: value, type: "digital", descriptionText: "$device.displayName was turned $value") +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def param = parameterMap.find( {it.paramZwaveNum == cmd.parameterNumber } ) + + if (state.currentConfig."$param.key".status != "sync") { + if (state.currentConfig."$param.key"?.newValue == cmd.scaledConfigurationValue || + state.currentConfig."$param.key".status == "init") { + log.debug "Parameter ${param.key} set to value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".status = "sync" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } else { + log.debug "Parameter ${param.key} set to value failed: is:${cmd.scaledConfigurationValue} <> ${state.currentConfig."$param.key".newValue}" + state.currentConfig."$param.key".status = "failed" + syncConfig() + } + } else { + log.debug "Parameter ${param.key} update received. value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "${device.displayName}: Unhandled: $cmd" + [:] +} + +def on() { + encapSequence([ + zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF), + zwave.switchBinaryV1.switchBinaryGet(), + meterGet(scale: 2) + ], 3000) +} + +def off() { + encapSequence([ + zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00), + zwave.switchBinaryV1.switchBinaryGet(), + meterGet(scale: 2) + ], 3000) +} + +def ping() { + log.debug "ping()" + refresh() +} + +def poll() { + sendHubCommand(refresh()) +} + +def refresh() { + log.debug "refresh()" + encapSequence([ + zwave.switchBinaryV1.switchBinaryGet(), + meterGet(scale: 0), + meterGet(scale: 2) + ]) +} + +def configure() { + log.debug "configure()" + def result = [] + + log.debug "Configure zwaveInfo: "+zwaveInfo + + initStateConfigFromDevice() + logStateConfig() + result << response(encap(meterGet(scale: 0))) + result << response(encap(meterGet(scale: 2))) + result << response(encap(zwave.switchBinaryV1.switchBinaryGet())) + result +} + +def meterGet(map) { + return zwave.meterV2.meterGet(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + log.debug "Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract Secure command from $cmd" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { + def version = commandClassVersions[cmd.commandClass as Integer] + def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) + def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data) + if (encapsulatedCommand) { + log.debug "Parsed Crc16Encap into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract CRC16 command from $cmd" + } +} + +private secEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using Secure Encapsulation, command: $cmd" + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() +} + +private crcEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using CRC16 Encapsulation, command: $cmd" + zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format() +} + +private encap(physicalgraph.zwave.Command cmd) { + if (zwaveInfo?.zw?.contains("s")) { + secEncap(cmd) + } else if (zwaveInfo?.cc?.contains("56")) { + crcEncap(cmd) + } else { + log.debug "no encapsulation supported for command: $cmd" + cmd.format() + } +} + +private encapSequence(cmds, Integer delay=250) { + delayBetween(cmds.collect{ encap(it) }, delay) +} + +private isConfigChanged(parameter) { + def settingsValue = settings."$parameter.key" + log.debug "isConfigChanged parameter:${parameter.key}: ${settingsValue}" + if (parameter.enableSwitch) { + if (settings."$parameter.enableKey" != null) { + if (settings."$parameter.enableKey" == false) { + settingsValue = 0; + } + } + } + if (settingsValue != null) { + Integer value = 0 + if (parameter.type == "number") { + value = settingsValue + } else { + value = Integer.parseInt(settingsValue) + } + if (state.currentConfig."$parameter.key".value != value) { + state.currentConfig."$parameter.key".newValue = value + log.debug "${parameter.key} set:${value} value:${state.currentConfig."$parameter.key".value} newValue:${state.currentConfig."$parameter.key".newValue}" + return true + } else if (state.currentConfig."$parameter.key".status != "sync") { + log.debug "${parameter.key} retry to set; is:${state.currentConfig."$parameter.key".value} should:${state.currentConfig."$parameter.key".newValue}" + return true + } + return false + } else { + log.debug "pref value not set yet" + return false + } +} + +private syncConfig() { + def commands = [] + parameterMap.each { + if (isConfigChanged(it)) { + log.debug "Parameter ${it.key} has been updated from value: ${state.currentConfig."$it.key".value} to ${state.currentConfig."$it.key".newValue}" + state.currentConfig."$it.key".status = "syncPending" + commands << response(encap(zwave.configurationV2.configurationSet(configurationValue: intToParam(state.currentConfig."$it.key".newValue, it.paramZwaveSize), + parameterNumber: it.paramZwaveNum, size: it.paramZwaveSize))) + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } else if (state.currentConfig."$it.key".value == null) { + log.warn "Parameter ${it.key} no. ${it.paramZwaveNum} has no value. Please check preference declaration for errors." + } + } + if(commands) { + sendHubCommand(commands,1000) + } +} + +private initStateConfig() { + log.debug "initStateConfig()" + state.currentConfig = [:] + parameterMap.each { + log.debug "set $it.key" + state.currentConfig."$it.key" = [:] + state.currentConfig."$it.key".value = new Integer('0') + state.currentConfig."$it.key".newValue = new Integer('0') + state.currentConfig."$it.key".status = "init" + } +} + +private initStateConfigFromDevice() { + log.debug "initStateConfigFromDevice()" + def commands = [] + parameterMap.each { + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } + if(commands) { + sendHubCommand(commands,1000) + } +} + +private logStateConfig() { + parameterMap.each { + log.debug "key:$it.key value: ${state.currentConfig."$it.key".value} newValue: ${state.currentConfig."$it.key".newValue} status: ${state.currentConfig."$it.key".status}" + } +} + +private List intToParam(Long value, Integer size = 1) { + def result = [] + size.times { + result = result.plus(0, (value & 0xFF) as Short) + value = (value >> 8) + } + return result +} + +private getParameterMap() { + [ + [ + title: "Wattage meter report interval", + descr: "Interval of current wattage meter reports in 10 seconds. 3 ... 8640 (30 seconds - 1 day)", + key: "wattageMeterReportInterval", + paramName: "Set Value (3..8640)", + type: "number", + range: "3..8640", + enableSwitch: true, + enableKey: "wattageMeterReportDisable", + paramZwaveNum: 2, + paramZwaveSize: 1 + ], + [ + title: "Energy meter report interval", + descr: "Interval of active energy meter reports in minutes. 10 ... 30240 (10 minutes - 3 weeks)", + key: "energyMeterReportInterval", + enableSwitch: true, + enableKey: "energyMeterReportDisable", + paramName: "Set Value (10..30240)", + type: "number", + range: "10..30240", + paramZwaveNum: 3, + paramZwaveSize: 2 + ], + [ + title: "Operation mode of button T", + descr: "Operation mode of button T", + key: "buttonModeSetting", + paramName: "Select", + type: "enum", + values: [ + 0: "0 - T1 turns L1 on, T2 turn L1 off", + 1: "1 - T1 & T2 toggle output L1" + ], + paramZwaveNum: 4, + paramZwaveSize: 1 + ], + [ + title: "External Connector", + descr: "Configuration of switch type connected to extension connector S", + key: "externalSwitchSetting", + paramName: "Select", + type: "enum", + values: [ + 0: "0 - toggle switch", + 1: "1 - push button switch" + ], + paramZwaveNum: 5, + paramZwaveSize: 1 + ], + ] +} From be854cf0f8a7e8fbcf8220d46a9ab0631b3b3e93 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Mon, 26 Apr 2021 12:46:00 -0700 Subject: [PATCH 214/422] Update PR-63423 to avoid potential exceptions, expedite logic, fix spacing (#64761) One of the map values from getTimeOptionValueMap was removed, which may cause exceptions. Also, there is a chance that on pairing the `state.MSR` value might not be available yet, so some logic from `configure()` might not get run, so we will create `state.MSR`. --- .../aeon-multisensor-6.groovy | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy index 698020403a4..aca2b1b55cf 100644 --- a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy +++ b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy @@ -75,12 +75,12 @@ metadata { preferences { input "motionDelayTime", "enum", title: "Motion Sensor Delay Time", - options: ["30 seconds", "40 seconds", "1 minute", "2 minutes", "3 minutes", "4 minutes"] + options: ["20 seconds", "30 seconds", "40 seconds", "1 minute", "2 minutes", "3 minutes", "4 minutes"] input "motionSensitivity", "enum", title: "Motion Sensor Sensitivity", options: ["maximum", "normal", "minimum", "disabled"] input "reportInterval", "enum", title: "Report Interval", description: "How often the device should report in minutes", - options: ["1 minute", "2 minutes", "3 minutes", "4 minutes", "8 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours"] + options: ["1 minute", "2 minutes", "3 minutes", "4 minutes", "8 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"] } tiles(scale: 2) { @@ -399,7 +399,7 @@ def ping() { def configure() { // This sensor joins as a secure device if you double-click the button to include it log.debug "${device.displayName} is configuring its settings" - + def request = [] //0. added as MSR wasn't getting detected upon pair. @@ -415,47 +415,52 @@ def configure() { //association group 2 request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 1, scaledConfigurationValue: 1) - + + // Expedite this if we know this info so that we can execute the code below + if (!state.MSR && zwaveInfo?.mfr && zwaveInfo.prod && zwaveInfo.model) { + state.MSR = "${zwaveInfo.mfr}-${zwaveInfo.prod}-${zwaveInfo.model}" + } + switch (state.MSR) { case "0086-0002-0064": // MultiSensor 6 EU case "0086-0102-0064": // MultiSensor 6 US case "0086-0202-0064": // MultiSensor 6 AU - //3. no-motion report x seconds after motion stops (default 20 secs) - request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 20) - - //4. motionSensitivity 3 levels: 3-normal, 5-maximum (default), 1-minimum, 0 - disabled - request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, - configurationValue: - motionSensitivity == "normal" ? [3] : - motionSensitivity == "minimum" ? [1] : - motionSensitivity == "disabled" ? [0] : [5]) - - //5. Parameters 111-113: report interval for association group 1-3 - //association group 1 - set in preferences, default 8 mins - request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) - - //association group 2 - report battery every 6 hours - request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6 * 60 * 60) + //3. no-motion report x seconds after motion stops (default 20 secs) + request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 20) + + //4. motionSensitivity 3 levels: 3-normal, 5-maximum (default), 1-minimum, 0 - disabled + request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, + configurationValue: + motionSensitivity == "normal" ? [3] : + motionSensitivity == "minimum" ? [1] : + motionSensitivity == "disabled" ? [0] : [5]) + + //5. Parameters 111-113: report interval for association group 1-3 + //association group 1 - set in preferences, default 8 mins + request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) + + //association group 2 - report battery every 6 hours + request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6 * 60 * 60) break case "0371-0002-0018": // MultiSensor 7 EU case "0371-0102-0018": // MultiSensor 7 US case "0371-0202-0018": // MultiSensor 7 AU - //3. no-motion report x seconds after motion stops (default 30 secs) - request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 30) - - //4. motionSensitivity 3 levels: 6-normal, 11-maximum (default), 1-minimum, 0 - disabled - request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, - configurationValue: - motionSensitivity == "normal" ? [6] : - motionSensitivity == "minimum" ? [1] : - motionSensitivity == "disabled" ? [0] : [11]) - - //5. Parameters 111-113: report interval for association group 1-3 - //association group 1 - set in preferences, default 8 mins - request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 2, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) - - //association group 2 - report battery every 6 hours - request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 2, scaledConfigurationValue: 6 * 60 * 60) + //3. no-motion report x seconds after motion stops (default 30 secs) + request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 30) + + //4. motionSensitivity 3 levels: 6-normal, 11-maximum (default), 1-minimum, 0 - disabled + request << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, + configurationValue: + motionSensitivity == "normal" ? [6] : + motionSensitivity == "minimum" ? [1] : + motionSensitivity == "disabled" ? [0] : [11]) + + //5. Parameters 111-113: report interval for association group 1-3 + //association group 1 - set in preferences, default 8 mins + request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 2, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8 * 60)) + + //association group 2 - report battery every 6 hours + request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 2, scaledConfigurationValue: 6 * 60 * 60) break } @@ -497,7 +502,8 @@ def configure() { private def getTimeOptionValueMap() { [ - "30 seconds": 30, + "20 seconds": 20, + "30 seconds": 30, "40 seconds": 40, "1 minute" : 60, "2 minutes" : 2 * 60, @@ -511,7 +517,7 @@ private def getTimeOptionValueMap() { "6 hours" : 6 * 60 * 60, "12 hours" : 12 * 60 * 60, "18 hours" : 18 * 60 * 60, - "24 hours" : 24 * 60 * 60, + "24 hours" : 24 * 60 * 60 ] } From cb38d87c0b6acce32cd2cb7e913937624d1ee904 Mon Sep 17 00:00:00 2001 From: Steven5517 <76581149+Steven5517@users.noreply.github.com> Date: Thu, 29 Apr 2021 01:01:17 +0800 Subject: [PATCH 215/422] DevWs for Vision-Elec. Technology Co., Ltd. containing containing Arrival Sensor HA (#53954) * DevWs for Vision-Elec. Technology Co., Ltd. containing containing Arrival Sensor HA * DevWs for Vision-Elec. Technology Co., Ltd. containing containing Arrival Sensor HA * I have reverted the comment section. I also have reverted the definition method parameters. I add "if" statements in parse() method, configure() method, updated() method and checkPresenceCallback() method to support another device. * I have fix my code. Furthermore, I add getBatteryMap() and isVision() method according to requested changes. * I already fix the code according to the comments. * I already modify the code according to the suggestion. * New separate DTH for Vision Zigbee Arrival Sensor * I already change the name of the DTH according to the suggestion. * Revert arrival-sensor-ha.groovy to original According to suggestion of reviewer revert arrival-sensor-ha.groovy to original. * Delete arrival-sensor-ha.groovy Delete this DTH that not used. --- .../vision-zigbee-arrival-sensor.groovy | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 devicetypes/vision-stevenchen/vision-zigbee-arrival-sensor.src/vision-zigbee-arrival-sensor.groovy diff --git a/devicetypes/vision-stevenchen/vision-zigbee-arrival-sensor.src/vision-zigbee-arrival-sensor.groovy b/devicetypes/vision-stevenchen/vision-zigbee-arrival-sensor.src/vision-zigbee-arrival-sensor.groovy new file mode 100644 index 00000000000..b067196b1cd --- /dev/null +++ b/devicetypes/vision-stevenchen/vision-zigbee-arrival-sensor.src/vision-zigbee-arrival-sensor.groovy @@ -0,0 +1,196 @@ +import groovy.json.JsonOutput +import physicalgraph.zigbee.zcl.DataType + +/** + * Vision Zigbee Arrival Sensor + * + * Author: Steven Chen + */ +metadata { + definition (name: "Vision Zigbee Arrival Sensor", namespace: "vision-stevenchen", author: "Steven Chen", vid: "SmartThings-smartthings-Arrival_Sensor_HA", mnmn: "SmartThings") { + capability "Tone" + capability "Actuator" + capability "Presence Sensor" + capability "Sensor" + capability "Battery" + capability "Configuration" + capability "Health Check" + + fingerprint profileId: "0104", deviceId: "000C", inClusters: "0000,0001,0003,0006,0020", outClusters: "0003,0019", manufacturer: "Vision", model: "ArrivalTagv1", deviceJoinName: "Vision Zigbee Arrival Sensor" + } + + preferences { + section { + image(name: 'educationalcontent', multiple: true, images: [ + "http://cdn.device-gse.smartthings.com/Arrival/Arrival1.png", + "http://cdn.device-gse.smartthings.com/Arrival/Arrival2.png" + ]) + } + section { + input "sensorcheckInterval", "enum", title: "Presence timeout (minutes)", description: "Tap to set", + defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false + } + section { + input "detectTime", "enum", title: "G Sensor detect time (base 16s)", description: "Tap to set", + defaultValue:"2", options: ["1", "2", "3", "4", "5", "6"], displayDuringSetup: false + } + } + + tiles { + standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { + state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#00a0dc" + state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff" + } + standardTile("beep", "device.beep", decoration: "flat") { + state "beep", label:'', action:"tone.beep", icon:"st.secondary.beep", backgroundColor:"#ffffff" + } + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + main "presence" + details(["presence", "beep", "battery"]) + } +} + +def updated() { + state.gsensor = 0 + def thedetectTime = (detectTime ? detectTime as int : 2) * 1 + def updatecmds = zigbee.writeAttribute(0x0000, 0x0000, 0x20, thedetectTime, [mfgCode: 0x120D]) + log.debug "Updatecmds: ${updatecmds}" + return response(updatecmds) +} + +def installed() { + // Arrival sensors only goes OFFLINE when Hub is off + sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false) +} + +def configure() { + def cmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.batteryConfig(3600, 3600, 0x01) + //3600 -> every 1hour battery report + zigbee.readAttribute(zigbee.ONOFF_CLUSTER, 0x0000) + zigbee.onOffConfig() + log.debug "configure -- cmds: ${cmds}" + return cmds +} + +def beep() { + log.debug "Sending Identify command to beep the sensor for 5 seconds" + return zigbee.command(0x0003, 0x00, "0500") +} + +def parse(String description) { + log.debug "description: $description" + state.lastCheckin = now() + if (description?.startsWith("catchall:")) { + def descMap = zigbee.parseDescriptionAsMap(description) + if (descMap && descMap.clusterInt == zigbee.ONOFF_CLUSTER) { + log.debug "Command: ${descMap.commandInt}" + if (descMap.commandInt == 0x01) { + log.debug "True" + handlePresenceEvent(true) + state.gsensor = 1 + } else { + log.debug "False" + stopTimer() + } + } + } else if (description?.startsWith('read attr -')) { + handleReportAttributeMessage(description) + } + + return [] +} + +private handleReportAttributeMessage(String description) { + def descMap = zigbee.parseDescriptionAsMap(description) + if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) { + handleBatteryEvent(Integer.parseInt(descMap.value, 16)) + } +} + +/** + * Create battery event from reported battery voltage. + * + * @param volts Battery voltage in .1V increments + */ +private handleBatteryEvent(volts) { + def descriptionText + if (volts == 0 || volts == 255) { + log.debug "Ignoring invalid value for voltage (${volts/10}V)" + } + else { + def batteryMap = [29:100, 28:90, 27:90, 26:70, 25:70, 24:50, 23:50, + 22:30, 21:30, 20:15, 19:8, 18:1, 17:0, 16:0, 15:0] + + def minVolts = 15 + def maxVolts = 29 + + if (volts < minVolts) { + volts = minVolts + } else if (volts > maxVolts) { + volts = maxVolts + } + def value = batteryMap[volts] + if (value != null) { + def linkText = getLinkText(device) + descriptionText = '{{ linkText }} battery was {{ value }}' + def eventMap = [ + name: 'battery', + value: value, + descriptionText: descriptionText, + translatable: true + ] + log.debug "Creating battery event for voltage=${volts/10}V: ${linkText} ${eventMap.name} is ${eventMap.value}%" + sendEvent(eventMap) + } + } +} + +private handlePresenceEvent(present) { + if (!state.gsensor && present) { + log.debug "Vision Sensor is present" + startTimer() + } else if (!present) { + log.debug "Vision Sensor is not present" + stopTimer() + } + def linkText = getLinkText(device) + def descriptionText + if ( present ) { + descriptionText = "{{ linkText }} has arrived" + } else { + descriptionText = "{{ linkText }} has left" + } + def eventMap = [ + name: "presence", + value: present ? "present" : "not present", + linkText: linkText, + descriptionText: descriptionText, + translatable: true + ] + log.debug "Creating presence event: ${device.displayName} ${eventMap.name} is ${eventMap.value}" + sendEvent(eventMap) +} + +private startTimer() { + log.debug "Scheduling periodic timer" + // Unlike stopTimer, only schedule this when running in the cloud since the hub will take care presence detection + // when it is running locally + runEvery1Minute("checkPresenceCallback", [forceForLocallyExecuting: false]) +} + +private stopTimer() { + log.debug "Stopping periodic timer" + // Always unschedule to handle the case where the DTH was running in the cloud and is now running locally + unschedule("checkPresenceCallback", [forceForLocallyExecuting: true]) + state.gsensor = 0 +} + +def checkPresenceCallback() { + def timeSinceLastCheckin = (now() - state.lastCheckin ?: 0) / 1000 + def theCheckInterval = (sensorcheckInterval ? sensorcheckInterval as int : 2) * 60 + log.debug "Sensor checked in ${timeSinceLastCheckin} seconds ago" + if (timeSinceLastCheckin >= theCheckInterval) { + handlePresenceEvent(false) + } +} \ No newline at end of file From f47d2dd4fb0f0f6b45d5a03872cd472284645476 Mon Sep 17 00:00:00 2001 From: GiuseppeWidom <57677585+GiuseppeWidom@users.noreply.github.com> Date: Tue, 4 May 2021 19:06:05 +0200 Subject: [PATCH 216/422] DevWs for WiDom srl containing containing WiDom Smart Dry Contact (#52683) * DevWs for WiDom srl containing containing WiDom Smart Dry Contact * Added in definition() "ocfDeviceType" Added deviceJoinName "WiDom Dry Contact" in this fingerprint and added Raw description in a comment Removed the whole tiles section Reformated the whole code and fixed indentation (change 4 spaces to 1 tab). Removed useless code. * Removed useless code * Deleted space inside if statements with one condition Deleted double empty lines and useless lines Indentation fix * added actuator capability, indentation fix * Indentation Fix * Indentation fix * Fix on Raw description. Removed useless code. * Fix Raw Description * Moved secEncap method code inside the encap method Fix on num clicks key control name. --- .../widom-smart-dry-contact.groovy | 321 ++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy diff --git a/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy b/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy new file mode 100644 index 00000000000..a5fdeabee2d --- /dev/null +++ b/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy @@ -0,0 +1,321 @@ +/** + * Widom Smart DRY contact + * + * 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: "WiDom Smart Dry Contact", namespace: "WiDomsrl", author: "WiDom srl", ocfDeviceType: "oic.d.switch", mnmn: "SmartThings", vid: "generic-switch") { + capability "Actuator" + capability "Switch" + capability "Configuration" + capability "Health Check" + + fingerprint mfr: "0149", prod: "1214", model: "0900", deviceJoinName: "WiDom Switch" // Raw Description zw:Ls2 type:1001 mfr:0149 prod:1214 model:0900 ver:1.00 zwv:6.04 lib:03 cc:5E,55,98,9F,6C sec:86,25,85,8E,59,72,5A,73,70,7A + } + + preferences { + input ( + title: "WiDom Smart Dry Contact manual", + description: "Tap to view the manual.", + image: "https://www.widom.it/wp-content/uploads/2019/03/widom-3d-smart-dry-contact.gif", + url: "https://www.widom.it/wp-content/uploads/2020/04/Widom_Dry_Contact_IT_070420.pdf", + type: "href", + element: "href" + ) + + parameterMap().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 + ) + } + input ( name: "logging", title: "Logging", type: "boolean", required: false ) + } +} + +def on() { + encap(zwave.basicV1.basicSet(value: 255)) +} + +def off() { + encap(zwave.basicV1.basicSet(value: 0)) +} + +//Configuration and synchronization +def updated() { + if ( state.lastUpdated && (now() - state.lastUpdated) < 500 ) return + def cmds = [] + logging("Executing updated()","info") + state.lastUpdated = now() + syncStart() +} + +private syncStart() { + boolean syncNeeded = false + boolean syncNeededGroup = false + Integer settingValue = null + parameterMap().each { + if (settings."$it.key" != null) { + settingValue = settings."$it.key" as Integer + if (state."$it.key" == null) { state."$it.key" = [value: null, 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 + } + } + } + + if (syncNeeded) { + logging("sync needed.", "info") + syncNext() + } + if (syncNeededGroup) { + logging("${device.displayName} - starting sync.", "info") + multiStatusEvent("Sync in progress.", true, true) + syncNext() + } +} + +private syncNext() { + logging("Executing syncNext()","info") + def cmds = [] + 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" + logging("Parameter number ${param.num}. Parameter Value: ${state."$param.key"?.value}","info") + cmds << response(encap(zwave.configurationV1.configurationSet(configurationValue: intToParam(state."$param.key".value, param.size), parameterNumber: param.num, size: param.size))) + cmds << response(encap(zwave.configurationV1.configurationGet(parameterNumber: param.num))) + break + } + } + + if (cmds) { + runIn(10, "syncCheck") + sendHubCommand(cmds,1000) + } else { + runIn(1, "syncCheck") + } +} + +private syncCheck() { + logging("Executing syncCheck()","info") + def failed = [] + def incorrect = [] + def notSynced = [] + parameterMap().each { + if (state."$it.key"?.state == "incorrect") { + incorrect << it + } else if (state."$it.key"?.state == "failed") { + failed << it + } else if (state."$it.key"?.state in ["inProgress","notSynced"]) { + notSynced << it + } + } + + if (failed) { + multiStatusEvent("Sync failed! Verify parameter: ${failed[0].num}", true, true) + } else if (incorrect) { + multiStatusEvent("Sync mismatch! Verify parameter: ${incorrect[0].num}", true, true) + } else if (notSynced) { + multiStatusEvent("Sync incomplete! Open settings and tap Done to try again.", true, true) + } else { + if (device.currentValue("multiStatus")?.contains("Sync")) { multiStatusEvent("Sync OK.", true, true) } + } +} + +private multiStatusEvent(String statusValue, boolean force = false, boolean display = false) { + if ( !device.currentValue("multiStatus")?.contains("Sync") || device.currentValue("multiStatus") == "Sync OK." || force ) { + sendEvent(name: "multiStatus", value: statusValue, descriptionText: statusValue, displayed: display) + } +} + +private deviceIdEvent(String value, boolean force = false, boolean display = false) { + sendEvent(name: "deviceID", value: value, descriptionText: value, displayed: display) +} + +//event handlers related to configuration and sync +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + def paramKey = parameterMap().find( {it.num == cmd.parameterNumber } ).key + logging("Parameter ${paramKey} value is ${cmd.scaledConfigurationValue} expected " + state."$paramKey".value, "info") + state."$paramKey".state = (state."$paramKey".value == cmd.scaledConfigurationValue) ? "synced" : "incorrect" + syncNext() +} + +def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationRejectedRequest cmd) { + logging("rejected request!","warn") + for ( param in parameterMap() ) { + if (state."$param.key"?.state == "inProgress") { + state."$param.key"?.state = "failed" + break + } + } +} +//event handlers +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + //ignore +} +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + logging("SwitchBinaryReport received, value: ${cmd.value} ","info") + sendEvent([name: "switch", value: (cmd.value == 0 ) ? "off": "on"]) +} + +/* +#################### +## Z-Wave Toolkit ## +#################### +*/ +def parse(String description) { + def result = [] + def deviceId = []; + logging("Parsing: ${description}") + if (description.startsWith("Err 106")) { + result = createEvent( + descriptionText: "Failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.", + eventType: "ALERT", + name: "secureInclusion", + value: "failed", + displayed: true, + ) + } else if (description == "updated") { + return null + } else { + deviceId = description.split(", ")[0] + deviceId = deviceId.split(":")[1] + logging("deviceId- ${deviceId}") + deviceIdEvent(deviceId, true, true) + def cmd = zwave.parse(description, cmdVersions()) + if (cmd) { + logging("Parsed: ${cmd}") + zwaveEvent(cmd) + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(cmdVersions()) + if (encapsulatedCommand) { + logging("Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}") + zwaveEvent(encapsulatedCommand) + } else { + logging("Unable to extract Secure command from $cmd","warn") + } +} + +def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { + def version = cmdVersions()[cmd.commandClass as Integer] + def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) + def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data) + if (encapsulatedCommand) { + logging("Parsed Crc16Encap into: ${encapsulatedCommand}") + zwaveEvent(encapsulatedCommand) + } else { + logging("Unable to extract CRC16 command from $cmd","warn") + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(cmdVersions()) + if (encapsulatedCommand) { + logging("Parsed MultiChannelCmdEncap ${encapsulatedCommand}") + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) + } else { + logging("Unable to extract MultiChannel command from $cmd","warn") + } +} + +private logging(text, type = "debug") { + if (settings.logging == "true" || type == "warn") { + log."$type" "${device.displayName} - $text" + } +} + +private multiEncap(physicalgraph.zwave.Command cmd, Integer ep) { + logging("encapsulating command using MultiChannel Encapsulation, ep: $ep command: $cmd","info") + zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:ep).encapsulate(cmd) +} + +private encap(physicalgraph.zwave.Command cmd, Integer ep) { + encap(multiEncap(cmd, ep)) +} + +private encap(physicalgraph.zwave.Command cmd) { + if (zwaveInfo.zw.contains("s")) { + logging("encapsulating command using Secure Encapsulation, command: $cmd","info") + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + logging("no encapsulation supported for command: $cmd","info") + cmd.format() + } +} + +private List intToParam(Long value, Integer size = 1) { + def result = [] + size.times { + result = result.plus(0, (value & 0xFF) as Short) + value = (value >> 8) + } + return result +} + +/* +########################## +## Device Configuration ## +########################## +*/ +private Map cmdVersions() { + [0x5E: 2, 0x86: 2, 0x72: 2, 0x59: 2, 0x98: 1, 0x25: 1, 0x5A: 1, 0x85: 2, 0x70: 1, 0x8E: 2, 0x6C: 1] +} + +private parameterMap() {[ + [key: "NumClicks", num: 1, size: 1, type: "number", min: 0, max: 7, def: 7, title: "Numbers of clicks to control the loads", + descr: "Define which sequences of clicks control the load (see device manual)."], + [key: "OffTimer", num: 10, size: 2, type: "number", def: 0, min: 0, max: 32000, title: " Timer to switch OFF the Relay", + descr: "Defines the time after which the relay is switched OFF. Time unit is set by parameter 15(see device manual)"], + [key: "OnTimer", num: 11, size: 2, type: "number", def: 0, min: 0, max: 32000, title: " Timer to switch ON the Relay", + descr: "Defines the time after which the relay is switched ON. Time unit is set by parameter 15(see device manual)"], + [key: "timerScale", num: 15, size: 1, type: "enum", options: [ + 1: "Tenth of seconds", + 2: "Seconds", + ], def: "1", title: "Timer scale", descr: "Defines the time unit used for parameters No.10 and No.11"], + [key: "oneClickScene", num: 20, size: 2, type: "number",min: 0, max: 255, def: 0, title: "One Click Scene ActivationSet", + descr: "Defines the Scene Activation Set value sent to the Lifeline group with 1 Clickon the external switch"], + [key: "twoClickScene", num: 21, size: 2, type: "number",min: 0, max: 255, def: 0, title: "Two Clicks Scene ActivationSet", + descr: "Defines the Scene Activation Set value sent to the Lifeline group with 2 Clickson the external switch"], + [key: "threeClickScene", num: 22, size: 2, type: "number",min: 0, max: 255, def: 0, title: "Three Clicks Scene ActivationSet", + descr: "Defines the Scene Activation Set value sent to the Lifeline group with 1 Clicks on the external switch"], + [key: "startUpStatus", num: 60, size: 1, type: "enum", options: [ + 1: "ON", + 2: "OFF", + 3: "PREVIOUS STATUS" + ], def: "3", title: "Start-up status", + descr: "Defines the status of the device following a restart"], + [key: "externalSwitchType", num: 62, size: 1, type: "enum", options: [ + 0: "IGNORE", + 1: "BUTTON", + 2: "SWITCH" + ], def: "1", title: " Type of external switches", + descr: "Defines the type of external switch"], +]} From 97c5d74953e30151ea051e2b961a9b72b4ffdbfe Mon Sep 17 00:00:00 2001 From: Przemyslaw Kacprowicz Date: Fri, 7 May 2021 12:21:04 +0200 Subject: [PATCH 217/422] Added multichannel encap to zwave-metering-switch --- .../zwave-metering-switch.groovy | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 d9228c0e275..b03a6136fa3 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -297,6 +297,19 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat } } +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { // Supervision encapsulated Message + // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header + cmd.parameter = cmd.parameter.drop(2) + // Updated Command Class/Command now with the remaining bytes + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand) +} + def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { def version = commandClassVersions[cmd.commandClass as Integer] def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) From eea5cd1aff84f52a3d056b8508b40a4def66a2e1 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Mon, 10 May 2021 11:00:26 -0700 Subject: [PATCH 218/422] CHAD-4340 Update Window Shade devices to use Window Shade Level (#65135) --- .../axis/axis-gear-st.src/axis-gear-st.groovy | 173 ++++---- .../qubino-flush-shutter.groovy | 5 +- .../springs-window-fashions-shade.groovy | 416 +++++++++--------- .../zigbee-window-shade-battery.groovy | 89 ++-- .../zigbee-window-shade.groovy | 77 +++- .../zwave-window-shade.groovy | 374 ++++++++-------- 6 files changed, 618 insertions(+), 516 deletions(-) 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 e9913b1128f..fd4821e5f8d 100644 --- a/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy +++ b/devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy @@ -1,8 +1,9 @@ import groovy.json.JsonOutput metadata { - definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") { + definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") { capability "Window Shade" + capability "Window Shade Level" capability "Window Shade Preset" capability "Switch Level" capability "Battery" @@ -10,18 +11,18 @@ metadata { capability "Health Check" capability "Actuator" capability "Configuration" - + // added in for Google Assistant Operability - capability "Switch" - + capability "Switch" + //Custom Commandes to achieve 25% increment control command "ShadesUp" command "ShadesDown" - + // command to stop blinds command "stop" command "getversion" - + fingerprint profileID: "0104", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear fingerprint profileId: "0104", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear fingerprint endpointID: "01, C4", profileId: "0104, C25D", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear @@ -36,7 +37,7 @@ metadata { //Updated 2019-08-09 - minor changes and improvements, onoff state reporting fixed //Updated 2019-11-11 - minor changes } - + tiles(scale: 2) { multiAttributeTile(name:"windowShade", type: "lighting", width: 3, height: 3) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { @@ -44,10 +45,10 @@ metadata { attributeState("partially open", label: 'Partially Open', action:"close", icon:"http://i.imgur.com/vBA17WL.png", backgroundColor:"#ffcc33", nextState: "closing") attributeState("closed", label: 'Closed', action:"open", icon:"http://i.imgur.com/mtHdMse.png", backgroundColor:"#bbbbdd", nextState: "opening") attributeState("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ffcc33", nextState: "stopping") - attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping") - attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") - attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") - attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping") + attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") + attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777") } tileAttribute ("device.level", key: "VALUE_CONTROL") { attributeState("VALUE_UP", action: "ShadesUp") @@ -61,9 +62,9 @@ metadata { state("closed", label:'Closed', action:"open", icon:"http://i.imgur.com/SAiEADI.png", backgroundColor:"#bbbbdd", nextState: "opening") state("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ffcc33", nextState: "stopping") state("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#bbbbdd", nextState: "stopping") - state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") - state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") - state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") + state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777") } controlTile("mediumSlider", "device.level", "slider",decoration:"flat",height:2, width: 2, inactiveLabel: true) { state("level", action:"switch level.setLevel") @@ -86,7 +87,7 @@ metadata { preferences { input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, required: false, displayDuringSetup: true, range:"1..100" } - + main(["main"]) details(["windowShade", "mediumSlider", "contPause", "home", "version", "battery", "refresh"]) } @@ -115,33 +116,37 @@ private getWINDOWCOVERING_CMD_GOTOLIFTPERCENTAGE() {0x05} private getMIN_WINDOW_COVERING_VERSION() {1093} +def getLastShadeLevel() { + device.currentState("shadeLevel") ? device.currentValue("shadeLevel") : (device.currentState("level") ? device.currentValue("level") : 0) // Try shadeLevel, if not use level, if not 0 +} + //Custom command to increment blind position by 25 % def ShadesUp() { - def shadeValue = device.latestValue("level") as Integer ?: 0 - + def shadeValue = lastShadeLevel as Integer + if (shadeValue < 100) { shadeValue = Math.min(25 * (Math.round(shadeValue / 25) + 1), 100) as Integer } - else { + else { shadeValue = 100 } //sendEvent(name:"level", value:shadeValue, displayed:true) - setLevel(shadeValue) + setShadeLevel(shadeValue) //sendEvent(name: "windowShade", value: "opening") } //Custom command to decrement blind position by 25 % def ShadesDown() { - def shadeValue = device.latestValue("level") as Integer ?: 0 - + def shadeValue = lastShadeLevel as Integer + if (shadeValue > 0) { shadeValue = Math.max(25 * (Math.round(shadeValue / 25) - 1), 0) as Integer } - else { + else { shadeValue = 0 } //sendEvent(name:"level", value:shadeValue, displayed:true) - setLevel(shadeValue) + setShadeLevel(shadeValue) //sendEvent(name: "windowShade", value: "closing") } @@ -149,7 +154,7 @@ def stop() { log.info "stop()" def shadeState = device.latestValue("windowShade") if (shadeState == "opening" || shadeState == "closing") { - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { sendEvent(name: "windowShade", value: "stopping") return zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_STOP) } @@ -159,12 +164,12 @@ def stop() { } } else { - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ - return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { + return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) } else { sendEvent(name: "windowShade", value: "stoppingNS") - return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000]) + return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000]) } } } @@ -176,42 +181,39 @@ def pause() { //Send Command through setLevel() def on() { log.info "on()" - sendEvent(name: "windowShade", value: "opening") sendEvent(name: "switch", value: "on") - - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { - zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN) - } - else { - setLevel(100) - } + open() } //Send Command through setLevel() def off() { log.info "off()" - sendEvent(name: "windowShade", value: "closing") sendEvent(name: "switch", value: "off") close() - //zigbee.off() } //Command to set the blind position (%) and log the event def setLevel(value, rate=null) { log.info "setLevel ($value)" - + + setShadeLevel(value) +} + +def setShadeLevel(value) { + log.info "setShadeLevel ($value)" Integer currentLevel = state.level - + def i = value as Integer - sendEvent(name:"level", value: value, displayed:true) - - if ( i == 0) { + sendEvent(name:"level", value: value, unit:"%", displayed: false) + sendEvent(name:"shadeLevel", value: value, unit:"%", displayed:true) + + if (i == 0) { sendEvent(name: "switch", value: "off") } else { sendEvent(name: "switch", value: "on") } - + if (i > currentLevel) { sendEvent(name: "windowShade", value: "opening") } @@ -219,8 +221,8 @@ def setLevel(value, rate=null) { sendEvent(name: "windowShade", value: "closing") } //setWindowShade(i) - - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ + + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { zigbee.command(CLUSTER_WINDOWCOVERING,WINDOWCOVERING_CMD_GOTOLIFTPERCENTAGE, zigbee.convertToHexString(100-i,2)) } else { @@ -232,28 +234,28 @@ def setLevel(value, rate=null) { def open() { log.info "open()" sendEvent(name: "windowShade", value: "opening") - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN) } else { - setLevel(100) - } + setShadeLevel(100) + } } //Send Command through setLevel() def close() { log.info "close()" sendEvent(name: "windowShade", value: "closing") - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_CLOSE) } else { - setLevel(0) + setShadeLevel(0) } } def presetPosition() { log.info "presetPosition()" - setLevel(preset ?: state.preset ?: 50) + setShadeLevel(preset ?: state.preset ?: 50) } //Reporting of Battery & position levels @@ -262,9 +264,9 @@ def ping(){ return refresh() } -//Set blind State based on position (which shows appropriate image) +//Set blind State based on position (which shows appropriate image) def setWindowShade(value) { - if ((value>0)&&(value<99)){ + if ((value>0)&&(value<99)) { sendEvent(name: "windowShade", value: "partially open", displayed:true) } else if (value >= 99) { @@ -279,32 +281,32 @@ def setWindowShade(value) { def refresh() { log.debug "parse() refresh" def cmds_refresh = null - - if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){ + + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { cmds_refresh = zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE) } else { cmds_refresh = zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) } - - cmds_refresh = cmds_refresh + + + cmds_refresh = cmds_refresh + zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) + zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) - + log.info "refresh() --- cmds: $cmds_refresh" - + return cmds_refresh } def getversion () { //state.currentVersion = 0 - sendEvent(name: "version", value: "Checking Version ... ") + sendEvent(name: "version", value: "Checking Version ... ") return zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID) } //configure reporting -def configure() { +def configure() { state.currentVersion = 0 sendEvent(name: "windowShade", value: "unknown") log.debug "Configuring Reporting and Bindings." @@ -316,33 +318,42 @@ def configure() { zigbee.readAttribute(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE) + zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) + zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) - - def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) + + + def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE, 0x10, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, 0x20, 1, 3600, 0x00) + zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY, 0x20, 1, 3600, 0x01) - + log.info "configure() --- cmds: $cmds" return attrs_refresh + cmds } def parse(String description) { log.trace "parse() --- description: $description" - + Map map = [:] + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + def event = zigbee.getEvent(description) if (event && description?.startsWith('on/off')) { log.trace "sendEvent(event)" sendEvent(event) } - + else if ((description?.startsWith('read attr -')) || (description?.startsWith('attr report -'))) { map = parseReportAttributeMessage(description) def result = map ? createEvent(map) : null + + if (map.name == "level") { + result = [result, createEvent([name: "shadeLevel", value: map.value, unit: map.unit])] + } + log.debug "parse() --- returned: $result" return result - } + } } private Map parseReportAttributeMessage(String description) { @@ -352,7 +363,7 @@ private Map parseReportAttributeMessage(String description) { resultMap.name = "battery" def batteryValue = Math.round((Integer.parseInt(descMap.value, 16))/2) log.debug "parseDescriptionAsMap() --- Battery: $batteryValue" - if ((batteryValue >= 0)&&(batteryValue <= 100)){ + if ((batteryValue >= 0)&&(batteryValue <= 100)) { resultMap.value = batteryValue } else { @@ -366,23 +377,27 @@ private Map parseReportAttributeMessage(String description) { //Set icon based on device feedback for the open, closed, & partial configuration resultMap.value = levelValue state.level = levelValue + resultMap.unit = "%" + resultMap.displayed = false setWindowShade(levelValue) } else if (descMap.clusterInt == CLUSTER_LEVEL && descMap.attrInt == LEVEL_ATTR_LEVEL) { //log.debug "parse() --- returned level :$state.currentVersion " - def currentLevel = state.level - + def currentLevel = state.level + resultMap.name = "level" def levelValue = Math.round(Integer.parseInt(descMap.value, 16)) def levelValuePercent = Math.round((levelValue/255)*100) //Set icon based on device feedback for the open, closed, & partial configuration resultMap.value = levelValuePercent state.level = levelValuePercent - + resultMap.unit = "%" + resultMap.displayed = false + if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) { //Integer currentLevel = state.level - sendEvent(name:"level", value: levelValuePercent, displayed:true) - + sendEvent(name:"level", value: levelValuePercent, unit: "%", displayed: false) + if (levelValuePercent > currentLevel) { sendEvent(name: "windowShade", value: "opening") } else if (levelValuePercent < currentLevel) { @@ -396,21 +411,21 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.clusterInt == CLUSTER_BASIC && descMap.attrInt == BASIC_ATTR_SWBUILDID) { resultMap.name = "version" def versionString = descMap.value - + StringBuilder output = new StringBuilder("") StringBuilder output2 = new StringBuilder("") - + for (int i = 0; i < versionString.length(); i += 2) { String str = versionString.substring(i, i + 2) - output.append((char) (Integer.parseInt(str, 16))) + output.append((char) (Integer.parseInt(str, 16))) if (i > 19) { output2.append((char) (Integer.parseInt(str, 16))) } - } - + } + def current = Integer.parseInt(output2.toString()) state.currentVersion = current - resultMap.value = output.toString() + resultMap.value = output.toString() } else { log.debug "parseReportAttributeMessage() --- ignoring attribute" diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy index 13845b2c378..f538774b19d 100644 --- a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -12,6 +12,7 @@ * for the specific language governing permissions and limitations under the License. * */ +import groovy.json.JsonOutput metadata { definition (name: "Qubino Flush Shutter", namespace: "qubino", author: "SmartThings", ocfDeviceType: "oic.d.blind", mcdSync: true) { @@ -92,7 +93,7 @@ def installed() { state.currentPreferencesState."$it.key".status = "synced" } // Preferences template end - sendEvent(name: "supportedWindowShadeCommands", value: ["open", "close", "pause"]) + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) } def updated() { @@ -251,7 +252,7 @@ def setShadeLevel(level) { } def setSlats(level) { - def time = (int) (state.timeOfVenetianMovement * 1.1) + def time = (int) (state.timeOfVenetianMovement * 1.1) sendHubCommand([ encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 2), "delay ${time}", 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 7af556576ba..48a0482e4e2 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 @@ -15,275 +15,285 @@ import groovy.json.JsonOutput metadata { - definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { - capability "Window Shade" - capability "Window Shade Preset" - capability "Battery" - capability "Refresh" - capability "Health Check" - capability "Actuator" - capability "Sensor" - - command "stop" - - capability "Switch Level" // until we get a Window Shade Level capability - - // This device handler is specifically for SWF window coverings - // -// fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade" -// fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade" - fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Springs Window Treatment" //Window Shade - fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Springs Window Treatment" //Roller Shade - } - - simulator { - status "open": "command: 2603, payload: FF" - status "closed": "command: 2603, payload: 00" - status "10%": "command: 2603, payload: 0A" - status "66%": "command: 2603, payload: 42" - status "99%": "command: 2603, payload: 63" - status "battery 100%": "command: 8003, payload: 64" - status "battery low": "command: 8003, payload: FF" - - // reply messages - reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" - reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" - reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" - reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" - } - - tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){ - tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" - attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open" - attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" - } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"setLevel" - } - } - - standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { - state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" - } - - standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" - state "disabled", label:'', action:"", icon:"st.secondary.refresh" - } - - valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { - state "battery", label:'batt.', unit:"", - backgroundColors:[ - [value: 0, color: "#bc2323"], - [value: 6, color: "#44b621"] - ] - } - - preferences { - input "switchDirection", "bool", title: "Flip the orientation of the shade", defaultValue: false, required: false, displayDuringSetup: false -// input "preset", "number", title: "Default half-open position (1-100). Springs Window Fashions users should consult their manuals.", defaultValue: 50, required: false, displayDuringSetup: false - } - - main(["windowShade"]) - details(["windowShade", "home", "refresh", "battery"]) - - } + definition (name: "Springs Window Fashions Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { + capability "Window Shade" + capability "Window Shade Level" + capability "Window Shade Preset" + capability "Switch Level" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Actuator" + capability "Sensor" + + command "stop" + + // This device handler is specifically for SWF window coverings + // + //fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Shade" + //fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Shade" + fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Springs Window Treatment" //Window Shade + fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Springs Window Treatment" //Roller Shade + } + + simulator { + status "open": "command: 2603, payload: FF" + status "closed": "command: 2603, payload: 00" + status "10%": "command: 2603, payload: 0A" + status "66%": "command: 2603, payload: 42" + status "99%": "command: 2603, payload: 63" + status "battery 100%": "command: 8003, payload: 64" + status "battery low": "command: 8003, payload: FF" + + // reply messages + reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" + reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" + reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" + reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){ + tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" + attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#00A0DC", nextState:"partially open" + attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" + } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" + } + } + + standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { + state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" + } + + standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" + state "disabled", label:'', action:"", icon:"st.secondary.refresh" + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'batt.', unit:"", + backgroundColors:[ + [value: 0, color: "#bc2323"], + [value: 6, color: "#44b621"] + ] + } + + preferences { + input "switchDirection", "bool", title: "Flip the orientation of the shade", defaultValue: false, required: false, displayDuringSetup: false + //input "preset", "number", title: "Default half-open position (1-100). Springs Window Fashions users should consult their manuals.", defaultValue: 50, required: false, displayDuringSetup: false + } + + main(["windowShade"]) + details(["windowShade", "home", "refresh", "battery"]) + + } } def parse(String description) { - def result = null - //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) - // TODO: Workaround manual parsing of v4 multilevel report - def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value - if (cmd) { - result = zwaveEvent(cmd) - } - log.debug "Parsed '$description' to ${result.inspect()}" - return result + def result = null + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + + //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) + // TODO: Workaround manual parsing of v4 multilevel report + def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug "Parsed '$description' to ${result.inspect()}" + return result } def getCheckInterval() { - // These are battery-powered devices, and it's not very critical - // to know whether they're online or not – 12 hrs - 4 * 60 * 60 + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + 4 * 60 * 60 } 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()) + 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()) } def updated() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - def cmds = [] - if (!device.latestState("battery")) { - cmds << zwave.batteryV1.batteryGet().format() - } - - if (!device.getDataValue("MSR")) { - cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() - } - - log.debug("Updated with settings $settings") - cmds << zwave.switchMultilevelV1.switchMultilevelGet().format() - response(cmds) + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + def cmds = [] + if (!device.latestState("battery")) { + cmds << zwave.batteryV1.batteryGet().format() + } + + if (!device.getDataValue("MSR")) { + cmds << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format() + } + + log.debug("Updated with settings $settings") + cmds << zwave.switchMultilevelV1.switchMultilevelGet().format() + response(cmds) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } private handleLevelReport(physicalgraph.zwave.Command cmd) { - def descriptionText = null - def shadeValue = null - - def level = cmd.value as Integer - level = switchDirection ? 99-level : level - if (level >= 99) { - level = 100 - shadeValue = "open" - } else if (level <= 0) { - level = 0 // unlike dimmer switches, the level isn't saved when closed - shadeValue = "closed" - } else { - shadeValue = "partially open" - descriptionText = "${device.displayName} shade is ${level}% open" - } - checkLevelReport(level) - def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) - def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) - - def result = [stateEvent, levelEvent] - if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { - log.debug "requesting battery" - state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row - result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) - } - result + def descriptionText = null + def shadeValue = null + + def level = cmd.value as Integer + level = switchDirection ? 99-level : level + if (level >= 99) { + level = 100 + shadeValue = "open" + } else if (level <= 0) { + level = 0 // unlike dimmer switches, the level isn't saved when closed + shadeValue = "closed" + } else { + shadeValue = "partially open" + descriptionText = "${device.displayName} shade is ${level}% open" + } + checkLevelReport(level) + + def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) + def shadeLevelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") + def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: shadeLevelEvent.isStateChange) + + def result = [stateEvent, shadeLevelEvent, levelEvent] + if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { + log.debug "requesting battery" + state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row + result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) + } + result } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { - [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), - response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] + [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { - def map = [ name: "battery", unit: "%" ] - if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { - map.value = 1 - map.descriptionText = "${device.displayName} has a low battery" - map.isStateChange = true - } else { - map.value = cmd.batteryLevel - } - state.lastbatt = now() - if (map.value <= 1 && device.latestValue("battery") != null && device.latestValue("battery") - map.value > 20) { - // Springs shades sometimes erroneously report a low battery when rapidly actuated manually. They'll still - // refuse to actuate after one of these reports, but this will limit the bad data that gets surfaced - log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" - } else { - createEvent(map) - } + def map = [ name: "battery", unit: "%" ] + if (cmd.batteryLevel == 0xFF || cmd.batteryLevel == 0) { + map.value = 1 + map.descriptionText = "${device.displayName} has a low battery" + map.isStateChange = true + } else { + map.value = cmd.batteryLevel + } + state.lastbatt = now() + if (map.value <= 1 && device.latestValue("battery") != null && device.latestValue("battery") - map.value > 20) { + // Springs shades sometimes erroneously report a low battery when rapidly actuated manually. They'll still + // refuse to actuate after one of these reports, but this will limit the bad data that gets surfaced + log.warn "Erroneous battery report dropped from ${device.latestValue("battery")} to $map.value. Not reporting" + } else { + createEvent(map) + } } def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { - // the docs we got said that the device would send a notification report, but we've determined that - // is not true + // the docs we got said that the device would send a notification report, but we've determined that + // is not true } def zwaveEvent(physicalgraph.zwave.Command cmd) { - log.debug "unhandled $cmd" - return [] + log.debug "unhandled $cmd" + return [] } def open() { - log.debug "open()" - def level = switchDirection ? 0 : 99 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() - // zwave.basicV1.basicSet(value: 0xFF).format() + log.debug "open()" + + setShadeLevel(99) // Handle switchDirection in setShadeLevel } def close() { - log.debug "close()" - def level = switchDirection ? 99 : 0 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() - //zwave.basicV1.basicSet(value: 0).format() + log.debug "close()" + + setShadeLevel(0) // Handle switchDirection in setShadeLevel } def setLevel(value, duration = null) { - log.debug "setLevel(${value.inspect()})" - Integer level = value as Integer - level = switchDirection ? 99-level : level - if (level < 0) level = 0 - if (level > 99) level = 99 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() + log.debug "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + Integer level = Math.max(Math.min(value as Integer, 99), 0) + + level = switchDirection ? 99-level : level + + log.debug "setShadeLevel($value) -> $level" + + levelChangeFollowUp(level) // Follow up in a few seconds to make sure the shades didn't "forget" to send us level updates + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { - zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() + zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format() } def pause() { - log.debug "pause()" - stop() + log.debug "pause()" + stop() } def stop() { - log.debug "stop()" - zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() + log.debug "stop()" + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() } def ping() { - zwave.switchMultilevelV1.switchMultilevelGet().format() + zwave.switchMultilevelV1.switchMultilevelGet().format() } def refresh() { - log.debug "refresh()" - delayBetween([ - zwave.switchMultilevelV1.switchMultilevelGet().format(), - zwave.batteryV1.batteryGet().format() - ], 1500) + log.debug "refresh()" + delayBetween([ + zwave.switchMultilevelV1.switchMultilevelGet().format(), + zwave.batteryV1.batteryGet().format() + ], 1500) } def levelChangeFollowUp(expectedLevel) { - state.expectedValue = expectedLevel - state.levelChecks = 0 - runIn(5, "checkLevel", [overwrite: true]) + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) } def checkLevelReport(value) { - if (state.expectedValue != null) { - if ((state.expectedValue == 99 && value >= 99) || - (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { - unschedule("checkLevel") - } - } + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } } def checkLevel() { - if (state.levelChecks != null && state.levelChecks < 5) { - state.levelChecks = state.levelChecks + 1 - runIn(5, "checkLevel", [overwrite: true]) - sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) - } else { - unschedule("checkLevel") - } -} \ No newline at end of file + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } +} diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index b47223f337b..174eceb9e44 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -11,6 +11,7 @@ * 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 @@ -21,6 +22,7 @@ metadata { capability "Configuration" capability "Refresh" capability "Window Shade" + capability "Window Shade Level" capability "Window Shade Preset" capability "Health Check" capability "Switch Level" @@ -41,13 +43,16 @@ metadata { } tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" - attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" - attributeState "partially open", label: 'Partially open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#d45614", nextState: "closing" - attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" - attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" + attributeState "open", label: 'Open', action: "close", icon: "st.shades.shade-open", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "closed", label: 'Closed', action: "open", icon: "st.shades.shade-closed", backgroundColor: "#ffffff", nextState: "opening" + attributeState "partially open", label: 'Partially open', action: "close", icon: "st.shades.shade-open", backgroundColor: "#00A0DC", nextState: "closing" + attributeState "opening", label: 'Opening', action: "pause", icon: "st.shades.shade-opening", backgroundColor: "#00A0DC", nextState: "partially open" + attributeState "closing", label: 'Closing', action: "pause", icon: "st.shades.shade-closing", backgroundColor: "#ffffff", nextState: "partially open" + } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" } } standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { @@ -59,18 +64,12 @@ metadata { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } - valueTile("shadeLevel", "device.level", width: 4, height: 1) { - state "level", label: 'Shade is ${currentValue}% up', defaultState: true - } valueTile("batteryLevel", "device.battery", width: 2, height: 2) { state "battery", label:'${currentValue}% battery', unit:"" } - controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { - state "level", action:"switch level.setLevel" - } main "windowShade" - details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh", "batteryLevel"]) + details(["windowShade", "contPause", "presetPosition", "refresh", "batteryLevel"]) } } @@ -98,22 +97,31 @@ private List collectAttributes(Map descMap) { def installed() { log.debug "installed" - sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"])) + + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) } // Parse incoming device messages to generate events def parse(String description) { log.debug "description:- ${description}" + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + if (description?.startsWith("read attr -")) { Map descMap = zigbee.parseDescriptionAsMap(description) + if (isBindingTableMessage(description)) { parseBindingTableMessage(description) } else if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) { log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}" List descMaps = collectAttributes(descMap) def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT } + if (liftmap && liftmap.value) { def newLevel = zigbee.convertHexToInt(liftmap.value) + if (shouldInvertLiftPercentage()) { // some devices report % level of being closed (instead of % level of being opened) // inverting that logic is needed here to avoid a code duplication @@ -127,18 +135,23 @@ def parse(String description) { levelEventHandler(valueInt) } else if (reportsBatteryPercentage() && descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && zigbee.convertHexToInt(descMap?.attrId) == BATTERY_PERCENTAGE_REMAINING && descMap.value) { def batteryLevel = zigbee.convertHexToInt(descMap.value) + batteryPercentageEventHandler(batteryLevel) } } } def levelEventHandler(currentLevel) { - def lastLevel = device.currentValue("level") + def lastLevel = device.currentState("shadeLevel") ? device.currentValue("shadeLevel") : device.currentValue("level") // Try shadeLevel, if not use level and pass to logic below + log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" + if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { - sendEvent(name: "level", value: currentLevel) + sendEvent(name: "shadeLevel", value: currentLevel, unit: "%") + sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) + if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { @@ -153,8 +166,9 @@ def levelEventHandler(currentLevel) { } def updateFinalState() { - def level = device.currentValue("level") + def level = device.currentValue("shadeLevel") log.debug "updateFinalState: ${level}" + if (level > 0 && level < 100) { sendEvent(name: "windowShade", value: "partially open") } @@ -169,27 +183,39 @@ def batteryPercentageEventHandler(batteryLevel) { def close() { log.info "close()" - setLevel(0) + + setShadeLevel(0) } def open() { log.info "open()" - setLevel(100) + + setShadeLevel(100) } -def setLevel(data, rate = null) { - log.info "setLevel()" +def setLevel(value, rate = null) { + log.info "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + log.info "setShadeLevel($value)" + + Integer level = Math.max(Math.min(value as Integer, 100), 0) def cmd + if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { // some devices keeps % level of being closed (instead of % level of being opened) // inverting that logic is needed here - data = 100 - data + level = 100 - level } - cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2)) + cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(level, 2)) } else { - cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) + cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(level * 255 / 100), 2)) } + return cmd } @@ -203,7 +229,7 @@ def pause() { } def presetPosition() { - setLevel(preset ?: 50) + setShadeLevel(preset ?: 50) } /** @@ -216,21 +242,26 @@ def ping() { def refresh() { log.info "refresh()" def cmds + if (supportsLiftPercentage()) { cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT) } else { cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL) } + return cmds } def configure() { - // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) + def cmds + log.info "configure()" + + // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + log.debug "Configuring Reporting and Bindings." - def cmds if (supportsLiftPercentage()) { cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 2, 600, null) } else { @@ -242,7 +273,6 @@ def configure() { } if (reportsBatteryPercentage()) { - cmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_PERCENTAGE_REMAINING, DataType.UINT8, 30, 21600, 0x01) } @@ -255,6 +285,7 @@ def usesLocalGroupBinding() { private def parseBindingTableMessage(description) { Integer groupAddr = getGroupAddrFromBindingTable(description) + if (groupAddr) { List cmds = addHubToGroup(groupAddr) cmds?.collect { new physicalgraph.device.HubAction(it) } @@ -265,7 +296,9 @@ private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) def groupEntry = btr?.table_entries?.find { it.dstAddrMode == 1 } + log.info "Found ${groupEntry}" + !groupEntry?.dstAddr ?: Integer.parseInt(groupEntry.dstAddr, 16) } 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 ee385f3c572..ab11c74955d 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -21,6 +21,7 @@ metadata { capability "Configuration" capability "Refresh" capability "Window Shade" + capability "Window Shade Level" capability "Window Shade Preset" capability "Health Check" capability "Switch Level" @@ -41,7 +42,7 @@ metadata { } tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4) { tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") { attributeState "open", label: 'Open', action: "close", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "closing" attributeState "closed", label: 'Closed', action: "open", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "opening" @@ -49,6 +50,9 @@ metadata { attributeState "opening", label: 'Opening', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_open.png", backgroundColor: "#00A0DC", nextState: "partially open" attributeState "closing", label: 'Closing', action: "pause", icon: "http://www.ezex.co.kr/img/st/window_close.png", backgroundColor: "#ffffff", nextState: "partially open" } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" + } } standardTile("contPause", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { state "pause", label:"", icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc" @@ -59,15 +63,9 @@ metadata { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 1) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } - valueTile("shadeLevel", "device.level", width: 4, height: 1) { - state "level", label: 'Shade is ${currentValue}% up', defaultState: true - } - controlTile("levelSliderControl", "device.level", "slider", width:2, height: 1, inactiveLabel: false) { - state "level", action:"switch level.setLevel" - } main "windowShade" - details(["windowShade", "contPause", "presetPosition", "shadeLevel", "levelSliderControl", "refresh"]) + details(["windowShade", "contPause", "presetPosition", "refresh"]) } } @@ -88,22 +86,31 @@ private List collectAttributes(Map descMap) { if (descMap.additionalAttrs) { descMaps.addAll(descMap.additionalAttrs) } + return descMaps } // Parse incoming device messages to generate events def parse(String description) { log.debug "description:- ${description}" + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + if (description?.startsWith("read attr -")) { Map descMap = zigbee.parseDescriptionAsMap(description) + if (isBindingTableMessage(description)) { parseBindingTableMessage(description) } else if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) { log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}" List descMaps = collectAttributes(descMap) def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT } + if (liftmap && liftmap.value) { def newLevel = zigbee.convertHexToInt(liftmap.value) + if (shouldInvertLiftPercentage()) { // some devices report % level of being closed (instead of % level of being opened) // inverting that logic is needed here to avoid a code duplication @@ -119,14 +126,21 @@ def parse(String description) { } } +def getLastLevel() { + device.currentState("shadeLevel") ? device.currentValue("shadeLevel") : device.currentValue("level") // Try shadeLevel, if not use level and pass to logic below +} + def levelEventHandler(currentLevel) { - def lastLevel = device.currentValue("level") log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" + if ((lastLevel == "undefined" || currentLevel == lastLevel) && state.invalidSameLevelEvent) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { state.invalidSameLevelEvent = true - sendEvent(name: "level", value: currentLevel) + + sendEvent(name: "shadeLevel", value: currentLevel, unit: "%") + sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) + if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { @@ -141,8 +155,9 @@ def levelEventHandler(currentLevel) { } def updateFinalState() { - def level = device.currentValue("level") + def level = device.currentValue("shadeLevel") log.debug "updateFinalState: ${level}" + if (level > 0 && level < 100) { sendEvent(name: "windowShade", value: "partially open") } @@ -162,25 +177,33 @@ def open() { zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_OPEN) } -def setLevel(data, rate = null) { - log.info "setLevel()" - def currentLevel = device.currentValue("level") +def setLevel(value, rate = null) { + log.info "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + log.info "setShadeLevel($value)" + + Integer level = Math.max(Math.min(value as Integer, 100), 0) + def cmd - if (isSomfy() && Math.abs(data - currentLevel) <= GLYDEA_MOVE_THRESHOLD) { + if (isSomfy() && Math.abs(level - lastLevel) <= GLYDEA_MOVE_THRESHOLD) { state.invalidSameLevelEvent = false } - def cmd if (supportsLiftPercentage()) { if (shouldInvertLiftPercentage()) { // some devices keeps % level of being closed (instead of % level of being opened) // inverting that logic is needed here - data = 100 - data + level = 100 - level } - cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2)) + cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(level, 2)) } else { - cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2)) + cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(level * 255 / 100), 2)) } + return cmd } @@ -196,7 +219,7 @@ def pause() { } def presetPosition() { - setLevel(preset ?: 50) + setShadeLevel(preset ?: 50) } /** @@ -209,26 +232,34 @@ def ping() { def refresh() { log.info "refresh()" def cmds + if (supportsLiftPercentage()) { cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT) } else { cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL) } + return cmds } def installed() { + log.debug "installed" + state.invalidSameLevelEvent = true + 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) + def cmds + log.info "configure()" + + // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + log.debug "Configuring Reporting and Bindings." - def cmds if (supportsLiftPercentage()) { cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 0, 600, null) } else { @@ -250,7 +281,9 @@ private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) def groupEntry = btr?.table_entries?.find { it.dstAddrMode == 1 } + log.info "Found ${groupEntry}" + !groupEntry?.dstAddr ?: Integer.parseInt(groupEntry.dstAddr, 16) } 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 c43694d37e9..67554aa4a70 100644 --- a/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy +++ b/devicetypes/smartthings/zwave-window-shade.src/zwave-window-shade.groovy @@ -15,252 +15,262 @@ import groovy.json.JsonOutput metadata { - definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { - capability "Window Shade" - capability "Window Shade Preset" - capability "Battery" - capability "Refresh" - capability "Health Check" - capability "Actuator" - capability "Sensor" - - command "stop" - - capability "Switch Level" // until we get a Window Shade Level capability - - // This device handler is specifically for non-SWF position-aware window coverings - // - fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade - fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade -// fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds" -// fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade" - } - - simulator { - status "open": "command: 2603, payload: FF" - status "closed": "command: 2603, payload: 00" - status "10%": "command: 2603, payload: 0A" - status "66%": "command: 2603, payload: 42" - status "99%": "command: 2603, payload: 63" - status "battery 100%": "command: 8003, payload: 64" - status "battery low": "command: 8003, payload: FF" - - // reply messages - reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" - reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" - reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" - reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" - } - - tiles(scale: 2) { - multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4){ - tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" - attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing" - attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open" - attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" - } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"setLevel" - } - } - - standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { - state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" - } - - standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" - state "disabled", label:'', action:"", icon:"st.secondary.refresh" - } - - valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { - state "battery", label:'${currentValue}% battery', unit:"" - } - - preferences { - input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false - } - - main(["windowShade"]) - details(["windowShade", "home", "refresh", "battery"]) - - } + definition (name: "Z-Wave Window Shade", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.blind") { + capability "Window Shade" + capability "Window Shade Level" + capability "Window Shade Preset" + capability "Switch Level" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Actuator" + capability "Sensor" + + command "stop" + + // This device handler is specifically for non-SWF position-aware window coverings + // + fingerprint type: "0x1107", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade + fingerprint type: "0x9A00", cc: "0x5E,0x26", deviceJoinName: "Window Treatment" //Window Shade +// fingerprint mfr:"026E", prod:"4353", model:"5A31", deviceJoinName: "Window Blinds" +// fingerprint mfr:"026E", prod:"5253", model:"5A31", deviceJoinName: "Roller Shade" + } + + simulator { + status "open": "command: 2603, payload: FF" + status "closed": "command: 2603, payload: 00" + status "10%": "command: 2603, payload: 0A" + status "66%": "command: 2603, payload: 42" + status "99%": "command: 2603, payload: 63" + status "battery 100%": "command: 8003, payload: 64" + status "battery low": "command: 8003, payload: FF" + + // reply messages + reply "2001FF,delay 1000,2602": "command: 2603, payload: 10 FF FE" + reply "200100,delay 1000,2602": "command: 2603, payload: 60 00 FE" + reply "200142,delay 1000,2602": "command: 2603, payload: 10 42 FE" + reply "200163,delay 1000,2602": "command: 2603, payload: 10 63 FE" + } + + tiles(scale: 2) { + multiAttributeTile(name:"windowShade", type: "lighting", width: 6, height: 4){ + tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") { + attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening" + attributeState "partially open", label:'Open', action:"close", icon:"st.shades.shade-open", backgroundColor:"#00A0DC", nextState:"closing" + attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#00A0DC", nextState:"partially open" + attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open" + } + tileAttribute ("device.windowShadeLevel", key: "SLIDER_CONTROL") { + attributeState "shadeLevel", action:"setShadeLevel" + } + } + + standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") { + state "default", label: "home", action:"presetPosition", icon:"st.Home.home2" + } + + standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled" + state "disabled", label:'', action:"", icon:"st.secondary.refresh" + } + + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + preferences { + input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false + } + + main(["windowShade"]) + details(["windowShade", "home", "refresh", "battery"]) + + } } def parse(String description) { - def result = null - //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) - // TODO: Workaround manual parsing of v4 multilevel report - def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value - if (cmd) { - result = zwaveEvent(cmd) - } - log.debug "Parsed '$description' to ${result.inspect()}" - return result + def result = null + + if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) { + sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%") + } + + //if (description =~ /command: 2603, payload: ([0-9A-Fa-f]{6})/) + // TODO: Workaround manual parsing of v4 multilevel report + def cmd = zwave.parse(description, [0x20: 1, 0x26: 3]) // TODO: switch to SwitchMultilevel v4 and use target value + if (cmd) { + result = zwaveEvent(cmd) + } + log.debug "Parsed '$description' to ${result.inspect()}" + return result } def getCheckInterval() { - // These are battery-powered devices, and it's not very critical - // to know whether they're online or not – 12 hrs - 4 * 60 * 60 + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + 4 * 60 * 60 } 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()) + 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()) } def updated() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - if (!device.latestState("battery")) { - response(zwave.batteryV1.batteryGet()) - } + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + if (!device.latestState("battery")) { + response(zwave.batteryV1.batteryGet()) + } } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - handleLevelReport(cmd) + handleLevelReport(cmd) } private handleLevelReport(physicalgraph.zwave.Command cmd) { - def descriptionText = null - def shadeValue = null - - def level = cmd.value as Integer - if (level >= 99) { - level = 100 - shadeValue = "open" - } else if (level <= 0) { - level = 0 // unlike dimmer switches, the level isn't saved when closed - shadeValue = "closed" - } else { - shadeValue = "partially open" - descriptionText = "${device.displayName} shade is ${level}% open" - } - checkLevelReport(level) - def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) - def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: levelEvent.isStateChange) - - def result = [stateEvent, levelEvent] - if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { - log.debug "requesting battery" - state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row - result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) - } - result + def descriptionText = null + def shadeValue = null + + def level = cmd.value as Integer + if (level >= 99) { + level = 100 + shadeValue = "open" + } else if (level <= 0) { + level = 0 // unlike dimmer switches, the level isn't saved when closed + shadeValue = "closed" + } else { + shadeValue = "partially open" + descriptionText = "${device.displayName} shade is ${level}% open" + } + checkLevelReport(level) + + def levelEvent = createEvent(name: "level", value: level, unit: "%", displayed: false) + def shadeLevelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") + def stateEvent = createEvent(name: "windowShade", value: shadeValue, descriptionText: descriptionText, isStateChange: shadeLevelEvent.isStateChange) + + def result = [stateEvent, shadeLevelEvent, levelEvent] + if (!state.lastbatt || now() - state.lastbatt > 24 * 60 * 60 * 1000) { + log.debug "requesting battery" + state.lastbatt = (now() - 23 * 60 * 60 * 1000) // don't queue up multiple battery reqs in a row + result << response(["delay 15000", zwave.batteryV1.batteryGet().format()]) + } + result } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { - [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), - response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] + [ createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV1.switchMultilevelGet().format()) ] } 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 - } - state.lastbatt = now() - createEvent(map) + 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() + + createEvent(map) } def zwaveEvent(physicalgraph.zwave.Command cmd) { - log.debug "unhandled $cmd" - return [] + log.debug "unhandled $cmd" + return [] } def open() { - levelChangeFollowUp(99) - log.debug "open()" - /*delayBetween([ - zwave.basicV1.basicSet(value: 0xFF).format(), - zwave.switchMultilevelV1.switchMultilevelGet().format() - ], 1000)*/ - zwave.basicV1.basicSet(value: 99).format() + log.debug "open()" + + setShadeLevel(99) } def close() { - levelChangeFollowUp(0) - log.debug "close()" - /*delayBetween([ - zwave.basicV1.basicSet(value: 0x00).format(), - zwave.switchMultilevelV1.switchMultilevelGet().format() - ], 1000)*/ - zwave.basicV1.basicSet(value: 0).format() + log.debug "close()" + + setShadeLevel(0) } def setLevel(value, duration = null) { - log.debug "setLevel(${value.inspect()})" - Integer level = value as Integer - if (level < 0) level = 0 - if (level > 99) level = 99 - levelChangeFollowUp(level) - zwave.basicV1.basicSet(value: level).format() + log.debug "setLevel($value)" + + setShadeLevel(value) +} + +def setShadeLevel(value) { + Integer level = Math.max(Math.min(value as Integer, 99), 0) + + log.debug "setShadeLevel($value) -> $level" + + levelChangeFollowUp(level) // Follow up in a few seconds to make sure the shades didn't "forget" to send us level updates + zwave.basicV1.basicSet(value: level).format() } def presetPosition() { - setLevel(preset ?: state.preset ?: 50) + setLevel(preset ?: state.preset ?: 50) } def pause() { - log.debug "pause()" - stop() + log.debug "pause()" + + stop() } def stop() { - log.debug "stop()" - zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() + log.debug "stop()" + + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() } def ping() { - zwave.switchMultilevelV1.switchMultilevelGet().format() + zwave.switchMultilevelV1.switchMultilevelGet().format() } def refresh() { - log.debug "refresh()" - delayBetween([ - zwave.switchMultilevelV1.switchMultilevelGet().format(), - zwave.batteryV1.batteryGet().format() - ], 1500) + log.debug "refresh()" + delayBetween([ + zwave.switchMultilevelV1.switchMultilevelGet().format(), + zwave.batteryV1.batteryGet().format() + ], 1500) } def levelChangeFollowUp(expectedLevel) { - state.expectedValue = expectedLevel - state.levelChecks = 0 - runIn(5, "checkLevel", [overwrite: true]) + state.expectedValue = expectedLevel + state.levelChecks = 0 + runIn(5, "checkLevel", [overwrite: true]) } def checkLevelReport(value) { - if (state.expectedValue != null) { - if ((state.expectedValue == 99 && value >= 99) || - (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { - unschedule("checkLevel") - } - } + if (state.expectedValue != null) { + if ((state.expectedValue == 99 && value >= 99) || + (value >= state.expectedValue - 2 && value <= state.expectedValue + 2)) { + unschedule("checkLevel") + } + } } def checkLevel() { - if (state.levelChecks != null && state.levelChecks < 5) { - state.levelChecks = state.levelChecks + 1 - runIn(5, "checkLevel", [overwrite: true]) - sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) - } else { - unschedule("checkLevel") - } + if (state.levelChecks != null && state.levelChecks < 5) { + state.levelChecks = state.levelChecks + 1 + runIn(5, "checkLevel", [overwrite: true]) + sendHubCommand(zwave.switchMultilevelV1.switchMultilevelGet()) + } else { + unschedule("checkLevel") + } } From 468cf368c7a9d75270bece5c31dff30210e6d765 Mon Sep 17 00:00:00 2001 From: Nico Arlt Date: Wed, 12 May 2021 05:27:32 -0700 Subject: [PATCH 219/422] DevWs for TechniSat Digital GmbH containing containing Z-Wave Metering Dimmer --- .../technisat-dimmer.groovy | 423 ++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy diff --git a/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy b/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy new file mode 100644 index 00000000000..d0dfbf0cb67 --- /dev/null +++ b/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy @@ -0,0 +1,423 @@ +/** + * Copyright 2021 TechniSat + * + * 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. + * + */ +import groovy.json.JsonOutput + +metadata { + definition (name: "TechniSat Dimmer", namespace: "TechniSat", author: "TechniSat", vid:"generic-dimmer-power-energy", + mnmn: "SmartThings", runLocally: true, minHubCoreVersion: '000.017.0012', + executeCommandsLocally: false) { + capability "Switch" + capability "Switch Level" + capability "Energy Meter" + capability "Power Meter" + capability "Refresh" + capability "Configuration" + capability "Health Check" + + fingerprint mfr: "0299", prod: "0004", model: "1A92", deviceJoinName: "TechniSat Dimmer" + } + + preferences { + parameterMap.each { + input(title: "Parameter ${it.paramZwaveNum}: ${it.title}", + description: it.descr, + type: "paragraph", + element: "paragraph") + if(it.enableSwitch) { + input(name: it.enableKey, + title: "Enable", + type: "bool", + required: false) + } + input(name: it.key, + title: it.paramName, + type: it.type, + options: it.values, + range: it.range, + required: false) + } + } +} + +def installed() { + log.debug "installed()" + initStateConfig() + initialize() +} + +def updated() { + log.debug "updated()" + initialize() + syncConfig() +} + +def initialize() { + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +def getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x26: 3, // SwitchMultilevel + 0x32: 3, // Meter + 0x56: 1, // Crc16Encap + 0x70: 2, // Configuration + 0x98: 1, // Security + ] +} + +def parse(String description) { + def result = null + if (description != "updated") { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result = zwaveEvent(cmd) + log.debug("'$description' parsed to $result") + } else { + log.debug("Couldn't zwave.parse '$description'") + } + } + result +} + +def handleMeterReport(cmd) { + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kWh") + } else if (cmd.scale == 1) { + createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kVAh") + } else if (cmd.scale == 2) { + createEvent(name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W") + } + } +} + +def dimmerEvents(physicalgraph.zwave.Command cmd) { + def result = [] + def value = (cmd.value ? "on" : "off") + def switchEvent = createEvent(name: "switch", value: value, descriptionText: "$device.displayName was turned $value") + result << switchEvent + result << createEvent(name: "level", value: cmd.value == 99 ? 100 : cmd.value , unit: "%") + if (switchEvent.isStateChange) { + result << response(["delay 3000", meterGet(scale: 2).format()]) + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + log.debug "v3 Meter report: "+cmd + handleMeterReport(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + dimmerEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { + dimmerEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { + dimmerEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def param = parameterMap.find( {it.paramZwaveNum == cmd.parameterNumber } ) + + if (state.currentConfig."$param.key".status != "sync") { + if (state.currentConfig."$param.key"?.newValue == cmd.scaledConfigurationValue || + state.currentConfig."$param.key".status == "init") { + log.debug "Parameter ${param.key} set to value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".status = "sync" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } else { + log.debug "Parameter ${param.key} set to value failed: is:${cmd.scaledConfigurationValue} <> ${state.currentConfig."$param.key".newValue}" + state.currentConfig."$param.key".status = "failed" + syncConfig() + } + } else { + log.debug "Parameter ${param.key} update received. value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "${device.displayName}: Unhandled: $cmd" + [:] +} + +def on() { + encapSequence([ + zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF), + zwave.switchMultilevelV1.switchMultilevelGet(), + ], 5000) +} + +def off() { + encapSequence([ + zwave.switchMultilevelV1.switchMultilevelSet(value: 0x00), + zwave.switchMultilevelV1.switchMultilevelGet(), + ], 5000) +} + +def setLevel(level, rate = null) { + if (level > 99) { + level = 99 + } + encapSequence([ + zwave.switchMultilevelV1.switchMultilevelSet(value: level), + zwave.switchMultilevelV1.switchMultilevelGet() + ], 5000) +} + +def ping() { + log.debug "ping()" + refresh() +} + +def poll() { + sendHubCommand(refresh()) +} + +def refresh() { + log.debug "refresh()" + encapSequence([ + zwave.switchMultilevelV1.switchMultilevelGet(), + meterGet(scale: 0), + meterGet(scale: 2), + ], 1000) +} + +def configure() { + log.debug "configure()" + def result = [] + + log.debug "Configure zwaveInfo: "+zwaveInfo + + initStateConfigFromDevice() + logStateConfig() + result << response(encap(meterGet(scale: 0))) + result << response(encap(meterGet(scale: 2))) + result << response(encap(zwave.switchMultilevelV1.switchMultilevelGet())) + result +} + +def meterGet(scale) { + zwave.meterV2.meterGet(scale) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + log.debug "Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract Secure command from $cmd" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { + def version = commandClassVersions[cmd.commandClass as Integer] + def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) + def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data) + if (encapsulatedCommand) { + log.debug "Parsed Crc16Encap into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract CRC16 command from $cmd" + } +} + +private secEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using Secure Encapsulation, command: $cmd" + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() +} + +private crcEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using CRC16 Encapsulation, command: $cmd" + zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format() +} + +private encap(physicalgraph.zwave.Command cmd) { + if (zwaveInfo?.zw?.contains("s")) { + secEncap(cmd) + } else if (zwaveInfo?.cc?.contains("56")){ + crcEncap(cmd) + } else { + log.debug "no encapsulation supported for command: $cmd" + cmd.format() + } +} + +private encapSequence(cmds, Integer delay=250) { + delayBetween(cmds.collect{ encap(it) }, delay) +} + +private isConfigChanged(parameter) { + def settingsValue = settings."$parameter.key" + log.debug "isConfigChanged parameter:${parameter.key}: ${settingsValue}" + if(parameter.enableSwitch) { + if(settings."$parameter.enableKey" != null) { + if(settings."$parameter.enableKey" == false) { + settingsValue = 0; + } + } + } + if (settingsValue != null) { + Integer value = 0 + if (parameter.type == "number") { + value = settingsValue + } else { + value = Integer.parseInt(settingsValue) + } + if(state.currentConfig."$parameter.key".value != value) { + state.currentConfig."$parameter.key".newValue = value + log.debug "${parameter.key} set:${value} value:${state.currentConfig."$parameter.key".value} newValue:${state.currentConfig."$parameter.key".newValue}" + return true + } else if(state.currentConfig."$parameter.key".status != "sync") { + log.debug "${parameter.key} retry to set; is:${state.currentConfig."$parameter.key".value} should:${state.currentConfig."$parameter.key".newValue}" + return true + } + return false + } else { + log.debug "pref value not set yet" + return false + } +} + +private syncConfig() { + def commands = [] + parameterMap.each { + if (isConfigChanged(it)) { + log.debug "Parameter ${it.key} has been updated from value: ${state.currentConfig."$it.key".value} to ${state.currentConfig."$it.key".newValue}" + state.currentConfig."$it.key".status = "syncPending" + commands << response(encap(zwave.configurationV2.configurationSet(configurationValue: intToParam(state.currentConfig."$it.key".newValue, it.paramZwaveSize), + parameterNumber: it.paramZwaveNum, size: it.paramZwaveSize))) + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } else if (state.currentConfig."$it.key".value == null) { + log.warn "Parameter ${it.key} no. ${it.paramZwaveNum} has no value. Please check preference declaration for errors." + } + } + if(commands) { + sendHubCommand(commands,1000) + } +} + +private initStateConfig() { + log.debug "initStateConfig()" + state.currentConfig = [:] + parameterMap.each { + log.debug "set $it.key" + state.currentConfig."$it.key" = [:] + state.currentConfig."$it.key".value = new Integer('0') + state.currentConfig."$it.key".newValue = new Integer('0') + state.currentConfig."$it.key".status = "init" + } +} + +private initStateConfigFromDevice() { + log.debug "initStateConfigFromDevice()" + def commands = [] + parameterMap.each { + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } + if(commands) { + sendHubCommand(commands,1000) + } +} + +private logStateConfig() { + parameterMap.each { + log.debug "key:$it.key value: ${state.currentConfig."$it.key".value} newValue: ${state.currentConfig."$it.key".newValue} status: ${state.currentConfig."$it.key".status}" + } +} + +private List intToParam(Long value, Integer size = 1) { + def result = [] + size.times { + result = result.plus(0, (value & 0xFF) as Short) + value = (value >> 8) + } + return result +} + +private getParameterMap() { + [ + [ + title: "Wattage meter report interval", + descr: "Interval of current wattage meter reports in 10 seconds. 3 ... 8640 (30 seconds - 1 day)", + key: "wattageMeterReportInterval", + paramName: "Set Value (3..8640)", + type: "number", + range: "3..8640", + enableSwitch: true, + enableSwitchDefaultValue: true, + enableKey: "wattageMeterReportDisable", + paramZwaveNum: 2, + paramZwaveSize: 1 + ], + [ + title: "Energy meter report interval", + descr: "Interval of active energy meter reports in minutes. 10 ... 30240 (10 minutes - 3 weeks)", + key: "energyMeterReportInterval", + enableSwitch: true, + enableSwitchDefaultValue: true, + enableKey: "energyMeterReportDisable", + paramName: "Set Value (10..30240)", + type: "number", + range: "10..30240", + paramZwaveNum: 3, + paramZwaveSize: 2 + ], + [ + title: "Operation mode of button T", + descr: "Operation mode of button T", + key: "buttonModeSetting", + paramName: "Select", + type: "enum", + values: [ + 0: "0 - T1 turns L1 on, T2 turn L1 off", + 1: "1 - T1 & T2 toggle output L1" + ], + paramZwaveNum: 4, + paramZwaveSize: 1 + ], + [ + title: "External Connector", + descr: "Configuration of switch type connected to extension connector S", + key: "externalSwitchSetting", + paramName: "Select", + type: "enum", + values: [ + 0: "0 - toggle switch", + 1: "1 - push button switch" + ], + paramZwaveNum: 5, + paramZwaveSize: 1 + ], + [ + title: "Dimming curve", + descr: "Dimming curve selection", + key: "dimmingCurve", + paramName: "Select", + type: "enum", + values: [ + 0: "0 - dimming curve 1", + 1: "1 - dimming curve 2" + ], + paramZwaveNum: 7, + paramZwaveSize: 1 + ], + ] +} \ No newline at end of file From cec851786a938376b46ad34ce0618e37c4d5c592 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Thu, 13 May 2021 02:10:58 +0900 Subject: [PATCH 220/422] Devws for shina system containin 66 (#62197) * USM TEST version * SiHAS USM-300-Z - SiHAS MultiPurpose Sensor * Capabilities:Battery,Configuration,Health Check,Illuminance Measurement,Motion Sensor,Refresh,Relative Humidity Measurement,Sensor,Temperature Measurement * Delete sihas-usm-300zrev2.groovy Now useless. * * SiHAS OSM-300-Z : first release . motion sensor * SiHAS TSM-300-Z : first release . Temperature / Relative Humidity sensor * * SiHAS TSM-300-Z : first release . Temperature/Relative Humidity sensor * * change : vid: "generic-humidity" adding * Delete devicetypes/shinasys directory * * first release - SiHAS Multiprupose Sensor DTH * * Initial commit : sihas contact sensor * * SiHAS Zigbee Button : BSM-300Z * SiHAS Zigbee Contact Sensor : DSM-300Z * * First Release - SiHAS Remote Control . MSM-300Z : Includes 4 buttons * * Add : BSM300Z code * * update * * max volts : 3.2->3.1v * Update sihas-multipurpose-sensor.groovy * USM TEST version * SiHAS USM-300-Z - SiHAS MultiPurpose Sensor * Capabilities:Battery,Configuration,Health Check,Illuminance Measurement,Motion Sensor,Refresh,Relative Humidity Measurement,Sensor,Temperature Measurement * Delete sihas-usm-300zrev2.groovy Now useless. * * SiHAS OSM-300-Z : first release . motion sensor * SiHAS TSM-300-Z : first release . Temperature / Relative Humidity sensor * * SiHAS TSM-300-Z : first release . Temperature/Relative Humidity sensor * * change : vid: "generic-humidity" adding * Delete devicetypes/shinasys directory * * first release - SiHAS Multiprupose Sensor DTH * * Initial commit : sihas contact sensor * * SiHAS Zigbee Button : BSM-300Z * SiHAS Zigbee Contact Sensor : DSM-300Z * * First Release - SiHAS Remote Control . MSM-300Z : Includes 4 buttons * * Add : BSM300Z code * * update * * max volts : 3.2->3.1v * Update sihas-multipurpose-sensor.groovy * Revert "Update sihas-multipurpose-sensor.groovy" This reverts commit ea8c35ac2748f17eda6205ebf4bef9dcda331464. * update commit * contact sensor : vid is changed. * Delete sihas-contact-sensor.groovy * Delete sihas-zigbee-button.groovy * Delete sihas-zigbee-remote-control.groovy * Delete sihas-zigbee-contact-sensor.groovy * Revert "Update sihas-multipurpose-sensor.groovy" This reverts commit ea8c35ac2748f17eda6205ebf4bef9dcda331464. * update commit * contact sensor : vid is changed. * Delete sihas-contact-sensor.groovy * Delete sihas-zigbee-button.groovy * Delete sihas-zigbee-remote-control.groovy * Delete sihas-zigbee-contact-sensor.groovy * removing : contact sensor, nit: spacing --- .../sihas-multipurpose-sensor.groovy | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index 0665334d685..9f3c040c3ce 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -27,12 +27,11 @@ metadata { capability "Refresh" capability "Health Check" capability "Sensor" - + fingerprint inClusters: "0000,0001,0003,0020,0400,0402,0405,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "USM-300Z", deviceJoinName: "SiHAS MultiPurpose Sensor", mnmn: "SmartThings", vid: "generic-motion-6" - fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" - fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "generic-humidity" + fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2", ocfDeviceType: "x.com.st.d.sensor.motion" + fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Temp/Humidity_Sensor", ocfDeviceType: "oic.d.thermostat" } - preferences { section { input "tempOffset" , "number", title: "Temperature offset", description: "Select how many degrees to adjust the temperature.", range: "-100..100", displayDuringSetup: false @@ -59,8 +58,9 @@ private List collectAttributes(Map descMap) { } def parse(String description) { + log.debug "Parsing message from device: $description" + Map map = zigbee.getEvent(description) - if (!map) { if (description?.startsWith('zone status')) { map = parseIasMessage(description) @@ -89,18 +89,20 @@ def parse(String description) { map.translatable = true } else if (map.name == "humidity") { if (humidityOffset) { - map.value = (int) map.value + (int) humidityOffset + map.value = map.value + (int) humidityOffset } map.descriptionText = "${device.displayName} humidity was ${map.value}%" + map.unit = "%" map.translatable = true } - + def result = map ? createEvent(map) : [:] - + if (description?.startsWith('enroll request')) { List cmds = zigbee.enrollResponse() result = cmds?.collect { new physicalgraph.device.HubAction(it) } } + log.debug "result: $result" return result } @@ -126,20 +128,20 @@ private Map getBatteryResult(rawValue) { def linkText = getLinkText(device) def result = [:] def volts = rawValue / 10 - + if (!(rawValue == 0 || rawValue == 255)) { result.name = 'battery' result.translatable = true - def minVolts = 2.3 - def maxVolts = 3.2 - + def minVolts = 2.3 + def maxVolts = 3.2 + // Get the current battery percentage as a multiplier 0 - 1 def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0 // Find the corresponding voltage from our range curValVolts = curValVolts * (maxVolts - minVolts) + minVolts // Round to the nearest 10th of a volt curValVolts = Math.round(10 * curValVolts) / 10.0 - + // Only update the battery reading if we don't have a last reading, // OR we have received the same reading twice in a row // OR we don't currently have a battery reading @@ -154,7 +156,7 @@ private Map getBatteryResult(rawValue) { // Don't update as we want to smooth the battery values, but do report the last battery state for record keeping purposes result.value = device.currentState("battery").value } - + result.descriptionText = "${device.displayName} battery was ${result.value}%" state.lastVolts = volts } @@ -180,57 +182,56 @@ def ping() { def refresh() { def refreshCmds = [] - + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) - + if (isUSM300() || isTSM300()) { refreshCmds += zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) refreshCmds += zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) } - + if (isUSM300()) { refreshCmds += zigbee.readAttribute(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) } - + if (isUSM300() || isOSM300()) { - refreshCmds += zigbee.readAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE) - refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + refreshCmds += zigbee.readAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE) refreshCmds += zigbee.enrollResponse() } - + return refreshCmds } def configure() { - def configCmds = [] - + def configCmds = [] + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - + // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity // battery minReport 30 seconds, maxReportTime 6 hrs by default // humidity minReportTime 30 seconds, maxReportTime 60 min // illuminance minReportTime 30 seconds, maxReportTime 60 min // occupancy sensing minReportTime 10 seconds, maxReportTime 60 min // ex) zigbee.configureReporting(0x0001, 0x0020, DataType.UINT8, 600, 21600, 0x01) - // This is for cluster 0x0001 (power cluster), attribute 0x0021 (battery level), whose type is UINT8, - // the minimum time between reports is 10 minutes (600 seconds) and the maximum time between reports is 6 hours (21600 seconds), - // and the amount of change needed to trigger a report is 1 unit (0x01). + // This is for cluster 0x0001 (power cluster), attribute 0x0021 (battery level), whose type is UINT8, + // the minimum time between reports is 10 minutes (600 seconds) and the maximum time between reports is 6 hours (21600 seconds), + // and the amount of change needed to trigger a report is 1 unit (0x01). configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE, DataType.UINT8, 30, 21600, 0x01/*100mv*1*/) - + if (isUSM300() || isTSM300()) { - configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 30, 300, 30/*30/100=0.3도*/) - configCmds += zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 30, 3600, 50/*50/100=0.5%*/) + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 20, 300, 10/*10/100=0.1도*/) + configCmds += zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 20, 300, 40/*10/100=0.4%*/) } - + if (isUSM300()) { - configCmds += zigbee.configureReporting(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 30, 3600, 20/*20 lux*/) + configCmds += zigbee.configureReporting(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 20, 3600, 10/*10 lux*/) } - + if (isUSM300() || isOSM300()) { configCmds += zigbee.configureReporting(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE, DataType.BITMAP8, 1, 600, 1) } - + return refresh() + configCmds } @@ -244,4 +245,5 @@ private Boolean isTSM300() { private Boolean isOSM300() { device.getDataValue("model") == "OSM-300Z" -} \ No newline at end of file +} + From a486822077ba846468045ec9d7afff50ae116fc1 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Thu, 20 May 2021 17:13:05 +0800 Subject: [PATCH 221/422] DevWs for www.easyiot.tech containing containing Zigbee Non-Holdable Button (#66779) Co-authored-by: jiang wegang --- .../zigbee-non-holdable-button.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy index edfa4c064d4..c5267f6ad80 100644 --- a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy +++ b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy @@ -27,6 +27,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0500", outClusters: "0019", manufacturer: "HEIMAN", model: "SOS-EM", deviceJoinName: "HEIMAN Button" //HEIMAN Emergency Button fingerprint manufacturer: "frient A/S", model: "MBTZB-110", deviceJoinName: "frient Button" // Frient Smart Button, 20 0104 0007 00 05 0000 0001 0003 000F 0020 04 0003 0006 000A 0019 + fingerprint manufacturer: "eWeLink", model: "KF01", deviceJoinName: "eWeLink Button" // 01 0104 0402 00 04 0000 0003 0500 0001 01 0003 } tiles(scale: 2) { @@ -224,4 +225,4 @@ private getLiIon3VTable() {[ 2.0: 1, 1.9: 0, 0.0: 0 -]} +]} \ No newline at end of file From 688a4213be5448918c74a9bbbcfbd59b13b7f5fa Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Thu, 20 May 2021 23:55:29 +0200 Subject: [PATCH 222/422] ICP-14136 Qubino Flush Thermostat - removed unnecesary temperature conversion (#62661) * Removed unnecesary temperature conversion * Removed range check in updateSetpoint which caused bug with Fahrenheit * Proper configuration which enables device to work with Fahrenheit * fixup! Proper configuration which enables device to work with Fahrenheit --- .../qubino-flush-thermostat.groovy | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy index 95adc4ec67b..3bf9ed75393 100644 --- a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy +++ b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy @@ -172,7 +172,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpo map.name = "coolingSetpoint" break } - map.value = convertTemperatureIfNeeded(cmd.scaledValue, cmd.scale ? 'F' : 'C', cmd.precision) + map.value = cmd.scaledValue map.unit = temperatureScale createEvent(map) } @@ -204,8 +204,7 @@ def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { } 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) + createEvent(name: "temperature", value: cmd.scaledSensorValue, unit: temperatureScale) } def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { @@ -277,17 +276,19 @@ def setCoolingSetpoint(setpoint) { } def updateSetpoint(setpoint, setpointType) { - setpoint = temperatureScale == 'C' ? setpoint : fahrenheitToCelsius(setpoint) - setpoint = Math.max(Math.min(setpoint.doubleValue(), maxSetpointTemperature.doubleValue()), minSetpointTemperature.doubleValue()) + def scale = temperatureScale == 'C' ? 0 : 1 [ - secure(zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: setpoint, setpointType: setpointType, size: 2])), + secure(zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: scale, scaledValue: setpoint, setpointType: setpointType, size: 2])), "delay 2000", secure(zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: setpointType)) ] } def configure() { - secure(zwave.configurationV1.configurationGet(parameterNumber: 59)) + [ + secure(zwave.configurationV1.configurationSet(parameterNumber: 78, scaledConfigurationValue: temperatureScale == 'C' ? 0 : 1, size: 1)), + secure(zwave.configurationV1.configurationGet(parameterNumber: 59)) + ] } def refresh() { From 4c3545766f956db5fb2aca203cdaba3898792f06 Mon Sep 17 00:00:00 2001 From: Konrad K <33450498+KKlimczukS@users.noreply.github.com> Date: Fri, 21 May 2021 20:23:25 +0200 Subject: [PATCH 223/422] P210513-02754 - fixes customized deviceJoinName for China locale (#66857) --- .../zigbee-window-shade.src/i18n/messages.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties index 0a1a5605dfb..f87f8152b6b 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties +++ b/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties @@ -15,7 +15,6 @@ # Chinese '''Wistar Window Treatment'''.zh-cn=威仕达开合帘电机(CMJ) '''Wistar Curtain Motor(CMJ)'''.zh-cn=威仕达开合帘电机(CMJ) -'''Window Treatment'''.zh-cn=智能窗帘电机(DT82TV) +'''Window Treatment'''.zh-cn=智能窗帘电机 '''Smart Curtain Motor(DT82TV)'''.zh-cn=智能窗帘电机(DT82TV) -'''Window Treatment'''.zh-cn=智能窗帘电机(BCM300D) '''Smart Curtain Motor(BCM300D)'''.zh-cn=智能窗帘电机(BCM300D) From c59379aeea94c6e4f3708540c90224aafc6903ed Mon Sep 17 00:00:00 2001 From: MWierzbinskaS <43334596+MWierzbinskaS@users.noreply.github.com> Date: Tue, 25 May 2021 10:51:14 +0200 Subject: [PATCH 224/422] [WWST-7575] Fingerprints for Aeotec Water Sensor 7 (#67170) * Added fingerprints for Aeotec Water Sensor 7 * WWST-7575 Added fingerprints for Aeotec Water Sensor 7 --- .../zwave-water-temp-humidity-sensor.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zwave-water-temp-humidity-sensor.src/zwave-water-temp-humidity-sensor.groovy b/devicetypes/smartthings/zwave-water-temp-humidity-sensor.src/zwave-water-temp-humidity-sensor.groovy index 01619a84f58..4793cd14455 100644 --- a/devicetypes/smartthings/zwave-water-temp-humidity-sensor.src/zwave-water-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/zwave-water-temp-humidity-sensor.src/zwave-water-temp-humidity-sensor.groovy @@ -30,6 +30,8 @@ metadata { fingerprint mfr:"0371", prod:"0002", model:"0013", deviceJoinName: "Aeotec Water Leak Sensor", mnmn: "SmartThings", vid: "aeotec-water-sensor-7-pro" //EU //Aeotec Water Sensor 7 Pro fingerprint mfr:"0371", prod:"0102", model:"0013", deviceJoinName: "Aeotec Water Leak Sensor", mnmn: "SmartThings", vid: "aeotec-water-sensor-7-pro" //US //Aeotec Water Sensor 7 Pro fingerprint mfr:"0371", prod:"0202", model:"0013", deviceJoinName: "Aeotec Water Leak Sensor", mnmn: "SmartThings", vid: "aeotec-water-sensor-7-pro" //AU //Aeotec Water Sensor 7 Pro + fingerprint mfr:"0371", prod:"0002", model:"0012", deviceJoinName: "Aeotec Water Leak Sensor", mnmn: "SmartThings", vid: "aeotec-water-sensor-7" //EU Aeotec Water Sensor 7 + fingerprint mfr:"0371", prod:"0102", model:"0012", deviceJoinName: "Aeotec Water Leak Sensor", mnmn: "SmartThings", vid: "aeotec-water-sensor-7" //US Aeotec Water Sensor 7 } tiles(scale: 2) { From 5985c4e3ad42bf1d83c2b80dab9a4d0b4e1dada1 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Fri, 28 May 2021 06:52:52 +0900 Subject: [PATCH 225/422] Devws for shina system containin 66 (#66551) * USM TEST version * SiHAS USM-300-Z - SiHAS MultiPurpose Sensor * Capabilities:Battery,Configuration,Health Check,Illuminance Measurement,Motion Sensor,Refresh,Relative Humidity Measurement,Sensor,Temperature Measurement * Delete sihas-usm-300zrev2.groovy Now useless. * * SiHAS OSM-300-Z : first release . motion sensor * SiHAS TSM-300-Z : first release . Temperature / Relative Humidity sensor * * SiHAS TSM-300-Z : first release . Temperature/Relative Humidity sensor * * change : vid: "generic-humidity" adding * Delete devicetypes/shinasys directory * * first release - SiHAS Multiprupose Sensor DTH * * Initial commit : sihas contact sensor * * SiHAS Zigbee Button : BSM-300Z * SiHAS Zigbee Contact Sensor : DSM-300Z * * First Release - SiHAS Remote Control . MSM-300Z : Includes 4 buttons * * Add : BSM300Z code * * update * * max volts : 3.2->3.1v * Update sihas-multipurpose-sensor.groovy * USM TEST version * SiHAS USM-300-Z - SiHAS MultiPurpose Sensor * Capabilities:Battery,Configuration,Health Check,Illuminance Measurement,Motion Sensor,Refresh,Relative Humidity Measurement,Sensor,Temperature Measurement * Delete sihas-usm-300zrev2.groovy Now useless. * * SiHAS OSM-300-Z : first release . motion sensor * SiHAS TSM-300-Z : first release . Temperature / Relative Humidity sensor * * SiHAS TSM-300-Z : first release . Temperature/Relative Humidity sensor * * change : vid: "generic-humidity" adding * Delete devicetypes/shinasys directory * * first release - SiHAS Multiprupose Sensor DTH * * Initial commit : sihas contact sensor * * SiHAS Zigbee Button : BSM-300Z * SiHAS Zigbee Contact Sensor : DSM-300Z * * First Release - SiHAS Remote Control . MSM-300Z : Includes 4 buttons * * Add : BSM300Z code * * update * * max volts : 3.2->3.1v * Update sihas-multipurpose-sensor.groovy * Revert "Update sihas-multipurpose-sensor.groovy" This reverts commit ea8c35ac2748f17eda6205ebf4bef9dcda331464. * update commit * contact sensor : vid is changed. * Delete sihas-contact-sensor.groovy * Delete sihas-zigbee-button.groovy * Delete sihas-zigbee-remote-control.groovy * Delete sihas-zigbee-contact-sensor.groovy * Revert "Update sihas-multipurpose-sensor.groovy" This reverts commit ea8c35ac2748f17eda6205ebf4bef9dcda331464. * update commit * contact sensor : vid is changed. * Delete sihas-contact-sensor.groovy * Delete sihas-zigbee-button.groovy * Delete sihas-zigbee-remote-control.groovy * Delete sihas-zigbee-contact-sensor.groovy * removing : contact sensor, nit: spacing * adding : contact sensor * Removed unnecessary space * added a space --- .../sihas-multipurpose-sensor.groovy | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index 9f3c040c3ce..2a2e83616df 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -27,10 +27,12 @@ metadata { capability "Refresh" capability "Health Check" capability "Sensor" + capability "Contact Sensor" fingerprint inClusters: "0000,0001,0003,0020,0400,0402,0405,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "USM-300Z", deviceJoinName: "SiHAS MultiPurpose Sensor", mnmn: "SmartThings", vid: "generic-motion-6" fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2", ocfDeviceType: "x.com.st.d.sensor.motion" fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Temp/Humidity_Sensor", ocfDeviceType: "oic.d.thermostat" + fingerprint inClusters: "0000,0001,0003,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "DSM-300Z", deviceJoinName: "SiHAS Contact Sensor", mnmn: "SmartThings", vid: "generic-contact-3", ocfDeviceType: "x.com.st.d.sensor.contact" } preferences { section { @@ -121,7 +123,11 @@ private Map parseIasMessage(String description) { private Map translateZoneStatus(ZoneStatus zs) { // Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion - return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') + if (isDSM300()) { + return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getContactResult('open') : getContactResult('closed') + } else { + return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') + } } private Map getBatteryResult(rawValue) { @@ -135,6 +141,8 @@ private Map getBatteryResult(rawValue) { def minVolts = 2.3 def maxVolts = 3.2 + if (isDSM300()) maxVolts = 3.1 + // Get the current battery percentage as a multiplier 0 - 1 def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0 // Find the corresponding voltage from our range @@ -173,6 +181,16 @@ private Map getMotionResult(value) { ] } +private Map getContactResult(value) { + def linkText = getLinkText(device) + def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}" + return [ + name: 'contact', + value: value, + descriptionText: descriptionText + ] +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ @@ -196,7 +214,13 @@ def refresh() { if (isUSM300() || isOSM300()) { refreshCmds += zigbee.readAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE) - refreshCmds += zigbee.enrollResponse() + refreshCmds += zigbee.enrollResponse() + } + + if (isDSM300()) { + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) + refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + refreshCmds += zigbee.enrollResponse() } return refreshCmds @@ -232,6 +256,11 @@ def configure() { configCmds += zigbee.configureReporting(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE, DataType.BITMAP8, 1, 600, 1) } + if (isDSM300()) { + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE, DataType.UINT8, 30, 21600, 0x01/*100mv*1*/) + configCmds += zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 0, 0xffff, null) + } + return refresh() + configCmds } @@ -247,3 +276,6 @@ private Boolean isOSM300() { device.getDataValue("model") == "OSM-300Z" } +private Boolean isDSM300() { + device.getDataValue("model") == "DSM-300Z" +} From 161c19ac16e60190da7f00854396cf46051faee2 Mon Sep 17 00:00:00 2001 From: Konrad Date: Tue, 1 Jun 2021 11:02:31 +0200 Subject: [PATCH 226/422] WWST-7581, WWST-7582 - fingerprints for the Aeotec Door/Window Sensor 7 (ZWA011-A, ZWA011-C) --- .../zwave-door-window-sensor.groovy | 2 ++ 1 file changed, 2 insertions(+) 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 1e73e6afc22..0afaf53a7ea 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 @@ -62,6 +62,8 @@ metadata { fingerprint mfr: "0371", prod: "0002", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //EU //Aeotec Door/Window Sensor 7 Pro fingerprint mfr: "0371", prod: "0102", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //US //Aeotec Door/Window Sensor 7 Pro fingerprint mfr: "0371", prod: "0202", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //AU //Aeotec Door/Window Sensor 7 Pro + fingerprint mfr: "0371", prod: "0002", model: "000B", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //EU //Aeotec Door/Window Sensor 7 zw:Ss2a type:0701 mfr:0371 prod:0002 model:000B ver:1.01 zwv:7.12 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,70,71,84,7A + fingerprint mfr: "0371", prod: "0102", model: "000B", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //US //Aeotec Door/Window Sensor 7 zw:Ss2a type:0701 mfr:0371 prod:0102 model:000B ver:1.01 zwv:7.12 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,70,71,84,7A } // simulator metadata From 1b31d413ff7fd57bb915344669ad2255a3aeec20 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 2 Jun 2021 13:33:28 +0200 Subject: [PATCH 227/422] Fingerprint for Somfy Sonesse 40 (#67561) --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 1 + 1 file changed, 1 insertion(+) 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 ab11c74955d..560d31ad23c 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -35,6 +35,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "REXENSE", model: "DY0010", deviceJoinName: "Window Treatment" //Smart Curtain Motor(DT82TV) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Ultra Curtain", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Sonesse 30 WF Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Sonesse 40 Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 40 } preferences { From 9e8e6659a00eecd024705e4c27165c105d775756 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Sat, 5 Jun 2021 06:26:28 +0900 Subject: [PATCH 228/422] DevWs for SHINA SYSTEM containing containing ZigBee Multi Switch (#67326) * DevWs for SHINA SYSTEM containing containing ZigBee Multi Switch * Move SiHAS 1gang switch to another switch DTH. * Add SiHAS Switch * Add SiHAS Switch * add blank line --- .../zigbee-multi-switch.groovy | 18 +++++++++++++++++- .../zigbee-switch.src/zigbee-switch.groovy | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 2a8915782a3..8d0fce3ee07 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -76,6 +76,12 @@ metadata { fingerprint manufacturer: "LELLKI", model: "JZ-ZB-005", deviceJoinName: "LELLKI Switch 1" //LELLKI 5 Gang Switch 1 // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 fingerprint manufacturer: "LELLKI", model: "JZ-ZB-006", deviceJoinName: "LELLKI Switch 1" //LELLKI 6 Gang Switch 1 + // SiHAS Switch (2~6 Gang) + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z2", deviceJoinName: "SiHAS Switch 1" + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z3", deviceJoinName: "SiHAS Switch 1" + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z4", deviceJoinName: "SiHAS Switch 1" + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z5", deviceJoinName: "SiHAS Switch 1" + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z6", deviceJoinName: "SiHAS Switch 1" } // simulator metadata simulator { @@ -145,7 +151,7 @@ def parse(String description) { } } -private void createChildDevices() { +private void createChildDevices() { if (!childDevices) { def x = getChildCount() for (i in 2..x) { @@ -267,6 +273,16 @@ private getChildCount() { return 6 } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB" || device.getDataValue("model") == "PM-S350-ZB" || device.getDataValue("model") == "ST-S350-ZB") { return 3 + } else if (device.getDataValue("model") == "SBM300Z2") { + return 2 + } else if (device.getDataValue("model") == "SBM300Z3") { + return 3 + } else if (device.getDataValue("model") == "SBM300Z4") { + return 4 + } else if (device.getDataValue("model") == "SBM300Z5") { + return 5 + } else if (device.getDataValue("model") == "SBM300Z6") { + return 6 } else { return 2 } diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 75c937d5620..7884121a1a8 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -114,6 +114,9 @@ metadata { // Focalcrest fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "Focalcrest", model: "SRB01", deviceJoinName: "Focalcrest Switch" // In-Wall Relay Switch + + // SiHAS Switch + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z1", deviceJoinName: "SiHAS Switch" } // simulator metadata From a7803c94181bda6ced1674716b276cc7b77c3731 Mon Sep 17 00:00:00 2001 From: greens Date: Fri, 4 Jun 2021 14:39:21 -0700 Subject: [PATCH 229/422] Refactor zigbee multi switch This code pattern was starting to get unwieldy, so I refactored it. --- .../zigbee-multi-switch.groovy | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 8d0fce3ee07..644fecf6490 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -259,31 +259,38 @@ private Boolean isOrvibo() { } private getChildCount() { - if (device.getDataValue("model") == "9f76c9f31b4c4a499e3aca0977ac4494" || device.getDataValue("model") == "HY0003" || device.getDataValue("model") == "HY0097" || device.getDataValue("model") == "HS2SW3L-EFR-3.0" ) { - return 3 - } else if (device.getDataValue("model") == "E220-KR2N0Z0-HA") { - return 2 - } else if (device.getDataValue("model") == "E220-KR3N0Z0-HA" || device.getDataValue("model") == "ZB-SW03" || device.getDataValue("model") == "JZ-ZB-003") { - return 3 - } else if (device.getDataValue("model") == "E220-KR4N0Z0-HA" || device.getDataValue("model") == "ZB-SW04" || device.getDataValue("model") == "JZ-ZB-004") { - return 4 - } else if (device.getDataValue("model") == "E220-KR5N0Z0-HA" || device.getDataValue("model") == "ZB-SW05" || device.getDataValue("model") == "JZ-ZB-005") { - return 5 - } else if (device.getDataValue("model") == "E220-KR6N0Z0-HA" || device.getDataValue("model") == "ZB-SW06" || device.getDataValue("model") == "JZ-ZB-006") { - return 6 - } else if (device.getDataValue("model") == "PM-S340-ZB" || device.getDataValue("model") == "PM-S340R-ZB" || device.getDataValue("model") == "PM-S350-ZB" || device.getDataValue("model") == "ST-S350-ZB") { - return 3 - } else if (device.getDataValue("model") == "SBM300Z2") { - return 2 - } else if (device.getDataValue("model") == "SBM300Z3") { - return 3 - } else if (device.getDataValue("model") == "SBM300Z4") { - return 4 - } else if (device.getDataValue("model") == "SBM300Z5") { - return 5 - } else if (device.getDataValue("model") == "SBM300Z6") { - return 6 - } else { - return 2 + switch (device.getDataValue("model")) { + case "9f76c9f31b4c4a499e3aca0977ac4494": + case "HY0003": + case "HY0097": + case "HS2SW3L-EFR-3.0": + case "E220-KR3N0Z0-HA": + case "ZB-SW03": + case "JZ-ZB-003": + case "PM-S340-ZB": + case "PM-S340R-ZB": + case "PM-S350-ZB": + case "ST-S350-ZB": + case "SBM300Z3": + return 3 + case "E220-KR4N0Z0-HA": + case "ZB-SW04": + case "JZ-ZB-004": + case "SBM300Z4": + return 4 + case "E220-KR5N0Z0-HA": + case "ZB-SW05": + case "JZ-ZB-005": + case "SBM300Z5": + return 5 + case "E220-KR6N0Z0-HA": + case "ZB-SW06": + case "JZ-ZB-006": + case "SBM300Z6": + return 6 + case "E220-KR2N0Z0-HA": + case "SBM300Z2": + default: + return 2 } } \ No newline at end of file From a069971fd8d32a191e212f62e28c7b402d36b644 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Tue, 8 Jun 2021 00:45:43 +0200 Subject: [PATCH 230/422] [ICP-14145] Z-Wave Metering Switch - power meter get after SwitchBinaryReport (#66846) * meterGet after evry SwitchBinaryReport, not only triggered by on/off commands * fixup! meterGet after evry SwitchBinaryReport, not only triggered by on/off commands * Added configuration for Aeotec Smart Switch 7 US --- .../zwave-metering-switch.groovy | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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 b03a6136fa3..b4c54658a1a 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -179,7 +179,7 @@ def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) def value = (cmd.value ? "on" : "off") def evt = createEvent(name: "switch", value: value, type: "physical", descriptionText: "$device.displayName was turned $value") if (evt.isStateChange) { - [evt, response(["delay 3000", meterGet(scale: 2).format()])] + [evt, response(["delay 3000", encap(meterGet(scale: 2))])] } else { evt } @@ -189,7 +189,10 @@ def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cm { log.debug "Switch binary report: "+cmd def value = (cmd.value ? "on" : "off") - createEvent(name: "switch", value: value, type: "digital", descriptionText: "$device.displayName was turned $value") + [ + createEvent(name: "switch", value: value, type: "digital", descriptionText: "$device.displayName was turned $value"), + response(["delay 3000", encap(meterGet(scale: 2))]) + ] } def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { @@ -210,16 +213,14 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { def on() { encapSequence([ zwave.basicV1.basicSet(value: 0xFF), - zwave.switchBinaryV1.switchBinaryGet(), - meterGet(scale: 2) + zwave.switchBinaryV1.switchBinaryGet() ], 3000) } def off() { encapSequence([ zwave.basicV1.basicSet(value: 0x00), - zwave.switchBinaryV1.switchBinaryGet(), - meterGet(scale: 2) + zwave.switchBinaryV1.switchBinaryGet() ], 3000) } @@ -261,6 +262,8 @@ def configure() { result << response(encap(zwave.configurationV1.configurationSet(parameterNumber: 13, size: 2, scaledConfigurationValue: 15))) //report kWH every 15 min } else if (zwaveInfo.mfr == "0154" && zwaveInfo.prod == "0003" && zwaveInfo.model == "000A") { result << response(encap(zwave.configurationV1.configurationSet(parameterNumber: 25, size: 1, scaledConfigurationValue: 1))) //report every 1W change + } else if (zwaveInfo.mfr == "0371" && zwaveInfo.prod == "0103" && zwaveInfo.model == "0017") { //Aeotec Smart Switch 7 US / ZWA023-A + result << response(encap(zwave.configurationV1.configurationSet(parameterNumber: 21, size: 2, scaledConfigurationValue: 2))) //report every 2W change } result << response(encap(meterGet(scale: 0))) result << response(encap(meterGet(scale: 2))) From ce09e6b0c3cca24b01a2dae3bb654b76a877d61b Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 8 Jun 2021 13:11:31 -0700 Subject: [PATCH 231/422] BUG-2739 Add preset position Chinese translation --- .../zigbee-window-shade.src/i18n/messages.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties index f87f8152b6b..ae6bca705f2 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties +++ b/devicetypes/smartthings/zigbee-window-shade.src/i18n/messages.properties @@ -18,3 +18,5 @@ '''Window Treatment'''.zh-cn=智能窗帘电机 '''Smart Curtain Motor(DT82TV)'''.zh-cn=智能窗帘电机(DT82TV) '''Smart Curtain Motor(BCM300D)'''.zh-cn=智能窗帘电机(BCM300D) +'''Preset position'''.zh-cn=预设位置 +'''Set the window shade preset position'''.zh-cn=设置窗帘预设位置 \ No newline at end of file From 37f87456abc8096649d571f96d3074698f614257 Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 9 Jun 2021 15:35:49 -0700 Subject: [PATCH 232/422] BUG-2759 Make strobe command strobe a likely typo resulted in the strobe command sending "siren" to the device. --- .../smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy index 8125cf4ea25..a2210c4901d 100644 --- a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy +++ b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy @@ -160,7 +160,7 @@ def siren() { def strobe() { log.debug "strobe()" - startCmd(ALARM_SIREN) + startCmd(ALARM_STROBE) } def startCmd(cmd) { From 2f7c814845426049f5e16ddf0d9d7d9311750179 Mon Sep 17 00:00:00 2001 From: NArlt <79513123+NArlt@users.noreply.github.com> Date: Sat, 12 Jun 2021 00:18:58 +0200 Subject: [PATCH 233/422] DevWs for TechniSat Digital GmbH containing containing TechniSat Shutter fixes (#67400) * DevWs for TechniSat Digital GmbH containing containing TechniSat Shutter * implemented requested changes * Changed to new vid; requested changes * changed deviceJoinName back to TechniSat Window Treatment --- .../technisat-roller-shutter-switch.groovy | 408 ++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy diff --git a/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy b/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy new file mode 100644 index 00000000000..04cc08fa301 --- /dev/null +++ b/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy @@ -0,0 +1,408 @@ +/** + * Copyright 2021 TechniSat + * + * 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. + * + */ +import groovy.json.JsonOutput + +metadata { + definition (name: "TechniSat Roller shutter switch", namespace: "TechniSat", author: "TechniSat", vid:"8b7b3238-1dfb-3c1e-a0a0-c8527d35abc4", + mnmn: "SmartThingsCommunity") { + capability "Window Shade" + capability "Window Shade Preset" + capability "Window Shade Level" + capability "Power Meter" + capability "Energy Meter" + capability "Refresh" + capability "Health Check" + capability "Configuration" + + fingerprint mfr: "0299", prod: "0005", model: "1A93", deviceJoinName: "TechniSat Window Treatment" + } + + preferences { + input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, range: "1..100", required: false, displayDuringSetup: false + parameterMap.each { + input(title: "Parameter ${it.paramZwaveNum}: ${it.title}", + description: it.descr, + type: "paragraph", + element: "paragraph") + if (it.enableSwitch) { + input(name: it.enableKey, + title: "Enable", + type: "bool", + required: false) + } + input(name: it.key, + title: it.paramName, + type: it.type, + options: it.values, + range: it.range, + defaultValue: it.defaultValue, + required: false) + } + } +} + +def installed() { + log.debug "installed()" + sendEvent(name: "supportedWindowShadeCommands", value: JsonOutput.toJson(["open", "close", "pause"]), displayed: false) + initStateConfig() + initialize() +} + +def updated() { + log.debug "updated()" + initialize() + syncConfig() +} + +def initialize() { + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +def getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x26: 3, // SwitchMultilevel + 0x32: 3, // Meter + 0x70: 2, // Configuration + 0x98: 1, // Security + ] +} + +def parse(String description) { + log.debug "parse() - description: "+description + def result = null + if (description != "updated") { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result = zwaveEvent(cmd) + log.debug("'$description' parsed to $result") + } else { + log.debug("Couldn't zwave.parse '$description'") + } + } + result +} + +def handleMeterReport(cmd) { + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kWh") + } else if (cmd.scale == 1) { + createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kVAh") + } else if (cmd.scale == 2) { + createEvent(name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W") + } + } +} + +def levelEvents(physicalgraph.zwave.Command cmd) { + def result = [] + def shadeValue = null + def level = cmd.value as Integer + + if (cmd.value >= 99) { + level = 100 + shadeValue = "open" + } else if (cmd.value <= 0) { + level = 0 + shadeValue = "closed" + } else { + shadeValue = "partially open" + } + + def levelEvent = createEvent(name: "shadeLevel", value: level, unit: "%") + result << createEvent(name: "windowShade", value: shadeValue, descriptionText: "${device.displayName} shade is ${level}% open", isStateChange: levelEvent.isStateChange) + result << levelEvent + result << response(encap(meterGet(scale: 0))) + result << response(encap(meterGet(scale: 2))) + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + log.debug "v3 Meter report: "+cmd + handleMeterReport(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + levelEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) { + levelEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { + levelEvents(cmd) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelStopLevelChange cmd) { + [ + createEvent(name: "windowShade", value: "partially open", displayed: false, descriptionText: "$device.displayName shade stopped"), + response(zwave.switchMultilevelV3.switchMultilevelGet().format()) + ] +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def param = parameterMap.find( {it.paramZwaveNum == cmd.parameterNumber } ) + + if (state.currentConfig."$param.key".status != "sync") { + if (state.currentConfig."$param.key"?.newValue == cmd.scaledConfigurationValue || + state.currentConfig."$param.key".status == "init") { + log.debug "Parameter ${param.key} set to value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".status = "sync" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } else { + log.debug "Parameter ${param.key} set to value failed: is:${cmd.scaledConfigurationValue} <> ${state.currentConfig."$param.key".newValue}" + state.currentConfig."$param.key".status = "failed" + syncConfig() + } + } else { + log.debug "Parameter ${param.key} update received. value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "${device.displayName}: Unhandled: $cmd" + [:] +} + +def open() { + encapSequence([ + zwave.switchMultilevelV3.switchMultilevelSet(value: 99), + zwave.switchMultilevelV3.switchMultilevelGet(), + ], 5000) +} + +def close() { + encapSequence([ + zwave.switchMultilevelV3.switchMultilevelSet(value: 0), + zwave.switchMultilevelV3.switchMultilevelGet(), + ], 5000) +} + +def setShadeLevel(level, rate = null) { + if (level < 0) { + level = 0 + } else if (level > 99) { + level = 99 + } + encapSequence([ + zwave.switchMultilevelV3.switchMultilevelSet(value: level), + zwave.switchMultilevelV3.switchMultilevelGet() + ], 5000) +} + +def presetPosition() { + log.debug "presetPosition called" + setShadeLevel(preset ?: state.preset ?: 50) +} + +def pause() { + log.debug "pause()" + zwave.switchMultilevelV3.switchMultilevelStopLevelChange().format() +} + +def ping() { + log.debug "ping()" + refresh() +} + +def refresh() { + log.debug "refresh()" + encapSequence([ + zwave.switchMultilevelV3.switchMultilevelGet(), + meterGet(scale: 0), + meterGet(scale: 2), + ], 1000) +} + +def configure() { + log.debug "configure()" + def result = [] + + log.debug "Configure zwaveInfo: "+zwaveInfo + + initStateConfigFromDevice() + logStateConfig() + result << response(encap(meterGet(scale: 0))) + result << response(encap(meterGet(scale: 2))) + result << response(encap(zwave.switchMultilevelV3.switchMultilevelGet())) + result +} + +def meterGet(scale) { + zwave.meterV2.meterGet(scale) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + log.debug "Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract Secure command from $cmd" + } +} + +private secEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using Secure Encapsulation, command: $cmd" + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() +} + +private encap(physicalgraph.zwave.Command cmd) { + if (zwaveInfo?.zw?.contains("s")) { + secEncap(cmd) + } else { + log.debug "no encapsulation supported for command: $cmd" + cmd.format() + } +} + +private encapSequence(cmds, Integer delay=250) { + delayBetween(cmds.collect{ encap(it) }, delay) +} + +private convertParamToInt(parameter, settingsValue) { + Integer value = 0 + if (parameter.type == "number") { + value = settingsValue + } else { + value = settingsValue? 1 : 0 + } +} + +private isConfigChanged(parameter) { + def settingsValue = settings."$parameter.key" + log.debug "isConfigChanged parameter:${parameter.key}: ${settingsValue}" + if (parameter.enableSwitch) { + if (settings."$parameter.enableKey" != null) { + if (settings."$parameter.enableKey" == false) { + settingsValue = 0; + } + } + } + if (settingsValue != null) { + Integer value = convertParamToInt(parameter, settingsValue) + if (state.currentConfig."$parameter.key".value != value) { + state.currentConfig."$parameter.key".newValue = value + log.debug "${parameter.key} set:${value} value:${state.currentConfig."$parameter.key".value} newValue:${state.currentConfig."$parameter.key".newValue}" + return true + } else if (state.currentConfig."$parameter.key".status != "sync") { + log.debug "${parameter.key} retry to set; is:${state.currentConfig."$parameter.key".value} should:${state.currentConfig."$parameter.key".newValue}" + return true + } + return false + } else { + log.debug "pref value not set yet" + return false + } +} + +private syncConfig() { + def commands = [] + parameterMap.each { + if (isConfigChanged(it)) { + log.debug "Parameter ${it.key} has been updated from value: ${state.currentConfig."$it.key".value} to ${state.currentConfig."$it.key".newValue}" + state.currentConfig."$it.key".status = "syncPending" + commands << response(encap(zwave.configurationV2.configurationSet(configurationValue: intToParam(state.currentConfig."$it.key".newValue, it.paramZwaveSize), + parameterNumber: it.paramZwaveNum, size: it.paramZwaveSize))) + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } else if (state.currentConfig."$it.key".value == null) { + log.warn "Parameter ${it.key} no. ${it.paramZwaveNum} has no value. Please check preference declaration for errors." + } + } + if (commands) { + sendHubCommand(commands,1000) + } +} + +private initStateConfig() { + log.debug "initStateConfig()" + state.currentConfig = [:] + parameterMap.each { + log.debug "set $it.key" + state.currentConfig."$it.key" = [:] + state.currentConfig."$it.key".value = new Integer('0') + state.currentConfig."$it.key".newValue = new Integer('0') + state.currentConfig."$it.key".status = "init" + } +} + +private initStateConfigFromDevice() { + log.debug "initStateConfigFromDevice()" + def commands = [] + parameterMap.each { + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } + if (commands) { + sendHubCommand(commands,1000) + } +} + +private logStateConfig() { + parameterMap.each { + log.debug "key:$it.key value: ${state.currentConfig."$it.key".value} newValue: ${state.currentConfig."$it.key".newValue} status: ${state.currentConfig."$it.key".status}" + } +} + +private List intToParam(Long value, Integer size = 1) { + def result = [] + size.times { + result = result.plus(0, (value & 0xFF) as Short) + value = (value >> 8) + } + return result +} + +private getParameterMap() { + [ + [ + title: "Wattage meter report interval", + descr: "Interval of current wattage meter reports in 10 seconds. 3 ... 8640 (30 seconds - 1 day)", + key: "wattageMeterReportInterval", + paramName: "Set Value (3..8640)", + type: "number", + range: "3..8640", + enableSwitch: true, + enableSwitchDefaultValue: true, + enableKey: "wattageMeterReportDisable", + paramZwaveNum: 2, + paramZwaveSize: 1 + ], + [ + title: "Energy meter report interval", + descr: "Interval of active energy meter reports in minutes. 10 ... 30240 (10 minutes - 3 weeks)", + key: "energyMeterReportInterval", + enableSwitch: true, + enableSwitchDefaultValue: true, + enableKey: "energyMeterReportDisable", + paramName: "Set Value (10..30240)", + type: "number", + range: "10..30240", + paramZwaveNum: 3, + paramZwaveSize: 2 + ], + [ + title: "Manual calibartion", + descr: "Setting this parameter will start a manual shutter calibartion", + key: "calibartionStart", + paramName: "Start", + type: "bool", + defaultValue: false, + paramZwaveNum: 4, + paramZwaveSize: 1 + ], + ] +} \ No newline at end of file From 27169c8332f7acef18b74e0af2fdf9693e83df5e Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 16 Jun 2021 13:37:38 -0700 Subject: [PATCH 234/422] BUG-2759 Remove strobe command for heiman siren The heiman siren (like the ozom) does not support strobe functionality. This will prevent it from being displayed in the UI. --- .../smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy index a2210c4901d..9f2d044d2c7 100644 --- a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy +++ b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy @@ -25,8 +25,8 @@ metadata { capability "Configuration" capability "Health Check" - fingerprint profileId: "0104", inClusters: "0000,0003,0500,0502", outClusters: "0000", manufacturer: "ClimaxTechnology", model: "SRAC_00.00.00.16TC", vid: "generic-siren-8", deviceJoinName: "Ozom Siren" // Ozom Siren - SRAC-23ZBS //Ozom Smart Siren - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0009,0500,0502", outClusters: "0003,0019", manufacturer: "Heiman", model: "WarningDevice", deviceJoinName: "HEIMAN Siren" //HEIMAN Smart Siren + fingerprint profileId: "0104", inClusters: "0000,0003,0500,0502", outClusters: "0000", manufacturer: "ClimaxTechnology", model: "SRAC_00.00.00.16TC", mnmn: "SmartThings", vid: "generic-siren-8", deviceJoinName: "Ozom Siren" // Ozom Siren - SRAC-23ZBS //Ozom Smart Siren + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0009,0500,0502", outClusters: "0003,0019", manufacturer: "Heiman", model: "WarningDevice", mnmn: "SmartThings", vid: "generic-siren-8", deviceJoinName: "HEIMAN Siren" //HEIMAN Smart Siren fingerprint manufacturer: "frient A/S", model :"SIRZB-110", deviceJoinName: "frient Siren", mnmn: "SmartThingsCommunity", vid: "33d3bbac-144c-3a31-b022-0fc5c74240a3" // frient Smart Siren, 2B 0104 0403 00 05 0000 0003 0502 0500 0001 02 000A 0019 } From a5a6716a5390e611dbf1de895b4f3466ebd1f109 Mon Sep 17 00:00:00 2001 From: ccheng-aeotec Date: Tue, 22 Jun 2021 02:23:49 -0700 Subject: [PATCH 235/422] DevWs for Aeotec Group GmbH containing containing Aeotec Wallmote (#68294) * DevWs for Aeotec Group GmbH containing containing Aeotec Wallmote * Updated code format as requested * Updated spacing on Line 193 Co-authored-by: Christopher Cheng --- .../aeotec-wallmote.groovy | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy b/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy index 9de9492599a..61e3d0cfa70 100644 --- a/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy +++ b/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy @@ -26,6 +26,7 @@ metadata { fingerprint mfr: "0086", model: "0082", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Aeotec Wallmote Quad fingerprint mfr: "0086", model: "0081", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Aeotec Wallmote fingerprint mfr: "0060", model: "0003", deviceJoinName: "Everspring Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Everspring Wall Switch + fingerprint mfr: "0371", model: "0016", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Aeotec illumino Wallmote 7 } tiles(scale: 2) { @@ -44,7 +45,7 @@ metadata { } def getNumberOfButtons() { - def modelToButtons = ["0082" : 4, "0081": 2, "0003": 2] + def modelToButtons = ["0082" : 4, "0081": 2, "0003": 2, "0016": 2] return modelToButtons[zwaveInfo.model] ?: 1 } @@ -171,6 +172,8 @@ def getChildDevice(button) { private getSupportedButtonValues() { if (isEverspring()) { return ["pushed", "held", "double"] + } else if (isWallMote7()) { + return ["pushed", "held", "double", "pushed_3x", "pushed_4x", "pushed_5x"] } else { return ["pushed", "held"] } @@ -181,8 +184,14 @@ private getButtonAttributesMap() { 0: "pushed", 2: "held", 3: "double" - ]} - else {[ + ]} else if (isWallMote7()) {[ + 0: "pushed", + 2: "held", + 3: "double", + 4: "pushed_3x", + 5: "pushed_4x", + 6: "pushed_5x" + ]} else {[ 0: "pushed", 1: "held" ]} @@ -192,4 +201,6 @@ private isEverspring() { zwaveInfo.model.equals("0003") } - +private isWallMote7() { + zwaveInfo.model.equals("0016") +} \ No newline at end of file From 1fcd7edc2bd653147e43d7ab3ed28b5d8b114f92 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Wed, 23 Jun 2021 05:52:24 +0900 Subject: [PATCH 236/422] DevWs for SHINA SYSTEM containing containing Ikea Button (#67175) * DevWs for SHINA SYSTEM containing containing Ikea Button * Change deviceJoinName * Updated with easier code * SiHAS BSM300Z is refactored. * battery voltage -> battery percet, Added SBM300Zx series * space to tab * Added the sendButtonEvent for BSM300 and SBM300Z1 * Added SiHAS Remote Control device * Restored ikea-button DTH * Merge divided functions into one --- .../zigbee-multi-button.groovy | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy index 317bab3c551..628bd4486ca 100644 --- a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy +++ b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy @@ -37,6 +37,11 @@ metadata { fingerprint inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FCCC, 1000", outClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FCCC, 1000", manufacturer: "ADUROLIGHT", model: "ADUROLIGHT_CSC", deviceJoinName: "Eria Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Eria scene button switch V2.0 fingerprint inClusters: "0000, 0003, 0008, FCCC, 1000", outClusters: "0003, 0004, 0006, 0008, FCCC, 1000", manufacturer: "AduroSmart Eria", model: "Adurolight_NCC", deviceJoinName: "Eria Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Eria dimming button switch V2.1 fingerprint inClusters: "0000, 0003, 0008, FCCC, 1000", outClusters: "0003, 0004, 0006, 0008, FCCC, 1000", manufacturer: "ADUROLIGHT", model: "Adurolight_NCC", deviceJoinName: "Eria Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Eria dimming button switch V2.0 + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "MSM-300Z", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "BSM-300Z", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB1", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB2", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB3", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" } tiles { @@ -90,11 +95,23 @@ def parseAttrMessage(description) { def getButtonEvent(descMap) { if (descMap.commandInt == 1) { - getButtonResult("press") - } - else if (descMap.commandInt == 0) { - def button = buttonMap[device.getDataValue("model")][descMap.sourceEndpoint] - getButtonResult("release", button) + if (isShinaButton()) { + def button = descMap.sourceEndpoint.toInteger() + getButtonResult("double", button) + } else { + getButtonResult("press") + } + } else if (descMap.commandInt == 0) { + if (isShinaButton()) { + def button = descMap.sourceEndpoint.toInteger() + getButtonResult("pushed", button) + } else { + def button = buttonMap[device.getDataValue("model")][descMap.sourceEndpoint] + getButtonResult("release", button) + } + } else if (descMap.commandInt == 2) { + def button = descMap.sourceEndpoint.toInteger() + getButtonResult("held", button) } } @@ -114,13 +131,18 @@ def getButtonResult(buttonState, buttonNumber = 1) { } else if (buttonState == 'press') { state.pressTime = now() return event - } + } else if ((buttonState == 'double') || (buttonState == 'pushed') || (buttonState == 'held')) { + def descriptionText = getButtonName() + " ${buttonNumber} was ${buttonState}" + event = createEvent(name: "button", value: buttonState, data: [buttonNumber: buttonNumber], descriptionText: descriptionText, isStateChange: true) + sendEventToChild(buttonNumber, event) + return createEvent(descriptionText: descriptionText) + } } def sendEventToChild(buttonNumber, event) { String childDni = "${device.deviceNetworkId}:$buttonNumber" def child = childDevices.find { it.deviceNetworkId == childDni } - child?.sendEvent(event) + child?.sendEvent(event) } def getBatteryPercentageResult(rawValue) { @@ -245,6 +267,8 @@ private getSupportedButtonValues() { values = ["pushed"] } else if (isAduroSmartRemote()) { values = ["pushed"] + } else if (isShinaButton()) { + values = ["pushed","held","double"] } else { values = ["pushed", "held"] } @@ -256,7 +280,12 @@ private getModelNumberOfButtons() {[ "3450-L2" : 4, "SceneSwitch-EM-3.0" : 4, "ADUROLIGHT_CSC" : 4, - "Adurolight_NCC" : 4 + "Adurolight_NCC" : 4, + "BSM-300Z" : 1, + "MSM-300Z" : 4, + "SBM300ZB1" : 1, + "SBM300ZB2" : 2, + "SBM300ZB3" : 3 ]} private getModelBindings(model) { @@ -316,5 +345,6 @@ def isHeimanButton(){ device.getDataValue("model") == "SceneSwitch-EM-3.0" } - - +private Boolean isShinaButton() { + ((device.getDataValue("model") == "BSM-300Z") || (device.getDataValue("model") == "MSM-300Z") || (device.getDataValue("model") == "SBM300ZB1") || (device.getDataValue("model") == "SBM300ZB2") || (device.getDataValue("model") == "SBM300ZB3")) +} \ No newline at end of file From 89a681dc155c037aa820d21fb31f06a1e7c8b350 Mon Sep 17 00:00:00 2001 From: ccheng-aeotec Date: Tue, 22 Jun 2021 23:24:56 -0700 Subject: [PATCH 237/422] DevWs for Aeotec Group GmbH containing containing Z-Wave Dimmer Switch Generic and 1 more (#68523) * DevWs for Aeotec Group GmbH containing containing Z-Wave Dimmer Switch Generic and 1 more * Updated Delay on On and Off definitions * Remove 200ms static input on line 183 and 190. Use Default which is already 200ms. * Tested without groovy secure encapsulation to confirm if S2_Authentication works. It works for both illumino Dimmer and illumino Switch. * Spotted a few formatting issues when re-updating code. All coding format should be fixed now. Co-authored-by: Christopher Cheng --- .../zwave-dimmer-switch-generic.groovy | 7 ++++--- .../zwave-switch-generic.src/zwave-switch-generic.groovy | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zwave-dimmer-switch-generic.src/zwave-dimmer-switch-generic.groovy b/devicetypes/smartthings/zwave-dimmer-switch-generic.src/zwave-dimmer-switch-generic.groovy index 2fe7eb248b6..08317893da6 100644 --- a/devicetypes/smartthings/zwave-dimmer-switch-generic.src/zwave-dimmer-switch-generic.groovy +++ b/devicetypes/smartthings/zwave-dimmer-switch-generic.src/zwave-dimmer-switch-generic.groovy @@ -51,6 +51,7 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF02", deviceJoinName: "Minoston Dimmer Switch" //Minoston Toggle Dimmer Switch fingerprint mfr: "0312", prod: "AA00", model: "AA02", deviceJoinName: "Evalogik Dimmer Switch" //Evalogik Smart Dimmer Switch fingerprint mfr: "0312", prod: "C000", model: "C002", deviceJoinName: "Evalogik Dimmer Switch" //Evalogik Smart Plug Dimmer + fingerprint mfr: "0371", prod: "0103", model: "0025", deviceJoinName: "Aeotec Dimmer Switch" //Aeotec illumino Dimmer Switch } simulator { @@ -272,8 +273,8 @@ def refresh() { def isHoneywellDimmer() { zwaveInfo?.mfr?.equals("0039") && ( (zwaveInfo?.prod?.equals("5044") && zwaveInfo?.model?.equals("3033")) || - (zwaveInfo?.prod?.equals("5044") && zwaveInfo?.model?.equals("3038")) || - (zwaveInfo?.prod?.equals("4944") && zwaveInfo?.model?.equals("3038")) || - (zwaveInfo?.prod?.equals("4944") && zwaveInfo?.model?.equals("3130")) + (zwaveInfo?.prod?.equals("5044") && zwaveInfo?.model?.equals("3038")) || + (zwaveInfo?.prod?.equals("4944") && zwaveInfo?.model?.equals("3038")) || + (zwaveInfo?.prod?.equals("4944") && zwaveInfo?.model?.equals("3130")) ) } \ No newline at end of file diff --git a/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy b/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy index 2d529d5af8c..c9727f3a775 100644 --- a/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy +++ b/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy @@ -62,6 +62,7 @@ metadata { fingerprint mfr: "0312", prod: "C000", model: "CO05", deviceJoinName: "Evalogik Outlet", ocfDeviceType: "oic.d.smartplug" //Evalogik Mini Outdoor Smart Plug fingerprint mfr: "031E", prod: "0004", model: "0001", deviceJoinName: "Inovelli Switch" //Inovelli Switch fingerprint mfr: "001D", prod: "0037", model: "0002", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Tamper Resistant Outlet ZW15R + fingerprint mfr: "0371", prod: "0103", model: "0026", deviceJoinName: "Aeotec Wall Switch" //Aeotec illumino Wall Switch } // simulator metadata From b0bfc656c88a99093217b96e5ba13281195a76cd Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 23 Jun 2021 13:16:09 -0700 Subject: [PATCH 238/422] GPD-1105 Remove number from preference title Teams have reported that they are unsatisfied with the format that Fibaro used for their preference titles. --- .../fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy | 2 +- .../fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy | 2 +- .../fibaro-door-window-sensor-2.groovy | 2 +- .../fibaro-double-switch-2-zw5.groovy | 2 +- .../fibaro-motion-sensor-zw5.groovy | 2 +- .../fibaro-single-switch-2-zw5.groovy | 4 ++-- .../fibaro-wall-plug-eu-zw5.groovy | 2 +- .../fibaro-wall-plug-us-zw5.groovy | 2 +- .../widom-smart-dry-contact.groovy | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy index 2c35cb117c8..842a8770507 100644 --- a/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-co-sensor-zw5.src/fibaro-co-sensor-zw5.groovy @@ -69,7 +69,7 @@ metadata { preferences { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" diff --git a/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy b/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy index dfed370f98a..9b2c41d1a5a 100644 --- a/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy @@ -70,7 +70,7 @@ metadata { preferences { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" diff --git a/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy b/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy index d7d0ce0dfee..a5e25d6a1af 100644 --- a/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy +++ b/devicetypes/fibargroup/fibaro-door-window-sensor-2.src/fibaro-door-window-sensor-2.groovy @@ -91,7 +91,7 @@ metadata { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" 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 01e563ef9b3..9fe9b363a43 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 @@ -45,7 +45,7 @@ metadata { preferences { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" 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 2443d2f86cc..4eec9644d80 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 @@ -91,7 +91,7 @@ metadata { ) parameterMap().each { input( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" diff --git a/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy b/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy index 6fdbc6262d5..4c66b89a48a 100644 --- a/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy @@ -47,7 +47,7 @@ metadata { preferences { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" @@ -75,7 +75,7 @@ private getPrefsFor(String name) { parameterMap().findAll( {it.key.contains(name)} ).each { input ( name: it.key, - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: it.type, options: it.options, 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 2f425ade486..710beff621a 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 @@ -47,7 +47,7 @@ metadata { preferences { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" 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 944b2744293..1417165a9d8 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 @@ -48,7 +48,7 @@ metadata { preferences { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" diff --git a/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy b/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy index a5fdeabee2d..a124d573b0f 100644 --- a/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy +++ b/devicetypes/widomsrl/widom-smart-dry-contact.src/widom-smart-dry-contact.groovy @@ -33,7 +33,7 @@ metadata { parameterMap().each { input ( - title: "${it.num}. ${it.title}", + title: "${it.title}", description: it.descr, type: "paragraph", element: "paragraph" From 9c2b27aa16b98ec534474273edf28493c67f6b5d Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Fri, 2 Jul 2021 18:39:35 +0800 Subject: [PATCH 239/422] DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch (#69686) * DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch * change jion name Co-authored-by: Winnie Wen --- .../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 b4c54658a1a..507ec4b53e2 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -52,6 +52,7 @@ metadata { fingerprint mfr: "031E", prod: "0002", model: "0001", deviceJoinName: "Inovelli Switch" //US //Inovelli Switch Red Series fingerprint mfr: "0154", prod: "0003", model: "000A", deviceJoinName: "POPP Outlet", ocfDeviceType: "oic.d.smartplug" //EU //POPP Smart Outdoor Plug fingerprint mfr: "010F", prod: "1F01", model: "1000", deviceJoinName: "Fibaro Outlet", ocfDeviceType: "oic.d.smartplug" //EU //Fibaro walli Outlet //Fibaro Outlet + fingerprint mfr: "0312", prod: "FF00", model: "FF0E", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP21ZP } // simulator metadata From 7bbd865dbf9c46822804cf6078e9f99e1ca12f15 Mon Sep 17 00:00:00 2001 From: jiangshanyang <72915227+jiangshanyang0203@users.noreply.github.com> Date: Fri, 2 Jul 2021 22:13:00 +0800 Subject: [PATCH 240/422] Adjust the calculation method of battery of ThirdReality door sensor --- .../Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index 29eb6e072dc..f36c65ea5f6 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -127,7 +127,7 @@ def refresh() { log.debug "Refreshing Battery and ZONE Status" def manufacturer = getDataValue("manufacturer") def refreshCmds = zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) - if (manufacturer == "ORVIBO" || manufacturer == "eWeLink" || manufacturer == "HEIMAN" || manufacturer == "Third Reality, Inc") { + if (manufacturer == "ORVIBO" || manufacturer == "eWeLink" || manufacturer == "HEIMAN" ) { refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } else { // this is actually just supposed to be for Aurora, but we'll make it the default as it's more widely supported refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) From 38031697b4a8a660a7124779a2139fb5b5182664 Mon Sep 17 00:00:00 2001 From: Konrad Date: Fri, 2 Jul 2021 16:54:14 +0200 Subject: [PATCH 241/422] ICP-14003 - adds mnmn and vid for Leviton Zigbee Dimmers: DG3HL,DG6HD fingerprints --- .../smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy b/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy index 8e90fbedd01..19854866e2a 100644 --- a/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy +++ b/devicetypes/smartthings/zll-dimmer-bulb.src/zll-dimmer-bulb.groovy @@ -53,8 +53,8 @@ metadata { fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0019", manufacturer: "innr", model: "RB 145", deviceJoinName: "Innr Light" //Innr Smart Candle White // Leviton - fingerprint manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-in DImmer DG3HL, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 01 0019 - fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 + fingerprint manufacturer: "Leviton", model: "DG3HL", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.smartplug", mnmn: "SmartThings", vid:"SmartThings-smartthings-Leviton_Zigbee_Dimmer" //Leviton Zigbee Plug-in DImmer DG3HL, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 01 0019 + fingerprint manufacturer: "Leviton", model: "DG6HD", deviceJoinName: "Leviton Dimmer Switch", ocfDeviceType: "oic.d.switch", mnmn: "SmartThings", vid:"SmartThings-smartthings-Leviton_Zigbee_Dimmer" //Leviton Zigbee Dimmer DG6HD, Raw Description: 01 0104 0101 00 08 0000 0003 0004 0005 0006 0008 0301 0B05 // OSRAM fingerprint profileId: "C05E", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 W clear", deviceJoinName: "OSRAM Light" //OSRAM SMART+ LED Smart Connected Light From 49e9a50099f6ff7f4ffa53940dfe4bf9b0c4776e Mon Sep 17 00:00:00 2001 From: natec007 Date: Fri, 2 Jul 2021 14:35:30 -0700 Subject: [PATCH 242/422] DevWs for Plaid Systems LLC containing containing Spruce Controller SST (#68969) * DevWs for Plaid Systems LLC containing containing Spruce Controller SST * fixed spaces to tabs --- .../spruce-controller.groovy | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy b/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy index 64c0a54dcd4..c2a6159c6df 100644 --- a/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy +++ b/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2020 PlaidSystems + * Copyright 2021 PlaidSystems * * 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: @@ -11,6 +11,14 @@ * for the specific language governing permissions and limitations under the License. * +Version v3.7 + * update add zoneOn, zoneOff commands for external integration + * move zone status update to parse + + Version v3.6 + * update setTouchButtonDuration to only apply when controller is switched off + * add external command settingsMap for use with user added Spruce Scheduler + Version v3.5 * update zigbee ONOFF cluster * update Health Check @@ -56,7 +64,7 @@ import groovy.json.JsonOutput import physicalgraph.zigbee.zcl.DataType //dth version -def getVERSION() {'v3.5 3-2021'} +def getVERSION() {'v3.7 6-2021'} def getDEBUG() {false} def getHC_INTERVAL_MINS() {60} //zigbee cluster, attribute, identifiers @@ -88,10 +96,13 @@ metadata { attribute "rainSensor", "string" attribute "valveDuration", "NUMBER" + command "zoneOn" + command "zoneOff" command "setStatus" command "setRainSensor" command "setControllerState" command "setValveDuration" + command "settingsMap" //new release fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01", zigbeeNodeType: "ROUTER", deviceJoinName: "Spruce Irrigation Controller" @@ -141,7 +152,8 @@ metadata { //----------------------zigbee parse-------------------------------// // Parse incoming device messages to generate events -def parse(String description) { +def parse(description) { + if (DEBUG) log.debug description def result = [] def endpoint, value, command def map = zigbee.parseDescriptionAsMap(description) @@ -179,7 +191,8 @@ def parse(String description) { def child = childDevices.find{it.deviceNetworkId == "${device.deviceNetworkId}:${endpoint}"} if (child) child.sendEvent(name: "valve", value: onoff) - if (device.latestValue("controllerState") == "off") return setTouchButtonDuration() + sendEvent(name: "status", value: "Zone ${endpoint-1} ${onoff}", descriptionText: "Zone ${endpoint-1} ${onoff}", displayed:true) + return setTouchButtonDuration() break case "rainsensor": def rainSensor = (value == 1 ? "wet" : "dry") @@ -317,7 +330,7 @@ def setTouchButtonDuration() { def sendCmds = [] sendCmds.push(zigbee.writeAttribute(zigbee.ONOFF_CLUSTER, OFF_WAIT_TIME_ATTRIBUTE, DataType.UINT16, touchButtonDuration, [destEndpoint: 1])) - return sendCmds + if (device.latestValue("controllerState") == "off") return sendCmds } //controllerState @@ -403,7 +416,6 @@ def valveOn(valueMap) { def endpoint = valueMap.dni.replaceFirst("${device.deviceNetworkId}:","").toInteger() def duration = (device.latestValue("valveDuration").toInteger()) - sendEvent(name: "status", value: "${valueMap.label} on for ${duration}min(s)", descriptionText: "Zone ${valueMap.label} on for ${duration}min(s)") if (DEBUG) log.debug "state ${state.hasConfiguredHealthCheck} ${zigbee.ONOFF_CLUSTER}" zoneOn(endpoint, duration) } @@ -411,14 +423,12 @@ def valveOn(valueMap) { def valveOff(valueMap) { def endpoint = valueMap.dni.replaceFirst("${device.deviceNetworkId}:","").toInteger() - sendEvent(name: "status", value: "${valueMap.label} turned off", descriptionText: "${valueMap.label} turned off") - zoneOff(endpoint) } def zoneOn(endpoint, duration) { - //send duration from slider - return zoneDuration(duration) + zigbee.command(zigbee.ONOFF_CLUSTER, 1, "", [destEndpoint: endpoint]) + //send duration + return zoneDuration(duration.toInteger()) + zigbee.command(zigbee.ONOFF_CLUSTER, 1, "", [destEndpoint: endpoint]) } def zoneOff(endpoint) { @@ -464,7 +474,7 @@ def startSchedule() { //write switch time settings map def settingsMap(WriteTimes, attrType) { - + if (DEBUG) log.debug "settingsMap ${WriteTimes}, ${attrType}" def runTime def sendCmds = [] for (endpoint in 1..17) { From f650e7d04f22b0a52ac58826d84eb786fd9fee02 Mon Sep 17 00:00:00 2001 From: NArlt <79513123+NArlt@users.noreply.github.com> Date: Fri, 2 Jul 2021 23:36:33 +0200 Subject: [PATCH 243/422] DevWs for TechniSat Digital GmbH containing containing TechniSat Double-Switch including child DTH (#66242) * DevWs for TechniSat Digital GmbH containing containing TechniSat Double-Switch * added child DTH for serial switch * requested changes * switched child DTH to "smartthings/Child Metering Switch" --- .../technisat-series-switch.groovy | 461 ++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy diff --git a/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy b/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy new file mode 100644 index 00000000000..c1287b2e8c7 --- /dev/null +++ b/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy @@ -0,0 +1,461 @@ +/** + * Copyright 2021 TechniSat + * + * 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. + * + */ +import groovy.json.JsonOutput + +metadata { + definition (name: "TechniSat Series switch", namespace: "TechniSat", author: "TechniSat", vid:"generic-switch-power-energy", + mnmn: "SmartThings") { + capability "Energy Meter" + capability "Switch" + capability "Power Meter" + capability "Refresh" + capability "Configuration" + capability "Health Check" + + fingerprint mfr: "0299", prod: "0003", model: "1A91", deviceJoinName: "TechniSat Switch 1" + } + + preferences { + parameterMap.each { + input(title: "Parameter ${it.paramZwaveNum}: ${it.title}", + description: it.descr, + type: "paragraph", + element: "paragraph") + if (it.enableSwitch) { + input(name: it.enableKey, + title: "Enable", + type: "bool", + required: false) + } + input(name: it.key, + title: it.paramName, + type: it.type, + options: it.values, + range: it.range, + required: false) + } + } +} + +private createChild() { + + log.debug "createChild componentLabel: ${componentLabel}" + try { + String dni = "${device.deviceNetworkId}:2" + def componentLabel = "${device.displayName[0..-2]}2" + addChildDevice("smartthings","Child Metering Switch", dni, device.getHub().getId(), + [completedSetup: true, label: "${componentLabel}", isComponent: false]) + log.debug "Endpoint 2 (TechniSat Series switch child) added as $componentLabel" + } catch (e) { + log.warn "Failed to add endpoint 2 ($desc) as TechniSat Series switch child - $e" + } +} + +def installed() { + log.debug "installed()" + createChild() + initStateConfig() + initialize() +} + +def updated() { + log.debug "updated()" + initialize() + syncConfig() +} + +def initialize() { + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +def getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x25: 1, // Switch Binary + 0x32: 3, // Meter + 0x56: 1, // Crc16Encap + 0x60: 3, // Multi-Channel + 0x70: 2, // Configuration + 0x98: 1, // Security + ] +} + +def parse(String description) { + def result = null + if (description != "updated") { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result = zwaveEvent(cmd) + log.debug("'$description' parsed to $result") + } else { + log.debug("Couldn't zwave.parse '$description'") + } + } + result +} + +def createMeterEvent(cmd) { + def eventMap = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kWh" + } else if (cmd.scale == 1) { + eventMap.name = "energy" + eventMap.value = cmd.scaledMeterValue + eventMap.unit = "kVAh" + } else if (cmd.scale == 2) { + eventMap.name = "power" + eventMap.value = Math.round(cmd.scaledMeterValue) + eventMap.unit = "W" + } + } + eventMap +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd, endpoint=null) { + log.debug "v3 Meter report endpoint $endpoint: "+cmd + if (endpoint == 1) { + createEvent(createMeterEvent(cmd)) + } else if (endpoint == 2) { + childDevices[0]?.sendEvent(createMeterEvent(cmd)) + } + +} + +def handlOnOffReport(cmd, endpoint) { + def value = (cmd.value ? "on" : "off") + if (endpoint == 1) { + def evt = createEvent(name: "switch", value: value, type: "physical", descriptionText: "$device.displayName was turned $value") + if (evt.isStateChange) { + [evt, response(["delay 3000",encapEp(endpoint, meterGet(scale: 2))])] + } else { + evt + } + } else if (endpoint == 2) { + childDevices[0]?.sendEvent(name: "switch", value: value, type: "physical", descriptionText: "$device.displayName was turned $value") + sendHubCommand(encapEp(endpoint, meterGet(scale: 2))) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, endpoint=null) { + log.debug "Basic report endpoint $endpoint: "+cmd + handlOnOffReport(cmd,endpoint) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint=null) { + log.debug "Switch binary report endpoint: $endpoint: "+cmd + handlOnOffReport(cmd,endpoint) +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def param = parameterMap.find( {it.paramZwaveNum == cmd.parameterNumber } ) + + if (state.currentConfig."$param.key".status != "sync") { + if (state.currentConfig."$param.key"?.newValue == cmd.scaledConfigurationValue || + state.currentConfig."$param.key".status == "init") { + log.debug "Parameter ${param.key} set to value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".status = "sync" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } else { + log.debug "Parameter ${param.key} set to value failed: is:${cmd.scaledConfigurationValue} <> ${state.currentConfig."$param.key".newValue}" + state.currentConfig."$param.key".status = "failed" + syncConfig() + } + } else { + log.debug "Parameter ${param.key} update received. value:${cmd.scaledConfigurationValue}" + state.currentConfig."$param.key".value = cmd.scaledConfigurationValue + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + if (cmd.commandClass == 0x6C && cmd.parameter.size >= 4) { + cmd.parameter = cmd.parameter.drop(2) + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 1, 0x32: 3]) + log.debug "handle cmd on endpoint ${cmd.sourceEndPoint}" + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, endpoint=null) { + if (endpoint == null) { + log.debug "${device.displayName}: Unhandled: $cmd" + } else { + log.debug("$device.displayName: $cmd endpoint: $endpoint") + } + [:] +} + +def getEndpoint(deviceNetworkId) { + def split = deviceNetworkId?.split(":") + return (split.length > 1) ? split[1] as Integer : null +} + +def createOnOffCmd(value, endpoint = 1) { + log.debug "createOnOffCmd value $value endpoint $endpoint" + delayBetween([ + encapEp(endpoint, zwave.switchBinaryV1.switchBinarySet(switchValue: value)), + encapEp(endpoint, zwave.switchBinaryV1.switchBinaryGet()), + encapEp(endpoint, meterGet(scale: 2)) + ]) +} + +def on() { + createOnOffCmd(0xFF) +} + +def off() { + createOnOffCmd(0x00) +} + +def childOnOff(deviceNetworkId, value) { + def endpoint = getEndpoint(deviceNetworkId) + log.debug("childOnOff from endpoint ${endpoint}") + if (endpoint != null) { + sendHubCommand(createOnOffCmd(value, endpoint)) + } +} + +def ping() { + log.debug "ping()" + refresh() +} + +def poll() { + sendHubCommand(refresh()) +} + +def refreshAll() { + sendHubCommand(refresh(1)) + sendHubCommand(refresh(2)) +} + +def refresh(endpoint = 1) { + log.debug "refresh()" + delayBetween([ + encapEp(endpoint, zwave.switchBinaryV1.switchBinaryGet()), + encapEp(endpoint, meterGet(scale: 0)), + encapEp(endpoint, meterGet(scale: 2)) + ]) +} + +def childRefresh(deviceNetworkId) { + def endpoint = getEndpoint(deviceNetworkId) + log.debug("childRefresh from endpoint ${endpoint}") + if (endpoint != null) { + sendHubCommand(refresh(endpoint)) + } +} + + +def childReset(deviceNetworkId) { + def endpoint = getEndpoint(deviceNetworkId) + log.debug("childReset from endpoint ${endpoint}") +} + +def configure() { + log.debug "configure()" + def result = [] + + log.debug "Configure zwaveInfo: "+zwaveInfo + + initStateConfigFromDevice() + logStateConfig() + refreshAll() +} + +def meterGet(map) { + return zwave.meterV2.meterGet(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + log.debug "Parsed SecurityMessageEncapsulation into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract Secure command from $cmd" + } +} + +def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) { + def version = commandClassVersions[cmd.commandClass as Integer] + def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass) + def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data) + if (encapsulatedCommand) { + log.debug "Parsed Crc16Encap into: ${encapsulatedCommand}" + zwaveEvent(encapsulatedCommand) + } else { + log.warn "Unable to extract CRC16 command from $cmd" + } +} + +private secEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using Secure Encapsulation, command: $cmd" + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() +} + +private crcEncap(physicalgraph.zwave.Command cmd) { + log.debug "encapsulating command using CRC16 Encapsulation, command: $cmd" + zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format() +} + +def encapEp(endpointNumber, cmd) { + if (cmd instanceof physicalgraph.zwave.Command) { + encap(zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpointNumber).encapsulate(cmd)) + } else if (cmd.startsWith("delay")) { + cmd + } else { + def header = "600D00" + String.format("%s%02X%s", header, endpointNumber, cmd) + } +} + +private encap(physicalgraph.zwave.Command cmd) { + if (zwaveInfo?.zw?.contains("s")) { + secEncap(cmd) + } else if (zwaveInfo?.cc?.contains("56")) { + crcEncap(cmd) + } else { + log.debug "no encapsulation supported for command: $cmd" + cmd.format() + } +} + +private isConfigChanged(parameter) { + def settingsValue = settings."$parameter.key" + log.debug "isConfigChanged parameter:${parameter.key}: ${settingsValue}" + if (parameter.enableSwitch) { + if (settings."$parameter.enableKey" != null) { + if (settings."$parameter.enableKey" == false) { + settingsValue = 0; + } + } + } + if (settingsValue != null) { + Integer value = 0 + if (parameter.type == "number") { + value = settingsValue + } else { + value = Integer.parseInt(settingsValue) + } + if (state.currentConfig."$parameter.key".value != value) { + state.currentConfig."$parameter.key".newValue = value + log.debug "${parameter.key} set:${value} value:${state.currentConfig."$parameter.key".value} newValue:${state.currentConfig."$parameter.key".newValue}" + return true + } else if (state.currentConfig."$parameter.key".status != "sync") { + log.debug "${parameter.key} retry to set; is:${state.currentConfig."$parameter.key".value} should:${state.currentConfig."$parameter.key".newValue}" + return true + } + return false + } else { + log.debug "pref value not set yet" + return false + } +} + +private syncConfig() { + def commands = [] + parameterMap.each { + if (isConfigChanged(it)) { + log.debug "Parameter ${it.key} has been updated from value: ${state.currentConfig."$it.key".value} to ${state.currentConfig."$it.key".newValue}" + state.currentConfig."$it.key".status = "syncPending" + commands << response(encap(zwave.configurationV2.configurationSet(scaledConfigurationValue: state.currentConfig."$it.key".newValue, + parameterNumber: it.paramZwaveNum, size: it.paramZwaveSize))) + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } else if (state.currentConfig."$it.key".value == null) { + log.warn "Parameter ${it.key} no. ${it.paramZwaveNum} has no value. Please check preference declaration for errors." + } + } + if (commands) { + sendHubCommand(commands,1000) + } +} + +private initStateConfig() { + log.debug "initStateConfig()" + state.currentConfig = [:] + parameterMap.each { + log.debug "set $it.key" + state.currentConfig."$it.key" = [:] + state.currentConfig."$it.key".value = new Integer('0') + state.currentConfig."$it.key".newValue = new Integer('0') + state.currentConfig."$it.key".status = "init" + } +} + +private initStateConfigFromDevice() { + log.debug "initStateConfigFromDevice()" + def commands = [] + parameterMap.each { + commands << response(encap(zwave.configurationV2.configurationGet(parameterNumber: it.paramZwaveNum))) + } + if (commands) { + sendHubCommand(commands,1000) + } +} + +private logStateConfig() { + parameterMap.each { + log.debug "key:$it.key value: ${state.currentConfig."$it.key".value} newValue: ${state.currentConfig."$it.key".newValue} status: ${state.currentConfig."$it.key".status}" + } +} + +private getParameterMap() { + [ + [ + title: "Wattage meter report interval", + descr: "Interval of current wattage meter reports in 10 seconds. 3 ... 8640 (30 seconds - 1 day)", + key: "wattageMeterReportInterval", + paramName: "Set Value (3..8640)", + type: "number", + range: "3..8640", + enableSwitch: true, + enableSwitchDefaultValue: true, + enableKey: "wattageMeterReportDisable", + paramZwaveNum: 2, + paramZwaveSize: 1 + ], + [ + title: "Energy meter report interval", + descr: "Interval of active energy meter reports in minutes. 10 ... 30240 (10 minutes - 3 weeks)", + key: "energyMeterReportInterval", + enableSwitch: true, + enableSwitchDefaultValue: true, + enableKey: "energyMeterReportDisable", + paramName: "Set Value (10..30240)", + type: "number", + range: "10..30240", + paramZwaveNum: 3, + paramZwaveSize: 2 + ], + [ + title: "Operation mode of buttons T1 - T4", + descr: "Operation mode of buttons T1 - T4", + key: "buttonModeSetting", + paramName: "Select", + type: "enum", + values: [ + 0: "0 - top buttons turn outputs on, bottom buttons turn outputs off", + 1: "1 - buttons toggle the outputs on/off" + ], + paramZwaveNum: 4, + paramZwaveSize: 1 + ] + ] +} \ No newline at end of file From 1ef97e41afb168c3d6e2c619321ddc5030fb2a0e Mon Sep 17 00:00:00 2001 From: jiangshanyang <72915227+jiangshanyang0203@users.noreply.github.com> Date: Thu, 8 Jul 2021 21:38:58 +0800 Subject: [PATCH 244/422] Add smartplug and modify switch of ThirdReality Add smartplug and modify switch of ThirdReality Plug and Switch --- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 7884121a1a8..4e9f7be26d1 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -95,9 +95,10 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0019", manufacturer: "", model: "TERNCY-LS01", deviceJoinName: "Terncy Switch" //Terncy Smart Light Socket // Third Reality - fingerprint profileId: "0104", inClusters: "0000, 0006", outClusters: "0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS009Z", deviceJoinName: "RealitySwitch Switch" //RealitySwitch-Gen3 Zigbee Mode - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS008Z", deviceJoinName: "RealitySwitch Switch" //RealitySwitch Plus - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS007Z", deviceJoinName: "RealitySwitch Switch" //RealitySwitch + fingerprint profileId: "0104", inClusters: "0000, 0006", outClusters: "0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS009Z", deviceJoinName: "ThirdReality Switch" //RealitySwitch-Gen3 Zigbee Mode + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS008Z", deviceJoinName: "ThirdReality Switch" //RealitySwitch Plus + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", manufacturer: "Third Reality, Inc", model: "3RSS007Z", deviceJoinName: "ThirdReality Switch" //RealitySwitch + fingerprint profileId: "0104", deviceId: "0051", inClusters: "0000, 0003, 0004, 0005, 0006",outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RSP019BZ", deviceJoinName: "ThirdReality Plug", ocfDeviceType: "oic.d.smartplug" //RealityPlug // Dawon fingerprint profileId: "0104", inClusters: "0000, 0004, 0003, 0006, 0019, 0002, 0009", manufacturer: "DAWON_DNS", model: "PM-S140-ZB", deviceJoinName: "Dawon Switch" //DAWOS DNS In-Wall Switch PM-S140-ZB @@ -184,4 +185,4 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() -} \ No newline at end of file +} From d05a6aa8f8acc9cb42d3f847b196d78c5f9f9110 Mon Sep 17 00:00:00 2001 From: Aaron Huus Date: Tue, 13 Jul 2021 15:58:10 -0500 Subject: [PATCH 245/422] Updating Ecolink URL --- smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy index 2a3a78cb1d9..ca7f0cc9be0 100644 --- a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy +++ b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy @@ -201,7 +201,7 @@ def callback() { redirect_uri: callbackUrl ] - def tokenUrl = "https://www.ecobee.com/home/token?${toQueryString(tokenParams)}" + def tokenUrl = "${apiEndpoint}/token?${toQueryString(tokenParams)}" httpPost(uri: tokenUrl) { resp -> state.refreshToken = resp.data.refresh_token From 932788bee5aec50151f2c158a246d8a5ec1d8a7e Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Thu, 15 Jul 2021 23:42:34 -0700 Subject: [PATCH 246/422] DevWs for Smartenit, Inc containing containing EVSE (#70221) * DevWs for Smartenit, Inc containing containing EVSE * Space fixes * Adjusted MeteringCluster constant reference. Syntax fixes, * Spacing fixes, removed refresh() commands from health check * Configuration modifications. --- .../smartelek-evse.src/smartelek-evse.groovy | 341 ++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy diff --git a/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy b/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy new file mode 100644 index 00000000000..ccd01619da8 --- /dev/null +++ b/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy @@ -0,0 +1,341 @@ +/**************************************************************************** + * DRIVER NAME: Smartenit EVSE + * DESCRIPTION: Device handler for Smartenit SmartElek EVSE + * + * $Rev: $: 2 + * $Author: $: Luis Contreras + * $Date: $: 06/23/2021 + * $HeadURL: $: + + **************************************************************************** + * This software is owned by Compacta and/or its supplier and is protected + * under applicable copyright laws. All rights are reserved. We grant You, + * and any third parties, a license to use this software solely and + * exclusively on Compacta products. You, and any third parties must reproduce + * the copyright and warranty notice and any other legend of ownership on each + * copy or partial copy of the software. + * + * THIS SOFTWARE IS PROVIDED "AS IS". COMPACTA MAKES NO WARRANTIES, WHETHER + * EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, + * ACCURACY OR LACK OF NEGLIGENCE. COMPACTA SHALL NOT, UNDERN ANY CIRCUMSTANCES, + * BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, SPECIAL, + * INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON WHATSOEVER. + * + * Copyright Compacta International, Ltd 2016. All rights reserved + ****************************************************************************/ + // EVSE Cluster Doc: https://docs.smartenit.io/display/SMAR/EVSE+Processor+Details + + import groovy.transform.Field + + @Field final EVSECluster = 0xFF00 + @Field final MeteringCurrentSummation = 0x0000 + @Field final MeteringInstantDemand = 0x0400 + @Field final EnergyDivisor = 100000 + @Field final CurrentDivisor = 100 + @Field final ChargingStatus = 0x0000 + @Field final ChargerLevel = 0x0001 + @Field final ChargerAutoStart = 0x0003 + @Field final ChargerFault = 0x0004 + @Field final ChargerMaximumCurrent = 0x0011 + @Field final ChargerSessionDuration = 0x0013 + @Field final ChargerDeliveredSummation = 0x0014 + @Field final ChargerSessionSummation = 0x0015 + @Field final ChargerSessionPeakCurrent = 0x0016 + @Field final ChargerVRMS = 0x0020 + @Field final ChargerIRMS = 0x0021 + @Field final SmartenitMfrCode = 0x1075 + @Field final StartCharging = 0x00 + @Field final StopCharging = 0x02 + @Field final EnableAutoStartMode = 0x04 + @Field final DisableAutoStartMode = 0x05 + +metadata { + definition (name: "SmartElek EVSE", namespace: "Smartenit", author: "Luis Contreras", mnmn: "SmartThingsCommunity", vid: "18da1704-2bbc-37ec-92a5-35e911024cea", ocfDeviceType: "oic.d.smartplug") { + capability "monthpublic25501.chargerstate" + capability "monthpublic25501.chargerlevel" + capability "monthpublic25501.chargerfault" + capability "monthpublic25501.chargerirms" + capability "monthpublic25501.chargersessionpeakcurrent" + capability "monthpublic25501.chargersessionsummation" + capability "monthpublic25501.chargerautostart" + capability "monthpublic25501.chargermaximumcurrent" + capability "monthpublic25501.chargersessionduration" + capability "Actuator" + capability "Configuration" + capability "Refresh" + capability "Power Meter" + capability "Energy Meter" + capability "Switch" + capability "Health Check" + capability "Voltage Measurement" + + command "stopcharging" + command "startcharging" + + fingerprint model: "IOTEVSE-Z", manufacturer: "Smartenit, Inc", deviceJoinName: "Smartenit EVSE" + } +} + +def getFPoint(String FPointHex){ + return (Float)Long.parseLong(FPointHex, 16) +} + +// Parse incoming device messages to generate events +def parse(String description) { + def event = zigbee.getEvent(description) + if (event) { + log.debug "event: ${event}, ${event.name}, ${event.value}" + if (event.name == "power") { + sendEvent(name: "power", value: (event.value/EnergyDivisor)) + } else { + sendEvent(event) + } + } + else { + def mapDescription = zigbee.parseDescriptionAsMap(description) + log.debug "mapDescription... : ${mapDescription}" + if (mapDescription) { + if (mapDescription.clusterInt == zigbee.SIMPLE_METERING_CLUSTER) { + if (mapDescription.attrInt == MeteringCurrentSummation) { + return sendEvent(name:"energy", value: getFPoint(mapDescription.value)/EnergyDivisor) + } else if (mapDescription.attrInt == MeteringInstantDemand) { + return sendEvent(name:"power", value: getFPoint(mapDescription.value/EnergyDivisor)) + } + } else if (mapDescription.clusterInt == EVSECluster) { + log.debug "EVSE cluster, attrId: ${mapDescription.attrId}, value: ${mapDescription.value}" + if (mapDescription.attrInt == ChargingStatus) { + def strvalue = parseChargerStatusValue(mapDescription.value) + log.debug "charging status attribute: ${mapDescription.value}, ${strvalue}" + if (strvalue == "unplugged") { + sendEvent(name:"sessionDuration", value: "--") + sendEvent(name:"sessionSummation", value: 0) + } + if (strvalue == "charging") { + sendEvent(name:"switch", value:"on") + } else { + sendEvent(name:"switch", value:"off") + } + return sendEvent(name:"chargerStatus", value: strvalue) + } else if (mapDescription.attrInt == ChargerLevel) { + def strvalue = parseChargerLevelValue(mapDescription.value) + return sendEvent(name:"level", value: strvalue) + } else if (mapDescription.attrInt == ChargerAutoStart) { + def val = zigbee.convertHexToInt(mapDescription.value) + log.debug "autostart value: ${val} " + return sendEvent(name:"autoStart", value: val) + } else if (mapDescription.attrInt == ChargerFault) { + def strvalue = parseChargerFaultValue(mapDescription.value) + return sendEvent(name:"fault", value: strvalue) + } else if (mapDescription.attrInt == ChargerMaximumCurrent) { + log.debug "charger max current: ${mapDescription.value}" + return sendEvent(name:"maximumCurrent", value: getFPoint(mapDescription.value)/CurrentDivisor, unit: "A") + } else if (mapDescription.attrInt == ChargerSessionDuration) { + int time = (int) Long.parseLong(mapDescription.value, 16); + log.debug "ChargerSessionDuration attribute: ${mapDescription.value}, time: ${time}" + def hours = Math.round(Math.floor(time / 3600)) + def secs = time % 3600 + def mins = Math.round(Math.floor(secs / 60)) + def timestr = "${hours} hr:${mins} min" + return sendEvent(name:"sessionDuration", value: timestr) + } else if (mapDescription.attrInt == ChargerSessionSummation) { + log.debug "ChargerSessionSummation attribute: ${mapDescription.value}" + return sendEvent(name:"sessionSummation", value: getFPoint(mapDescription.value)/EnergyDivisor, unit: "kWh") + } else if (mapDescription.attrInt == ChargerSessionPeakCurrent) { + log.debug "ChargerSessionPeakCurrent attribute: ${mapDescription.value}" + return sendEvent(name:"sessionPeakCurrent", value: getFPoint(mapDescription.value) / 100, unit: "A") + } else if (mapDescription.attrInt == ChargerVRMS) { + log.debug "ChargerVRMS attribute: ${mapDescription.value}" + return sendEvent(name:"voltage", value: getFPoint(mapDescription.value) / 100) + } else if (mapDescription.attrInt == ChargerIRMS) { + log.debug "ChargerIRMS attribute: ${mapDescription.value}" + return sendEvent(name:"current", value: getFPoint(mapDescription.value) / 100, unit: "A") + } else { + log.debug "attribute not handled" + } + } + } + } +} + +def setAutoStart(val) { + log.debug "Set auto start to: ${val}" + sendEvent(name:"autoStart", value: val) + + if (val == "1") { + log.debug "Sending enable autostart" + zigbee.command(EVSECluster, EnableAutoStartMode, "", [mfgCode: SmartenitMfrCode]) + } else if (val == "0") { + log.debug "Sending disable autostart" + zigbee.command(EVSECluster, DisableAutoStartMode, "", [mfgCode: SmartenitMfrCode]) + } +} + +def setMaximumCurrent(val) { + log.debug "Set max current val: ${val}" + + sendEvent(name:"maximumCurrent", value: val, unit: "A") + + int newMax = (int) (val * 100) + int convert = ((newMax << 8) & 0xFF00) | ((newMax >> 8) & 0xFF) + + zigbee.writeAttribute(EVSECluster, ChargerMaximumCurrent, 0x21, convert, [mfgCode: SmartenitMfrCode]) +} + +def parseChargerLevelValue(val) { + log.debug "parseChargerLevelValue: ${val}" + switch (val as Integer) { + case 0: + log.debug "level is unknown" + return "Unknown" + case 1: + log.debug "Charging @ L1" + return "Level 1" + case 2: + log.debug "Charging @ L2" + return "Level 2" + default: + return "" + } +} + +def parseChargerFaultValue(val) { + log.debug "parseChargerFaultValue: ${val}" + switch (val as Integer) { + case 0: + log.debug "No fault" + return "None" + case 1: + log.debug "Meter failed" + return "Meter failure" + case 2: + log.debug "Overvoltage" + return "Overvoltage" + case 3: + log.debug "Undervoltage" + return "Undervoltage" + case 4: + log.debug "Overcurrent" + return "Overcurrent" + case 5: + log.debug "Overheating" + return "Overheating" + case 16: + log.debug "Contact Wet" + return "Contact Wet" + case 17: + log.debug "Contact Dry" + return "Contact Dry" + case 18: + log.debug "Ground fault" + return "Ground Fault" + case 19: + log.debug "Pilot Short Circuit" + return "Short Circuit" + case 20: + log.debug "Wrong Supply" + return "Wrong Supply" + case 21: + log.debug "GFCI Failure" + return "GFCI Failure" + case 22: + log.debug "GMI Fault" + return "GMI Fault" + default: + log.debug "Unknown fault" + return "Unknown fault" + } +} + +def parseChargerStatusValue(val) { + log.debug "parseChargerStatusValue: ${val}" + switch (val as Integer) { + case 0: + log.debug "value is Unplugged" + return "unplugged" + break; + case 1: + log.debug "value is Plugged In" + return "pluggedin" + break; + case 2: + return "pluggedin" + break; + case 3: + log.debug "value is Charging" + return "charging" + break; + case 4: + log.debug "value is Fault" + return "fault" + break; + case 5: + log.debug "value is Charging Completed" + return "chargingcompleted" + break; + default: + return "" + break; + } +} + +def on() { + log.debug "received on command" + zigbee.command(EVSECluster, StartCharging, "", [mfgCode: SmartenitMfrCode]) +} + +def off() { + log.debug "received off command" + zigbee.command(EVSECluster, StopCharging, "", [mfgCode: SmartenitMfrCode]) +} + +def stopcharging() { + log.debug "sending stopcharging command.." + zigbee.command(EVSECluster, StopCharging, "", [mfgCode: SmartenitMfrCode]) +} + +def startcharging() { + log.debug "sending startcharging command.." + zigbee.command(EVSECluster, StartCharging, "", [mfgCode: SmartenitMfrCode]) +} + +def refresh() { + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation) + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringInstantDemand) + + zigbee.readAttribute(EVSECluster, ChargingStatus, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerVRMS, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerSessionSummation, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerSessionDuration, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerIRMS, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerLevel, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerMaximumCurrent, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerFault, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerSessionPeakCurrent, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(EVSECluster, ChargerAutoStart, [mfgCode: SmartenitMfrCode]) +} + +def configure() { + log.debug "in configure()" + configureHealthCheck() + return (zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation, 0x25, 0, 600, 50) + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, MeteringInstantDemand, 0x2a, 0, 600, 50) + + zigbee.configureReporting(EVSECluster, ChargingStatus, 0x30, 0x0, 0x0, null, [mfgCode: SmartenitMfrCode]) + + refresh() + ) +} + +def configureHealthCheck() { + Integer hcIntervalMinutes = 10 + sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) +} + +def updated() { + log.debug "in updated()" + // updated() doesn't have it's return value processed as hub commands, so we have to send them explicitly + def cmds = configureHealthCheck() + cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it)) } +} + +def ping() { + return refresh() +} \ No newline at end of file From 75efb54ee39b81ddda44ec86515f65182084f697 Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Fri, 16 Jul 2021 15:13:15 +0800 Subject: [PATCH 247/422] WWST-7664 HEIMAN wall outlet HS6ESK-EF (#70996) --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 1 + 1 file changed, 1 insertion(+) 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 64b6b97e3c8..a280ced8888 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -29,6 +29,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B04", outClusters: "0003", manufacturer: "REXENSE", model: "HY0105", deviceJoinName: "HONYAR Outlet" //HONYAR Smart Outlet (USB) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B04", outClusters: "0003", manufacturer: "REXENSE", model: "HY0104", deviceJoinName: "HONYAR Outlet" //HONYAR Smart Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0009, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "E_Socket", deviceJoinName: "HEIMAN Outlet" //HEIMAN Smart Outlet + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "HS6ESK-W-EF-3.0", deviceJoinName: "HEIMAN Outlet", ocfDeviceType: "oic.d.smartplug" //HEIMAN Smart Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04, 0702, FC82", outClusters: "0003, 000A, 0019", manufacturer: "sengled", model: "E1C-NB7", deviceJoinName: "Sengled Outlet" //Sengled Smart Plug with Energy Tracker fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-131", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-132", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 From 05bdf17ee2f4419d1dfdf07f542f8ff9914cc3b3 Mon Sep 17 00:00:00 2001 From: Steven Green Date: Fri, 16 Jul 2021 12:59:07 -0700 Subject: [PATCH 248/422] ICP-14063 Yale YDF40 reports double lock percentage (#68519) * ICP-14063 Yale YDF40 reports double lock percentage * refactoring --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 8f63826a068..cb71cefc23f 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -477,10 +477,11 @@ private def parseAttributeResponse(String description) { def deviceName = device.displayName if (clusterInt == CLUSTER_POWER && attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) { responseMap.name = "battery" - responseMap.value = Math.round(Integer.parseInt(descMap.value, 16) / 2) // Handling Yale locks incorrect battery reporting issue if (reportsBatteryIncorrectly()) { responseMap.value = Integer.parseInt(descMap.value, 16) + } else { + responseMap.value = Math.round(Integer.parseInt(descMap.value, 16) / 2) } responseMap.descriptionText = "Battery is at ${responseMap.value}%" } else if (clusterInt == CLUSTER_DOORLOCK && attrInt == DOORLOCK_ATTR_LOCKSTATE) { @@ -1158,8 +1159,9 @@ def reportsBatteryIncorrectly() { "YRD210 PB DB", "YRD220/240 TSDB", "YRL210 PB LL", + "c700000202" //YDF40 ] - return (isYaleLock() && device.getDataValue("model") in badModels) + return device.getDataValue("model") in badModels } /** From 2a77200943ff07b494fb5598d2e76392d1c3ffa9 Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Fri, 16 Jul 2021 15:03:18 -0700 Subject: [PATCH 249/422] DevWs for Smartenit, Inc containing containing Testing (#68971) * DevWs for Smartenit, Inc containing containing Testing * Style adjustments, child device handlers added. * Removed installed(), updated(), parse() from Child DTHes. Constants access modified. JoinName changed to Smartenit Switch. * Event description map comparison type modifications * Space fixes, cluster integer comparison fix * Child device reference changed to Child Switch --- .../iot8-z-child-analog-contact-switch.groovy | 32 +++ .../iot8-z-child-contact-switch.groovy | 32 +++ .../smartenit/iot8-z.src/iot8-z.groovy | 223 ++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 devicetypes/smartenit/iot8-z-child-analog-contact-switch.src/iot8-z-child-analog-contact-switch.groovy create mode 100644 devicetypes/smartenit/iot8-z-child-contact-switch.src/iot8-z-child-contact-switch.groovy create mode 100644 devicetypes/smartenit/iot8-z.src/iot8-z.groovy diff --git a/devicetypes/smartenit/iot8-z-child-analog-contact-switch.src/iot8-z-child-analog-contact-switch.groovy b/devicetypes/smartenit/iot8-z-child-analog-contact-switch.src/iot8-z-child-analog-contact-switch.groovy new file mode 100644 index 00000000000..5b86c69f3d2 --- /dev/null +++ b/devicetypes/smartenit/iot8-z-child-analog-contact-switch.src/iot8-z-child-analog-contact-switch.groovy @@ -0,0 +1,32 @@ +/** + * Virtual IOT8Z + * + * Copyright 2021 Luis Contreras + * + * 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: "IOT8-Z-child-analog-contact-switch", namespace: "Smartenit", author: "Luis Contreras", cstHandler: true, mnmn: "SmartThingsCommunity", vid: "50830b33-69d6-32a0-bebd-952eac44d074") { + capability "Contact Sensor" + capability "Sensor" + capability "Switch" + capability "monthpublic25501.analogSensor" + } +} + +// handle commands +def on() { + parent.childOn(device.deviceNetworkId) +} + +def off() { + parent.childOff(device.deviceNetworkId) +} \ No newline at end of file diff --git a/devicetypes/smartenit/iot8-z-child-contact-switch.src/iot8-z-child-contact-switch.groovy b/devicetypes/smartenit/iot8-z-child-contact-switch.src/iot8-z-child-contact-switch.groovy new file mode 100644 index 00000000000..d142fe92d17 --- /dev/null +++ b/devicetypes/smartenit/iot8-z-child-contact-switch.src/iot8-z-child-contact-switch.groovy @@ -0,0 +1,32 @@ +/** + * IOT8-Z_DI + * + * Copyright 2021 Luis Contreras + * + * 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: "IOT8-Z-child-contact-switch", namespace: "Smartenit", author: "Luis Contreras") { + capability "Actuator" + capability "Contact Sensor" + capability "Switch" + capability "Health Check" + } +} + +def on() { + log.debug "Executing 'on'" + parent.childOn(device.deviceNetworkId) +} + +def off() { + log.debug "Executing 'off'" + parent.childOff(device.deviceNetworkId) +} \ No newline at end of file diff --git a/devicetypes/smartenit/iot8-z.src/iot8-z.groovy b/devicetypes/smartenit/iot8-z.src/iot8-z.groovy new file mode 100644 index 00000000000..c500d1325ce --- /dev/null +++ b/devicetypes/smartenit/iot8-z.src/iot8-z.groovy @@ -0,0 +1,223 @@ +/** + * IOT8Z + * + * Copyright 2021 Luis Contreras + * + * 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: "IOT8-Z", namespace: "Smartenit", author: "Luis Contreras", cstHandler: true, mnmn: "SmartThingsCommunity", vid: "6d510b74-469c-3fa0-be7a-ec0894d38dcb") { + capability "Contact Sensor" + capability "Configuration" + capability "Refresh" + capability "Health Check" + capability "Sensor" + capability "Switch" + capability "monthpublic25501.analogSensor" + + command "childOn", ["string"] + command "childOff", ["string"] + + fingerprint manufacturer: "Smartenit, Inc", model: "IOT8-Z", deviceJoinName: "Smartenit Switch", profileId: "0104", inClusters: "0000, 0003, 0006, 000C, 000F", outClusters: "0019" + } +} + +private getANALOG_INPUT_CLUSTER() { 0x000C } +private getBINARY_INPUT_CLUSTER() { 0x000F } +private getPRESENT_VALUE_ATTRIBUTE() { 0x0055 } +private getONOFF_ATTRIBUTE() { 0x0000 } + +def installed() { + log.debug "Installed" + createChildDevices() +} + +def updated() { + log.debug "Updated" + refresh() +} + +// parse events into attributes +def parse(String description) { + Map eventMap = zigbee.getEvent(description) + Map eventDescMap = zigbee.parseDescriptionAsMap(description) + + if (eventMap) { + if ((eventDescMap?.sourceEndpoint == "01") || (eventDescMap?.endpoint == "01")) { + if (eventDescMap?.clusterInt == ANALOG_INPUT_CLUSTER) { + return createEvent(name: "inputValue", value: eventDescMap?.value) + } else { + sendEvent(eventMap) + } + } else { + def childDevice = childDevices.find { + it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.sourceEndpoint}" || it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.endpoint}" + } + if (childDevice) { + childDevice.sendEvent(eventMap) + } else { + log.debug "Child device: $device.deviceNetworkId:${eventDescMap.sourceEndpoint} was not found" + } + } + } else if (eventDescMap) { + if ((eventDescMap?.sourceEndpoint == "01") || (eventDescMap?.endpoint == "01")) { + if (eventDescMap?.clusterInt == BINARY_INPUT_CLUSTER) { + if (eventDescMap?.value == "00") { + return createEvent(name: "contact", value: "open") + } else if (eventDescMap?.value == "01") { + return createEvent(name: "contact", value: "closed") + } + } else if (eventDescMap?.clusterInt == ANALOG_INPUT_CLUSTER) { + long convertedValue = Long.parseLong(eventDescMap?.value, 16) + Float percentage = Float.intBitsToFloat(convertedValue.intValue()) + percentage = (percentage / 1.60) * 100.0 + def ceilingVal = Math.ceil(percentage) + if (ceilingVal > 100.0) { + ceilingVal = 100.0 + } + + int intValue = (int) ceilingVal + return createEvent(name: "inputValue", value: intValue) + } + } else { + def childDevice = childDevices.find { + it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.sourceEndpoint}" || it.deviceNetworkId == "$device.deviceNetworkId:${eventDescMap.endpoint}" + } + if (childDevice) { + if (eventDescMap?.clusterInt == BINARY_INPUT_CLUSTER) { + if (eventDescMap?.value == "00") { + def map = createEvent(name: "contact", value: "open") + childDevice.sendEvent(map) + } else if (eventDescMap?.value == "01") { + def map = createEvent(name: "contact", value: "closed") + childDevice.sendEvent(map) + } + } else if (eventDescMap?.clusterInt == ANALOG_INPUT_CLUSTER) { + long convertedValue = Long.parseLong(eventDescMap?.value, 16) + Float percentage = Float.intBitsToFloat(convertedValue.intValue()) + percentage = (percentage / 1.60) * 100.0 + def ceilingVal = Math.ceil(percentage) + if (ceilingVal > 100.0) { + ceilingVal = 100.0 + } + + int intValue = (int) ceilingVal + + def map = createEvent(name: "inputValue", value: intValue) + childDevice.sendEvent(map) + } + } + } + } +} + +def on() { + zigbee.on() +} + +def off() { + zigbee.off() +} + +def childOn(String dni) { + def childEndpoint = getChildEndpoint(dni) + zigbee.command(zigbee.ONOFF_CLUSTER, 0x01, "", [destEndpoint: childEndpoint]) +} + +def childOff(String dni) { + def childEndpoint = getChildEndpoint(dni) + zigbee.command(zigbee.ONOFF_CLUSTER, 0x00, "", [destEndpoint: childEndpoint]) +} + +def ping() { + refresh() +} + +def refresh() { + def refreshCommands = zigbee.onOffRefresh() + def numberOfChildDevices = 8 + + for (def endpoint : 2..numberOfChildDevices) { + refreshCommands += zigbee.readAttribute(zigbee.ONOFF_CLUSTER, ONOFF_ATTRIBUTE, [destEndpoint: endpoint]) + } + for (def endpoint : 1..4) { + refreshCommands += zigbee.readAttribute(BINARY_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, [destEndpoint: endpoint]) + } + + refreshCommands += zigbee.readAttribute(ANALOG_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, [destEndpoint: 0x0001]); + refreshCommands += zigbee.readAttribute(ANALOG_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, [destEndpoint: 0x0002]); + log.debug "refreshCommands: $refreshCommands" + + return refreshCommands +} + +private void createChildDevices() { + def numberOfChildDevices = 8 + + for (def endpoint: 2..numberOfChildDevices) { + try { + if (endpoint == 2) { + addChildDevice("Smartenit", "IOT8-Z-child-analog-contact-switch", "${device.deviceNetworkId}:0${endpoint}", device.hubId, + [completedSetup: true, + label: "${device.displayName} ${endpoint}", + isComponent: false + ]) + } else if (endpoint >= 3 && endpoint <= 4) { + addChildDevice("Smartenit", "IOT8-Z-child-contact-switch", "${device.deviceNetworkId}:0${endpoint}", device.hubId, + [completedSetup: true, + label: "${device.displayName} ${endpoint}", + isComponent: false + ]) + } else if (endpoint >= 5 && endpoint <= 8) { + addChildDevice("smartthings", "Child Switch", "${device.deviceNetworkId}:0${endpoint}", device.hubId, + [completedSetup: true, + label: "${device.displayName} ${endpoint}", + isComponent: false + ]) + } + } catch (Exception e) { + log.debug "Exception creating child device: ${e}" + } + } +} + +def configure() { + log.debug "configure" + + configureHealthCheck() + def configurationCommands = zigbee.configureReporting(BINARY_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, 0x10, 10, 600, null) + for (def endpoint: 2..4) { + configurationCommands += zigbee.configureReporting(BINARY_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, 0x10, 10, 600, null, [destEndpoint: endpoint]) + } + + configurationCommands += zigbee.onOffConfig(0, 120) + for (def endpoint : 2..8) { + configurationCommands += zigbee.configureReporting(zigbee.ONOFF_CLUSTER, ONOFF_ATTRIBUTE, 0x10, 0, 120, null, [destEndpoint: endpoint]) + } + + configurationCommands += zigbee.configureReporting(ANALOG_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, 0x39, 10, 600, 0x3dcccccd) + configurationCommands += zigbee.configureReporting(ANALOG_INPUT_CLUSTER, PRESENT_VALUE_ATTRIBUTE, 0x39, 10, 600, 0x3dcccccd, [destEndpoint: 2]) + + configurationCommands << refresh() + log.debug "configurationCommands: $configurationCommands" + return configurationCommands +} + +private getChildEndpoint(String dni) { + dni.split(":")[-1] as Integer +} + +def configureHealthCheck() { + log.debug "configureHealthCheck" + Integer hcIntervalMinutes = 12 + def healthEvent = [name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]] + sendEvent(healthEvent) +} \ No newline at end of file From ea240df07f07e87dd3eba3fb2be1d29b0c671d7a Mon Sep 17 00:00:00 2001 From: natec007 Date: Fri, 16 Jul 2021 15:05:41 -0700 Subject: [PATCH 250/422] DevWs for Plaid Systems LLC containing containing Spruce Sensor (#70599) * DevWs for Plaid Systems LLC containing containing Spruce Sensor * Fix device join name Update magic numbers to use constants or predefined values --- .../spruce-sensor.src/spruce-sensor.groovy | 525 +++++++----------- 1 file changed, 189 insertions(+), 336 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy index d8ff0f971e9..6d20eb83694 100644 --- a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy +++ b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy @@ -1,7 +1,7 @@ /** - * Spruce Sensor -updated with SLP3 model number 3/2019 + * Spruce Sensor -updated for new Samsung App * - * Copyright 2014 Plaid Systems + * Copyright 2021 Plaid Systems * * 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: @@ -12,257 +12,164 @@ * 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. * - -------10/20/2015 Updates-------- - -Fix/add battery reporting interval to update - -remove polling and/or refresh - - -------5/2017 Updates-------- - -Add fingerprints for SLP - -add device health, check every 60mins + 2mins - - -------3/2019 Updates-------- - -Add fingerprints for SLP3 - -change device health from 62mins to 3 hours + + -------6/2021 Updates-------- + - Update for 2021 Samsung SmartThings App + */ - + +import groovy.json.JsonOutput +import physicalgraph.zigbee.zcl.DataType + +//dth version +def getVERSION() {"v1.0 6-2021"} +def getDEBUG() {true} +def getHC_INTERVAL_SECS() {3720} +def getMEASURED_VALUE_ATTRIBUTE() {0x0000} +def getCONFIGURE_REPORTING_RESPONSE_COMMAND() {0x07} + metadata { - definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems") { - - capability "Configuration" + definition (name: "Spruce Sensor", namespace: "plaidsystems", author: "Plaid Systems", mnmn: "SmartThingsCommunity", + mcdSync: true, vid: "4cff4731-67ce-310b-ada0-4d8e169a6df0") { + + capability "Sensor" + capability "Temperature Measurement" + capability "Relative Humidity Measurement" capability "Battery" - capability "Relative Humidity Measurement" - capability "Temperature Measurement" - capability "Sensor" - capability "Health Check" - //capability "Polling" - - attribute "maxHum", "string" - attribute "minHum", "string" - - - command "resetHumidity" - command "refresh" - - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", deviceJoinName: "Spruce Irrigation" //Spruce Sensor - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", deviceJoinName: "Spruce Irrigation" //Spruce Sensor - fingerprint profileId: "0104", inClusters: "0000,0001,0003,0402,0405", outClusters: "0003, 0019", manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP3", deviceJoinName: "Spruce Irrigation" //Spruce Sensor + capability "Health Check" + capability "Configuration" + capability "Refresh" + + attribute "reportingInterval", "NUMBER" + + //new release + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", zigbeeNodeType: "SLEEPY_END_DEVICE", deviceJoinName: "Spruce Irrigation" //Spruce Sensor + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", zigbeeNodeType: "SLEEPY_END_DEVICE", deviceJoinName: "Spruce Irrigation" //Spruce Sensor + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP3", zigbeeNodeType: "SLEEPY_END_DEVICE", deviceJoinName: "Spruce Irrigation" //Spruce Sensor } preferences { - input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "-100..100", displayDuringSetup: false - input "interval", "number", title: "Report Interval", description: "How often the device should report in minutes", range: "1..120", defaultValue: 10, displayDuringSetup: false - input "resetMinMax", "bool", title: "Reset Humidity min and max", required: false, displayDuringSetup: false - } - - tiles { - valueTile("temperature", "device.temperature", canChangeIcon: false, canChangeBackground: false) { - state "temperature", label:'${currentValue}°', - backgroundColors:[ - [value: 31, 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"] - ] - } - valueTile("humidity", "device.humidity", width: 2, height: 2, canChangeIcon: false, canChangeBackground: true) { - state "humidity", label:'${currentValue}%', unit:"", - backgroundColors:[ - [value: 0, color: "#635C0C"], - [value: 16, color: "#EBEB21"], - [value: 22, color: "#C7DE6A"], - [value: 42, color: "#9AD290"], - [value: 64, color: "#44B621"], - [value: 80, color: "#3D79D9"], - [value: 96, color: "#0A50C2"] - ], icon:"st.Weather.weather12" - } - - valueTile("maxHum", "device.maxHum", canChangeIcon: false, canChangeBackground: false) { - state "maxHum", label:'High ${currentValue}%', unit:"", - backgroundColors:[ - [value: 0, color: "#635C0C"], - [value: 16, color: "#EBEB21"], - [value: 22, color: "#C7DE6A"], - [value: 42, color: "#9AD290"], - [value: 64, color: "#44B621"], - [value: 80, color: "#3D79D9"], - [value: 96, color: "#0A50C2"] - ] - } - valueTile("minHum", "device.minHum", canChangeIcon: false, canChangeBackground: false) { - state "minHum", label:'Low ${currentValue}%', unit:"", - backgroundColors:[ - [value: 0, color: "#635C0C"], - [value: 16, color: "#EBEB21"], - [value: 22, color: "#C7DE6A"], - [value: 42, color: "#9AD290"], - [value: 64, color: "#44B621"], - [value: 80, color: "#3D79D9"], - [value: 96, color: "#0A50C2"] - ] - } - - valueTile("battery", "device.battery", decoration: "flat", canChangeIcon: false, canChangeBackground: false) { - state "battery", label:'${currentValue}% battery' - } - - main (["humidity"]) - details(["humidity","maxHum","minHum","temperature","battery"]) + input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph", title: "" + input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false + + input description: "Gen 1 & 2 Sensors only: Measurement Interval 1-120 minutes (default: 10 minutes)", displayDuringSetup: false, type: "paragraph", element: "paragraph", title: "" + input "interval", "number", title: "Measurement Interval", description: "Set how often you would like to check soil moisture in minutes", range: "1..120", defaultValue: 10, displayDuringSetup: false + + input title: "Version", description: VERSION, displayDuringSetup: true, type: "paragraph", element: "paragraph" } + } -def parse(String description) { - log.debug "Parse description $description config: ${device.latestValue('configuration')} interval: $interval" - - Map map = [:] - - if (description?.startsWith('catchall:')) { - map = parseCatchAllMessage(description) - } - else if (description?.startsWith('read attr -')) { +// Parse incoming device messages to generate events +def parse(description) { + + def map + if (description?.startsWith("read attr -")) { + log.debug "read attr - ${description}" map = parseReportAttributeMessage(description) - } - else if (description?.startsWith('temperature: ') || description?.startsWith('humidity: ')) { - map = parseCustomMessage(description) } - def result = map ? createEvent(map) : null - - //check in configuration change - if (!device.latestValue('configuration')) result = poll() - if (device.latestValue('configuration') as float != interval && interval != null) { - result = poll() - } - log.debug "result: $result" - return result - -} + else if (isSupportedDescription(description)) { + log.debug "supported description: $description" + map = parseSupportedMessage(description) + } + else if (description?.startsWith("catchall:")) { + log.debug "catchall ${description}" + map = parseCatchAllMessage(description) + } + else if (DEBUG) log.debug "uncaught ${description}" + def result = map ? createEvent(map) : null + //check for configuration change and send configuration change + if (map && map.name == "temperature" && isIntervalChange()) result = ping() + + if (DEBUG) log.debug "parse result: $result" + return result +} private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def linkText = getLinkText(device) - //log.debug "Catchall" - def descMap = zigbee.parse(description) - - //check humidity configuration is complete - if (descMap.command == 0x07 && descMap.clusterId == 0x0405){ - def configInterval = 10 - if (interval != null) configInterval = interval - sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration Successful") - //setConfig() - log.debug "config complete" - //return resultMap = [name: 'configuration', value: configInterval, descriptionText: "Settings configured successfully"] - } - else if (descMap.command == 0x0001){ - def hexString = "${hex(descMap.data[5])}" + "${hex(descMap.data[4])}" - def intString = Integer.parseInt(hexString, 16) - //log.debug "command: $descMap.command clusterid: $descMap.clusterId $hexString $intString" - - if (descMap.clusterId == 0x0402){ - def value = getTemperature(hexString) - resultMap = getTemperatureResult(value) - } - else if (descMap.clusterId == 0x0405){ - def value = Math.round(new BigDecimal(intString / 100)).toString() - resultMap = getHumidityResult(value) - - } - else return null - } - else return null - - return resultMap -} - -private Map parseReportAttributeMessage(String description) { - def descMap = parseDescriptionAsMap(description) - log.debug "Desc Map: $descMap" - log.debug "Report Attributes" - Map resultMap = [:] - if (descMap.cluster == "0001" && descMap.attrId == "0000") { - resultMap = getBatteryResult(descMap.value) - } - return resultMap + def map = zigbee.parseDescriptionAsMap(description) + + def command = zigbee.convertHexToInt(map.command) + def cluster = ( map.clusterId == null ? zigbee.convertHexToInt(map.cluster) : zigbee.convertHexToInt(map.clusterId) ) + def value = (map.value != null ? zigbee.convertHexToInt(map.value) : null) + + if (DEBUG) log.debug "command: ${command} cluster: ${cluster} value: ${value}" + + //check humidity configuration update is complete + if (command == CONFIGURE_REPORTING_RESPONSE_COMMAND && cluster == zigbee.RELATIVE_HUMIDITY_CLUSTER){ + sendEvent(name: "reportingInterval", value: getReportInterval(), descriptionText: "Configuration Successful") + sendEvent(name: "checkInterval", value: deviceWatchSeconds(), displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + log.debug "config complete ${getReportInterval()}" + } + + if (DEBUG) log.debug "no catchall found" + return null + } -def parseDescriptionAsMap(description) { - (description - "read attr - ").split(",").inject([:]) { map, param -> - def nameAndValue = param.split(":") - map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] +private Map parseReportAttributeMessage(String description) { + def map = zigbee.parseDescriptionAsMap(description) + + def cluster = ( map.cluster != null ? zigbee.convertHexToInt(map.cluster) : null ) + def attribute = ( map.attrId != null ? zigbee.convertHexToInt(map.attrId) : null ) + def value = ( map.value != null ? zigbee.convertHexToInt(map.value) : null ) + + if (cluster == zigbee.POWER_CONFIGURATION_CLUSTER && attribute == MEASURED_VALUE_ATTRIBUTE) { + return getBatteryResult(value) } + + if (DEBUG) log.debug "no read attr found" + return null } -private Map parseCustomMessage(String description) { - Map resultMap = [:] - - log.debug "parseCustom" - if (description?.startsWith('temperature: ')) { +private Map parseSupportedMessage(String description) { + + //temperature + if (description?.startsWith("temperature: ")) { def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale()) - resultMap = getTemperatureResult(value) + return getTemperatureResult(value) } - else if (description?.startsWith('humidity: ')) { + + //humidity + if (description?.startsWith("humidity: ")) { def pct = (description - "humidity: " - "%").trim() - if (pct.isNumber()) { - def value = Math.round(new BigDecimal(pct)).toString() - resultMap = getHumidityResult(value) - } else { - log.error "invalid humidity: ${pct}" - } + if (pct.isNumber()) { + def value = Math.round(new BigDecimal(pct)).toString() + return getHumidityResult(value) + } } - return resultMap -} - -private Map getHumidityResult(value) { - def linkText = getLinkText(device) - def maxHumValue = 0 - def minHumValue = 0 - if (device.currentValue("maxHum") != null) maxHumValue = device.currentValue("maxHum").toInteger() - if (device.currentValue("minHum") != null) minHumValue = device.currentValue("minHum").toInteger() - log.debug "Humidity max: ${maxHumValue} min: ${minHumValue}" - def compare = value.toInteger() - - if (compare > maxHumValue) { - sendEvent(name: 'maxHum', value: value, unit: '%', descriptionText: "${linkText} soil moisture high is ${value}%") - } - else if (((compare < minHumValue) || (minHumValue <= 2)) && (compare != 0)) { - sendEvent(name: 'minHum', value: value, unit: '%', descriptionText: "${linkText} soil moisture low is ${value}%") - } - - return [ - name: 'humidity', - value: value, - unit: '%', - descriptionText: "${linkText} soil moisture is ${value}%" - ] } +//----------------------event values-------------------------------// -def getTemperature(value) { - def celsius = (Integer.parseInt(value, 16).shortValue()/100) - //log.debug "Report Temp $value : $celsius C" - if(getTemperatureScale() == "C"){ - return celsius - } else { - return celsiusToFahrenheit(celsius) as Integer - } +private Map getHumidityResult(value) { + log.debug "Humidity: $value" + def linkText = getLinkText(device) + + return [ + name: "humidity", + value: value, + unit: "%", + descriptionText: "${linkText} soil moisture is ${value}%" + ] } private Map getTemperatureResult(value) { log.debug "Temperature: $value" def linkText = getLinkText(device) - + if (tempOffset) { - value = new BigDecimal((value as float) + (tempOffset as float)).setScale(1, BigDecimal.ROUND_HALF_UP) + def offset = tempOffset as int + def v = value as int + value = v + offset } def descriptionText = "${linkText} is ${value}°${temperatureScale}" + return [ - name: 'temperature', + name: "temperature", value: value, descriptionText: descriptionText, unit: temperatureScale @@ -270,148 +177,94 @@ private Map getTemperatureResult(value) { } private Map getBatteryResult(value) { - log.debug 'Battery' + log.debug "Battery: $value" def linkText = getLinkText(device) - - def result = [ - name: 'battery' - ] - - def min = 2500 - def percent = ((Integer.parseInt(value, 16) - min) / 5) + + def min = 2500 + def percent = (value - min) / 5 percent = Math.max(0, Math.min(percent, 100.0)) - result.value = Math.round(percent) - - def descriptionText - if (percent < 10) result.descriptionText = "${linkText} battery is getting low $percent %." - else result.descriptionText = "${linkText} battery is ${result.value}%" - - return result -} + value = Math.round(percent) -def resetHumidity(){ - def linkText = getLinkText(device) - def minHumValue = 0 - def maxHumValue = 0 - sendEvent(name: 'minHum', value: minHumValue, unit: '%', descriptionText: "${linkText} min soil moisture reset to ${minHumValue}%") - sendEvent(name: 'maxHum', value: maxHumValue, unit: '%', descriptionText: "${linkText} max soil moisture reset to ${maxHumValue}%") -} - -def setConfig(){ - def configInterval = 100 - if (interval != null) configInterval = interval - sendEvent(name: 'configuration',value: configInterval, descriptionText: "Configuration initialized") + def descriptionText = "${linkText} battery is ${value}%" + if (percent < 10) descriptionText = "${linkText} battery is getting low $percent %." + + return [ + name: "battery", + value: value, + descriptionText: descriptionText + ] } -def installed(){ + +//----------------------configuration-------------------------------// + +def installed() { //check every 62 minutes - sendEvent(name: "checkInterval", value: 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + sendEvent(name: "checkInterval", value: deviceWatchSeconds(), displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) } //when device preferences are changed -def updated(){ - log.debug "device updated" - if (!device.latestValue('configuration')) configure() - else{ - if (resetMinMax == true) resetHumidity() - if (device.latestValue('configuration') as float != interval && interval != null){ - sendEvent(name: 'configuration',value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${interval} mins") - } - } - //check every 62mins or interval + 120s - def reportingInterval = interval * 60 + 2 * 60 - if (reportingInterval < 3720) reportingInterval = 3720 - sendEvent(name: "checkInterval", value: reportingInterval, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) +def updated() { + if (DEBUG) log.debug "device updated" + + //set reportingInterval = 0 to trigger update + if (isIntervalChange()) sendEvent(name: "reportingInterval", value: 0, descriptionText: "Settings changed and will update at next report. Measure interval set to ${getReportInterval()} mins") } -//poll -def poll() { - log.debug "poll called" - List cmds = [] - if (!device.latestValue('configuration')) cmds += configure() - else if (device.latestValue('configuration').toInteger() != interval && interval != null) { - cmds += intervalUpdate() - } - //cmds += refresh() - log.debug "commands $cmds" - return cmds?.collect { new physicalgraph.device.HubAction(it) } +//has interval been updated +def isIntervalChange() { + if (DEBUG) log.debug "isIntervalChange ${getReportInterval()} ${device.latestValue("reportingInterval")}" + return (getReportInterval() != device.latestValue("reportingInterval")) } -//update intervals -def intervalUpdate(){ - log.debug "intervalUpdate" - def minReport = 10 - def maxReport = 610 - if (interval != null) { - minReport = interval - maxReport = interval * 61 - } - [ - "zcl global send-me-a-report 0x405 0x0000 0x21 $minReport $maxReport {6400}", "delay 500", - "send 0x${device.deviceNetworkId} 1 1", "delay 500", - "zcl global send-me-a-report 1 0x0000 0x21 0x0C 0 {0500}", "delay 500", - "send 0x${device.deviceNetworkId} 1 1", "delay 500", - ] +//settings default interval +def getReportInterval() { + return (interval != null ? interval : 10) } -def refresh() { - log.debug "refresh" - [ - "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 500", - "st rattr 0x${device.deviceNetworkId} 1 0x405 0", "delay 500", - "st rattr 0x${device.deviceNetworkId} 1 1 0" - ] +//Device-Watch every 62mins or settings interval + 120s +def deviceWatchSeconds() { + def intervalSeconds = getReportInterval() * 60 + 2 * 60 + if (intervalSeconds < HC_INTERVAL_SECS) intervalSeconds = HC_INTERVAL_SECS + return intervalSeconds +} + +//ping +def ping() { + if (DEBUG) log.debug "device health ping" + + List cmds = [] + if (isIntervalChange()) cmds = reporting() + else cmds = refresh() + + return cmds?.collect { new physicalgraph.device.HubAction(it) } } //configure def configure() { - //set minReport = measurement in minutes - def minReport = 10 - def maxReport = 610 - - //String zigbeeId = swapEndianHex(device.hub.zigbeeId) - //log.debug "zigbeeid ${device.zigbeeId} deviceId ${device.deviceNetworkId}" - if (!device.zigbeeId) sendEvent(name: 'configuration',value: 0, descriptionText: "Device Zigbee Id not found, remove and attempt to rejoin device") - else sendEvent(name: 'configuration',value: 100, descriptionText: "Configuration initialized") - //log.debug "Configuring Reporting and Bindings. min: $minReport max: $maxReport " - - [ - "zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500", - "zdo bind 0x${device.deviceNetworkId} 1 1 0x405 {${device.zigbeeId}} {}", "delay 500", - "zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}", "delay 1000", - - //temperature - "zcl global send-me-a-report 0x402 0x0000 0x29 1 0 {3200}", - "send 0x${device.deviceNetworkId} 1 1", "delay 500", - - //min = soil measure interval - "zcl global send-me-a-report 0x405 0x0000 0x21 $minReport $maxReport {6400}", - "send 0x${device.deviceNetworkId} 1 1", "delay 500", - - //min = battery measure interval 1 = 1 hour - "zcl global send-me-a-report 1 0x0000 0x21 0x0C 0 {0500}", - "send 0x${device.deviceNetworkId} 1 1", "delay 500" - ] + refresh() + return reporting() + refresh() } -private hex(value) { - new BigInteger(Math.round(value).toString()).toString(16) -} +//set reporting +def reporting() { + //set min/max report from interval setting + def minReport = getReportInterval() + def maxReport = getReportInterval() * 61 -private String swapEndianHex(String hex) { - reverseArray(hex.decodeHex()).encodeHex() -} + def reportingCmds = [] + reportingCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 1, 0, 0x01, [destEndpoint: 1]) + reportingCmds += zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, minReport, maxReport, 0x6400, [destEndpoint: 1]) + reportingCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 0x0C, 0, 0x0500, [destEndpoint: 1]) -private byte[] reverseArray(byte[] array) { - int i = 0; - int j = array.length - 1; - byte tmp; - while (j > i) { - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - j--; - i++; - } - return array + return reportingCmds } + +def refresh() { + log.debug "refresh" + def refreshCmds = [] + refreshCmds += zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, MEASURED_VALUE_ATTRIBUTE, [destEndpoint: 1]) + refreshCmds += zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, MEASURED_VALUE_ATTRIBUTE, [destEndpoint: 1]) + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, MEASURED_VALUE_ATTRIBUTE, [destEndpoint: 1]) + + return refreshCmds +} \ No newline at end of file From a51dfa880487e8913c19d9f2a51ae1843c6d08ad Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Sat, 17 Jul 2021 06:06:21 +0800 Subject: [PATCH 251/422] add eWeLink temp/humi Sensor (#70669) * add eWeLink temp/humi Sensor * Update smartsense-temp-humidity-sensor.groovy change deviceJoinName: "eWeLink Multipurpose Sensor" * Update smartsense-temp-humidity-sensor.groovy Co-authored-by: yu zhou <2217902261@qq.com> --- .../smartsense-temp-humidity-sensor.groovy | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 2cca97187ed..7e04310670c 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 @@ -34,6 +34,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0009, 0402", manufacturer: "HEIMAN", model: "HT-EM", deviceJoinName: "HEIMAN Multipurpose Sensor" //HEIMAN Temperature & Humidity Sensor fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0B05", manufacturer: "HEIMAN", model: "HT-EF-3.0", deviceJoinName: "HEIMAN Multipurpose Sensor" //HEIMAN Temperature & Humidity Sensor fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0003,0020,0402,0405", outClusters: "0003,000A,0019", manufacturer: "frient A/S", model :"HMSZB-110", deviceJoinName: "frient Multipurpose Sensor" // frient Humidity Sensor + + //eWeLink + fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0405", outClusters: "0003", manufacturer: "eWeLink", model: "TH01", deviceJoinName: "eWeLink Multipurpose Sensor" } simulator { @@ -174,6 +177,12 @@ def refresh() { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)+ zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)+ zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000) + } else if (isEWeLinkTh01()) { + 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(0x0405, 0x0000) + + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) } else { return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware @@ -202,6 +211,13 @@ def configure() { zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000, DataType.UINT16, 60, 600, 1*100) + zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 60, 600, 0xA) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020, DataType.UINT8, 30, 21600, 0x1) + } else if (isEWeLinkTh01()) { + 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(3600, 7200) + + zigbee.configureReporting(0x0405, 0x0000, DataType.UINT16, 3600, 7200, null) } else { return refresh() + zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0x104E]) + // New firmware @@ -214,3 +230,7 @@ def configure() { private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "frient A/S" } + +private Boolean isEWeLinkTh01() { + device.getDataValue("manufacturer") == "eWeLink" && device.getDataValue("model") == "TH01" +} From 3e4dc6d8116a28c9eecc3a1bd3c262c5d7d71b7f Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Sat, 17 Jul 2021 06:19:41 +0800 Subject: [PATCH 252/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug (#69769) * DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug * delete dummy code for pull request review. * 1.Uniform parameter organization; 2.restore codes about checkInterval except parameter offlinePingable. * restore offlinePingable: "1" except dimmer * remove code about lastCheckIn and ignored offlinePingable * delete dummy code as review requested * 1. Syntax format compliance adjustment 2. delete dummy code * Syntax format compliance adjustment * delete capability "Light" Co-authored-by: Winnie Wen --- .../min-smart-plug.src/min-smart-plug.groovy | 390 ++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy diff --git a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy new file mode 100644 index 00000000000..1c07abef3bd --- /dev/null +++ b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy @@ -0,0 +1,390 @@ +/** + * Min Smart Plug v1.0.4 + * + * Models: MINOSTON (MP21Z) + * + * Author: + * winnie (sky-nie) + * + * Documentation: + * + * Changelog: + * + * 1.0.4 (07/13/2021) + * - Syntax format compliance adjustment + * - delete dummy code + * + * 1.0.3 (07/12/2021) + * 1.0.2 (07/07/2021) + * - delete dummy code + * + * 1.0.1 (03/17/2021) + * - Simplify the code, delete dummy code + * + * 1.0.0 (03/11/2021) + * - Initial Release + * + * Reference: + * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/eva-logik-in-wall-smart-switch.src/eva-logik-in-wall-smart-switch.groovy + * + * 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: "Min Smart Plug", namespace: "sky-nie", author: "winnie", mnmn: "SmartThings", vid:"generic-switch", ocfDeviceType: "oic.d.smartplug") { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Configuration" + capability "Refresh" + capability "Health Check" + + attribute "firmwareVersion", "string" + attribute "syncStatus", "string" + + fingerprint mfr: "0312", prod: "C000", model: "C009", deviceJoinName: "Minoston Outlet" // old MP21Z + fingerprint mfr: "0312", prod: "FF00", model: "FF0C", deviceJoinName: "Minoston Outlet" //MP21Z Minoston Mini Smart Plug + } + + preferences { + configParams.each { + if (it.name) { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options:it.options + } + } + } + } +} + +def installed() { + logDebug "installed()..." + sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +private static def getCheckInterval() { + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + return (60 * 60 * 3) + (5 * 60) +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 5000)) { + state.lastUpdated = new Date().time + + logDebug "updated()..." + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + + runIn(5, executeConfigureCmds, [overwrite: true]) + } + return [] +} + +def configure() { + logDebug "configure()..." + + if (state.resyncAll == null) { + state.resyncAll = true + runIn(8, executeConfigureCmds, [overwrite: true]) + } else { + if (!pendingChanges) { + state.resyncAll = true + } + executeConfigureCmds() + } + return [] +} + +def executeConfigureCmds() { + runIn(6, refreshSyncStatus) + + def cmds = [] + + if (!device.currentValue("switch")) { + cmds << switchBinaryGetCmd() + } + + if (state.resyncAll || !device.currentValue("firmwareVersion")) { + cmds << secureCmd(zwave.versionV1.versionGet()) + } + + configParams.each { param -> + def storedVal = getParamStoredValue(param.num) + def paramVal = param.value + + if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { + logDebug "Changing ${param.name}(#${param.num}) from ${storedVal} to ${paramVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + + state.resyncAll = false + if (cmds) { + sendCommands(delayBetween(cmds, 500)) + } + return [] +} + +def ping() { + logDebug "ping()..." + + return [ switchBinaryGetCmd() ] +} + +def on() { + logDebug "on()..." + + return [ switchBinarySetCmd(0xFF) ] +} + +def off() { + logDebug "off()..." + + return [ switchBinarySetCmd(0x00) ] +} + +def refresh() { + logDebug "refresh()..." + + refreshSyncStatus() + + sendCommands([switchBinaryGetCmd()]) +} + +private sendCommands(cmds) { + if (cmds) { + def actions = [] + cmds.each { + actions << new physicalgraph.device.HubAction(it) + } + sendHubCommand(actions) + } + return [] +} + +private switchBinaryGetCmd() { + return secureCmd(zwave.switchBinaryV1.switchBinaryGet()) +} + +private switchBinarySetCmd(val) { + return secureCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: val)) +} + +private secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s") || ("0x98" in device?.rawDescription?.split(" "))) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + return cmd.format() + } +} + +def parse(String description) { + def result = [] + try { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result += zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + } catch (e) { + log.error "${e}" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + + def result = [] + if (encapsulatedCmd) { + result += zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + logTrace "${cmd}" + + sendEvent(name: "syncStatus", value: "Syncing...", displayed: false) + runIn(4, refreshSyncStatus) + + def param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + def val = cmd.scaledConfigurationValue + logDebug "${param.name}(#${param.num}) = ${val}" + state["configVal${param.num}"] = val + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logTrace "VersionReport: ${cmd}" + + def subVersion = String.format("%02d", cmd.applicationSubVersion) + def fullVersion = "${cmd.applicationVersion}.${subVersion}" + + sendEvent(name: "firmwareVersion", value: fullVersion) + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + logTrace "${cmd}" + sendSwitchEvents(cmd.value, "physical") + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + logTrace "${cmd}" + sendSwitchEvents(cmd.value, "digital") + return [] +} + +private sendSwitchEvents(rawVal, type) { + sendEvent(name: "switch", value: (rawVal == 0xFF) ? "on" : "off", displayed: true, type: type) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: $cmd" + return [] +} + +def refreshSyncStatus() { + def changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +private static getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x25: 1, // Switch Binary + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x27: 1, // Switch All + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x8E: 2, // Multi Channel Association + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 + ] +} + +private getPendingChanges() { + return configParams.count { "${it.value}" != "${getParamStoredValue(it.num)}" } +} + +private getParamStoredValue(paramNum) { + return safeToInt(state["configVal${paramNum}"] , null) +} + +private getConfigParams() { + return [ + ledModeParam, + autoOffIntervalParam, + autoOnIntervalParam, + powerFailureRecoveryParam + ] +} + +private getLedModeParam() { + return getParam(1, "LED Indicator Mode", 1, 0, ledModeOptions) +} + +private getAutoOffIntervalParam() { + return getParam(2, "Auto Turn-Off Timer(0, Disabled; 1--60480 minutes)", 4, 0, null, "0..60480") +} + +private getAutoOnIntervalParam() { + return getParam(4, "Auto Turn-On Timer(0, Disabled; 1--60480 minutes)", 4, 0, null, "0..60480") +} + +private getPowerFailureRecoveryParam() { + return getParam(6, "Power Failure Recovery", 1, 0, powerFailureRecoveryOptions) +} + +private getParam(num, name, size, defaultVal, options=null, range=null) { + def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + + def map = [num: num, name: name, size: size, value: val] + if (options) { + map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value + map.options = setDefaultOption(options, defaultVal) + } + if (range) { + map.range = range + } + + return map +} + +private static setDefaultOption(options, defaultVal) { + return options?.collectEntries { k, v -> + if ("${k}" == "${defaultVal}") { + v = "${v} [DEFAULT]" + } + ["$k": "$v"] + } +} + +private static getLedModeOptions() { + return [ + "0":"On When On", + "1":"Off When On", + "2":"Always Off" + ] +} + +private static getPowerFailureRecoveryOptions() { + return [ + "0":"Turn Off", + "1":"Turn On", + "2":"Restore Last State" + ] +} + +private static safeToInt(val, defaultVal=0) { + return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal +} + +private static isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +private logDebug(msg) { + log.debug "$msg" +} + +private logTrace(msg) { + log.trace "$msg" +} \ No newline at end of file From 915ce7c6da1d98c12601293481b787ef13417f64 Mon Sep 17 00:00:00 2001 From: greens Date: Thu, 22 Jul 2021 11:07:33 -0700 Subject: [PATCH 253/422] BUG-3164 Overlapping lock events Lock events for devices for which we delay the sending of lock operation reports that happen too close together will cause incorrect state to temporarily be displayed. Overwriting the scheduled function call responsible for these events should be enough to essentially drop the "bad" event on the floor so it never has a chance to overwrite good state. --- .../zigbee-lock-without-codes.groovy | 2 +- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 2 +- devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy index 1844303ae48..36626a03291 100644 --- a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy +++ b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy @@ -213,7 +213,7 @@ private def parseAttributeResponse(String description) { with less info will be marked as not displayed */ log.debug "Lock attribute report received: ${responseMap.value}. Delaying event." - runIn(1, "delayLockEvent", [data : [map : responseMap]]) + runIn(1, "delayLockEvent", [overwrite: true, forceForLocallyExecuting: true, data: [map: responseMap]]) return [:] } } else { diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index cb71cefc23f..45cf8378873 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -506,7 +506,7 @@ private def parseAttributeResponse(String description) { with less info will be marked as not displayed */ log.debug "Lock attribute report received: ${responseMap.value}. Delaying event." - runIn(1, "delayLockEvent", [data : [map : responseMap]]) + runIn(1, "delayLockEvent", [overwrite: true, forceForLocallyExecuting: true, data: [map: responseMap]]) return [:] } } else if (clusterInt == CLUSTER_DOORLOCK && attrInt == DOORLOCK_ATTR_MIN_PIN_LENGTH && descMap.value) { diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index 3a17e4763cb..4b654e85bbc 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -380,7 +380,7 @@ def zwaveEvent(DoorLockOperationReport cmd) { } if (generatesDoorLockOperationReportBeforeAlarmReport()) { // we're expecting lock events to come after notification events, but for specific yale locks they come out of order - runIn(3, "delayLockEvent", [data: [map: map]]) + runIn(3, "delayLockEvent", [overwrite: true, forceForLocallyExecuting: true, data: [map: map]]) return [:] } else { return result ? [createEvent(map), *result] : createEvent(map) From 8eabbad64ef7bcec1c2318d78ca74a6965df2eff Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Fri, 23 Jul 2021 22:30:51 +0200 Subject: [PATCH 254/422] [ICP-14234] Zigbee Window Shade - included lastLevel in methods scope as local variable (#69361) * Included lastLevel in methods scope as local variable * lastLevel -> priorLevel --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 560d31ad23c..6bb2b707b3a 100755 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -132,9 +132,10 @@ def getLastLevel() { } def levelEventHandler(currentLevel) { - log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}" + def priorLevel = lastLevel + log.debug "levelEventHandle - currentLevel: ${currentLevel} priorLevel: ${priorLevel}" - if ((lastLevel == "undefined" || currentLevel == lastLevel) && state.invalidSameLevelEvent) { //Ignore invalid reports + if ((priorLevel == "undefined" || currentLevel == priorLevel) && state.invalidSameLevelEvent) { //Ignore invalid reports log.debug "Ignore invalid reports" } else { state.invalidSameLevelEvent = true @@ -145,9 +146,9 @@ def levelEventHandler(currentLevel) { if (currentLevel == 0 || currentLevel == 100) { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } else { - if (lastLevel < currentLevel) { + if (priorLevel < currentLevel) { sendEvent([name:"windowShade", value: "opening"]) - } else if (lastLevel > currentLevel) { + } else if (priorLevel > currentLevel) { sendEvent([name:"windowShade", value: "closing"]) } runIn(1, "updateFinalState", [overwrite:true]) From 088f6c714c13e0bd5599b4527dc341c52b537946 Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Fri, 23 Jul 2021 16:24:56 -0700 Subject: [PATCH 255/422] DevWs for Smartenit, Inc containing containing Smartenit Zigbee MLC30 (#70915) * DevWs for Smartenit, Inc containing containing Smartenit Zigbee MLC30 * Removed events from commands, device name changed to Smartenit Switch, parse modifications * Parsing function return adjustments * Added null check for event --- ...tenit-metering-dual-load-controller.groovy | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy diff --git a/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy b/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy new file mode 100644 index 00000000000..b78e67d6099 --- /dev/null +++ b/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy @@ -0,0 +1,177 @@ +/** + * MLC30 + * + * Copyright 2021 Luis Contreras + * + * 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. + */ + + import groovy.transform.Field + import physicalgraph.zigbee.zcl.DataType + + @Field final CurrentLevel = 0x0000 + @Field final MoveToLevelWOnOff = 0x0004 + @Field final MeteringCurrentSummation = 0x0000 + @Field final MeteringInstantDemand = 0x0400 + @Field final EnergyDivisor = 100000 + @Field final CurrentDivisor = 100 + @Field final Current = 0x00f0 + @Field final Voltage = 0x00f1 + @Field final OnOff = 0x0000 + @Field final SmartenitMfrCode = 0x1075 + +metadata { + definition (name: "Smartenit Metering Dual Load Controller", namespace: "Smartenit", author: "Luis Contreras", mnmn: "SmartThingsCommunity", vid: "472dac67-bbdd-344e-944b-43abafeeb82b") { + capability "Actuator" + capability "Configuration" + capability "Refresh" + capability "Power Meter" + capability "Energy Meter" + capability "Health Check" + capability "Voltage Measurement" + capability "monthpublic25501.current" + capability "monthpublic25501.load1" + capability "monthpublic25501.load2" + capability "monthpublic25501.levelControl" + + fingerprint model: "ZBMLC30NC", manufacturer: "Smartenit, Inc", deviceJoinName: "Smartenit Switch" + fingerprint model: "ZBMLC30NO", manufacturer: "Smartenit, Inc", deviceJoinName: "Smartenit Switch" + } +} + +def getFPoint(String FPointHex){ + return (Float)Long.parseLong(FPointHex, 16) +} + +// Parse incoming device messages to generate events +def parse(String description) { + log.debug "Basic description: ${description}" + def event = zigbee.getEvent(description) + Map eventDescMap = zigbee.parseDescriptionAsMap(description) + + if (description?.startsWith("on/off")) { + def cmds = zigbee.readAttribute(zigbee.ONOFF_CLUSTER, OnOff) + zigbee.readAttribute(zigbee.ONOFF_CLUSTER, OnOff, [destEndpoint: 2]) + return cmds.collect { new physicalgraph.device.HubAction(it) } + } + + if (event && event.name != "switch") { + log.debug "Collecting event: ${event}, ${event.name}, ${event.value}" + if ((eventDescMap?.sourceEndpoint == "01") || (eventDescMap?.endpoint == "01")) { + if (event.name == "power") { + return createEvent(name: "power", value: (event.value/EnergyDivisor)) + } else { + return createEvent(event) + } + } else if ((eventDescMap?.sourceEndpoint == "03") || (eventDescMap?.endpoint == "03")) { + if (event.name == "level") { + log.debug "Creating level event" + return createEvent(name: "level", value: event.value) + } + } + } else { + def mapDescription = zigbee.parseDescriptionAsMap(description) + log.debug "mapDescription... : ${mapDescription}" + + if (mapDescription) { + if (mapDescription.clusterInt == zigbee.SIMPLE_METERING_CLUSTER) { + if (mapDescription.attrInt == MeteringCurrentSummation) { + return createEvent(name:"energy", value: getFPoint(mapDescription.value)/EnergyDivisor) + } else if (mapDescription.attrInt == MeteringInstantDemand) { + return createEvent(name:"power", value: getFPoint(mapDescription.value/EnergyDivisor)) + } else if (mapDescription.attrInt == Voltage) { + return createEvent(name:"voltage", value: getFPoint(mapDescription.value) / 100) + } else if (mapDescription.attrInt == Current) { + return createEvent(name:"current", value: getFPoint(mapDescription.value) / 100, unit: "A") + } + } else if (mapDescription.clusterInt == zigbee.ONOFF_CLUSTER) { + if (mapDescription.attrInt == OnOff) { + def nameVal = mapDescription.sourceEndpoint == "01" ? "loadone" : "loadtwo" + def status = mapDescription.value == "00" ? "off" : "on" + return createEvent(name:nameVal, value: status) + } else if (event) { + return createEvent(event) + } + } else if (mapDescription.clusterInt == zigbee.LEVEL_CONTROL_CLUSTER) { + if (mapDescription.attrInt == CurrentLevel) { + log.debug "Received response for level control: ${eventDescMap?.value}" + long convertedValue = Long.parseLong(eventDescMap?.value, 16) + def ceilingVal = Math.ceil((convertedValue * 100) / 255.0 ) + return createEvent(name: "level", value: ceilingVal) + } + } + } + } +} + +def setLevel(val) { + log.debug "Setting level to ${val}" + int newval = 0 + if (val != 0) { + newval = (255.0 / (100.0 / val)) + } + + zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, MoveToLevelWOnOff, + DataType.pack(newval, DataType.UINT8, 1) + DataType.pack(0xffff, DataType.UINT16, 1), [destEndpoint: 3]) +} + +def setLoadone(val) { + log.debug "toggling load one to: ${val}" + + if (val == "on") { + zigbee.on() + } else if (val == "off") { + zigbee.off() + } +} + +def setLoadtwo(val) { + log.debug "Setting load two to: ${val}" + + if (val == "on") { + zigbee.command(zigbee.ONOFF_CLUSTER, 0x01, "", [destEndpoint: 2]) + } else if (val == "off") { + zigbee.command(zigbee.ONOFF_CLUSTER, 0x00, "", [destEndpoint: 2]) + } +} + +def refresh() { + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation) + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringInstantDemand) + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, Voltage, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, Current, [mfgCode: SmartenitMfrCode]) + + zigbee.readAttribute(zigbee.ONOFF_CLUSTER, OnOff) + + zigbee.readAttribute(zigbee.ONOFF_CLUSTER, OnOff, [destEndpoint: 2]) + + zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, CurrentLevel, [destEndpoint: 3]) +} + +def configure() { + log.debug "in configure()" + configureHealthCheck() + return (zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation, 0x25, 0, 600, 50) + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, MeteringInstantDemand, 0x2a, 0, 600, 50) + + zigbee.configureReporting(zigbee.ONOFF_CLUSTER, OnOff, 0x10, 0, 120, null, [destEndpoint: 1]) + + zigbee.configureReporting(zigbee.ONOFF_CLUSTER, OnOff, 0x10, 0, 120, null, [destEndpoint: 2]) + + refresh() + ) +} + +def configureHealthCheck() { + Integer hcIntervalMinutes = 10 + sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) +} + +def updated() { + log.debug "in updated()" + configureHealthCheck() +} + +def ping() { + return zigbee.readAttribute(zigbee.ONOFF_CLUSTER, ONOFF_ATTRIBUTE) + zigbee.readAttribute(zigbee.ONOFF_CLUSTER, OnOff, [destEndpoint: 2]) +} \ No newline at end of file From b524112b6a4f937038a7e7c928b25436ccfd4fda Mon Sep 17 00:00:00 2001 From: shingchen <85251891+shingchen@users.noreply.github.com> Date: Tue, 27 Jul 2021 16:15:00 -0700 Subject: [PATCH 256/422] DevWs for Ecolink Intelligent Technology containing containing Ecolink Chime+Siren (#67570) * DevWs for Ecolink Intelligent Technology containing containing Ecolink Chime+Siren * * 1.0 (07/15/2021) * - Initial Release * * 1.0.1 (07/25/2021) * - Changes requested by ST --- .../ecolink-chime-siren.groovy | 693 ++++++++++++++++++ 1 file changed, 693 insertions(+) create mode 100644 devicetypes/krlaframboise/ecolink-chime-siren.src/ecolink-chime-siren.groovy diff --git a/devicetypes/krlaframboise/ecolink-chime-siren.src/ecolink-chime-siren.groovy b/devicetypes/krlaframboise/ecolink-chime-siren.src/ecolink-chime-siren.groovy new file mode 100644 index 00000000000..ea59cd15d3a --- /dev/null +++ b/devicetypes/krlaframboise/ecolink-chime-siren.src/ecolink-chime-siren.groovy @@ -0,0 +1,693 @@ +/* + * Ecolink Chime+Siren v1.0.1 + * + * Changelog: + * + * 1.0.1 (07/25/2021) + * - Changes requested by ST + * + * 1.0 (07/15/2021) + * - Initial Release + * + * + * Copyright 2021 Ecolink + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x20: 1, // Basic + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x71: 3, // Notification v4 + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x79: 1, // Sound Switch + 0x7A: 2, // FirmwareUpdateMd + 0x80: 1, // Battery + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x87: 3, // Indicator + 0x8E: 2, // Multi Channel Association + 0x9F: 1 // Security S2 +] + +@Field static List sounds = [ + [number:1, name:"1. One long beep"], + [number:2, name:"2. Two beeps"], + [number:3, name:"3. E1 Beep"], + [number:4, name:"4. Tinker"], + [number:5, name:"5. Droplet"], + [number:6, name:"6. Rain"], + [number:7, name:"7. Marimba"], + [number:8, name:"8. Water dew"], + [number:9, name:"9. Phone"], + [number:10, name:"10. Pong"], + [number:11, name:"11. Error Sound"], + [number:12, name:"12. Chirp"], + [number:13, name:"13. Alarm Siren", type:"siren"], + [number:14, name:"14. Exit Delay", type:"siren"], + [number:15, name:"15. Entry Delay", type:"siren"], + [number:16, name:"16. Smoke Alarm", type:"siren"], + [number:17, name:"17. CO Alarm", type:"siren"], + [number:18, name:"18. Armed Away"], + [number:19, name:"19. Armed Stay"], + [number:20, name:"20. Disarmed"], + [number:21, name:"21. Front Door"], + [number:22, name:"22. Side Door"], + [number:23, name:"23. Back Door"], + [number:24, name:"24. Garage Door"], + [number:25, name:"25. Alarm Siren 2", type:"siren"], + [number:26, name:"26. Alarm Siren 3", type:"siren"], + [number:27, name:"27. Traditional Marimba"], + [number:28, name:"28. Westminster Piano"], + [number:29, name:"29. Forest"], + [number:30, name:"30. Garden Strings"], + [number:126, name:"Entry/Exit Delay (15 Seconds)", indicatorID:0x16], + [number:127, name:"Entry/Exit Delay (30 Seconds)", indicatorID:0x26], + [number:128, name:"Entry/Exit Delay (45 Seconds)", indicatorID:0x36], + [number:129, name:"Entry/Exit Delay (255 Seconds)", indicatorID:0xF6] +] + +@Field static int powerManagement = 8 +@Field static int powerDisconnected = 2 +@Field static int powerReconnected = 3 +@Field static int batteryCharging = 12 +@Field static int batteryFullyCharged = 13 +@Field static int chargeBatterySoon = 14 +@Field static int chargeBatteryNow = 15 +@Field static int batteryStatusDischarging = 0 +@Field static int batteryStatusCharging = 1 +@Field static int batteryStatusMaintaining = 2 +@Field static String batteryCC = "80" +@Field static String soundSwitchCC = "79" +@Field static String soundSwitchConfigurationSet = "7905" +@Field static String soundSwitchConfigurationGet = "7906" +@Field static String soundSwitchConfigurationReport = "7907" +@Field static String soundSwitchTonePlaySet = "7908" +@Field static String soundSwitchTonePlayGet = "7909" +@Field static String soundSwitchTonePlayReport = "790A" + + +metadata { + definition ( + name: "Ecolink Chime+Siren", + namespace: "krlaframboise", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType: "x.com.st.d.siren", + mnmn: "SmartThingsCommunity", + vid: "02a8f57f-6c7b-37f9-86c8-4705bf4faa6f" + ) { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "platemusic11009.soundVolume" + capability "platemusic11009.ecoPlaySoundNumber" + capability "platemusic11009.ecoSirenSound" + capability "platemusic11009.sirenVolume" + capability "Alarm" + capability "platemusic11009.ecoChimeSound" + capability "platemusic11009.chimeVolume" + capability "Chime" + capability "Power Source" + capability "Battery" + capability "Refresh" + capability "Configuration" + capability "Health Check" + capability "platemusic11009.firmware" + + fingerprint mfr:"014A", prod:"0007", model: "3975", deviceJoinName:"Ecolink Chime+Siren" // zw:L type:0301 mfr:014A prod:0007 model:3975 ver:2.04 zwv:7.13 lib:03 cc:5E,85,59,80,70,5A,7A,87,72,8E,71,73,98,9F,79,6C,55,86 + } + + preferences { + [heartBeatParam, supervisionParam].each { param -> + if (param.options) { + input "configParam${param.num}", "enum", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input "configParam${param.num}", "number", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + + input "debugOutput", "enum", + title: "Enable Debug Logging?", + required: false, + displayDuringSetup: false, + defaultValue: 1, + options: [0:"No", 1:"Yes [DEFAULT]"] + } +} + +def installed() { + logDebug "installed()..." + + initialize() +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 2000)) { + state.lastUpdated = new Date().time + + logDebug "updated()..." + + initialize() + + runIn(2, executeConfigureCmds) + } +} + +void initialize() { + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((60 * 60) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + sendInitEvent("activeSoundNumber", 0) + sendInitEvent("soundVolume", 25, "%") + sendInitEvent("chimeSound", "1") + sendInitEvent("chimeVolume", 50, "%") + sendInitEvent("sirenSound", "13") + sendInitEvent("sirenVolume", 100, "%") + + state.debugLoggingEnabled = (safeToInt(settings?.debugOutput, 1) != 0) +} + +void sendInitEvent(String name, value, String unit="") { + if (device.currentValue(name) == null) { + sendEventIfNew(name, value, unit) + } +} + +def configure() { + logDebug "configure()..." + + executeConfigureCmds() + + runIn(15, refresh) +} + +void executeConfigureCmds() { + List cmds = [] + + if (!device.currentValue("battery")) { + cmds << batteryGetCmd() + } + + if (!device.currentValue("switch")) { + cmds << soundSwitchTonePlayGetCmd() + } + + configParams.each { param -> + if (param.value != null) { + Integer storedVal = getParamStoredValue(param.num) + if (storedVal != param.value) { + logDebug "Changing ${param.name}(#${param.num}) from ${storedVal} to ${param.value}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: param.value)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + } + sendCommands(cmds) +} + +def ping() { + logDebug "ping()..." + return [ batteryGetCmd() ] +} + +def setChimeVolume(chimeVolume) { + sendEventIfNew("chimeVolume", chimeVolume, "%") +} + +def setChimeSound(chimeSound) { + sendEventIfNew("chimeSound", chimeSound) +} + +def chime() { + logDebug "chime()..." + + int volume = safeToInt(device.currentValue("chimeVolume"), 50) + int sound = safeToInt(device.currentValue("chimeSound"), 1) + + state.pendingAction = "chime" + playSoundAtVolume(sound, volume) +} + +def setSoundVolume(soundVolume) { + sendEventIfNew("soundVolume", soundVolume, "%") +} + +def playSound(soundNumber) { + logDebug "playSound(${soundNumber})..." + + int volume = safeToInt(device.currentValue("soundVolume"), 25) + int sound = safeToInt(soundNumber, 1) + + state.pendingAction = soundNumber + playSoundAtVolume(sound, volume) +} + +def setSirenVolume(sirenVolume) { + sendEventIfNew("sirenVolume", sirenVolume, "%") +} + +def setSirenSound(sirenSound) { + sendEventIfNew("sirenSound", sirenSound) +} + +def both() { + siren() +} + +def strobe() { + siren() +} + +def siren() { + logDebug "siren()..." + + int volume = safeToInt(device.currentValue("sirenVolume"), 100) + int sound = safeToInt(device.currentValue("sirenSound"), 13) + + state.pendingAction = "siren" + playSoundAtVolume(sound, volume) +} + +void playSoundAtVolume(soundNumber, volume) { + logDebug "playSoundAtVolume(${soundNumber}, ${volume})..." + + Map sound = getSound(soundNumber) + state.lastSound = sound + + logDebug "Playing '${sound.name}' at ${volume}%..." + + List cmds = [ + soundSwitchConfigSetCmd(volume, 1) + ] + + if (sound.indicatorID) { + cmds << secureCmd(zwave.indicatorV1.indicatorSet(value: sound.indicatorID)) + } else { + cmds << soundSwitchTonePlaySetCmd(soundNumber) + } + + cmds << soundSwitchTonePlayGetCmd() + + sendCommands(cmds, 100) +} + +def on() { + chime() +} + +def off() { + logDebug "off()..." + return delayBetween([ + soundSwitchTonePlaySetCmd(0), + soundSwitchTonePlayGetCmd() + ]) +} + +def refresh() { + logDebug "refresh()..." + sendCommands([ + batteryGetCmd(), + secureCmd(zwave.versionV1.versionGet()), + soundSwitchTonePlayGetCmd() + ]) +} + +void sendCommands(List cmds, Integer delay=1000) { + if (cmds) { + def actions = [] + cmds.each { + actions << new physicalgraph.device.HubAction(it) + } + sendHubCommand(actions, delay) + } +} + +String batteryGetCmd() { + return secureCmd(zwave.batteryV1.batteryGet()) +} + +String secureCmd(cmd) { + if (isSecurityEnabled()) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +String soundSwitchConfigSetCmd(int volume, int tone) { + return soundSwitchCmd("${soundSwitchConfigurationSet}${intToHex(volume)}${intToHex(tone)}") +} + +String soundSwitchConfigGetCmd() { + return soundSwitchCmd(soundSwitchConfigurationGet) +} + +String soundSwitchTonePlaySetCmd(int tone) { + return soundSwitchCmd("${soundSwitchTonePlaySet}${intToHex(tone)}") +} + +String soundSwitchTonePlayGetCmd() { + return soundSwitchCmd(soundSwitchTonePlayGet) +} + +String soundSwitchCmd(cmd) { + if (isSecurityEnabled()) { + return "988100${cmd}" + } else { + return cmd + } +} + +boolean isSecurityEnabled() { + return zwaveInfo?.zw?.contains("s") +} + +def parse(String description) { + if ("${description}".contains("command: 9881, payload: 00 ${soundSwitchCC}") || "${description}".contains("command: ${soundSwitchCC}")) { + // SOUND SWITCH NOT SUPPORTED BY SMARTTHINGS + handleSoundSwitchEvent(description) + } else if ("${description}".contains("command: 9881, payload: 00 ${batteryCC}") || "${description}".contains("command: ${batteryCC}")) { + // BATTERY V2 NOT SUPPORTED BY SMARTTHINGS + handleBatteryReport(description) + } else { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + Map param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + Integer val = cmd.scaledConfigurationValue + logDebug "${param.name}(#${param.num}) = ${val}" + setParamStoredValue(param.num, val) + } + else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + sendEventIfNew("firmwareVersion", (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + sendEventIfNew("switch", (cmd.value ? "on" : "off")) +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: $cmd" +} + +void zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + if (cmd.notificationType == powerManagement) { + String powerSource = null + switch (cmd.event) { + case powerDisconnected: + logDebug "AC Mains Disconnected" + powerSource = "battery" + break + case powerReconnected: + logDebug "AC Mains Re-Connected" + powerSource = "mains" + break + case batteryCharging: + logDebug "Battery is charging" + break + case batteryFullyCharged: + logDebug "battery is fully charged" + break + case chargeBatterySoon: + logDebug "charge battery soon" + break + case chargeBatteryNow: + logDebug "charge battery now" + break + default: + logDebug "Unknown Power Management Event: ${cmd}" + } + if (powerSource) { + sendEventIfNew("powerSource", powerSource) + } + } else { + logDebug "Unknown notificationType: ${cmd}" + } +} + +void handleBatteryReport(String description) { + // BATTERY V2 NOT SUPPORTED BY SMARTTHINGS + + // The handler can't rely on the Power Management Notification Reports to determine the power source because of a firmware issue that occurs when the device is plugged back in after the battery gets low. + + Map cmd = parseCommand(description) + if (cmd?.payloadBytes?.size() >= 2) { + + int value = hexToInt(cmd.payloadBytes[0]) + + sendEvent(getEventMap("battery", (value == 0xFF ? 1 : value), "%")) + + try { + String powerSource = null + + int chargingStatus = Integer.parseInt(Integer.toBinaryString(hexToInt(cmd.payloadBytes[1])).padLeft(8, "0").substring(0, 2), 2) + + switch (chargingStatus) { + case batteryStatusDischarging: + powerSource = "battery" + break + case batteryStatusCharging: + powerSource = "mains" + break + case batteryStatusMaintaining: + powerSource = "mains" + break + } + + if (powerSource) { + sendEventIfNew("powerSource", powerSource) + } + } catch (ex) { + log.warn "Unable to parse battery charging status from ${description}" + } + } +} + +void handleSoundSwitchEvent(String description) { + // SOUND SWITCH CC NOT SUPPORTED BY SMARTTHINGS + Map cmd = parseCommand(description) + + switch (cmd?.command) { + case soundSwitchConfigurationReport: + handleSoundSwitchConfigurationReport(cmd.payloadBytes) + break + case soundSwitchTonePlayReport: + handleSoundSwitchTonePlayReport(cmd.payloadBytes) + break + default: + logDebug "Unknown Sound Switch Command: ${description}" + } +} + +void handleSoundSwitchConfigurationReport(List payloadBytes) { + if (payloadBytes?.size() == 2) { + int volume = hexToInt(payloadBytes[0]) + int tone = hexToInt(payloadBytes[1]) + logDebug "Tone: ${tone} - Volume: ${volume}" + } else { + log.warn "Sound Switch Configuration Report: Unexpected Payload '${payloadBytes}'" + } +} + +void handleSoundSwitchTonePlayReport(List payloadBytes) { + if (payloadBytes?.size() == 1) { + int soundNumber = hexToInt(payloadBytes[0]) + if (soundNumber) { + + Map sound = state.lastSound + if ((sound?.number != soundNumber) && !sound?.indicatorID) { + sound = getSound(soundNumber) + state.lastSound = sound + } else { + logDebug "Active Sound Number: ${soundNumber}" + } + + sendEventIfNew("switch", "on") + + switch (state.pendingAction) { + case "siren": + sendEventIfNew("alarm", "siren") + break + case "chime": + sendEventIfNew("chime", "chime") + break + default: + sendEvent(getEventMap("activeSoundNumber", soundNumber)) + } + state.pendingAction = null + } else { + if ("${state.pendingAction}".isNumber()) { + // Workaround for timeout error the mobile app throws when a user attempts to play an unsupported sound #. This workaround wouldn't be necessary if the device followed the z-wave specs and played the default sound. + log.warn "Sound #${state.pendingAction} Doesn't Exist" + sendEvent(getEventMap("activeSoundNumber", safeToInt(state.pendingAction))) + state.pendingAction = null + } + + sendEventIfNew("switch", "off") + sendEventIfNew("alarm", "off") + sendEventIfNew("chime", "off") + sendEventIfNew("activeSoundNumber", 0) + } + } else { + log.warn "Sound Switch Tone Play Report: Unexpected Payload '${payloadBytes}'" + } +} + +Map parseCommand(String description) { + Map cmd = description.split(", ").collectEntries { entry -> + def pair = entry.split(": ") + [(pair.first()): pair.last()] + } + + List payloadBytes = null + if (cmd?.payload) { + payloadBytes = cmd.payload.split(" ") + } + + cmd.payloadBytes = payloadBytes + return cmd +} + +Map getSound(int soundNumber) { + Map sound = sounds.find { it.number == soundNumber } + if (!sound) { + sound = [number: soundNumber, name:"${soundNumber}. Custom"] + } + return sound +} + +Integer getParamStoredValue(Integer paramNum) { + return safeToInt(state["configVal${paramNum}"] , null) +} + +void setParamStoredValue(Integer paramNum, Integer value) { + state["configVal${paramNum}"] = value +} + +List getConfigParams() { + return [ + heartBeatParam, + supervisionParam, + emergencySoundVolumeParam + ] +} + +Map getHeartBeatParam() { + return getParam(2, "Heartbeat Notification Timing (seconds)", 4, 3600, null, "120..86400") // seconds +} + +Map getSupervisionParam() { + return getParam(3, "Supervision Encapsulation", 1, 1, [0:"Disabled", 1:"Enabled [DEFAULT]"]) +} + +Map getEmergencySoundVolumeParam() { + return getParam(6, "Emergency Sound Volume Adjustable", 1, 1, [0:"Disabled", 1:"Enabled [DEFAULT]"]) +} + +Map getParam(Integer num, String name, Integer size, Integer defaultVal, Map options, range=null) { + Integer val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + + return [num: num, name: name, size: size, value: val, options: options, range: range, defaultVal: defaultVal] +} + +void sendEventIfNew(String name, value, String unit="") { + if (device.currentValue(name) != value) { + sendEvent(getEventMap(name, value, unit)) + } +} + +Map getEventMap(String name, value, String unit="") { + Map event = [ + name: name, + value: value, + displayed: true, + isStateChange: true, + descriptionText: "${name} is ${value}${unit}" + ] + if (unit) { + event.unit = unit + } + logDebug(event.descriptionText) + return event +} + +String intToHex(int value) { + return Integer.toHexString(value).padLeft(2, "0").toUpperCase() +} + +Integer hexToInt(String value) { + return Integer.parseInt(value, 16) +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +boolean isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file From d798a888ae58cef9b207ada2ec9452d52b5bc6c9 Mon Sep 17 00:00:00 2001 From: dbradmit Date: Thu, 29 Jul 2021 18:20:04 -0700 Subject: [PATCH 257/422] Setting ocfDeviceType to correct icon discrepancy. --- .../child-switch-health-power.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/child-switch-health-power.src/child-switch-health-power.groovy b/devicetypes/smartthings/child-switch-health-power.src/child-switch-health-power.groovy index 5da65abc46b..16d37fa0db3 100644 --- a/devicetypes/smartthings/child-switch-health-power.src/child-switch-health-power.groovy +++ b/devicetypes/smartthings/child-switch-health-power.src/child-switch-health-power.groovy @@ -12,7 +12,7 @@ * */ metadata { - definition(name: "Child Switch Health Power", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "generic-switch-power") { + definition(name: "Child Switch Health Power", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.smartplug", mnmn: "SmartThings", vid: "generic-switch-power") { capability "Switch" capability "Actuator" capability "Sensor" From 4ae07ba1c92a88a26c99604c4c4f868c2e27492d Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 3 Aug 2021 05:36:19 +0800 Subject: [PATCH 258/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer (#69768) * DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer * delete dummy code for pull request. * 1.Uniform parameter organization; 2.restore codes about checkInterval except parameter offlinePingable. * change code as reviewer request. * 1.Syntax format compliance adjustment 2.delete dummy code * Syntax format compliance adjustment * 1.Syntax format compliance adjustment 2.Adjust the preferences interface prompts for SmartTings App 3.Simplify the process of calling sendHubCommand * fix a bug about sendHubCommand * fix a bug about temperature report threshold sync. * Syntax format compliance adjustment * 1. remove code about "Temperature Measurement" for release's product. 2. change "auto off interval" and "auto on interval" 's range for release's product. * add a fingerprint for a new device Co-authored-by: Winnie Wen --- .../min-smart-plug-dimmer.groovy | 451 ++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy new file mode 100644 index 00000000000..f17e80c487b --- /dev/null +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -0,0 +1,451 @@ +/** + * Min Smart Plug Dimmer v1.1.9 + * + * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) + * + * Author: + * winnie (sky-nie) + * + * Documentation: + * + * Changelog: + * + * 1.1.9 (07/29/2021) + * - add a fingerprint for a new device + * + * 1.1.8 (07/22/2021) + * - remove code about "Temperature Measurement" as beta product. + * - change "auto off interval" and "auto on interval" 's range + * + * 1.1.7 (07/22/2021) + * - fix a bug about temperature report threshold sync. + * + * 1.1.6 (07/13/2021) + * - Syntax format compliance adjustment + * - Adjust the preferences interface prompts for SmartTings App + * - Simplify the process of calling sendHubCommand + * + * 1.1.5 (07/13/2021) + * - Syntax format compliance adjustment + * - delete dummy code + * + * 1.1.4 (07/12/2021) + * 1.1.3 (07/07/2021) + * - delete dummy code + * + * 1.1.2 (06/30/2021) + * - Add new product supported + * + * 1.1.1 (05/06/2021) + * - 1.Solve the problem that the temperature cannot be displayed normally + * - 2.Synchronize some of the latest processing methods, refer to Minoston Door/Window Sensor + * + * 1.0.1 (03/17/2021) + * - Simplify the code, delete dummy code + * + * 1.0.0 (03/11/2021) + * - Initial Release + * + * Reference: + * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/eva-logik-in-wall-smart-dimmer.src/eva-logik-in-wall-smart-dimmer.groovy + * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/aeotec-trisensor.src/aeotec-trisensor.groovy + * + * 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: "Min Smart Plug Dimmer", namespace: "sky-nie", author: "winnie", ocfDeviceType: "oic.d.smartplug") { + capability "Actuator" + capability "Switch" + capability "Switch Level" + capability "Configuration" + capability "Refresh" + + attribute "firmwareVersion", "string" + attribute "lastCheckIn", "string" + attribute "syncStatus", "string" + + fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Dimmer Switch" //MP21ZD + fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Dimmer Switch" //MP22ZD + fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "Minoston Dimmer Switch" //N4002 + } + + preferences { + configParams.each { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options + } + } + } +} + +def installed() { + logDebug "installed()..." + sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + state.refreshConfig = true +} + +private static def getCheckInterval() { + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + return (60 * 60 * 3) + (5 * 60) +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 5000)) { + state.lastUpdated = new Date().time + + logDebug "updated()..." + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + + runIn(5, executeConfigureCmds, [overwrite: true]) + } + + return [] +} + +def configure() { + logDebug "configure()..." + + if (state.resyncAll == null) { + state.resyncAll = true + runIn(8, executeConfigureCmds, [overwrite: true]) + } else { + if (!pendingChanges) { + state.resyncAll = true + } + executeConfigureCmds() + } + return [] +} + +def executeConfigureCmds() { + runIn(6, refreshSyncStatus) + + def cmds = [] + + configParams.each { param -> + def storedVal = getParamStoredValue(param.num) + def paramVal = param.value + if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + + state.resyncAll = false + if (cmds) { + sendHubCommand(cmds, 500) + } + return [] +} + +def parse(String description) { + def result = [] + try { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result += zwaveEvent(cmd) + } else { + logDebug "Unable to parse description: $description" + } + + sendEvent(name: "lastCheckIn", value: convertToLocalTimeString(new Date()), displayed: false) + } catch (e) { + log.error "$e" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapCmd = cmd.encapsulatedCommand(commandClassVersions) + + def result = [] + if (encapCmd) { + result += zwaveEvent(encapCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + logTrace "ConfigurationReport ${cmd}" + + sendEvent(name: "syncStatus", value: "Syncing...", displayed: false) + runIn(4, refreshSyncStatus) + + def param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + def val = cmd.scaledConfigurationValue + + logDebug "${param.name}(#${param.num}) = ${val}" + state["configParam${param.num}"] = val + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.configurationValue}" + } + return [] +} + +def refreshSyncStatus() { + def changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Ignored Command: $cmd" + return [] +} + +private secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s") || ("0x98" in device?.rawDescription?.split(" "))) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + log.error("caught exception", ex) + } +} + +private static getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x26: 3, // Switch Multilevel + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x71: 3, // Notification + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x8E: 2, // Multi Channel Association + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 + ] +} + +private getPendingChanges() { + return configParams.count { "${it.value}" != "${getParamStoredValue(it.num)}" } +} + +private getParamStoredValue(paramNum) { + return safeToInt(state["configParam${paramNum}"] , null) +} + +// Configuration Parameters +private getConfigParams() { + [ + ledModeParam, + autoOffIntervalParam, + autoOnIntervalParam, + nightLightParam, + powerFailureRecoveryParam, + pushDimmingDurationParam, + holdDimmingDurationParam, + minimumBrightnessParam, + maximumBrightnessParam + ] +} + +private getLedModeParam() { + return getParam(2, "LED Indicator Mode", 1, 0, ledModeOptions) +} + +private getAutoOffIntervalParam() { + return getParam(4, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") +} + +private getAutoOnIntervalParam() { + return getParam(6, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") +} + +private getNightLightParam() { + return getParam(7, "Night Light Settings(1 - 10:10% - 100%)", 1, 2, null, "1..10") +} + +private getPowerFailureRecoveryParam() { + return getParam(8, "Power Failure Recovery", 1, 2, powerFailureRecoveryOptions) +} + +private getPushDimmingDurationParam() { + return getParam(9, "Push Dimming Duration(0, Disabled; 1 - 10 Seconds)", 1, 2, null, "0..10") +} + +private getHoldDimmingDurationParam() { + return getParam(10, "Hold Dimming Duration(1 - 10 Seconds)", 1, 4, null, "1..10") +} + +private getMinimumBrightnessParam() { + return getParam(11, "Minimum Brightness(0, Disabled; 1 - 99:1% - 99%)", 1, 10, null,"0..99") +} + +private getMaximumBrightnessParam() { + return getParam(12, "Maximum Brightness(0, Disabled; 1 - 99:1% - 99%)", 1, 99, null,"0..99") +} + +private getParam(num, name, size, defaultVal, options=null, range=null) { + def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + + def map = [num: num, name: name, size: size, value: val] + if (options) { + map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value + map.options = setDefaultOption(options, defaultVal) + } + if (range) { + map.range = range + } + + return map +} + +private static setDefaultOption(options, defaultVal) { + return options?.collectEntries { k, v -> + if ("${k}" == "${defaultVal}") { + v = "${v} [DEFAULT]" + } + ["$k": "$v"] + } +} + +private static getLedModeOptions() { + return [ + "0":"Off When On", + "1":"On When On", + "2":"Always Off", + "3":"Always On" + ] +} + +private static getPowerFailureRecoveryOptions() { + return [ + "0":"Turn Off", + "1":"Turn On", + "2":"Restore Last State" + ] +} + +private static validateRange(val, defaultVal, lowVal, highVal) { + val = safeToInt(val, defaultVal) + if (val > highVal) { + return highVal + } else if (val < lowVal) { + return lowVal + } else { + return val + } +} + +private static safeToInt(val, defaultVal=0) { + return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal +} + +private convertToLocalTimeString(dt) { + def timeZoneId = location?.timeZone?.ID + if (timeZoneId) { + return dt.format("MM/dd/yyyy hh:mm:ss a", TimeZone.getTimeZone(timeZoneId)) + } else { + return "$dt" + } +} + +private static isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +private logDebug(msg) { + log.debug "$msg" +} + +private logTrace(msg) { + log.trace "$msg" +} + +def on() { + logDebug "on()..." + + return [ basicSetCmd(0xFF) ] +} + +def off() { + logDebug "off()..." + + return [ basicSetCmd(0x00) ] +} + +def setLevel(level) { + logDebug "setLevel($level)..." + return setLevel(level, 1) +} + +def setLevel(level, duration) { + logDebug "setLevel($level, $duration)..." + if (duration > 30) { + duration = 30 + } + return [ switchMultilevelSetCmd(level, duration) ] +} + +private basicSetCmd(val) { + return secureCmd(zwave.basicV1.basicSet(value: val)) +} + +private switchMultilevelSetCmd(level, duration) { + def levelVal = validateRange(level, 99, 0, 99) + + def durationVal = validateRange(duration, 1, 0, 100) + + return secureCmd(zwave.switchMultilevelV3.switchMultilevelSet(dimmingDuration: durationVal, value: levelVal)) +} + +def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logTrace "VersionReport: ${cmd}" + + def subVersion = String.format("%02d", cmd.applicationSubVersion) + def fullVersion = "${cmd.applicationVersion}.${subVersion}" + + sendEvent(name: "firmwareVersion", value:fullVersion, displayed: true, type: null) + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + logTrace "${cmd}" + sendSwitchEvents(cmd.value, "physical") + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { + logTrace "${cmd}" + sendSwitchEvents(cmd.value, "digital") + return [] +} + +private sendSwitchEvents(rawVal, type) { + def switchVal = rawVal ? "on" : "off" + + sendEvent(name: "switch", value:switchVal, displayed: true, type: type) + + if (rawVal) { + sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") + } +} \ No newline at end of file From 3ea1ece8666a20ccd167c2a319badb5bb65048d6 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 3 Aug 2021 14:31:23 +0800 Subject: [PATCH 259/422] DevWs for NIE-TECH CO., LTD. containing containing Evalogik Door/Window Sensor (#71706) * DevWs for NIE-TECH CO., LTD. containing containing Evalogik Door/Window Sensor * 1.Syntax format compliance adjustment 2.fixed a bug for order repeated * Syntax format compliance adjustment * Simplify the process of calling sendHubCommand * Syntax format compliance adjustment * 1.Syntax format compliance adjustment 2.delete dummy code * omitted all the parameters related to associations group * remove the genericHandler and executeCommandsLocally flags Co-authored-by: Winnie Wen --- .../evalogik-door-window-sensor.groovy | 585 ++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 devicetypes/sky-nie/evalogik-door-window-sensor.src/evalogik-door-window-sensor.groovy diff --git a/devicetypes/sky-nie/evalogik-door-window-sensor.src/evalogik-door-window-sensor.groovy b/devicetypes/sky-nie/evalogik-door-window-sensor.src/evalogik-door-window-sensor.groovy new file mode 100644 index 00000000000..5d9d5797f46 --- /dev/null +++ b/devicetypes/sky-nie/evalogik-door-window-sensor.src/evalogik-door-window-sensor.groovy @@ -0,0 +1,585 @@ +/** + * Evalogik Door/Window Sensor v1.0.5 + * + * Models: MSE30Z + * + * Author: + * winnie (sky-nie) + * + * Documentation: + * + * Changelog: + * + * 1.0.5 (07/28/2021) + * - omitted all the parameters related to associations group + * + * 1.0.4 (07/16/2021) + * - Syntax format compliance adjustment + * - fixed a bug for order repeated + * + * 1.0.3 (07/16/2021) + * - change lastBatteryReport to record the time of fresh battery + * - add lastBattery to record the battery value + * + * 1.0.2 (07/15/2021) + * - update ConfigParams as product designed + * - update DTH name as product designed + * + * 1.0.1 (07/13/2021) + * - Syntax format compliance adjustment + * - delete dummy code + * + * 1.0.0 (04/26/2021) + * - Initial Release + * + * Reference: + * https://community.smartthings.com/t/release-aeotec-trisensor/140556?u=krlaframboise + * + * 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. + * + */ +final int NOTIFICATION_TYPE_ACCESS_CONTROL = 0x06 +final int NOTIFICATION_TYPE_HOME_SECURITY = 0x07 + +final int NOTIFICATION_EVENT_DOOR_WINDOW_OPEN = 0x16 +final int NOTIFICATION_EVENT_DOOR_WINDOW_CLOSED = 0x17 + +final int NOTIFICATION_EVENT_STATE_IDLE = 0x00 +final int NOTIFICATION_EVENT_INSTRUSION_WITH_LOCATION = 0x01 +final int NOTIFICATION_EVENT_INSTRUSION = 0x02 +final int NOTIFICATION_EVENT_TEMPERING = 0x03 + +metadata { + definition(name: "Evalogik Door/Window Sensor", namespace: "sky-nie", author: "winnie", ocfDeviceType: "x.com.st.d.sensor.contact") { + capability "Sensor" + capability "Contact Sensor" + capability "Temperature Measurement" + capability "Relative Humidity Measurement" + capability "Battery" + capability "Configuration" + capability "Refresh" + capability "Health Check" + + attribute "lastCheckIn", "string" + attribute "pendingChanges", "string" + + fingerprint mfr: "0312", prod: "0713", model: "D100", deviceJoinName: "Minoston 3-in-1 Sensor"//MSE30Z + } + + preferences { + configParams.each { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options:it.options + } + } + } +} + +def installed() { + log.debug "installed()..." + state.refreshConfig = true + sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +private static def getCheckInterval() { + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + return (60 * 60 * 3) + (5 * 60) +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 5000)) { + state.lastUpdated = new Date().time + + log.trace "updated()" + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + + refreshPendingChanges() + + logForceWakeupMessage "Configuration changes will be sent to the device the next time it wakes up." + } +} + +def configure() { + log.trace "configure()" + + runIn(8, executeConfigure) +} + +def executeConfigure() { + def cmds = [ + sensorBinaryGetCmd(), + batteryGetCmd() + ] + + cmds += getConfigCmds() + sendHubCommand(cmds, 500) +} + +private getConfigCmds() { + def cmds = [] + configParams.each { param -> + def storedVal = getParamStoredValue(param.num) + if (state.refreshConfig) { + cmds << configGetCmd(param) + } else if ("${storedVal}" != "${param.value}") { + log.debug "Changing ${param.name}(#${param.num}) from ${storedVal} to ${param.value}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: param.value)) + cmds << configGetCmd(param) + + if (param.num == minTemperatureOffsetParam.num) { + cmds << "delay 3000" + cmds << sensorMultilevelGetCmd(tempSensorType) + } else if (param.num == minHumidityOffsetParam.num) { + cmds << "delay 3000" + cmds << sensorMultilevelGetCmd(lightSensorType) + } + } + } + state.refreshConfig = false + return cmds +} + +// Required for HealthCheck Capability, but doesn't actually do anything because this device sleeps. +def ping() { + log.debug "ping()" +} + +// Forces the configuration to be resent to the device the next time it wakes up. +def refresh() { + logForceWakeupMessage "The sensor data will be refreshed the next time the device wakes up." + state.lastBatteryReport = null + state.lastBattery = null + if (!state.refreshSensors) { + state.refreshSensors = true + } else { + state.refreshConfig = true + } + refreshPendingChanges() + return [] +} + +private logForceWakeupMessage(msg) { + log.debug "${msg} You can force the device to wake up immediately by holding the z-button for 2 seconds." +} + +def parse(String description) { + def result = [] + try { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result += zwaveEvent(cmd) + } else { + log.debug "Unable to parse description: $description" + } + + sendEvent(name: "lastCheckIn", value: convertToLocalTimeString(new Date()), displayed: false) + } catch (e) { + log.error "$e" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapCmd = cmd.encapsulatedCommand(commandClassVersions) + + def result = [] + if (encapCmd) { + result += zwaveEvent(encapCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) { + log.debug "Device Woke Up" + + def cmds = [] + if (state.refreshConfig || pendingChanges > 0) { + cmds += getConfigCmds() + } + + if (canReportBattery()) { + cmds << batteryGetCmd() + } + + if (state.refreshSensors) { + cmds += [ + sensorBinaryGetCmd(), + sensorMultilevelGetCmd(tempSensorType), + sensorMultilevelGetCmd(lightSensorType) + ] + state.refreshSensors = false + } + + if (cmds) { + cmds = delayBetween(cmds, 1000) + cmds << "delay 3000" + } + cmds << secureCmd(zwave.wakeUpV1.wakeUpNoMoreInformation()) + return response(cmds) +} + +def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + def val = (cmd.batteryLevel == 0xFF ? 1 : cmd.batteryLevel) + if (val > 100) { + val = 100 + } else if (val < 1) { + val = 1 + } + state.lastBatteryReport = new Date().time + state.lastBattery = val + log.debug "Battery ${val}%" + sendEvent(getEventMap("battery", val, null, null, "%")) + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + log.trace "SensorMultilevelReport: ${cmd}" + switch (cmd.sensorType) { + case tempSensorType: + def unit = cmd.scale ? "F" : "C" + def temp = convertTemperatureIfNeeded(cmd.scaledSensorValue, unit, cmd.precision) + sendEvent(getEventMap("temperature", temp, true, null, getTemperatureScale())) + break + case lightSensorType: + sendEvent(getEventMap( "humidity", cmd.scaledSensorValue, true, null, "%")) + break + default: + log.debug "Unknown Sensor Type: ${cmd.sensorType}" + } + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + log.trace "ConfigurationReport ${cmd}" + + runIn(4, refreshPendingChanges) + + def param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + def val = cmd.scaledConfigurationValue + + log.debug "${param.name}(#${param.num}) = ${val}" + state["configParam${param.num}"] = val + } else { + log.debug "Parameter #${cmd.parameterNumber} = ${cmd.configurationValue}" + } + return [] +} + +def refreshPendingChanges() { + sendEvent(name: "pendingChanges", value: "${pendingChanges} Pending Changes", displayed: false) +} + +def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + log.trace "NotificationReport: $cmd" + def result = [] + + if(cmd.notificationType == NOTIFICATION_TYPE_ACCESS_CONTROL){ + if(cmd.event == NOTIFICATION_EVENT_DOOR_WINDOW_OPEN){ + result << sensorValueEvent(1) + } else if(cmd.event == NOTIFICATION_EVENT_DOOR_WINDOW_CLOSED) { + result << sensorValueEvent(0) + } + } else if (cmd.notificationType == NOTIFICATION_TYPE_HOME_SECURITY) { + if (cmd.event == NOTIFICATION_EVENT_STATE_IDLE) { + result << createEvent(descriptionText: "$device.displayName covering was restored", isStateChange: true) + cmds = [zwave.batteryV1.batteryGet(), zwave.wakeUpV1.wakeUpNoMoreInformation()] + result << response(commands(cmds, 1000)) + } else if (cmd.event == NOTIFICATION_EVENT_INSTRUSION_WITH_LOCATION || cmd.event == NOTIFICATION_EVENT_INSTRUSION) { + result << sensorValueEvent(1) + } else if (cmd.event == NOTIFICATION_EVENT_TEMPERING) { + result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true) + } + } else if (cmd.notificationType) { + def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}" + result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false) + } else { + def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive" + result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false) + } + + result +} + +def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { + log.trace "SensorBinaryReport: $cmd" + def map = [:] + map.value = cmd.sensorValue ? "open" : "closed" + map.name = "contact" + if (map.value == "open") { + map.descriptionText = "${device.displayName} is open" + } else { + map.descriptionText = "${device.displayName} is closed" + } + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.indicatorv1.IndicatorReport cmd) { + log.trace "${cmd}" +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Ignored Command: $cmd" + return [] +} + +private getEventMap(name, value, displayed=null, desc=null, unit=null) { + def isStateChange = (device.currentValue(name) != value) + displayed = (displayed == null ? isStateChange : displayed) + def eventMap = [ + name: name, + value: value, + displayed: displayed, + isStateChange: isStateChange, + descriptionText: desc ?: "${device.displayName} ${name} is ${value}" + ] + + if (unit) { + eventMap.unit = unit + eventMap.descriptionText = "${eventMap.descriptionText}${unit}" + } + if (displayed) { + log.debug "${eventMap.descriptionText}" + } + return eventMap +} + +private batteryGetCmd() { + return secureCmd(zwave.batteryV1.batteryGet()) +} + +private sensorBinaryGetCmd() { + return secureCmd(zwave.sensorBinaryV2.sensorBinaryGet()) +} + +private sensorMultilevelGetCmd(sensorType) { + def scale = (sensorType == tempSensorType) ? 0 : 1 + return secureCmd(zwave.sensorMultilevelV5.sensorMultilevelGet(scale: scale, sensorType: sensorType)) +} + +private configGetCmd(param) { + return secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) +} + +private secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s") || ("0x98" in device?.rawDescription?.split(" "))) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + throw new RuntimeException(ex) + } +} + +private static getCommandClassVersions() { + [ + 0x30: 2, // SensorBinary + 0x31: 5, // SensorMultilevel + 0x55: 1, // TransportServices + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x71: 3, // Notification + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd + 0x80: 1, // Battery + 0x84: 1, // WakeUp + 0x85: 2, // Association + 0x86: 1, // Version + 0x8E: 2, // MultChannelAssociation + 0x87: 1, // Indicator + 0x9F: 1 // Security 2 + ] +} + +private canReportBattery() { + return state.refreshSensors || (!isDuplicateCommand(state.lastBatteryReport, (12 * 60 * 60 * 1000))) +} + +private getPendingChanges() { + return configParams.count { "${it.value}" != "${getParamStoredValue(it.num)}" } +} + +private getParamStoredValue(paramNum) { + return safeToInt(state["configParam${paramNum}"] , null) +} + +// Sensor Types +private static getTempSensorType() { return 1 } +private static getLightSensorType() { return 5 } + +// Configuration Parameters +private getConfigParams() { + [ + batteryReportThresholdParam, + lowBatteryAlarmReportParam, + sensorModeWhenClosedParam, + delayReportSecondsWhenClosedParam, + delayReportSecondsWhenOpenedParam, + minTemperatureOffsetParam, + minHumidityOffsetParam, + temperatureUpperWatermarkParam, + temperatureLowerWatermarkParam, + humidityUpperWatermarkParam, + humidityLowerWatermarkParam, + switchTemperatureUnitParam, + temperatureOffsetParam, + humidityOffsetParam, + associationGroupSettingParam + ] +} + +private getBatteryReportThresholdParam() { + return getParam(1, "Battery report threshold(1% - 20%)", 1, 10, null,"1..20") +} + +private getLowBatteryAlarmReportParam() { + return getParam(2, "Low battery alarm report(5% - 20%)", 1, 5, null, "5..20") +} + +private getSensorModeWhenClosedParam() { + return getParam(3, "State of the sensor when the magnet closes the reed", 1, 0, sensorModeWhenCloseOptions) +} + +private getDelayReportSecondsWhenClosedParam() { + return getParam(4, "Delay in seconds with ON command report(door closed)", 2, 0, null, "0..3600") +} + +private getDelayReportSecondsWhenOpenedParam() { + return getParam(5, "Delay in seconds with OFF command report(door open)", 2, 0, null, "0..3600") +} + +private getMinTemperatureOffsetParam() { + return getParam(6, "Minimum Temperature change to report(0.5℃/0.9°F - 5.0℃/9°F)", 1, 10, null, "5..50") +} + +private getMinHumidityOffsetParam() { + return getParam(7, "Minimum Humidity change to report(5% - 20%)", 1, 10, null, "5..20") +} + +private getTemperatureUpperWatermarkParam() { + return getParam(8, "Temperature Upper Watermark value(0,Disabled; 1℃/33.8°F-50℃/122.0°F)", 2, 0, null, "0..50") +} + +private getTemperatureLowerWatermarkParam() { + return getParam(10, "Temperature Lower Watermark value(0,Disabled; 1℃/33.8°F - 50℃/122.0°F)", 2, 0, null, "0..50") +} + +private getHumidityUpperWatermarkParam() { + return getParam(12, "Humidity Upper Watermark value(0,Disabled; 1% - 100%)", 1, 0, null, "0..100") +} + +private getHumidityLowerWatermarkParam() { + return getParam(14, "Humidity Lower Watermark value(0,Disabled; 1%-100%)", 1, 0, null, "0..100") +} + +private getSwitchTemperatureUnitParam() { + return getParam(16, "Switch the unit of Temperature report", 1, 1, switchTemperatureUnitOptions) +} + +private getTemperatureOffsetParam() { + return getParam(17, "Offset value for temperature(-10℃/14.0°F - 10℃/50.0°F)", 1, 0, null, "-100..100") +} + +private getHumidityOffsetParam() { + return getParam(18, "Offset value for humidity (-20% - 20%)", 1, 0, null, "-20..20") +} + +private getAssociationGroupSettingParam() { + return getParam(19, "Association Group 2 Setting", 1, 1, associationGroupSettingOptions) +} + +private getParam(num, name, size, defaultVal, options=null, range=null) { + def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + + def map = [num: num, name: name, size: size, value: val] + if (options) { + map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value + map.options = setDefaultOption(options, defaultVal) + } + if (range) { + map.range = range + } + + return map +} + +private static setDefaultOption(options, defaultVal) { + return options?.collectEntries { k, v -> + if ("${k}" == "${defaultVal}") { + v = "${v} [DEFAULT]" + } + ["$k": "$v"] + } +} + +// Setting Options +private static getSwitchTemperatureUnitOptions() { + return [ + "0":"Celsius", + "1":"Fahrenheit" + ] +} + +private static getAssociationGroupSettingOptions() { + return [ + "0":"Disable completely", + "1":"Send Basic SET 0xFF when Magnet is away,and send Basic SET 0x00 when Magnet is near.", + "2":"Send Basic SET 0x00 when Magnet is away,and send Basic SET 0xFF when Magnet is near", + "3":"Only send Basic SET 0xFF when Magnet is away", + "4":"Only send Basic SET 0x00 when Magnet is near", + "5":"Only send Basic SET 0x00 when Magnet is away", + "6":"Only send Basic SET 0xFF when Magnet is near" + ] +} + +private static getSensorModeWhenCloseOptions() { + return [ + "0":"door/window closed", + "1":"door/window opened" + ] +} + +def sensorValueEvent(value) { + if (value) { + createEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open") + } else { + createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed") + } +} + +private static safeToInt(val, defaultVal=0) { + return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal +} + +private convertToLocalTimeString(dt) { + def timeZoneId = location?.timeZone?.ID + if (timeZoneId) { + return dt.format("MM/dd/yyyy hh:mm:ss a", TimeZone.getTimeZone(timeZoneId)) + } else { + return "$dt" + } +} + +private static isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} \ No newline at end of file From 736f6e9e0dad5f5b5893f8b96abfe1d1a50f133f Mon Sep 17 00:00:00 2001 From: dparyani <37111386+dparyani@users.noreply.github.com> Date: Tue, 3 Aug 2021 08:34:19 +0200 Subject: [PATCH 260/422] Sensative's Strips Multi-Sensor Product's Device Handler. (#2898) * Sensative's Strips Multi-Sensor Product's Device Handler. * Changes based on review comments for Strips Guard-700. * Changes based on review comments for Strips Drip-700. * Revert "Sensative's Strips Multi-Sensor Product's Device Handler." This reverts commit 6f220a50c82b6c78201de3c23d1ee19b7ac52cf5. * Cosmetic changes as requested by SmartThings. * Remove updateLastCheckIn() and String convertToLocalTimeString(dt)functions based on review and Kevin's input Co-authored-by: Dhiraj Paryani --- .../sensative-strips-drip-700.groovy | 518 ++++++++++++++++++ .../sensative-strips-guard-700.groovy | 397 ++++++++++++++ 2 files changed, 915 insertions(+) create mode 100644 devicetypes/sensative/sensative-strips-drip-700.src/sensative-strips-drip-700.groovy create mode 100644 devicetypes/sensative/sensative-strips-guard-700.src/sensative-strips-guard-700.groovy diff --git a/devicetypes/sensative/sensative-strips-drip-700.src/sensative-strips-drip-700.groovy b/devicetypes/sensative/sensative-strips-drip-700.src/sensative-strips-drip-700.groovy new file mode 100644 index 00000000000..9e4a255e30b --- /dev/null +++ b/devicetypes/sensative/sensative-strips-drip-700.src/sensative-strips-drip-700.groovy @@ -0,0 +1,518 @@ +/* + * Sensative Strips Drip 700 v1.3 + * + * + * Changelog: + * + * 1.3 (26/07/2021) + * - Remove updateLastCheckIn() and String convertToLocalTimeString(dt)functions based on review and Kevin's input + * + * 1.2 (28/06/2021) + * - Requested Changes + * + * 1.1 (06/06/2021) + * - Requested Changes + * + * 1.0 (05/12/2021) + * - Initial Release + * + * + * Copyright 2021 Sensative + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x22: 1, // ApplicationStatus + 0x31: 5, // Sensor Multilevel (v7) + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 2, // Configuration + 0x71: 3, // Alarm v1 or Notification v4 + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd + 0x80: 1, // Battery + 0x84: 2, // WakeUp + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x87: 1, // Indicator + 0x8E: 2, // Multi Channel Association + 0x9F: 1 // Security 2 +] + +@Field static int wakeUpIntervalSeconds = 43200 +@Field static int tempSensorType = 1 +@Field static int leakageCalibrationParamNum = 23 +@Field static int heatAlarm = 4 +@Field static int heatAlarmHigh = 2 +@Field static int heatAlarmLow = 6 +@Field static int waterAlarm = 5 +@Field static int waterAlarmWet = 2 +@Field static int homeSecurity = 7 +@Field static int homeSecurityTamper = 11 + +metadata { + definition ( + name: "Sensative Strips Drip 700", + namespace: "Sensative", + author: "Kevin LaFramboise", + ocfDeviceType:"x.com.st.d.sensor.moisture", + vid: "480ed59e-91d4-3cfc-a077-b06151590ef0", + mnmn: "SmartThingsCommunity" + ) { + capability "Sensor" + capability "Water Sensor" + capability "Temperature Measurement" + capability "Tamper Alert" + capability "Battery" + capability "Configuration" + capability "Refresh" + capability "Health Check" + capability "platemusic11009.firmware" + capability "platemusic11009.temperatureAlarm" + + fingerprint mfr:"019A", prod:"0004", model:"000B", deviceJoinName: "Strips Drip 700" //Raw Description: zw:Ss2a type:2101 mfr:019A prod:0004 model:000B ver:8.1A zwv:7.13 lib:07 cc:5E,22,55,9F,6C sec:86,85,8E,59,72,31,5A,87,73,80,70,71,84,7A + } + + preferences { + configParams.each { param -> + if (param.options) { + input "configParam${param.num}", "enum", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + options: param.options + } else if (param.range) { + input "configParam${param.num}", "number", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + + input "debugLogging", "enum", + title: "Logging:", + required: false, + defaultValue: 1, + options: [0:"Disabled", 1:"Enabled [DEFAULT]"] + } +} + +def installed() { + logDebug "installed()..." + state.pendingRefresh = true + initialize() +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 1000)) { + state.lastUpdated = new Date().time + + logDebug "updated()..." + initialize() + + if (!getSettingValue(leakageCalibrationParamNum) && (state.leakageCalibrated != null)) { + // reset flag so that it performs calibration the next time it's set to true. + logDebug "Resetting leakage/moisture sensor calibration setting..." + state.leakageCalibrated = null + } + + if (pendingChanges) { + logForceWakeupMessage("The configuration changes will be sent to the device the next time it wakes up.") + } + } +} + +void initialize() { + state.debugLoggingEnabled = (safeToInt(settings?.debugOutput, 1) != 0) + + if (!device.currentValue("tamper")) { + sendEventIfNew("tamper", "clear") + } + + if (!device.currentValue("water")) { + sendEventIfNew("water", "dry") + } + + if (!device.currentValue("temperatureAlarm")) { + sendEventIfNew("temperatureAlarm", "normal") + } + + if (!device.currentValue("checkInterval")) { + sendEvent(name: "checkInterval", value: ((wakeUpIntervalSeconds * 2) + 300), displayed: falsle, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } +} + +def configure() { + logDebug "configure()..." + state.pendingRefresh = true + sendCommands(getConfigureCmds()) +} + +List getConfigureCmds() { + runIn(6, refreshSyncStatus) + + int changes = pendingChanges + if (changes) { + log.warn "Syncing ${changes} Change(s)" + } + + List cmds = [ ] + + if (state.pendingRefresh) { + cmds << batteryGetCmd() + cmds << secureCmd(zwave.versionV1.versionGet()) + cmds << secureCmd(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: tempSensorType)) + } + + if (state.pendingRefresh || (state.wakeUpInterval != wakeUpIntervalSeconds)) { + logDebug "Changing wake up interval to ${wakeUpIntervalSeconds} seconds" + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalSet(seconds:wakeUpIntervalSeconds, nodeid:zwaveHubNodeId)) + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + configParams.each { + Integer storedVal = getParamStoredValue(it.num) + Integer settingVal = getSettingValue(it.num) + + if (it.num != leakageCalibrationParamNum) { + if ((settingVal != null) && (settingVal != storedVal)) { + logDebug "CHANGING ${it.name}(#${it.num}) from ${storedVal} to ${settingVal}" + cmds << configSetCmd(it, settingVal) + cmds << configGetCmd(it) + } else if (state.pendingRefresh) { + cmds << configGetCmd(it) + } + } else { + if (settingVal && !state.leakageCalibrated) { + logDebug "Performing leakage/moisture sensor calibration..." + state.leakageCalibrated = false // Indicate that calibration has been started + cmds << configSetCmd(it, settingVal) + cmds << configGetCmd(it) + } + } + } + + state.pendingRefresh = false + return cmds +} + +// Required for HealthCheck Capability, but doesn't actually do anything because this device sleeps. +def ping() { + logDebug "ping()" +} + +def refresh() { + logDebug "refresh()..." + state.pendingRefresh = true + logForceWakeupMessage("The device will be refreshed the next time it wakes up.") +} + +void logForceWakeupMessage(String msg) { + log.warn "${msg} To force the device to wake up immediately, move the magnet towards the round end 3 times." +} + +String batteryGetCmd() { + return secureCmd(zwave.batteryV1.batteryGet()) +} + +String configSetCmd(Map param, int value) { + return secureCmd(zwave.configurationV2.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: value)) +} + +String configGetCmd(Map param) { + return secureCmd(zwave.configurationV2.configurationGet(parameterNumber: param.num)) +} + +String secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + return cmd.format() + } +} + +void sendCommands(List cmds, Integer delay=250) { + if (cmds) { + def actions = [] + cmds.each { + actions << new physicalgraph.device.HubAction(it) + } + sendHubCommand(actions, delay) + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + logDebug "Device Woke Up..." + List cmds = [] + + cmds += getConfigureCmds() + + if (cmds) { + cmds << "delay 1000" + } else { + cmds << batteryGetCmd() + } + + cmds << secureCmd(zwave.wakeUpV2.wakeUpNoMoreInformation()) + sendCommands(cmds) +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + logDebug "Wake Up Interval = ${cmd.seconds} seconds" + state.wakeUpInterval = cmd.seconds +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logDebug "${cmd}" + sendEventIfNew("firmwareVersion", (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + int val = (cmd.batteryLevel == 0xFF ? 1 : safeToInt(cmd.batteryLevel)) + if (val > 100) val = 100 + if (val < 1) val = 1 + + String desc = "${device.displayName}: battery is ${val}%" + logDebug(desc) + + sendEvent(name: "battery", value: val, unit: "%", isStateChange: true, descriptionText: desc) +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + runIn(4, refreshSyncStatus) + + Map param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + logDebug "${param.name}(#${param.num}) = ${cmd.scaledConfigurationValue}" + setParamStoredValue(param.num, cmd.scaledConfigurationValue) + + if ((param.num == leakageCalibrationParamNum) && (state.leakageCalibrated == false) && !cmd.scaledConfigurationValue) { + state.leakageCalibrated = true // calibration was started so indicate it completed to prevent it from being run again. + logDebug "Leakage/moisture sensor calibration finished..." + } + } else { + logDebug "Unknown Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + logDebug "${cmd}" + if (cmd.sensorType == tempSensorType) { + def unit = cmd.scale == 1 ? "F" : "C" + def temp = convertTemperatureIfNeeded(cmd.scaledSensorValue, unit, cmd.precision) + sendEventIfNew("temperature", temp, true, temperatureScale) + } +} + +void zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + logDebug "${cmd}" + switch (cmd.notificationType) { + case heatAlarm: + if ((cmd.event == heatAlarmHigh) || (cmd.eventParameter[0] == heatAlarmHigh)) { + sendEventIfNew("temperatureAlarm", ((cmd.event == heatAlarmHigh) ? "high" : "normal")) + } else if ((cmd.event == heatAlarmLow) || (cmd.eventParameter[0] == heatAlarmLow)) { + sendEventIfNew("temperatureAlarm", ((cmd.event == heatAlarmLow) ? "low" : "normal")) + } + break + case waterAlarm: + sendEventIfNew("water", ((cmd.event == waterAlarmWet) ? "wet" : "dry")) + break + case homeSecurity: + if ((cmd.event == homeSecurityTamper) || (cmd.eventParameter[0] == homeSecurityTamper)) { + sendEventIfNew("tamper", ((cmd.event == homeSecurityTamper) ? "detected" : "clear")) + } + break + } +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "${cmd}" +} + +void refreshSyncStatus() { + int changes = pendingChanges + sendEventIfNew("syncStatus", (changes ? "${changes} Pending Changes" : "Synced"), false) +} + +int getPendingChanges() { + int configChanges = safeToInt(configParams.count { + ((it.num != leakageCalibrationParam.num) && (getSettingValue(it.num) != null) && (getSettingValue(it.num) != getParamStoredValue(it.num))) + }, 0) + return (configChanges + ((state.wakeUpInterval != wakeUpIntervalSeconds) ? 1 : 0)) +} + +Integer getSettingValue(int paramNum) { + return safeToInt((settings ? settings["configParam${paramNum}"] : null), null) +} + +Integer getParamStoredValue(int paramNum) { + return safeToInt(state["configVal${paramNum}"], null) +} + +void setParamStoredValue(int paramNum, int value) { + state["configVal${paramNum}"] = value +} + +void sendEventIfNew(String name, value, boolean displayed=true, String unit="") { + String desc = "${device.displayName}: ${name} is ${value}${unit}" + if (device.currentValue(name) != value) { + if (name != "syncStatus") { + logDebug(desc) + } + + Map evt = [ + name: name, + value: value, + descriptionText: desc, + displayed: displayed + ] + + if (unit) { + evt.unit = unit + } + sendEvent(evt) + } +} + +List getConfigParams() { + return [ + ledAlarmParam, + tempReportingTypeParam, + tempAlarmsParam, + highTempAlarmLevelParam, + lowTempAlarmLevelParam, + leakageAlarmParam, + leakageAlarmLevelParam, + leakageAlarmIntervalParam, + activateSupervisionParam, + leakageCalibrationParam, + tempOffsetParam, + tempReportingIntervalParam, + tempDeltaParam, + tempHysteresisParam + ] +} + +Map getLedAlarmParam() { + return [num:2, name:"LED alarm event reporting", size:1, options:[0:"Off", 1:"On [DEFAULT]"]] +} + +Map getTempReportingTypeParam() { + return [num:4, name:"Temperature reporting type", size:1, options:[ + 0:"Off [DEFAULT]", + 1:"Actual value on Temperature Delta change", + 2:"Actual value at Temperature Reporting Interval", + 3:"Average value every 12 hours" + ]] +} + +Map getTempAlarmsParam() { + return [num:6, name:"Temperature alarms", size:1, options:[0:"Off [DEFAULT]", 1:"On"]] +} + +Map getHighTempAlarmLevelParam() { + return [num:7, name:"High temperature alarm level (°C)", size:1, defaultVal: 40, range:"-20..80"] +} + +Map getLowTempAlarmLevelParam() { + return [num:8, name:"Low temperature alarm level (°C)", size:1, defaultVal: 5, range:"-20..60"] +} + +Map getLeakageAlarmParam() { + return [num:12, name:"Leakage/moisture alarm", size:1, options:[0:"Off", 1:"On [DEFAULT]"]] +} + +Map getLeakageAlarmLevelParam() { + return [num:13, name:"Leakage/moisture alarm level (1:almost dry ~ 100:wet)", size:1, defaultVal: 10, range:"1..100"] +} + +Map getLeakageAlarmIntervalParam() { + return [num:14, name:"Leakage/moisture reporting period (hours)", size:1, defaultVal:0, range:"0..120"] +} + +Map getActivateSupervisionParam() { + return [num:15, name:"Activate Supervision", size:1, options:[0:"Off", 1:"Alarm Report [DEFAULT]", 2:"All Reports"]] +} + +Map getLeakageCalibrationParam() { + return [num:23, name:"Leakage/moisture sensor calibration", size:1, options:[0:"Off [DEFAULT]", 1:"Perform calibration"]] +} + +Map getTempOffsetParam() { + return [num:24, name:"Temperature offset (-10.0°C ~ +10.0°C)", size:1, defaultVal:0, range:"-100..100"] +} + +Map getTempReportingIntervalParam() { + return [num:25, name:"Temperature reporting period (minutes)", size:2, range:"15..1440", defaultVal:1440] +} + +Map getTempDeltaParam() { + return [num:26, name:"Temperature delta (0.5°C ~ 10°C)", size:1, defaultVal: 20, range:"5..100"] +} + +Map getTempHysteresisParam() { + return [num:27, name:"Temperature hysteresis for temperature alarms (0.5°C ~ 10°C)", size:1, defaultVal: 20, range:"5..100"] +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +boolean isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file diff --git a/devicetypes/sensative/sensative-strips-guard-700.src/sensative-strips-guard-700.groovy b/devicetypes/sensative/sensative-strips-guard-700.src/sensative-strips-guard-700.groovy new file mode 100644 index 00000000000..4b6c00148c9 --- /dev/null +++ b/devicetypes/sensative/sensative-strips-guard-700.src/sensative-strips-guard-700.groovy @@ -0,0 +1,397 @@ +/* + * Sensative Strips Guard 700 v1.2 + * + * + * Changelog: + * + * 1.3 (26/07/2021) + * - Remove updateLastCheckIn() and String convertToLocalTimeString(dt)functions based on review and Kevin's input + * + * 1.2 (28/06/2021) + * - Requested Changes + * + * 1.1 (06/06/2021) + * - Requested Changes + * + * 1.0 (05/12/2021) + * - Initial Release + * + * + * Copyright 2021 Sensative + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x22: 1, // ApplicationStatus + 0x30: 1, // SensorBinary + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 2, // Configuration + 0x71: 3, // Alarm v1 or Notification v4 + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd + 0x80: 1, // Battery + 0x84: 2, // WakeUp + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x87: 1, // Indicator + 0x8E: 2, // Multi Channel Association + 0x9F: 1 // Security 2 +] + +@Field static int accessControl = 6 +@Field static int accessControlOpen = 22 +@Field static int accessControlClosed = 23 +@Field static int homeSecurity = 7 +@Field static int homeSecurityOpen = 2 +@Field static int homeSecurityTamper = 11 +@Field static int wakeUpIntervalSeconds = 43200 + +metadata { + definition ( + name: "Sensative Strips Guard 700", + namespace: "Sensative", + author: "Kevin LaFramboise", + ocfDeviceType:"x.com.st.d.sensor.contact", + mnmn: "SmartThingsCommunity", + vid: "6d19b679-a36a-327f-809d-163f8b8d54d9" + ) { + capability "Sensor" + capability "Contact Sensor" + capability "Tamper Alert" + capability "Battery" + capability "Configuration" + capability "Refresh" + capability "Health Check" + capability "platemusic11009.firmware" + + fingerprint mfr:"019A", prod:"0004", model:"0004", deviceJoinName: "Strips Guard 700" //Raw Description: zw:Ss2a type:0701 mfr:019A prod:0004 model:0004 ver:8.1A zwv:7.13 lib:07 cc:5E,22,55,9F,6C sec:86,85,8E,59,72,30,5A,87,73,80,70,71,84,7A + } + + preferences { + configParams.each { param -> + input "configParam${param.num}", "enum", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + options: param.options + } + + input "debugLogging", "enum", + title: "Logging:", + required: false, + defaultValue: 1, + options: [0:"Disabled", 1:"Enabled [DEFAULT]"] + } +} + +def installed() { + logDebug "installed()..." + state.pendingRefresh = true + initialize() +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 1000)) { + state.lastUpdated = new Date().time + + logDebug "updated()..." + initialize() + + if (pendingChanges) { + logForceWakeupMessage("The configuration changes will be sent to the device the next time it wakes up.") + } + } +} + +void initialize() { + state.debugLoggingEnabled = (safeToInt(settings?.debugOutput, 1) != 0) + + if (!device.currentValue("tamper")) { + sendEventIfNew("tamper", "clear") + } + + if (!device.currentValue("contact")) { + sendEventIfNew("contact", "open") + } + + if (!device.currentValue("checkInterval")) { + sendEvent(name: "checkInterval", value: ((wakeUpIntervalSeconds * 2) + 300), displayed: falsle, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } +} + +def configure() { + logDebug "configure()..." + state.pendingRefresh = true + sendCommands(getConfigureCmds()) +} + +List getConfigureCmds() { + runIn(6, refreshSyncStatus) + + int changes = pendingChanges + if (changes) { + log.warn "Syncing ${changes} Change(s)" + } + + List cmds = [ ] + + if (state.pendingRefresh) { + cmds << batteryGetCmd() + cmds << secureCmd(zwave.versionV1.versionGet()) + cmds << secureCmd(zwave.sensorBinaryV1.sensorBinaryGet()) + } + + if (state.pendingRefresh || (state.wakeUpInterval != wakeUpIntervalSeconds)) { + logDebug "Changing wake up interval to ${wakeUpIntervalSeconds} seconds" + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalSet(seconds:wakeUpIntervalSeconds, nodeid:zwaveHubNodeId)) + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + configParams.each { + Integer storedVal = getParamStoredValue(it.num) + Integer settingVal = getSettingValue(it.num) + + if ((settingVal != null) && (settingVal != storedVal)) { + logDebug "CHANGING ${it.name}(#${it.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV2.configurationSet(parameterNumber: it.num, size: it.size, scaledConfigurationValue: settingVal)) + cmds << configGetCmd(it) + } else if (state.pendingRefresh) { + cmds << configGetCmd(it) + } + } + + state.pendingRefresh = false + return cmds +} + +// Required for HealthCheck Capability, but doesn't actually do anything because this device sleeps. +def ping() { + logDebug "ping()" +} + +def refresh() { + logDebug "refresh()..." + state.pendingRefresh = true + logForceWakeupMessage("The device will be refreshed the next time it wakes up.") +} + +void logForceWakeupMessage(String msg) { + log.warn "${msg} To force the device to wake up immediately, move the magnet towards the round end 3 times." +} + +String batteryGetCmd() { + return secureCmd(zwave.batteryV1.batteryGet()) +} + +String configGetCmd(Map param) { + return secureCmd(zwave.configurationV2.configurationGet(parameterNumber: param.num)) +} + +String secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + return cmd.format() + } +} + +void sendCommands(List cmds, Integer delay=100) { + if (cmds) { + def actions = [] + cmds.each { + actions << new physicalgraph.device.HubAction(it) + } + sendHubCommand(actions, delay) + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + logDebug "Device Woke Up..." + List cmds = [] + cmds += getConfigureCmds() + + if (cmds) { + cmds << "delay 500" + } else { + cmds << batteryGetCmd() + } + + cmds << secureCmd(zwave.wakeUpV2.wakeUpNoMoreInformation()) + sendCommands(cmds) +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + logDebug "Wake Up Interval = ${cmd.seconds} seconds" + state.wakeUpInterval = cmd.seconds +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logDebug "${cmd}" + sendEventIfNew("firmwareVersion", (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + int val = (cmd.batteryLevel == 0xFF ? 1 : safeToInt(cmd.batteryLevel)) + if (val > 100) val = 100 + if (val < 1) val = 1 + + String desc = "${device.displayName}: battery is ${val}%" + logDebug(desc) + + sendEvent(name: "battery", value: val, unit: "%", isStateChange: true, descriptionText: desc) +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + runIn(4, refreshSyncStatus) + + Map param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + logDebug "${param.name}(#${param.num}) = ${cmd.scaledConfigurationValue}" + setParamStoredValue(param.num, cmd.scaledConfigurationValue) + } else { + logDebug "Unknown Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) { + logDebug "${cmd}" + sendContactEvent(cmd.sensorValue) +} + +void zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + logDebug "${cmd}" + switch (cmd.notificationType) { + case accessControl: + if ((cmd.event == accessControlOpen) || (cmd.event == accessControlClosed)) { + sendContactEvent(cmd.event == accessControlOpen) + } + break + case homeSecurity: + if ((cmd.event == homeSecurityTamper) || (cmd.eventParameter[0] == homeSecurityTamper)) { + sendTamperEvent(cmd.event == homeSecurityTamper) + } else if ((cmd.event == homeSecurityOpen) || (cmd.eventParameter[0] == homeSecurityOpen)) { + sendContactEvent(cmd.event == homeSecurityOpen) + } + break + } +} + +void sendContactEvent(rawVal) { + sendEventIfNew("contact", (rawVal ? "open" : "closed")) +} + +void sendTamperEvent(rawVal) { + sendEventIfNew("tamper", (rawVal ? "detected" : "clear")) +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "${cmd}" +} + +void refreshSyncStatus() { + int changes = pendingChanges + sendEventIfNew("syncStatus", (changes ? "${changes} Pending Changes" : "Synced"), false) +} + +int getPendingChanges() { + return safeToInt(configParams.count { ((getSettingValue(it.num) != null) && (getSettingValue(it.num) != getParamStoredValue(it.num))) }) + ((state.wakeUpInterval != wakeUpIntervalSeconds) ? 1 : 0) +} + +Integer getSettingValue(int paramNum) { + return safeToInt((settings ? settings["configParam${paramNum}"] : null), null) +} + +Integer getParamStoredValue(int paramNum) { + return safeToInt(state["configVal${paramNum}"], null) +} + +void setParamStoredValue(int paramNum, int value) { + state["configVal${paramNum}"] = value +} + +void sendEventIfNew(String name, value, boolean displayed=true) { + String desc = "${device.displayName}: ${name} is ${value}" + if (device.currentValue(name) != value) { + if (name != "syncStatus") { + logDebug(desc) + } + sendEvent(name: name, value: value, descriptionText: desc, displayed: displayed) + } +} + +List getConfigParams() { + return [ + ledAlarmParam, + activateSupervisionParam + ] +} + +Map getLedAlarmParam() { + return [num: 2, name: "LED alarm event reporting", size: 1, options: [0: "Turns off LED for door open events", 1:"On [DEFAULT]"]] +} + +Map getActivateSupervisionParam() { + return [num:15, name:"Activate Supervision", size:1, options:[0:"Off", 1:"Alarm Report [DEFAULT]", 2:"All Reports"]] +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +boolean isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file From 77af4b9bb2fc6614ddaade317adb25c4eb778e25 Mon Sep 17 00:00:00 2001 From: natec007 Date: Tue, 17 Aug 2021 13:40:33 -0700 Subject: [PATCH 261/422] Update fingerprint (#73965) --- .../spruce-controller.src/spruce-controller.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy b/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy index c2a6159c6df..e013806a53a 100644 --- a/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy +++ b/devicetypes/plaidsystems/spruce-controller.src/spruce-controller.groovy @@ -11,6 +11,9 @@ * for the specific language governing permissions and limitations under the License. * +Version v3.8 + * remove zigbeeNodeType: "ROUTER" from fingerprint + Version v3.7 * update add zoneOn, zoneOff commands for external integration * move zone status update to parse @@ -64,7 +67,7 @@ import groovy.json.JsonOutput import physicalgraph.zigbee.zcl.DataType //dth version -def getVERSION() {'v3.7 6-2021'} +def getVERSION() {'v3.8 8-2021'} def getDEBUG() {false} def getHC_INTERVAL_MINS() {60} //zigbee cluster, attribute, identifiers @@ -105,7 +108,7 @@ metadata { command "settingsMap" //new release - fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01", zigbeeNodeType: "ROUTER", deviceJoinName: "Spruce Irrigation Controller" + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZ16-01", deviceJoinName: "Spruce Irrigation Controller" } preferences { From 41eaa7e48eb15ff0e55d988602ff0e4b83ffa0df Mon Sep 17 00:00:00 2001 From: natec007 Date: Tue, 17 Aug 2021 13:41:24 -0700 Subject: [PATCH 262/422] update fingerprint to correct join issue (#73948) --- .../plaidsystems/spruce-sensor.src/spruce-sensor.groovy | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy index 6d20eb83694..54ff1c8f713 100644 --- a/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy +++ b/devicetypes/plaidsystems/spruce-sensor.src/spruce-sensor.groovy @@ -15,6 +15,9 @@ -------6/2021 Updates-------- - Update for 2021 Samsung SmartThings App + + -------8/2021 Updates-------- + - remove zigbeeNodeType from fingerprints */ @@ -43,9 +46,9 @@ metadata { attribute "reportingInterval", "NUMBER" //new release - fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", zigbeeNodeType: "SLEEPY_END_DEVICE", deviceJoinName: "Spruce Irrigation" //Spruce Sensor - fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", zigbeeNodeType: "SLEEPY_END_DEVICE", deviceJoinName: "Spruce Irrigation" //Spruce Sensor - fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP3", zigbeeNodeType: "SLEEPY_END_DEVICE", deviceJoinName: "Spruce Irrigation" //Spruce Sensor + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-01", deviceJoinName: "Spruce Irrigation" //Spruce Sensor + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP1", deviceJoinName: "Spruce Irrigation" //Spruce Sensor + fingerprint manufacturer: "PLAID SYSTEMS", model: "PS-SPRZMS-SLP3", deviceJoinName: "Spruce Irrigation" //Spruce Sensor } preferences { From af08e5ad44a0d605339f6fcf8a2fef90e244f79f Mon Sep 17 00:00:00 2001 From: Eric Barnett Date: Thu, 19 Aug 2021 18:45:29 -0500 Subject: [PATCH 263/422] DevWs for HAB Home Intelligence containing containing Z-Wave Window Shade --- devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy b/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy index cfc352d7fdc..eecb8a466e7 100644 --- a/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy +++ b/devicetypes/iblinds/iblinds-zwave.src/iblinds-zwave.groovy @@ -28,6 +28,7 @@ metadata { fingerprint mfr:"0287", prod:"0003", model:"000D", deviceJoinName: "iBlinds Window Treatment" fingerprint mfr:"0287", prod:"0004", model:"0071", deviceJoinName: "iBlinds Window Treatment" + fingerprint mfr:"0287", prod:"0004", model:"0072", deviceJoinName: "iBlinds Window Treatment" } simulator { @@ -354,4 +355,4 @@ def getBattery() { def isV3Device() { zwaveInfo.mfr == "0287" && zwaveInfo.prod == "0004" && zwaveInfo.model == "0071" -} +} \ No newline at end of file From 278499267fcdfa41f3c58a6baa58e08d38cf4e07 Mon Sep 17 00:00:00 2001 From: dwd-kwon <59678391+dwd-kwon@users.noreply.github.com> Date: Mon, 23 Aug 2021 17:04:48 +0900 Subject: [PATCH 264/422] Update zigbee-metering-plug-power-consumption-report.groovy --- .../zigbee-metering-plug-power-consumption-report.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy index 7b95c8e18e6..1e147e010a9 100644 --- a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy @@ -81,7 +81,7 @@ def parse(String description) { map.value = zigbee.convertHexToInt(it.value)/getEnergyDiv() map.unit = "kWh" - def currentEnergy = map.value + def currentEnergy = zigbee.convertHexToInt(it.value) def currentPowerConsumption = device.currentState("powerConsumption")?.value Map previousMap = currentPowerConsumption ? new groovy.json.JsonSlurper().parseText(currentPowerConsumption) : [:] def deltaEnergy = calculateDelta (currentEnergy, previousMap) From ff0f4cee31a9f4ccbb2f6d8a974ae80aabfd663e Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Tue, 24 Aug 2021 08:11:06 +0200 Subject: [PATCH 265/422] [WWST-6928] Z-Wave Mold Detector DTH (#74134) * Z-Wave Mold detector handler * Removed wakeUpIntervalSet --- .../zwave-mold-detector.groovy | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 devicetypes/smartthings/zwave-mold-detector.src/zwave-mold-detector.groovy diff --git a/devicetypes/smartthings/zwave-mold-detector.src/zwave-mold-detector.groovy b/devicetypes/smartthings/zwave-mold-detector.src/zwave-mold-detector.groovy new file mode 100644 index 00000000000..d57ebe26150 --- /dev/null +++ b/devicetypes/smartthings/zwave-mold-detector.src/zwave-mold-detector.groovy @@ -0,0 +1,204 @@ +/** + * Copyright 2020 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. + * + * Generic Z-Wave Water/Temp/Humidity Sensor + * + * Author: SmartThings + * Date: 2020-07-22 + */ + +metadata { + definition(name: "Z-Wave Mold Detector", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "generic-mold", ocfDeviceType: "oic.d.thermostat") { + capability "Temperature Measurement" + capability "Relative Humidity Measurement" + capability "Dew Point" + capability "Mold Health Concern" + capability "Battery" + capability "Sensor" + capability "Health Check" + + // Aeotec Aerq Temperature and Humidity Sensor + fingerprint mfr:"0371", prod:"0002", model:"0009", deviceJoinName: "Aeotec Multipurpose Sensor", mnmn: "SmartThings", vid: "aeotec-temp-humidity" //EU + fingerprint mfr:"0371", prod:"0102", model:"0009", deviceJoinName: "Aeotec Multipurpose Sensor", mnmn: "SmartThings", vid: "aeotec-temp-humidity" //US + fingerprint mfr:"0371", prod:"0202", model:"0009", deviceJoinName: "Aeotec Multipurpose Sensor", mnmn: "SmartThings", vid: "aeotec-temp-humidity" //AU + // POPP Mold Detector + fingerprint mfr:"0154", prod:"0004", model:"0014", deviceJoinName: "POPP Multipurpose Sensor" //EU + } + + tiles(scale: 2) { + multiAttributeTile(name: "temperature", type: "generic", width: 6, height: 4, canChangeIcon: true) { + tileAttribute("device.temperature", key: "PRIMARY_CONTROL") { + attributeState "temperature", label: '${currentValue}°', + backgroundColors: [ + [value: 31, 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"] + ] + } + } + valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) { + state "humidity", label: '${currentValue}% humidity', unit: "" + } + valueTile("dewPoint", "device.dewPoint", inactiveLabel: false, width: 2, height: 2) { + state "dewPoint", label: '${currentValue}° dewPoint', unit: "" + } + valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "battery", label: '${currentValue}% battery', unit: "" + } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh" + } + + main "temperature", "humidity", "dewPoint" + details(["temperature", "humidity", "dewPoint", "battery"]) + } +} + +def installed() { + sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 10 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + // device doesn't send it on inclusion by itslef, so event is needed to populate plugin + sendEvent(name: "moldHealthConcern", value: "good", displayed: false) + + def cmds = [ + secure(zwave.batteryV1.batteryGet()), + secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x05)), // humidity + secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01)), // temperature + secure(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x0B)), // dew point + secure(zwave.wakeUpV2.wakeUpNoMoreInformation()) + ] + + response(cmds) +} + +def parse(String description) { + def results = [] + + if (description.startsWith("Err")) { + results += createEvent(descriptionText: description, displayed: true) + } else { + def cmd = zwave.parse(description) + if (cmd) { + results += zwaveEvent(cmd) + } + } + + log.debug "parse() result ${results.inspect()}" + + return results +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + log.debug "Wake Up Interval Report: ${cmd}" +} + +def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + log.debug "Event: ${cmd.event}, Notification type: ${cmd.notificationType}" + + def value + def description + + if (cmd.notificationType == 0x10) { // Mold Environment Detection + switch (cmd.event) { + case 0x00: + value = "good" + description = "Mold environment not detected" + break + case 0x02: + value = "unhealthy" + description = "Mold environment detected" + break + default: + log.warn "Not handled event type for Mold Environment Detection: ${cmd.event}" + return + } + + createEvent(name: "moldHealthConcern", value: value, descriptionText: description, isStateChange: true, displayed: true) + } +} + +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 + } + + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def map = [:] + + switch (cmd.sensorType) { + case 0x01: + map.name = "temperature" + map.unit = temperatureScale + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision) + map.displayed = true + map.isStateChange = true + break + case 0x05: + map.name = "humidity" + map.value = cmd.scaledSensorValue.toInteger() + map.unit = "%" + map.displayed = true + map.isStateChange = true + break + case 0x0B: + map.name = "dewpoint" + map.unit = temperatureScale + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision) + map.displayed = true + map.isStateChange = true + break + default: + map.descriptionText = cmd.toString() + } + + createEvent(map) +} + +def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + def cmds = [] + def result = createEvent(descriptionText: "$device.displayName woke up", isStateChange: false) + + if (!state.lastbatt || (now() - state.lastbatt) >= 10 * 60 * 60 * 1000) { + cmds += [ + "delay 1000", + secure(zwave.batteryV1.batteryGet()), + "delay 2000" + ] + } + cmds += secure(zwave.wakeUpV2.wakeUpNoMoreInformation()) + + [result, response(cmds)] +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + log.warn "Unhandled command: ${cmd}" +} + +private secure(cmd) { + if (zwaveInfo.zw.contains("s")) { + zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + cmd.format() + } +} From baedfcbfff71000ba1b56e6138d38c9a890204f5 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 24 Aug 2021 17:50:38 +0800 Subject: [PATCH 266/422] DevWs for NIE-TECH CO., LTD. containing Minoston Wallmote (#74138) * DevWs for NIE-TECH CO., LTD. containing containing Aeotec Wallmote * pull down the last version from public master branch,then add my change. * Syntax format compliance adjustment for PR review. * add "pushed_3x" to SupportedButtonValues, as product designed. * Syntax format compliance adjustment Co-authored-by: Winnie Wen --- .../aeotec-wallmote.src/aeotec-wallmote.groovy | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy b/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy index 61e3d0cfa70..f04b9d09bd3 100644 --- a/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy +++ b/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy @@ -27,6 +27,7 @@ metadata { fingerprint mfr: "0086", model: "0081", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Aeotec Wallmote fingerprint mfr: "0060", model: "0003", deviceJoinName: "Everspring Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Everspring Wall Switch fingerprint mfr: "0371", model: "0016", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Aeotec illumino Wallmote 7 + fingerprint mfr: "0312", model: "D001", deviceJoinName: "Minoston Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Minoston Wallmote } tiles(scale: 2) { @@ -45,7 +46,7 @@ metadata { } def getNumberOfButtons() { - def modelToButtons = ["0082" : 4, "0081": 2, "0003": 2, "0016": 2] + def modelToButtons = ["D001" : 4, "0082" : 4, "0081": 2, "0003": 2, "0016": 2] return modelToButtons[zwaveInfo.model] ?: 1 } @@ -172,6 +173,8 @@ def getChildDevice(button) { private getSupportedButtonValues() { if (isEverspring()) { return ["pushed", "held", "double"] + } else if (isMinoston()) { + return ["pushed", "held", "double", "pushed_3x"] } else if (isWallMote7()) { return ["pushed", "held", "double", "pushed_3x", "pushed_4x", "pushed_5x"] } else { @@ -184,6 +187,11 @@ private getButtonAttributesMap() { 0: "pushed", 2: "held", 3: "double" + ]} else if (isMinoston()) {[ + 0: "pushed", + 2: "held", + 3: "double", + 4: "pushed_3x" ]} else if (isWallMote7()) {[ 0: "pushed", 2: "held", @@ -201,6 +209,10 @@ private isEverspring() { zwaveInfo.model.equals("0003") } +private isMinoston() { + zwaveInfo.model.equals("D001") +} + private isWallMote7() { zwaveInfo.model.equals("0016") } \ No newline at end of file From b3aa851e2910825e88c12c1f3724ea827e3e4b78 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Wed, 25 Aug 2021 17:40:11 +0800 Subject: [PATCH 267/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug (#74612) * DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug * update base on the last publish version Co-authored-by: Winnie Wen --- .../sky-nie/min-smart-plug.src/min-smart-plug.groovy | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy index 1c07abef3bd..0d4a00d1dc5 100644 --- a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy +++ b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy @@ -55,6 +55,7 @@ metadata { fingerprint mfr: "0312", prod: "C000", model: "C009", deviceJoinName: "Minoston Outlet" // old MP21Z fingerprint mfr: "0312", prod: "FF00", model: "FF0C", deviceJoinName: "Minoston Outlet" //MP21Z Minoston Mini Smart Plug + fingerprint mfr: "0312", prod: "AC01", model: "4001", deviceJoinName: "New One Outlet" // N4001 New One Mini Smart Plug } preferences { @@ -63,7 +64,7 @@ metadata { if (it.range) { input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range } else { - input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options:it.options + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options } } } @@ -143,27 +144,22 @@ def executeConfigureCmds() { def ping() { logDebug "ping()..." - return [ switchBinaryGetCmd() ] } def on() { logDebug "on()..." - return [ switchBinarySetCmd(0xFF) ] } def off() { logDebug "off()..." - return [ switchBinarySetCmd(0x00) ] } def refresh() { logDebug "refresh()..." - refreshSyncStatus() - sendCommands([switchBinaryGetCmd()]) } @@ -322,11 +318,11 @@ private getLedModeParam() { } private getAutoOffIntervalParam() { - return getParam(2, "Auto Turn-Off Timer(0, Disabled; 1--60480 minutes)", 4, 0, null, "0..60480") + return getParam(2, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") } private getAutoOnIntervalParam() { - return getParam(4, "Auto Turn-On Timer(0, Disabled; 1--60480 minutes)", 4, 0, null, "0..60480") + return getParam(4, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") } private getPowerFailureRecoveryParam() { From 22e7cfbbad2dec1978f58c8567254bd5351bb220 Mon Sep 17 00:00:00 2001 From: Nixx-deyi <83563017+Nixx-deyi@users.noreply.github.com> Date: Wed, 25 Aug 2021 21:20:07 +0800 Subject: [PATCH 268/422] DevWs for Deyi smart home containing containing ZigBee Window Shade Battery (#71072) * DevWs for Deyi smart home containing containing ZigBee Window Shade Battery * Use the new version of the DTH * Remove extra spaces * Add one empty line between those two methods. * Fix spacing and add space before || * Add space before || --- .../zigbee-window-shade-battery.groovy | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index 174eceb9e44..a67d14754f7 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -36,6 +36,9 @@ metadata { // Yookee yooksmart fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Yookee", model: "D10110", deviceJoinName: "Yookee Window Treatment" fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "yooksmart", model: "D10110", deviceJoinName: "yooksmart Window Treatment" + + // SMARTWINGS + fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Smartwings", model: "WM25/L-Z", deviceJoinName: "Smartwings Window Treatment" } preferences { @@ -280,7 +283,7 @@ def configure() { } def usesLocalGroupBinding() { - isIkeaKadrilj() || isIkeaFyrtur() + isIkeaKadrilj() || isIkeaFyrtur() || isSmartwings() } private def parseBindingTableMessage(description) { @@ -311,15 +314,15 @@ private List readDeviceBindingTable() { } def supportsLiftPercentage() { - isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() + isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() || isSmartwings() } def shouldInvertLiftPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() + return isIkeaKadrilj() || isIkeaFyrtur() || isSmartwings() } def reportsBatteryPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() + return isIkeaKadrilj() || isIkeaFyrtur() || isSmartwings() } def isIkeaKadrilj() { @@ -333,3 +336,7 @@ def isIkeaFyrtur() { def isYooksmartOrYookee() { device.getDataValue("model") == "D10110" } + +def isSmartwings() { + device.getDataValue("model") == "WM25/L-Z" +} \ No newline at end of file From e12d3f07c8f65abafa4c2135e08cbef211f5349b Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Wed, 25 Aug 2021 23:54:48 +0800 Subject: [PATCH 269/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer (#74613) Co-authored-by: Winnie Wen --- .../min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index f17e80c487b..299436c6a13 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -78,7 +78,7 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Dimmer Switch" //MP21ZD fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Dimmer Switch" //MP22ZD - fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "Minoston Dimmer Switch" //N4002 + fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "New One Dimmer Switch" //N4002 } preferences { @@ -193,7 +193,6 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport def param = configParams.find { it.num == cmd.parameterNumber } if (param) { def val = cmd.scaledConfigurationValue - logDebug "${param.name}(#${param.num}) = ${val}" state["configParam${param.num}"] = val } else { From 53dd83f6adb0a0f3c76f5312ea69feab2c36bd9d Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Fri, 27 Aug 2021 11:55:11 +0800 Subject: [PATCH 270/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer --- .../min-smart-plug-dimmer.groovy | 245 +++++++++++++++--- 1 file changed, 210 insertions(+), 35 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 299436c6a13..89f80b3ed0b 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -1,5 +1,5 @@ /** - * Min Smart Plug Dimmer v1.1.9 + * Min Smart Plug Dimmer v2.0.1 * * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) * @@ -10,6 +10,13 @@ * * Changelog: * + * 2.0.1 (08/27/2021) + * - Syntax format compliance adjustment + * - fix some bugs + * + * 2.0.0 (07/30/2021) + * - add some fingerprint for new devices + * * 1.1.9 (07/29/2021) * - add a fingerprint for a new device * @@ -48,7 +55,6 @@ * * Reference: * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/eva-logik-in-wall-smart-dimmer.src/eva-logik-in-wall-smart-dimmer.groovy - * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/aeotec-trisensor.src/aeotec-trisensor.groovy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,6 +69,7 @@ * limitations under the License. * */ +import groovy.json.JsonOutput metadata { definition (name: "Min Smart Plug Dimmer", namespace: "sky-nie", author: "winnie", ocfDeviceType: "oic.d.smartplug") { @@ -71,6 +78,7 @@ metadata { capability "Switch Level" capability "Configuration" capability "Refresh" + capability "Health Check" attribute "firmwareVersion", "string" attribute "lastCheckIn", "string" @@ -79,21 +87,139 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Dimmer Switch" //MP21ZD fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Dimmer Switch" //MP22ZD fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "New One Dimmer Switch" //N4002 + fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS11ZS Minoston Smart Dimmer Switch + fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS13ZS Minoston Smart Toggle Dimmer Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB02", deviceJoinName: "Evalogik Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //ZW31S Evalogik Smart Dimmer Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB04", deviceJoinName: "Evalogik Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //ZW31TS Evalogik Smart Toggle Dimmer Switch } preferences { - configParams.each { - if (it.range) { - input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range - } else { - input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options - } + getConfigParamInput(ledModeParam) + getConfigParamInput(autoOffIntervalParam) + getConfigParamInput(autoOnIntervalParam) + getConfigParamInput(powerFailureRecoveryParam) + getConfigParamInput(pushDimmingDurationParam) + getConfigParamInput(holdDimmingDurationParam) + getConfigParamInput(minimumBrightnessParam) + input "disclaimer", "paragraph", + title: "WARNING", + description: "Configuring for 'Night Light Settings' is only valid for the devices with product number of MP21ZD、MP22ZD、N4002(one of them)", + required: false + getConfigParamInput(nightLightParam) + input "disclaimer", "paragraph", + title: "WARNING", + description: "Configuring for 'createButton'、'Maximum Brightness' and 'Paddle Control' are only valid for the devices with product number of MS11ZS、MS13ZS、ZW31S、ZW31TS(one of them)", + required: false + getConfigParamInput(maximumBrightnessParam) + getConfigParamInput(paddleControlParam) + input(type: "enum", name: "createButton", required: false, title: "Create Button for Paddles?", options: ["No", "Yes"], defaultValue:"Yes") + } +} + +private getConfigParamInput(param) { + if (param.range) { + input "configParam${param.num}", "number", title: "${param.name}:", required: false, defaultValue: "${param.value}", range: param.range + } else { + input "configParam${param.num}", "enum", title: "${param.name}:", required: false, defaultValue: "${param.value}", options: param.options + } +} + +private initialize() { + if (state.createButtonEnabled && !childDevices) { + try { + def child = addChildButton() + child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } catch (ex) { + log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) } + } else if (!state.createButtonEnabled && childDevices) { + removeChildButton(childDevices[0]) } } +private addChildButton() { + log.warn "Creating Button Device" + def child = addChildDevice( + "smartthings", + "Child Button", + "${device.deviceNetworkId}-2", + device.getHub().getId(), + [ + completedSetup: true, + isComponent: false, + label: "plugButton", + componentLabel: "${device.displayName[0..-8]} Button" + ] + ) + child?.sendEvent(name:"supportedButtonValues", value:JsonOutput.toJson(["pushed", "down", "down_2x", "up", "up_2x"]), displayed:false) + child?.sendEvent(name:"numberOfButtons", value:1, displayed:false) + sendButtonEvent("pushed") + return child +} + +private removeChildButton(child) { + try { + log.warn "Removing ${child.displayName}} " + deleteChildDevice(child.deviceNetworkId) + } catch (ex) { + log.error("Unable to remove ${child.displayName}! Make sure that the device is not being used by any SmartApps.",ex) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd){ + if (state.lastSequenceNumber != cmd.sequenceNumber) { + state.lastSequenceNumber = cmd.sequenceNumber + logTrace "${cmd}" + def paddle = (cmd.sceneNumber == 1) ? "down" : "up" + def btnVal + switch (cmd.keyAttributes){ + case 0: + btnVal = paddle + break + case 1: + logDebug "Button released not supported" + break + case 2: + logDebug "Button held not supported" + break + case 3: + btnVal = paddle + "_2x" + break + } + + if (btnVal) { + sendButtonEvent(btnVal) + } + } + return [] +} + +private sendButtonEvent(value) { + if (childDevices) { + childDevices[0].sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) + } +} + +def ping() { + logDebug "ping()..." + return [ switchMultilevelGetCmd() ] +} + +def refresh() { + logDebug "refresh()..." + refreshSyncStatus() + return [ switchMultilevelGetCmd() ] +} + +private switchMultilevelGetCmd() { + return secureCmd(zwave.switchMultilevelV3.switchMultilevelGet()) +} + def installed() { logDebug "installed()..." + if (isButtonAvailable()) { + state.createButtonEnabled = true + } sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) state.refreshConfig = true } @@ -113,6 +239,10 @@ def updated() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } + if (isButtonAvailable()) { + state.createButtonEnabled = (safeToInt(settings?.createButton) != 0) + initialize() + } runIn(5, executeConfigureCmds, [overwrite: true]) } @@ -142,6 +272,11 @@ def executeConfigureCmds() { configParams.each { param -> def storedVal = getParamStoredValue(param.num) def paramVal = param.value + if (isButtonAvailable()) { + if ((param == paddleControlParam) && state.createButtonEnabled && (param.value == 2)) { + log.warn "Only 'pushed', 'up_2x', and 'down_2x' button events are supported when Paddle Control is set to Toggle." + } + } if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) @@ -225,22 +360,24 @@ private secureCmd(cmd) { private static getCommandClassVersions() { [ - 0x20: 1, // Basic - 0x26: 3, // Switch Multilevel - 0x55: 1, // Transport Service - 0x59: 1, // AssociationGrpInfo - 0x5A: 1, // DeviceResetLocally - 0x71: 3, // Notification - 0x6C: 1, // Supervision - 0x70: 1, // Configuration - 0x7A: 2, // FirmwareUpdateMd - 0x72: 2, // ManufacturerSpecific - 0x73: 1, // Powerlevel - 0x85: 2, // Association - 0x86: 1, // Version (2) - 0x8E: 2, // Multi Channel Association - 0x98: 1, // Security S0 - 0x9F: 1 // Security S2 + 0x20: 1, // Basic + 0x26: 3, // Switch Multilevel + 0x5B: 1, // CentralScene (3) + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x71: 3, // Notification + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x8E: 2, // Multi Channel Association + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 ] } @@ -263,10 +400,23 @@ private getConfigParams() { pushDimmingDurationParam, holdDimmingDurationParam, minimumBrightnessParam, - maximumBrightnessParam + maximumBrightnessParam, + paddleControlParam ] } +private static getPaddleControlOptions() { + return [ + "0":"Normal", + "1":"Reverse", + "2":"Toggle" + ] +} + +private getPaddleControlParam() { + return getParam(1, "Paddle Control", 1, 0, paddleControlOptions) +} + private getLedModeParam() { return getParam(2, "LED Indicator Mode", 1, 0, ledModeOptions) } @@ -284,11 +434,13 @@ private getNightLightParam() { } private getPowerFailureRecoveryParam() { - return getParam(8, "Power Failure Recovery", 1, 2, powerFailureRecoveryOptions) + def defaultVal = isButtonAvailable()? 0:2 + return getParam(8, "Power Failure Recovery", 1, defaultVal, powerFailureRecoveryOptions) } private getPushDimmingDurationParam() { - return getParam(9, "Push Dimming Duration(0, Disabled; 1 - 10 Seconds)", 1, 2, null, "0..10") + def defaultVal = isButtonAvailable()? 1:2 + return getParam(9, "Push Dimming Duration(0, Disabled; 1 - 10 Seconds)", 1, defaultVal, null, "0..10") } private getHoldDimmingDurationParam() { @@ -382,13 +534,11 @@ private logTrace(msg) { def on() { logDebug "on()..." - return [ basicSetCmd(0xFF) ] } def off() { logDebug "off()..." - return [ basicSetCmd(0x00) ] } @@ -411,18 +561,14 @@ private basicSetCmd(val) { private switchMultilevelSetCmd(level, duration) { def levelVal = validateRange(level, 99, 0, 99) - def durationVal = validateRange(duration, 1, 0, 100) - return secureCmd(zwave.switchMultilevelV3.switchMultilevelSet(dimmingDuration: durationVal, value: levelVal)) } def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { logTrace "VersionReport: ${cmd}" - def subVersion = String.format("%02d", cmd.applicationSubVersion) def fullVersion = "${cmd.applicationVersion}.${subVersion}" - sendEvent(name: "firmwareVersion", value:fullVersion, displayed: true, type: null) return [] } @@ -441,10 +587,39 @@ def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelR private sendSwitchEvents(rawVal, type) { def switchVal = rawVal ? "on" : "off" - sendEvent(name: "switch", value:switchVal, displayed: true, type: type) - if (rawVal) { sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") } + if(isButtonAvailable()) { + def paddlesReversed = (paddleControlParam.value == 1) + if (state.createButtonEnabled && (type == "physical") && childDevices) { + if (paddleControlParam.value == 2) { + sendButtonEvent("pushed") + } else { + def btnVal = ((rawVal && !paddlesReversed) || (!rawVal && paddlesReversed)) ? "up" : "down" + def oldSwitch = device.currentValue("switch") + def oldLevel = device.currentValue("level") + if ((oldSwitch == "on") && (btnVal == "up") && (oldLevel > rawVal)) { + btnVal = "down" + } + sendButtonEvent(btnVal) + } + } + } +} + +private isButtonAvailable() { + if(device == null){ + log.error "isButtonAvailable device = null" + return true + }else{ + log.debug "isButtonAvailable device.rawDescription = ${device.rawDescription}" + def v20 = "${device.rawDescription}".contains("model:EE02") + def v21 = "${device.rawDescription}".contains("model:EE04") + def v22 = "${device.rawDescription}".contains("model:BB02") + def v23 = "${device.rawDescription}".contains("model:BB04") + def v2 = v20||v21||v22||v23 + return v2 + } } \ No newline at end of file From acd639ec8da1ca6f1266b4356ea2dd14ef9dd985 Mon Sep 17 00:00:00 2001 From: "SmartThings, Inc" Date: Mon, 30 Aug 2021 15:32:21 -0700 Subject: [PATCH 271/422] DevWs for Sinope Technologies containing containing VA4200WZ-VA4200ZB Sinope Valve (#73935) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aldéric Bourdeau-Guilbault --- .../va4200wz-va4200zb-sinope-valve.groovy | 322 ++++++++++-------- 1 file changed, 172 insertions(+), 150 deletions(-) diff --git a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy index 1cc0172b0e3..160c2a03334 100644 --- a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy +++ b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy @@ -1,6 +1,6 @@ /** Copyright Sinopé Technologies -1.3.0 +1.3.2 SVN-571 * 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. @@ -11,88 +11,88 @@ import physicalgraph.zigbee.zcl.DataType metadata { preferences { - input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") + input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") // input("logFilter", "number", title: "Trace level", range: "1..5", // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") - } - - definition (name: "VA4200WZ-VA4200ZB Sinope Valve", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.watervalve") { - capability "Configuration" - capability "Refresh" - capability "Actuator" - capability "Valve" - capability "Battery" - capability "Power Source" - capability "Health Check" - - fingerprint manufacturer: "Sinope Technologies", model: "VA4200WZ", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4200WZ - fingerprint manufacturer: "Sinope Technologies", model: "VA4200ZB", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4200ZB - fingerprint manufacturer: "Sinope Technologies", model: "VA4220ZB", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4220ZB - } - - 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", nextState:"closing" - attributeState "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" - } - tileAttribute ("powerSource", key: "SECONDARY_CONTROL") { - attributeState "powerSource", label:'Power Source: ${currentValue}' - } - } - - valueTile("battery", "device.battery", inactiveLabel:false, decoration:"flat", width:2, height:2) { - state "battery", label:'${currentValue}% battery', unit:"" - } - - standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" - } - - main(["valve"]) - details(["valve", "battery", "refresh"]) - } + } + + definition (name: "VA4200WZ-VA4200ZB Sinope Valve", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.watervalve") { + capability "Configuration" + capability "Refresh" + capability "Actuator" + capability "Valve" + capability "Battery" + capability "Power Source" + capability "Health Check" + + fingerprint manufacturer: "Sinope Technologies", model: "VA4200WZ", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4200WZ + fingerprint manufacturer: "Sinope Technologies", model: "VA4200ZB", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4200ZB + fingerprint manufacturer: "Sinope Technologies", model: "VA4220ZB", deviceJoinName: "Sinope Valve", mnmn:"SmartThings", vid:"SmartThings-smartthings-ZigBee_Valve" //VA4220ZB + } + + 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", nextState:"closing" + attributeState "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" + } + tileAttribute ("powerSource", key: "SECONDARY_CONTROL") { + attributeState "powerSource", label:'Power Source: ${currentValue}' + } + } + + valueTile("battery", "device.battery", inactiveLabel:false, decoration:"flat", width:2, height:2) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + + main(["valve"]) + details(["valve", "battery", "refresh"]) + } } def open() { - zigbee.on() + zigbee.on() } def close() { - zigbee.off() + zigbee.off() } def refresh() { - traceEvent(settings.logFilter, "refresh called", settings.trace, get_LOG_DEBUG()) - def cmds = [] - cmds += zigbee.readAttribute(0x0006, 0x0000)//refresh on/off - cmds += zigbee.readAttribute(0x0000, 0x0007)//refresh power source - cmds += zigbee.readAttribute(0x0001, 0x0021)//refresh battery percentage remaining - cmds += zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null)//configure reporting on/off min: 0sec, max 600sec - cmds += zigbee.configureReporting(0x0001, 0x0021, 0x20, 60, 60*60, 1)//configure reporting battery percentage remaining min: 6sec, max 1hour - return sendZigbeeCommands(cmds) + traceEvent(settings.logFilter, "refresh called", settings.trace, get_LOG_DEBUG()) + def cmds = [] + cmds += zigbee.readAttribute(0x0006, 0x0000)//refresh on/off + cmds += zigbee.readAttribute(0x0000, 0x0007)//refresh power source + cmds += zigbee.readAttribute(0x0001, 0x0020)//refresh battery voltage remaining + cmds += zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null)//configure reporting on/off min: 0sec, max 600sec + cmds += zigbee.configureReporting(0x0001, 0x0020, 0x20, 60, 60*60, 1)//configure reporting battery voltage remaining min: 6sec, max 1hour + return sendZigbeeCommands(cmds) } def configure() { - traceEvent(settings.logFilter, "Configuring Reporting and Bindings", settings.trace, get_LOG_DEBUG()) + traceEvent(settings.logFilter, "Configuring Reporting and Bindings", settings.trace, get_LOG_DEBUG()) - //allow 15 minutes withour receiving on/off state + //allow 15 minutes withour receiving on/off state sendEvent(name: "checkInterval", value: 15*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - refresh() + refresh() } def installed() { traceEvent(settings.logFilter, "installed>Device is now Installed", settings.trace) initialize() } -def initialize(){ +def initialize() { traceEvent(settings.logFilter, "device is initializing", settings.trace) runEvery15Minutes(refreshPowerSource)//the POWER_SOURCE attribute is not reportable. - runIn(10,refreshPowerSource) - refresh() + runIn(10,refreshPowerSource) + refresh() } /** @@ -105,42 +105,42 @@ def ping() { // Parse incoming device messages to generate events def parse(String description) { - traceEvent(settings.logFilter, "description is $description", settings.trace, get_LOG_DEBUG()) - def result = [] - def event = zigbee.getEvent(description) - if(event){ - if(event.name == "switch") { - event.name = "valve" - if(event.value == "on") { - event.value = "open" - } - else if(event.value == "off") { - event.value = "closed" - } - sendEvent(name: "checkInterval", value: 15*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - } - sendEvent(event) - } - else{ - Map map = [:] - if (description?.startsWith('catchall:')) { - map = parseCatchAllMessage(description) - } - else if (description?.startsWith('read attr -')) { - map = parseReportAttributeMessage(description) - } - - if(map){ - result += createEvent(map) - if(map.additionalAttrs){ - def additionalAttrs = map.additionalAttrs - additionalAttrs.each{allMaps -> - result += createEvent(allMaps) - } - } - } - } - + traceEvent(settings.logFilter, "description is $description", settings.trace, get_LOG_DEBUG()) + def result = [] + def event = zigbee.getEvent(description) + if (event) { + if (event.name == "switch") { + event.name = "valve" + if (event.value == "on") { + event.value = "open" + } + else if (event.value == "off") { + event.value = "closed" + } + sendEvent(name: "checkInterval", value: 15*60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + sendEvent(event) + } + else { + Map map = [:] + if (description?.startsWith('catchall:')) { + map = parseCatchAllMessage(description) + } + else if (description?.startsWith('read attr -')) { + map = parseReportAttributeMessage(description) + } + + if (map) { + result += createEvent(map) + if (map.additionalAttrs) { + def additionalAttrs = map.additionalAttrs + additionalAttrs.each{allMaps -> + result += createEvent(allMaps) + } + } + } + } + return result } @@ -148,29 +148,29 @@ private Map parseCatchAllMessage(String description) { Map resultMap = [:] def cluster = zigbee.parse(description) if (shouldProcessMessage(cluster)) { - traceEvent(settings.logFilter, "parseCatchAllMessage > $cluster", settings.trace) + traceEvent(settings.logFilter, "parseCatchAllMessage > $cluster", settings.trace) switch(cluster.clusterId) { - case 0x0000://power source - // 0x07 - configure reporting - if (cluster.command != 0x07) { + case 0x0000://power source + // 0x07 - configure reporting + if (cluster.command != 0x07) { resultMap = getPowerSourceResult(cluster.data.last()) } - break + break case 0x0001://battery percentage remaining // 0x07 - configure reporting if (cluster.command != 0x07) { resultMap = getBatteryResult(cluster.data.last()) } break - case 0x0006://on/off - //0x07 - configure reporting - if (cluster.command != 0x07) { + case 0x0006://on/off + //0x07 - configure reporting + if (cluster.command != 0x07 && cluster.data.length) { resultMap = getOnOffResult(cluster.data.last()) } - break - } - } - return resultMap + break + } + } + return resultMap } private boolean shouldProcessMessage(cluster) { @@ -182,84 +182,79 @@ private boolean shouldProcessMessage(cluster) { } private Map parseReportAttributeMessage(String description) { - Map descMap = zigbee.parseDescriptionAsMap(description) + Map descMap = zigbee.parseDescriptionAsMap(description) traceEvent(settings.logFilter, "Desc Map: $descMap" + cluster, settings.trace, get_LOG_DEBUG()) Map resultMap = [:] if (descMap.cluster == "0000" && descMap.attrId == "0007") { resultMap = getPowerSourceResult(descMap.value) } - else if (descMap.cluster == "0001" && descMap.attrId == "0021") { - resultMap = getBatteryResult(zigbee.convertHexToInt(descMap.value)) + else if (descMap.cluster == "0001" && descMap.attrId == "0020") { + resultMap = getBatteryResult(zigbee.convertHexToInt(descMap.value)) } - else if (descMap.cluster == "0006" && descMap.attrId == "0000") { - resultMap = getOnOffResult(descMap.value) + else if (descMap.cluster == "0006" && descMap.attrId == "0000") { + resultMap = getOnOffResult(descMap.value) } return resultMap } private Map getBatteryResult(rawValue) { - traceEvent(settings.logFilter, "Battery rawValue = ${rawValue}" + cluster, settings.trace, get_LOG_DEBUG()) + traceEvent(settings.logFilter, "Battery rawValue = ${rawValue}" + cluster, settings.trace, get_LOG_DEBUG()) def result = [:] - result.name = 'battery' - result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" - - int batteryPercent = rawValue / 2 - result.value = Math.min(100, batteryPercent) + result.name = 'battery' + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" + result.value = convertVoltToPercent(rawValue) return result } private Map getOnOffResult(rawValue) { - traceEvent(settings.logFilter, "On/Off rawValue = ${rawValue}" + cluster, settings.trace, get_LOG_DEBUG()) + traceEvent(settings.logFilter, "On/Off rawValue = ${rawValue}" + cluster, settings.trace, get_LOG_DEBUG()) Map result = [:] - result.name = 'valve' - result.descriptionText = "{{ device.displayName }} state was {{ value }}" - if(rawValue == "0000"){ - result.value == "off" - } - else{ - result.value == "on" - } - - List addAttribsList = [] - Map addAttrib = [:] - - addAttrib.name = 'valve' + result.name = 'valve' + result.descriptionText = "{{ device.displayName }} state was {{ value }}" + if (rawValue == "0000") { + result.value == "off" + } + else { + result.value == "on" + } + + List addAttribsList = [] + Map addAttrib = [:] + + addAttrib.name = 'valve' addAttrib.descriptionText = "{{ device.displayName }} state was {{ value }}" addAttrib.value = result.value addAttribsList += addAttrib result.additionalAttrs = addAttribsList - + return result } private Map getPowerSourceResult(rawValue) { traceEvent(settings.logFilter, "powerSource rawValue = ${rawValue}" + cluster, settings.trace, get_LOG_DEBUG()) def result = [:] - result.name = 'powerSource' - result.translatable = true - result.descriptionText = "{{ device.displayName }} powerSource was {{ value }}%" - if(rawValue == "0081" || rawValue == "0082"){ - result.value = "mains" - } - else if(rawValue == "0003"){ - result.value = "battery" - } - else if(rawValue == "0004"){ - result.value = "dc" - } - else{ - result.value = "unknown" - } + result.name = 'powerSource' + result.translatable = true + result.descriptionText = "{{ device.displayName }} powerSource was {{ value }}%" + if (rawValue == "0081" || rawValue == "0082") { + result.value = "mains" + } else if (rawValue == "0003") { + result.value = "battery" + } else if (rawValue == "0004") { + result.value = "dc" + } else { + result.value = "unknown" + } return result } -def refreshPowerSource(){ - def cmds = [] - cmds += zigbee.readAttribute(0x0000, 0x0007)//read power source attribute +def refreshPowerSource() { + def cmds = [] + cmds += zigbee.readAttribute(0x0000, 0x0007)//read power source attribute return sendZigbeeCommands(cmds) } @@ -286,6 +281,33 @@ private int get_LOG_TRACE() { return 5 } +private def convertVoltToPercent(value) { + def levelValue; + def levelsTable = [0, 20000, 40000, 60000, 80000, 100000]; + def anglesTable = [30, 55, 56, 57, 58.5, 60]; + + if (value > anglesTable[anglesTable.size - 1]) { // if the value of the angle is greater than the maximum + value = anglesTable[anglesTable.size - 1]; // use the maximum value instead + } + + def index = 1; + + while ( value > anglesTable[index]) { index++ } + + def ratioBetweenPointXandY = (levelsTable[index] - levelsTable[index - 1]) / (anglesTable[index] - anglesTable[index - 1]); + def angleToAdd = levelsTable[index] - (anglesTable[index] * ratioBetweenPointXandY); + def levelWithFactor = (ratioBetweenPointXandY * value) + angleToAdd; + def roundedLevelValue = Math.round(levelWithFactor / 1000); + + if (roundedLevelValue > 100) { + return 100 + } else if (roundedLevelValue < 0) { + return 0 + } else { + return roundedLevelValue; + } +} + def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMessage = true) { int LOG_ERROR = get_LOG_ERROR() int LOG_WARN = get_LOG_WARN() From c6e496cbe0956068a97688f7231b826baf2f3ed3 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Wed, 1 Sep 2021 03:37:37 -0400 Subject: [PATCH 272/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZSE42 XS Water Leak Sensor (#74778) * DevWs for Zooz (The Smartest House) containing containing Zooz ZSE42 XS Water Leak Sensor * Added fingerprint for Zooz ZSE42 XS Water Leak Sensor * Delete zooz-zse42-xs-water-leak-sensor.groovy * Moved raw description above fingerprint --- .../zwave-water-sensor.groovy | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy b/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy index 86e57ec9995..47b6a39d7c6 100644 --- a/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy +++ b/devicetypes/smartthings/zwave-water-sensor.src/zwave-water-sensor.groovy @@ -33,6 +33,8 @@ metadata { fingerprint mfr: "000C", prod: "0201", model: "000A", deviceJoinName: "HomeSeer Water Leak Sensor" //HomeSeer LS100+ Water Sensor //zw:Ss2 type:0701 mfr:0173 prod:4C47 model:4C44 ver:1.10 zwv:4.61 lib:03 cc:5E,55,98,9F sec:86,71,85,59,72,5A,6C,7A,84,80 fingerprint mfr: "0173", prod: "4C47", model: "4C44", deviceJoinName: "Leak Gopher Water Leak Sensor" //Leak Intelligence Leak Gopher Z-Wave Leak Detector + //zw:Ss2a type:0701 mfr:027A prod:7000 model:E002 ver:1.05 zwv:7.13 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,71,30,70,84,7A + fingerprint mfr: "027A", prod: "7000", model: "E002", deviceJoinName: "Zooz Water Leak Sensor" //Zooz ZSE42 XS Water Leak Sensor } simulator { @@ -60,8 +62,8 @@ metadata { } def initialize() { - if (isAeotec() || isNeoCoolcam() || isDome() || isLeakGopher()) { - // 8 hour (+ 2 minutes) ping for Aeotec, NEO Coolcam, Dome, Leak Gopher + if (isAeotec() || isNeoCoolcam() || isDome() || isLeakGopher() || isZooz()) { + // 8 hour (+ 2 minutes) ping for Aeotec, NEO Coolcam, Dome, Leak Gopher, Zooz sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } else { // 12 hours (+ 2 minutes) for other devices @@ -92,8 +94,8 @@ def configure() { // Tell sensor to send us battery information instead of USB power information commands << encap(zwave.configurationV1.configurationSet(parameterNumber: 0x5E, scaledConfigurationValue: 1, size: 1)) response(delayBetween(commands, 1000) + ["delay 20000", encap(zwave.wakeUpV1.wakeUpNoMoreInformation())]) - } else if (isNeoCoolcam() || isDome() || isLeakGopher()) { - // wakeUpInterval set to 4 h for NEO Coolcam, Dome, Leak Gopher + } else if (isNeoCoolcam() || isDome() || isLeakGopher() || isZooz()) { + // wakeUpInterval set to 4 h for NEO Coolcam, Dome, Leak Gopher, Zooz zwave.wakeUpV1.wakeUpIntervalSet(seconds: 4 * 3600, nodeid: zwaveHubNodeId).format() } } @@ -332,4 +334,8 @@ private isAeotec() { private isLeakGopher() { zwaveInfo.mfr == "0173" && zwaveInfo.model == "4C44" +} + +private isZooz() { + zwaveInfo.mfr == "027A" && zwaveInfo.model == "E002" } \ No newline at end of file From 5970bf2a4128e8edc4e445e9aa682647e1540392 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Wed, 1 Sep 2021 03:38:29 -0400 Subject: [PATCH 273/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZSE41 XS Open Close Sensor (#74779) * DevWs for Zooz (The Smartest House) containing containing Zooz ZSE41 XS Open Close Sensor * Added fingerprint for Zooz ZSE41 XS Open | Close Sensor * Delete zooz-zse41-xs-open-close-sensor.groovy * Moved raw description above fingerprint --- .../zwave-door-window-sensor.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 0afaf53a7ea..53a98366159 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 @@ -64,6 +64,8 @@ metadata { fingerprint mfr: "0371", prod: "0202", model: "000C", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //AU //Aeotec Door/Window Sensor 7 Pro fingerprint mfr: "0371", prod: "0002", model: "000B", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //EU //Aeotec Door/Window Sensor 7 zw:Ss2a type:0701 mfr:0371 prod:0002 model:000B ver:1.01 zwv:7.12 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,70,71,84,7A fingerprint mfr: "0371", prod: "0102", model: "000B", deviceJoinName: "Aeotec Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact-5" //US //Aeotec Door/Window Sensor 7 zw:Ss2a type:0701 mfr:0371 prod:0102 model:000B ver:1.01 zwv:7.12 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,70,71,84,7A + //zw:Ss2a type:0701 mfr:027A prod:7000 model:E001 ver:1.05 zwv:7.13 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,71,30,70,84,7A + fingerprint mfr: "027A", prod: "7000", model: "E001", deviceJoinName: "Zooz Open/Closed Sensor" //Zooz ZSE41 XS Open Close Sensor } // simulator metadata @@ -384,4 +386,4 @@ private isEnerwave() { def clearTamper() { sendEvent(name: "tamper", value: "clear") -} +} \ No newline at end of file From 413e2049eebb2548b546d16593bbe3c7ebd1fd37 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Wed, 1 Sep 2021 03:42:05 -0400 Subject: [PATCH 274/422] DevWs for Zooz (The Smartest House) containing containing Fortrezz Water Valve (#74860) * DevWs for Zooz (The Smartest House) containing containing Fortrezz Water Valve * Added raw description to Zooz fingerprint * Moved raw description above fingerprint --- .../fortrezz-water-valve.src/fortrezz-water-valve.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 6b91427a1d9..abfd74f546a 100644 --- a/devicetypes/smartthings/fortrezz-water-valve.src/fortrezz-water-valve.groovy +++ b/devicetypes/smartthings/fortrezz-water-valve.src/fortrezz-water-valve.groovy @@ -21,6 +21,8 @@ metadata { fingerprint deviceId: "0x1000", inClusters: "0x25,0x72,0x86,0x71,0x22,0x70", deviceJoinName: "FortrezZ Valve" fingerprint mfr:"0084", prod:"0213", model:"0215", deviceJoinName: "FortrezZ Valve" //FortrezZ Water Valve + //zw:Ls2a type:1000 mfr:027A prod:0101 model:0036 ver:1.07 zwv:7.13 lib:03 cc:5E,55,98,9F,6C,22 sec:25,85,8E,59,71,86,72,5A,87,73,7A,31,70,80 + fingerprint mfr:"027A", prod:"0101", model:"0036", deviceJoinName: "Zooz Valve" //Zooz ZAC36 Titan Valve Actuator } // simulator metadata @@ -114,4 +116,4 @@ def createEventWithDebug(eventMap) { def event = createEvent(eventMap) log.debug "Event created with ${event?.name}:${event?.value} - ${event?.descriptionText}" return event -} +} \ No newline at end of file From f60379c389aafcc2f78935189c28a1a030a51273 Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:46:58 -0700 Subject: [PATCH 275/422] DevWs for Smartenit, Inc containing containing Smartenit Open/Closed Sensor (#71536) * DevWs for Smartenit, Inc containing containing Smartenit Open/Closed Sensor * Adding Smartenit fingerprint to smartsense DTH * Added raw description to Smartenit finger print --- .../smartsense-open-closed-sensor.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index 1428c938e98..dc680d8b68f 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -45,6 +45,8 @@ metadata { fingerprint inClusters: "0000, 0003, 0006, 0500", outClusters: "0003, 0019", manufacturer: "DAWON_DNS", model: "SS-B100-ZB", deviceJoinName: "Dawon Signal Interlock", mnmn: "0AIg", vid: "dawon-zigbee-signal-interlock2" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"WISZB-120", deviceJoinName: "frient Open/Closed Sensor" fingerprint manufacturer: "frient A/S", model :"WISZB-121", deviceJoinName: "frient Open/Closed Sensor", mnmn: "SmartThingsCommunity", vid: "aaca16c3-fade-3cb3-b742-e2237f4ffd76" // Raw description: 23 0104 0402 00 06 0000 0001 0003 000F 0020 0500 02 000A 0019 + //Smartenit + fingerprint manufacturer: "Compacta", model :"ZBWDS", deviceJoinName: "Smartenit Open/Closed Sensor", mnmn: "SmartThings", vid: "generic-contact" // Raw description: 01 0104 0000 00 04 0000 0001 0003 0007 01 0006 } simulator { @@ -245,4 +247,4 @@ private Boolean isBoschRadionMultiSensor() { private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "frient A/S" -} +} \ No newline at end of file From 8c943970299c2c97e73dcb6a15b2ecef8714a91b Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:49:26 -0700 Subject: [PATCH 276/422] DevWs for Smartenit, Inc containing containing Smartenit Motion Sensor (#71621) * DevWs for Smartenit, Inc containing containing Smartenit Motion Sensor * Adding Smartenit fingerprint to smartsense motion sensor DTH * Added raw description to Smartenit motion sensor model --- .../smartsense-motion-sensor.groovy | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index 9f7746c193e..4ccddb0f3b5 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -46,6 +46,8 @@ metadata { fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "AduroSmart Eria", model: "VMS_ADUROLIGHT", deviceJoinName: "ERIA Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2" //ERIA Motion Sensor V2.1 fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "frient A/S", model :"MOSZB-140", deviceJoinName: "frient Motion Sensor" fingerprint manufacturer: "frient A/S", model :"MOSZB-141", deviceJoinName: "frient Motion Sensor", mnmn: "SmartThingsCommunity", vid: "87753fce-8cd6-3b91-8bde-2483e564252d" // Raw description: 22 0104 0107 00 03 0000 0003 0406 00 + //Smartenit + fingerprint manufacturer: "Compacta", model: "ZBMS3-1", deviceJoinName: "Smartenit Motion Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Motion_Sensor" // Raw description: 01 0104 0402 00 07 0000 0001 0003 0015 0500 0020 0B05 00 } simulator { @@ -324,6 +326,8 @@ def configure() { if (isFrientSensor()) { configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 300, 0x64, [destEndpoint: 0x26]) + } else if (isCompactaSensor()) { + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 30, 300, 0x64, [destEndpoint: 0x0003]) } else { configCmds += zigbee.temperatureConfig(30, 300) } @@ -355,3 +359,7 @@ private shouldUseOldBatteryReporting() { private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "frient A/S" } + +private Boolean isCompactaSensor() { + device.getDataValue("manufacturer") == "Compacta" +} \ No newline at end of file From e979df75b9a3b4c0ee859da0d9916121da4063fa Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:51:57 -0700 Subject: [PATCH 277/422] DevWs for Smartenit, Inc containing containing Smartenit Valve (#72090) * DevWs for Smartenit, Inc containing containing Smartenit Valve * Adding Smartenit fingerprint to Zigbee Valve DTH * Added Raw Description for Smartenit model/fixed spacing issue. --- devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy b/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy index a60797ac468..3fac110ab18 100644 --- a/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy +++ b/devicetypes/smartthings/zigbee-valve.src/zigbee-valve.groovy @@ -27,6 +27,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0006, 0020, 0B02, FC02", outClusters: "0019", manufacturer: "WAXMAN", model: "leakSMART Water Valve v2.10", deviceJoinName: "leakSMART Valve" //leakSMART Valve fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0004, 0005, 0006, 0008, 000F, 0020, 0B02", outClusters: "0003, 0019", manufacturer: "WAXMAN", model: "House Water Valve - MDL-TBD", deviceJoinName: "Waxman Valve" //Waxman House Water Valve fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006, 0500", outClusters: "0019", manufacturer: "", model: "E253-KR0B0ZX-HA", deviceJoinName: "Valve" //Smart Gas Valve Actuator + fingerprint manufacturer: "Compacta", model: "ZBVC1(1023A)", deviceJoinName: "Smartenit Valve" // Raw Description: 01 0104 0002 00 06 0000 0003 0004 0005 0006 0015 00 } // simulator metadata @@ -153,4 +154,4 @@ def installed() { def ping() { zigbee.onOffRefresh() -} +} \ No newline at end of file From 7abc94d90c71852cd082f0689b02226e14c9ba50 Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Wed, 1 Sep 2021 22:55:48 -0700 Subject: [PATCH 278/422] DevWs for Smartenit, Inc containing containing ZBALRM (#71537) * DevWs for Smartenit, Inc containing containing ZBALRM * Adding Smartenit fingerprint to Ozom siren DTH * Added raw description to Smartenit model --- .../ozom-smart-siren.groovy | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy index 9f2d044d2c7..e52f9b25d7b 100644 --- a/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy +++ b/devicetypes/smartthings/ozom-smart-siren.src/ozom-smart-siren.groovy @@ -28,6 +28,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0003,0500,0502", outClusters: "0000", manufacturer: "ClimaxTechnology", model: "SRAC_00.00.00.16TC", mnmn: "SmartThings", vid: "generic-siren-8", deviceJoinName: "Ozom Siren" // Ozom Siren - SRAC-23ZBS //Ozom Smart Siren fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0009,0500,0502", outClusters: "0003,0019", manufacturer: "Heiman", model: "WarningDevice", mnmn: "SmartThings", vid: "generic-siren-8", deviceJoinName: "HEIMAN Siren" //HEIMAN Smart Siren fingerprint manufacturer: "frient A/S", model :"SIRZB-110", deviceJoinName: "frient Siren", mnmn: "SmartThingsCommunity", vid: "33d3bbac-144c-3a31-b022-0fc5c74240a3" // frient Smart Siren, 2B 0104 0403 00 05 0000 0003 0502 0500 0001 02 000A 0019 + fingerprint model: "ZBALRM", manufacturer: "Compacta", deviceJoinName: "Smartenit Alarm", mnmn: "SmartThings" // Raw Description: 01 0104 0403 00 07 0000 0001 0003 0015 0500 0502 0B05 00 } tiles { @@ -54,7 +55,9 @@ private getCOMMAND_DEFAULT_RESPONSE() { 0x0B } private getMODE_SIREN() { "13" } private getMODE_STROBE() { "04" } +private getMODE_SMARTENIT_STROBE() { "DF" } private getMODE_BOTH() { "17" } +private getMODE_SMARTENIT_BOTH() { "1A" } private getMODE_OFF() { "00" } private getSTROBE_DUTY_CYCLE() { "40" } private getSTROBE_LEVEL() { "03" } @@ -179,11 +182,23 @@ def startCmd(cmd) { paramDutyCycle = BASIC_DUTY_CYCLE paramStrobeLevel = BASIC_LEVEL } else if (cmd == ALARM_STROBE) { - paramMode = isFrientSiren() ? FRIENT_MODE_SIREN : MODE_STROBE + if (isFrientSiren()) { + paramMode = FRIENT_MODE_SIREN + } else if (isCompactaSiren()) { + paramMode = MODE_SMARTENIT_STROBE + } else { + paramMode = MODE_STROBE + } paramDutyCycle = isFrientSiren() ? BASIC_DUTY_CYCLE : STROBE_DUTY_CYCLE paramStrobeLevel = isFrientSiren() ? BASIC_LEVEL : STROBE_LEVEL } else if (cmd == ALARM_BOTH) { - paramMode = isFrientSiren() ? FRIENT_MODE_SIREN : MODE_BOTH + if (isFrientSiren()) { + paramMode = FRIENT_MODE_SIREN + } else if (isCompactaSiren()) { + paramMode = MODE_SMARTENIT_BOTH + } else { + paramMode = MODE_BOTH + } paramDutyCycle = isFrientSiren() ? BASIC_DUTY_CYCLE : STROBE_DUTY_CYCLE paramStrobeLevel = isFrientSiren() ? BASIC_LEVEL : STROBE_LEVEL } @@ -215,3 +230,7 @@ private isOzomSiren() { private Boolean isFrientSiren() { device.getDataValue("manufacturer") == "frient A/S" } + +private Boolean isCompactaSiren() { + device.getDataValue("manufacturer") == "Compacta" +} \ No newline at end of file From 4b42c34eb0fc12753ea143f00cea244465174696 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Thu, 2 Sep 2021 22:50:21 +0900 Subject: [PATCH 279/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#74527) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * A unnecessary empty tab was deleted * Changed parse method for analoginput(DataType.FLOAT4) value. --- .../sihas-multipurpose-sensor.groovy | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index 2a2e83616df..e193f5b6e30 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -28,11 +28,13 @@ metadata { capability "Health Check" capability "Sensor" capability "Contact Sensor" - + capability "afterguide46998.peopleCounter" + fingerprint inClusters: "0000,0001,0003,0020,0400,0402,0405,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "USM-300Z", deviceJoinName: "SiHAS MultiPurpose Sensor", mnmn: "SmartThings", vid: "generic-motion-6" fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2", ocfDeviceType: "x.com.st.d.sensor.motion" fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Temp/Humidity_Sensor", ocfDeviceType: "oic.d.thermostat" fingerprint inClusters: "0000,0001,0003,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "DSM-300Z", deviceJoinName: "SiHAS Contact Sensor", mnmn: "SmartThings", vid: "generic-contact-3", ocfDeviceType: "x.com.st.d.sensor.contact" + fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "b4e6d6e1-65e2-3f2e-8167-8ddd820f578e", ocfDeviceType: "x.com.st.d.sensor.motion" } preferences { section { @@ -44,11 +46,13 @@ metadata { private getILLUMINANCE_MEASUREMENT_CLUSTER() { 0x0400 } private getOCCUPANCY_SENSING_CLUSTER() { 0x0406 } +private getANALOG_INPUT_BASIC_CLUSTER() { 0x000C } private getPOWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE() { 0x0020 } private getTEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } private getRALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } private getILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } private getOCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE() { 0x0000 } +private getANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE() { 0x0055 } private List collectAttributes(Map descMap) { List descMaps = new ArrayList() @@ -79,6 +83,8 @@ def parse(String description) { map = translateZoneStatus(zs) } else if (descMap?.clusterInt == OCCUPANCY_SENSING_CLUSTER && descMap.attrInt == OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE && descMap?.value) { map = getMotionResult(descMap.value == "01" ? "active" : "inactive") + } else if (descMap?.clusterInt == ANALOG_INPUT_BASIC_CLUSTER && descMap.attrInt == ANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE && descMap?.value) { + map = getAnalogInputResult(Integer.parseInt(descMap.value,16)) } } else if (description?.startsWith('illuminance:')) { //parse illuminance map = parseCustomMessage(description) @@ -191,6 +197,18 @@ private Map getContactResult(value) { ] } +private Map getAnalogInputResult(value) { + Float f = Float.intBitsToFloat(value.intValue()) + int pc = f.round(0) + String descriptionText = "${device.displayName} : $pc" + return [ + name : 'peopleCounter', + value : pc, + descriptionText: descriptionText, + translatable : true + ] +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ @@ -257,9 +275,12 @@ def configure() { } if (isDSM300()) { - configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE, DataType.UINT8, 30, 21600, 0x01/*100mv*1*/) configCmds += zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 0, 0xffff, null) } + + if (isCSM300()) { + configCmds += zigbee.configureReporting(ANALOG_INPUT_BASIC_CLUSTER, ANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE, DataType.FLOAT4, 1, 600, 1) + } return refresh() + configCmds } @@ -279,3 +300,7 @@ private Boolean isOSM300() { private Boolean isDSM300() { device.getDataValue("model") == "DSM-300Z" } + +private Boolean isCSM300() { + device.getDataValue("model") == "CSM-300Z" +} From b32d855a24d0533638bfce590750665061255b89 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Fri, 3 Sep 2021 09:09:07 +0800 Subject: [PATCH 280/422] * - Syntax format compliance adjustment * - fix some bugs --- .../child-button.src/child-button.groovy | 53 +++++++++++++++++++ .../min-smart-plug-dimmer.groovy | 52 +++++------------- 2 files changed, 67 insertions(+), 38 deletions(-) create mode 100644 devicetypes/sky-nie/child-button.src/child-button.groovy diff --git a/devicetypes/sky-nie/child-button.src/child-button.groovy b/devicetypes/sky-nie/child-button.src/child-button.groovy new file mode 100644 index 00000000000..a3e0c370ceb --- /dev/null +++ b/devicetypes/sky-nie/child-button.src/child-button.groovy @@ -0,0 +1,53 @@ +/** + * Child Button v1.0 (CHILD DEVICE) + * + * Author: + * winnie (sky-nie) + * + * Changelog: + * + * 1.0 (03/16/2020) + * - Initial Release + * + * Reference: + * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/component-button.src/component-button.groovy + * + * 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: "Child Button", namespace: "sky-nie", author: "winnie", ocfDeviceType: "x.com.st.d.remotecontroller") { + capability "Button" + capability "Sensor" + } + + tiles(scale: 2) { + multiAttributeTile(name: "button", type: "generic", width: 6, height: 4, canChangeIcon: true) { + tileAttribute("device.button", key: "PRIMARY_CONTROL") { + attributeState "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff" + } + } + main "button" + details(["button"]) + } +} + +def installed() { + log.debug "installed()..." +} + +def updated() { + log.debug "updated()..." +} + +def uninstalled() { + log.warn "uninstalled()..." +} \ No newline at end of file diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 89f80b3ed0b..4cbf1e48e73 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -1,5 +1,5 @@ /** - * Min Smart Plug Dimmer v2.0.1 + * Min Smart Plug Dimmer v2.0.2 * * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) * @@ -10,6 +10,7 @@ * * Changelog: * + * 2.0.2 (09/02/2021) * 2.0.1 (08/27/2021) * - Syntax format compliance adjustment * - fix some bugs @@ -74,6 +75,7 @@ import groovy.json.JsonOutput metadata { definition (name: "Min Smart Plug Dimmer", namespace: "sky-nie", author: "winnie", ocfDeviceType: "oic.d.smartplug") { capability "Actuator" + capability "Sensor" capability "Switch" capability "Switch Level" capability "Configuration" @@ -87,8 +89,8 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Dimmer Switch" //MP21ZD fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Dimmer Switch" //MP22ZD fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "New One Dimmer Switch" //N4002 - fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS11ZS Minoston Smart Dimmer Switch - fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS13ZS Minoston Smart Toggle Dimmer Switch + fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS11ZS Minoston Smart Dimmer Switch + fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS13ZS Minoston Smart Toggle Dimmer Switch fingerprint mfr: "0312", prod: "BB00", model: "BB02", deviceJoinName: "Evalogik Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //ZW31S Evalogik Smart Dimmer Switch fingerprint mfr: "0312", prod: "BB00", model: "BB04", deviceJoinName: "Evalogik Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //ZW31TS Evalogik Smart Toggle Dimmer Switch } @@ -132,15 +134,13 @@ private initialize() { } catch (ex) { log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) } - } else if (!state.createButtonEnabled && childDevices) { - removeChildButton(childDevices[0]) } } private addChildButton() { log.warn "Creating Button Device" def child = addChildDevice( - "smartthings", + "sky-nie", "Child Button", "${device.deviceNetworkId}-2", device.getHub().getId(), @@ -157,31 +157,16 @@ private addChildButton() { return child } -private removeChildButton(child) { - try { - log.warn "Removing ${child.displayName}} " - deleteChildDevice(child.deviceNetworkId) - } catch (ex) { - log.error("Unable to remove ${child.displayName}! Make sure that the device is not being used by any SmartApps.",ex) - } -} - -def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd){ +def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { if (state.lastSequenceNumber != cmd.sequenceNumber) { state.lastSequenceNumber = cmd.sequenceNumber logTrace "${cmd}" def paddle = (cmd.sceneNumber == 1) ? "down" : "up" def btnVal - switch (cmd.keyAttributes){ + switch (cmd.keyAttributes) { case 0: btnVal = paddle break - case 1: - logDebug "Button released not supported" - break - case 2: - logDebug "Button held not supported" - break case 3: btnVal = paddle + "_2x" break @@ -233,7 +218,6 @@ private static def getCheckInterval() { def updated() { if (!isDuplicateCommand(state.lastUpdated, 5000)) { state.lastUpdated = new Date().time - logDebug "updated()..." if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) @@ -245,7 +229,6 @@ def updated() { } runIn(5, executeConfigureCmds, [overwrite: true]) } - return [] } @@ -331,7 +314,7 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport logDebug "${param.name}(#${param.num}) = ${val}" state["configParam${param.num}"] = val } else { - logDebug "Parameter #${cmd.parameterNumber} = ${cmd.configurationValue}" + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" } return [] } @@ -457,7 +440,6 @@ private getMaximumBrightnessParam() { private getParam(num, name, size, defaultVal, options=null, range=null) { def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) - def map = [num: num, name: name, size: size, value: val] if (options) { map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value @@ -466,7 +448,6 @@ private getParam(num, name, size, defaultVal, options=null, range=null) { if (range) { map.range = range } - return map } @@ -591,7 +572,7 @@ private sendSwitchEvents(rawVal, type) { if (rawVal) { sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") } - if(isButtonAvailable()) { + if (isButtonAvailable()) { def paddlesReversed = (paddleControlParam.value == 1) if (state.createButtonEnabled && (type == "physical") && childDevices) { if (paddleControlParam.value == 2) { @@ -610,16 +591,11 @@ private sendSwitchEvents(rawVal, type) { } private isButtonAvailable() { - if(device == null){ + if (device == null) { log.error "isButtonAvailable device = null" return true - }else{ + } else { log.debug "isButtonAvailable device.rawDescription = ${device.rawDescription}" - def v20 = "${device.rawDescription}".contains("model:EE02") - def v21 = "${device.rawDescription}".contains("model:EE04") - def v22 = "${device.rawDescription}".contains("model:BB02") - def v23 = "${device.rawDescription}".contains("model:BB04") - def v2 = v20||v21||v22||v23 - return v2 + return "${device.rawDescription}".contains("model:EE02") || "${device.rawDescription}".contains("model:EE04") ||"${device.rawDescription}".contains("model:BB02") || "${device.rawDescription}".contains("model:BB04") } -} \ No newline at end of file +} From 2b2190378c37bcb6d5c4c76ccebf273eb41b5ea4 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Fri, 3 Sep 2021 09:28:23 +0800 Subject: [PATCH 281/422] Syntax format compliance adjustment --- .../min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 4cbf1e48e73..ef95d05c65c 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -596,6 +596,6 @@ private isButtonAvailable() { return true } else { log.debug "isButtonAvailable device.rawDescription = ${device.rawDescription}" - return "${device.rawDescription}".contains("model:EE02") || "${device.rawDescription}".contains("model:EE04") ||"${device.rawDescription}".contains("model:BB02") || "${device.rawDescription}".contains("model:BB04") + return "${device.rawDescription}".contains("model:EE02") || "${device.rawDescription}".contains("model:EE04") || "${device.rawDescription}".contains("model:BB02") || "${device.rawDescription}".contains("model:BB04") } } From 99c246b0d7bf7a4d075461fc1f2efec74caf6c20 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Fri, 3 Sep 2021 10:55:28 +0800 Subject: [PATCH 282/422] 1.restore removeChildButton; 2.Simplify the code --- .../min-smart-plug-dimmer.groovy | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index ef95d05c65c..cf824d1b3cb 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -127,12 +127,24 @@ private getConfigParamInput(param) { } private initialize() { - if (state.createButtonEnabled && !childDevices) { - try { - def child = addChildButton() - child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - } catch (ex) { - log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + if (isButtonAvailable()) { + state.createButtonEnabled = (safeToInt(settings?.createButton) != 0) + if (state.createButtonEnabled && !childDevices) { + try { + def child = addChildButton() + child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } catch (ex) { + log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) + } + } else if (!state.createButtonEnabled && childDevices) { + removeChildButton(childDevices[0]) + } + } else { + if (childDevices) { + removeChildButton(childDevices[0]) } } } @@ -206,7 +218,22 @@ def installed() { state.createButtonEnabled = true } sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - state.refreshConfig = true +} + +def uninstalled() { + logger("debug", "uninstalled()") + if (childDevices) { + removeChildButton(childDevices[0]) + } +} + +private removeChildButton(child) { + try { + log.warn "Removing ${child.displayName}} " + deleteChildDevice(child.deviceNetworkId) + } catch (ex) { + log.error("Unable to remove ${child.displayName}! Make sure that the device is not being used by any SmartApps.", ex) + } } private static def getCheckInterval() { @@ -219,14 +246,7 @@ def updated() { if (!isDuplicateCommand(state.lastUpdated, 5000)) { state.lastUpdated = new Date().time logDebug "updated()..." - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - - if (isButtonAvailable()) { - state.createButtonEnabled = (safeToInt(settings?.createButton) != 0) - initialize() - } + initialize() runIn(5, executeConfigureCmds, [overwrite: true]) } return [] From 0c0229847c75a960824b51563bf4177e8cf2f608 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Sat, 4 Sep 2021 14:14:08 +0800 Subject: [PATCH 283/422] * - remove the preferences item "createButton", Fixedly create a child button * Restrict its use based on fingerprints-because the child buttons are not visible to the user . * - Simplify the code, Syntax format compliance adjustment --- .../min-smart-plug-dimmer.groovy | 103 +++++++----------- 1 file changed, 38 insertions(+), 65 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index cf824d1b3cb..73e1382aef1 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -1,5 +1,5 @@ /** - * Min Smart Plug Dimmer v2.0.2 + * Min Smart Plug Dimmer v2.1.0 * * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) * @@ -10,6 +10,11 @@ * * Changelog: * + * 2.1.0 (09/04/2021) + * - remove the preferences item "createButton", Fixedly create a child button + * Restrict its use based on fingerprints-because the child buttons are not visible to the user . + * - Simplify the code, Syntax format compliance adjustment + * * 2.0.2 (09/02/2021) * 2.0.1 (08/27/2021) * - Syntax format compliance adjustment @@ -110,11 +115,10 @@ metadata { getConfigParamInput(nightLightParam) input "disclaimer", "paragraph", title: "WARNING", - description: "Configuring for 'createButton'、'Maximum Brightness' and 'Paddle Control' are only valid for the devices with product number of MS11ZS、MS13ZS、ZW31S、ZW31TS(one of them)", + description: "Configuring for 'Maximum Brightness' and 'Paddle Control' are only valid for the devices with product number of MS11ZS、MS13ZS、ZW31S、ZW31TS(one of them)", required: false getConfigParamInput(maximumBrightnessParam) getConfigParamInput(paddleControlParam) - input(type: "enum", name: "createButton", required: false, title: "Create Button for Paddles?", options: ["No", "Yes"], defaultValue:"Yes") } } @@ -126,29 +130,6 @@ private getConfigParamInput(param) { } } -private initialize() { - if (device.latestValue("checkInterval") != checkInterval) { - sendEvent(name: "checkInterval", value: checkInterval, displayed: false) - } - if (isButtonAvailable()) { - state.createButtonEnabled = (safeToInt(settings?.createButton) != 0) - if (state.createButtonEnabled && !childDevices) { - try { - def child = addChildButton() - child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - } catch (ex) { - log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) - } - } else if (!state.createButtonEnabled && childDevices) { - removeChildButton(childDevices[0]) - } - } else { - if (childDevices) { - removeChildButton(childDevices[0]) - } - } -} - private addChildButton() { log.warn "Creating Button Device" def child = addChildDevice( @@ -170,6 +151,7 @@ private addChildButton() { } def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + logTrace "CentralSceneNotification: ${cmd}" if (state.lastSequenceNumber != cmd.sequenceNumber) { state.lastSequenceNumber = cmd.sequenceNumber logTrace "${cmd}" @@ -192,9 +174,7 @@ def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotificat } private sendButtonEvent(value) { - if (childDevices) { - childDevices[0].sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) - } + childDevices[0].sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) } def ping() { @@ -214,17 +194,18 @@ private switchMultilevelGetCmd() { def installed() { logDebug "installed()..." - if (isButtonAvailable()) { - state.createButtonEnabled = true + try { + def child = addChildButton() + child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } catch (ex) { + log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) } sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } def uninstalled() { logger("debug", "uninstalled()") - if (childDevices) { - removeChildButton(childDevices[0]) - } + removeChildButton(childDevices[0]) } private removeChildButton(child) { @@ -246,7 +227,9 @@ def updated() { if (!isDuplicateCommand(state.lastUpdated, 5000)) { state.lastUpdated = new Date().time logDebug "updated()..." - initialize() + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } runIn(5, executeConfigureCmds, [overwrite: true]) } return [] @@ -254,7 +237,6 @@ def updated() { def configure() { logDebug "configure()..." - if (state.resyncAll == null) { state.resyncAll = true runIn(8, executeConfigureCmds, [overwrite: true]) @@ -275,11 +257,6 @@ def executeConfigureCmds() { configParams.each { param -> def storedVal = getParamStoredValue(param.num) def paramVal = param.value - if (isButtonAvailable()) { - if ((param == paddleControlParam) && state.createButtonEnabled && (param.value == 2)) { - log.warn "Only 'pushed', 'up_2x', and 'down_2x' button events are supported when Paddle Control is set to Toggle." - } - } if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) @@ -302,7 +279,6 @@ def parse(String description) { } else { logDebug "Unable to parse description: $description" } - sendEvent(name: "lastCheckIn", value: convertToLocalTimeString(new Date()), displayed: false) } catch (e) { log.error "$e" @@ -311,8 +287,8 @@ def parse(String description) { } def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + logTrace "SecurityMessageEncapsulation: ${cmd}" def encapCmd = cmd.encapsulatedCommand(commandClassVersions) - def result = [] if (encapCmd) { result += zwaveEvent(encapCmd) @@ -323,11 +299,9 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat } def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { - logTrace "ConfigurationReport ${cmd}" - + logTrace "ConfigurationReport: ${cmd}" sendEvent(name: "syncStatus", value: "Syncing...", displayed: false) runIn(4, refreshSyncStatus) - def param = configParams.find { it.num == cmd.parameterNumber } if (param) { def val = cmd.scaledConfigurationValue @@ -345,7 +319,7 @@ def refreshSyncStatus() { } def zwaveEvent(physicalgraph.zwave.Command cmd) { - logDebug "Ignored Command: $cmd" + logDebug "Unhandled zwaveEvent: $cmd" return [] } @@ -357,7 +331,8 @@ private secureCmd(cmd) { return cmd.format() } } catch (ex) { - log.error("caught exception", ex) + log.error("secureCmd exception", ex) + return cmd.format() } } @@ -458,7 +433,7 @@ private getMaximumBrightnessParam() { return getParam(12, "Maximum Brightness(0, Disabled; 1 - 99:1% - 99%)", 1, 99, null,"0..99") } -private getParam(num, name, size, defaultVal, options=null, range=null) { +private getParam(num, name, size, defaultVal, options = null, range = null) { def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) def map = [num: num, name: name, size: size, value: val] if (options) { @@ -508,7 +483,7 @@ private static validateRange(val, defaultVal, lowVal, highVal) { } } -private static safeToInt(val, defaultVal=0) { +private static safeToInt(val, defaultVal = 0) { return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal } @@ -575,13 +550,13 @@ def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - logTrace "${cmd}" + logTrace "BasicReport: ${cmd}" sendSwitchEvents(cmd.value, "physical") return [] } def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { - logTrace "${cmd}" + logTrace "SwitchMultilevelReport: ${cmd}" sendSwitchEvents(cmd.value, "digital") return [] } @@ -592,20 +567,18 @@ private sendSwitchEvents(rawVal, type) { if (rawVal) { sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") } - if (isButtonAvailable()) { - def paddlesReversed = (paddleControlParam.value == 1) - if (state.createButtonEnabled && (type == "physical") && childDevices) { - if (paddleControlParam.value == 2) { - sendButtonEvent("pushed") - } else { - def btnVal = ((rawVal && !paddlesReversed) || (!rawVal && paddlesReversed)) ? "up" : "down" - def oldSwitch = device.currentValue("switch") - def oldLevel = device.currentValue("level") - if ((oldSwitch == "on") && (btnVal == "up") && (oldLevel > rawVal)) { - btnVal = "down" - } - sendButtonEvent(btnVal) + if (isButtonAvailable() && type == "physical") { + if (paddleControlParam.value == 2) { + sendButtonEvent("pushed") + } else { + def paddlesReversed = (paddleControlParam.value == 1) + def btnVal = ((rawVal && !paddlesReversed) || (!rawVal && paddlesReversed)) ? "up" : "down" + def oldSwitch = device.currentValue("switch") + def oldLevel = device.currentValue("level") + if ((oldSwitch == "on") && (btnVal == "up") && (oldLevel > rawVal)) { + btnVal = "down" } + sendButtonEvent(btnVal) } } } From e16906e271ed6c6bb9b250e0dfe588858d1c91b5 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Sat, 4 Sep 2021 14:22:14 +0800 Subject: [PATCH 284/422] Syntax format compliance adjustment --- .../min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 73e1382aef1..be5244bdc4d 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -12,7 +12,7 @@ * * 2.1.0 (09/04/2021) * - remove the preferences item "createButton", Fixedly create a child button - * Restrict its use based on fingerprints-because the child buttons are not visible to the user . + * Restrict its use based on fingerprints-because the child buttons is not visible to the user . * - Simplify the code, Syntax format compliance adjustment * * 2.0.2 (09/02/2021) From 5a0e2d4ca0a9c6cdc7a66cf10f75f951ac5fe9dc Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Mon, 6 Sep 2021 21:37:59 +0800 Subject: [PATCH 285/422] Delete devicetypes/sky-nie/child-button.src directory The file will be merged from another branch, so delete in this branch --- .../child-button.src/child-button.groovy | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 devicetypes/sky-nie/child-button.src/child-button.groovy diff --git a/devicetypes/sky-nie/child-button.src/child-button.groovy b/devicetypes/sky-nie/child-button.src/child-button.groovy deleted file mode 100644 index a3e0c370ceb..00000000000 --- a/devicetypes/sky-nie/child-button.src/child-button.groovy +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Child Button v1.0 (CHILD DEVICE) - * - * Author: - * winnie (sky-nie) - * - * Changelog: - * - * 1.0 (03/16/2020) - * - Initial Release - * - * Reference: - * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/component-button.src/component-button.groovy - * - * 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: "Child Button", namespace: "sky-nie", author: "winnie", ocfDeviceType: "x.com.st.d.remotecontroller") { - capability "Button" - capability "Sensor" - } - - tiles(scale: 2) { - multiAttributeTile(name: "button", type: "generic", width: 6, height: 4, canChangeIcon: true) { - tileAttribute("device.button", key: "PRIMARY_CONTROL") { - attributeState "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff" - } - } - main "button" - details(["button"]) - } -} - -def installed() { - log.debug "installed()..." -} - -def updated() { - log.debug "updated()..." -} - -def uninstalled() { - log.warn "uninstalled()..." -} \ No newline at end of file From 5a8b41393b1b7c3e843b2d1ebdbdd0a91713cccc Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Tue, 7 Sep 2021 00:42:03 +0800 Subject: [PATCH 286/422] * - Syntax format compliance adjustment * - delete dummy code --- .../min-smart-plug-dimmer.groovy | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index be5244bdc4d..3aae44fc02e 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -1,5 +1,5 @@ /** - * Min Smart Plug Dimmer v2.1.0 + * Min Smart Plug Dimmer v2.1.1 * * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) * @@ -10,6 +10,10 @@ * * Changelog: * + * 2.1.1 (09/07/2021) + * - Syntax format compliance adjustment + * - delete dummy code + * * 2.1.0 (09/04/2021) * - remove the preferences item "createButton", Fixedly create a child button * Restrict its use based on fingerprints-because the child buttons is not visible to the user . @@ -86,6 +90,7 @@ metadata { capability "Configuration" capability "Refresh" capability "Health Check" + capability "Button" attribute "firmwareVersion", "string" attribute "lastCheckIn", "string" @@ -179,13 +184,15 @@ private sendButtonEvent(value) { def ping() { logDebug "ping()..." - return [ switchMultilevelGetCmd() ] + sendHubCommand(switchMultilevelGetCmd()) + return [] } def refresh() { logDebug "refresh()..." refreshSyncStatus() - return [ switchMultilevelGetCmd() ] + sendHubCommand(switchMultilevelGetCmd()) + return [] } private switchMultilevelGetCmd() { @@ -203,20 +210,6 @@ def installed() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } -def uninstalled() { - logger("debug", "uninstalled()") - removeChildButton(childDevices[0]) -} - -private removeChildButton(child) { - try { - log.warn "Removing ${child.displayName}} " - deleteChildDevice(child.deviceNetworkId) - } catch (ex) { - log.error("Unable to remove ${child.displayName}! Make sure that the device is not being used by any SmartApps.", ex) - } -} - private static def getCheckInterval() { // These are battery-powered devices, and it's not very critical // to know whether they're online or not – 12 hrs @@ -510,12 +503,14 @@ private logTrace(msg) { def on() { logDebug "on()..." - return [ basicSetCmd(0xFF) ] + sendHubCommand(basicSetCmd(0xFF)) + return [] } def off() { logDebug "off()..." - return [ basicSetCmd(0x00) ] + sendHubCommand(basicSetCmd(0x00)) + return [] } def setLevel(level) { @@ -528,7 +523,8 @@ def setLevel(level, duration) { if (duration > 30) { duration = 30 } - return [ switchMultilevelSetCmd(level, duration) ] + sendHubCommand(switchMultilevelSetCmd(level, duration)) + return [] } private basicSetCmd(val) { From 2dfa9f5656215806aba07dc70a23b5ef113c8047 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Tue, 7 Sep 2021 09:43:38 +0800 Subject: [PATCH 287/422] add removeChildButton in updated() when isButtonAvailable() return false --- .../min-smart-plug-dimmer.groovy | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 3aae44fc02e..074f8a451ec 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -210,6 +210,15 @@ def installed() { sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } +private removeChildButton(child) { + try { + log.warn "Removing ${child.displayName}} " + deleteChildDevice(child.deviceNetworkId) + } catch (ex) { + log.error("Unable to remove ${child.displayName}! Make sure that the device is not being used by any SmartApps.", ex) + } +} + private static def getCheckInterval() { // These are battery-powered devices, and it's not very critical // to know whether they're online or not – 12 hrs @@ -223,6 +232,9 @@ def updated() { if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } + if(!isButtonAvailable() && childDevices){ + removeChildButton(childDevices[0]) + } runIn(5, executeConfigureCmds, [overwrite: true]) } return [] From 9009786f6f41389ae782427771f597631029d394 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Tue, 7 Sep 2021 11:04:24 +0800 Subject: [PATCH 288/422] restore addChildButton in updated() when it' needed. --- .../min-smart-plug-dimmer.groovy | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 074f8a451ec..433e3baf2fb 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -179,7 +179,9 @@ def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotificat } private sendButtonEvent(value) { - childDevices[0].sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) + if (childDevices) { + childDevices[0].sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) + } } def ping() { @@ -201,24 +203,9 @@ private switchMultilevelGetCmd() { def installed() { logDebug "installed()..." - try { - def child = addChildButton() - child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) - } catch (ex) { - log.error("Unable to create button device because the 'Child Button' DTH is not installed",ex) - } sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) } -private removeChildButton(child) { - try { - log.warn "Removing ${child.displayName}} " - deleteChildDevice(child.deviceNetworkId) - } catch (ex) { - log.error("Unable to remove ${child.displayName}! Make sure that the device is not being used by any SmartApps.", ex) - } -} - private static def getCheckInterval() { // These are battery-powered devices, and it's not very critical // to know whether they're online or not – 12 hrs @@ -232,8 +219,14 @@ def updated() { if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } - if(!isButtonAvailable() && childDevices){ - removeChildButton(childDevices[0]) + + if (isButtonAvailable() && !childDevices) { + try { + def child = addChildButton() + child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } catch (ex) { + log.error("Unable to create button device because the 'Child Button' DTH is not installed", ex) + } } runIn(5, executeConfigureCmds, [overwrite: true]) } From 24b2652c824467720337f0a30e71a070e69a2275 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Tue, 7 Sep 2021 18:09:56 +0800 Subject: [PATCH 289/422] * - Syntax format compliance adjustment * - delete dummy code --- .../min-smart-plug-dimmer.groovy | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 433e3baf2fb..5ff2149f18f 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -186,15 +186,13 @@ private sendButtonEvent(value) { def ping() { logDebug "ping()..." - sendHubCommand(switchMultilevelGetCmd()) - return [] + return [ switchMultilevelGetCmd() ] } def refresh() { logDebug "refresh()..." refreshSyncStatus() - sendHubCommand(switchMultilevelGetCmd()) - return [] + return [ switchMultilevelGetCmd() ] } private switchMultilevelGetCmd() { @@ -219,11 +217,9 @@ def updated() { if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } - - if (isButtonAvailable() && !childDevices) { + if (isButtonAvailable() && !childDevices) { try { - def child = addChildButton() - child?.sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + addChildButton() } catch (ex) { log.error("Unable to create button device because the 'Child Button' DTH is not installed", ex) } @@ -508,14 +504,12 @@ private logTrace(msg) { def on() { logDebug "on()..." - sendHubCommand(basicSetCmd(0xFF)) - return [] + return [ basicSetCmd(0xFF) ] } def off() { logDebug "off()..." - sendHubCommand(basicSetCmd(0x00)) - return [] + return [ basicSetCmd(0x00) ] } def setLevel(level) { @@ -528,8 +522,7 @@ def setLevel(level, duration) { if (duration > 30) { duration = 30 } - sendHubCommand(switchMultilevelSetCmd(level, duration)) - return [] + return [ switchMultilevelSetCmd(level, duration) ] } private basicSetCmd(val) { From 8069f29f19f245a88938b8727f1ee0a6e132b1a2 Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Fri, 10 Sep 2021 09:54:34 +0800 Subject: [PATCH 290/422] Syntax format compliance adjustment as PR review --- .../min-smart-plug-dimmer.groovy | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 5ff2149f18f..4f5a9fbc634 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -82,7 +82,7 @@ import groovy.json.JsonOutput metadata { - definition (name: "Min Smart Plug Dimmer", namespace: "sky-nie", author: "winnie", ocfDeviceType: "oic.d.smartplug") { + definition (name: "Min Smart Plug Dimmer", namespace: "sky-nie", author: "winnie", mnmn: "SmartThings", vid:"generic-dimmer") { capability "Actuator" capability "Sensor" capability "Switch" @@ -90,19 +90,18 @@ metadata { capability "Configuration" capability "Refresh" capability "Health Check" - capability "Button" attribute "firmwareVersion", "string" attribute "lastCheckIn", "string" attribute "syncStatus", "string" - fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Dimmer Switch" //MP21ZD - fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Dimmer Switch" //MP22ZD - fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "New One Dimmer Switch" //N4002 - fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS11ZS Minoston Smart Dimmer Switch - fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //MS13ZS Minoston Smart Toggle Dimmer Switch - fingerprint mfr: "0312", prod: "BB00", model: "BB02", deviceJoinName: "Evalogik Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //ZW31S Evalogik Smart Dimmer Switch - fingerprint mfr: "0312", prod: "BB00", model: "BB04", deviceJoinName: "Evalogik Dimmer Switch", mnmn: "SmartThings", vid:"generic-dimmer" //ZW31TS Evalogik Smart Toggle Dimmer Switch + fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Smart Plug Dimmer", ocfDeviceType: "oic.d.smartplug" //MP21ZD + fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Outdoor Dimmer", ocfDeviceType: "oic.d.smartplug" //MP22ZD + fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "New One Smart Plug Dimmer", ocfDeviceType: "oic.d.smartplug" //N4002 + fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", ocfDeviceType: "oic.d.switch" //MS11ZS Minoston Smart Dimmer Switch + fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", ocfDeviceType: "oic.d.switch" //MS13ZS Minoston Smart Toggle Dimmer Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB02", deviceJoinName: "Evalogik Dimmer Switch", ocfDeviceType: "oic.d.switch" //ZW31S Evalogik Smart Dimmer Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB04", deviceJoinName: "Evalogik Dimmer Switch", ocfDeviceType: "oic.d.switch" //ZW31TS Evalogik Smart Toggle Dimmer Switch } preferences { @@ -140,7 +139,7 @@ private addChildButton() { def child = addChildDevice( "sky-nie", "Child Button", - "${device.deviceNetworkId}-2", + "${device.deviceNetworkId}:2", device.getHub().getId(), [ completedSetup: true, From 1d1faa5c4326f56007dd4edf8ec663b5bfbcf8f1 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Mon, 13 Sep 2021 14:27:01 -0400 Subject: [PATCH 291/422] DevWs for Zooz (The Smartest House) containing containing Zooz Double Switch ZEN30 (#74859) * DevWs for Zooz (The Smartest House) containing containing Zooz Double Switch ZEN30 * made requested changes * Moved raw description above fingerprint --- .../zooz-double-switch-zen30.groovy | 482 ++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 devicetypes/zooz/zooz-double-switch-zen30.src/zooz-double-switch-zen30.groovy diff --git a/devicetypes/zooz/zooz-double-switch-zen30.src/zooz-double-switch-zen30.groovy b/devicetypes/zooz/zooz-double-switch-zen30.src/zooz-double-switch-zen30.groovy new file mode 100644 index 00000000000..5310dc07a2e --- /dev/null +++ b/devicetypes/zooz/zooz-double-switch-zen30.src/zooz-double-switch-zen30.groovy @@ -0,0 +1,482 @@ +/* + * Zooz Double Switch ZEN30 + * + * Changelog: + * + * 2021-08-30 + * - Requested changes + * 2021-08-28 + * - Publication Release + * + * Copyright 2021 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x20: 1, // Basic + 0x25: 1, // SwitchBinary + 0x26: 3, // SwitchMultilevel + 0x55: 1, // TransportService + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5B: 1, // CentralScene + 0x5E: 2, // ZwaveplusInfo + 0x60: 3, // MultiChannel + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version + 0x8E: 2, // MultiChannelAssociation + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 +] + +@Field static int supervisionCC = 108 +@Field static int upperPaddle = 1 +@Field static int lowerPaddle = 2 +@Field static int relayButton = 3 +@Field static int btnPushed = 0 +@Field static int btnReleased = 1 +@Field static int btnHeld = 2 +@Field static Map endpoints = [dimmer: 0, relay: 1] + +@Field static List supportedButtonValues = ["pushed","held","pushed_2x","pushed_3x","pushed_4x","pushed_5x","down","down_hold","down_2x","down_3x","down_4x","down_5x","up","up_hold","up_2x","up_3x","up_4x","up_5x"] + +@Field static Map configParams = [ + powerFailureParam: [num:12, title:"On Off Status After Power Failure", size:1, defaultVal:3, options:[0:"Dimmer Off / Relay Off", 1:"Dimmer Off / Relay On", 2:"Dimmer On / Relay Off", 3:"Dimmer Remember / Relay Remember [DEFAULT]", 4:"Dimmer Remember / Relay On", 5:"Dimmer Remember / Relay Off", 6:"Dimmer On / Relay Remember", 7:"Dimmer Off / Relay Remember", 8:"Dimmer On / Relay On"]], + ledSceneControlParam: [num:7, title:"LED Indicator Mode for Scene Control", size:1, defaultVal:1, options:[0:"LED Enabled", 1:"LED Disabled [DEFAULT]"]], + relayLedModeParam: [num:2, title:"Relay LED Indicator Mode", size:1, defaultVal:0, options:[0:"On When Off [DEFAULT]", 1:"On When On", 2:"Always Off", 3:"Always On"]], + relayLedColorParam: [num:4, title:"Relay LED Indicator Color", size:1, defaultVal:0, options:[0:"White [DEFAULT]", 1:"Blue", 2:"Green", 3:"Red"]], + relayLedBrightnessParam: [num:6, title:"Relay LED Indicator Brightness", size:1, defaultVal:1, options:[0:"100%", 1:"60% [DEFAULT]", 2:"30%"]], + relayAutoOffParam: [num:10, title:"Relay Auto Turn-Off Timer (Minutes)", size:4, defaultVal:0, range:"0..65535"], + relayAutoOnParam: [num:11, title:"Relay Auto Turn-On Timer (Minutes)", size:4, defaultVal:0, range:"0..65535"], + relayLoadControlParam: [num:20, title:"Relay Load Control", size:1, defaultVal:1, options:[0:"Physical Disabled", 1:"Physical / Digital Enabled [DEFAULT]", 2:"Physical / Digital Disabled"]], + relayPhysicalDisabledBehaviorParam: [num:25, title:"Relay Physical Disabled Behavior [FIRMWARE >= 1.05]", size:1, defaultVal:0, options:[0:"Change Status/LED [DEFAULT]", 1:"Don't Change Status/LED"], minFirmware: 1.05], + dimmerLedModeParam: [num:1, title:"Dimmer LED Indicator Mode", size:1, defaultVal:0, options:[0:"On When Off [DEFAULT]", 1:"On When On", 2:"Always Off", 3:"Always On"]], + dimmerLedColorParam: [num:3, title:"Dimmer LED Indicator Color", size:1, defaultVal:0, options:[0:"White [DEFAULT]", 1:"Blue", 2:"Green", 3:"Red"]], + dimmerLedBrightnessParam: [num:5, title:"Dimmer LED Indicator Brightness", size:1, defaultVal:1, options:[0:"100%", 1:"60% [DEFAULT]", 2:"30%"]], + dimmerAutoOffParam: [num:8, title:"Dimmer Auto Turn-Off Timer (Minutes)", size:4, defaultVal:0, range:"0..65535"], + dimmerAutoOnParam: [num:9, title:"Dimmer Auto Turn-On Timer (Minutes)", size:4, defaultVal:0, range:"0..65535"], + dimmerRampRateParam: [num:13, title:"Dimmer Physical Ramp Rate (Seconds)", size:1, defaultVal:1, range:"0..99"], + dimmerPaddleHeldRampRateParam: [num:21, title:"Dimming Speed when Paddle is Held (Seconds)", size:1, defaultVal:4, range:"1..99"], + dimmerMinimumBrightnessParam: [num:14, title:"Dimmer Minimum Brightness (%)", size:1, defaultVal:1, range:"1..99"], + dimmerMaximumBrightnessParam: [num:15, title:"Dimmer Maximum Brightness (%)", size:1, defaultVal:99, range:"1..99"], + dimmerCustomBrightnessParam: [num:23, title:"Custom Brightness (%)", size:1, defaultVal:0, range:"0..99"], + dimmerBrightnessControlParam: [num:18, title:"Dimmer Brightness Control", size:1, defaultVal:0, options:[0:"Double Tap Maximum [DEFAULT]", 1:"Single Tap Custom", 2:"Single Tap Maximum"]], + dimmerDoubleTapFunctionParam: [num:17, title:"Dimmer Double Tap Function", size:1, defaultVal:0, options:[0:"Turn on Full Brightness [DEFAULT]", 1:"Turn on Maximum Brightness"]], + dimmerLoadControlParam: [num:19, title:"Dimmer Load Control", size:1, defaultVal:1, options:[0:"Physical Disabled", 1:"Physical / Digital Enabled [DEFAULT]", 2:"Physical / Digital Disabled"]], + dimmerPhysicalDisabledBehaviorParam: [num:24, title:"Dimmer Physical Disabled Behavior [FIRMWARE >= 1.05]", size:1, defaultVal:0, options:[0:"Change Status/LED [DEFAULT]", 1:"Don't Change Status/LED"], minFirmware:1.05], + dimmerNightModeBrightnessParam: [num:26, title:"Night Mode Brightness (%) [FIRMWARE >= 1.05]", size:1, defaultVal:20, range:"0..99", minFirmware:1.05], + dimmerPaddleControlParam: [num:27, title:"Paddle Orientation for Dimmer [FIRMWARE >= 1.05]", size:1, defaultVal:0, options:[0:"Normal [DEFAULT]", 1:"Reverse", 2:"Toggle"], minFirmware:1.05] +] + +metadata { + definition ( + name: "Zooz Double Switch ZEN30", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType: "oic.d.light", + mnmn: "SmartThingsCommunity", + vid: "8e189c52-eb8b-36e4-b9e2-2ba459caa6af" + ) { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Switch Level" + capability "Configuration" + capability "Refresh" + capability "Health Check" + capability "Button" + capability "platemusic11009.firmware" + capability "platemusic11009.syncStatus" + + //zw:Ls2 type:1101 mfr:027A prod:A000 model:A008 ver:2.00 zwv:5.03 lib:03 cc:5E,6C,55,9F sec:86,26,25,85,8E,59,72,5A,73,5B,60,70,7A epc:1 + fingerprint mfr: "027A", prod: "A000", model: "A008", deviceJoinName: "Zooz Switch" //Zooz Double Switch ZEN30 + } + + preferences { + configParams.each { name, param -> + if (param.options) { + input name, "enum", + title: param.title, + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input name, "number", + title: param.title, + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + + input "debugLogging", "enum", + title: "Logging:", + required: false, + defaultValue: "1", + options: ["0":"Disabled", "1":"Enabled [DEFAULT]"] + } +} + +def installed() { + logDebug "installed()..." + initialize() +} + +def updated() { + logDebug "updated()..." + initialize() + configure() +} + +void initialize() { + state.debugLoggingEnabled = (safeToInt(settings?.debugLogging, 1) != 0) + + refreshSyncStatus() + + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((60 * 60 * 3) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + if (!device.currentValue("supportedButtonValues")) { + sendEvent(name:"supportedButtonValues", value:supportedButtonValues.encodeAsJSON(), displayed:false) + } + + if (!device.currentValue("numberOfButtons")) { + sendEvent(name:"numberOfButtons", value:1, displayed:false) + } + + if (!device.currentValue("button")) { + sendButtonEvent("pushed") + } + + if (!childDevices) { + addChildDevice( + "smartthings", + "Child Switch", + "${device.deviceNetworkId}:${endpoints.relay}", + null, + [ + completedSetup: true, + label: "${device.displayName} Relay", + isComponent: false + ] + ) + refresh() + } +} + +def configure() { + logDebug "configure()..." + List cmds = [] + BigDecimal firmware = safeToDec(device.currentValue("firmwareVersion"), 0.0) + + if (device.currentValue("firmwareVersion") == null) { + cmds << secureCmd(zwave.versionV1.versionGet()) + } + + configParams.each { name, param -> + if (firmwareSupportsParam(firmware, param)) { + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if (storedVal != settingVal) { + logDebug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: settingVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + } + if (cmds) { + sendHubCommand(cmds, 500) + } +} + +def ping() { + logDebug "ping()..." + return [ multiChannelCmdEncapCmd(zwave.switchMultilevelV3.switchMultilevelGet(), endpoints.dimmer) ] +} + +def on() { + logDebug "on()..." + return getSetLevelCmds(state.lastLevel) +} + +def off() { + logDebug "off()..." + return getSetLevelCmds(0x00) +} + +def setLevel(level, duration=null) { + logDebug "setLevel($level, $duration)..." + return getSetLevelCmds(level, duration) +} + +List getSetLevelCmds(level, duration=null) { + state.expectedLevel = level + def levelVal = validateRange(level, 99, 0, 99) + def durationVal = validateRange(duration, 1, 0, 30) + return [ + multiChannelCmdEncapCmd(zwave.switchMultilevelV3.switchMultilevelSet(dimmingDuration: durationVal, value: levelVal), endpoints.dimmer) + ] +} + +def refresh() { + logDebug "refresh()..." + refreshSyncStatus() + + if (device.currentValue("syncStatus") != "Synced") { + configure() + } + + return sendHubCommand([ + multiChannelCmdEncapCmd(zwave.switchMultilevelV3.switchMultilevelGet(), endpoints.dimmer), + multiChannelCmdEncapCmd(zwave.switchBinaryV1.switchBinaryGet(), endpoints.relay), + secureCmd(zwave.versionV1.versionGet()) + ], 500) +} + +def childOn(dni) { + logDebug "childOn(${dni})..." + sendHubCommand([ + multiChannelCmdEncapCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF), endpoints.relay) + ]) +} + +def childOff(dni) { + logDebug "childOff(${dni})..." + sendHubCommand([ + multiChannelCmdEncapCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00), endpoints.relay) + ]) +} + +String multiChannelCmdEncapCmd(cmd, endpoint) { + if (endpoint) { + return secureCmd(zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:safeToInt(endpoint)).encapsulate(cmd)) + } else { + return secureCmd(cmd) + } +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + // Workaround that was added to all SmartThings Multichannel DTHs. + if ((cmd.commandClass == supervisionCC) && (cmd.parameter.size >= 4)) { // Supervision encapsulated Message + // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header + cmd.parameter = cmd.parameter.drop(2) + // Updated Command Class/Command now with the remaining bytes + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint) + } else { + logDebug "Unable to get encapsulated command: $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + runIn(4, refreshSyncStatus) + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + int val = cmd.scaledConfigurationValue + state[name] = val + logDebug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${val}" + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logDebug "${cmd}" + sendEvent(name: "firmwareVersion", value: (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, endpoint=0) { + logDebug "${cmd} (${endpoint})" + sendSwitchEvents(cmd.value, endpoint) +} + +void zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint=0) { + logDebug "${cmd} (${endpoint})" + sendSwitchEvents(cmd.value, endpoint) +} + +void zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd, endpoint=0) { + logDebug "${cmd} (${endpoint})" + sendSwitchEvents(cmd.value, endpoint) +} + +void sendSwitchEvents(rawVal, Integer endpoint) { + String switchVal = rawVal ? "on" : "off" + if (endpoint == endpoints.dimmer) { + logDebug "switch is ${switchVal}" + sendEvent(name: "switch", value: switchVal) + + int level = (state.expectedLevel == 100 ? 100 : rawVal) + sendEvent(name: "level", value: level, unit: "%") + if (level > 0) { + state.lastLevel = level + } + state.expectedLevel = null + } else { + def child = childDevices[0] + if ((child != null) && (child.currentValue("switch") != switchVal)) { + logDebug "${child.displayName} switch is ${switchVal}" + child.sendEvent(name: "switch", value: switchVal) + } + } +} + +void zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + if (state.lastSequenceNumber != cmd.sequenceNumber) { + state.lastSequenceNumber = cmd.sequenceNumber + + String actionType + String btnVal + String displayName = "" + + switch (cmd.sceneNumber) { + case upperPaddle: + actionType = "up" + break + case lowerPaddle: + actionType = "down" + break + case relayButton: + actionType = "pushed" + displayName = "${childDevices[0]?.displayName} " + } + + switch (cmd.keyAttributes){ + case btnPushed: + btnVal = actionType + break + case btnReleased: + // btnVal = (cmd.sceneNumber == relayButton) ? "released" : "${actionType}_released" + logDebug "Button Value 'released' is not supported by SmartThings" + break + case btnHeld: + btnVal = (actionType == "pushed") ? "held" : "${actionType}_hold" + break + default: + btnVal = "${actionType}_${cmd.keyAttributes - 1}x" + } + + if (btnVal) { + logDebug "${displayName} Button ${btnVal}" + sendButtonEvent(btnVal) + } + } +} + +void sendButtonEvent(String value) { + sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: $cmd" +} + +void refreshSyncStatus() { + int changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +Integer getPendingChanges() { + BigDecimal firmware = safeToDec(device.currentValue("firmwareVersion"), 0.0) + return configParams.count { name, param -> + ((firmwareSupportsParam(firmware, param)) && (getSettingVal(name) != getStoredVal(name))) + } +} + +Integer getSettingVal(String name) { + return (settings ? safeToInt(settings[name], null) : null) +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +boolean firmwareSupportsParam(BigDecimal firmware, Map param) { + return (firmware >= safeToDec(param.minFirmware, 0.0)) +} + +Integer validateRange(val, Integer defaultVal, Integer lowVal, Integer highVal) { + Integer intVal = safeToInt(val, defaultVal) + if (intVal > highVal) { + return highVal + } else if (intVal < lowVal) { + return lowVal + } else { + return intVal + } +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +BigDecimal safeToDec(val, BigDecimal defaultVal=0) { + return "${val}"?.isBigDecimal() ? "${val}".toBigDecimal() : defaultVal +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file From d5821c36ba4d45037a0e27a2618601fc2587b9fe Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Mon, 13 Sep 2021 13:17:41 -0700 Subject: [PATCH 292/422] MS7 and MS6 have different sizes for parameter 101/102 MultiSensor 7 uses size 1 for parameter 101/102 MultiSensor 6 uses size 4 for parameter 101/102 Having Parameter 101/102 sent with size 1 for both sensor causes configuration issues for MultiSensor 6. This fixes the issue in regards to MultiSensor 6 configuration. --- .../aeon-multisensor-6.groovy | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy index aca2b1b55cf..d11c01618d8 100644 --- a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy +++ b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy @@ -407,15 +407,6 @@ def configure() { //1. set association groups for hub - 2 groups are used to set battery refresh interval different than sensor report interval request << zwave.associationV1.associationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId) - //2. automatic report flags - // param 101 -103 [4 bytes] 128: light sensor, 64 humidity, 32 temperature sensor, 15 ultraviolet sensor, 1 battery sensor - // set value 241 (default for 101) to get all reports. Set value 0 for no reports (default for 102-103) - //association group 1 - request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 1, scaledConfigurationValue: 240) - - //association group 2 - request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 1, scaledConfigurationValue: 1) - // Expedite this if we know this info so that we can execute the code below if (!state.MSR && zwaveInfo?.mfr && zwaveInfo.prod && zwaveInfo.model) { state.MSR = "${zwaveInfo.mfr}-${zwaveInfo.prod}-${zwaveInfo.model}" @@ -425,6 +416,15 @@ def configure() { case "0086-0002-0064": // MultiSensor 6 EU case "0086-0102-0064": // MultiSensor 6 US case "0086-0202-0064": // MultiSensor 6 AU + //2. automatic report flags + // param 101 -103 [4 bytes] 128: light sensor, 64 humidity, 32 temperature sensor, 15 ultraviolet sensor, 1 battery sensor + // set value 241 (default for 101) to get all reports. Set value 0 for no reports (default for 102-103) + //association group 1 + request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 240) + + //association group 2 + request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 1) + //3. no-motion report x seconds after motion stops (default 20 secs) request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 20) @@ -445,6 +445,15 @@ def configure() { case "0371-0002-0018": // MultiSensor 7 EU case "0371-0102-0018": // MultiSensor 7 US case "0371-0202-0018": // MultiSensor 7 AU + //2. automatic report flags + // param 101 -103 [4 bytes] 128: light sensor, 64 humidity, 32 temperature sensor, 15 ultraviolet sensor, 1 battery sensor + // set value 241 (default for 101) to get all reports. Set value 0 for no reports (default for 102-103) + //association group 1 + request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 1, scaledConfigurationValue: 240) + + //association group 2 + request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 1, scaledConfigurationValue: 1) + //3. no-motion report x seconds after motion stops (default 30 secs) request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 30) From 33537e590fcb4cea4b13d491bea1da4f82fbc7a6 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Thu, 16 Sep 2021 07:09:06 +0900 Subject: [PATCH 293/422] WWST-5862, added FP for the SAMSUNG LED(Fan lightings) (#68445) * Update zigbee-rgbw-bulb.groovy * Create .st-ignore 2021.06.18 Ceiling fan light added * Create README.md 2021.06.18 first report * Create led-fan-lightings.groovy 2021.06.18 create first main lightings file * Create itm-fan-child.groovy 2021.06.18 first time : create a child device(fan) file * Delete .st-ignore 20210708 delete * Update itm-fan-child.groovy 20210728 clean code update * Update led-fan-lightings.groovy 20210727 clean code update * Update led-fan-lightings.groovy Edit code style * Update led-fan-lightings.groovy Edit code style * Update itm-fan-child.groovy Edit code style * Update led-fan-lightings.groovy Edit code style * Update led-fan-lightings.groovy Edit code style * Update itm-fan-child.groovy Edit code style * Update led-fan-lightings.groovy change code style * Update led-fan-lightings.groovy Change code style * Update led-fan-lightings.groovy Update code style * Update led-fan-lightings.groovy Edit code style * Update led-fan-lightings.groovy Change code style * Update led-fan-lightings.groovy Change point (4 codes) "${device.deviceNetworkId}-Fan" to "${device.deviceNetworkId}:1" * Update itm-fan-child.groovy Change code - Add capability "Switch Level" - Add function setLevel * Update led-fan-lightings.groovy Change code style * Update itm-fan-child.groovy Change code style * Update itm-fan-child.groovy Add a comment for capability "Switch Level" * Update itm-fan-child.groovy Code update * Update led-fan-lightings.groovy Code update * Update itm-fan-child.groovy Align codes. * Update led-fan-lightings.groovy Edit codes * Update led-fan-lightings.groovy Clean up the codes * Update itm-fan-child.groovy Clean up the codes * Update itm-fan-child.groovy Fix the codes * Update itm-fan-child.groovy Clean up the codes * Update led-fan-lightings.groovy Edit the name of zigbeeMap.name * Update led-fan-lightings.groovy Edit codes * Update led-fan-lightings.groovy Edit code * Update led-fan-lightings.groovy Edit codes * Update itm-fan-child.groovy Edit year * Update led-fan-lightings.groovy Edit year * Update led-fan-lightings.groovy Edit code * Update itm-fan-child.groovy Edit code * Update itm-fan-child.groovy Edit code * Update itm-fan-child.groovy Align codes * Update led-fan-lightings.groovy Align codes * Update itm-fan-child.groovy Align codes * Update led-fan-lightings.groovy Align codes * Update itm-fan-child.groovy Align code * Update led-fan-lightings.groovy Edit code * Update led-fan-lightings.groovy Fix codes * Update led-fan-lightings.groovy Fix codes * Update led-fan-lightings.groovy Delete log.bebug * * Update itm-fan-child.groovy Delete log.debug * * Update led-fan-lightings.groovy Fix the configure function * Update led-fan-lightings.groovy Edit codes * Update itm-fan-child.groovy Clear codes * Update itm-fan-child.groovy Clear codes * Update led-fan-lightings.groovy Fix the code * Update itm-fan-child.groovy Clear codes * Update zigbee-rgbw-bulb.groovy Add the finger print for ABL Juno connect E-series * Update led-fan-lightings.groovy Edit addChildFan function * Update led-fan-lightings.groovy Clear codes --- .../zigbee-ceiling-fan-light.src/README.md | 29 ++++ .../itm-fan-child.groovy | 67 +++++++ .../led-fan-lightings.groovy | 163 ++++++++++++++++++ .../zigbee-rgbw-bulb.groovy | 3 + 4 files changed, 262 insertions(+) create mode 100644 devicetypes/smartthings/zigbee-ceiling-fan-light.src/README.md create mode 100644 devicetypes/smartthings/zigbee-ceiling-fan-light.src/itm-fan-child.groovy create mode 100644 devicetypes/smartthings/zigbee-ceiling-fan-light.src/led-fan-lightings.groovy diff --git a/devicetypes/smartthings/zigbee-ceiling-fan-light.src/README.md b/devicetypes/smartthings/zigbee-ceiling-fan-light.src/README.md new file mode 100644 index 00000000000..d22c4562f2c --- /dev/null +++ b/devicetypes/smartthings/zigbee-ceiling-fan-light.src/README.md @@ -0,0 +1,29 @@ +# ZigBee Ceiling Fan Light + +Cloud Execution + +Works with: + +* Samsung ITM + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) + +## Capabilities + +* **Actuator** - represents that a Device has commands* +* **Configuration** - _configure()_ command called when device is installed or device preferences updated. +* **Refresh** - _refresh()_ command for status updates +* **Switch** - can detect state (possible values: on/off) +* **Switch Level** - represents current light level, usually 0-100 in percent +* **Health Check** - indicates ability to get device health notifications +* **Fan Speed** - represents current fan speed, 0 - 4(Off, Low, Mid, High, Max) + +## Device Health + +Zigbee Bulb with reporting interval of 5 mins. +SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE` + +*__12min__ checkInterval diff --git a/devicetypes/smartthings/zigbee-ceiling-fan-light.src/itm-fan-child.groovy b/devicetypes/smartthings/zigbee-ceiling-fan-light.src/itm-fan-child.groovy new file mode 100644 index 00000000000..78c1847496d --- /dev/null +++ b/devicetypes/smartthings/zigbee-ceiling-fan-light.src/itm-fan-child.groovy @@ -0,0 +1,67 @@ +/* + * Copyright 2021 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. + * + * ZigBee Ceiling Fan Light + * + * Author: SAMSUNG LED + * Date: 2021-06-30 + */ + +import physicalgraph.zigbee.zcl.DataType +import groovy.json.JsonOutput + +metadata { + definition(name: "ITM Fan Child", namespace: "SAMSUNG LED", author: "SAMSUNG LED", ocfDeviceType: "oic.d.fan") { + capability "Actuator" + capability "Configuration" + capability "Refresh" + capability "Switch" + /* Capability "Switch Level" is used to control fan speed for platforms don't support capability "Fan speed" + * when you connect other platforms via SmartThings cloud to cloud connection. */ + capability "Switch Level" + capability "Fan Speed" + } +} + +def off() { + setFanSpeed(0x00) +} + +def on() { + setFanSpeed(0x01) +} + +def setLevel(value) { + if (value <= 1) { + setFanSpeed(0x00) + } else if (value <= 25) { + setFanSpeed(0x01) + } else if (value <= 50) { + setFanSpeed(0x02) + } else if (value <= 75) { + setFanSpeed(0x03) + } else if (value <= 100) { + setFanSpeed(0x04) + } +} + +def setFanSpeed(speed) { + parent.sendFanSpeed(speed) +} + +void refresh() { + parent.refresh() +} + +def ping() { + parent.ping() +} diff --git a/devicetypes/smartthings/zigbee-ceiling-fan-light.src/led-fan-lightings.groovy b/devicetypes/smartthings/zigbee-ceiling-fan-light.src/led-fan-lightings.groovy new file mode 100644 index 00000000000..c4126cef2c0 --- /dev/null +++ b/devicetypes/smartthings/zigbee-ceiling-fan-light.src/led-fan-lightings.groovy @@ -0,0 +1,163 @@ +/* + * Copyright 2021 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. + * + * ZigBee Ceiling Fan Light + * + * Author: SAMSUNG LED + * Date: 2021-06-30 + */ + +import physicalgraph.zigbee.zcl.DataType +import groovy.json.JsonOutput + +metadata { + definition (name: "LED FAN lightings", namespace: "SAMSUNG LED", author: "SAMSUNG LED") { + capability "Actuator" + capability "Configuration" + capability "Health Check" + capability "Refresh" + capability "Switch" + capability "Switch Level" + + // Samsung LED + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-003", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-003" + } + + // UI tile definitions + tiles(scale: 2) { + multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) { + tileAttribute("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label: '${name}', action: "off", icon: "st.switches.light.on", backgroundColor: "#00A0DC", nextState: "turningOff" + attributeState "off", label: '${name}', action: "on", icon: "st.switches.light.off", backgroundColor: "#ffffff", nextState: "turningOn" + attributeState "turningOn", label: '${name}', action: "off", icon: "st.switches.light.on", backgroundColor: "#00A0DC", nextState: "turningOff" + attributeState "turningOff", label: '${name}', action: "on", icon: "st.switches.light.off", backgroundColor: "#ffffff", nextState: "turningOn" + } + tileAttribute("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action: "switch level.setLevel" + } + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh" + } + + main(["switch"]) + details(["switch", "refresh", "switchLevel"]) + } +} + +private getFAN_CLUSTER_VALUE() { 0x0202 } +private getFAN_STATUS_VALUE() { 0x0000 } +private getON_OFF_CLUSTER_VALUE() { 0x0006 } + +def parse(String description) { + // Parse incoming device messages to generate events + def event = zigbee.getEvent(description) + if (event) { + sendEvent(event) + } else if (description?.startsWith('read attr -')) { + def zigbeeMap = zigbee.parseDescriptionAsMap(description) + if (zigbeeMap.clusterInt == FAN_CLUSTER_VALUE && + zigbeeMap.attrInt == FAN_STATUS_VALUE) { + def childDevice = childDevices.find { + //find light child device + it.device.deviceNetworkId == "${device.deviceNetworkId}:1" + } + def fanSpeedEvent = createEvent(name: "fanSpeed", value: zigbeeMap.value as Integer) + childDevice.sendEvent(fanSpeedEvent) + if (fanSpeedEvent.value == 0) { + childDevice.sendEvent(name: "switch", value: "off") + childDevice.sendEvent(name: "level", value: 0) // For cloud to cloud device UI update + } else { + childDevice.sendEvent(name: "switch", value: "on") + def int_v = fanSpeedEvent.value + int_v = int_v * 25 + int_v = int_v > 100 ? 100 : int_v + childDevice.sendEvent(name: "level", value: int_v) // For cloud to cloud device UI update + } + } + } else { + def cluster = zigbee.parse(description) + if (cluster && + cluster.clusterInt == ON_OFF_CLUSTER_VALUE && + cluster.command == 0x07) { + if (cluster.data[0] == 0x00) { + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + } + } +} + +def off() { + zigbee.off() +} + +def on() { + zigbee.on() +} + +def setLevel(value, duration) { + zigbee.setLevel(value) +} + +def sendFanSpeed(val) { + delayBetween([zigbee.writeAttribute(FAN_CLUSTER_VALUE, FAN_STATUS_VALUE, DataType.ENUM8, val), zigbee.readAttribute(FAN_CLUSTER_VALUE, FAN_STATUS_VALUE)], 100) +} + +def ping() { + // PING is used by Device-Watch in attempt to reach the Device + return zigbee.onOffRefresh() + + zigbee.readAttribute(FAN_CLUSTER_VALUE, FAN_STATUS_VALUE) +} + +def refresh() { + zigbee.onOffRefresh() + + zigbee.levelRefresh() + + zigbee.readAttribute(FAN_CLUSTER_VALUE, FAN_STATUS_VALUE) +} + +def configure() { + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity + return zigbee.onOffConfig(0, 300) + + zigbee.levelConfig() + + refresh() +} + +def installed() { + addChildFan() +} + +def addChildFan() { + def componentLabel + def childDevice + + if (device.displayName.endsWith(' Light') || + device.displayName.endsWith(' light')) { + componentLabel = "${device.displayName[0..-6]} Fan" + } else { + // no '1' at the end of deviceJoinName - use 2 to indicate second switch anyway + componentLabel = "$device.displayName Fan" + } + try { + String dni = "${device.deviceNetworkId}:1" + childDevice = addChildDevice("ITM Fan Child", dni, device.hub.id, [completedSetup: true, label: "${componentLabel}", isComponent: false]) + } catch(e) { + log.warn "Failed to add ITM Fan Controller - $e" + } + + if (childDevice != null) { + childDevice.sendEvent(name: "switch", value: "off") + } +} diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 0f71f169eab..52cb074bf55 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -38,6 +38,9 @@ metadata { // Samsung LED fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-002", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-002" //ITM RGBW + + // ABL + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-201", deviceJoinName: "Juno Connect", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-201" //E-series // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010D", manufacturer: "AduroSmart Eria", model: "AD-RGBW3001", deviceJoinName: "Eria Light" //Eria ZigBee RGBW Bulb From 02c06219bf85b161d46861d84715f10d714c7a8e Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Thu, 16 Sep 2021 10:05:31 +0800 Subject: [PATCH 294/422] repalce my own child button with smartthings offical child button --- .../min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 4f5a9fbc634..fa14ba13aad 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -137,7 +137,7 @@ private getConfigParamInput(param) { private addChildButton() { log.warn "Creating Button Device" def child = addChildDevice( - "sky-nie", + "smartthings", "Child Button", "${device.deviceNetworkId}:2", device.getHub().getId(), From bb57df34bb2030339b236d96d3ea837f556be842 Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Wed, 15 Sep 2021 23:24:30 -0700 Subject: [PATCH 295/422] DevWs for Smartenit, Inc containing containing Smartenit Moisture Sensor (#71932) * DevWs for Smartenit, Inc containing containing Smartenit Moisture Sensor * Adding Moisture Sensor Child device handler * Battery report additions, constants adjustments * Indentation fixes * Spacing fixes * Removed escape from label * Added refresh to child handler --- .../moisture-sensor-child.groovy | 31 +++ .../smartenit-moisture-sensor.groovy | 180 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 devicetypes/smartenit/moisture-sensor-child.src/moisture-sensor-child.groovy create mode 100644 devicetypes/smartenit/smartenit-moisture-sensor.src/smartenit-moisture-sensor.groovy diff --git a/devicetypes/smartenit/moisture-sensor-child.src/moisture-sensor-child.groovy b/devicetypes/smartenit/moisture-sensor-child.src/moisture-sensor-child.groovy new file mode 100644 index 00000000000..ff4cf8eead3 --- /dev/null +++ b/devicetypes/smartenit/moisture-sensor-child.src/moisture-sensor-child.groovy @@ -0,0 +1,31 @@ +/** + * Moisture Sensor Child + * + * Copyright 2021 Luis Contreras + * + * 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: "Moisture Sensor Child", namespace: "Smartenit", author: "Luis Contreras") { + capability "Configuration" + capability "Refresh" + capability "Water Sensor" + capability "Health Check" + capability "Sensor" + } +} + +ping() { + refresh() +} + +refresh() { + parent.refresh() +} \ No newline at end of file diff --git a/devicetypes/smartenit/smartenit-moisture-sensor.src/smartenit-moisture-sensor.groovy b/devicetypes/smartenit/smartenit-moisture-sensor.src/smartenit-moisture-sensor.groovy new file mode 100644 index 00000000000..d5724f120a0 --- /dev/null +++ b/devicetypes/smartenit/smartenit-moisture-sensor.src/smartenit-moisture-sensor.groovy @@ -0,0 +1,180 @@ +/* + * Copyright 2016 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. + */ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition(name: "Smartenit Moisture Sensor", namespace: "Smartenit", author: "Luis Contreras") { + capability "Battery" + capability "Configuration" + capability "Refresh" + capability "Water Sensor" + capability "Health Check" + capability "Sensor" + + fingerprint manufacturer: "Compacta", model: "ZBLIQS", deviceJoinName: "Smartenit Moisture Sensor" + } +} + +def getBATTERY_VOLTAGE_ATTR() { 0x0020 } + +def installed() { + log.debug "Installed" + createChildDevices() +} + +private List collectAttributes(Map descMap) { + List descMaps = new ArrayList() + + descMaps.add(descMap) + + if (descMap.additionalAttrs) { + descMaps.addAll(descMap.additionalAttrs) + } + + return descMaps +} + +def parse(String description) { + log.debug "description: $description" + + // getEvent will handle temperature and humidity + Map map = zigbee.getEvent(description) + if (!map) { + if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } else { + Map descMap = zigbee.parseDescriptionAsMap(description) + + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap?.value) { + map = getBatteryResult(Integer.parseInt(descMap.value, 16)) + } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS) { + def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 16)) + map = translateZoneStatus(zs) + } + } + } + + 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 +} + +private Map parseIasMessage(String description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + + translateZoneStatus(zs) +} + +private Map translateZoneStatus(ZoneStatus zs) { + if (zs.isAlarm2Set()) { + setChildMoistureState("wet") + } else { + setChildMoistureState("dry") + } + return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry') +} + +private setChildMoistureState(value) { + def childDevice = childDevices.find { + it.deviceNetworkId == "$device.deviceNetworkId:02" || it.deviceNetworkId == "$device.deviceNetworkId:02" + } + + if (childDevice) { + def map = createEvent(name: "water", value: value, translatable: true) + childDevice.sendEvent(map) + } +} + +private Map getMoistureResult(value) { + log.debug "water" + def descriptionText + if (value == "wet") { + descriptionText = '{{ device.displayName }} is wet' + } else { + descriptionText = '{{ device.displayName }} is dry' + } + return [ + name : 'water', + value : value, + descriptionText: descriptionText, + translatable : true + ] +} + +private Map getBatteryResult(rawValue) { + log.debug "Battery ${rawValue}" + def linkText = getLinkText(device) + + def result = [:] + + def volts = rawValue / 10 + if (!(rawValue == 0 || rawValue == 255)) { + def minVolts = 2.1 + def maxVolts = 3.0 + def pct = (volts - minVolts) / (maxVolts - minVolts) + def roundedPct = Math.round(pct * 100) + if (roundedPct <= 0) + roundedPct = 1 + result.value = Math.min(100, roundedPct) + result.descriptionText = "${linkText} battery was ${result.value}%" + result.name = 'battery' + } + + return result +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + refresh() +} + +def refresh() { + log.debug "Refreshing Values" + + return zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, BATTERY_VOLTAGE_ATTR) + + zigbee.enrollResponse() +} + +def configure() { + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) + + log.debug "Configuring Reporting" + return refresh() + zigbee.batteryConfig() +} + +private void createChildDevices() { + try { + addChildDevice("Smartenit", "Moisture Sensor Child", "${device.deviceNetworkId}:02", device.hubId, + [completedSetup: true, + label: "${device.displayName} 2", + isComponent: false + ]) + } catch (Exception e) { + log.debug "Exception creating child device: ${e}" + } +} \ No newline at end of file From 3f1cdd530445f2d93e0a4c6eca5a7823e3ee5563 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Thu, 16 Sep 2021 17:56:01 +0900 Subject: [PATCH 296/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#75541) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * Update sihas-multipurpose-sensor.groovy Changes the log output for setPeopleCounter(peoplecounter). * Update sihas-multipurpose-sensor.groovy Add refresh for CSM300. --- .../sihas-multipurpose-sensor.groovy | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index e193f5b6e30..94ca4fef5ce 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -34,7 +34,7 @@ metadata { fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2", ocfDeviceType: "x.com.st.d.sensor.motion" fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Temp/Humidity_Sensor", ocfDeviceType: "oic.d.thermostat" fingerprint inClusters: "0000,0001,0003,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "DSM-300Z", deviceJoinName: "SiHAS Contact Sensor", mnmn: "SmartThings", vid: "generic-contact-3", ocfDeviceType: "x.com.st.d.sensor.contact" - fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "b4e6d6e1-65e2-3f2e-8167-8ddd820f578e", ocfDeviceType: "x.com.st.d.sensor.motion" + fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "15962fd0-22b8-352e-9641-de640d672bb6", ocfDeviceType: "x.com.st.d.sensor.motion" } preferences { section { @@ -209,6 +209,12 @@ private Map getAnalogInputResult(value) { ] } +def setPeopleCounter(peoplecounter) { + int pc = Float.floatToIntBits(peoplecounter); + log.debug "SetPeopleCounter = $peoplecounter" + zigbee.writeAttribute(ANALOG_INPUT_BASIC_CLUSTER, ANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE, DataType.FLOAT4, pc) +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ @@ -240,7 +246,11 @@ def refresh() { refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) refreshCmds += zigbee.enrollResponse() } - + + if (isCSM300()) { + refreshCmds += zigbee.readAttribute(ANALOG_INPUT_BASIC_CLUSTER, ANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE) + } + return refreshCmds } @@ -282,7 +292,7 @@ def configure() { configCmds += zigbee.configureReporting(ANALOG_INPUT_BASIC_CLUSTER, ANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE, DataType.FLOAT4, 1, 600, 1) } - return refresh() + configCmds + return configCmds + refresh() } private Boolean isUSM300() { From 0baf66a7fd228608bb467b9d344f458e15eab241 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Fri, 17 Sep 2021 03:23:03 -0400 Subject: [PATCH 297/422] DevWs for Zooz (The Smartest House) containing containing Zooz Remote Switch ZEN34 (#75070) * DevWs for Zooz (The Smartest House) containing containing Zooz Remote Switch ZEN34 * Moved raw description above fingerprint * -requested change --- .../zooz-remote-switch-zen34.groovy | 323 ++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 devicetypes/zooz/zooz-remote-switch-zen34.src/zooz-remote-switch-zen34.groovy diff --git a/devicetypes/zooz/zooz-remote-switch-zen34.src/zooz-remote-switch-zen34.groovy b/devicetypes/zooz/zooz-remote-switch-zen34.src/zooz-remote-switch-zen34.groovy new file mode 100644 index 00000000000..43692643328 --- /dev/null +++ b/devicetypes/zooz/zooz-remote-switch-zen34.src/zooz-remote-switch-zen34.groovy @@ -0,0 +1,323 @@ +/* + * Zooz Remote Switch ZEN34 + * + * Changelog: + * + * 2021-09-15 + * - requested change + * 2021-08-31 + * - Publication Release + * + * Copyright 2021 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x20: 1, // Basic + 0x26: 3, // Switch Multilevel (4) + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5B: 1, // CentralScene (3) + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // Firmware Update Md (3) + 0x80: 1, // Battery + 0x84: 2, // WakeUp + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x87: 1, // Indicator + 0x8E: 2, // MultiChannelAssociation (3) + 0x9F: 1 // Security 2 +] + +@Field static List supportedButtonValues = ["down","down_hold","down_2x","down_3x","down_4x","down_5x","up","up_hold","up_2x","up_3x","up_4x","up_5x","down_released","up_released"] + +@Field static Map configParams = [ + ledMode: [num:1, title:"LED Indicator Mode", size:1, defaultVal:1, options:[0:"Always off", 1:"On when pressed [DEFAULT]", 2:"Always on (upper paddle color)", 3:"Always on (lower paddle color)"]], + upperPaddleLedColor: [num:2, title:"Upper Paddled LED Indicator Color", size:1, defaultVal:1, options:[0:"White", 1:"Blue [DEFAULT]", 2:"Green", 3:"Red", 4:"Magenta", 5:"Yellow", 6:"Cyan"]], + lowerPaddleLedColor: [num:3, title:"Lower Paddle LED Indicator Color", size:1, defaultVal:0, options:[0:"White [DEFAULT]", 1:"Blue", 2:"Green", 3:"Red", 4:"Magenta", 5:"Yellow", 6:"Cyan"]] +] + +@Field static int wakeUpInterval = 43200 +@Field static int btnPushed = 0 +@Field static int btnReleased = 1 +@Field static int btnHeld = 2 +@Field static int btnPushed2x = 3 +@Field static int btnPushed6x = 7 + +metadata { + definition ( + name:"Zooz Remote Switch ZEN34", + namespace:"Zooz", + author: "Kevin LaFramboise (krlaframboise)", + ocfDeviceType: "x.com.st.d.remotecontroller", + mnmn: "SmartThingsCommunity", + vid: "540fce12-499a-3b90-b276-f4159eb55f42" + ) { + capability "Sensor" + capability "Battery" + capability "Button" + capability "Refresh" + capability "Configuration" + capability "Health Check" + capability "platemusic11009.firmware" + capability "platemusic11009.syncStatus" + + fingerprint mfr: "027A", prod: "7000", model: "F001", deviceJoinName: "Zooz Remote" //Zooz Remote Switch ZEN34, raw description: zw:Ss2a type:1800 mfr:027A prod:7000 model:F001 ver:1.01 zwv:7.13 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,73,80,5B,70,84,7A + } + + preferences { + configParams.each { name, param -> + input name, "enum", + title: param.title, + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } + + input "debugLogging", "enum", + title: "Logging:", + required: false, + defaultValue: "1", + options: ["0":"Disabled", "1":"Enabled [DEFAULT]"] + } +} + +def installed() { + logDebug "installed()..." + state.refreshAll = true + initialize() +} + +def updated() { + logDebug "updated()..." + initialize() + + if (pendingChanges) { + logForceWakeupMessage("The setting changes will be sent to the device the next time it wakes up.") + } +} + +void initialize() { + state.debugLoggingEnabled = (safeToInt(settings?.debugOutput, 1) != 0) + + refreshSyncStatus() + + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((wakeUpInterval * 2) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + if (!device.currentValue("supportedButtonValues")) { + sendEvent(name:"supportedButtonValues", value: supportedButtonValues.encodeAsJSON(), displayed:false) + } + + if (!device.currentValue("numberOfButtons")) { + sendEvent(name:"numberOfButtons", value:1, displayed:false) + } + + if (!device.currentValue("button")) { + sendButtonEvent("up") + } +} + +def configure() { + logDebug "configure()..." + List cmds = [] + + if (state.refreshAll || !device.currentValue("firmwareVersion")) { + cmds << secureCmd(zwave.versionV1.versionGet()) + } + + if (state.refreshAll || !device.currentValue("battery")) { + cmds << secureCmd(zwave.batteryV1.batteryGet()) + } + + state.refreshAll = false + + if (state.wakeUpInterval != wakeUpInterval) { + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalSet(seconds: wakeUpInterval, nodeid: zwaveHubNodeId)) + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + configParams.each { name, param -> + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if (storedVal != settingVal) { + logDebug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: settingVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + if (cmds) { + sendHubCommand(cmds, 500) + } +} + +def ping() { + logDebug "ping()..." +} + +def refresh() { + logDebug "refresh()..." + state.refreshAll = true + + refreshSyncStatus() + + logForceWakeupMessage("The next time the device wakes up, the sensor data will be requested.") +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + logDebug "$cmd" + runIn(4, refreshSyncStatus) + state.wakeUpInterval = cmd.seconds +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + logDebug "Device Woke Up..." + runIn(4, refreshSyncStatus) + configure() + sendHubCommand([secureCmd(zwave.wakeUpV2.wakeUpNoMoreInformation())]) +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + runIn(4, refreshSyncStatus) + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + int val = cmd.scaledConfigurationValue + state[name] = val + logDebug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${val}" + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logDebug "${cmd}" + sendEvent(name: "firmwareVersion", value: (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + def val = (cmd.batteryLevel == 0xFF ? 1 : cmd.batteryLevel) + if (val > 100) { + val = 100 + } + logDebug "Battery is ${val}%" + sendEvent(name:"battery", value:val, unit:"%", isStateChange: true) +} + +void zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd){ + if (state.lastSequenceNumber != cmd.sequenceNumber) { + state.lastSequenceNumber = cmd.sequenceNumber + + String paddle = (cmd.sceneNumber == 1) ? "up" : "down" + String btnVal + switch (cmd.keyAttributes){ + case btnPushed: + btnVal = paddle + break + case btnReleased: + logDebug "${paddle}_released is not supported by SmartThings" + btnVal = paddle + "_released" + break + case btnHeld: + btnVal = paddle + "_hold" + break + case { it >= btnPushed2x && it <= btnPushed6x}: + btnVal = paddle + "_${cmd.keyAttributes - 1}x" + break + default: + logDebug "keyAttributes ${cmd.keyAttributes} not supported" + } + + if (btnVal) { + sendButtonEvent(btnVal) + } + } +} + +void sendButtonEvent(String value) { + String desc = "${device.displayName} ${value}" + logDebug(desc) + sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true, descriptionText: desc) +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: $cmd" +} + +void refreshSyncStatus() { + int changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +void logForceWakeupMessage(String msg) { + log.warn "${msg} You can force the device to wake up immediately by tapping the upper paddle 7x." +} + +Integer getPendingChanges() { + int configChanges = safeToInt(configParams.count { name, param -> + (getSettingVal(name) != getStoredVal(name)) + }, 0) + int pendingWakeUpInterval = (state.wakeUpInterval != wakeUpInterval ? 1 : 0) + return (configChanges + pendingWakeUpInterval) +} + +Integer getSettingVal(String name) { + return (settings ? safeToInt(settings[name], null) : null) +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file From 1da0f5f643afff721880e2f7cdb1f9f372042bcc Mon Sep 17 00:00:00 2001 From: Winnie Wen Date: Wed, 22 Sep 2021 10:48:04 +0800 Subject: [PATCH 298/422] Remove the function related to CentralScene --- .../min-smart-plug-dimmer.groovy | 77 ++----------------- 1 file changed, 5 insertions(+), 72 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index fa14ba13aad..1a92fad2b4d 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -1,5 +1,5 @@ /** - * Min Smart Plug Dimmer v2.1.1 + * Min Smart Plug Dimmer v2.2.0 * * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) * @@ -10,6 +10,10 @@ * * Changelog: * + * 2.2.0 (09/22/2021) + * - Remove the function related to CentralScene-the function did not achieve the expected effect, + * and it can be replaced by the Automation function in the SmartThings APP + * * 2.1.1 (09/07/2021) * - Syntax format compliance adjustment * - delete dummy code @@ -134,55 +138,6 @@ private getConfigParamInput(param) { } } -private addChildButton() { - log.warn "Creating Button Device" - def child = addChildDevice( - "smartthings", - "Child Button", - "${device.deviceNetworkId}:2", - device.getHub().getId(), - [ - completedSetup: true, - isComponent: false, - label: "plugButton", - componentLabel: "${device.displayName[0..-8]} Button" - ] - ) - child?.sendEvent(name:"supportedButtonValues", value:JsonOutput.toJson(["pushed", "down", "down_2x", "up", "up_2x"]), displayed:false) - child?.sendEvent(name:"numberOfButtons", value:1, displayed:false) - sendButtonEvent("pushed") - return child -} - -def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { - logTrace "CentralSceneNotification: ${cmd}" - if (state.lastSequenceNumber != cmd.sequenceNumber) { - state.lastSequenceNumber = cmd.sequenceNumber - logTrace "${cmd}" - def paddle = (cmd.sceneNumber == 1) ? "down" : "up" - def btnVal - switch (cmd.keyAttributes) { - case 0: - btnVal = paddle - break - case 3: - btnVal = paddle + "_2x" - break - } - - if (btnVal) { - sendButtonEvent(btnVal) - } - } - return [] -} - -private sendButtonEvent(value) { - if (childDevices) { - childDevices[0].sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) - } -} - def ping() { logDebug "ping()..." return [ switchMultilevelGetCmd() ] @@ -216,13 +171,6 @@ def updated() { if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } - if (isButtonAvailable() && !childDevices) { - try { - addChildButton() - } catch (ex) { - log.error("Unable to create button device because the 'Child Button' DTH is not installed", ex) - } - } runIn(5, executeConfigureCmds, [overwrite: true]) } return [] @@ -333,7 +281,6 @@ private static getCommandClassVersions() { [ 0x20: 1, // Basic 0x26: 3, // Switch Multilevel - 0x5B: 1, // CentralScene (3) 0x55: 1, // Transport Service 0x59: 1, // AssociationGrpInfo 0x5A: 1, // DeviceResetLocally @@ -560,20 +507,6 @@ private sendSwitchEvents(rawVal, type) { if (rawVal) { sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") } - if (isButtonAvailable() && type == "physical") { - if (paddleControlParam.value == 2) { - sendButtonEvent("pushed") - } else { - def paddlesReversed = (paddleControlParam.value == 1) - def btnVal = ((rawVal && !paddlesReversed) || (!rawVal && paddlesReversed)) ? "up" : "down" - def oldSwitch = device.currentValue("switch") - def oldLevel = device.currentValue("level") - if ((oldSwitch == "on") && (btnVal == "up") && (oldLevel > rawVal)) { - btnVal = "down" - } - sendButtonEvent(btnVal) - } - } } private isButtonAvailable() { From 191cfdf788cfffd119c55b71ba576d1d6f9f78e3 Mon Sep 17 00:00:00 2001 From: Taejun Park Date: Thu, 23 Sep 2021 12:43:04 +0900 Subject: [PATCH 299/422] Add parsing logic of default response command --- .../zigbee-multi-switch.src/zigbee-multi-switch.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 644fecf6490..62a9322e630 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -134,7 +134,7 @@ def parse(String description) { Map eventDescMap = zigbee.parseDescriptionAsMap(description) if (eventMap) { - if (eventDescMap && eventDescMap?.attrId == "0000") {//0x0000 : OnOff attributeId + if (eventDescMap && (eventDescMap?.attrId == "0000" || eventDescMap?.command == "0B")) {//0x0000 : OnOff attributeId, 0x0B : default response command if (eventDescMap?.sourceEndpoint == "01" || eventDescMap?.endpoint == "01") { sendEvent(eventMap) } else { @@ -293,4 +293,4 @@ private getChildCount() { default: return 2 } -} \ No newline at end of file +} From b7563cdaa3f2060a2bfaa6679f85d117a4d52da2 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Mon, 27 Sep 2021 14:59:53 +0900 Subject: [PATCH 300/422] DevWs for SHINA SYSTEM containing containing Zigbee Metering Plug (#75962) Adding the device handler for SHINA SYSTEM Zigbee Metering Plug. --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 a280ced8888..795cf0d3d5a 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -39,6 +39,7 @@ metadata { fingerprint manufacturer: "Jasco Products", model: "43095", deviceJoinName: "Enbrighten Outlet" //Enbrighten Plug-in Smart Switch With Energy Monitoring 43095, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43132", deviceJoinName: "Jasco Outlet" //Enbrighten In-Wall Smart Outlet With Energy Monitoring 43132, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43078", deviceJoinName: "Enbrighten Switch", ocfDeviceType: "oic.d.switch" //Enbrighten In-Wall Smart Switch With Energy Monitoring 43078, Raw Description: 01 0104 0100 00 07 0000 0003 0004 0005 0006 0702 0B05 02 000A 0019 + fingerprint inClusters: "0000,0001,0003,0006,0020,0B04,0702", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CCM-300Z", deviceJoinName: "SiHAS Outlet" // SIHAS Smart Plug with on/off button } tiles(scale: 2){ @@ -168,7 +169,7 @@ private int getPowerDiv() { } private int getEnergyDiv() { - (isSengledOutlet() || isJascoProductsOutlet()) ? 10000 : isFrientOutlet() ? 1000 : 100 + (isSengledOutlet() || isJascoProductsOutlet()) ? 10000 : (isFrientOutlet() || isCCM300()) ? 1000 : 100 } private boolean isSengledOutlet() { @@ -182,3 +183,7 @@ private boolean isJascoProductsOutlet() { private boolean isFrientOutlet() { device.getDataValue("manufacturer") == "frient A/S" } + +private Boolean isCCM300() { + device.getDataValue("model") == "CCM-300Z" +} From 3fa5c24c3edb3a625379c98c1f84a8b3e496bb04 Mon Sep 17 00:00:00 2001 From: frient-design <73893001+frient-design@users.noreply.github.com> Date: Wed, 29 Sep 2021 09:49:02 +0200 Subject: [PATCH 301/422] DevWs for frient containing containing Zigbee Non-Holdable Button (#75979) * DevWs for frient containing containing Zigbee Non-Holdable Button * Fix device join name Co-authored-by: Mohammed Kemal Co-authored-by: rboy1 <3846367+rboy1@users.noreply.github.com> --- .../zigbee-non-holdable-button.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy index c5267f6ad80..6f2bb0260ec 100644 --- a/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy +++ b/devicetypes/smartthings/zigbee-non-holdable-button.src/zigbee-non-holdable-button.groovy @@ -27,6 +27,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0500", outClusters: "0019", manufacturer: "HEIMAN", model: "SOS-EM", deviceJoinName: "HEIMAN Button" //HEIMAN Emergency Button fingerprint manufacturer: "frient A/S", model: "MBTZB-110", deviceJoinName: "frient Button" // Frient Smart Button, 20 0104 0007 00 05 0000 0001 0003 000F 0020 04 0003 0006 000A 0019 + fingerprint manufacturer: "frient A/S", model: "SBTZB-110", deviceJoinName: "frient Button" // Frient Smart Button, 20 0104 0007 00 05 0000 0001 0003 000F 0020 04 0003 0006 000A 0019 fingerprint manufacturer: "eWeLink", model: "KF01", deviceJoinName: "eWeLink Button" // 01 0104 0402 00 04 0000 0003 0500 0001 01 0003 } @@ -225,4 +226,4 @@ private getLiIon3VTable() {[ 2.0: 1, 1.9: 0, 0.0: 0 -]} \ No newline at end of file +]} From ffe10c3c160819bf347ef3884f8c995b421d6b2f Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Wed, 6 Oct 2021 00:53:19 +0800 Subject: [PATCH 302/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug (#74784) * DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug * preferences content adjust * * - Syntax format compliance adjustment * - fix a bugs * * - Syntax format compliance adjustment * - fix some bugs * Syntax format compliance adjustment * Syntax format compliance adjustment * 1.restore removeChildButton; 2.Simplify the code * delete dummy code. * * - remove the preferences item "createButton", Fixedly create a child button * Restrict its use based on fingerprints--because the child buttons is not visible to the user . * - fix a bug: when isButtonAvailable() return false,getLedModeParam is conflict with getPaddleControlParam * - Simplify the code, Syntax format compliance adjustment * Syntax format compliance adjustment * * - Syntax format compliance adjustment * - delete dummy code * add removeChildButton in updated() when isButtonAvailable() return false * restore addChildButton in updated() when it' needed. * * - Syntax format compliance adjustment * - delete dummy code * Syntax format compliance adjustment as PR review * Delete devicetypes/sky-nie/child-button.src directory repalce my own child button with smartthings offical child button * repalce my own child button with smartthings offical child button * Remove the function related to CentralScene Co-authored-by: Winnie Wen --- .../min-smart-plug.src/min-smart-plug.groovy | 177 +++++++++++------- 1 file changed, 111 insertions(+), 66 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy index 0d4a00d1dc5..ffb60b0eee4 100644 --- a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy +++ b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy @@ -1,7 +1,7 @@ /** - * Min Smart Plug v1.0.4 + * Min Smart Plug v2.2.0 * - * Models: MINOSTON (MP21Z) + * Models: MINOSTON (MP21Z) And Eva Logik (ZW30) / MINOSTON (MS10Z) * * Author: * winnie (sky-nie) @@ -10,6 +10,28 @@ * * Changelog: * + * 2.2.0 (09/22/2021) + * - Remove the function related to CentralScene-the function did not achieve the expected effect, + * and it can be replaced by the Automation function in the SmartThings APP + * + * 2.1.1 (09/07/2021) + * - Syntax format compliance adjustment + * - delete dummy code + * + * 2.1.0 (09/04/2021) + * - remove the preferences item "createButton", Fixedly create a child button + * Restrict its use based on fingerprints--because the child buttons is not visible to the user . + * - fix a bug: when isButtonAvailable() return false,getLedModeParam is conflict with getPaddleControlParam + * - Simplify the code, Syntax format compliance adjustment + * + * 2.0.2 (09/02/2021) + * 2.0.1 (08/27/2021) + * - Syntax format compliance adjustment + * - fix some bugs + * + * 2.0.0 (08/26/2021) + * - add new products supported + * * 1.0.4 (07/13/2021) * - Syntax format compliance adjustment * - delete dummy code @@ -40,9 +62,10 @@ * limitations under the License. * */ +import groovy.json.JsonOutput metadata { - definition (name: "Min Smart Plug", namespace: "sky-nie", author: "winnie", mnmn: "SmartThings", vid:"generic-switch", ocfDeviceType: "oic.d.smartplug") { + definition (name: "Min Smart Plug", namespace: "sky-nie", author: "winnie", mnmn: "SmartThings", vid:"generic-switch") { capability "Actuator" capability "Sensor" capability "Switch" @@ -53,21 +76,34 @@ metadata { attribute "firmwareVersion", "string" attribute "syncStatus", "string" - fingerprint mfr: "0312", prod: "C000", model: "C009", deviceJoinName: "Minoston Outlet" // old MP21Z - fingerprint mfr: "0312", prod: "FF00", model: "FF0C", deviceJoinName: "Minoston Outlet" //MP21Z Minoston Mini Smart Plug - fingerprint mfr: "0312", prod: "AC01", model: "4001", deviceJoinName: "New One Outlet" // N4001 New One Mini Smart Plug + fingerprint mfr: "0312", prod: "C000", model: "C009", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" // old MP21Z + fingerprint mfr: "0312", prod: "FF00", model: "FF0C", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //MP21Z Minoston Mini Smart Plug + fingerprint mfr: "0312", prod: "AC01", model: "4001", deviceJoinName: "New One Outlet", ocfDeviceType: "oic.d.smartplug" // N4001 New One Mini Smart Plug + fingerprint mfr: "0312", prod: "EE00", model: "EE01", deviceJoinName: "Minoston Switch", ocfDeviceType: "oic.d.switch" //MS10ZS Minoston Smart Switch + fingerprint mfr: "0312", prod: "EE00", model: "EE03", deviceJoinName: "Minoston Switch", ocfDeviceType: "oic.d.switch" //MS12ZS Minoston Smart on/off Toggle Switch + fingerprint mfr: "0312", prod: "A000", model: "A005", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30 + fingerprint mfr: "0312", prod: "BB00", model: "BB01", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30S Evalogik Smart on/off Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB03", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30TS Evalogik Smart on/off Toggle Switch } preferences { - configParams.each { - if (it.name) { - if (it.range) { - input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range - } else { - input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options - } - } - } + getConfigParamInput(ledModeParam) + getConfigParamInput(autoOffIntervalParam) + getConfigParamInput(autoOnIntervalParam) + getConfigParamInput(powerFailureRecoveryParam) + input "disclaimer", "paragraph", + title: "WARNING", + description: "Configuring for 'Paddle Control'is only valid for the devices with product number of MS10ZS, MS12ZS, ZW30, ZW30S, ZW30TS(one of them)", + required: false + getConfigParamInput(paddleControlParam) + } +} + +private getConfigParamInput(param) { + if (param.range) { + input "configParam${param.num}", "number", title: "${param.name}:", required: false, defaultValue: "${param.value}", range: param.range + } else { + input "configParam${param.num}", "enum", title: "${param.name}:", required: false, defaultValue: "${param.value}", options: param.options } } @@ -85,12 +121,10 @@ private static def getCheckInterval() { def updated() { if (!isDuplicateCommand(state.lastUpdated, 5000)) { state.lastUpdated = new Date().time - logDebug "updated()..." if (device.latestValue("checkInterval") != checkInterval) { sendEvent(name: "checkInterval", value: checkInterval, displayed: false) } - runIn(5, executeConfigureCmds, [overwrite: true]) } return [] @@ -98,7 +132,6 @@ def updated() { def configure() { logDebug "configure()..." - if (state.resyncAll == null) { state.resyncAll = true runIn(8, executeConfigureCmds, [overwrite: true]) @@ -116,20 +149,10 @@ def executeConfigureCmds() { def cmds = [] - if (!device.currentValue("switch")) { - cmds << switchBinaryGetCmd() - } - - if (state.resyncAll || !device.currentValue("firmwareVersion")) { - cmds << secureCmd(zwave.versionV1.versionGet()) - } - configParams.each { param -> def storedVal = getParamStoredValue(param.num) def paramVal = param.value - if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { - logDebug "Changing ${param.name}(#${param.num}) from ${storedVal} to ${paramVal}" cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) } @@ -137,7 +160,7 @@ def executeConfigureCmds() { state.resyncAll = false if (cmds) { - sendCommands(delayBetween(cmds, 500)) + sendHubCommand(cmds, 500) } return [] } @@ -160,18 +183,7 @@ def off() { def refresh() { logDebug "refresh()..." refreshSyncStatus() - sendCommands([switchBinaryGetCmd()]) -} - -private sendCommands(cmds) { - if (cmds) { - def actions = [] - cmds.each { - actions << new physicalgraph.device.HubAction(it) - } - sendHubCommand(actions) - } - return [] + return [ switchBinaryGetCmd() ] } private switchBinaryGetCmd() { @@ -190,6 +202,7 @@ private secureCmd(cmd) { return cmd.format() } } catch (ex) { + log.error("secureCmd exception", ex) return cmd.format() } } @@ -210,8 +223,8 @@ def parse(String description) { } def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + logTrace "SecurityMessageEncapsulation: ${cmd}" def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) - def result = [] if (encapsulatedCmd) { result += zwaveEvent(encapsulatedCmd) @@ -222,11 +235,9 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat } def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { - logTrace "${cmd}" - + logTrace "ConfigurationReport: ${cmd}" sendEvent(name: "syncStatus", value: "Syncing...", displayed: false) runIn(4, refreshSyncStatus) - def param = configParams.find { it.num == cmd.parameterNumber } if (param) { def val = cmd.scaledConfigurationValue @@ -240,28 +251,27 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { logTrace "VersionReport: ${cmd}" - def subVersion = String.format("%02d", cmd.applicationSubVersion) def fullVersion = "${cmd.applicationVersion}.${subVersion}" - sendEvent(name: "firmwareVersion", value: fullVersion) return [] } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { - logTrace "${cmd}" + logTrace "BasicReport: ${cmd}" sendSwitchEvents(cmd.value, "physical") return [] } def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { - logTrace "${cmd}" + logTrace "SwitchBinaryReport: ${cmd}" sendSwitchEvents(cmd.value, "digital") return [] } private sendSwitchEvents(rawVal, type) { - sendEvent(name: "switch", value: (rawVal == 0xFF) ? "on" : "off", displayed: true, type: type) + def switchVal = (rawVal == 0xFF) ? "on" : "off" + sendEvent(name: "switch", value: switchVal, displayed: true, type: type) } def zwaveEvent(physicalgraph.zwave.Command cmd) { @@ -309,29 +319,46 @@ private getConfigParams() { ledModeParam, autoOffIntervalParam, autoOnIntervalParam, - powerFailureRecoveryParam + powerFailureRecoveryParam, + paddleControlParam + ] +} + +private static getPaddleControlOptions() { + return [ + "0":"Normal", + "1":"Reverse", + "2":"Toggle" ] } +private getPaddleControlParam() { + def num = isButtonAvailable()? 1 : 1000 + return getParam(num, "Paddle Control", 1, 0, paddleControlOptions) +} + private getLedModeParam() { - return getParam(1, "LED Indicator Mode", 1, 0, ledModeOptions) + def num = isButtonAvailable()? 2 : 1 + return getParam(num, "LED Indicator Mode", 1, 0, alternativeLedOptions) } private getAutoOffIntervalParam() { - return getParam(2, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") + def num = isButtonAvailable()? 4 : 2 + return getParam(num, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") } private getAutoOnIntervalParam() { - return getParam(4, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") + def num = isButtonAvailable()? 6 : 4 + return getParam(num, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") } private getPowerFailureRecoveryParam() { - return getParam(6, "Power Failure Recovery", 1, 0, powerFailureRecoveryOptions) + def num = isButtonAvailable()? 8 : 6 + return getParam(num, "Power Failure Recovery", 1, 0, powerFailureRecoveryOptions) } -private getParam(num, name, size, defaultVal, options=null, range=null) { +private getParam(num, name, size, defaultVal, options = null, range = null) { def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) - def map = [num: num, name: name, size: size, value: val] if (options) { map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value @@ -340,7 +367,6 @@ private getParam(num, name, size, defaultVal, options=null, range=null) { if (range) { map.range = range } - return map } @@ -353,12 +379,21 @@ private static setDefaultOption(options, defaultVal) { } } -private static getLedModeOptions() { - return [ - "0":"On When On", - "1":"Off When On", - "2":"Always Off" - ] +private getAlternativeLedOptions() { + if (isButtonAvailable()) { + return [ + "0":"On When On", + "1":"Off When On", + "2":"Always Off" + ] + } else { + return [ + "0":"Off When On", + "1":"On When On", + "2":"Always Off", + "3":"Always On" + ] + } } private static getPowerFailureRecoveryOptions() { @@ -369,7 +404,7 @@ private static getPowerFailureRecoveryOptions() { ] } -private static safeToInt(val, defaultVal=0) { +private static safeToInt(val, defaultVal = 0) { return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal } @@ -383,4 +418,14 @@ private logDebug(msg) { private logTrace(msg) { log.trace "$msg" -} \ No newline at end of file +} + +private isButtonAvailable() { + if (device == null) { + log.error "isButtonAvailable device = null" + return true + } else { + log.debug "isButtonAvailable device.rawDescription = ${device.rawDescription}" + return "${device.rawDescription}".contains("model:EE01") || "${device.rawDescription}".contains("model:EE03") || "${device.rawDescription}".contains("model:A005") || "${device.rawDescription}".contains("model:BB01") || "${device.rawDescription}".contains("model:BB03") + } +} From d1ecbacd7ea9f9aea34d7db861bed46440769954 Mon Sep 17 00:00:00 2001 From: Dawid Sobierajski <89920842+dsobierajsk@users.noreply.github.com> Date: Tue, 5 Oct 2021 22:44:50 +0200 Subject: [PATCH 303/422] ICP-14305, ICP-14310 - fixes (#76007) * fixes ICP-14305, ICP-14310 * clean --- .../zwave-metering-switch.groovy | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 507ec4b53e2..feb0c8051a2 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -211,18 +211,30 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) { [:] } +def isEverspringOutlet() { + return zwaveInfo.mfr == "0060" && zwaveInfo.prod == "0004" && zwaveInfo.model == "000B" +} + +def getDelay() { + if(isEverspringOutlet()){ + return 1000 + } else { + return 3000 + } +} + def on() { encapSequence([ zwave.basicV1.basicSet(value: 0xFF), zwave.switchBinaryV1.switchBinaryGet() - ], 3000) + ], getDelay()) } def off() { encapSequence([ zwave.basicV1.basicSet(value: 0x00), zwave.switchBinaryV1.switchBinaryGet() - ], 3000) + ], getDelay()) } /** From 49486dceca37fe9f3924c66132b19f136d4ac3a6 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 12 Oct 2021 15:09:27 -0700 Subject: [PATCH 304/422] BUG-3640 Update lock code events Some lock code events were using undocumented fields. --- .../zigbee-lock.src/zigbee-lock.groovy | 17 ++----- .../zwave-lock.src/zwave-lock.groovy | 44 ++++++------------- 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 45cf8378873..299840f8888 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -423,7 +423,7 @@ def nameSlot(codeSlot, codeName) { def newCodeName = codeName ?: "Code $codeSlot" lockCodes[codeSlot] = newCodeName sendEvent(lockCodesEvent(lockCodes)) - sendEvent(name: "codeChanged", value: "$codeSlot renamed", data: [ lockName: deviceName, notify: false, notificationText: "Renamed \"$oldCodeName\" to \"$newCodeName\" in $deviceName at ${location.name}" ], + sendEvent(name: "codeChanged", value: "$codeSlot renamed", data: [ notify: false, notificationText: "Renamed \"$oldCodeName\" to \"$newCodeName\" in $deviceName at ${location.name}" ], descriptionText: "Renamed \"$oldCodeName\" to \"$newCodeName\"", displayed: true, isStateChange: true) } } @@ -523,11 +523,6 @@ private def parseAttributeResponse(String description) { return null } - if (responseMap.data) { - responseMap.data.lockName = deviceName - } else { - responseMap.data = [ lockName: deviceName ] - } result << createEvent(responseMap) log.info "ZigBee DTH - parseAttributeResponse() returning with result:- $result" return result @@ -585,7 +580,7 @@ private def parseCommandResponse(String description) { return null } codeName = getCodeName(lockCodes, codeID) - responseMap.data = [ codeId: codeID as String, usedCode: codeID, codeName: codeName, method: "keypad" ] + responseMap.data = [ codeId: codeID as String, codeName: codeName, method: "keypad" ] } else if (eventSource == 1) { responseMap.data = [ method: "command" ] } else if (eventSource == 2) { @@ -858,11 +853,6 @@ private def parseCommandResponse(String description) { } if(responseMap["value"]) { - if (responseMap.data) { - responseMap.data.lockName = deviceName - } else { - responseMap.data = [ lockName: deviceName ] - } result << createEvent(responseMap) } if (result) { @@ -931,8 +921,7 @@ private def allCodesDeletedEvent() { def codeName = code result << createEvent(name: "codeChanged", value: "$id deleted", - data: [ codeName: codeName, lockName: deviceName, notify: true, - notificationText: "Deleted \"$codeName\" in $deviceName at ${location.name}" ], + data: [ codeName: codeName, notify: true, notificationText: "Deleted \"$codeName\" in $deviceName at ${location.name}" ], descriptionText: "Deleted \"$codeName\"", displayed: true, isStateChange: true) clearStateForSlot(id) diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index 4b654e85bbc..39af5e53372 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -283,8 +283,7 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport log.trace "[DTH] Executing 'ConfigurationReport' for device $deviceName - all codes deleted" result = allCodesDeletedEvent() result << createEvent(name: "codeChanged", value: "all deleted", descriptionText: "Deleted all user codes", - isStateChange: true, data: [lockName: deviceName, notify: true, - notificationText: "Deleted all user codes in $deviceName at ${location.name}"]) + isStateChange: true, data: [notify: true, notificationText: "Deleted all user codes in $deviceName at ${location.name}"]) result << createEvent(name: "lockCodes", value: util.toJson([:]), displayed: false, descriptionText: "'lockCodes' attribute updated") } result << createEvent(name:"codeLength", value: length, descriptionText: "Code length is $length", displayed: false) @@ -356,7 +355,6 @@ def zwaveEvent(DoorLockOperationReport cmd) { // 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 (isKeyweLock()) { map.value = cmd.doorCondition >> 1 ? "unlocked" : "locked" map.descriptionText = cmd.doorCondition >> 1 ? "Unlocked" : "Locked" @@ -459,7 +457,7 @@ private def handleAccessAlarmReport(cmd) { codeID = readCodeSlotId(cmd) codeName = getCodeName(lockCodes, codeID) map.descriptionText = "Locked by \"$codeName\"" - map.data = [ codeId: codeID as String, usedCode: codeID, codeName: codeName, method: "keypad" ] + map.data = [ codeId: codeID as String, codeName: codeName, method: "keypad" ] } else { // locked by pressing the Schlage button map.descriptionText = "Locked manually" @@ -471,7 +469,7 @@ private def handleAccessAlarmReport(cmd) { codeID = readCodeSlotId(cmd) codeName = getCodeName(lockCodes, codeID) map.descriptionText = "Unlocked by \"$codeName\"" - map.data = [ codeId: codeID as String, usedCode: codeID, codeName: codeName, method: "keypad" ] + map.data = [ codeId: codeID as String, codeName: codeName, method: "keypad" ] } break case 7: @@ -569,11 +567,6 @@ private def handleAccessAlarmReport(cmd) { } if (map) { - if (map.data) { - map.data.lockName = deviceName - } else { - map.data = [ lockName: deviceName ] - } result << createEvent(map) } result = result.flatten() @@ -594,7 +587,6 @@ private def handleBurglarAlarmReport(cmd) { def deviceName = device.displayName def map = [ name: "tamper", value: "detected" ] - map.data = [ lockName: deviceName ] switch (cmd.zwaveAlarmEvent) { case 0: map.value = "clear" @@ -639,10 +631,10 @@ private def handleBatteryAlarmReport(cmd) { result << response(secure(zwave.batteryV1.batteryGet())) break; case 0x0A: - map = [ name: "battery", value: 1, descriptionText: "Battery level critical", displayed: true, data: [ lockName: deviceName ] ] + map = [ name: "battery", value: 1, descriptionText: "Battery level critical", displayed: true] break case 0x0B: - map = [ name: "battery", value: 0, descriptionText: "Battery too low to operate lock", isStateChange: true, displayed: true, data: [ lockName: deviceName ] ] + map = [ name: "battery", value: 0, descriptionText: "Battery too low to operate lock", isStateChange: true, displayed: true] break default: // delegating it to handleAlarmReportUsingAlarmType @@ -680,7 +672,7 @@ private def handleAlarmReportUsingAlarmType(cmd) { codeName = getCodeName(lockCodes, codeID) map.isStateChange = true // Non motorized locks, mark state changed since it can be unlocked multiple times map.descriptionText = "Unlocked by \"$codeName\"" - map.data = [ codeId: codeID as String, usedCode: codeID, codeName: codeName, method: "keypad" ] + map.data = [ codeId: codeID as String, codeName: codeName, method: "keypad" ] } break case 18: // Locked with keypad @@ -693,7 +685,7 @@ private def handleAlarmReportUsingAlarmType(cmd) { } else { codeName = getCodeName(lockCodes, codeID) map.descriptionText = "Locked by \"$codeName\"" - map.data = [ codeId: codeID as String, usedCode: codeID, codeName: codeName, method: "keypad" ] + map.data = [ codeId: codeID as String, codeName: codeName, method: "keypad" ] } break case 21: // Manually locked @@ -808,11 +800,6 @@ private def handleAlarmReportUsingAlarmType(cmd) { } if (map) { - if (map.data) { - map.data.lockName = deviceName - } else { - map.data = [ lockName: deviceName ] - } result << createEvent(map) } result = result.flatten() @@ -854,13 +841,12 @@ def zwaveEvent(UserCodeReport cmd) { map.value = "$codeID $changeType" map.isStateChange = true map.descriptionText = "${getStatusForDescription(changeType)} \"$codeName\"" - map.data = [ codeName: codeName, lockName: deviceName, notify: true, notificationText: "${getStatusForDescription(changeType)} \"$codeName\" in $deviceName at ${location.name}" ] + map.data = [ codeName: codeName, notify: true, notificationText: "${getStatusForDescription(changeType)} \"$codeName\" in $deviceName at ${location.name}" ] if(!isMasterCode(codeID)) { result << codeSetEvent(lockCodes, codeID, codeName) } else { map.descriptionText = "${getStatusForDescription('set')} \"$codeName\"" map.data.notificationText = "${getStatusForDescription('set')} \"$codeName\" in $deviceName at ${location.name}" - map.data.lockName = deviceName } } else { // We'll land here during scanning of codes @@ -873,14 +859,14 @@ def zwaveEvent(UserCodeReport cmd) { } map.value = "$codeID $changeType" map.descriptionText = "${getStatusForDescription(changeType)} \"$codeName\"" - map.data = [ codeName: codeName, lockName: deviceName ] + map.data = [ codeName: codeName ] } } else if(userIdStatus == 254 && isSchlageLock()) { // This is code creation/updation error for Schlage locks. // It should be OK to mark this as duplicate pin code error since in case the batteries are down, or lock is not in range, // or wireless interference is there, the UserCodeReport will anyway not be received. map = [ name: "codeChanged", value: "$codeID failed", descriptionText: "User code is not added", isStateChange: true, - data: [ lockName: deviceName, isCodeDuplicate: true] ] + data: [ isCodeDuplicate: true] ] } else { // We are using userIdStatus here because codeID = 0 is reported when user tries to set programming code as the user code if (codeID == "0" && userIdStatus == UserCodeReport.USER_ID_STATUS_AVAILABLE_NOT_SET && isSchlageLock()) { @@ -888,7 +874,7 @@ def zwaveEvent(UserCodeReport cmd) { log.trace "[DTH] All user codes deleted for Schlage lock" result << allCodesDeletedEvent() map = [ name: "codeChanged", value: "all deleted", descriptionText: "Deleted all user codes", isStateChange: true, - data: [ lockName: deviceName, notify: true, + data: [ notify: true, notificationText: "Deleted all user codes in $deviceName at ${location.name}"] ] lockCodes = [:] result << lockCodesEvent(lockCodes) @@ -898,12 +884,11 @@ def zwaveEvent(UserCodeReport cmd) { def codeName = getCodeName(lockCodes, codeID) map.value = "$codeID deleted" map.descriptionText = "Deleted \"$codeName\"" - map.data = [ codeName: codeName, lockName: deviceName, notify: true, notificationText: "Deleted \"$codeName\" in $deviceName at ${location.name}" ] + map.data = [ codeName: codeName, notify: true, notificationText: "Deleted \"$codeName\" in $deviceName at ${location.name}" ] result << codeDeletedEvent(lockCodes, codeID) } else { map.value = "$codeID unset" map.displayed = false - map.data = [ lockName: deviceName ] } } } @@ -1425,7 +1410,7 @@ void nameSlot(codeSlot, codeName) { def newCodeName = codeName ?: "Code $codeSlot" lockCodes[codeSlot] = newCodeName sendEvent(lockCodesEvent(lockCodes)) - sendEvent(name: "codeChanged", value: "$codeSlot renamed", data: [ lockName: deviceName, notify: false, notificationText: "Renamed \"$oldCodeName\" to \"$newCodeName\" in $deviceName at ${location.name}" ], + sendEvent(name: "codeChanged", value: "$codeSlot renamed", data: [ notify: false, notificationText: "Renamed \"$oldCodeName\" to \"$newCodeName\" in $deviceName at ${location.name}" ], descriptionText: "Renamed \"$oldCodeName\" to \"$newCodeName\"", displayed: true, isStateChange: true) } @@ -1642,8 +1627,7 @@ private def allCodesDeletedEvent() { displayed: false, isStateChange: true) def codeName = code - result << createEvent(name: "codeChanged", value: "$id deleted", data: [ codeName: codeName, lockName: deviceName, - notify: true, notificationText: "Deleted \"$codeName\" in $deviceName at ${location.name}" ], + result << createEvent(name: "codeChanged", value: "$id deleted", data: [ codeName: codeName, notify: true, notificationText: "Deleted \"$codeName\" in $deviceName at ${location.name}" ], descriptionText: "Deleted \"$codeName\"", displayed: true, isStateChange: true) clearStateForSlot(id) From e708a886242791c090597886d5ccbe160d10e1b7 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 12 Oct 2021 15:11:57 -0700 Subject: [PATCH 305/422] a few missing from other lock dths --- .../samsung-smart-doorlock.groovy | 6 ------ .../zwave-lock-without-codes.groovy | 15 ++------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/devicetypes/samsungsds/samsung-smart-doorlock.src/samsung-smart-doorlock.groovy b/devicetypes/samsungsds/samsung-smart-doorlock.src/samsung-smart-doorlock.groovy index 3ee4c5f3868..84e9699746e 100755 --- a/devicetypes/samsungsds/samsung-smart-doorlock.src/samsung-smart-doorlock.groovy +++ b/devicetypes/samsungsds/samsung-smart-doorlock.src/samsung-smart-doorlock.groovy @@ -194,7 +194,6 @@ private def parseAttributeResponse(String description) { return null } - responseMap.data = [ lockName: deviceName ] result << createEvent(responseMap) log.info "ZigBee DTH - parseAttributeResponse() returning with result:- $result" return result @@ -386,11 +385,6 @@ private def parseCommandResponse(String description) { } if (responseMap["value"]) { - if (responseMap.data) { - responseMap.data.lockName = deviceName - } else { - responseMap.data = [ lockName: deviceName ] - } result << createEvent(responseMap) } if (result) { 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 index 964f9376f74..410f05c316b 100644 --- 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 @@ -278,7 +278,6 @@ def zwaveEvent(DoorLockOperationReport cmd) { // 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" @@ -383,11 +382,6 @@ private def handleAccessAlarmReport(cmd) { } if (map) { - if (map.data) { - map.data.lockName = deviceName - } else { - map.data = [lockName: deviceName] - } result << createEvent(map) } result = result.flatten() @@ -414,10 +408,10 @@ private def handleBatteryAlarmReport(cmd) { result << response(secure(zwave.batteryV1.batteryGet())) break; case 0x0A: - map = [name: "battery", value: 1, descriptionText: "Battery level critical", displayed: true, data: [lockName: deviceName]] + map = [name: "battery", value: 1, descriptionText: "Battery level critical", displayed: true] break case 0x0B: - map = [name: "battery", value: 0, descriptionText: "Battery too low to operate lock", isStateChange: true, displayed: true, data: [lockName: deviceName]] + map = [name: "battery", value: 0, descriptionText: "Battery too low to operate lock", isStateChange: true, displayed: true] break default: map = [displayed: false, descriptionText: "Alarm event ${cmd.alarmType} level ${cmd.alarmLevel}"] @@ -512,11 +506,6 @@ private def handleAlarmReportUsingAlarmType(cmd) { } if (map) { - if (map.data) { - map.data.lockName = deviceName - } else { - map.data = [ lockName: deviceName ] - } result << createEvent(map) } result = result.flatten() From 8671b098038cac59523ed0df289f258311e0ca99 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:42:54 +0900 Subject: [PATCH 306/422] Update zigbee-white-color-temperature-bulb.groovy (#76129) Add a fingerprint of Samsung Korea B2B Marketing --- .../zigbee-white-color-temperature-bulb.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 2c7140539bc..50426507a0c 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -41,6 +41,9 @@ metadata { // Samsung LED fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-001", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" //ITM CCT + // Samsung Korea B2B Marketing + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "HAN-LIGHT-Z-001", deviceJoinName: "SamsungB2B Light", mnmn: "Samsung Electronics", vid: "HAN-LIGHT-Z-001" //Samsung Korea B2B Marketing CCT + // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010C", manufacturer: "AduroSmart Eria", model: "AD-ColorTemperature3001", deviceJoinName: "Eria Light" //Eria ZigBee Color Temperature Bulb From 378f57c54422cc6c7b6f485f71bc8375828ab9f5 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 13 Oct 2021 17:00:22 +0200 Subject: [PATCH 307/422] [BUG-3524] POPP TRV - changed logic to the one not relying on multiCmdEncap (#76120) * Changed logic to the one not relying on multiCmdEncap * Moved clearing cache to more optimal line --- .../zwave-radiator-thermostat.groovy | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy index db5a31c526d..453d8e233d7 100644 --- a/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy +++ b/devicetypes/smartthings/zwave-radiator-thermostat.src/zwave-radiator-thermostat.groovy @@ -95,7 +95,6 @@ def initialize() { } def installed() { - state.isSetpointChangeRequestedByController = false initialize() } @@ -136,21 +135,16 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat } } -def zwaveEvent(physicalgraph.zwave.commands.multicmdv1.MultiCmdEncap cmd) { - cmd.encapsulatedCommands().collect { encapsulatedCommand -> - isPoppRadiatorThermostat() ? zwaveEvent(encapsulatedCommand, true) : zwaveEvent(encapsulatedCommand) - //in case any future device would support MultiCmdEncap - //and won't need any special handler, like POPP does - }.flatten() -} - def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { def cmds = [] if (!isPoppRadiatorThermostat()) { cmds += zwave.batteryV1.batteryGet() // POPP sends battery report automatically every wake up by itself, there's no need to duplicate it } + if (state.cachedSetpoint) { + cmds += zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: state.cachedSetpoint, setpointType: 1, size: 2]) + state.cachedSetpoint = null + } cmds += [ - zwave.thermostatSetpointV2.thermostatSetpointSet([precision: 1, scale: 0, scaledValue: state.cachedSetpoint, setpointType: 1, size: 2]), zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: 1), zwave.wakeUpV2.wakeUpNoMoreInformation() ] @@ -185,19 +179,17 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeRepor def updateSetpoint(cmd) { def deviceTemperatureScale = cmd.scale ? 'F' : 'C' def setpoint = Float.parseFloat(convertTemperatureIfNeeded(cmd.scaledValue, deviceTemperatureScale, cmd.precision)) - state.cachedSetpoint = setpoint + state.expectedSetpoint = setpoint createEvent(name: "heatingSetpoint", value: setpoint, unit: temperatureScale) } -def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd, isResponseOfWakeUp = false) { - if (!state.isSetpointChangeRequestedByController) { - updateSetpoint(cmd) - } else if (isResponseOfWakeUp) { - state.isSetpointChangeRequestedByController = false - updateSetpoint(cmd) - } else { - [:] +def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd) { + def reportedSetpoint = Float.parseFloat(convertTemperatureIfNeeded(cmd.scaledValue, deviceTemperatureScale, cmd.precision)) + // User manually adjusted setpoint on device, after changing it in the app + if (reportedSetpoint != state.expectedSetpoint && reportedSetpoint != state.cachedSetpoint) { + state.cachedSetpoint = null } + updateSetpoint(cmd) } def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { @@ -258,7 +250,6 @@ def off() { def setHeatingSetpoint(setpoint) { if (isPoppRadiatorThermostat() && device.status == "ONLINE") { - state.isSetpointChangeRequestedByController = true sendEvent(name: "heatingSetpoint", value: setpoint, unit: temperatureScale) } setpoint = temperatureScale == 'C' ? setpoint : fahrenheitToCelsius(setpoint) From 8b76b0a73f8fca4621c335d561fc4fcc70226d5d Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Thu, 14 Oct 2021 15:52:18 +0800 Subject: [PATCH 308/422] DevWs for NIE-TECH CO., LTD. containing containing Aeotec Wallmote (#76125) Co-authored-by: Winnie Wen --- .../smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy b/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy index f04b9d09bd3..8255a106459 100644 --- a/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy +++ b/devicetypes/smartthings/aeotec-wallmote.src/aeotec-wallmote.groovy @@ -27,7 +27,7 @@ metadata { fingerprint mfr: "0086", model: "0081", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Aeotec Wallmote fingerprint mfr: "0060", model: "0003", deviceJoinName: "Everspring Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Everspring Wall Switch fingerprint mfr: "0371", model: "0016", deviceJoinName: "Aeotec Remote Control", mnmn: "SmartThings", vid: "generic-2-button" //Aeotec illumino Wallmote 7 - fingerprint mfr: "0312", model: "D001", deviceJoinName: "Minoston Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Minoston Wallmote + fingerprint mfr: "0312", prod: "0924", model: "D001", deviceJoinName: "Minoston Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Minoston Wallmote } tiles(scale: 2) { From b6941bed4bd4661b741e32aca51d8555d6fa70b0 Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Thu, 14 Oct 2021 20:39:30 +0800 Subject: [PATCH 309/422] HEIMAN Outlet WWST-7664 (#76136) * HEIMAN Outlet * HEIMAN Outlet WWST-7664 * HEIMAN Outlet WWST-7664 --- .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 1 - devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) 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 795cf0d3d5a..e28128d91da 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -29,7 +29,6 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B04", outClusters: "0003", manufacturer: "REXENSE", model: "HY0105", deviceJoinName: "HONYAR Outlet" //HONYAR Smart Outlet (USB) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B04", outClusters: "0003", manufacturer: "REXENSE", model: "HY0104", deviceJoinName: "HONYAR Outlet" //HONYAR Smart Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0009, 0702, 0B04", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "E_Socket", deviceJoinName: "HEIMAN Outlet" //HEIMAN Smart Outlet - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "HS6ESK-W-EF-3.0", deviceJoinName: "HEIMAN Outlet", ocfDeviceType: "oic.d.smartplug" //HEIMAN Smart Outlet fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04, 0702, FC82", outClusters: "0003, 000A, 0019", manufacturer: "sengled", model: "E1C-NB7", deviceJoinName: "Sengled Outlet" //Sengled Smart Plug with Energy Tracker fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-131", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 fingerprint profileId: "0104", manufacturer: "frient A/S", model: "SPLZB-132", deviceJoinName: "frient Outlet" // frient smart plug mini, raw description: 02 0104 0051 10 09 0000 0702 0003 0009 0B04 0006 0004 0005 0002 05 0000 0019 000A 0003 0406 diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 4e9f7be26d1..db8b9119aad 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -45,6 +45,7 @@ metadata { // HEIMAN fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS2SW1L-EFR-3.0", deviceJoinName: "HEIMAN Switch" //HEIMAN Smart Switch + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "HS6ESK-W-EF-3.0", deviceJoinName: "HEIMAN Outlet", ocfDeviceType: "oic.d.smartplug" //HEIMAN Smart Outlet // HONYAR fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", manufacturer: "REX", model: "HY0095", deviceJoinName: "HONYAR Switch" //HONYAR Smart Switch From 8752d1e254eaaf9bba59e63aa5c9e892edd38612 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Mon, 25 Oct 2021 09:05:05 +0200 Subject: [PATCH 310/422] [ICP-14316] Qubino Thermostat - inital 0 report with correct unit for disabled setpoint (#76168) * Added inital 0 report with correct unit for disabled setpoint * Sending straightforward event to ST, instead of a command to device * fixup! Sending straightforward event to ST, instead of a command to device --- .../qubino-flush-thermostat.src/qubino-flush-thermostat.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy index 3bf9ed75393..6660041570d 100644 --- a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy +++ b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy @@ -212,6 +212,7 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport //this device doesn't act like normal thermostat, it can support either 'cool' or 'heat' after configuration if (cmd.parameterNumber == 59 && !state.isThermostatModeSet) { state.supportedModes.add(cmd.scaledConfigurationValue ? "cool" : "heat") + sendEvent([name: cmd.scaledConfigurationValue ? "heatingSetpoint" : "coolingSetpoint", value: 0, unit: temperatureScale, isStateChange: true]) state.isThermostatModeSet = true } createEvent(name: "supportedThermostatModes", value: state.supportedModes.encodeAsJson(), displayed: false) From e0828efc9bc0fcbf7f49ec4ac21e16091729b267 Mon Sep 17 00:00:00 2001 From: lecontr <86373197+lecontr@users.noreply.github.com> Date: Tue, 26 Oct 2021 00:15:05 -0700 Subject: [PATCH 311/422] DevWs for Smartenit, Inc containing containing Smartenit Zigbee Metering Outlet (#71931) * DevWs for Smartenit, Inc containing containing Smartenit Zigbee Metering Outlet * Numerical values substituted by constants. Removed deprecated capability reference. * Removed constants * Removed references to power multiplier and power divisor * - updated copyright date --- .../smartenit-zigbee-metering-outlet.groovy | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy diff --git a/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy b/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy new file mode 100644 index 00000000000..a7d6d846e86 --- /dev/null +++ b/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy @@ -0,0 +1,130 @@ +/* + * Copyright 2021 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. + */ + import groovy.transform.Field + + @Field final MeteringCurrentSummation = 0x0000 + @Field final MeteringInstantDemand = 0x0400 + @Field final Current = 0x0508 + @Field final Voltage = 0x0505 + @Field final EnergyDivisor = 100000 + @Field final CurrentDivisor = 1000 + @Field final SmartenitMfrCode = 0x1075 + @Field final ElectricalMeasurement = 0x0b04 + @Field final ActivePower = 0x050b + @Field final ReportingResponse = 0x07 + +metadata { + // Automatically generated. Make future change here. + definition(name: "Smartenit Zigbee Metering Outlet", namespace: "Smartenit", author: "Luis Contreras", mnmn: "SmartThingsCommunity", vid: "9f4df74b-f0d4-3515-9384-f5297ee3b11c", ocfDeviceType: "oic.d.smartplug", minHubCoreVersion: '000.017.0012') { + capability "Actuator" + capability "Switch" + capability "Power Meter" + capability "Energy Meter" + capability "Voltage Measurement" + capability "Configuration" + capability "monthpublic25501.current" + capability "Refresh" + capability "Sensor" + capability "Health Check" + + fingerprint manufacturer: "Compacta", model: "ZBMSKT1 (4035A)", deviceJoinName: "Smartenit Outlet" // rawDescription 01 0104 0009 00 09 0000 0003 0004 0005 0006 0015 0702 0B04 0B05 00 + } +} + +def getFPoint(String FPointHex){ + log.debug "printing fpointHex ${FPointHex}" + return (Float)Long.parseLong(FPointHex, 16) +} + +// Parse incoming device messages to generate events +def parse(String description) { + log.debug "description is $description" + + def event = zigbee.getEvent(description) + log.debug "event: ${event}" + + if (event) { + if (event.name == "power") { + event = createEvent(name: event.name, value: (event.value as Integer), descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true) + } else if (event.name == "switch") { + def descriptionText = event.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off' + event = createEvent(name: event.name, value: event.value, descriptionText: descriptionText, translatable: true) + } + } else { + def cluster = zigbee.parse(description) + log.debug "cluster def: ${cluster}" + def mapDescription = zigbee.parseDescriptionAsMap(description) + + if (cluster && cluster.clusterId == zigbee.ONOFF_CLUSTER && cluster.command == ReportingResponse) { + if (cluster.data[0] == 0x00) { + log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster + event = createEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } else { + log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + event = null + } + } else if (mapDescription && (mapDescription.clusterInt == zigbee.SIMPLE_METERING_CLUSTER)) { + if (mapDescription.attrInt == MeteringCurrentSummation) { + event = createEvent(name: "energy", value: getFPoint(mapDescription.value)/EnergyDivisor) + } else if (mapDescription.attrInt == MeteringInstantDemand) { + event = createEvent(name: "power", value: getFPoint(mapDescription.value)/EnergyDivisor) + } else { + log.debug "Could not find attribute mapping for ${mapDescription.clusterInt} ${mapDescription.attrInt}" + } + } else if (mapDescription && (mapDescription.clusterInt == ElectricalMeasurement)) { + if (mapDescription.attrInt == Voltage) { + event = createEvent(name: "voltage", value: getFPoint(mapDescription.value)) + } else if (mapDescription.attrInt == Current) { + event = createEvent(name: "current", value: getFPoint(mapDescription.value)/CurrentDivisor, unit: "A") + } + } else { + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug "${cluster}" + } + } + return event ? createEvent(event) : event +} + +def off() { + zigbee.off() +} + +def on() { + zigbee.on() +} +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return zigbee.onOffRefresh() +} + +def refresh() { + zigbee.onOffRefresh() + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER , Voltage) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER , Current) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER , ActivePower) +} + +def configure() { + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity + refresh() + zigbee.onOffConfig(0, 300) + zigbee.electricMeasurementPowerConfig() +} \ No newline at end of file From b9284b36897d5ff0d0d94692a3362d09ee711e9e Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Wed, 27 Oct 2021 14:47:21 +0800 Subject: [PATCH 312/422] HEIMAN Wall Switch HS6SW1A-W-EF-3.0 (#76185) --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index db8b9119aad..b2efdf4d453 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -46,6 +46,7 @@ metadata { // HEIMAN fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS2SW1L-EFR-3.0", deviceJoinName: "HEIMAN Switch" //HEIMAN Smart Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "HEIMAN", model: "HS6ESK-W-EF-3.0", deviceJoinName: "HEIMAN Outlet", ocfDeviceType: "oic.d.smartplug" //HEIMAN Smart Outlet + fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS6SW1A-W-EF-3.0", deviceJoinName: "HEIMAN Switch" //HEIMAN Smart Switch // HONYAR fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", manufacturer: "REX", model: "HY0095", deviceJoinName: "HONYAR Switch" //HONYAR Smart Switch From 45666f9ae2a14a26a4dc5c4cfd712e3cae61cf89 Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Tue, 2 Nov 2021 15:55:30 +0800 Subject: [PATCH 313/422] HEIMAN Wall Switch HS6SW3A-W-EF-3.0 (#76187) * HEIMAN Wall Switch HS6SW3A-W-EF-3.0 * HEIMAN Switch(2/3gang) --- .../zigbee-multi-switch.src/zigbee-multi-switch.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 62a9322e630..04194274d74 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -42,6 +42,8 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", manufacturer: "REX", model: "HY0096", deviceJoinName: "HONYAR Switch 1" //HONYAR 2 Gang Switch 1 fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS2SW3L-EFR-3.0", deviceJoinName: "HEIMAN Switch 1" //HEIMAN 3 Gang Switch 1 fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS2SW2L-EFR-3.0", deviceJoinName: "HEIMAN Switch 1" //HEIMAN 2 Gang Switch 1 + fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS6SW2A-W-EF-3.0", deviceJoinName: "HEIMAN Switch 1" //HEIMAN 2 Gang Switch 1 + fingerprint profileId: "0104", inClusters: "0005, 0004, 0006", outClusters: "0003, 0019", manufacturer: "HEIMAN", model: "HS6SW3A-W-EF-3.0", deviceJoinName: "HEIMAN Switch 1" //HEIMAN 3 Gang Switch 1 // Dawon fingerprint profileId: "0104", inClusters: "0000, 0002, 0004, 0003, 0006, 0009, 0019", manufacturer: "DAWON_DNS", model: "PM-S240-ZB", deviceJoinName: "Dawon Switch 1" //DAWOS DNS In-Wall Switch PM-S240-ZB @@ -272,6 +274,7 @@ private getChildCount() { case "PM-S350-ZB": case "ST-S350-ZB": case "SBM300Z3": + case "HS6SW3A-W-EF-3.0": return 3 case "E220-KR4N0Z0-HA": case "ZB-SW04": From 6f2fff4bd437ffd19acc6cc3e01ead2dc51e8776 Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Fri, 5 Nov 2021 18:04:42 +0800 Subject: [PATCH 314/422] HEIMAN Scene Panel new (#76578) * HEIMAN Scene Panel new * HEIMAN Scene Panel modify * HEIMAN Scene Panel modify1 * HEIMAN Scene Panel modify2 * HEIMAN Scene Panel modify3 --- .../zigbee-scene-keypad.groovy | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/devicetypes/smartthings/zigbee-scene-keypad.src/zigbee-scene-keypad.groovy b/devicetypes/smartthings/zigbee-scene-keypad.src/zigbee-scene-keypad.groovy index 2fddefda37e..eb13b0d8775 100644 --- a/devicetypes/smartthings/zigbee-scene-keypad.src/zigbee-scene-keypad.groovy +++ b/devicetypes/smartthings/zigbee-scene-keypad.src/zigbee-scene-keypad.groovy @@ -30,6 +30,8 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005", outClusters: "0003, 0004, 0005", manufacturer: "REXENSE", model: "0106-G", deviceJoinName: "GDKES Remote Control", vid: "generic-6-button-alt" //GDKES Scene Keypad fingerprint profileId: "0104", inClusters: "0000, 0005", outClusters: "0000, 0005, 0017", manufacturer: "ORVIBO", model: "cef8701bb8664a67a83033c071ef05f2", deviceJoinName: "ORVIBO Remote Control", vid: "generic-3-button-alt" //ORVIBO Scene Keypad fingerprint profileId: "0104", inClusters: "0004", outClusters: "0000, 0001, 0003, 0004, 0005, 0B05", manufacturer: "HEIMAN", model: "E-SceneSwitch-EM-3.0", deviceJoinName: "HEIMAN Remote Control", vid: "generic-4-button-alt" //HEIMAN Scene Keypad + fingerprint profileId: "0104", inClusters: "0004", outClusters: "0000, 0001, 0003, 0004, 0005, 0B05", manufacturer: "HEIMAN", model: "HS6SSA-W-EF-3.0", deviceJoinName: "HEIMAN Scene Panel", vid: "generic-4-button-alt" //HEIMAN Scene Keypad + fingerprint profileId: "0104", inClusters: "0004", outClusters: "0000, 0001, 0003, 0004, 0005, 0B05", manufacturer: "HEIMAN", model: "HS6SSB-W-EF-3.0", deviceJoinName: "HEIMAN Scene Panel", vid: "generic-3-button-alt" //HEIMAN Scene Keypad } @@ -97,7 +99,7 @@ def configure() { def cmds = zigbee.enrollResponse() if (isHeimanButton()) cmds += zigbee.writeAttribute(0x0000, 0x0012, DataType.BOOLEAN, 0x01) + - addHubToGroup(0x000F) + addHubToGroup(0x0010) + addHubToGroup(0x0011) + addHubToGroup(0x0013) + addHubToGroup(0x000F) + addHubToGroup(0x0010) + addHubToGroup(0x0011) + addHubToGroup(0x0012) + addHubToGroup(0x0013) return cmds } @@ -153,19 +155,25 @@ private getSupportedButtonValues() { } private getChildCount() { - if (device.getDataValue("model") == "0106-G") { - return 6 - } else if (device.getDataValue("model") == "HY0048" || device.getDataValue("model") == "E-SceneSwitch-EM-3.0") { - return 4 - } else if (device.getDataValue("model") == "cef8701bb8664a67a83033c071ef05f2") { - return 3 + def modelName = device.getDataValue("model") + switch(modelName) { + case "cef8701bb8664a67a83033c071ef05f2": + case "HS6SSB-W-EF-3.0": + return 3 + case "E-SceneSwitch-EM-3.0": + case "HS6SSA-W-EF-3.0": + case "HY0048": + return 4 + case "0106-G": + return 6 } } private getCLUSTER_GROUPS() { 0x0004 } private boolean isHeimanButton() { - device.getDataValue("model") == "E-SceneSwitch-EM-3.0" + def modelName = device.getDataValue("model") + modelName == "E-SceneSwitch-EM-3.0" || modelName == "HS6SSA-W-EF-3.0" || modelName == "HS6SSB-W-EF-3.0" } private List addHubToGroup(Integer groupAddr) { @@ -179,5 +187,16 @@ private getButtonNum() {[ "02" : 1, "03" : 3, "05" : 4 + ], + "HS6SSA-W-EF-3.0" : [ + "01" : 3, + "02" : 2, + "03" : 4, + "04" : 1 + ], + "HS6SSB-W-EF-3.0" : [ + "02" : 1, + "03" : 3, + "04" : 2 ] ]} \ No newline at end of file From a86d400413190df4e6313e7cb67a9382d069b835 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Tue, 9 Nov 2021 18:52:24 +0800 Subject: [PATCH 315/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Switch (#76128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Switch * commit again * commit again * add profileId、inClusters、outClusters Co-authored-by: 啦啦 王 --- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index b2efdf4d453..497833cad67 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -92,7 +92,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "SONOFF", model: "BASICZBR3", deviceJoinName: "SONOFF Outlet", ocfDeviceType: "oic.d.smartplug" //SONOFF Basic (R3 Zigbee) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "SONOFF", model: "S31 Lite zb", deviceJoinName: "S31 Outlet", ocfDeviceType: "oic.d.smartplug" //S31 Lite zb fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "1000", manufacturer: "SONOFF", model: "01MINIZB", deviceJoinName: "SONOFF 01MINIZB" //01MINIZB - + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, FC57", outClusters: "0019", manufacturer: "SONOFF", model: "S26R2ZB", deviceJoinName: "SONOFF Plug", ocfDeviceType: "oic.d.smartplug" //SONOFF S26R2 Plug + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, FC57", outClusters: "0019", manufacturer: "SONOFF", model: "S40LITE", deviceJoinName: "SONOFF Plug", ocfDeviceType: "oic.d.smartplug" //SONOFF S40Lite Plug + // Terncy fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0019", manufacturer: "", model: "TERNCY-LS01", deviceJoinName: "Terncy Switch" //Terncy Smart Light Socket @@ -187,4 +189,4 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() -} +} \ No newline at end of file From cbfccd9058762ab9765c01216976d730bf267352 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Wed, 10 Nov 2021 18:53:32 +0900 Subject: [PATCH 316/422] DevWs for SHINA SYSTEM containing containing Zigbee Power Meter (#76343) * DevWs for SHINA SYSTEM containing containing Zigbee Power Meter * Update zigbee-power-meter.groovy Remove inClusters and outClusters from the fingerprint and add whole Raw Description in a comment after this fingerprint. * Update zigbee-power-meter.groovy Add "zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 1, 600, 1)" * Update zigbee-power-meter.groovy Fix spacing. --- .../zigbee-power-meter.groovy | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy index 88be0594b85..ee7830a35a0 100644 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -11,6 +11,8 @@ * for the specific language governing permissions and limitations under the License. * */ +import physicalgraph.zigbee.zcl.DataType + metadata { definition (name: "Zigbee Power Meter", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", ocfDeviceType: "x.com.st.d.energymeter", vid: "SmartThings-smartthings-Aeon_Home_Energy_Meter") { capability "Energy Meter" @@ -23,6 +25,7 @@ metadata { fingerprint profileId: "0104", deviceId:"0053", inClusters: "0000, 0003, 0004, 0B04, 0702", outClusters: "0019", manufacturer: "", model: "E240-KR080Z0-HA", deviceJoinName: "Energy Monitor" //Smart Sub-meter(CT Type) fingerprint profileId: "0104", deviceId:"0007", inClusters: "0000,0003,0702", outClusters: "000A", manufacturer: "Develco", model: "ZHEMI101", deviceJoinName: "frient Energy Monitor" // frient External Meter Interface (develco) 02 0104 0007 00 03 0000 0003 0702 01 000A fingerprint profileId: "0104", manufacturer: "Develco Products A/S", model: "EMIZB-132", deviceJoinName: "frient Energy Monitor" // frient Norwegian HAN (develco) 02 0104 0053 00 06 0000 0003 0020 0702 0704 0B04 03 0003 000A 0019 + fingerprint profileId: "0104", manufacturer: "ShinaSystem", model: "PMM-300Z1", deviceJoinName: "SiHAS Energy Monitor" // SIHAS Power Meter 01 0104 0000 01 05 0000 0004 0003 0B04 0702 02 0004 0019 } // tile definitions @@ -47,6 +50,10 @@ metadata { } } +def getATTRIBUTE_READING_INFO_SET() { 0x0000 } +def getATTRIBUTE_HISTORICAL_CONSUMPTION() { 0x0400 } +def getATTRIBUTE_ACTIVE_POWER() { 0x050B } + def parse(String description) { log.debug "description is $description" def event = zigbee.getEvent(description) @@ -55,7 +62,7 @@ def parse(String description) { if (event.name == "power") { def descMap = zigbee.parseDescriptionAsMap(description) log.debug "event : Desc Map: $descMap" - if (descMap.clusterInt == 0x0B04 && descMap.attrInt == 0x050b) { + if (descMap.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && descMap.attrInt == ATTRIBUTE_ACTIVE_POWER) { event.value = event.value/activePowerDivisor event.unit = "W" } else { @@ -80,19 +87,19 @@ def parse(String description) { attrData.each { def map = [:] if (it.isValidForDataType && (it.value != null)) { - if (it.clusterInt == 0x0702 && it.attrInt == 0x0400) { + if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_HISTORICAL_CONSUMPTION) { log.debug "meter" map.name = "power" map.value = zigbee.convertHexToInt(it.value)/powerDivisor map.unit = "W" } - if (it.clusterInt == 0x0B04 && it.attrInt == 0x050b) { + if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_ACTIVE_POWER) { log.debug "meter" map.name = "power" map.value = zigbee.convertHexToInt(it.value)/activePowerDivisor map.unit = "W" } - if (it.clusterInt == 0x0702 && it.attrInt == 0x0000) { + if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_READING_INFO_SET) { log.debug "energy" map.name = "energy" map.value = zigbee.convertHexToInt(it.value)/(energyDivisor * 1000) @@ -120,6 +127,7 @@ def ping() { def refresh() { log.debug "refresh " zigbee.electricMeasurementPowerRefresh() + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET) + zigbee.simpleMeteringPowerRefresh() } @@ -130,14 +138,19 @@ def configure() { log.debug "Configuring Reporting" return refresh() + zigbee.simpleMeteringPowerConfig() + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 1, 600, 1) + zigbee.electricMeasurementPowerConfig() } -private getActivePowerDivisor() { 10 } -private getPowerDivisor() { isFrientSensor() ? 1 : 1000 } -private getEnergyDivisor() { isFrientSensor() ? 1 : 1000 } +private getActivePowerDivisor() { isPMM300Z1() ? 1 : 10 } +private getPowerDivisor() { (isFrientSensor() || isPMM300Z1()) ? 1 : 1000 } +private getEnergyDivisor() { (isFrientSensor() || isPMM300Z1()) ? 1 : 1000 } private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "Develco Products A/S" || device.getDataValue("manufacturer") == "Develco" } + +private Boolean isPMM300Z1() { + device.getDataValue("model") == "PMM-300Z1" +} From 224f8de6d88a32e445dbeeffd6d177dbd42f3130 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Mon, 15 Nov 2021 20:14:16 -0800 Subject: [PATCH 317/422] BUG-3088 Force metadata refresh for CT100 thermostat --- .../smartthings/ct100-thermostat.src/ct100-thermostat.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy index cfb29ecdabe..7c103d20d38 100644 --- a/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy +++ b/devicetypes/smartthings/ct100-thermostat.src/ct100-thermostat.groovy @@ -1,6 +1,6 @@ metadata { // Automatically generated. Make future change here. - definition (name: "CT100 Thermostat", namespace: "smartthings", author: "SmartThings") { + definition (name: "CT100 Thermostat", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", vid: "SmartThings-smartthings-CT100_Thermostat") { capability "Actuator" capability "Temperature Measurement" capability "Relative Humidity Measurement" From 8a8785093b87843307352de1c6d1c9b02d030e68 Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Tue, 16 Nov 2021 19:38:30 +0800 Subject: [PATCH 318/422] DevWs for CoolKit Technology Co.,Ltd containing containing Orvibo Contact Sensor (#76628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing Orvibo Contact Sensor * Modify format * Modify format * Update Orvibo-Contact-Sensor.groovy * Delete orvibo-contact-sensor.groovy Co-authored-by: 啦啦 王 --- .../Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index f36c65ea5f6..a33260a3676 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -30,6 +30,7 @@ metadata { capability "Sensor" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500", outClusters: "0003", manufacturer: "eWeLink", model: "DS01", deviceJoinName: "eWeLink Open/Closed Sensor" //eWeLink Door Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0020,0500,FC57", outClusters: "0003,0019", manufacturer: "eWeLink", model: "SNZB-04P", deviceJoinName: "eWeLink Open/Closed Sensor" //eWeLink Door Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "ORVIBO", model: "e70f96b3773a4c9283c6862dbafb6a99", deviceJoinName: "Orvibo Open/Closed Sensor" fingerprint inClusters: "0000,0001,0003,000F,0020,0500", outClusters: "000A,0019", manufacturer: "Aurora", model: "WindowSensor51AU", deviceJoinName: "Aurora Open/Closed Sensor" //Aurora Smart Door/Window Sensor fingerprint manufacturer: "Aurora", model: "DoorSensor50AU", deviceJoinName: "Aurora Open/Closed Sensor" // Raw Description: 01 0104 0402 00 06 0000 0001 0003 0020 0500 0B05 01 0019 //Aurora Smart Door/Window Sensor From be5125c63b792a297e40bbfdcb6535991a7b307c Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Wed, 17 Nov 2021 16:16:17 +0800 Subject: [PATCH 319/422] DevWs for CoolKit Technology Co.,Ltd containing containing SmartSense Temp/Humidity Sensor (#76624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing SmartSense Temp/Humidity Sensor * Modify format Co-authored-by: 啦啦 王 --- .../smartsense-temp-humidity-sensor.groovy | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 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 7e04310670c..543d629fc53 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 @@ -37,6 +37,7 @@ metadata { //eWeLink fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0405", outClusters: "0003", manufacturer: "eWeLink", model: "TH01", deviceJoinName: "eWeLink Multipurpose Sensor" + fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0020, 0402, 0405, FC57", outClusters: "0003, 0019", manufacturer: "eWeLink", model: "SNZB-02P", deviceJoinName: "eWeLink Multipurpose Sensor" } simulator { @@ -177,12 +178,10 @@ def refresh() { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)+ zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)+ zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000) - } else if (isEWeLinkTh01()) { - return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware - zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware - zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + + } else if (isEWeLink()) { + return zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + zigbee.readAttribute(0x0405, 0x0000) + - zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } else { return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware @@ -211,13 +210,11 @@ def configure() { zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000, DataType.UINT16, 60, 600, 1*100) + zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 60, 600, 0xA) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020, DataType.UINT8, 30, 21600, 0x1) - } else if (isEWeLinkTh01()) { + } else if (isEWeLink()) { 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(3600, 7200) + - zigbee.configureReporting(0x0405, 0x0000, DataType.UINT16, 3600, 7200, null) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 3600, 7200, 0x10) + + zigbee.temperatureConfig(10, 7200, 50) + + zigbee.configureReporting(0x0405, 0x0000, DataType.UINT16, 10, 7200, 300) } else { return refresh() + zigbee.configureReporting(0xFC45, 0x0000, DataType.UINT16, 30, 3600, 100, ["mfgCode": 0x104E]) + // New firmware @@ -231,6 +228,6 @@ private Boolean isFrientSensor() { device.getDataValue("manufacturer") == "frient A/S" } -private Boolean isEWeLinkTh01() { - device.getDataValue("manufacturer") == "eWeLink" && device.getDataValue("model") == "TH01" +private Boolean isEWeLink() { + device.getDataValue("manufacturer") == "eWeLink" } From c193d322a6bcb88ee59b5c560dc0eb1c056c9746 Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Wed, 17 Nov 2021 16:30:40 +0800 Subject: [PATCH 320/422] DevWs for CoolKit Technology Co.,Ltd containing containing Zigbee Motion Detector (#76625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing Zigbee Motion Detector * Modify format Co-authored-by: 啦啦 王 --- .../zigbee-motion-detector.src/zigbee-motion-detector.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy index 3a52885a02d..14003b028af 100644 --- a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy +++ b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy @@ -27,6 +27,7 @@ metadata { capability "Sensor" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0500", outClusters: "0003", manufacturer: "eWeLink", model: "MS01", deviceJoinName: "eWeLink Motion Sensor" //eWeLink Motion Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0001,0003,0020,0500,FC57", outClusters: "0003,0019", manufacturer: "eWeLink", model: "SNZB-03P", deviceJoinName: "eWeLink Motion Sensor" //eWeLink Motion Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "ORVIBO", model: "895a2d80097f4ae2b2d40500d5e03dcc", deviceJoinName: "Orvibo Motion Sensor" //Orvibo Motion Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "Megaman", model: "PS601/z1", deviceJoinName: "INGENIUM Motion Sensor" //INGENIUM ZB PIR Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001", outClusters: "0019", manufacturer: "HEIMAN", model: "PIRSensor-N", deviceJoinName: "HEIMAN Motion Sensor" //HEIMAN Motion Sensor @@ -101,7 +102,7 @@ def parse(String description) { def batteyHandler(String description){ def descMap = zigbee.parseDescriptionAsMap(description) def map = [:] - if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) { + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value && descMap?.attrInt == 0x0021) { map = getBatteryPercentageResult(Integer.parseInt(descMap.value, 16)) } return map @@ -171,7 +172,7 @@ def configure() { 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"]) - return zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 3600, 0x10) + refresh() + return zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 3600, 7200, 0x10) + refresh() } else if (manufacturer == "Third Reality, Inc") { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } else { From 7616f23b21fb44b83d2904fa7610807ce192a4d8 Mon Sep 17 00:00:00 2001 From: Donald Kirker Date: Thu, 18 Nov 2021 17:10:05 -0800 Subject: [PATCH 321/422] CHAD-6599 Add resetEnergyMeter to energy meter device handlers with reset command (#76604) * CHAD-6599 Add resetEnergyMeter to energy meter device handlers with reset command * Add placeholder for device handlers with no reset command --- .../curb/curb-power-meter.src/curb-power-meter.groovy | 4 ++++ .../fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy | 4 ++++ .../fibaro-double-switch-2-usb.groovy | 4 ++++ .../fibaro-double-switch-2-zw5.groovy | 4 ++++ .../fibaro-single-switch-2-zw5.groovy | 4 ++++ .../fibaro-wall-plug-eu-zw5.groovy | 4 ++++ .../fibaro-wall-plug-us-zw5.groovy | 4 ++++ .../fibaro-wall-plug-usb.src/fibaro-wall-plug-usb.groovy | 4 ++++ .../fibaro-walli-dimmer-switch.groovy | 4 ++++ .../fibaro-walli-double-switch.groovy | 4 ++++ .../fibaro-walli-roller-shutter-driver.groovy | 4 ++++ .../fibaro-walli-roller-shutter-venetian.groovy | 4 ++++ .../fibaro-walli-roller-shutter.groovy | 4 ++++ .../qubino-3-phase-meter.src/qubino-3-phase-meter.groovy | 4 ++++ devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy | 4 ++++ .../qubino-flush-2-relay.src/qubino-flush-2-relay.groovy | 4 ++++ .../qubino-flush-shutter.src/qubino-flush-shutter.groovy | 4 ++++ .../smartenit/smartelek-evse.src/smartelek-evse.groovy | 4 ++++ .../smartenit-metering-dual-load-controller.groovy | 4 ++++ .../smartenit-zigbee-metering-outlet.groovy | 5 +++++ .../aeon-home-energy-meter-c3.groovy | 4 ++++ .../aeon-home-energy-meter.groovy | 4 ++++ .../aeon-illuminator-module.groovy | 4 ++++ devicetypes/smartthings/aeon-outlet.src/aeon-outlet.groovy | 4 ++++ .../smartthings/aeon-smartstrip.src/aeon-smartstrip.groovy | 4 ++++ .../child-energy-meter.src/child-energy-meter.groovy | 6 +++++- .../child-metering-switch.src/child-metering-switch.groovy | 4 ++++ .../dawon-zwave-smart-plug.groovy | 4 ++++ .../ezex-smart-electric-switch.groovy | 4 ++++ .../home-energy-meter.src/home-energy-meter.groovy | 4 ++++ .../smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy | 4 ++++ .../qubino-flush-thermostat.groovy | 4 ++++ .../zigbee-metering-dimmer.groovy | 4 ++++ .../zigbee-metering-plug-power-consumption-report.groovy | 4 ++++ .../zigbee-metering-plug.src/zigbee-metering-plug.groovy | 4 ++++ .../zigbee-power-meter.src/zigbee-power-meter.groovy | 3 +++ .../zwave-metering-dimmer.src/zwave-metering-dimmer.groovy | 4 ++++ .../zwave-metering-switch-secure.groovy | 4 ++++ .../zwave-metering-switch.src/zwave-metering-switch.groovy | 4 ++++ .../zwave-multi-metering-switch.groovy | 4 ++++ .../technisat/technisat-dimmer.src/technisat-dimmer.groovy | 4 ++++ .../technisat-on-off-switch.groovy | 4 ++++ .../technisat-roller-shutter-switch.groovy | 4 ++++ .../technisat-series-switch.groovy | 4 ++++ 44 files changed, 177 insertions(+), 1 deletion(-) diff --git a/devicetypes/curb/curb-power-meter.src/curb-power-meter.groovy b/devicetypes/curb/curb-power-meter.src/curb-power-meter.groovy index dfc78e0cfe4..4e0f596f643 100644 --- a/devicetypes/curb/curb-power-meter.src/curb-power-meter.groovy +++ b/devicetypes/curb/curb-power-meter.src/curb-power-meter.groovy @@ -48,6 +48,10 @@ metadata { } } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def handlePower(value) { sendEvent(name: "power", value: value) } diff --git a/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy b/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy index 9b2c41d1a5a..1562609b5c0 100644 --- a/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-dimmer-2-zw5.src/fibaro-dimmer-2-zw5.groovy @@ -111,6 +111,10 @@ def setLevel(level, rate = null ) { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { logging("${device.displayName} - Executing reset()","info") def cmds = [] cmds << zwave.meterV3.meterReset() diff --git a/devicetypes/fibargroup/fibaro-double-switch-2-usb.src/fibaro-double-switch-2-usb.groovy b/devicetypes/fibargroup/fibaro-double-switch-2-usb.src/fibaro-double-switch-2-usb.groovy index 370879f8c7b..4038a8e6f04 100644 --- a/devicetypes/fibargroup/fibaro-double-switch-2-usb.src/fibaro-double-switch-2-usb.groovy +++ b/devicetypes/fibargroup/fibaro-double-switch-2-usb.src/fibaro-double-switch-2-usb.groovy @@ -68,6 +68,10 @@ def off() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { parent.childReset() } 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 9fe9b363a43..71d33d6cae1 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 @@ -94,6 +94,10 @@ def childOff() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { def cmds = [] cmds << [zwave.meterV3.meterReset(), 1] cmds << [zwave.meterV3.meterGet(scale: 0), 1] diff --git a/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy b/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy index 4c66b89a48a..645beea8127 100644 --- a/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-single-switch-2-zw5.src/fibaro-single-switch-2-zw5.groovy @@ -95,6 +95,10 @@ def off() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { def cmds = [] cmds << zwave.meterV3.meterReset() cmds << zwave.meterV3.meterGet(scale: 0) 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 710beff621a..9290030e364 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 @@ -80,6 +80,10 @@ def off() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { def cmds = [] cmds << zwave.meterV3.meterReset() cmds << zwave.meterV3.meterGet(scale: 0) 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 1417165a9d8..eebab0251f8 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 @@ -95,6 +95,10 @@ def off() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { def cmds = [] cmds << [zwave.meterV3.meterReset(), 1] cmds << [zwave.meterV3.meterGet(scale: 0), 1] diff --git a/devicetypes/fibargroup/fibaro-wall-plug-usb.src/fibaro-wall-plug-usb.groovy b/devicetypes/fibargroup/fibaro-wall-plug-usb.src/fibaro-wall-plug-usb.groovy index 13386ea814a..add5ea4ef5e 100644 --- a/devicetypes/fibargroup/fibaro-wall-plug-usb.src/fibaro-wall-plug-usb.groovy +++ b/devicetypes/fibargroup/fibaro-wall-plug-usb.src/fibaro-wall-plug-usb.groovy @@ -46,6 +46,10 @@ def installed() { def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { parent.childReset() } diff --git a/devicetypes/fibargroup/fibaro-walli-dimmer-switch.src/fibaro-walli-dimmer-switch.groovy b/devicetypes/fibargroup/fibaro-walli-dimmer-switch.src/fibaro-walli-dimmer-switch.groovy index 675df2a6b20..aec4c6694dd 100644 --- a/devicetypes/fibargroup/fibaro-walli-dimmer-switch.src/fibaro-walli-dimmer-switch.groovy +++ b/devicetypes/fibargroup/fibaro-walli-dimmer-switch.src/fibaro-walli-dimmer-switch.groovy @@ -364,6 +364,10 @@ def refresh() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { encapSequence([ meterReset(), meterGet(scale: 0) diff --git a/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy b/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy index a6357b31202..5531c592fc8 100644 --- a/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy +++ b/devicetypes/fibargroup/fibaro-walli-double-switch.src/fibaro-walli-double-switch.groovy @@ -394,6 +394,10 @@ def childReset(deviceNetworkId = null) { } } +def resetEnergyMeter() { + reset(1) +} + def reset(endpoint = 1) { log.debug "Resetting endpoint: ${endpoint}" delayBetween([ diff --git a/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy b/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy index 21ae9e826f3..bdcba95b888 100644 --- a/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy +++ b/devicetypes/fibargroup/fibaro-walli-roller-shutter-driver.src/fibaro-walli-roller-shutter-driver.groovy @@ -295,6 +295,10 @@ def setShadeLevel(level) { encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 1) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def refresh() { sendHubCommand([ encap(zwave.switchMultilevelV3.switchMultilevelGet()) diff --git a/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy b/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy index 7cfbf097669..e44e8e4d604 100644 --- a/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy +++ b/devicetypes/fibargroup/fibaro-walli-roller-shutter-venetian.src/fibaro-walli-roller-shutter-venetian.groovy @@ -329,6 +329,10 @@ def setShadeLevel(level) { encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 1) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def setSlats(childDni, level) { state.isManualCommand = false def time = (int) (state.timeOfVenetianMovement * 1.1) diff --git a/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy b/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy index 3cb91b3a7ff..fb76b073651 100644 --- a/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy +++ b/devicetypes/fibargroup/fibaro-walli-roller-shutter.src/fibaro-walli-roller-shutter.groovy @@ -323,6 +323,10 @@ def setShadeLevel(level) { encap(zwave.switchMultilevelV3.switchMultilevelSet(value: Math.min(0x63, level)), 1) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def refresh() { sendHubCommand([ encap(zwave.switchMultilevelV3.switchMultilevelGet()) diff --git a/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy b/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy index e61f2982096..a3d80ddd6ff 100644 --- a/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy +++ b/devicetypes/qubino/qubino-3-phase-meter.src/qubino-3-phase-meter.groovy @@ -66,6 +66,10 @@ def ping() { refresh() } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def parse(String description) { def result = null def cmd = zwave.parse(description) diff --git a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy index 807f7ce7ca7..8147733bff7 100644 --- a/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy +++ b/devicetypes/qubino/qubino-dimmer.src/qubino-dimmer.groovy @@ -473,6 +473,10 @@ def setLevel(value, duration = null) { encapCommands(commands, getStatusDelay) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ diff --git a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy index a23cbf7f262..54ee2647a3e 100644 --- a/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy +++ b/devicetypes/qubino/qubino-flush-2-relay.src/qubino-flush-2-relay.groovy @@ -423,6 +423,10 @@ def childReset(deviceNetworkId) { } } +def resetEnergyMeter() { + reset(1) +} + def reset(endpoint = 1) { log.debug "Resetting endpoint: ${endpoint}" delayBetween([ diff --git a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy index f538774b19d..c11a5cd5a8a 100644 --- a/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy +++ b/devicetypes/qubino/qubino-flush-shutter.src/qubino-flush-shutter.groovy @@ -260,6 +260,10 @@ def setSlats(level) { ]) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def refresh() { [ encap(zwave.switchMultilevelV3.switchMultilevelGet()), diff --git a/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy b/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy index ccd01619da8..4a096ffc6f0 100644 --- a/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy +++ b/devicetypes/smartenit/smartelek-evse.src/smartelek-evse.groovy @@ -299,6 +299,10 @@ def startcharging() { zigbee.command(EVSECluster, StartCharging, "", [mfgCode: SmartenitMfrCode]) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def refresh() { zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation) + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringInstantDemand) + diff --git a/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy b/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy index b78e67d6099..0dc17950d3e 100644 --- a/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy +++ b/devicetypes/smartenit/smartenit-metering-dual-load-controller.src/smartenit-metering-dual-load-controller.groovy @@ -141,6 +141,10 @@ def setLoadtwo(val) { } } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def refresh() { zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringCurrentSummation) + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, MeteringInstantDemand) + diff --git a/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy b/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy index a7d6d846e86..be71653d2dd 100644 --- a/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy +++ b/devicetypes/smartenit/smartenit-zigbee-metering-outlet.src/smartenit-zigbee-metering-outlet.groovy @@ -105,6 +105,11 @@ def off() { def on() { zigbee.on() } + +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ diff --git a/devicetypes/smartthings/aeon-home-energy-meter-c3.src/aeon-home-energy-meter-c3.groovy b/devicetypes/smartthings/aeon-home-energy-meter-c3.src/aeon-home-energy-meter-c3.groovy index d4e09f229fc..b3dfc81b217 100644 --- a/devicetypes/smartthings/aeon-home-energy-meter-c3.src/aeon-home-energy-meter-c3.groovy +++ b/devicetypes/smartthings/aeon-home-energy-meter-c3.src/aeon-home-energy-meter-c3.groovy @@ -125,6 +125,10 @@ def refresh() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { // No V1 available return [ zwave.meterV2.meterReset().format(), diff --git a/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy b/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy index 568740d34ff..fda6a24af10 100644 --- a/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy +++ b/devicetypes/smartthings/aeon-home-energy-meter.src/aeon-home-energy-meter.groovy @@ -147,6 +147,10 @@ def refresh() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { log.debug "reset()..." // No V1 available delayBetween([ diff --git a/devicetypes/smartthings/aeon-illuminator-module.src/aeon-illuminator-module.groovy b/devicetypes/smartthings/aeon-illuminator-module.src/aeon-illuminator-module.groovy index 66e26f71c79..6e050eb088c 100644 --- a/devicetypes/smartthings/aeon-illuminator-module.src/aeon-illuminator-module.groovy +++ b/devicetypes/smartthings/aeon-illuminator-module.src/aeon-illuminator-module.groovy @@ -177,6 +177,10 @@ def refresh() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { return [ zwave.meterV2.meterReset().format(), zwave.meterV2.meterGet().format() diff --git a/devicetypes/smartthings/aeon-outlet.src/aeon-outlet.groovy b/devicetypes/smartthings/aeon-outlet.src/aeon-outlet.groovy index f828c411825..44c4a4237ea 100644 --- a/devicetypes/smartthings/aeon-outlet.src/aeon-outlet.groovy +++ b/devicetypes/smartthings/aeon-outlet.src/aeon-outlet.groovy @@ -126,6 +126,10 @@ def refresh() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { return [ zwave.meterV2.meterReset().format(), zwave.meterV2.meterGet().format() diff --git a/devicetypes/smartthings/aeon-smartstrip.src/aeon-smartstrip.groovy b/devicetypes/smartthings/aeon-smartstrip.src/aeon-smartstrip.groovy index fd506d75300..7aba9fa4b43 100644 --- a/devicetypes/smartthings/aeon-smartstrip.src/aeon-smartstrip.groovy +++ b/devicetypes/smartthings/aeon-smartstrip.src/aeon-smartstrip.groovy @@ -248,6 +248,10 @@ def resetCmd(endpoint = null) { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { delayBetween([resetCmd(null), reset1(), reset2(), reset3(), reset4()]) } diff --git a/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy b/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy index a182b0c19a8..3fc9dea705c 100644 --- a/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy +++ b/devicetypes/smartthings/child-energy-meter.src/child-energy-meter.groovy @@ -37,6 +37,10 @@ metadata { } } +def resetEnergyMeter() { + parent.childReset(device.deviceNetworkId) +} + def refresh() { parent.childRefresh(device.deviceNetworkId) } @@ -47,4 +51,4 @@ def ping() { def installed() { sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [hubHardwareId: device.hub.hardwareID]) -} \ No newline at end of file +} diff --git a/devicetypes/smartthings/child-metering-switch.src/child-metering-switch.groovy b/devicetypes/smartthings/child-metering-switch.src/child-metering-switch.groovy index 26980dd9479..457088e9993 100644 --- a/devicetypes/smartthings/child-metering-switch.src/child-metering-switch.groovy +++ b/devicetypes/smartthings/child-metering-switch.src/child-metering-switch.groovy @@ -66,6 +66,10 @@ def ping() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { parent.childReset(device.deviceNetworkId) } diff --git a/devicetypes/smartthings/dawon-zwave-smart-plug.src/dawon-zwave-smart-plug.groovy b/devicetypes/smartthings/dawon-zwave-smart-plug.src/dawon-zwave-smart-plug.groovy index 2bd3afbde3f..cc593b87d71 100755 --- a/devicetypes/smartthings/dawon-zwave-smart-plug.src/dawon-zwave-smart-plug.groovy +++ b/devicetypes/smartthings/dawon-zwave-smart-plug.src/dawon-zwave-smart-plug.groovy @@ -233,6 +233,10 @@ def configure() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { encapSequence([ meterReset(), meterGet(scale: 0) diff --git a/devicetypes/smartthings/ezex-smart-electric-switch.src/ezex-smart-electric-switch.groovy b/devicetypes/smartthings/ezex-smart-electric-switch.src/ezex-smart-electric-switch.groovy index 7ed24482a84..dfe05ec6596 100755 --- a/devicetypes/smartthings/ezex-smart-electric-switch.src/ezex-smart-electric-switch.groovy +++ b/devicetypes/smartthings/ezex-smart-electric-switch.src/ezex-smart-electric-switch.groovy @@ -108,6 +108,10 @@ def on() { zigbee.on() } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ diff --git a/devicetypes/smartthings/home-energy-meter.src/home-energy-meter.groovy b/devicetypes/smartthings/home-energy-meter.src/home-energy-meter.groovy index 72f1596b0fb..48693aab128 100644 --- a/devicetypes/smartthings/home-energy-meter.src/home-energy-meter.groovy +++ b/devicetypes/smartthings/home-energy-meter.src/home-energy-meter.groovy @@ -101,6 +101,10 @@ def poll() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { delayBetween([ zwave.meterV2.meterReset().format(), zwave.meterV2.meterGet(scale: 0).format() diff --git a/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy b/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy index c883a81c017..b613f5c0c2b 100644 --- a/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy +++ b/devicetypes/smartthings/inovelli-dimmer.src/inovelli-dimmer.groovy @@ -354,6 +354,10 @@ def setLevel(level) { ], 1000) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { def map = [:] if (cmd.meterType == 1 && cmd.scale == 0) { diff --git a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy index 6660041570d..e6a7a539959 100644 --- a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy +++ b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy @@ -285,6 +285,10 @@ def updateSetpoint(setpoint, setpointType) { ] } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def configure() { [ secure(zwave.configurationV1.configurationSet(parameterNumber: 78, scaledConfigurationValue: temperatureScale == 'C' ? 0 : 1, size: 1)), diff --git a/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy b/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy index 1c67c7b9fb3..03ad547bd93 100644 --- a/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-metering-dimmer.src/zigbee-metering-dimmer.groovy @@ -102,6 +102,10 @@ def setLevel(value, rate = null) { zigbee.setLevel(value) + (value?.toInteger() > 0 ? zigbee.on() : []) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ diff --git a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy index 1e147e010a9..2ad7266b121 100644 --- a/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug-power-consumption-report.src/zigbee-metering-plug-power-consumption-report.groovy @@ -110,6 +110,10 @@ def on() { return cmds } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ 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 e28128d91da..8ead950a318 100644 --- a/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy +++ b/devicetypes/smartthings/zigbee-metering-plug.src/zigbee-metering-plug.groovy @@ -138,6 +138,10 @@ def on() { return cmds } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + /** * PING is used by Device-Watch in attempt to reach the Device * */ diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy index ee7830a35a0..36b48b6e924 100644 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -116,6 +116,9 @@ def parse(String description) { } } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} /** * PING is used by Device-Watch in attempt to reach the Device diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index 74a99e03a63..23c8af60299 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -281,6 +281,10 @@ def configure() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { encapSequence([ meterReset(), meterGet(scale: 0) 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 6d6d9309b2e..43b477d923a 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 @@ -276,6 +276,10 @@ def off() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { log.debug "Executing 'reset'" encap(zwave.meterV2.meterReset()) } 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 feb0c8051a2..170107bfdba 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -284,6 +284,10 @@ def configure() { } def reset() { + resetEnergyMeter() +} + +def resetEnergyMeter() { encapSequence([ meterReset(), meterGet(scale: 0) diff --git a/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy b/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy index 7c591ab529c..7b0be10c797 100644 --- a/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-multi-metering-switch.src/zwave-multi-metering-switch.groovy @@ -339,6 +339,10 @@ def childReset(deviceNetworkId) { } } +def resetEnergyMeter() { + reset(1) +} + def reset(endpoint = 1) { log.debug "Resetting endpoint: ${endpoint}" delayBetween([ diff --git a/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy b/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy index d0dfbf0cb67..dba23fd8eb8 100644 --- a/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy +++ b/devicetypes/technisat/technisat-dimmer.src/technisat-dimmer.groovy @@ -199,6 +199,10 @@ def refresh() { ], 1000) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def configure() { log.debug "configure()" def result = [] diff --git a/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy b/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy index f9e5bb6d036..0715c3a15ca 100644 --- a/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy +++ b/devicetypes/technisat/technisat-on-off-switch.src/technisat-on-off-switch.groovy @@ -183,6 +183,10 @@ def refresh() { ]) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def configure() { log.debug "configure()" def result = [] diff --git a/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy b/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy index 04cc08fa301..9ba7079cb06 100644 --- a/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy +++ b/devicetypes/technisat/technisat-roller-shutter-switch.src/technisat-roller-shutter-switch.groovy @@ -228,6 +228,10 @@ def refresh() { ], 1000) } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def configure() { log.debug "configure()" def result = [] diff --git a/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy b/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy index c1287b2e8c7..fbd079005f4 100644 --- a/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy +++ b/devicetypes/technisat/technisat-series-switch.src/technisat-series-switch.groovy @@ -268,6 +268,10 @@ def childReset(deviceNetworkId) { log.debug("childReset from endpoint ${endpoint}") } +def resetEnergyMeter() { + log.debug "resetEnergyMeter: not implemented" +} + def configure() { log.debug "configure()" def result = [] From 58655e6c959f014d4eeb1db631ff81fde8f1e3c8 Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Fri, 19 Nov 2021 16:14:12 +0800 Subject: [PATCH 322/422] DevWs for www.easyiot.tech containing containing ZigBee RGBW Bulb (#76635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for www.easyiot.tech containing containing ZigBee RGBW Bulb * Update zigbee-rgbw-bulb.groovy * Update zigbee-rgbw-bulb.groovy use 2 tabs,please review,thank you Co-authored-by: jiang wegang --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 52cb074bf55..40c753e69e4 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -103,6 +103,8 @@ metadata { // Ajax Online fingerprint manufacturer: "Ajaxonline", model: "AJ-RGBCCT 5 in 1", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" fingerprint manufacturer: "Ajax online Ltd", model: "AJ_ZB30_GU10", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" // Raw Description: 0B 0104 010D 01 08 0000 0003 0004 0005 0006 0008 0300 1000 00 + // Shenzhen C-Lux + fingerprint manufacturer: "Shenzhen C-Lux", model: "CL000ZB", deviceJoinName: "C-Lux Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" } // UI tile definitions From 671aad49fc492d6e939a80318783ecdf2933d225 Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:35:31 +0800 Subject: [PATCH 323/422] DevWs for CoolKit Technology Co.,Ltd containing containing Ikea Button (#76622) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing Ikea Button * Modify format * reformat * reformat Co-authored-by: 啦啦 王 --- .../ikea-button.src/ikea-button.groovy | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index 0463afe45b8..b7a48ada477 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -33,6 +33,8 @@ metadata { fingerprint manufacturer: "KE", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote fingerprint manufacturer: "SOMFY", model: "Situo 4 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_Situo4_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 fingerprint manufacturer: "SOMFY", model: "Situo 1 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 + fingerprint inClusters: "0000, 0001, 0003", outClusters: "0003, 0006", manufacturer: "eWeLink", model: "WB01", deviceJoinName: "eWeLink Button" //eWeLink Button + fingerprint inClusters: "0000, 0001, 0003, 0020, FC57", outClusters: "0003, 0006, 0019", manufacturer: "eWeLink", model: "SNZB-01P", deviceJoinName: "eWeLink Button" //eWeLink Button } tiles { @@ -179,6 +181,7 @@ private void createChildButtonDevices(numberOfButtons) { def installed() { def numberOfButtons = 1 + def supportedButtons = [] if (isIkeaRemoteControl()) { numberOfButtons = 5 @@ -194,7 +197,14 @@ def installed() { createChildButtonDevices(numberOfButtons) } - def supportedButtons = isIkeaOpenCloseRemote() || isSomfy() ? ["pushed"] : ["pushed", "held"] + if (isIkeaOpenCloseRemote() || isSomfy()) { + supportedButtons = ["pushed"] + } else if (isEWeLink()) { + supportedButtons = ["pushed", "held", "double"] + } else { + supportedButtons = ["pushed", "held"] + } + sendEvent(name: "supportedButtonValues", value: supportedButtons.encodeAsJSON(), displayed: false) sendEvent(name: "numberOfButtons", value: numberOfButtons, displayed: false) numberOfButtons.times { @@ -374,6 +384,17 @@ private Map getButtonEvent(Map descMap) { buttonNumber = OPENCLOSESTOP_BUTTONS_ENDPOINTS[endpoint].STOP } } + } else if (isEWeLink()) { + if (descMap.clusterInt == zigbee.ONOFF_CLUSTER) { + buttonNumber = 1 + if (descMap.commandInt == 0x00) { + buttonState = "held" + } else if (descMap.commandInt == 0x01) { + buttonState = "double" + } else { + buttonState = "pushed" + } + } } if (buttonNumber != 0) { @@ -415,6 +436,10 @@ private boolean isSomfySituo4() { isSomfy() && device.getDataValue("model") == "Situo 4 Zigbee" } +private boolean isEWeLink() { + device.getDataValue("manufacturer") == "eWeLink" +} + private Integer getGroupAddrFromBindingTable(description) { log.info "Parsing binding table - '$description'" def btr = zigbee.parseBindingTableResponse(description) From a76b92e009d851f9f602924512fe7f5b11d99e79 Mon Sep 17 00:00:00 2001 From: Kevin LaFramboise Date: Tue, 30 Nov 2021 13:56:18 -0500 Subject: [PATCH 324/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZSE43 Tilt | Shock XS Sensor --- .../zooz-zse43-tilt-shock-xs-sensor.groovy | 386 ++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 devicetypes/zooz/zooz-zse43-tilt-shock-xs-sensor.src/zooz-zse43-tilt-shock-xs-sensor.groovy diff --git a/devicetypes/zooz/zooz-zse43-tilt-shock-xs-sensor.src/zooz-zse43-tilt-shock-xs-sensor.groovy b/devicetypes/zooz/zooz-zse43-tilt-shock-xs-sensor.src/zooz-zse43-tilt-shock-xs-sensor.groovy new file mode 100644 index 00000000000..27eb6f8a979 --- /dev/null +++ b/devicetypes/zooz/zooz-zse43-tilt-shock-xs-sensor.src/zooz-zse43-tilt-shock-xs-sensor.groovy @@ -0,0 +1,386 @@ +/* + * Zooz ZSE43 Tilt | Shock XS Sensor + * + * Changelog: + * + * 2021-11-25 + * - Publication Release + * + * Copyright 2021 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x30: 2, // SensorBinary + 0x55: 1, // Transport Service v2 + 0x59: 1, // AssociationGrpInfo v3 + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo v2 + 0x6C: 1, // Supervision + 0x70: 2, // Configuration v4 + 0x71: 3, // Notification v4 + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd v5 + 0x80: 1, // Battery + 0x84: 2, // WakeUp + 0x85: 2, // Association v3 + 0x86: 1, // Version v2 + 0x87: 1, // Indicator v3 + 0x8E: 2, // Multi Channel Association v4 + 0x9F: 1 // Security 2 +] + +@Field static Map configParams = [ + ledIndicator: [num:1, title:"LED Indicator", size:1, defaultVal:3, options:[0:"LED off", 1:"Blinks on vibration only", 2:"Blinks for open/close only", 3:"Blinks for any status change [DEFAULT]"]], + lowBatteryReports: [num:3, title:"Low Battery Reports", size:1, defaultVal:20, options:[10:"10%", 20:"20% [DEFAULT]", 30:"30%", 40:"40%", 50:"50%"]], + vibrationSensitivity: [num:4, title:"Vibration Sensitivity", size:1, defaultVal:0, options:[0:"High [DEFAULT]", 1:"Medium", 2:"Low"]], + disableEnableSensors: [num:7, title:"Disable / Enable Sensors", size:1, defaultVal:2, options:[0:"Only tilt sensor enabled", 1:"Only vibration sensor enabled", 2:"Both sensors enabled [DEFAULT]"]] +] + +@Field static int contactOnly = 0 +@Field static int vibrationOnly = 1 +@Field static int accessControl = 6 +@Field static int accessControlOpen = 22 +@Field static int accessControlClosed = 23 +@Field static int homeSecurity = 7 +@Field static int homeSecurityVibration = 3 +@Field static int sensorTypeContact = 10 +@Field static int wakeUpInterval = 43200 + +metadata { + definition ( + name: "Zooz ZSE43 Tilt | Shock XS Sensor", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType:"oic.d.sensor", + vid: "11ae8701-e665-34ea-8b46-3ce2ce15d0f3", + mnmn: "SmartThingsCommunity" + ) { + capability "Sensor" + capability "Acceleration Sensor" + capability "Contact Sensor" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Configuration" + capability "platemusic11009.contactVibrationSensor" + capability "platemusic11009.firmware" + capability "platemusic11009.syncStatus" + + // zw:Ss2a type:0701 mfr:027A prod:7000 model:E003 ver:1.10 zwv:7.13 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,72,5A,87,73,80,71,30,70,84,7A + fingerprint mfr:"027A", prod:"7000", model:"E003", deviceJoinName: "Zooz Tilt | Shock Sensor" // Zooz ZSE43 Tilt | Shock XS Sensor + } + + preferences { + configParams.each { name, param -> + if (param.options) { + input name, "enum", + title: param.title, + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } + } + + input "debugLogging", "enum", + title: "Logging:", + required: false, + defaultValue: "1", + options: ["0":"Disabled", "1":"Enabled [DEFAULT]"] + } +} + +def installed() { + logDebug "installed()..." + state.pendingRefresh = true + initialize() +} + +def updated() { + logDebug "updated()..." + initialize() + + if (pendingChanges) { + logForceWakeupMessage("The setting changes will be sent to the device the next time it wakes up.") + } +} + +void initialize() { + state.debugLoggingEnabled = (safeToInt(settings?.debugLogging, 1) != 0) + refreshSyncStatus() + + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((wakeUpInterval * 2) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + if (!device.currentValue("acceleration") || ((device.currentValue("acceleration") == "active") && (getSettingVal("disableEnableSensors") == contactOnly))) { + sendEvent(name: "acceleration", value: "inactive", displayed:false) + } + + if (!device.currentValue("contactVibration")) { + sendEvent(name: "contactVibration", value: "inactive", displayed:false) + } else { + sendContactVibrationEvent(device.currentValue("contact"), device.currentValue("acceleration")) + } +} + +def refresh() { + logDebug "refresh()..." + + if (state.pendingRefresh) { + sendAccelerationEvent("inactive") + } + + refreshSyncStatus() + state.pendingRefresh = true + logForceWakeupMessage("The device will be refreshed the next time it wakes up.") +} + +void logForceWakeupMessage(String msg) { + log.warn "${msg} To force the device to wake up immediately press the action button 4x quickly." +} + +def configure() { + logDebug "configure()..." + sendHubCommand(getRefreshCmds(), 250) +} + +List getRefreshCmds() { + List cmds = [] + + if (state.pendingRefresh || !device.currentValue("battery")) { + cmds << secureCmd(zwave.batteryV1.batteryGet()) + } + + if (state.pendingRefresh || !device.currentValue("firmwareVersion")) { + cmds << secureCmd(zwave.versionV1.versionGet()) + } + + if (state.pendingRefresh || !device.currentValue("contact")) { + cmds << secureCmd(zwave.sensorBinaryV2.sensorBinaryGet(sensorType: sensorTypeContact)) + } + + if (state.wakeUpInterval == null) { + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + state.pendingRefresh = false + return cmds +} + +List getConfigureCmds() { + List cmds = [] + + int changes = pendingChanges + if (changes) { + log.warn "Syncing ${changes} Change(s)" + } + + if (state.wakeUpInterval != wakeUpInterval) { + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalSet(seconds: wakeUpInterval, nodeid:zwaveHubNodeId)) + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + configParams.each { name, param -> + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if (storedVal != settingVal) { + logDebug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: settingVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + return cmds +} + +def ping() { + logDebug "ping()" +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + logDebug "Device Woke Up..." + List cmds = [] + + cmds += getRefreshCmds() + cmds += getConfigureCmds() + + if (!cmds) { + cmds << secureCmd(zwave.batteryV1.batteryGet()) + } + + cmds << secureCmd(zwave.wakeUpV2.wakeUpNoMoreInformation()) + sendHubCommand(cmds, 150) +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + logDebug "Wake Up Interval = ${cmd.seconds} seconds" + state.wakeUpInterval = cmd.seconds + refreshSyncStatus() +} + +void zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + Integer val = (cmd.batteryLevel == 0xFF ? 1 : cmd.batteryLevel) + if (val > 100) { + val = 100 + } + logDebug "Battery is ${val}%" + sendEvent(name:"battery", value:val, unit:"%", isStateChange: true) +} + +void zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { + logDebug "${cmd}" + if (cmd.sensorType == sensorTypeContact) { + sendContactEvent(cmd.sensorValue ? "open" : "closed") + } +} + +void zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + if (cmd.notificationType == accessControl) { + if (cmd.event == accessControlOpen) { + sendContactEvent("open") + } else if (cmd.event == accessControlClosed) { + sendContactEvent("closed") + } else { + logDebug "${cmd}" + } + } else if (cmd.notificationType == homeSecurity) { + sendAccelerationEvent((cmd.event == homeSecurityVibration) ? "active" : "inactive") + } else { + logDebug "${cmd}" + } +} + +void sendContactEvent(String value) { + logDebug "Contact is ${value}" + sendEvent(name: "contact", value: value) + sendContactVibrationEvent(value, device.currentValue("acceleration")) +} + +void sendAccelerationEvent(String value) { + logDebug "Acceleration is ${value}" + sendEvent(name: "acceleration", value: value) + sendContactVibrationEvent(device.currentValue("contact"), value) +} + +void sendContactVibrationEvent(String contactValue, String vibrationValue) { + String value + switch (getSettingVal("disableEnableSensors")) { + case contactOnly: + value = contactValue + break + case vibrationOnly: + value = vibrationValue + break + default: + value = "${contactValue}${vibrationValue.capitalize()}" + } + + if (device.currentValue("contactVibration") != value) { + sendEvent(name: "contactVibration", value: value, displayed: false) + } +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logDebug "${cmd}" + sendEvent(name: "firmwareVersion", value: (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + runIn(4, refreshSyncStatus) + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + int val = cmd.scaledConfigurationValue + state[name] = val + logDebug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${val}" + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: ${cmd}" +} + +void refreshSyncStatus() { + int changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +Integer getPendingChanges() { + int configChanges = safeToInt(configParams.count { name, param -> + (getSettingVal(name) != getStoredVal(name)) + }, 0) + int pendingWakeUpInterval = (state.wakeUpInterval != wakeUpInterval ? 1 : 0) + return (configChanges + pendingWakeUpInterval) +} + +Integer getSettingVal(String name) { + Integer value = safeToInt(settings[name], null) + if ((value == null) && (getStoredVal(name) != null)) { + return configParams[name].defaultVal + } else { + return value + } +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file From 579c60327431042b6768a9adfb65f226337cc60f Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Thu, 2 Dec 2021 22:00:58 +0900 Subject: [PATCH 325/422] WWST-7793, change FP of the ABL E-series and the Samsung Korea B2B Marketing (#76691) * Update zigbee-white-color-temperature-bulb.groovy Change VID/MNMN of Samsung Korea B2B Marketing * Update zigbee-rgbw-bulb.groovy Change VID/MNMN of ABL --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 4 ++-- .../zigbee-white-color-temperature-bulb.groovy | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 40c753e69e4..17001cf5738 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -40,8 +40,8 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-002", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-002" //ITM RGBW // ABL - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-201", deviceJoinName: "Juno Connect", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-201" //E-series - + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-201", deviceJoinName: "Juno Connect", mnmn: "SmartThingsCommunity", vid: "0c0d8ed8-d536-324c-9b80-d4705a55e4df" //E-series + // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010D", manufacturer: "AduroSmart Eria", model: "AD-RGBW3001", deviceJoinName: "Eria Light" //Eria ZigBee RGBW Bulb diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 50426507a0c..b2e1aca9909 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -42,7 +42,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-001", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" //ITM CCT // Samsung Korea B2B Marketing - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "HAN-LIGHT-Z-001", deviceJoinName: "SamsungB2B Light", mnmn: "Samsung Electronics", vid: "HAN-LIGHT-Z-001" //Samsung Korea B2B Marketing CCT + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "HAN-LIGHT-Z-001", deviceJoinName: "SamsungB2B Light", mnmn: "SmartThingsCommunity", vid: "c0b88b06-99f7-3781-a5a8-8a66fccf2bae" //Samsung Korea B2B Marketing CCT // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010C", manufacturer: "AduroSmart Eria", model: "AD-ColorTemperature3001", deviceJoinName: "Eria Light" //Eria ZigBee Color Temperature Bulb From ba57af1a4af810eafa5ac96109d0b4ea91b2cfdb Mon Sep 17 00:00:00 2001 From: Juan Pablo Risso Date: Fri, 3 Dec 2021 13:51:36 -0500 Subject: [PATCH 326/422] C2C-1619 - Ecobee Migration - Remove Groovy --- .../ecobee-sensor.src/ecobee-sensor.groovy | 92 -- .../ecobee-switch.src/ecobee-switch.groovy | 97 -- .../ecobee-thermostat.groovy | 617 -------- .../ecobee-connect.src/ecobee-connect.groovy | 1362 ----------------- .../ecobee-connect.src/i18n/ar-AE.properties | 26 - .../ecobee-connect.src/i18n/bg-BG.properties | 26 - .../ecobee-connect.src/i18n/ca-ES.properties | 26 - .../ecobee-connect.src/i18n/cs-CZ.properties | 26 - .../ecobee-connect.src/i18n/da-DK.properties | 26 - .../ecobee-connect.src/i18n/de-DE.properties | 26 - .../ecobee-connect.src/i18n/el-GR.properties | 26 - .../ecobee-connect.src/i18n/en-GB.properties | 20 - .../ecobee-connect.src/i18n/es-ES.properties | 26 - .../ecobee-connect.src/i18n/es-MX.properties | 26 - .../ecobee-connect.src/i18n/es-US.properties | 20 - .../ecobee-connect.src/i18n/et-EE.properties | 26 - .../ecobee-connect.src/i18n/fi-FI.properties | 26 - .../ecobee-connect.src/i18n/fr-CA.properties | 26 - .../ecobee-connect.src/i18n/fr-FR.properties | 26 - .../ecobee-connect.src/i18n/hr-HR.properties | 26 - .../ecobee-connect.src/i18n/hu-HU.properties | 26 - .../ecobee-connect.src/i18n/it-IT.properties | 26 - .../ecobee-connect.src/i18n/ko-KR.properties | 26 - .../ecobee-connect.src/i18n/nl-NL.properties | 26 - .../ecobee-connect.src/i18n/no-NO.properties | 26 - .../ecobee-connect.src/i18n/pl-PL.properties | 26 - .../ecobee-connect.src/i18n/pt-BR.properties | 26 - .../ecobee-connect.src/i18n/pt-PT.properties | 26 - .../ecobee-connect.src/i18n/ro-RO.properties | 26 - .../ecobee-connect.src/i18n/ru-RU.properties | 26 - .../ecobee-connect.src/i18n/sk-SK.properties | 26 - .../ecobee-connect.src/i18n/sl-SI.properties | 26 - .../ecobee-connect.src/i18n/sq-AL.properties | 26 - .../ecobee-connect.src/i18n/sr-RS.properties | 26 - .../ecobee-connect.src/i18n/sv-SE.properties | 26 - .../ecobee-connect.src/i18n/th-TH.properties | 26 - .../ecobee-connect.src/i18n/tr-TR.properties | 26 - .../ecobee-connect.src/i18n/zh-CN.properties | 26 - 38 files changed, 3040 deletions(-) delete mode 100644 devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy delete mode 100644 devicetypes/smartthings/ecobee-switch.src/ecobee-switch.groovy delete mode 100644 devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy delete mode 100644 smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties delete mode 100644 smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties diff --git a/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy b/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy deleted file mode 100644 index bb20924a21b..00000000000 --- a/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2015 SmartThings - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License - * for the specific language governing permissions and limitations under the License. - * - * Ecobee Sensor - * - * Author: SmartThings - */ -import groovy.json.JsonOutput -metadata { - definition (name: "Ecobee Sensor", namespace: "smartthings", author: "SmartThings") { - capability "Health Check" - capability "Sensor" - capability "Temperature Measurement" - capability "Motion Sensor" - capability "Refresh" - } - - tiles(scale: 2) { - multiAttributeTile(name: "temperature", type: "generic", width: 6, height: 4, canChangeIcon: true) { - tileAttribute ("device.temperature", key: "PRIMARY_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"] - ] - } - } - - standardTile("motion", "device.motion", inactiveLabel: false, width: 2, height: 2) { - state "active", label:"Motion", icon:"st.motion.motion.active", backgroundColor:"#00A0DC" - state "inactive", label:"No Motion", icon:"st.motion.motion.inactive", backgroundColor:"#cccccc" - } - - standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", action:"refresh.refresh", icon:"st.secondary.refresh" - } - - main (["temperature","motion"]) - details(["temperature","motion","refresh"]) - } -} - -def initialize() { - sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "cloud", scheme:"untracked"]), displayed: false) - updateDataValue("EnrolledUTDH", "true") -} - -void installed() { - initialize() -} - -def updated() { - log.debug "updated()" - parent.setSensorName(device.label, device.deviceNetworkId) - initialize() -} - -// Called when the DTH is uninstalled, is this true for cirrus/gadfly integrations? -// Informs parent to purge its associated data -def uninstalled() { - log.debug "uninstalled() parent.purgeChildDevice($device.deviceNetworkId)" - // purge DTH from parent - parent?.purgeChildDevice(this) -} - -def refresh() { - log.debug "refresh, calling parent poll" - parent.poll() -} diff --git a/devicetypes/smartthings/ecobee-switch.src/ecobee-switch.groovy b/devicetypes/smartthings/ecobee-switch.src/ecobee-switch.groovy deleted file mode 100644 index 9375b30bccf..00000000000 --- a/devicetypes/smartthings/ecobee-switch.src/ecobee-switch.groovy +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Ecobee Switch+ - * - * Copyright 2016 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: "Ecobee Switch", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") { - capability "Switch" - capability "Refresh" - capability "Sensor" - capability "Health Check" - } - - simulator { - // TODO: define status and reply messages here - } - - tiles(scale: 2) { - multiAttributeTile(name:"rich-control", type: "generic", canChangeIcon: true){ - tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { - attributeState "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#00a0dc", nextState:"turningOff" - attributeState "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn" - attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#00a0dc", nextState:"turningOff" - attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn" - } - } - - standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { - state "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#00a0dc", nextState:"turningOff" - state "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn" - state "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#00a0dc", nextState:"turningOff" - state "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn" - state "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000" - } - standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") { - state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" - } - main(["switch"]) - details(["rich-control", "refresh"]) - } -} - -// parse events into attributes -def parse(String description) { - log.debug "Parsing '${description}'" -} - -void initialize() { - sendEvent(name: "DeviceWatch-Enroll", value: toJson([protocol: "cloud", scheme:"untracked"]), displayed: false) -} - -void installed() { - log.trace "[DTH] Executing installed() for device=${this.device.displayName}" - initialize() -} - -void updated() { - log.trace "[DTH] Executing updated() for device=${this.device.displayName}" - initialize() -} - -//remove from the selected devices list in SM -void uninstalled() { - log.trace "[DTH] Executing uninstalled() for device=${this.device.displayName}" - parent?.purgeChildDevice(this) -} - -def refresh() { - log.trace "[DTH] Executing 'refresh' for ${this.device.displayName}" - parent?.poll() -} - -def on() { - log.trace "[DTH] Executing 'on' for ${this.device.displayName}" - boolean desiredState = true - parent.controlSwitch( this.device.deviceNetworkId, desiredState ) -} - -def off() { - log.trace "[DTH] Executing 'off' for ${this.device.displayName}" - boolean desiredState = false - parent.controlSwitch( this.device.deviceNetworkId, desiredState ) -} - -def toJson(Map m) { - return groovy.json.JsonOutput.toJson(m) -} diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy deleted file mode 100644 index 3aaa542b3d4..00000000000 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ /dev/null @@ -1,617 +0,0 @@ -/** - * Copyright 2015 SmartThings - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License - * for the specific language governing permissions and limitations under the License. - * - * Ecobee Thermostat - * - * Author: SmartThings - * Date: 2013-06-13 - */ -import groovy.json.JsonOutput -metadata { - definition (name: "Ecobee Thermostat", namespace: "smartthings", author: "SmartThings") { - capability "Actuator" - capability "Thermostat" - capability "Temperature Measurement" - capability "Sensor" - capability "Refresh" - capability "Relative Humidity Measurement" - capability "Health Check" - - // New discrete thermostat capabilities, added to replace "Thermostat" and prepare for migration to st-schema - capability "Thermostat Cooling Setpoint" - capability "Thermostat Heating Setpoint" - capability "Thermostat Mode" - capability "Thermostat Fan Mode" - capability "Thermostat Operating State" - - command "generateEvent" - command "resumeProgram" - command "switchMode" - command "switchFanMode" - command "lowerHeatingSetpoint" - command "raiseHeatingSetpoint" - command "lowerCoolSetpoint" - command "raiseCoolSetpoint" - // To satisfy some SA/rules that incorrectly using poll instead of Refresh - command "poll" - - attribute "thermostat", "string" - attribute "maxHeatingSetpoint", "number" - attribute "minHeatingSetpoint", "number" - attribute "maxCoolingSetpoint", "number" - attribute "minCoolingSetpoint", "number" - attribute "deviceTemperatureUnit", "string" - attribute "deviceAlive", "enum", ["true", "false"] - } - - tiles { - multiAttributeTile(name:"temperature", type:"generic", width:3, height:2, canChangeIcon: true) { - tileAttribute("device.temperature", key: "PRIMARY_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.humidity", key: "SECONDARY_CONTROL") { - attributeState "humidity", label:'${currentValue}%', icon:"st.Weather.weather12" - } - } - standardTile("lowerHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "heatingSetpoint", action:"lowerHeatingSetpoint", icon:"st.thermostat.thermostat-left" - } - valueTile("heatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "heatingSetpoint", label:'${currentValue}° heat', backgroundColor:"#ffffff" - } - standardTile("raiseHeatingSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "heatingSetpoint", action:"raiseHeatingSetpoint", icon:"st.thermostat.thermostat-right" - } - standardTile("lowerCoolSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "coolingSetpoint", action:"lowerCoolSetpoint", icon:"st.thermostat.thermostat-left" - } - valueTile("coolingSetpoint", "device.coolingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "coolingSetpoint", label:'${currentValue}° cool', backgroundColor:"#ffffff" - } - standardTile("raiseCoolSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "heatingSetpoint", action:"raiseCoolSetpoint", icon:"st.thermostat.thermostat-right" - } - standardTile("mode", "device.thermostatMode", width:2, height:2, inactiveLabel: false, decoration: "flat") { - state "off", action:"switchMode", nextState: "updating", icon: "st.thermostat.heating-cooling-off" - state "heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.heat" - state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool" - state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto" - state "emergency heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.emergency-heat" - state "updating", label:"Updating...", icon: "st.secondary.secondary" - } - standardTile("fanMode", "device.thermostatFanMode", width:2, height:2, inactiveLabel: false, decoration: "flat") { - state "auto", action:"switchFanMode", nextState: "updating", icon: "st.thermostat.fan-auto" - state "on", action:"switchFanMode", nextState: "updating", icon: "st.thermostat.fan-on" - state "updating", label:"Updating...", icon: "st.secondary.secondary" - } - valueTile("thermostat", "device.thermostat", width:2, height:1, decoration: "flat") { - state "thermostat", label:'${currentValue}', backgroundColor:"#ffffff" - } - standardTile("refresh", "device.thermostatMode", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "default", action:"refresh.refresh", icon:"st.secondary.refresh" - } - standardTile("resumeProgram", "device.resumeProgram", width:2, height:1, inactiveLabel: false, decoration: "flat") { - state "resume", action:"resumeProgram", nextState: "updating", label:'Resume', icon:"st.samsung.da.oven_ic_send" - state "updating", label:"Working", icon: "st.secondary.secondary" - } - main "temperature" - details(["temperature", "lowerHeatingSetpoint", "heatingSetpoint", "raiseHeatingSetpoint", - "lowerCoolSetpoint", "coolingSetpoint", "raiseCoolSetpoint", "mode", "fanMode", - "thermostat", "resumeProgram", "refresh"]) - } - - preferences { - input "holdType", "enum", title: "Hold Type", - description: "When changing temperature, use Temporary (Until next transition) or Permanent hold (default)", - required: false, options:["Temporary", "Permanent"] - input "deadbandSetting", "number", title: "Minimum temperature difference between the desired Heat and Cool " + - "temperatures in Auto mode:\nNote! This must be the same as configured on the thermostat", - description: "temperature difference °F", defaultValue: 5, - required: false - } - -} - -void installed() { - // The device refreshes every 5 minutes by default so if we miss 2 refreshes we can consider it offline - // Using 12 minutes because in testing, device health team found that there could be "jitter" - initialize() -} -def initialize() { - sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "cloud", scheme:"untracked"]), displayed: false) - updateDataValue("EnrolledUTDH", "true") -} - -def updated() { - log.debug "updated()" - parent.setName(device.label, device.deviceNetworkId) - initialize() -} - -// Called when the DTH is uninstalled, is this true for cirrus/gadfly integrations? -// Informs parent to purge its associated data -def uninstalled() { - log.debug "uninstalled() parent.purgeChildDevice($device.deviceNetworkId)" - // purge DTH from parent - parent?.purgeChildDevice(this) -} - -def ping() { - log.debug "ping() NOP" -} - -// parse events into attributes -def parse(String description) { - log.debug "Parsing '${description}'" -} - -def refresh() { - log.debug "refresh, calling parent poll" - parent.poll() -} - -void poll() { - log.debug "poll not implemented as it is done by parent SmartApp every 5 minutes" -} - -def generateEvent(Map results) { - if(results) { - def linkText = getLinkText(device) - def supportedThermostatModes = ["off"] - def thermostatMode = null - def locationScale = getTemperatureScale() - - results.each { name, value -> - def event = [name: name, linkText: linkText, handlerName: name] - def sendValue = value - - if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) { - sendValue = getTempInLocalScale(value, "F") // API return temperature values in F - event << [value: sendValue, unit: locationScale] - } else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") { - // Old attributes, keeping for backward compatibility - sendValue = getTempInLocalScale(value, "F") // API return temperature values in F - event << [value: sendValue, unit: locationScale, displayed: false] - // Store min/max setpoint in device unit to avoid conversion rounding error when updating setpoints - device.updateDataValue(name+"Fahrenheit", "${value}") - } else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){ - if (value == true) { - supportedThermostatModes << ((name == "auxHeatMode") ? "emergency heat" : name - "Mode") - } - return // as we don't want to send this event here, proceed to next name/value pair - } else if (name=="thermostatFanMode"){ - sendEvent(name: "supportedThermostatFanModes", value: fanModes(), displayed: false) - event << [value: value, data:[supportedThermostatFanModes: fanModes()]] - } else if (name=="humidity") { - event << [value: value, displayed: false, unit: "%"] - } else if (name == "deviceAlive") { - event['displayed'] = false - } else if (name == "thermostatMode") { - thermostatMode = (value == "auxHeatOnly") ? "emergency heat" : value.toLowerCase() - return // as we don't want to send this event here, proceed to next name/value pair - } else if (name == "name") { - return // as we don't want to send this event, proceed to next name/value pair - } else { - event << [value: value.toString()] - } - event << [descriptionText: getThermostatDescriptionText(name, sendValue, linkText)] - sendEvent(event) - } - if (state.supportedThermostatModes != supportedThermostatModes) { - state.supportedThermostatModes = supportedThermostatModes - sendEvent(name: "supportedThermostatModes", value: supportedThermostatModes, displayed: false) - } - if (thermostatMode) { - sendEvent(name: "thermostatMode", value: thermostatMode, data:[supportedThermostatModes:state.supportedThermostatModes], linkText: linkText, - descriptionText: getThermostatDescriptionText("thermostatMode", thermostatMode, linkText), handlerName: "thermostatMode") - } - generateSetpointEvent () - generateStatusEvent () - } -} - -//return descriptionText to be shown on mobile activity feed -def getThermostatDescriptionText(name, value, linkText) { - if(name == "temperature") { - return "temperature is ${value}°${location.temperatureScale}" - - } else if(name == "heatingSetpoint") { - return "heating setpoint is ${value}°${location.temperatureScale}" - - } else if(name == "coolingSetpoint"){ - return "cooling setpoint is ${value}°${location.temperatureScale}" - - } else if (name == "thermostatMode") { - return "thermostat mode is ${value}" - - } else if (name == "thermostatFanMode") { - return "thermostat fan mode is ${value}" - - } else if (name == "humidity") { - return "humidity is ${value} %" - } else { - return "${name} = ${value}" - } -} - -void setHeatingSetpoint(setpoint) { -log.debug "***setHeatingSetpoint($setpoint)" - if (setpoint) { - state.heatingSetpoint = setpoint.toDouble() - runIn(2, "updateSetpoints", [overwrite: true]) - } -} - -def setCoolingSetpoint(setpoint) { -log.debug "***setCoolingSetpoint($setpoint)" - if (setpoint) { - state.coolingSetpoint = setpoint.toDouble() - runIn(2, "updateSetpoints", [overwrite: true]) - } -} - -def updateSetpoints() { - def deviceScale = "F" //API return/expects temperature values in F - def data = [targetHeatingSetpoint: null, targetCoolingSetpoint: null] - def heatingSetpoint = getTempInLocalScale("heatingSetpoint") - def coolingSetpoint = getTempInLocalScale("coolingSetpoint") - if (state.heatingSetpoint) { - data = enforceSetpointLimits("heatingSetpoint", [targetValue: state.heatingSetpoint, - heatingSetpoint: heatingSetpoint, coolingSetpoint: coolingSetpoint]) - } - if (state.coolingSetpoint) { - heatingSetpoint = data.targetHeatingSetpoint ? getTempInLocalScale(data.targetHeatingSetpoint, deviceScale) : heatingSetpoint - coolingSetpoint = data.targetCoolingSetpoint ? getTempInLocalScale(data.targetCoolingSetpoint, deviceScale) : coolingSetpoint - data = enforceSetpointLimits("coolingSetpoint", [targetValue: state.coolingSetpoint, - heatingSetpoint: heatingSetpoint, coolingSetpoint: coolingSetpoint]) - } - state.heatingSetpoint = null - state.coolingSetpoint = null - updateSetpoint(data) -} - -void resumeProgram() { - log.debug "resumeProgram() is called" - - sendEvent("name":"thermostat", "value":"resuming schedule", "description":statusText, displayed: false) - def deviceId = device.deviceNetworkId.split(/\./).last() - if (parent.resumeProgram(deviceId)) { - sendEvent("name":"thermostat", "value":"setpoint is updating", "description":statusText, displayed: false) - } else { - sendEvent("name":"thermostat", "value":"resume failed", "description":statusText, displayed: false) - log.error "Error resumeProgram() check parent.resumeProgram(deviceId)" - } - // Prevent double tap and spamming of resume command - runIn(5, "updateResume", [overwrite: true]) -} - -def updateResume() { - sendEvent("name":"resumeProgram", "value":"resume", descriptionText: "resumeProgram is done", displayed: false, isStateChange: true) - refresh() -} - -def modes() { - return state.supportedThermostatModes -} - -def fanModes() { - // Ecobee does not report its supported fanModes; use hard coded values - ["on", "auto"] -} - -def switchMode() { - def currentMode = device.currentValue("thermostatMode") - def modeOrder = modes() - if (modeOrder) { - def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } - def nextMode = next(currentMode) - switchToMode(nextMode) - } else { - log.warn "supportedThermostatModes not defined" - } -} - -def switchToMode(mode) { - log.debug "switchToMode: ${mode}" - def deviceId = device.deviceNetworkId.split(/\./).last() - // Thermostat's mode for "emergency heat" is "auxHeatOnly" - if (!(parent.setMode(((mode == "emergency heat") ? "auxHeatOnly" : mode), deviceId))) { - log.warn "Error setting mode:$mode" - // Ensure the DTH tile is reset - mode = device.currentValue("thermostatMode") - } - generateModeEvent(mode) - generateStatusEvent() -} - -def switchFanMode() { - def currentFanMode = device.currentValue("thermostatFanMode") - def fanModeOrder = fanModes() - def next = { fanModeOrder[fanModeOrder.indexOf(it) + 1] ?: fanModeOrder[0] } - switchToFanMode(next(currentFanMode)) -} - -def switchToFanMode(fanMode) { - log.debug "switchToFanMode: $fanMode" - def heatingSetpoint = getTempInDeviceScale("heatingSetpoint") - def coolingSetpoint = getTempInDeviceScale("coolingSetpoint") - def deviceId = device.deviceNetworkId.split(/\./).last() - def sendHoldType = holdType ? ((holdType=="Temporary") ? "nextTransition" : "indefinite") : "indefinite" - - if (!(parent.setFanMode(heatingSetpoint, coolingSetpoint, deviceId, sendHoldType, fanMode))) { - log.warn "Error setting fanMode:fanMode" - // Ensure the DTH tile is reset - fanMode = device.currentValue("thermostatFanMode") - } - generateFanModeEvent(fanMode) -} - -def getDataByName(String name) { - state[name] ?: device.getDataValue(name) -} - -def setThermostatMode(String mode) { - log.debug "setThermostatMode($mode)" - def supportedModes = modes() - if (supportedModes) { - mode = mode.toLowerCase() - def modeIdx = supportedModes.indexOf(mode) - if (modeIdx < 0) { - log.warn("Thermostat mode $mode not valid for this thermostat") - return - } - mode = supportedModes[modeIdx] - switchToMode(mode) - } else { - log.warn "supportedThermostatModes not defined" - } -} - -def setThermostatFanMode(String mode) { - log.debug "setThermostatFanMode($mode)" - mode = mode.toLowerCase() - def supportedFanModes = fanModes() - def modeIdx = supportedFanModes.indexOf(mode) - if (modeIdx < 0) { - log.warn("Thermostat fan mode $mode not valid for this thermostat") - return - } - mode = supportedFanModes[modeIdx] - switchToFanMode(mode) -} - -def generateModeEvent(mode) { - sendEvent(name: "thermostatMode", value: mode, data:[supportedThermostatModes: modes()], - isStateChange: true, descriptionText: "$device.displayName is in ${mode} mode") -} - -def generateFanModeEvent(fanMode) { - sendEvent(name: "thermostatFanMode", value: fanMode, data:[supportedThermostatFanModes: fanModes()], - isStateChange: true, descriptionText: "$device.displayName fan is in ${fanMode} mode") -} - -def generateOperatingStateEvent(operatingState) { - sendEvent(name: "thermostatOperatingState", value: operatingState, descriptionText: "$device.displayName is ${operatingState}", displayed: true) -} - -def off() { setThermostatMode("off") } -def heat() { setThermostatMode("heat") } -def emergencyHeat() { setThermostatMode("emergency heat") } -def cool() { setThermostatMode("cool") } -def auto() { setThermostatMode("auto") } - -def fanOn() { setThermostatFanMode("on") } -def fanAuto() { setThermostatFanMode("auto") } -def fanCirculate() { setThermostatFanMode("circulate") } - -// =============== Setpoints =============== -def generateSetpointEvent() { - def mode = device.currentValue("thermostatMode") - def setpoint = getTempInLocalScale("heatingSetpoint") // (mode == "heat") || (mode == "emergency heat") - def coolingSetpoint = getTempInLocalScale("coolingSetpoint") - - if (mode == "cool") { - setpoint = coolingSetpoint - } else if ((mode == "auto") || (mode == "off")) { - setpoint = roundC((setpoint + coolingSetpoint) / 2) - } // else (mode == "heat") || (mode == "emergency heat") - sendEvent("name":"thermostatSetpoint", "value":setpoint, "unit":location.temperatureScale) -} - -def raiseHeatingSetpoint() { - alterSetpoint(true, "heatingSetpoint") -} - -def lowerHeatingSetpoint() { - alterSetpoint(false, "heatingSetpoint") -} - -def raiseCoolSetpoint() { - alterSetpoint(true, "coolingSetpoint") -} - -def lowerCoolSetpoint() { - alterSetpoint(false, "coolingSetpoint") -} - -// Adjusts nextHeatingSetpoint either .5° C/1° F) if raise true/false -def alterSetpoint(raise, setpoint) { - // don't allow setpoint change if thermostat is off - if (device.currentValue("thermostatMode") == "off") { - return - } - def locationScale = getTemperatureScale() - def deviceScale = "F" - def heatingSetpoint = getTempInLocalScale("heatingSetpoint") - def coolingSetpoint = getTempInLocalScale("coolingSetpoint") - def targetValue = (setpoint == "heatingSetpoint") ? heatingSetpoint : coolingSetpoint - def delta = (locationScale == "F") ? 1 : 0.5 - targetValue += raise ? delta : - delta - - def data = enforceSetpointLimits(setpoint, - [targetValue: targetValue, heatingSetpoint: heatingSetpoint, coolingSetpoint: coolingSetpoint], raise) - // update UI without waiting for the device to respond, this to give user a smoother UI experience - // also, as runIn's have to overwrite and user can change heating/cooling setpoint separately separate runIn's have to be used - if (data.targetHeatingSetpoint) { - sendEvent("name": "heatingSetpoint", "value": getTempInLocalScale(data.targetHeatingSetpoint, "F"), - unit: locationScale, eventType: "ENTITY_UPDATE", displayed: false) - } - if (data.targetCoolingSetpoint) { - sendEvent("name": "coolingSetpoint", "value": getTempInLocalScale(data.targetCoolingSetpoint, "F"), - unit: locationScale, eventType: "ENTITY_UPDATE", displayed: false) - } - runIn(5, "updateSetpoint", [data: data, overwrite: true]) -} - -def enforceSetpointLimits(setpoint, data, raise = null) { - def locationScale = getTemperatureScale() - def minSetpoint = (setpoint == "heatingSetpoint") ? device.getDataValue("minHeatingSetpointFahrenheit") : device.getDataValue("minCoolingSetpointFahrenheit") - def maxSetpoint = (setpoint == "heatingSetpoint") ? device.getDataValue("maxHeatingSetpointFahrenheit") : device.getDataValue("maxCoolingSetpointFahrenheit") - minSetpoint = minSetpoint ? Double.parseDouble(minSetpoint) : ((setpoint == "heatingSetpoint") ? 45 : 65) // default 45 heat, 65 cool - maxSetpoint = maxSetpoint ? Double.parseDouble(maxSetpoint) : ((setpoint == "heatingSetpoint") ? 79 : 92) // default 79 heat, 92 cool - def deadband = deadbandSetting ? deadbandSetting : 5 // °F - def delta = (locationScale == "F") ? 1 : 0.5 - def targetValue = getTempInDeviceScale(data.targetValue, locationScale) - def heatingSetpoint = getTempInDeviceScale(data.heatingSetpoint, locationScale) - def coolingSetpoint = getTempInDeviceScale(data.coolingSetpoint, locationScale) - // Enforce min/mix for setpoints - if (targetValue > maxSetpoint) { - targetValue = maxSetpoint - } else if (targetValue < minSetpoint) { - targetValue = minSetpoint - } else if ((raise != null) && ((setpoint == "heatingSetpoint" && targetValue == heatingSetpoint) || - (setpoint == "coolingSetpoint" && targetValue == coolingSetpoint))) { - // Ensure targetValue differes from old. When location scale differs from device, - // converting between C -> F -> C may otherwise result in no change. - targetValue += raise ? delta : - delta - } - // Enforce deadband between setpoints - if (setpoint == "heatingSetpoint") { - heatingSetpoint = targetValue - coolingSetpoint = (heatingSetpoint + deadband > coolingSetpoint) ? heatingSetpoint + deadband : coolingSetpoint - } - if (setpoint == "coolingSetpoint") { - coolingSetpoint = targetValue - heatingSetpoint = (coolingSetpoint - deadband < heatingSetpoint) ? coolingSetpoint - deadband : heatingSetpoint - } - return [targetHeatingSetpoint: heatingSetpoint, targetCoolingSetpoint: coolingSetpoint] -} - -def updateSetpoint(data) { - def deviceId = device.deviceNetworkId.split(/\./).last() - def sendHoldType = holdType ? ((holdType=="Temporary") ? "nextTransition" : "indefinite") : "indefinite" - - if (parent.setHold(data.targetHeatingSetpoint, data.targetCoolingSetpoint, deviceId, sendHoldType)) { - log.debug "updateSetpoint succeed to change setpoints:${data}" - sendEvent("name": "heatingSetpoint", "value": getTempInLocalScale(data.targetHeatingSetpoint, "F"), - unit: getTemperatureScale(), eventType: "ENTITY_UPDATE", displayed: false) - sendEvent("name": "coolingSetpoint", "value": getTempInLocalScale(data.targetCoolingSetpoint, "F"), - unit: getTemperatureScale(), eventType: "ENTITY_UPDATE", displayed: false) - generateStatusEvent() - } else { - log.error "Error updateSetpoint" - runIn(5, "refresh", [overwrite: true]) - } -} - -def generateStatusEvent() { - def mode = device.currentValue("thermostatMode") - def heatingSetpoint = device.currentValue("heatingSetpoint") - def coolingSetpoint = device.currentValue("coolingSetpoint") - def temperature = device.currentValue("temperature") - def statusText = "Right Now: Idle" - def operatingState = "idle" - - if (mode == "heat" || mode == "emergency heat") { - if (temperature < heatingSetpoint) { - statusText = "Heating to ${heatingSetpoint}°${location.temperatureScale}" - operatingState = "heating" - } - } else if (mode == "cool") { - if (temperature > coolingSetpoint) { - statusText = "Cooling to ${coolingSetpoint}°${location.temperatureScale}" - operatingState = "cooling" - } - } else if (mode == "auto") { - if (temperature < heatingSetpoint) { - statusText = "Heating to ${heatingSetpoint}°${location.temperatureScale}" - operatingState = "heating" - } else if (temperature > coolingSetpoint) { - statusText = "Cooling to ${coolingSetpoint}°${location.temperatureScale}" - operatingState = "cooling" - } - } else if (mode == "off") { - statusText = "Right Now: Off" - } else { - statusText = "?" - } - - sendEvent("name":"thermostat", "value":statusText, "description":statusText, displayed: true) - sendEvent("name":"thermostatOperatingState", "value":operatingState, "description":operatingState, displayed: false) -} - -def generateActivityFeedsEvent(notificationMessage) { - sendEvent(name: "notificationMessage", value: "$device.displayName $notificationMessage", descriptionText: "$device.displayName $notificationMessage", displayed: true) -} - -// Get stored temperature from currentState in current local scale -def getTempInLocalScale(state) { - def temp = device.currentState(state) - def scaledTemp = convertTemperatureIfNeeded(temp.value.toBigDecimal(), temp.unit).toDouble() - return (getTemperatureScale() == "F" ? scaledTemp.round(0).toInteger() : roundC(scaledTemp)) -} - -// Get/Convert temperature to current local scale -def getTempInLocalScale(temp, scale) { - def scaledTemp = convertTemperatureIfNeeded(temp.toBigDecimal(), scale).toDouble() - return (getTemperatureScale() == "F" ? scaledTemp.round(0).toInteger() : roundC(scaledTemp)) -} - -// Get stored temperature from currentState in device scale -def getTempInDeviceScale(state) { - def temp = device.currentState(state) - if (temp && temp.value && temp.unit) { - return getTempInDeviceScale(temp.value.toBigDecimal(), temp.unit) - } - return 0 -} - -def getTempInDeviceScale(temp, scale) { - if (temp && scale) { - //API return/expects temperature values in F - return ("F" == scale) ? temp : celsiusToFahrenheit(temp).toDouble().round(0).toInteger() - } - return 0 -} - -def roundC (tempC) { - return (Math.round(tempC.toDouble() * 2))/2 -} diff --git a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy deleted file mode 100644 index ca7f0cc9be0..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy +++ /dev/null @@ -1,1362 +0,0 @@ -/** - * Copyright 2015 SmartThings - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License - * for the specific language governing permissions and limitations under the License. - * - * Ecobee Service Manager - * - * Author: scott - * Date: 2013-08-07 - * - * Last Modification: - * JLH - 01-23-2014 - Update for Correct SmartApp URL Format - * JLH - 02-15-2014 - Fuller use of ecobee API - * 10-28-2015 DVCSMP-604 - accessory sensor, DVCSMP-1174, DVCSMP-1111 - not respond to routines - */ -import groovy.json.JsonSlurper -include 'localization' - -definition( - name: "Ecobee (Connect)", - namespace: "smartthings", - author: "SmartThings", - description: "Connect your Ecobee thermostat to SmartThings.", - category: "SmartThings Labs", - iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee.png", - iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee@2x.png", - singleInstance: true, - usesThirdPartyAuthentication: true, - pausable: false -) { - appSetting "clientId" - appSetting "serverUrl" // See note below - // NOTE regarding OAuth settings. On NA01 (i.e. graph.api) and NA01S the serverUrl app setting can be left - // Blank. For other shards is should be set to the callback URL registered with Honeywell, which is: - // - // Production -- https://graph.api.smartthings.com - // Staging -- https://graph-na01s-useast1.smartthingsgdev.com -} - -preferences { - page(name: "auth", title: "ecobee", nextPage:"", content:"authPage", uninstall: true, install:false) - page(name: "deviceList", title: "ecobee", content:"ecobeeDeviceList", install:true) -} - -mappings { - path("/oauth/initialize") {action: [GET: "oauthInitUrl"]} - path("/oauth/callback") {action: [GET: "callback"]} -} - -def authPage() { - log.debug "authPage()" - // Make sure poll/devices are not unscheduled/silenced when authPage is called when the app exits. - // For some reason the first page is called when the app exits normally. - if (!state.initializeEndTime || (now() - state.initializeEndTime > 2000)) { - // Make sure the poll is stopped to prevent state changes while user is configuring the app - unschedule() - // Schedule pollRestart in 15 minutes in case user exits the app abnormally. TODO: Is 15min short/long enough? - runIn(15*60, "restartPoll") - // TODO Make sure no child is calling any poll or command methods to prevent state changes - //def childDevices = getChildDevices() - //if (childDevices) { - // childDevices*.parentBusy(true) - //} - } - - if(!state.accessToken) { //this is to access token for 3rd party to make a call to connect app - state.accessToken = createAccessToken() - } - - def description - def uninstallAllowed = false - def oauthTokenProvided = false - - if(state.authToken) { - if(!state.jwt) { - state.jwt = true - refreshAuthToken() - } - description = "You are connected." - uninstallAllowed = true - oauthTokenProvided = true - } else { - description = "Click to enter Ecobee Credentials" - } - - def redirectUrl = buildRedirectUrl - //log.debug "RedirectUrl = ${redirectUrl}" - // get rid of next button until the user is actually auth'd - if (!oauthTokenProvided) { - return dynamicPage(name: "auth", title: "Login", nextPage: "", uninstall:uninstallAllowed) { - section() { - paragraph "Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button." - href url:redirectUrl, style:"embedded", required:true, title:"ecobee", description:description - } - } - } else { - return dynamicPage(name: "auth", title: "Log In", nextPage:"deviceList", install: false, uninstall:uninstallAllowed) { - section(){ - paragraph "Tap Next to continue to set up your ecobee thermostats." - href url:redirectUrl, style:"embedded", state:"complete", title:"ecobee", description:description - } - } - } -} - -def restartPoll() { - // This method should only be called in case the SA was terminated abnormally without - // calling initialize which will unschedule this and start the poll as part of the normal flow - // TODO Make sure child is calling any poll or command methods to prevent state changes - //def childDevices = getChildDevices() - //if (childDevices) { - // childDevices*.parentBusy(false) - //} - // Call poll - unschedule() - poll() - runEvery5Minutes("poll") -} - -def ecobeeDeviceList() { - getEcobeeDevices() - - def thermostatList = thermostatsDiscovered() - def numThermostats = thermostatList.size() - def sensors = sensorsDiscovered() - def numSensors = sensors.size() - def switches = switchesDiscovered() - def numSwitches = switches.size() - - if (!numThermostats && !numSensors && !numSwitches) { - return dynamicPage(name: "deviceList", title: "No devices found", uninstall: true) { - section ("") { - paragraph "Could not find any devices avilable for SmartThings to control. Please check your ecobee account and retry later." - } - } - } - - return dynamicPage(name: "deviceList", title: "Select Your ecobee Devices", uninstall: true) { - if (numThermostats > 0) { - def preselectedThermostats = thermostatList.collect{it.key} - section("") { - paragraph "Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings." - input(name: "thermostats", title:"Select ecobee Thermostats ({{numThermostats}} found)", messageArgs: [numThermostats: numThermostats], - type: "enum", required:false, multiple:true, - description: "Tap to choose", metadata:[values:thermostatList], defaultValue: preselectedThermostats) - } - } - if (numSensors > 0) { - def preselectedSensors = sensors.collect{it.key} - section("") { - paragraph "Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings." - input(name: "ecobeesensors", title: "Select ecobee remote sensors ({{numSensors}} found)", messageArgs: [numSensors: numSensors], - type: "enum", required:false, description: "Tap to choose", multiple:true, options:sensors, defaultValue: preselectedSensors) - } - } - if (numSwitches > 0) { - def preselectedSwitches = switches.collect{it.key} - section("") { - paragraph "Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings." - input(name: "ecobeeswitches", title: "Select ecobee switches ({{numSwitches}} found)", messageArgs: [numSwitches: numSwitches], - type: "enum", required:false, description: "Tap to choose", multiple:true, options:switches, defaultValue: preselectedSwitches) - } - } - } -} - -def oauthInitUrl() { - log.debug "oauthInitUrl with callback: ${callbackUrl}" - - state.oauthInitState = UUID.randomUUID().toString() - - def oauthParams = [ - response_type: "code", - scope: "smartRead,smartWrite", - client_id: smartThingsClientId, - state: state.oauthInitState, - redirect_uri: callbackUrl - ] - - redirect(location: "${apiEndpoint}/authorize?${toQueryString(oauthParams)}") -} - -def callback() { - log.debug "callback()>> params: $params, params.code ${params.code}" - - def code = params.code - def oauthState = params.state - - if (oauthState == state.oauthInitState) { - def tokenParams = [ - grant_type: "authorization_code", - code : code, - client_id : smartThingsClientId, - redirect_uri: callbackUrl - ] - - def tokenUrl = "${apiEndpoint}/token?${toQueryString(tokenParams)}" - - httpPost(uri: tokenUrl) { resp -> - state.refreshToken = resp.data.refresh_token - state.authToken = resp.data.access_token - } - if ( state.authToken ) { - // get jwt for switch+ devices - state.jwt = true - refreshAuthToken() - } - if (state.authToken) { - success() - } else { - fail() - } - - } else { - log.error "callback() failed oauthState != state.oauthInitState" - } -} - -def success() { - def message = """ -

Your ecobee Account is now connected to SmartThings!

-

Click 'Done' to finish setup.

- """ - connectionStatus(message) -} - -def fail() { - def message = """ -

The connection could not be established!

-

Click 'Done' to return to the menu.

- """ - connectionStatus(message) -} - -def connectionStatus(message, redirectUrl = null) { - def redirectHtml = "" - if (redirectUrl) { - redirectHtml = """ - - """ - } - - def html = """ - - - - - Ecobee & SmartThings connection - - - -
- ecobee icon - connected device icon - SmartThings logo - ${message} -
- - - """ - - render contentType: 'text/html', data: html -} - -def getEcobeeDevices() { - log.debug "getting device list" - state.remoteSensors = [] // reset depriciated application state, replaced by remoteSensors2 - - def thermostatList = [:] - def remoteSensors = [:] - def switchList = [:] - def isThermostatPolled = false - def isSwitchesPolled = false - def pollAttempt = 1 - // try obtain devices twice, in case authToken needs to be refreshed - while (!(isThermostatPolled && isSwitchesPolled) && (pollAttempt < 3)) { - try { - // First get thermostats their remote sensors - if (!isThermostatPolled) { - def bodyParams = [ - selection: [ - selectionType: "registered", - selectionMatch: "", - includeSettings: true, - includeRuntime: true, - includeSensors: true - ] - ] - def deviceListParams = [ - uri: apiEndpoint, - path: "/1/thermostat", - headers: ["Content-Type": "text/json", "Authorization": "Bearer ${state.authToken}"], - // TODO - the query string below is not consistent with the Ecobee docs: - // https://www.ecobee.com/home/developer/api/documentation/v1/operations/get-thermostats.shtml - query: [format: 'json', body: toJson(bodyParams)] - ] - httpGet(deviceListParams) { resp -> - isThermostatPolled = true - if (resp.status == 200) { - resp.data.thermostatList.each { stat -> - def dni = [ app.id, stat.identifier ].join('.') - def data = getThermostatData(stat) - thermostatList[dni] = thermostatList[dni] ? thermostatList[dni] << [data:data] : [data:data] - thermostatList[dni].polled = true - thermostatList[dni].pollAttempts = 0 - // compile all remote sensors conected to the thermostat - stat.remoteSensors.each { sensor -> - if (sensor.type == "ecobee3_remote_sensor") { - def rsDni = "ecobee_sensor-"+ sensor?.id + "-" + sensor?.code - remoteSensors[rsDni] = sensor - remoteSensors[rsDni] << [thermostatId: dni] - } - } - } - state.remoteSensors2 = remoteSensors - state.thermostats = thermostatList - } else { - log.debug "Failed to get thermostats and sensors, status:${resp.status}" - } - } - } - // Now get light swiches - if (!isSwitchesPolled) { - def switchListParams = [ - uri: apiEndpoint + "/ea/devices", - headers: ["Content-Type": "application/json;charset=UTF-8", "Authorization": "Bearer ${state.authToken}"], - ] - httpGet(switchListParams) { resp -> - isSwitchesPolled = true - if (resp.status == 200) { - resp.data?.devices?.each { - if (it.type == "LIGHT_SWITCH") { - switchList[it?.identifier] = it - switchList[it?.identifier] << [deviceAlive: (it?.connected ?: false)] - } - } - state.switchList = switchList - } else { - log.warn "Unable to get switch device list, status:${resp.status}" - } - } - } - } catch (groovyx.net.http.HttpResponseException e) { - log.error "Exception getEcobeeDevices: ${e?.getStatusCode()}, e:${e}, data:${e.response?.data}" - if (e.response?.data?.status?.code == 14) { - pollAttempt++ - if (pollAttempt > 2 || !refreshAuthToken()) { - pollAttempt = 3 - log.error "Ecobee failed getting devices despite refreshing authToken" - } - } - } catch (Exception e) { - log.error "Unhandled exception $e in getEcobeeDevices tried:${pollAttempt} times" - // break the loop and exit - pollAttempt = 3 - } - } -} - -Map thermostatsDiscovered() { - def map = [:] - def thermostatList = state.thermostats ?: [:] - thermostatList.each { key, stat -> - map[key] = stat.data.name - } - return map -} - -Map sensorsDiscovered() { - def map = [:] - def remoteSensors = state.remoteSensors2 ?: [:] - remoteSensors.each { key, sensors -> - map[key] = sensors.name - } - return map -} - -def switchesDiscovered() { - def map = [:] - def switches = state.switchList ?: [:] - switches.each { key, ecobeeSwitch -> - map[key] = ecobeeSwitch.name - } - return map -} - -def getThermostatDisplayName(stat) { - if(stat?.name) { - return stat.name.toString() - } - return (getThermostatTypeName(stat) + " (${stat.identifier})").toString() -} - -def getThermostatTypeName(stat) { - return stat.modelNumber == "siSmart" ? "Smart Si" : "Smart" -} - -def installed() { - log.debug "Installed with settings: ${settings}" - // initialize will be called by the updated method -} - -def updated() { - log.debug "Updated with settings: ${settings}" - unsubscribe() - unschedule() - initialize() -} - -def initialize() { - def thermostatList = state.thermostats ?: [:] - def remoteSensors = state.remoteSensors2 ?: [:] - def switchList = state.switchList ?: [:] - def childThermostats = thermostats.collect { dni -> - def d = getChildDevice(dni) - if(!d) { - d = addChildDevice(app.namespace, getChildName(), dni, null, ["label":"${thermostatList[dni].data.name}" ?: getChildName()]) - log.debug "created ${d.displayName} with id $dni" - // initialize DTH with default data will be done using the first poll data - // TODO: Move this to DTH install method - d.generateEvent(thermostatList[dni].data) - } else { - log.debug "found ${d.displayName} with id $dni already exists" - } - return d - } - def childSensors = ecobeesensors.collect { dni -> - def d = getChildDevice(dni) - if(!d) { - d = addChildDevice(app.namespace, getSensorChildName(), dni, null, ["label":remoteSensors[dni].name ?: getSensorChildName()]) - log.debug "created ${d.displayName} with id $dni" - // initialize DTH with default data - TODO: Move this to DTH install method - d.sendEvent(name:"temperature", value: 0, unit: location.temperatureScale, - descriptionText: "temperature is unknown", displayed: true) - d.sendEvent(name:"motion", value: "inactive") - } else { - log.debug "found ${d.displayName} with id $dni already exists" - } - return d - } - def childSwitches = ecobeeswitches.collect { dni -> - def d = getChildDevice(dni) - if(!d) { - d = addChildDevice(app.namespace, getSwitchChildName(), dni, null, ["label":"${switchList[dni].name}" ?: getSwitchChildName()]) - log.debug "created ${d.displayName} with id $dni" - // initialize DTH with default data - TODO: Move this to DTH install method - d.sendEvent(name:"switch", value: "off") - } else { - log.debug "found ${d.displayName} with id $dni already exists" - } - return d - } - - log.debug "Now have ${childThermostats.size()} thermostats, ${childSensors.size()} sensors and ${childSwitches.size()} switches" - - def delete // Delete any that are no longer in settings - if(!thermostats && !ecobeesensors && !ecobeeswitches) { - log.debug "delete thermostats ands sensors" - delete = getAllChildDevices() //inherits from SmartApp (data-management) - } else { //delete only thermostat - log.debug "delete individual thermostat and sensor" - delete = getChildDevices().findAll { - !thermostats?.contains(it.deviceNetworkId) && - !ecobeesensors?.contains(it.deviceNetworkId) && - !ecobeeswitches?.contains(it.deviceNetworkId) - } - } - log.warn "force deleting devices ${delete}" - delete.each { deleteChildDevice(it.deviceNetworkId, true) } //inherits from SmartApp (data-management) - - // TODO Schedule purge of uninstalled device data as it takes some time before the child is gone - runIn(10, "purgeUninstalledDeviceData", [overwrite: true]) - - //send activity feeds to tell that device is connected - def notificationMessage = "is connected to SmartThings" - sendActivityFeeds(notificationMessage) - state.timeSendPush = null - state.reAttempt = 0 - -// pollHandler() //first time polling data data from thermostat - // clear depreciated data - state.remoteSensors = [] - state.sensors = [] - //automatically update devices status every 5 mins - runEvery5Minutes("poll") - poll() - state.initializeEndTime = now() -} - -def purgeUninstalledDeviceData() { - // purge state from devices that are not selected - def thermostatList = state.thermostats ?: [:] - def remoteSensors = state.remoteSensors2 ?: [:] - def switchList = state.switchList ?: [:] - - // clean up device lists - thermostatList.keySet().removeAll(thermostatList.keySet() - thermostats) - remoteSensors.keySet().removeAll(remoteSensors.keySet() - ecobeesensors) - switchList.keySet().removeAll(switchList.keySet() - ecobeeswitches) - state.thermostats = thermostatList - state.remoteSensors2 = remoteSensors - state.switchList = switchList -} - -def purgeChildDevice(childDevice) { - def dni = childDevice.device.deviceNetworkId - def thermostatList = state.thermostats ?: [:] - def remoteSensors = state.remoteSensors2 ?: [:] - def switchList = state.switchList ?: [:] - if (thermostatList[dni]) { - thermostatList.remove(dni) - state.thermostats = thermostatList - if (thermostats) { - thermostats.remove(dni) - } - app.updateSetting("thermostats", thermostats ? thermostats : []) - } else if (remoteSensors[dni]){ - remoteSensors.remove(dni) - state.remoteSensors2 = remoteSensors - if (ecobeesensors) { - ecobeesensors.remove(dni) - } - app.updateSetting("ecobeesensors", ecobeesensors ? ecobeesensors : []) - } else if(switchList[dni]) { - switchList.remove(dni) - state.switchList = switchList - if (ecobeeswitches) { - ecobeeswitches.remove(dni) - } - app.updateSetting("ecobeeswitches", ecobeeswitches ? ecobeeswitches : []) - } else { - log.error "Failed to purge data for childDevice dni:$dni" - } - if (getChildDevices().size <= 1) { - log.info "No more thermostats to poll, unscheduling" - unschedule() - state.authToken = null - runIn(1, "terminateMe") - } -} - -def terminateMe() { - log.info "terminateMe" - try { - app.delete() - } catch (Exception e) { - log.error "Termination failed, I’m invincible!" - } -} - -def poll() { - // No need to keep trying to poll if authToken is null - if (!state.authToken) { - log.info "poll failed due to authToken=null" - def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials." - sendPushAndFeeds(notificationMessage) - markChildrenOffline(true) - unschedule() - unsubscribe() - return - } - def isThermostatPolled = !(thermostats || ecobeesensors) // If no thermostats or sensors, mark them polled - def isSwitchesPolled = !(ecobeeswitches) // If no switches, mark them polled - def pollAttempt = 1 - - // Mark all devices as offline for device health - def remoteSensors = state.remoteSensors2 ?: [:] - def thermostatList = state.thermostats ?: [:] - def switchList = state.switchList ?: [:] - remoteSensors.each { rdni, sensor -> - sensor.deviceAlive = false - sensor.polled = false - } - thermostatList.each { dni, stat -> - stat.polled = false - stat.data = stat.data ? stat.data << [deviceAlive:false] : [deviceAlive:false] - } - switchList.each { sdni, sw -> - sw.deviceAlive = false - sw.polled = false - } - state.remoteSensors2 = remoteSensors - state.thermostats = thermostatList - state.switchList = switchList - - while (!(isThermostatPolled && isSwitchesPolled) && (pollAttempt < 3)) { - try{ - // First check if we need to poll thermostats or sensors - if (!isThermostatPolled) { - def requestBody = [ - selection: [ - selectionType: "registered", - selectionMatch: "", - includeExtendedRuntime: true, - includeSettings: true, - includeRuntime: true, - includeSensors: true - ] - ] - def pollParams = [ - uri: apiEndpoint, - path: "/1/thermostat", - headers: ["Content-Type": "text/json", "Authorization": "Bearer ${state.authToken}"], - // TODO - the query string below is not consistent with the Ecobee docs: - // https://www.ecobee.com/home/developer/api/documentation/v1/operations/get-thermostats.shtml - query: [format: 'json', body: toJson(requestBody)] - ] - - httpGet(pollParams) { resp -> - isThermostatPolled = true - if(resp.status == 200) { - storeThermostatData(resp.data.thermostatList) - if (ecobeesensors) { - updateSensorData(resp.data.thermostatList.remoteSensors) - } - } - } - } - // Check if we have switches that needs to be polled - if (!isSwitchesPolled) { - def switchListParams = [ - uri: apiEndpoint + "/ea/devices", - headers: ["Content-Type": "application/json;charset=UTF-8", "Authorization": "Bearer ${state.authToken}"], - ] - - httpGet(switchListParams) { resp -> - isSwitchesPolled = true - if (resp.status == 200) { - updateSwitches(resp.data?.devices) - } else { - log.warn "Unable to get switch device list!" - } - } - } - - } catch (groovyx.net.http.HttpResponseException e) { - log.info "HttpResponseException ${e}, ${e?.getStatusCode()} polling ecobee pollAttempt:${pollAttempt}, " + - "isThermostatPolled:${isThermostatPolled}, isSwitchesPolled:${isSwitchesPolled}, ${e?.response?.data}" - if (e?.getStatusCode() == 401 || e?.response?.data?.status?.code == 14) { - pollAttempt++ - // Try refresh authToken and try poll one more time - if (pollAttempt > 2 || !refreshAuthToken()) { - // refresh of authToken failed, break the loop and exit - pollAttempt = 3 - log.error "Ecobee poll failed despite refreshing authToken" - } - } else { - log.error "Ecobee poll failed for other reason than expired authToken" - // break the loop and exit - pollAttempt = 3 - } - } catch (Exception e) { - log.error "Unhandled exception $e in ecobee polling pollAttempt:${pollAttempt}, " + - "isThermostatPolled:${isThermostatPolled}, isSwitchesPolled:${isSwitchesPolled}" - // break the loop and exit - pollAttempt = 3 - } - } - markChildrenOffline() - log.trace "poll exit pollAttempt:${pollAttempt}, isThermostatPolled:${isThermostatPolled}, " + - "isSwitchesPolled:${isSwitchesPolled}" -} - -def markChildrenOffline(boolean markAllOffline = false) { - if (markAllOffline) { - def childDevices = getChildDevices() - childDevices.each{ child -> - child.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) - child.sendEvent("name":"thermostat", "value":"Offline") - } - } else { - def remoteSensors = state.remoteSensors2 ?: [:] - def thermostatList = state.thermostats ?: [:] - def switchList = state.switchList ?: [:] - // For devices offline that wasn't polled update pollAttemps, if this is 3rd pollAttempts, mark device offline - def thermostatsOffline = thermostatList.findAll { dni, stat -> - if ((stat.data.deviceAlive == false) && (stat.polled == false)) { - stat.pollAttempts = stat.pollAttempts ? stat.pollAttempts + 1 : 1 - if (stat.pollAttempts > 2) { - return dni - } - } - }?.keySet() - def remoteSensorsOffline = remoteSensors.findAll { rsdni, sensor -> - if ((sensor.deviceAlive == false) && (sensor.polled == false)) { - sensor.pollAttempts = sensor.pollAttempts ? sensor.pollAttempts + 1 : 1 - if (sensor.pollAttempts > 2) { - return rsdni - } - } - }?.keySet() - def switchesOffline = switchList.findAll { sdni, sw -> - if ((sw.deviceAlive == false) && (sw.polled == false)) { - sw.pollAttempts = sw.pollAttempts ? sw.pollAttempts + 1 : 1 - if (sw.pollAttempts > 2) { - return sdni - } - } - }?.keySet() - def devicesOffline = thermostatsOffline + remoteSensorsOffline + switchesOffline - devicesOffline.each { dni -> - def child = getChildDevice(dni) - if (child) { - child.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) - } - } - state.remoteSensors2 = remoteSensors - state.thermostats = thermostatList - state.switchList = switchList - } -} - -// Poll Child is invoked from the Child Device itself as part of the Poll Capability -def pollChild() { - log.warn "Depreciated method pollChild is called" -} - -void controlSwitch( dni, desiredState ) { - // no need to try sending a command if authToken is null - if (!state.authToken) { - log.warn "controlSwitch failed due to authToken=null" - return - } - - def deviceAlive = state.switchList[dni].deviceAlive - // Only send command to online switches - if (deviceAlive == true) { - def d = getChildDevice(dni) - log.trace "[SM] Executing '${(desiredState ? "on" : "off")}' controlSwitch for ${d.device.displayName}" - def body = [ "on": desiredState ] - def params = [ - uri: apiEndpoint + "/ea/devices/ls/$dni/state", - headers: ["Content-Type": "application/json;charset=UTF-8", "Authorization": "Bearer ${state.authToken}"], - body: toJson(body) - ] - def keepTrying = true - def tokenRefreshTries = 0 - - while (keepTrying) { - try { - httpPut(params) { resp -> - keepTrying = false - def rspDataString = "${resp?.data}".toString() - log.info "RESPONSE CODE: ${resp.status}, data:${rspDataString}" - } - } catch (groovyx.net.http.HttpResponseException e) { - //log.warn "Code=${e.getStatusCode()}" - if (e.getStatusCode() == 401) { - tokenRefreshTries++ - if (tokenRefreshTries > 1 || !refreshAuthToken()) { - // refresh of authToken failed, break the loop and exit - log.info "Error refreshing auth_token! Unable to control switch: ${d.device.displayName}" - keepTrying = false - } else { - params.headers.Authorization = "Bearer ${state.authToken}" - } - } else if (e.getStatusCode() == 200) { - // Due to ecobee API returning empty boddy on success we get HttpResponseException from platfrom - // so handle sucess response here - keepTrying = false - log.debug "Ecobee response to switch control = 'Success' for ${d.device.displayName}" - def switchState = desiredState == true ? "on" : "off" - d.sendEvent(name:"switch", value: switchState) - } else { - keepTrying = false - log.error "Exception from device control status:${e.getStatusCode()}, getMessage:${e.getMessage()}" - } - } catch (Exception e) { - def rspDataString = "${e.response?.data}".toString() - log.error "Unhandled exception ${e.getStatusCode()}, $e, response data:$rspDataString" - keepTrying = false - } - } - } else { - log.debug "Can't send command to offline swich!" - } -} - -def availableModes(child) { - def tData = state.thermostats[child.device.deviceNetworkId] - - if(!tData) { - log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling" - return null - } - - def modes = ["off"] - - if (tData.data.heatMode) { - modes.add("heat") - } - if (tData.data.coolMode) { - modes.add("cool") - } - if (tData.data.autoMode) { - modes.add("auto") - } - if (tData.data.auxHeatMode) { - modes.add("auxHeatOnly") - } - - return modes -} - -def currentMode(child) { - - def tData = state.thermostats[child.device.deviceNetworkId] - - if(!tData) { - log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId} after polling" - return null - } - - def mode = tData.data.thermostatMode - return mode -} - -def updateSwitches(switches) { - if (switches) { - def switchList = state.switchList ?: [:] - switches.each { - if ( it.type == "LIGHT_SWITCH" ) { - def childSwitch = getChildDevice(it?.identifier) - if (childSwitch) { - switchList[it?.identifier] = it - switchList[it?.identifier] << [deviceAlive: (it?.connected ?: false)] - switchList[it?.identifier].polled = true - switchList[it?.identifier].pollAttempts = 0 - if (it?.connected) { - def switchState = it?.state?.on == true ? "on" : "off" - childSwitch.sendEvent(name:"switch", value: switchState) - childSwitch.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false) - } else { - childSwitch.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) - } - } else { - log.info "[SM] pollSwitches received data for non-smarthings switch, ingoring" - } - } - } - state.switchList = switchList - } -} - -def updateSensorData(sensorData) { - def remoteSensors = state.remoteSensors2 ?: [:] - sensorData.each { - it.each { - if (it.type == "ecobee3_remote_sensor") { - def temperature = "" - def occupancy = "" - def dni = "ecobee_sensor-"+ it?.id + "-" + it?.code - def child = getChildDevice(dni) - if(child) { - // If DeviceWatch hasn't be enrolled as untracked scheme, re-enroll - if (!child.getDataValue("EnrolledUTDH")) { - child.updated() - } else if (it?.name && (it.name != child.displayName)) { - // Only allowing name change after DeviceWatch has been enrolled, this to ensure the ST name - // is preserved and not changed to name from ecobee cloud as this is the first name change is allowed - child.setDisplayName(it.name) - } - if (remoteSensors[dni] && remoteSensors[dni].deviceAlive) { - it.capability.each { - if (it.type == "temperature") { - if (it.value == "unknown") { - // setting to 0 as "--" is not a valid number depite 0 being a valid value - temperature = 0 - } else { - if (location.temperatureScale == "F") { - temperature = Math.round(it.value.toDouble() / 10) - } else { - temperature = convertFtoC(it.value.toDouble() / 10) - } - } - } else if (it.type == "occupancy") { - occupancy = (it.value == "true") ? "active" : "inactive" - } - } - remoteSensors[dni] << it - child.sendEvent(name:"temperature", value: temperature, unit: location.temperatureScale, - descriptionText: "temperature is " + (temperature ? "${temperature}°${location.temperatureScale}" : "unknown"), displayed: true) - child.sendEvent(name:"motion", value: occupancy) - child.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false) - } else { - if (remoteSensors[dni]) { - remoteSensors[dni] << it - } else if (!child.getDataValue("DeviceIssue")) { - child.updateDataValue("DeviceIssue", "Please remove and re-add sensor") - } - child.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) - } - } - } - } - } - state.remoteSensors2 = remoteSensors -} - -def getChildDeviceIdsString() { - return thermostats.collect { it.split(/\./).last() }.join(',') -} - -def toJson(Map m) { - return groovy.json.JsonOutput.toJson(m) -} - -def toQueryString(Map m) { - return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&") -} - -boolean refreshAuthToken() { - log.debug "refreshing auth token" - def notificationMessage = "is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials." - def isSuccess = false - - if(!state.refreshToken) { - log.warn "Can not refresh OAuth token since there is no refreshToken stored" - sendPushAndFeeds(notificationMessage) - } else { - def refreshParams = [ - method: 'POST', - uri : apiEndpoint, - path : "/token" - ] - if (state.jwt) { - refreshParams.query = [ - grant_type: "refresh_token", - refresh_token: state.refreshToken, - client_id : smartThingsClientId, - ecobee_type: "jwt" - ] - } else { - refreshParams.query = [ - grant_type: "refresh_token", - code: state.refreshToken, - client_id: smartThingsClientId - ] - } - try { - httpPost(refreshParams) { resp -> - if(resp.status == 200) { - log.debug "Token refreshed, ${resp.data}" - state.refreshToken = resp.data?.refresh_token - state.authToken = resp.data?.access_token - state.reAttempt = 0 - isSuccess = true - } - } - } catch (groovyx.net.http.HttpResponseException e) { - def rspDataString = "${e.response?.data}".toString() - log.error "Error refreshing auth_token:${e.statusCode}, refreshAttempt:${state.reAttempt}, response data:$rspDataString" - if ((e.statusCode == 400) || (e.statusCode == 302)) { - def slurper = new JsonSlurper() - def rspData = slurper.parseText(rspDataString) - if (rspData && (rspData.error != "invalid_request" || rspData.error != "not_supported")) { - // either "invalid_grant", "unauthorized_client", "unsupported_grant_type", - // or "invalid_scope", request user to re-enter credentials - sendPushAndFeeds(notificationMessage) - } - } else if (e.statusCode == 401) { // unauthorized*/ - state.reAttempt = state.reAttempt ? state.reAttempt + 1 : 1 - log.warn "reAttempt refreshAuthToken ${state.reAttempt}" - if (state.reAttempt > 3) { - sendPushAndFeeds(notificationMessage) - state.reAttempt = 0 - } - } - } - } - return isSuccess -} - -/** - * Executes the resume program command on the Ecobee thermostat - * @param deviceId - the ID of the device - * - * @retrun true if the command was successful, false otherwise. - */ -boolean resumeProgram(deviceId) { - def payload = [ - selection: [ - selectionType: "thermostats", - selectionMatch: deviceId, - includeRuntime: true - ], - functions: [ - [ - type: "resumeProgram" - ] - ] - ] - return sendCommandToEcobee(payload) -} - -/** - * Executes the set hold command on the Ecobee thermostat - * @param heating - The heating temperature to set in fahrenheit - * @param cooling - the cooling temperature to set in fahrenheit - * @param deviceId - the ID of the device - * @param sendHoldType - the hold type to execute - * - * @return true if the command was successful, false otherwise - */ -boolean setHold(heating, cooling, deviceId, sendHoldType) { - // Ecobee requires that temp values be in fahrenheit multiplied by 10. - int h = heating * 10 - int c = cooling * 10 - - def payload = [ - selection: [ - selectionType: "thermostats", - selectionMatch: deviceId, - includeRuntime: true - ], - functions: [ - [ - type: "setHold", - params: [ - coolHoldTemp: c, - heatHoldTemp: h, - holdType: sendHoldType - ] - ] - ] - ] - - return sendCommandToEcobee(payload) -} - -/** - * Executes the set fan mode command on the Ecobee thermostat - * @param heating - The heating temperature to set in fahrenheit - * @param cooling - the cooling temperature to set in fahrenheit - * @param deviceId - the ID of the device - * @param sendHoldType - the hold type to execute - * @param fanMode - the fan mode to set to - * - * @return true if the command was successful, false otherwise - */ -boolean setFanMode(heating, cooling, deviceId, sendHoldType, fanMode) { - // Ecobee requires that temp values be in fahrenheit multiplied by 10. - int h = heating * 10 - int c = cooling * 10 - - def payload = [ - selection: [ - selectionType: "thermostats", - selectionMatch: deviceId, - includeRuntime: true - ], - functions: [ - [ - type: "setHold", - params: [ - coolHoldTemp: c, - heatHoldTemp: h, - holdType: sendHoldType, - fan: fanMode - ] - ] - ] - ] - - return sendCommandToEcobee(payload) -} - -/** - * Sets the mode of the Ecobee thermostat - * @param mode - the mode to set to - * @param deviceId - the ID of the device - * - * @return true if the command was successful, false otherwise - */ -boolean setMode(mode, deviceId) { - def payload = [ - selection: [ - selectionType: "thermostats", - selectionMatch: deviceId, - includeRuntime: true - ], - thermostat: [ - settings: [ - hvacMode: mode - ] - ] - ] - return sendCommandToEcobee(payload) -} - -/** - * Sets the name of the Ecobee thermostat - * @param name - the name to set to - * @param deviceId - the ID of the device - * - * @return true if the command was successful, false otherwise - */ -def setName(name, deviceId) { - def thermostatList = state.thermostats ?: [:] - if (thermostatList[deviceId]?.data?.name != name) { - def payload = [ - selection: [ - selectionType: "thermostats", - selectionMatch: deviceId.split(/\./).last(), - includeRuntime: true - ], - thermostat: [ - name: name - ] - ] - log.debug "setName: payload:$payload" - sendCommandToEcobee(payload) - } -} - -/** - * Sets the name of the Ecobee3 remote sensor - * @param name - the name to set to - * @param deviceId - the ID of the device - * - * @return true if the command was successful, false otherwise - */ -def setSensorName(name, deviceId) { - def remoteSensors = state.remoteSensors2 ?: [:] - if (remoteSensors[deviceId] && (remoteSensors[deviceId]?.name != name)) { - def payload = [ - selection: [ - selectionType: "thermostats", - selectionMatch: remoteSensors[deviceId].thermostatId?.split(/\./).last(), - includeRuntime: true - ], - functions: [ - [ - "type": "updateSensor", - "params": [ - "deviceId": remoteSensors[deviceId].id, - "sensorId": remoteSensors[deviceId].capability?.first()?.id, - "name": name - ] - ] - ] - ] - log.debug "setSensorName: payload:$payload" - sendCommandToEcobee(payload) - } -} - -/** - * Makes a request to the Ecobee API to actuate the thermostat. - * Used by command methods to send commands to Ecobee. - * - * @param bodyParams - a map of request parameters to send to Ecobee. - * - * @return true if the command was accepted by Ecobee without error, false otherwise. - */ -boolean sendCommandToEcobee(Map bodyParams) { - // no need to try sending a command if authToken is null - if (!state.authToken) { - log.warn "sendCommandToEcobee failed due to authToken=null" - return false - } - def isSuccess = false - def cmdParams = [ - uri: apiEndpoint, - path: "/1/thermostat", - headers: ["Content-Type": "application/json", "Authorization": "Bearer ${state.authToken}"], - body: toJson(bodyParams) - ] - def keepTrying = true - def cmdAttempt = 1 - - while (keepTrying) { - try{ - httpPost(cmdParams) { resp -> - keepTrying = false - if(resp.status == 200) { - log.debug "updated ${resp.data}" - def returnStatus = resp.data.status.code - if (returnStatus == 0) { - log.debug "Successful call to ecobee API." - isSuccess = true - } else { - log.debug "Error return code = ${returnStatus}" - } - } - } - } catch (groovyx.net.http.HttpResponseException e) { - log.info "Exception sending command: $e, status:${e.getStatusCode()}, ${e?.response?.data}" - if (e.response.data.status.code == 14) { - cmdAttempt++ - if (cmdAttempt > 2 || !refreshAuthToken()) { - // refresh authToken failed, break loop and exit - log.error "Error refreshing auth_token! Unable to send command" - keepTrying = false - } else { - cmdParams.headers.Authorization = "Bearer ${state.authToken}" - } - } else { - log.error "Exception sending command: Authentication error, invalid authentication method, lack of credentials, etc." - keepTrying = false - } - } - } - return isSuccess -} - -def getChildName() { return "Ecobee Thermostat" } -def getSensorChildName() { return "Ecobee Sensor" } -def getSwitchChildName() { return "Ecobee Switch" } -def getServerUrl() { return appSettings.serverUrl ?: apiServerUrl } -def getCallbackUrl() { return "${serverUrl}/oauth/callback" } -def getBuildRedirectUrl() { return "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" } -def getApiEndpoint() { return "https://api.ecobee.com" } -def getSmartThingsClientId() { return appSettings.clientId } -def getVendorIcon() { return "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee.png" } - -//send both push notification and mobile activity feeds -def sendPushAndFeeds(notificationMessage) { - def timeNow = now() - log.warn "sendPushAndFeeds >> notificationMessage: ${notificationMessage}" - log.warn "sendPushAndFeeds >> state.timeSendPush: ${state.timeSendPush}" - // notification is sent to remind user once a day - if (!state.timeSendPush || (24 * 60 * 60 * 1000 < (timeNow - state.timeSendPush))) { - sendPush("Your Ecobee thermostat " + notificationMessage) - sendActivityFeeds(notificationMessage) - state.timeSendPush = now() - } - state.authToken = null -} - -def getThermostatData(data) { - - return [ - name: getThermostatDisplayName(data),//stat.name ? stat.name : stat.identifier), - coolMode: (data.settings.coolStages > 0), - heatMode: (data.settings.heatStages > 0), - deviceTemperatureUnit: (data.settings.useCelsius == false && location.temperatureScale == "F") ? "F" : "C", - minHeatingSetpoint: (data.settings.heatRangeLow / 10), - maxHeatingSetpoint: (data.settings.heatRangeHigh / 10), - minCoolingSetpoint: (data.settings.coolRangeLow / 10), - maxCoolingSetpoint: (data.settings.coolRangeHigh / 10), - autoMode: data.settings.autoHeatCoolFeatureEnabled, - deviceAlive: data.runtime.connected, - auxHeatMode: (data.settings.hasHeatPump) && (data.settings.hasForcedAir || data.settings.hasElectric || data.settings.hasBoiler), - temperature: (data.runtime.actualTemperature / 10), - heatingSetpoint: (data.runtime.desiredHeat / 10), - coolingSetpoint: (data.runtime.desiredCool / 10), - thermostatMode: data.settings.hvacMode, - humidity: data.runtime.actualHumidity, - thermostatFanMode: data.runtime.desiredFanMode - ] -} - -/** - * Stores data about the thermostats in atomicState. - * @param thermostats - a list of thermostats as returned from the Ecobee API - */ -void storeThermostatData(thermostatData) { - def data - def remoteSensors = state.remoteSensors2 ?: [:] - def thermostatList = state.thermostats ?: [:] - thermostatData.each { stat -> - def dni = [ app.id, stat.identifier ].join('.') - def childDevice = getChildDevice(dni) - data = getThermostatData(stat) - if (childDevice) { - // Adjust autoMode in regards to coolMode and heatMode as thermostat may report autoMode:true despite only having heat or cool mode - data["autoMode"] = data["autoMode"] && data.coolMode && data.heatMode - if (!childDevice.getDataValue("EnrolledUTDH")) { - childDevice.updated() - } - if (childDevice.displayName != data.name) { - childDevice.setDisplayName(data.name) - } - if (data["deviceAlive"]) { - childDevice.generateEvent(data) - childDevice.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false) - } else { - childDevice.sendEvent("name":"thermostat", "value":"Offline") - childDevice.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) - } - thermostatList[dni] = thermostatList[dni] ? thermostatList[dni] << [data:data] : [data:data] - thermostatList[dni].polled = true - thermostatList[dni].pollAttempts = 0 - } else { - log.info "Got poll data for ${data.name} with identifier ${stat.identifier} that doesn't have a DTH" - } - // Make sure any remote senors connected to the thermostat are marked offline too - stat.remoteSensors.each { sensor -> - if (sensor.type == "ecobee3_remote_sensor") { - def rsDni = "ecobee_sensor-"+ sensor?.id + "-" + sensor?.code - if (ecobeesensors?.contains(rsDni)) { - remoteSensors[rsDni] = remoteSensors[rsDni] ? - remoteSensors[rsDni] << [deviceAlive:data["deviceAlive"]] : [deviceAlive:data["deviceAlive"]] - remoteSensors[rsDni] << [thermostatId: dni] - remoteSensors[rsDni].polled = true - remoteSensors[rsDni].pollAttempts = 0 - } - } - } - } - state.thermostats = thermostatList - state.remoteSensors2 = remoteSensors -} - -def sendActivityFeeds(notificationMessage) { - def devices = getChildDevices() - devices.each { child -> - child.generateActivityFeedsEvent(notificationMessage) //parse received message from parent - } -} - -def convertFtoC (tempF) { - return String.format("%.1f", (Math.round(((tempF - 32)*(5/9)) * 2))/2) -} diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties deleted file mode 100644 index 555db6a9e05..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ar-AE.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=قم بتوصيل ثرموستات Ecobee بـ SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=أنت متصل. -'''Click to enter Ecobee Credentials'''=النقر لإدخال بيانات اعتماد Ecobee -'''Login'''=تسجيل الدخول -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=انقر أدناه لتسجيل الدخول إلى خدمة ecobee والمصادقة على الوصول إلى SmartThings. تأكد من التمرير للأسفل على الصفحة ٢ والضغط على زر ”السماح“. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=تحديد أجهزة الثرموستات -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=انقر أدناه لرؤية قائمة أجهزة ثرموستات ecobee المتوفرة في حساب ecobee، وحدد الأجهزة التي ترغب في توصيلها بـ SmartThings. -'''Tap to choose'''=النقر لاختيار -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=انقر أدناه لرؤية قائمة مستشعرات ecobee المتوفرة في حساب ecobee، وحدد الأجهزة التي ترغب في توصيلها بـ SmartThings. -'''Tap to choose'''=النقر لاختيار -'''Select Ecobee Sensors ({{numFound}} found)'''=تحديد مستشعرات Ecobee‏ ‎({{numFound}} found) -'''Your ecobee Account is now connected to SmartThings!'''=حساب ecobee متصل الآن بـ SmartThings! -'''Click 'Done' to finish setup.'''=انقر فوق ”تم“ لإنهاء الإعداد. -'''The connection could not be established!'''=يتعذر إنشاء الاتصال! -'''Click 'Done' to return to the menu.'''=انقر فوق ”تم“ للعودة إلى القائمة. -'''is connected to SmartThings'''={{deviceName}} متصل بـ SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=تم قطع اتصال {{deviceName}} بـ SmartThings، لأن بيانات اعتماد الوصول قد تغيرت أو فُقدت. يُرجى الانتقال إلى التطبيق الذكي Ecobee (Connect)‎ وإعادة إدخال بيانات اعتماد تسجيل الدخول إلى حسابك. -'''Your Ecobee thermostat '''=ثرموستات Ecobee -'''Select your ecobee devices'''=تحديد أجهزة ecobee لديك -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=انقر أدناه لإضافة أجهزة الثرموستات المتوفرة في حساب ecobee لديك أو إزالتها. سيتم توصيل أجهزة الثرموستات المحددة بـ SmartThings. -'''Log In'''=تسجيل الدخول -'''Tap Next to continue to set up your ecobee thermostats.'''=انقر فوق ”التالي“ لمتابعة إعداد أجهزة الثرموستات ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=انقر أدناه لإضافة المستشعرات عن بُعد المتوفرة في حساب ecobee لديك أو إزالتها منه. سيتم توصيل المستشعرات المحددة بـ SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=انقر أدناه لإضافة مفاتيح التبديل المتوفرة في حساب ecobee لديك أو إزالتها. سيتم توصيل مفاتيح التبديل المحددة بـ SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties b/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties deleted file mode 100644 index 19f0cf6bdd4..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/bg-BG.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Свържете термостата Ecobee към SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Свързани сте. -'''Click to enter Ecobee Credentials'''=Щракнете, за да въведете идентификационни данни за Ecobee -'''Login'''=Вход -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Докоснете по-долу, за да влезете в услугата ecobee и да упълномощите достъпа на SmartThings. Превъртете надолу в страница 2 и натиснете бутона Allow (Позволяване). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Избор на термостати -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Докоснете по-долу, за да видите списък с термостатите ecobee във вашия ecobee акаунт, и изберете онези, които искате да свържете към SmartThings. -'''Tap to choose'''=Докосване за избор -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Докоснете по-долу, за да видите списък със сензорите ecobee във вашия ecobee акаунт, и изберете онези, които искате да свържете към SmartThings. -'''Tap to choose'''=Докосване за избор -'''Select Ecobee Sensors ({{numFound}} found)'''=Избор на сензори Ecobee ({{numFound}} с намерени) -'''Your ecobee Account is now connected to SmartThings!'''=Вашият ecobee акаунт вече е свързан към SmartThings! -'''Click 'Done' to finish setup.'''=Щракнете върху Done (Готово), за да завършите настройката. -'''The connection could not be established!'''=Връзката не може да се осъществи! -'''Click 'Done' to return to the menu.'''=Щракнете върху Done (Готово), за да се върнете към менюто. -'''is connected to SmartThings'''=е свързан към SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=е прекъснат от SmartThings, тъй като идентификационните данни за достъп са променени или изгубени. Отидете в Ecobee (Connect) SmartApp и въведете отново идентификационните си данни за влизане в акаунта. -'''Your Ecobee thermostat '''=Вашият термостат Ecobee -'''Select your ecobee devices'''=Избор на ecobee устройства -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Докоснете по-долу, за да добавите или премахнете термостатите, налични във вашия ecobee акаунт. Избраните термостати ще се свържат със SmartThings. -'''Log In'''=Влизане -'''Tap Next to continue to set up your ecobee thermostats.'''=Докоснете Next (Напред), за да продължите с настройването на термостатите ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Докоснете по-долу, за да премахнете отдалечените сензори, налични във вашия ecobee акаунт. Избраните сензори ще се свържат със SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Докоснете по-долу, за да добавите или премахнете превключвателите, налични във вашия ecobee акаунт. Избраните превключватели ще се свържат със SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties deleted file mode 100644 index e6364de3c01..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ca-ES.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Conecte su termostato Ecobee a SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Está conectado. -'''Click to enter Ecobee Credentials'''=Haga clic para introducir las credenciales de Ecobee -'''Login'''=Inicio de sesión -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Pulse a continuación para iniciar sesión en el servicio de ecobee y autorizar el acceso a SmartThings. Asegúrese de desplazarse hacia abajo a la página 2 y pulsar el botón “Allow” (Permitir). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Seleccionar los termostatos -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de termostatos ecobee disponibles en su cuenta de ecobee y seleccione los que quiera conectar a SmartThings. -'''Tap to choose'''=Pulsar para seleccionar -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de sensores ecobee disponibles en su cuenta de ecobee y seleccione los que quiera conectar a SmartThings. -'''Tap to choose'''=Pulsar para seleccionar -'''Select Ecobee Sensors ({{numFound}} found)'''=Seleccionar sensores de Ecobee ({{numFound}} encontrados) -'''Your ecobee Account is now connected to SmartThings!'''=¡Su cuenta de ecobee ya está conectada a SmartThings! -'''Click 'Done' to finish setup.'''=Haga clic en “Done” (Hecho) para finalizar la configuración. -'''The connection could not be established!'''=¡No se ha podido establecer la conexión! -'''Click 'Done' to return to the menu.'''=Haga clic en “Done” (Hecho) para volver al menú. -'''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. -'''Your Ecobee thermostat '''=Su termostato Ecobee -'''Select your ecobee devices'''=Selecciona os teus dispositivos de ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toca a continuación para engadir ou eliminar termóstatos dispoñibles na túa conta de ecobee. Os termóstatos seleccionados conectaranse a SmartThings. -'''Log In'''=Iniciar sesión -'''Tap Next to continue to set up your ecobee thermostats.'''=Toca Seguinte para continuar coa configuración dos teus termóstatos de ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Toca a continuación para engadir ou eliminar sensores remotos dispoñibles na túa conta de ecobee. Os sensores seleccionados conectaranse a SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Toca a continuación para engadir ou eliminar interruptores dispoñibles na túa conta de ecobee. Os interruptores seleccionados conectaranse a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties b/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties deleted file mode 100644 index da3e40a8f84..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/cs-CZ.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Připojte termostat Ecobee k systému SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Jste připojeni. -'''Click to enter Ecobee Credentials'''=Klepněte a zadejte přihlašovací údaje Ecobee -'''Login'''=Přihlásit -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Klepnutím na následující tlačítko se přihlásíte ke službě ecobee a autorizujete přístup pro systém SmartThings. Posuňte se dolů na stránku 2 a stiskněte tlačítko „Allow“ (Povolit). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Vyberte termostaty -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Klepnutím na následující tlačítko zobrazte seznam termostatů ecobee dostupných na vašem účtu ecobee a vyberte ty, které chcete připojit k systému SmartThings. -'''Tap to choose'''=Klepnutím zvolte -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Klepnutím na následující tlačítko zobrazte seznam senzorů ecobee dostupných na vašem účtu ecobee a vyberte ty, které chcete připojit k systému SmartThings. -'''Tap to choose'''=Klepnutím zvolte -'''Select Ecobee Sensors ({{numFound}} found)'''=Vyberte senzory Ecobee (nalezeno {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Účet ecobee je nyní připojen k systému SmartThings! -'''Click 'Done' to finish setup.'''=Dokončete nastavení klepnutím na tlačítko „Done“ (Hotovo). -'''The connection could not be established!'''=Připojení nelze navázat! -'''Click 'Done' to return to the menu.'''=Klepnutím na tlačítko „Done“ (Hotovo) se vrátíte do menu. -'''is connected to SmartThings'''=je připojen ke SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=byl odpojen od systému SmartThings, protože přístupové přihlašovací údaje byly změněny nebo ztraceny. Přejděte do Ecobee (Connect) SmartApp a znovu zadejte své přihlašovací údaje k účtu. -'''Your Ecobee thermostat '''=Termostat Ecobee -'''Select your ecobee devices'''=Vyberte zařízení ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Klepnutím na následující tlačítko přidáte nebo odeberete termostaty dostupné na účtu ecobee. Vybrané termostaty budou připojeny k aplikaci SmartThings. -'''Log In'''=Přihlásit -'''Tap Next to continue to set up your ecobee thermostats.'''=Klepněte na tlačítko Next (Další) a pokračujte nastavením termostatů ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Klepnutím na následující tlačítko přidáte nebo odeberete vzdálené termostaty dostupné na účtu ecobee. Vybrané senzory budou připojeny k aplikaci SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Klepnutím na následující tlačítko přidáte nebo odeberete spínače dostupné na účtu ecobee. Vybrané spínače budou připojeny k aplikaci SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties b/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties deleted file mode 100644 index ab837fde6b3..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/da-DK.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Forbind din Ecobee-termostat med SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Du er nu forbundet. -'''Click to enter Ecobee Credentials'''=Klik for at indtaste Ecobee-legitimationsoplysninger -'''Login'''=Log ind -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Tryk nedenfor for at logge ind på din ecobee-tjeneste og godkende SmartThings-adgang. Sørg for at rulle ned på side 2 og trykke på knappen “Allow” (Tillad). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Vælg dine termostater -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tryk herunder for at se listen over ecobee-termostater, der er tilgængelige på din ecobee-konto, og vælg dem, du vil forbinde med SmartThings. -'''Tap to choose'''=Tryk for at vælge -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tryk herunder for at se listen over ecobee-sensorer, der er tilgængelige på din ecobee-konto, og vælg dem, du vil forbinde med SmartThings. -'''Tap to choose'''=Tryk for at vælge -'''Select Ecobee Sensors ({{numFound}} found)'''=Vælg Ecobee-sensorer ({{numFound}} fundet) -'''Your ecobee Account is now connected to SmartThings!'''=Din ecobee-konto er nu forbundet med SmartThings! -'''Click 'Done' to finish setup.'''=Klik på “Done” (Udført) for at afslutte konfigurationen. -'''The connection could not be established!'''=Der kunne ikke oprettes forbindelse! -'''Click 'Done' to return to the menu.'''=Klik på “Done” (Udført) for at vende tilbage til menuen. -'''is connected to SmartThings'''=er forbundet med SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=er koblet fra SmartThings, fordi adgangslegitimationsoplysningerne er ændret eller gået tabt. Gå til Ecobee (Connect (Forbind)) SmartApp, og indtast dine kontologinoplysninger igen. -'''Your Ecobee thermostat '''=Din Ecobee-termostat -'''Select your ecobee devices'''=Vælg dine ecobee-enheder -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tryk herunder for at tilføje eller fjerne termostater, der er tilgængelige på din ecobee-konto. De valgte termostater bliver forbundet til SmartThings. -'''Log In'''=Log ind -'''Tap Next to continue to set up your ecobee thermostats.'''=Tryk på Næste for at fortsætte med at konfigurere dine ecobee-termostater. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Tryk herunder for at tilføje eller fjerne eksterne sensorer, der er tilgængelige på din ecobee-konto. De valgte sensorer bliver forbundet til SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Tryk herunder for at tilføje eller fjerne kontakter, der er tilgængelige på din ecobee-konto. De valgte kontakter bliver forbundet til SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties deleted file mode 100644 index a5909eef5b5..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/de-DE.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Verbinden Sie Ihr Ecobee-Thermostat mit SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Sie sind verbunden. -'''Click to enter Ecobee Credentials'''=Hier klicken, um die ecobee-Zugangsdaten einzugeben. -'''Login'''=Anmeldung -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Tippen Sie unten, um sich am ecobee-Dienst anzumelden und den SmartThings-Zugriff zu autorisieren. Stellen Sie sicher, dass Sie bis auf Seite 2 herunterscrollen und auf die Schaltfläche „Allow“ (Zulassen) tippen. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Ihre Thermostate auswählen -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tippen Sie unten, um eine Liste der ecobee-Thermostate anzuzeigen, die in Ihrem ecobee-Konto verfügbar sind, und wählen Sie diejenigen aus, mit denen Sie eine Verbindung zu SmartThings herstellen möchten. -'''Tap to choose'''=Zur Auswahl tippen -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tippen Sie unten, um eine Liste der ecobee-Sensoren anzuzeigen, die in Ihrem ecobee-Konto verfügbar sind, und wählen Sie diejenigen aus, mit denen Sie eine Verbindung zu SmartThings herstellen möchten. -'''Tap to choose'''=Zur Auswahl tippen -'''Select Ecobee Sensors ({{numFound}} found)'''=ecobee-Sensoren auswählen ({{numFound}} gefunden) -'''Your ecobee Account is now connected to SmartThings!'''=Ihr ecobee-Konto ist jetzt mit SmartThings verbunden! -'''Click 'Done' to finish setup.'''=Klicken Sie auf „Done“ (OK), um die Einrichtung abzuschließen. -'''The connection could not be established!'''=Es konnte keine Verbindung hergestellt werden! -'''Click 'Done' to return to the menu.'''=Klicken Sie auf „Done“ (OK), um zum Menü zurückzukehren. -'''is connected to SmartThings'''=ist mit SmartThings verbunden -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=ist von SmartThings getrennt, da die Zugangsdaten für den Zugriff geändert wurden oder verloren gingen. Wechseln Sie zur ecobee (Connect)-SmartApp und geben Sie Ihre Kontozugangsdaten erneut ein. -'''Your Ecobee thermostat '''=Ihr ecobee-Thermostat -'''Select your ecobee devices'''=Wählen Sie Ihre ecobee-Geräte aus -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tippen Sie unten, um Thermostate hinzuzufügen oder zu entfernen, die in Ihrem ecobee-Konto verfügbar sind. Ausgewählte Thermostate werden mit SmartThings verbunden. -'''Log In'''=Anmelden -'''Tap Next to continue to set up your ecobee thermostats.'''=Tippen Sie auf „Next“ (Weiter), um die Einrichtung Ihrer ecobee-Thermostate fortzusetzen. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Tippen Sie unten, um Remote-Sensoren hinzuzufügen oder zu entfernen, die in Ihrem ecobee-Konto verfügbar sind. Ausgewählte Sensoren werden mit SmartThings verbunden. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Tippen Sie unten, um Schalter hinzuzufügen oder zu entfernen, die in Ihrem ecobee-Konto verfügbar sind. Ausgewählte Schalter werden mit SmartThings verbunden. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties deleted file mode 100644 index abe17d298d5..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/el-GR.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Συνδέστε το θερμοστάτη Ecobee στο SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Έχετε συνδεθεί. -'''Click to enter Ecobee Credentials'''=Κάντε κλικ για να καταχωρήσετε διαπιστευτήρια Ecobee -'''Login'''=Σύνδεση -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Πατήστε παρακάτω για να συνδεθείτε στην υπηρεσία ecobee και να δώσετε εξουσιοδότηση πρόσβασης για το SmartThings. Κάνετε κύλιση προς τα κάτω στη σελίδα 2 και πατήστε το κουμπί "Επιτρ.". -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Επιλέξτε τους θερμοστάτες σας -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Πατήστε παρακάτω για να δείτε τη λίστα με τους θερμοστάτες ecobee που είναι διαθέσιμοι στο λογαριασμό ecobee και να επιλέξετε αυτούς που θέλετε να συνδέσετε στο SmartThings. -'''Tap to choose'''=Πατήστε για να επιλέξετε -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Πατήστε παρακάτω για να δείτε τη λίστα με τους αισθητήρες ecobee που είναι διαθέσιμοι στο λογαριασμό ecobee και να επιλέξετε αυτούς που θέλετε να συνδέσετε στο SmartThings. -'''Tap to choose'''=Πατήστε για να επιλέξετε -'''Select Ecobee Sensors ({{numFound}} found)'''=Επιλογή αισθητήρων Ecobee (βρέθηκαν {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Ο λογαριασμός σας στο ecobee έχει τώρα συνδεθεί στο SmartThings! -'''Click 'Done' to finish setup.'''=Πατήστε "Done" (Τέλος) για να ολοκληρωθεί η ρύθμιση. -'''The connection could not be established!'''=Δεν ήταν δυνατή η δημιουργία σύνδεσης! -'''Click 'Done' to return to the menu.'''=Κάντε κλικ στο "Done" (Τέλος) για να επιστρέψετε στο μενού. -'''is connected to SmartThings'''=συνδέθηκε στο SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=αποσυνδέθηκε από το SmartThings, επειδή τα διαπιστευτήρια πρόσβασης άλλαξαν ή έχουν χαθεί. Μεταβείτε στην εφαρμογή Ecobee (Connect) SmartApp και καταχωρήστε ξανά τα διαπιστευτήρια σύνδεσης για το λογαριασμό σας. -'''Your Ecobee thermostat '''=Θερμοστάτης Ecobee -'''Select your ecobee devices'''=Επιλέξτε τις συσκευές σας ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Πατήστε παρακάτω για να προσθέσετε ή να καταργήσετε τους θερμοστάτες που είναι διαθέσιμοι στο λογαριασμό σας ecobee. Οι επιλεγμένοι θερμοστάτες θα συνδεθούν στο SmartThings. -'''Log In'''=Σύνδεση -'''Tap Next to continue to set up your ecobee thermostats.'''=Πατήστε «Επόμενο» για να συνεχίσετε τη ρύθμιση των θερμοστατών ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Πατήστε παρακάτω για να προσθέσετε ή να καταργήσετε τους απομακρυσμένους αισθητήρες που είναι διαθέσιμοι στο λογαριασμό σας ecobee. Οι επιλεγμένοι αισθητήρες θα συνδεθούν στο SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Πατήστε παρακάτω για να προσθέσετε ή να καταργήσετε τους διακόπτες που είναι διαθέσιμοι στο λογαριασμό σας ecobee. Οι επιλεγμένοι διακόπτες θα συνδεθούν στο SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties b/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties deleted file mode 100644 index 2e434f86057..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/en-GB.properties +++ /dev/null @@ -1,20 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Connect your Ecobee thermostat to SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=You are connected. -'''Click to enter Ecobee Credentials'''=Click to enter Ecobee Credentials -'''Login'''=Login -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Tap below to log in to the ecobee service and authorise SmartThings access. Be sure to scroll down on page 2 and press the ’Allow’ button. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Select Your Thermostats -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings. -'''Tap to choose'''=Tap to choose -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings. -'''Tap to choose'''=Tap to choose -'''Select Ecobee Sensors ({{numFound}} found)'''=Select Ecobee Sensors ({{numFound}} found) -'''Your ecobee Account is now connected to SmartThings!'''=Your ecobee Account is now connected to SmartThings! -'''Click 'Done' to finish setup.'''=Click ’Done’ to finish setup. -'''The connection could not be established!'''=The connection could not be established! -'''Click 'Done' to return to the menu.'''=Click ’Done’ to return to the menu. -'''is connected to SmartThings'''=is connected to SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials. -'''Your Ecobee thermostat '''=Your Ecobee thermostat diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties b/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties deleted file mode 100644 index 387a54218c0..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/es-ES.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Conecte su termostato Ecobee a SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Está conectado. -'''Click to enter Ecobee Credentials'''=Haga clic para introducir las credenciales de Ecobee -'''Login'''=Inicio de sesión -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Pulse a continuación para iniciar sesión en el servicio de ecobee y autorizar el acceso a SmartThings. Asegúrese de desplazarse hacia abajo a la página 2 y pulsar el botón “Allow” (Permitir). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Seleccionar los termostatos -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de termostatos ecobee disponibles en su cuenta de ecobee y seleccione los que quiera conectar a SmartThings. -'''Tap to choose'''=Pulsar para seleccionar -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de sensores ecobee disponibles en su cuenta de ecobee y seleccione los que quiera conectar a SmartThings. -'''Tap to choose'''=Pulsar para seleccionar -'''Select Ecobee Sensors ({{numFound}} found)'''=Seleccionar sensores de Ecobee ({{numFound}} encontrados) -'''Your ecobee Account is now connected to SmartThings!'''=¡Su cuenta de ecobee ya está conectada a SmartThings! -'''Click 'Done' to finish setup.'''=Haga clic en “Done” (Hecho) para finalizar la configuración. -'''The connection could not be established!'''=¡No se ha podido establecer la conexión! -'''Click 'Done' to return to the menu.'''=Haga clic en “Done” (Hecho) para volver al menú. -'''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. -'''Your Ecobee thermostat '''=Su termostato Ecobee -'''Select your ecobee devices'''=Selecciona tus dispositivos ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Pulsa a continuación para añadir o eliminar los termostatos disponibles en tu cuenta de ecobee. Los termostatos seleccionados se conectarán a SmartThings. -'''Log In'''=Iniciar sesión -'''Tap Next to continue to set up your ecobee thermostats.'''=Pulsa Siguiente para continuar con la configuración de tus termostatos ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Pulsa a continuación para añadir o eliminar los sensores remotos disponibles en tu cuenta de ecobee. Los sensores seleccionados se conectarán a SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Pulsa a continuación para añadir o eliminar los interruptores disponibles en tu cuenta de ecobee. Los interruptores seleccionados se conectarán a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties b/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties deleted file mode 100644 index 14b3027e7f1..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/es-MX.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Conecte su termostato Ecobee a SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Está conectado. -'''Click to enter Ecobee Credentials'''=Haga clic para introducir las credenciales de Ecobee -'''Login'''=Inicio de sesión -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Pulse a continuación para iniciar sesión en el servicio de ecobee y autorizar el acceso a SmartThings. Asegúrese de desplazarse hacia abajo a la página 2 y pulsar el botón “Allow” (Permitir). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Seleccionar los termostatos -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de termostatos ecobee disponibles en su cuenta de ecobee y seleccione los que quiera conectar a SmartThings. -'''Tap to choose'''=Pulsar para seleccionar -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de sensores ecobee disponibles en su cuenta de ecobee y seleccione los que quiera conectar a SmartThings. -'''Tap to choose'''=Pulsar para seleccionar -'''Select Ecobee Sensors ({{numFound}} found)'''=Seleccionar sensores de Ecobee ({{numFound}} encontrados) -'''Your ecobee Account is now connected to SmartThings!'''=¡Su cuenta de ecobee ya está conectada a SmartThings! -'''Click 'Done' to finish setup.'''=Haga clic en “Done” (Hecho) para finalizar la configuración. -'''The connection could not be established!'''=¡No se ha podido establecer la conexión! -'''Click 'Done' to return to the menu.'''=Haga clic en “Done” (Hecho) para volver al menú. -'''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=está desconectado de SmartThings porque se han cambiado o perdido las credenciales de acceso. Vaya a la aplicación inteligente de Ecobee (Conectar) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. -'''Your Ecobee thermostat '''=Su termostato Ecobee -'''Select your ecobee devices'''=Seleccione sus dispositivos ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Pulse a continuación para añadir o eliminar los termostatos disponibles en su cuenta de ecobee. Los dispositivos seleccionados se conectarán a SmartThings. -'''Log In'''=Iniciar sesión -'''Tap Next to continue to set up your ecobee thermostats.'''=Pulse Siguiente para continuar con la configuración de los termostatos ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Pulse a continuación para añadir o eliminar los sensores remotos disponibles en su cuenta de ecobee. Los sensores seleccionados se conectarán a SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Pulse a continuación para añadir o eliminar los interruptores disponibles en su cuenta de ecobee. Los interruptores seleccionados se conectarán a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties b/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties deleted file mode 100644 index bf026c87491..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/es-US.properties +++ /dev/null @@ -1,20 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Conecte el termostato Ecobee a SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Está conectado. -'''Click to enter Ecobee Credentials'''=Haga clic para introducir las credenciales de Ecobee -'''Login'''=Inicio de sesión -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Pulse a continuación para iniciar sesión en el servicio de ecobee y otorgar acceso a SmartThings. Asegúrese de desplazarse hacia abajo en la página 2 y de presionar el botón 'Allow' ('Permitir'). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Seleccionar Your Thermostats (Sus termostatos) -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de termostatos ecobee disponibles en su cuenta de ecobee y seleccione los que desea conectar a SmartThings. -'''Tap to choose'''=Pulsar para elegir -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pulse a continuación para ver la lista de sensores ecobee disponibles en su cuenta de ecobee y seleccione los que desea conectar a SmartThings. -'''Tap to choose'''=Pulsar para elegir -'''Select Ecobee Sensors ({{numFound}} found)'''=Seleccionar sensores Ecobee (hay {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=¡Su cuenta de ecobee ahora está conectada a SmartThings! -'''Click 'Done' to finish setup.'''=Haga clic en 'Done' ('Listo') para finalizar la configuración. -'''The connection could not be established!'''=¡No fue posible establecer la conexión! -'''Click 'Done' to return to the menu.'''=Haga clic en 'Done' ('Listo') para volver al menú. -'''is connected to SmartThings'''=está conectado a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=no está conectado a SmartThings debido a que la credencial de acceso se cambió o se perdió. Vaya a la SmartApp de Ecobee (Connect) y vuelva a introducir las credenciales de inicio de sesión de su cuenta. -'''Your Ecobee thermostat '''=Su termostato Ecobee diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties deleted file mode 100644 index 6a70d1e52ac..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/et-EE.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Ühendage oma termostaat Ecobee teenusega SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Ühendus on loodud. -'''Click to enter Ecobee Credentials'''=Klõpsake, et sisestada teenuse Ecobee volitused -'''Login'''=Sisselogimine -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Toksake all, et logida sisse teenusesse ecobee ja autoriseerida teenuse SmartThings juurdepääs. Kerige kindlasti alla lehele 2 ja vajutage nuppu Luba. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Valige oma termostaadid -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toksake all, et näha oma ecobee kontole registreeritud ecobee termostaate ja valige need, mida soovite ühendada teenusega SmartThings. -'''Tap to choose'''=Toksake, et valida -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toksake all, et näha oma ecobee kontole registreeritud ecobee andureid ja valige need, mida soovite ühendada teenusega SmartThings. -'''Tap to choose'''=Toksake, et valida -'''Select Ecobee Sensors ({{numFound}} found)'''=Valige Ecobee andurid (leiti {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Teie ecobee konto on nüüd ühendatud teenusega SmartThings! -'''Click 'Done' to finish setup.'''=Klõpsake valikut Valmis, et seadistamine lõpule viia. -'''The connection could not be established!'''=Ühenduse loomine nurjus! -'''Click 'Done' to return to the menu.'''=Klõpsake valikut Valmis, et naasta menüüsse. -'''is connected to SmartThings'''=on ühendatud teenusega SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=on teenusest SmartThings lahti ühendatud, kuna juurdepääsu volitus muutus või kadus. Avage rakendus Ecobee (Connect) SmartApp ja sisestage uuesti oma konto sisselogimisandmed. -'''Your Ecobee thermostat '''=Teie Ecobee termostaat -'''Select your ecobee devices'''=Valige oma ecobee seadmed -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toksake allpool, et lisada või eemaldada ecobee konto all olevaid termostaate. Valitud termostaadid ühendatakse teenusega SmartThings. -'''Log In'''=Sisselogimine -'''Tap Next to continue to set up your ecobee thermostats.'''=Toksake käsku Edasi, et jätkata oma ecobee termostaatide seadistamist. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Toksake allpool, et lisada või eemaldada ecobee konto all olevaid kaugandureid. Valitud andurid ühendatakse teenusega SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Toksake allpool, et lisada või eemaldada ecobee konto all olevaid lüliteid. Valitud lülitid ühendatakse teenusega SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties b/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties deleted file mode 100644 index cd9eb4b092c..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/fi-FI.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Yhdistä Ecobee-termostaattisi SmartThingsiin. -'''ecobee'''=ecobee -'''You are connected.'''=Yhteys muodostettu. -'''Click to enter Ecobee Credentials'''=Napsauta ja anna Ecobee-tunnistetiedot -'''Login'''=Kirjautuminen -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Kirjaudu ecobee-palveluun ja myönnä SmartThingsille käyttöoikeudet napauttamalla alla. Vieritä alas sivulle 2 ja paina Allow (Salli) -painiketta. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Valitse termostaatit -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Napauttamalla alla voit tuoda ecobee-tililläsi käytettävissä olevien ecobee-termostaattien luettelon näyttöön ja valita SmartThingsiin yhdistettävät laitteet. -'''Tap to choose'''=Valitse napauttamalla -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Napauttamalla alla voit tuoda ecobee-tililläsi käytettävissä olevien ecobee-tunnistimien luettelon näyttöön ja valita SmartThingsiin yhdistettävät laitteet. -'''Tap to choose'''=Valitse napauttamalla -'''Select Ecobee Sensors ({{numFound}} found)'''=Valitse Ecobee-tunnistimet ({{numFound}} löydetty) -'''Your ecobee Account is now connected to SmartThings!'''=ecobee-tilisi on nyt yhdistetty SmartThingsiin! -'''Click 'Done' to finish setup.'''=Viimeistele asennus napsauttamalla Done (Valmis). -'''The connection could not be established!'''=Yhteyden muodostaminen epäonnistui! -'''Click 'Done' to return to the menu.'''=Palaa valikkoon napsauttamalla Done (Valmis). -'''is connected to SmartThings'''=on yhdistetty SmartThingsiin -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=ei enää ole yhteydessä SmartThingsiin, sillä käyttötunnukset ovat muuttuneet tai kadonneet. Siirry Ecobee (Connect) SmartAppiin ja anna tilisi kirjautumistiedot uudelleen. -'''Your Ecobee thermostat '''=Ecobee-termostaattisi -'''Select your ecobee devices'''=Valitse ecobee-laitteet -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Napauttamalla alla voit lisätä tai poistaa ecobee-tililläsi käytettävissä olevat termostaatit. Valitut termostaatit muodostavat yhteyden SmartThingsiin. -'''Log In'''=Kirjaudu sisään -'''Tap Next to continue to set up your ecobee thermostats.'''=Jatka ecobee-termostaattien määritystä napauttamalla Next (Seuraava). -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Napauttamalla alla voit lisätä tai poistaa ecobee-tililläsi käytettävissä olevat etätunnistimet. Valitut tunnistimet muodostavat yhteyden SmartThingsiin. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Napauttamalla alla voit lisätä tai poistaa ecobee-tililläsi käytettävissä olevat kytkimet. Valitut kytkimet muodostavat yhteyden SmartThingsiin. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties b/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties deleted file mode 100644 index c11220a9114..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/fr-CA.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Connectez votre thermostat Ecobee à SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Vous êtes connecté. -'''Click to enter Ecobee Credentials'''=Cliquez pour saisir les informations d'identification Ecobee -'''Login'''=Connexion -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Appuyez ci-dessous pour vous connecter au service ecobee et autoriser l'accès pour SmartThings. Faites défiler l'écran jusqu'en bas de la page 2 et appuyez sur le bouton Allow (Autoriser). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Sélection de vos thermostats -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Appuyez ci-dessous pour afficher la liste des thermostats ecobee disponibles dans votre compte ecobee et sélectionner ceux que vous souhaitez connecter à SmartThings. -'''Tap to choose'''=Appuyez pour sélectionner -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Appuyez ci-dessous pour afficher la liste des capteurs ecobee disponibles dans votre compte ecobee et sélectionner ceux que vous souhaitez connecter à SmartThings. -'''Tap to choose'''=Appuyez pour sélectionner -'''Select Ecobee Sensors ({{numFound}} found)'''=Sélection des capteurs Ecobee ({{numFound}} trouvé(s)) -'''Your ecobee Account is now connected to SmartThings!'''=Votre compte ecobee est maintenant connecté à SmartThings ! -'''Click 'Done' to finish setup.'''=Cliquez sur Done (Terminé) pour terminer la configuration. -'''The connection could not be established!'''=La connexion n'a pas pu être établie ! -'''Click 'Done' to return to the menu.'''=Cliquez sur Done (Terminé) pour revenir au menu. -'''is connected to SmartThings'''=est connecté à SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=est déconnecté de SmartThings, car les identifiants d'accès ont été modifiés ou perdus. Accédez à la SmartApp Ecobee (Connect) et saisissez à nouveau les informations de connexion à votre compte. -'''Your Ecobee thermostat '''=Votre thermostat Ecobee -'''Select your ecobee devices'''=Sélectionnez vos appareils ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Touchez ci-dessous pour ajouter ou retirer des thermostats disponibles dans votre compte ecobee. Les thermostats sélectionnés se connecteront à SmartThings. -'''Log In'''=Connexion -'''Tap Next to continue to set up your ecobee thermostats.'''=Appuyez sur Suivant pour poursuivre la configuration de vos thermostats ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Touchez ci-dessous pour ajouter ou retirer des capteurs de télédétection disponibles dans votre compte ecobee. Les thermostats sélectionnés se connecteront à SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Touchez ci-dessous pour ajouter ou retirer des interrupteurs disponibles dans votre compte ecobee. Les interrupteurs sélectionnés se connecteront à SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties deleted file mode 100644 index 2b065d62c9e..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/fr-FR.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Connectez votre thermostat Ecobee à SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Vous êtes connecté. -'''Click to enter Ecobee Credentials'''=Cliquez pour saisir les informations d'identification Ecobee -'''Login'''=Connexion -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Appuyez ci-dessous pour vous connecter au service ecobee et autoriser l'accès pour SmartThings. Faites défiler l'écran jusqu'en bas de la page 2 et appuyez sur le bouton Allow (Autoriser). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Sélection de vos thermostats -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Appuyez ci-dessous pour afficher la liste des thermostats ecobee disponibles dans votre compte ecobee et sélectionner ceux que vous souhaitez connecter à SmartThings. -'''Tap to choose'''=Appuyez pour sélectionner -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Appuyez ci-dessous pour afficher la liste des capteurs ecobee disponibles dans votre compte ecobee et sélectionner ceux que vous souhaitez connecter à SmartThings. -'''Tap to choose'''=Appuyez pour sélectionner -'''Select Ecobee Sensors ({{numFound}} found)'''=Sélection des capteurs Ecobee ({{numFound}} trouvé(s)) -'''Your ecobee Account is now connected to SmartThings!'''=Votre compte ecobee est maintenant connecté à SmartThings ! -'''Click 'Done' to finish setup.'''=Cliquez sur Done (Terminé) pour terminer la configuration. -'''The connection could not be established!'''=La connexion n'a pas pu être établie ! -'''Click 'Done' to return to the menu.'''=Cliquez sur Done (Terminé) pour revenir au menu. -'''is connected to SmartThings'''=est connecté à SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=est déconnecté de SmartThings, car les identifiants d'accès ont été modifiés ou perdus. Accédez à la SmartApp Ecobee (Connect) et saisissez à nouveau les informations de connexion à votre compte. -'''Your Ecobee thermostat '''=Votre thermostat Ecobee -'''Select your ecobee devices'''=Sélectionnez vos appareils ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Appuyez ci-dessous pour ajouter ou supprimer les thermostats disponibles dans votre compte ecobee. Les thermostats sélectionnés se connecteront à SmartThings. -'''Log In'''=Connexion -'''Tap Next to continue to set up your ecobee thermostats.'''=Appuyez sur Suivant pour poursuivre la configuration de vos thermostats ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Appuyez ci-dessous pour ajouter ou supprimer les télédétecteurs disponibles dans votre compte ecobee. Les détecteurs sélectionnés se connecteront à SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Appuyez ci-dessous pour ajouter ou supprimer les interrupteurs disponibles dans votre compte ecobee. Les interrupteurs sélectionnés se connecteront à SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties deleted file mode 100644 index a65bd0e6ea8..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/hr-HR.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Povežite termostat Ecobee s uslugom SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Povezani ste. -'''Click to enter Ecobee Credentials'''=Kliknite za unos podataka za prijavu za Ecobee -'''Login'''=Prijava -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Dodirnite u nastavku da biste se prijavili u uslugu ecobee i odobrili pristup za SmartThings. Na 2. se stranici pomaknite prema dolje i pritisnite gumb „Allow” (Dopusti). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Odaberite termostate -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Dodirnite u nastavku da biste vidjeli popis termostata ecobee dostupnih na vašem računu za ecobee i odaberite one koje želite povezati s uslugom SmartThings. -'''Tap to choose'''=Dodirnite za odabir -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Dodirnite u nastavku da biste vidjeli popis senzora ecobee dostupnih na vašem računu za ecobee i odaberite one koje želite povezati s uslugom SmartThings. -'''Tap to choose'''=Dodirnite za odabir -'''Select Ecobee Sensors ({{numFound}} found)'''=Odaberite senzore Ecobee (pronađeno: {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Račun za ecobee sada je povezan s uslugom SmartThings! -'''Click 'Done' to finish setup.'''=Kliknite „Done” (Gotovo) da biste dovršili postavljanje. -'''The connection could not be established!'''=Veza se nije uspostavila! -'''Click 'Done' to return to the menu.'''=Kliknite „Done” (Gotovo) za vraćanje na izbornik. -'''is connected to SmartThings'''=povezan je s uslugom SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=nije povezan s uslugom SmartThings jer su se pristupni podaci promijenili ili izgubili. Idite na Ecobee (Connect) SmartApp i ponovno unesite podatke za prijavu na račun. -'''Your Ecobee thermostat '''=Termostat Ecobee -'''Select your ecobee devices'''=Odaberite uređaje ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Dodirnite u nastavku da biste dodali ili uklonili termostate dostupne na računu za ecobee. Odabrani termostati povezat će se s uslugom SmartThings. -'''Log In'''=Prijava -'''Tap Next to continue to set up your ecobee thermostats.'''=Dodirnite Next (Dalje) da biste nastavili s postavljanjem termostata ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Dodirnite u nastavku da biste dodali ili uklonili daljinske senzore dostupne na računu za ecobee. Odabrani senzori povezat će se s uslugom SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Dodirnite u nastavku da biste dodali ili uklonili prekidače dostupne na računu za ecobee. Odabrani prekidači povezat će se s uslugom SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties b/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties deleted file mode 100644 index 4527593558c..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/hu-HU.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Ecobee termosztátot csatlakoztathat a SmartThings rendszerhez. -'''ecobee'''=ecobee -'''You are connected.'''=Kapcsolódott. -'''Click to enter Ecobee Credentials'''=Kattintson az Ecobee-hitelesítőadatok megadásához -'''Login'''=Bejelentkezés -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Az alábbi hivatkozás megérintésével bejelentkezhet az ecobee szolgáltatásba, és engedélyezheti a SmartThings-hozzáférést. Görgessen le a 2. oldalon, és nyomja meg az „Allow” (Engedélyezés) gombot. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=A termosztátok kiválasztása -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Az alábbi hivatkozás megérintésével megjelenítheti az ecobee-fiókjában rendelkezésre álló ecobee termosztátok listáját, és kiválaszthatja azokat, amelyeket csatlakoztatni szeretne a SmartThings rendszerhez. -'''Tap to choose'''=Érintse meg a kiválasztáshoz -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Az alábbi hivatkozás megérintésével megjelenítheti az ecobee-fiókjában rendelkezésre álló ecobee érzékelők listáját, és kiválaszthatja azokat, amelyeket csatlakoztatni szeretne a SmartThings rendszerhez. -'''Tap to choose'''=Érintse meg a kiválasztáshoz -'''Select Ecobee Sensors ({{numFound}} found)'''=Ecobee érzékelők kiválasztása ({{numFound}} találat) -'''Your ecobee Account is now connected to SmartThings!'''=Csatlakoztatta ecobee-fiókját a SmartThings rendszerhez! -'''Click 'Done' to finish setup.'''=A telepítés befejezéséhez kattintson a „Done” (Kész) gombra. -'''The connection could not be established!'''=Nem sikerült kapcsolatot létesíteni! -'''Click 'Done' to return to the menu.'''=A menühöz való visszatéréshez kattintson a „Done” (Kész) gombra. -'''is connected to SmartThings'''=kapcsolódott a SmartThings rendszerhez -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=le lett választva a SmartThings rendszerről, mert megváltoztak vagy elvesztek a hozzáférési hitelesítő adatok. Adja meg újra a fiókja bejelentkezési hitelesítő adatait a Ecobee (Connect) SmartApp segítségével. -'''Your Ecobee thermostat '''=Az Ön Ecobee termosztátja -'''Select your ecobee devices'''=Az ecobee eszközök kiválasztása -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Az alábbi lehetőség megérintésével veheti fel, illetve távolíthatja el az ecobee-fiókjában rendelkezésre álló termosztátokat. A kiválasztott eszközök csatlakozni fognak a SmartThings szolgáltatáshoz. -'''Log In'''=Bejelentkezés -'''Tap Next to continue to set up your ecobee thermostats.'''=Érintse meg a Next (Tovább) gombot az ecobee termosztátok beállításának folytatásához. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Az alábbi lehetőség megérintésével veheti fel, illetve távolíthatja el az ecobee-fiókjában rendelkezésre álló távoli érzékelőket. A kiválasztott érzékelők csatlakozni fognak a SmartThings szolgáltatáshoz. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Az alábbi lehetőség megérintésével veheti fel, illetve távolíthatja el az ecobee-fiókjában rendelkezésre álló kapcsolókat. A kiválasztott kapcsolók csatlakozni fognak a SmartThings szolgáltatáshoz. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties b/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties deleted file mode 100644 index 1b7f518acd8..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/it-IT.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Connettete il termostato Ecobee a SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Connessione effettuata. -'''Click to enter Ecobee Credentials'''=Fate clic per inserire le credenziali Ecobee -'''Login'''=Accesso -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Toccate di seguito per accedere al servizio ecobee e autorizzare l'accesso a SmartThings. Scorrete fino in fondo alla pagina 2 e premete il pulsante “Allow” (Consenti). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Selezionate i termostati -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toccate di seguito per visualizzare l'elenco dei termostati ecobee disponibili nell'account ecobee e selezionate quelli che volete connettere a SmartThings. -'''Tap to choose'''=Toccate per scegliere -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toccate di seguito per visualizzare l'elenco dei sensori ecobee disponibili nell'account ecobee e selezionate quelli che volete connettere a SmartThings. -'''Tap to choose'''=Toccate per scegliere -'''Select Ecobee Sensors ({{numFound}} found)'''=Selezionate i sensori Ecobee ({{numFound}} trovati) -'''Your ecobee Account is now connected to SmartThings!'''=L'account ecobee è ora connesso a SmartThings. -'''Click 'Done' to finish setup.'''=Fate clic su “Done” (Fatto) per terminare la configurazione. -'''The connection could not be established!'''=Non è stato possibile stabilire la connessione. -'''Click 'Done' to return to the menu.'''=Fate clic su “Done” (Fatto) per tornare al menu. -'''is connected to SmartThings'''=connesso a SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=disconnesso da SmartThings. Le credenziali di accesso sono state modificate o sono andate perse. Andate alla SmartApp di Ecobee (Connect) e inserite nuovamente le credenziali di accesso all'account. -'''Your Ecobee thermostat '''=Il termostato Ecobee -'''Select your ecobee devices'''=Selezionate i dispositivi ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toccate di seguito per aggiungere o rimuovere i termostati disponibili nell’account ecobee. I termostati selezionati si connetteranno a SmartThings. -'''Log In'''=Accesso -'''Tap Next to continue to set up your ecobee thermostats.'''=Toccate Next (Avanti) per proseguire con la configurazione dei termostati ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Toccate di seguito per aggiungere o rimuovere i sensori remoti disponibili nell’account ecobee. I sensori selezionati si connetteranno a SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Toccate di seguito per aggiungere o rimuovere gli interruttori disponibili nell’account ecobee. Gli interruttori selezionati si connetteranno a SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties deleted file mode 100644 index 0dd465aff0f..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ko-KR.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Ecobee 온도조절기를 SmartThings에 연결하세요. -'''ecobee'''=Ecobee -'''You are connected.'''=연결되었습니다. -'''Click to enter Ecobee Credentials'''=Ecobee 로그인 정보를 입력하려면 클릭하세요 -'''Login'''=로그인 -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Ecobee 서비스에 로그인하여 SmartThings를 사용할 수 있도록 인증하려면 아래를 누르세요. 2페이지에서 아래로 스크롤한 후 [허용]을 누르세요. -'''ecobee'''=Ecobee -'''Select Your Thermostats'''=온도조절기 선택 -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Ecobee 계정에 등록된 Ecobee 온도조절기 목록을 확인하고 SmartThings에 연결할 기기를 선택하려면 아래를 누르세요. -'''Tap to choose'''=눌러서 선택 -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Ecobee 계정에 등록된 Ecobee 센서 목록을 확인하고 SmartThings에 연결할 기기를 선택하려면 아래를 누르세요. -'''Tap to choose'''=눌러서 선택 -'''Select Ecobee Sensors ({{numFound}} found)'''=Ecobee 센서 선택 ({{numFound}}개 찾음) -'''Your ecobee Account is now connected to SmartThings!'''=Ecobee 계정이 SmartThings에 연결되었습니다! -'''Click 'Done' to finish setup.'''=설정을 완료하려면 [완료]를 클릭하세요. -'''The connection could not be established!'''=연결을 실행할 수 없습니다! -'''Click 'Done' to return to the menu.'''=메뉴로 돌아가려면 [완료]를 클릭하세요. -'''is connected to SmartThings'''=이(가) SmartThings에 연결되었습니다 -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=이(가) SmartThings에서 연결 해제되었습니다. 로그인 정보가 변경되었거나 유실되었습니다. Ecobee (연결) 스마트앱에서 계정의 로그인 정보를 다시 입력하세요. -'''Your Ecobee thermostat '''=Ecobee 온도조절기 -'''Select your ecobee devices'''=ecobee 기기 선택 -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=ecobee 계정에서 사용할 수 있는 온도조절기를 추가하거나 삭제하려면 아래를 누르세요. 선택된 온도조절기를 SmartThings에 연결합니다. -'''Log In'''=로그인 -'''Tap Next to continue to set up your ecobee thermostats.'''=계속해서 ecobee 온도조절기를 설정하려면 [다음]을 누르세요. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=ecobee 계정에서 사용할 수 있는 원격 센서를 추가하거나 삭제하려면 아래를 누르세요. 선택된 센서를 SmartThings에 연결합니다. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=ecobee 계정에서 사용할 수 있는 스위치를 추가하거나 삭제하려면 아래를 누르세요. 선택된 스위치를 SmartThings에 연결합니다. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties b/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties deleted file mode 100644 index 173ef4e7e3c..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/nl-NL.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Verbind uw Ecobee-thermostaat met SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=U bent verbonden. -'''Click to enter Ecobee Credentials'''=Klik om Ecobee-inloggegevens in te voeren -'''Login'''=Inloggen -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Tik hieronder om in te loggen bij uw ecobee-service en toegang door SmartThings toe te staan. Scrol naar beneden op pagina 2 en druk op de knop Allow (Toestaan). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Selecteer uw thermostaten -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tik hieronder om de lijst met ecobee-thermostaten in uw ecobee-account weer te geven en de apparaten te selecteren die u wilt verbinden met SmartThings. -'''Tap to choose'''=Tik om te kiezen -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tik hieronder om de lijst met ecobee-sensoren in uw ecobee-account weer te geven en de apparaten te selecteren die u wilt verbinden met SmartThings. -'''Tap to choose'''=Tik om te kiezen -'''Select Ecobee Sensors ({{numFound}} found)'''=Selecteer Ecobee-sensoren ({{numFound}} gevonden) -'''Your ecobee Account is now connected to SmartThings!'''=Uw ecobee-account is nu verbonden met SmartThings. -'''Click 'Done' to finish setup.'''=Klik op Done (Gereed) om het instellen te voltooien. -'''The connection could not be established!'''=Er kan geen verbinding worden gemaakt. -'''Click 'Done' to return to the menu.'''=Klik op Done (Gereed) om terug te gaan naar het menu. -'''is connected to SmartThings'''=is verbonden met SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=-verbinding met SmartThings is verbroken, omdat de inloggegevens zijn gewijzigd of verloren zijn gegaan. Ga naar de Ecobee (Connect) SmartApp en voer de inloggegevens voor uw account opnieuw in. -'''Your Ecobee thermostat '''=Uw Ecobee-thermostaat -'''Select your ecobee devices'''=Selecteer uw ecobee-apparaten -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tik hieronder om thermostaten die beschikbaar zijn in uw ecobee-account, toe te voegen of te verwijderen. Geselecteerde thermostaten maken verbinding met SmartThings. -'''Log In'''=Inloggen -'''Tap Next to continue to set up your ecobee thermostats.'''=Tik op Volgende om verder te gaan met het instellen van uw ecobee-thermostaten. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Tik hieronder om externe sensoren die beschikbaar zijn in uw ecobee-account, toe te voegen of te verwijderen. Geselecteerde sensoren maken verbinding met SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Tik hieronder om schakelaars die beschikbaar zijn in uw ecobee-account, toe te voegen of te verwijderen. Geselecteerde schakelaars maken verbinding met SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties b/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties deleted file mode 100644 index b38993c2ba0..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/no-NO.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Koble Ecobee-termostaten til SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Du er tilkoblet. -'''Click to enter Ecobee Credentials'''=Klikk for å angi Ecobee-informasjon -'''Login'''=Logg på -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Trykk nedenfor for å logge på ecobee-tjenesten og godkjenne SmartThings-tilgang. Pass på å bla ned på side 2 og trykke på Allow (Tillat)-knappen. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Velg termostatene dine -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Trykk nedenfor for å se listen over ecobee-termostatene som er tilgjengelige i ecobee-kontoen din, og velg de du vil koble til SmartThings. -'''Tap to choose'''=Trykk for å velge -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Trykk nedenfor for å se listen over ecobee-sensorene som er tilgjengelige i ecobee-kontoen din, og velg de du vil koble til SmartThings. -'''Tap to choose'''=Trykk for å velge -'''Select Ecobee Sensors ({{numFound}} found)'''=Velg Ecobee-sensorer (fant {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=ecobee-kontoen din er nå koblet til SmartThings! -'''Click 'Done' to finish setup.'''=Klikk på Done (Ferdig) for å fullføre oppsettet. -'''The connection could not be established!'''=Kunne ikke opprette tilkoblingen! -'''Click 'Done' to return to the menu.'''=Klikk på Done (Ferdig) for å gå tilbake til menyen. -'''is connected to SmartThings'''=er koblet til SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=er koblet fra SmartThings fordi tilgangsinformasjonen ble endret eller mistet. Gå til Ecobee (Connect) SmartApp, og angi påloggingsinformasjonen for kontoen på nytt. -'''Your Ecobee thermostat '''=Ecobee-termostaten -'''Select your ecobee devices'''=Velg ecobee-enhetene dine -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Trykk nedenfor for å legge til eller fjerne termostater som er tilgjengelige i ecobee-kontoen din. Valgte termostater blir koblet til SmartThings. -'''Log In'''=Logg på -'''Tap Next to continue to set up your ecobee thermostats.'''=Trykk på Next (Neste) for å fortsette å sette opp ecobee-termostatene. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Trykk nedenfor for å legge til eller fjerne sensorer som er tilgjengelige i ecobee-kontoen din. Valgte sensorer blir koblet til SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Trykk nedenfor for å legge til eller fjerne brytere som er tilgjengelige i ecobee-kontoen din. Valgte brytere blir koblet til SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties b/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties deleted file mode 100644 index b5c48ef48ac..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/pl-PL.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Połącz termostat Ecobee ze SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Połączono. -'''Click to enter Ecobee Credentials'''=Kliknij, aby wprowadzić poświadczenia Ecobee -'''Login'''=Logowanie -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Dotknij poniżej, aby zalogować się do usługi ecobee i autoryzować dostęp SmartThings. Przewiń w dół na stronie 2 i naciśnij przycisk „Allow” (Zezwól). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Wybierz termostaty -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Dotknij poniżej, aby wyświetlić listę termostatów ecobee dostępnych na koncie ecobee, i wybierz te, które chcesz połączyć ze SmartThings. -'''Tap to choose'''=Dotknij, aby wybrać -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Dotknij poniżej, aby wyświetlić listę czujników ecobee dostępnych na koncie ecobee, i wybierz te, które chcesz połączyć ze SmartThings. -'''Tap to choose'''=Dotknij, aby wybrać -'''Select Ecobee Sensors ({{numFound}} found)'''=Wybierz czujniki Ecobee (znaleziono {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Konto ecobee jest teraz połączone ze SmartThings. -'''Click 'Done' to finish setup.'''=Kliknij opcję „Done” (Gotowe), aby ukończyć instalację. -'''The connection could not be established!'''=Nie można ustanowić połączenia. -'''Click 'Done' to return to the menu.'''=Kliknij opcję „Done” (Gotowe), aby powrócić do menu. -'''is connected to SmartThings'''=jest połączony ze SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=jest odłączony od SmartThings, ponieważ poświadczenie dostępu zostało zmienione lub utracone. Przejdź do aplikacji Ecobee (Connect) SmartApp i wprowadź ponownie poświadczenia logowania konta. -'''Your Ecobee thermostat '''=Termostat Ecobee -'''Select your ecobee devices'''=Wybierz urządzenia ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Dotknij poniżej, aby dodać lub usunąć termostaty dostępne na koncie ecobee. Wybrane termostaty zostaną połączone ze SmartThings. -'''Log In'''=Logowanie -'''Tap Next to continue to set up your ecobee thermostats.'''=Dotknij opcji Dalej, aby skonfigurować termostaty ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Dotknij poniżej, aby dodać lub usunąć zdalne czujniki dostępne na koncie ecobee. Wybrane czujniki zostaną połączone ze SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Dotknij poniżej, aby dodać lub usunąć przełączniki dostępne na koncie ecobee. Wybrane przełączniki zostaną połączone ze SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties deleted file mode 100644 index cedf11f9b31..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/pt-BR.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Conecte seu termostato Ecobee ao SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Você está conectado. -'''Click to enter Ecobee Credentials'''=Clique para inserir as credenciais do Ecobee -'''Login'''=Conectar -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Toque abaixo para entrar no serviço ecobee e autorizar o acesso ao SmartThings. Certifique-se de rolar para baixo na página 2 e pressionar o botão 'Allow' (Permitir). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Selecionar seus termostatos -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toque abaixo para ver a lista de termostatos ecobee disponíveis na sua conta ecobee e selecione os que deseja conectar ao SmartThings. -'''Tap to choose'''=Tocar para escolher -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toque abaixo para ver a lista de sensores ecobee disponíveis na sua conta ecobee e selecione os que deseja conectar ao SmartThings. -'''Tap to choose'''=Tocar para escolher -'''Select Ecobee Sensors ({{numFound}} found)'''=Selecionar sensores Ecobee ({{numFound}} encontrado(s)) -'''Your ecobee Account is now connected to SmartThings!'''=Agora sua conta ecobee está conectada ao SmartThings! -'''Click 'Done' to finish setup.'''=Clique em 'Done' (Concluído) para concluir a configuração. -'''The connection could not be established!'''=Não foi possível estabelecer a conexão! -'''Click 'Done' to return to the menu.'''=Clique em 'Done' (Concluído) para retornar ao menu. -'''is connected to SmartThings'''=está conectado ao SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=foi desconectado do SmartThings, pois a credencial de acesso foi alterada ou perdida. Vá para Ecobee (Connect) SmartApp e insira novamente suas credenciais de acesso à conta. -'''Your Ecobee thermostat '''=Seu termostato Ecobee -'''Select your ecobee devices'''=Selecionar seus aparelhos ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toque abaixo para adicionar ou remover os termostatos disponíveis na sua conta ecobee. Os termostatos selecionados serão conectados ao SmartThings. -'''Log In'''=Entrar -'''Tap Next to continue to set up your ecobee thermostats.'''=Toque em Avançar para configurar seus termostatos ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Toque abaixo para adicionar ou remover os sensores remotos disponíveis na sua conta ecobee. Os sensores selecionados serão conectados ao SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Toque abaixo para adicionar ou remover os interruptores disponíveis na sua conta ecobee. Os interruptores selecionados serão conectados ao SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties b/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties deleted file mode 100644 index d70f944e1e2..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/pt-PT.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Ligue o seu termóstato Ecobee ao SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Está ligado. -'''Click to enter Ecobee Credentials'''=Clique para introduzir as Credenciais da Ecobee -'''Login'''=Iniciar Sessão -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Toque abaixo para iniciar sessão no serviço ecobee e autorizar o acesso ao SmartThings. Certifique-se de que se desloca para baixo na página 2 e prime o botão "Allow" (Permitir). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Seleccionar os seus Termóstatos -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toque abaixo para ver a lista de termóstatos da ecobee disponíveis na sua conta ecobee e seleccione aqueles que pretende ligar ao SmartThings. -'''Tap to choose'''=Toque para escolher -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Toque abaixo para ver a lista de sensores da ecobee disponíveis na sua conta ecobee e seleccione aqueles que pretende ligar ao SmartThings. -'''Tap to choose'''=Toque para escolher -'''Select Ecobee Sensors ({{numFound}} found)'''=Seleccionar sensores da Ecobee ({{numFound}} encontrado) -'''Your ecobee Account is now connected to SmartThings!'''=Agora, a sua Conta ecobee está ligada ao SmartThings! -'''Click 'Done' to finish setup.'''=Clique em "Done" (Concluir) para terminar a configuração. -'''The connection could not be established!'''=Não foi possível estabelecer a ligação! -'''Click 'Done' to return to the menu.'''=Clique em "Done" (Concluir) para regressar ao menu. -'''is connected to SmartThings'''=está ligado ao SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=foi desligado do SmartThings, porque a credencial de acesso foi alterada ou perdida. Vá para Ecobee (Connect) SmartApp e introduza novamente as suas credenciais de início de sessão na conta. -'''Your Ecobee thermostat '''=O seu termóstato Ecobee -'''Select your ecobee devices'''=Seleccionar os seus dispositivos ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Toque abaixo para adicionar ou remover termóstatos disponíveis na sua conta ecobee. Os termóstatos seleccionados irão ligar-se ao SmartThings. -'''Log In'''=Iniciar Sessão -'''Tap Next to continue to set up your ecobee thermostats.'''=Toque em Seguinte para continuar a configurar os seus termóstatos ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Toque abaixo para adicionar ou remover sensores remotos disponíveis na sua conta ecobee. Os sensores seleccionados irão ligar-se ao SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Toque abaixo para adicionar ou remover interruptores disponíveis na sua conta ecobee. Os interruptores seleccionados irão ligar-se ao SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties deleted file mode 100644 index d552f704983..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ro-RO.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Conectați termostatul Ecobee la SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Sunteți conectat. -'''Click to enter Ecobee Credentials'''=Faceți clic pentru a introduce acreditările Ecobee -'''Login'''=Conectare -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Atingeți mai jos pentru a vă conecta la serviciul ecobee și a autoriza accesul la SmartThings. Asigurați-vă că ați derulat în jos până la pagina 2 și apăsați butonul „Allow” (Permitere). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Selectați termostatele -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Atingeți mai jos pentru a vizualiza o listă de termostate ecobee disponibile în contul dvs. ecobee și selectați-le pe cele pe care doriți să le conectați la SmartThings. -'''Tap to choose'''=Atingeți pentru a selecta -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Atingeți mai jos pentru a vizualiza o listă de senzori ecobee disponibili în contul dvs. ecobee și selectați-i pe cei pe care doriți să îi conectați la SmartThings. -'''Tap to choose'''=Atingeți pentru a selecta -'''Select Ecobee Sensors ({{numFound}} found)'''=Selectare senzori Ecobee ({{numFound}} găsiți) -'''Your ecobee Account is now connected to SmartThings!'''=Contul dvs. ecobee este acum conectat la SmartThings! -'''Click 'Done' to finish setup.'''=Faceți clic pe „Done” (Efectuat) pentru a finaliza configurarea. -'''The connection could not be established!'''=Nu a putut fi stabilită conexiunea! -'''Click 'Done' to return to the menu.'''=Faceți clic pe „Done” (Efectuat) pentru a reveni la meniu. -'''is connected to SmartThings'''=este conectat la SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=este deconectat de la SmartThings, deoarece acreditările de acces au fost schimbate sau pierdute. Accesați aplicația Ecobee (Connect) SmartApp și reintroduceți acreditările de conectare la cont. -'''Your Ecobee thermostat '''=Termostatul dvs. Ecobee -'''Select your ecobee devices'''=Selectați dispozitivele dvs. ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Atingeți mai jos pentru a adăuga sau elimina termostate disponibile în contul dvs. ecobee. Termostatele selectate se vor conecta la SmartThings. -'''Log In'''=Conectare -'''Tap Next to continue to set up your ecobee thermostats.'''=Atingeți Înainte pentru a continua să configurați termostatele ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Atingeți mai jos pentru a adăuga sau elimina senzorii la distanță disponibili în contul dvs. ecobee. Senzorii selectați se vor conecta la SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Atingeți mai jos pentru a adăuga sau elimina comutatoare disponibile în contul dvs. ecobee. Comutatoarele selectate se vor conecta la SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties b/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties deleted file mode 100644 index bfa4b083b0e..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/ru-RU.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Подключите свой термостат Ecobee к SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Подключено. -'''Click to enter Ecobee Credentials'''=Нажмите для ввода учетных данных Ecobee -'''Login'''=Вход -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Коснитесь ниже, чтобы войти в службу ecobee и предоставить доступ SmartThings. Обязательно прокрутите страницу 2 до самого низа и нажмите кнопку «Разрешить». -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Выберите свои термостаты -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Коснитесь ниже, чтобы отобразить список доступных термостатов ecobee в вашей учетной записи ecobee, и выберите те, которые нужно подключить к SmartThings. -'''Tap to choose'''=Коснитесь, чтобы выбрать -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Коснитесь ниже, чтобы отобразить список доступных датчиков ecobee в вашей учетной записи ecobee, и выберите те, которые нужно подключить к SmartThings. -'''Tap to choose'''=Коснитесь, чтобы выбрать -'''Select Ecobee Sensors ({{numFound}} found)'''=Выбрать датчики Ecobee (найдено {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Теперь ваша учетная запись ecobee подключена к SmartThings! -'''Click 'Done' to finish setup.'''=Для завершения настройки нажмите «Готово». -'''The connection could not be established!'''=Не удалось установить соединение! -'''Click 'Done' to return to the menu.'''=Чтобы вернуться в меню, нажмите «Готово». -'''is connected to SmartThings'''=подключено к SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=отключено от SmartThings, поскольку данные для доступа были изменены или потеряны. Перейдите в Ecobee (Подключить) SmartApp и повторно введите регистрационные данные своей учетной записи. -'''Your Ecobee thermostat '''=Ваш термостат Ecobee -'''Select your ecobee devices'''=Выберите свои устройства ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Чтобы добавить или удалить доступные термостаты в учетной записи ecobee, коснитесь ниже. Выбранные термостаты будут подключены к SmartThings. -'''Log In'''=Войти -'''Tap Next to continue to set up your ecobee thermostats.'''=Чтобы продолжить настройку термостатов ecobee, нажмите “Далее”. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Чтобы добавить или удалить доступные дистанционные датчики в учетной записи ecobee, коснитесь ниже. Выбранные датчики будут подключены к SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Чтобы добавить или удалить доступные переключатели в учетной записи ecobee, коснитесь ниже. Выбранные переключатели будут подключены к SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties deleted file mode 100644 index e0e9fb1ced3..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sk-SK.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Pripojte termostat Ecobee k systému SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Ste pripojení. -'''Click to enter Ecobee Credentials'''=Kliknite a zadajte poverenia pre Ecobee -'''Login'''=Prihlásiť sa -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Ťuknutím nižšie sa prihláste k službe ecobee a autorizujte prístup do systému SmartThings. Prejdite nadol na stránku 2 a stlačte tlačidlo Allow (Povoliť). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Vyberte termostaty -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Ťuknutím nižšie môžete zobraziť zoznam termostatov ecobee dostupných vo vašom konte ecobee a vybrať tie, ktoré chcete pripojiť k systému SmartThings. -'''Tap to choose'''=Ťuknutím vyberte -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Ťuknutím nižšie môžete zobraziť zoznam senzorov ecobee dostupných vo vašom konte ecobee a vybrať tie, ktoré chcete pripojiť k systému SmartThings. -'''Tap to choose'''=Ťuknutím vyberte -'''Select Ecobee Sensors ({{numFound}} found)'''=Vyberte senzory Ecobee (nájdené: {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Vaše konto ecobee je teraz prepojené so systémom SmartThings. -'''Click 'Done' to finish setup.'''=Kliknutím na tlačidlo Done (Hotovo) dokončite inštaláciu. -'''The connection could not be established!'''=Nepodarilo sa nadviazať spojenie. -'''Click 'Done' to return to the menu.'''=Kliknutím na tlačidlo Done (Hotovo) sa vráťte do menu. -'''is connected to SmartThings'''=je pripojený k systému SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=je odpojený od systému SmartThings, pretože prístupové poverenia boli zmenené alebo stratené. Prejdite do aplikácie Ecobee (Connect) SmartApp a znova zadajte prihlasovacie poverenia pre konto. -'''Your Ecobee thermostat '''=Váš termostat Ecobee -'''Select your ecobee devices'''=Vyberte svoje zariadenia ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Ťuknutím nižšie môžete pridať alebo odstrániť termostaty dostupné vo vašom konte ecobee. Vybraté termostaty sa pripoja k systému SmartThings. -'''Log In'''=Prihlásiť sa -'''Tap Next to continue to set up your ecobee thermostats.'''=Ťuknutím na tlačidlo Ďalej pokračujte v nastavovaní termostatov ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Ťuknutím nižšie môžete pridať alebo odstrániť diaľkové senzory dostupné vo vašom konte ecobee. Vybraté senzory sa pripoja k systému SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Ťuknutím nižšie môžete pridať alebo odstrániť vypínače dostupné vo vašom konte ecobee. Vybraté vypínače sa pripoja k systému SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties deleted file mode 100644 index c4b71c54066..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sl-SI.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Povežite termostat Ecobee s storitvijo SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Povezani ste. -'''Click to enter Ecobee Credentials'''=Kliknite za vnos poverilnic Ecobee -'''Login'''=Prijava -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Pritisnite spodaj, da se prijavite v storitev ecobee in odobrite dostop do storitve SmartThings. Pomaknite se na 2. stran in pritisnite gumb »Allow« (Dovoli). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Izberite svoje termostate -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pritisnite spodaj za prikaz seznama termostatov ecobee, ki so na voljo v vašem računu ecobee, in izberite tiste, ki jih želite povezati s storitvijo SmartThings. -'''Tap to choose'''=Pritisnite za izbiranje -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Pritisnite spodaj za prikaz seznama senzorjev ecobee, ki so na voljo v vašem računu ecobee, in izberite tiste, ki jih želite povezati s storitvijo SmartThings. -'''Tap to choose'''=Pritisnite za izbiranje -'''Select Ecobee Sensors ({{numFound}} found)'''=Izberite senzorje Ecobee (št. najdenih: {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Vaš račun ecobee je zdaj povezan s storitvijo SmartThings! -'''Click 'Done' to finish setup.'''=Kliknite »Done« (Končano), da zaključite nastavitev. -'''The connection could not be established!'''=Povezave ni bilo mogoče vzpostaviti! -'''Click 'Done' to return to the menu.'''=Kliknite »Done« (Končano), da se vrnete v meni. -'''is connected to SmartThings'''=je povezan s storitvijo SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=ni povezan s storitvijo SmartThings, ker so bile poverilnice za dostop spremenjene ali izgubljene. Pojdite v aplikacijo Ecobee (Connect) SmartApp in znova vnesite poverilnice za prijavo v račun. -'''Your Ecobee thermostat '''=Vaš termostat Ecobee -'''Select your ecobee devices'''=Izberite naprave ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Pritisnite spodaj, da dodate ali odstranite termostate, ki so na voljo v vašem računu ecobee. Izbrani termostati se bodo povezali s storitvijo SmartThings. -'''Log In'''=Prijava -'''Tap Next to continue to set up your ecobee thermostats.'''=Za nadaljevanje pritisnite Next (Naprej), da nastavite termostate ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Pritisnite spodaj, da dodate ali odstranite oddaljene senzorje, ki so na voljo v vašem računu ecobee. Izbrani senzorji se bodo povezali s storitvijo SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Pritisnite spodaj, da dodate ali odstranite stikala, ki so na voljo v vašem računu ecobee. Izbrana stikala se bodo povezala s storitvijo SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties deleted file mode 100644 index d92f42c06bf..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sq-AL.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Lidh termostatin Ecobee me SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Je lidhur. -'''Click to enter Ecobee Credentials'''=Kliko për të futur kredencialet Ecobee -'''Login'''=Login -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Trokit më poshtë për t’u loguar në shërbimin ecobee dhe autorizuar aksesin në SmartThings. Sigurohu që të lundrosh poshtë në faqen 2 dhe të shtypësh butonin ‘Allow’ (Lejo). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Përzgjidh termostatet e tua -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Trokit më poshtë për të parë listën e termostateve ecobee që janë në dispozicion në llogarinë tënde ecobee dhe përzgjidh ato që dëshiron të lidhen me SmartThings. -'''Tap to choose'''=Trokit për të zgjedhur -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Trokit më poshtë për të parë listën e sensorëve ecobee që janë në dispozicion në llogarinë tënde ecobee dhe përzgjidh ato që dëshiron të lidhen me SmartThings. -'''Tap to choose'''=Trokit për të zgjedhur -'''Select Ecobee Sensors ({{numFound}} found)'''=Përzgjidh sensorët Ecobee (u gjet {{numFound}}) -'''Your ecobee Account is now connected to SmartThings!'''=Llogaria jote ecobee tani është lidhur me SmartThings! -'''Click 'Done' to finish setup.'''=Kliko mbi ‘Done’ (U krye) për ta mbaruar konfigurimin. -'''The connection could not be established!'''=Lidhja nuk u vendos dot! -'''Click 'Done' to return to the menu.'''=Kliko mbi ‘Done’ (U krye) për t’u kthyer në meny. -'''is connected to SmartThings'''=është lidhur me SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=është shkëputur nga SmartThings, sepse kredenciali i aksesit ka ndryshuar ose ka humbur. Shko te Ecobee (Connect) SmartApp dhe futi sërish kredencialet e logimit në llogari. -'''Your Ecobee thermostat '''=Termostati yt Ecobee -'''Select your ecobee devices'''=Përzgjidh pajisjet e tua ecobee -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Trokit më poshtë për të shtuar ose hequr termostate që gjenden në llogarinë tënde ecobee. Termostatet e përzgjedhura do të lidhen me SmartThings. -'''Log In'''=Logohu -'''Tap Next to continue to set up your ecobee thermostats.'''=Trokit mbi Next (Tjetri) për të konfiguruar termostatet e tua ecobee. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Trokit më poshtë për të shtuar ose hequr sensorë në distancë që gjenden në llogarinë tënde ecobee. Sensorët e përzgjedhur do të lidhen me SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Trokit më poshtë për të shtuar ose hequr çelësa që gjenden në llogarinë tënde ecobee. Çelësat e përzgjedhur do të lidhen me SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties deleted file mode 100644 index 2ed4545e50c..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sr-RS.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Povežite Ecobee termostat na SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Povezani ste. -'''Click to enter Ecobee Credentials'''=Kliknite da biste uneli Ecobee akreditive -'''Login'''=Login (Prijava) -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Kucnite ispod da biste se prijavili na uslugu ecobee i odobrili pristup aplikaciji SmartThings. Obavezno listajte nadole do stranice broj 2 i pritisnite dugme „Allow” (Dozvoli). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Izaberite termostate -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Kucnite ispod da biste videli listu dostupnih ecobee termostata na svom ecobee nalogu i izaberite one koje želite da povežete na SmartThings. -'''Tap to choose'''=Kucnite da biste odabrali -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Kucnite ispod da biste videli listu dostupnih senzora na svom ecobee nalogu i izaberite one koje želite da povežete na SmartThings. -'''Tap to choose'''=Kucnite da biste odabrali -'''Select Ecobee Sensors ({{numFound}} found)'''=Izaberite Ecobee senzore ({{numFound}} pronađeno) -'''Your ecobee Account is now connected to SmartThings!'''=Vaš ecobee nalog je sada povezan na SmartThings! -'''Click 'Done' to finish setup.'''=Kliknite na „Done” (Gotovo) za kraj konfiguracije. -'''The connection could not be established!'''=Veza nije uspostavljena! -'''Click 'Done' to return to the menu.'''=Kliknite na „Done” (Gotovo) da biste se vratili na meni. -'''is connected to SmartThings'''=je povezan na SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=je prekinuo vezu sa aplikacijom SmartThings zato što su akreditivi za pristup promenjeni ili izgubljeni. Idite na aplikaciju Ecobee (Connect) SmartApp i ponovo unesite akreditive za prijavljivanje na nalog. -'''Your Ecobee thermostat '''=Vaš Ecobee termostat -'''Select your ecobee devices'''=Izaberite ecobee uređaje -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Kucnite ispod da biste dodali ili uklonili dostupne termostate na ecobee nalogu. Izabrani termostati će se povezati na SmartThings. -'''Log In'''=Prijavljivanje -'''Tap Next to continue to set up your ecobee thermostats.'''=Kucnite na Dalje da biste nastavili konfiguraciju ecobee termostata. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Kucnite ispod da biste dodali ili uklonili dostupne daljinske termostate na ecobee nalogu. Izabrani senzori će se povezati na SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Kucnite ispod da biste dodali ili uklonili dostupne prekidače na ecobee nalogu. Izabrani prekidači će se povezati na SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties b/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties deleted file mode 100644 index ea957914439..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/sv-SE.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Anslut din Ecobee-termostat till SmartThings. -'''ecobee'''=ecobee -'''You are connected.'''=Du är ansluten. -'''Click to enter Ecobee Credentials'''=Klicka för att ange dina Ecobee-inloggningsuppgifter -'''Login'''=Logga in -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Tryck nedan för att logga in på ecobee-tjänsten och ge SmartThings åtkomst. Rulla ned till sidan 2 och tryck på knappen Allow (Tillåt). -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Välj dina termostater -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tryck nedan om du vill se listan med ecobee-termostater som är tillgängliga i ditt ecobee-konto och välj dem du vill ansluta till SmartThings. -'''Tap to choose'''=Tryck för att välja -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Tryck nedan om du vill se listan med ecobee-givare som är tillgängliga i ditt ecobee-konto och välj dem du vill ansluta till SmartThings. -'''Tap to choose'''=Tryck för att välja -'''Select Ecobee Sensors ({{numFound}} found)'''=Välj Ecobee-givare ({{numFound}} hittades) -'''Your ecobee Account is now connected to SmartThings!'''=Ditt ecobee-konto är nu anslutet till SmartThings! -'''Click 'Done' to finish setup.'''=Klicka på Done (Klart) för att slutföra konfigurationen. -'''The connection could not be established!'''=Det gick inte att upprätta anslutningen! -'''Click 'Done' to return to the menu.'''=Klicka på Done (Klart) för att återgå till menyn. -'''is connected to SmartThings'''=är ansluten till SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=är frånkopplad från SmartThings, eftersom inloggningsuppgifterna har ändrats eller gått förlorade. Starta Ecobee (Connect) SmartApp och ange kontots inloggningsuppgifter igen. -'''Your Ecobee thermostat '''=Din Ecobee-termostat -'''Select your ecobee devices'''=Välj dina ecobee-enheter -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=Tryck nedan om du vill lägga till eller ta bort termostater som är tillgängliga i ditt ecobee-konto. De valda termostaterna ansluter till SmartThings. -'''Log In'''=Logga in -'''Tap Next to continue to set up your ecobee thermostats.'''=Fortsätt ställa in ecobee-termostaterna genom att trycka på Next (Nästa). -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=Tryck nedan om du vill lägga till eller ta bort fjärrsensorer som är tillgängliga i ditt ecobee-konto. De valda sensorerna ansluter till SmartThings. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=Tryck nedan om du vill lägga till eller ta bort strömbrytare som är tillgängliga i ditt ecobee-konto. De valda strömbrytarna ansluter till SmartThings. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties b/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties deleted file mode 100644 index 57a2f36f35b..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/th-TH.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=เชื่อมต่อตัวควบคุมอุณหภูมิ Ecobee ของคุณเข้ากับ SmartThings -'''ecobee'''=Ecobee -'''You are connected.'''=คุณได้เชื่อมต่อแล้ว -'''Click to enter Ecobee Credentials'''=คลิกเพื่อใส่ ข้อมูลยืนยันตัวตน Ecobee -'''Login'''=เข้าสู่ระบบ -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=แตะด้านล่างเพื่อเข้าสู่บริการ Ecobee และอนุญาตการเข้าถึงของ SmartThings ดูให้แน่ใจว่าได้เลื่อนลงมาที่หน้า 2 แล้วกดปุ่ม 'อนุญาต' -'''ecobee'''=Ecobee -'''Select Your Thermostats'''=เลือกตัวควบคุมอุณหภูมิของคุณ -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=แตะที่ด้านล่างเพื่อดูรายการตัวควบคุมอุณหภูมิ Ecobee ที่มีอยู่ในบัญชีผู้ใช้ Ecobee ของคุณ และเลือกตัวควบคุมอุณหภูมิที่คุณต้องการจะเชื่อมต่อกับ SmartThings -'''Tap to choose'''=แตะเพื่อเลือก -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=แตะที่ด้านล่างเพื่อดูรายการเซ็นเซอร์ Ecobee ที่มีอยู่ในบัญชีผู้ใช้ Ecobee ของคุณ และเลือกเซ็นเซอร์ที่คุณต้องการจะเชื่อมต่อกับ SmartThings -'''Tap to choose'''=แตะเพื่อเลือก -'''Select Ecobee Sensors ({{numFound}} found)'''=เลือกเซ็นเซอร์ Ecobee ({{numFound}} found) -'''Your ecobee Account is now connected to SmartThings!'''=ตอนนี้บัญชีผู้ใช้ Ecobee ของคุณเชื่อมต่อกับ SmartThings แล้ว -'''Click 'Done' to finish setup.'''=คลิก 'เสร็จสิ้น' เพื่อทำการตั้งค่าให้เสร็จสิ้น -'''The connection could not be established!'''=ไม่สามารถสร้างการเชื่อมต่อได้! -'''Click 'Done' to return to the menu.'''=คลิก 'เสร็จสิ้น' เพื่อกลับไปยังเมนู -'''is connected to SmartThings'''={{deviceName}} เชื่อมต่อกับ SmartThings แล้ว -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''={{deviceName}} ถูกตัดการเชื่อมต่อจาก SmartThings เนื่องจากข้อมูลการเข้าถึงถูกเปลี่ยนแปลงหรือหายไป กรุณาไปที่ Ecobee (การเชื่อมต่อ) SmartApp และใส่ข้อมูลยืนยันตัวตนการเข้าสู่บัญชีผู้ใช้ของคุณอีกครั้ง -'''Your Ecobee thermostat '''=ตัวควบคุมอุณหภูมิ Ecobee ของคุณ -'''Select your ecobee devices'''=เลือกอุปกรณ์ ecobee ของคุณ -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=แตะด้านล่างเพื่อเพิ่มหรือลบตัวควบคุมอุณหภูมิที่พร้อมใช้งานในบัญชี ecobee ของคุณ ตัวควบคุมอุณหภูมิที่เลือกจะเชื่อมต่อกับ SmartThings -'''Log In'''=เข้าสู่ระบบ -'''Tap Next to continue to set up your ecobee thermostats.'''=แตะ ถัดไป เพื่อดำเนินการตั้งค่าตัวควบคุมอุณหภูมิ ecobee ต่อ -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=แตะด้านล่างเพื่อเพิ่มหรือลบเซ็นเซอร์ระยะไกลที่พร้อมใช้งานในบัญชี ecobee ของคุณ เซ็นเซอร์ที่เลือกจะเชื่อมต่อกับ SmartThings -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=แตะด้านล่างเพื่อเพิ่มหรือลบสวิตช์ที่พร้อมใช้งานในบัญชี ecobee ของคุณ สวิตช์ที่เลือกจะเชื่อมต่อกับ SmartThings diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties b/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties deleted file mode 100644 index 59f4e905ff3..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/tr-TR.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=Ecobee termostatınızı SmartThings'e bağlayın. -'''ecobee'''=ecobee -'''You are connected.'''=Bağlantı kurdunuz. -'''Click to enter Ecobee Credentials'''=Ecobee Kimlik Bilgilerinizi girmek için tıklayın -'''Login'''=Oturum aç -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=Ecobee servisinde oturum açmak ve SmartThings erişimine izin vermek için aşağıya dokunun. Ekranı 2. sayfaya kaydırdığınızdan emin olun ve 'İzin Ver' tuşuna basın. -'''ecobee'''=ecobee -'''Select Your Thermostats'''=Termostatlarınızı Seçin -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=Ecobee hesabınızda mevcut olan ecobee termostatlarının listesini görüntülemek için aşağıya dokunun ve SmartThings'e bağlamak istediklerinizi seçin. -'''Tap to choose'''= seçmek için dokunun -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=Ecobee hesabınızda mevcut olan ecobee sensörlerinin listesini görüntülemek için aşağıya dokunun ve SmartThings'e bağlamak istediklerinizi seçin. -'''Tap to choose'''= seçmek için dokunun -'''Select Ecobee Sensors ({{numFound}} found)'''=Ecobee Sensörlerini seçin ({{numFound}} bulundu) -'''Your ecobee Account is now connected to SmartThings!'''=Ecobee Hesabınız artık SmartThings'e bağlandı! -'''Click 'Done' to finish setup.'''=Kurulumu bitirmek için 'Bitti' öğesine tıklayın. -'''The connection could not be established!'''=Bağlantı kurulamadı! -'''Click 'Done' to return to the menu.'''=Menüye dönmek için 'Bitti' öğesine tıklayın. -'''is connected to SmartThings'''={{cihazİsmi}} SmartThings'e bağlandı -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=Erişim kimlik doğruları değiştirildiğinden veya kaybolduğundan {{cihazİsmi}} ile SmartThings arasındaki bağlantı kesildi. Lütfen Ecobee (Connect) SmartApp'e gidin ve hesabınızın oturum açma kimlik bilgilerini tekrar girin. -'''Your Ecobee thermostat '''=Ecobee termostatınız -'''Select your ecobee devices'''=ecobee Cihazlarınızı seçin -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=ecobee hesabınızdaki kullanılabilir termostatları eklemek veya kaldırmak için aşağıya dokunun. Seçilen termostatlar SmartThings'e bağlanır. -'''Log In'''=Oturum Açın -'''Tap Next to continue to set up your ecobee thermostats.'''=ecobee termostatlarınızı kurmaya devam etmek için İleri ögesine dokunun. -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=ecobee hesabınızdaki kullanılabilir sensörleri eklemek veya kaldırmak için aşağıya dokunun. Seçilen sensörler SmartThings'e bağlanır. -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=ecobee hesabınızdaki kullanılabilir anahtarları eklemek veya kaldırmak için aşağıya dokunun. Seçilen anahtarlar SmartThings'e bağlanır. diff --git a/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties b/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties deleted file mode 100644 index a927eb82b41..00000000000 --- a/smartapps/smartthings/ecobee-connect.src/i18n/zh-CN.properties +++ /dev/null @@ -1,26 +0,0 @@ -'''Connect your Ecobee thermostat to SmartThings.'''=将 Ecobee 恒温器连接至 SmartThings。 -'''ecobee'''=ecobee -'''You are connected.'''=已连接。 -'''Click to enter Ecobee Credentials'''=点击以输入 Ecobee 凭据 -'''Login'''=登录 -'''Tap below to log in to the ecobee service and authorize SmartThings access. Be sure to scroll down on page 2 and press the 'Allow' button.'''=点击下方以登录 ecobee 服务并授予 SmartThings 访问权限。务必在第 2 页上向下滚动,然后按下“允许”按钮。 -'''ecobee'''=ecobee -'''Select Your Thermostats'''=选择恒温器 -'''Tap below to see the list of ecobee thermostats available in your ecobee account and select the ones you want to connect to SmartThings.'''=点击下方以查看 ecobee 帐户中可用 ecobee 恒温器的列表,然后选择要连接至 SmartThings 的恒温器。 -'''Tap to choose'''=点击以选择 -'''Tap below to see the list of ecobee sensors available in your ecobee account and select the ones you want to connect to SmartThings.'''=点击下方以查看 ecobee 帐户中可用 ecobee 传感器的列表,然后选择要连接至 SmartThings 的传感器。 -'''Tap to choose'''=点击以选择 -'''Select Ecobee Sensors ({{numFound}} found)'''=选择 Ecobee 传感器 (发现 {{numFound}} 个) -'''Your ecobee Account is now connected to SmartThings!'''=ecobee 帐户现在已连接至 SmartThings! -'''Click 'Done' to finish setup.'''=单击“完成”以完成设置。 -'''The connection could not be established!'''=无法建立连接! -'''Click 'Done' to return to the menu.'''=单击“完成”返回菜单。 -'''is connected to SmartThings'''=已连接至 SmartThings -'''is disconnected from SmartThings, because the access credential changed or was lost. Please go to the Ecobee (Connect) Linked Service and re-enter your account login credentials.'''=已从 SmartThings 断开,因为访问凭据已更改或丢失。请转到 Ecobee (连接) SmartApp,然后重新输入您的帐户登录凭据。 -'''Your Ecobee thermostat '''=您的 Ecobee 恒温器 -'''Select your ecobee devices'''=选择您的 ecobee 设备 -'''Tap below to add or remove thermostats available in your ecobee account. Selected thermostats will connect to SmartThings.'''=点击下方可添加或删除 ecobee 帐户中的恒温器。选定的恒温器将连接到 SmartThings。 -'''Log In'''=登录 -'''Tap Next to continue to set up your ecobee thermostats.'''=点击下一步继续设置您的 ecobee 恒温器。 -'''Tap below to add or remove remote sensors available in your ecobee account. Selected sensors will connect to SmartThings.'''=点击下方可添加或删除 ecobee 帐户中可用的远程传感器。选定的传感器将连接到 SmartThings。 -'''Tap below to add or remove switches available in your ecobee account. Selected switches will connect to SmartThings.'''=点击下方添加或删除您的 ecobee 帐户中可用的开关。选定的开关将连接到 SmartThings。 From fa029dee992088e2c5430c970e8df5cd9201445d Mon Sep 17 00:00:00 2001 From: Focalcrest-Madi <58587489+Focalcrest-Madi@users.noreply.github.com> Date: Mon, 6 Dec 2021 17:43:07 +0800 Subject: [PATCH 327/422] DevWs for Focalcrest containing containing ZigBee Switch (#76710) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for Focalcrest containing containing ZigBee Switch * Fix: replace 4 spaces to 1 tab * Fix: indentations(replace 4 spaces to 1 tab) * Fix:indentations Line 125 (replace 4 spaces to 1 tab) --- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 497833cad67..765ed7cb010 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -117,9 +117,11 @@ metadata { fingerprint manufacturer: "Jasco Products", model: "43102", deviceJoinName: "Enbrighten Outlet", ocfDeviceType: "oic.d.smartplug" //Enbrighten, In-Wall Smart Outlet 43102, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 fingerprint manufacturer: "Jasco Products", model: "43076", deviceJoinName: "Enbrighten Switch" //Enbrighten, In-Wall Smart Switch 43076, Raw Description: 01 0104 0100 00 06 0000 0003 0004 0005 0006 0B05 02 000A 0019 - // Focalcrest + // Focalcrest/Evvr fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "Focalcrest", model: "SRB01", deviceJoinName: "Focalcrest Switch" // In-Wall Relay Switch - + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "EVVR", model: "SRB01A", deviceJoinName: "Evvr Switch" // Evvr IRS + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "EVVR", model: "SRB02A", deviceJoinName: "Evvr Switch" // Evvr IRS Lite + // SiHAS Switch fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z1", deviceJoinName: "SiHAS Switch" } From a4aa7b862c70558e70ba885489fd1918cb97f3bc Mon Sep 17 00:00:00 2001 From: Sarkis008 <92102906+Sarkis008@users.noreply.github.com> Date: Tue, 7 Dec 2021 00:59:35 +0400 Subject: [PATCH 328/422] DevWs for HELTUN containing containing HE-HLS01 Handler 'Binary Switch' (#76116) * DevWs for HELTUN containing containing HE-HLS01 Handler 'Binary Switch' * Changed "polling" capability to "Health Check" Minor changes * Spacing changes * Removed device model * placed comments // right after the code * Defined each device setting explicitly some minor improvements * Formmating * Minor Improvement * Reformatting remove HubActions * Formmating * fixed a type in parameters description * changed inClusters as requested * fixed spacing * fixed formatting Co-authored-by: Artur Sargsyan --- .../heltun-hls01-switch.groovy | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 devicetypes/heltun/heltun-hls01-switch.src/heltun-hls01-switch.groovy diff --git a/devicetypes/heltun/heltun-hls01-switch.src/heltun-hls01-switch.groovy b/devicetypes/heltun/heltun-hls01-switch.src/heltun-hls01-switch.groovy new file mode 100644 index 00000000000..821ecf413b6 --- /dev/null +++ b/devicetypes/heltun/heltun-hls01-switch.src/heltun-hls01-switch.groovy @@ -0,0 +1,310 @@ +/** + * Copyright 2021 Sarkis Kabrailian + * + * 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: "HELTUN HLS01 Switch", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true) { + capability "Energy Meter" + capability "Power Meter" + capability "Switch" + capability "Temperature Measurement" + capability "Voltage Measurement" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0344", prod: "0004", inClusters:"0x25", deviceJoinName: "HELTUN Switch" //model: "000A" + } + preferences { + input ( + title: "HE-HLS01 | HELTUN High Load Switch", + description: "The user manual document with all technical information is available in support.heltun.com page. In case of technical questions please contact HELTUN Support Team at support@heltun.com", + type: "paragraph", + element: "paragraph" + ) + parameterMap().each { + input ( + title: "${it.title}", + description: it.description, + type: "paragraph", + element: "paragraph" + ) + def unit = it.unit ? it.unit : "" + def defV = it.default as Integer + def defVDescr = it.options ? it.options.get(defV) : "${defV}${unit} - Default Value" + input ( + name: it.name, + title: null, + description: "$defVDescr", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.default, + required: false + ) + } + } +} + +def initialize() { + runIn(3, "checkParam") +} + +def parse(String description) { + def result = null + def cmd = zwave.parse(description) + if (cmd) {result = zwaveEvent(cmd)} + return result +} + +def updated() { + initialize() +} + +def checkParam() { + boolean needConfig = false + parameterMap().each { + if (state."$it.name" == null || state."$it.name".state == "defNotConfigured") { + state."$it.name" = [value: it.default as Integer, state: "defNotConfigured"] + needConfig = true + } + if (settings."$it.name" != null && (state."$it.name".value != settings."$it.name" as Integer || state."$it.name".state == "notConfigured")) { + state."$it.name".value = settings."$it.name" as Integer + state."$it.name".state = "notConfigured" + needConfig = true + } + } + if ( needConfig ) { + configParam() + } +} + +private configParam() { + def cmds = [] + for (parameter in parameterMap()) { + if ( state."$parameter.name"?.value != null && state."$parameter.name"?.state in ["notConfigured", "defNotConfigured"] ) { + cmds << zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$parameter.name".value, parameterNumber: parameter.paramNum, size: parameter.size).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: parameter.paramNum).format() + break + } + } + if (cmds) { + runIn(5, "checkParam") + sendHubCommand(cmds,500) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def parameter = parameterMap().find( {it.paramNum == cmd.parameterNumber } ).name + if (state."$parameter".value == cmd.scaledConfigurationValue) { + state."$parameter".state = "configured" + } + else { + state."$parameter".state = "error" + } + configParam() +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def locaScale = getTemperatureScale() //HubScale + def externalTemp = 1 + def map = [:] + if (externalTemp == cmd.sensorType) { + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def deviceTemp = cmd.scaledSensorValue + def scaledTemp = (deviceScale == locaScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + map.name = "temperature" + map.value = scaledTemp + map.unit = locaScale + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + map.name = "energy" + map.value = cmd.scaledMeterValue + map.unit = "kWh" + sendEvent(map) + } else if (cmd.scale == 2) { + map.name = "power" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "W" + sendEvent(map) + } else if (cmd.scale == 4) { + map.name = "voltage" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "V" + sendEvent(map) + } else if (cmd.scale == 5) { + map.name = "current" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "A" + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.clockv1.ClockReport cmd) { + def currDate = Calendar.getInstance(location.timeZone) + def time = [hour: currDate.get(Calendar.HOUR_OF_DAY), minute: currDate.get(Calendar.MINUTE), weekday: currDate.get(Calendar.DAY_OF_WEEK)] + if ((time.hour != cmd.hour) || (time.minute != cmd.minute) || (time.weekday != cmd.weekday)){ + sendHubCommand(zwave.clockV1.clockSet(time).format()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + def state = cmd.value ? "on" : "off" + sendEvent(name: "switch", value: state) +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) { + def cmds = [] + if (cmd.groupingIdentifier == 1) { + if (cmd.nodeId != [zwaveHubNodeId]) { + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1).format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId).format() + } + } + if (cmds) { + sendHubCommand(cmds, 1200) + } +} + +def roundC (tempInC) { + return (Math.round(tempInC.toDouble() * 2))/2 +} + +def refresh() { + def cmds = [] + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() //get Temperature + cmds << zwave.meterV3.meterGet(scale: 0).format() //get kWh + cmds << zwave.meterV3.meterGet(scale: 2).format() //get Watts + cmds << zwave.meterV3.meterGet(scale: 4).format() //get Voltage + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1).format() //get channel association + sendHubCommand(cmds, 1200) + runIn(10, "checkParam") +} + +def ping() { + refresh() +} + +def resetEnergyMeter() { + sendHubCommand(zwave.meterV3.meterReset().format()) +} + +def on() { + delayBetween([ + zwave.basicV1.basicSet(value: 0xFF).format(), + zwave.switchBinaryV1.switchBinaryGet().format() + ]) +} + +def off() { + delayBetween([ + zwave.basicV1.basicSet(value: 0x00).format(), + zwave.switchBinaryV1.switchBinaryGet().format() + ]) +} + +def configure() { + ping() +} + +private parameterMap() {[ +[title: "Relay Output Mode", description: "This Parameter determines the type of load connected to the device relay output. The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 7, size: 1, default: "0", type: "enum"], + +[title: "Floor Sensor Resistance", description: "If an external floor NTC temperature sensor is used it is necessary to select the correct resistance value in kiloOhms (kΩ) of the sensor", + name: "Selected Floor Resistance in kΩ", paramNum: 10, size: 1, default: 10, type: "number", min: 1, max: 100, unit: "kΩ"], + +[title: "Temperature Sensor Calibration", description: "This Parameter defines the offset value for floor temperature. This value will be added or subtracted from the floor temperature sensor reading.Through the Z-Wave network the value of this Parameter should be x10, e.g. for 1.5°C set the value 15.", + name: "Selected Temperature Offset in °Cx10", paramNum: 17, size: 1, default: 0, type: "number", min: -100, max: 100, unit: " °Cx10"], + +[title: "Auto On/Off", description: "If this function is enabled the device will switch Off the relay output when there is no consumption and switch On the output again when the load is reconnected. It is possible to set a delay for Auto Off and Auto On functions in configurations (Auto Off Timeout) & (Auto On Reconnect Timeout) below", + name: "Selected Mode", options: [ + 0: "Auto On/Off Disabled", + 1: "Auto On/Off Enabled" + ], paramNum: 23, size: 1, default: "0", type: "enum"], + +[title: "Auto Off Timeout", description: "If Auto On/Off is enabled, it is possible to delay the Auto Off function. The output will be switched Off when there is no consumption for the interval defined in minutes", + name: "Seleced Auto Off Timeout in minutes", paramNum: 24, size: 1, default: 0, type: "number", min: 0, max: 120, unit: "min"], + +[title: "Auto On Reconnect Timeout", description: "If Auto On/Off is enabled, it is possible to delay the Auto On function. When the load is reconnected the relay output will be switched On after the time defined in minutes", + name: "Seleced Auto On Reconnect Timeout in minutes", paramNum: 25, size: 1, default: 5, type: "number", min: 0, max: 120, unit: "min"], + +[title: "High Load Timeout Protection: Power Threshold", description: "If the HLS01 is used to control an electric socket, you can configure the device so that it automatically switch Off the socket if the potentially dangerous high load is connected longer than allowable time set below (High Load Timeout Protection: Time Threshold). Set the threshold value in watts, reaching which the connected load will be considered high The value of this parameter can be set from 100 to 3500 in watts. Use the value 0 if there is a need to disable this function.", + name: "Selected Power Threshold in watts", paramNum: 26, size: 2, default: 0, type: "number", min: 0 , max: 3500, unit: "W"], + +[title: "High Load Timeout Protection: Time Threshold", description: "If High Load Timeout Protection is activated: Power Threshold is enabled, use this parameter to set the threshold value in minutes. If the load is connected longer than this value, the device will automatically switch Off the socket. Use the value 0 if there is a need to disable this function.", + name: "Selected Time Threshold in minutes", paramNum: 27, size: 2, default: 0, type: "number", min: 0 , max: 1440, unit: "min"], + +[title: "External Input: Hold Control Mode", description: "This Parameter defines how the relay should react while holding the button connected to the external input. The options are: Hold is disabled, Operate like click, Momentary Switch: When the button is held, the relay output state is ON, as soon as the button is released the relay output state changes to OFF, Reversed Momentary: When the button is held, the relay output state is OFF, as soon as the button is released the relay output state changes to ON.", + name: "Selected Hold Control Mode", options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary" + ], paramNum: 41, size: 1, default: "2", type: "enum"], + +[title: "Hold Mode Duration for External Input S1", description: "This parameter specifies the time the device needs to recognize a hold mode when the button connected to an external input is held (key closed). This parameter is available on firmware V1.3 or higher", + name: "Selected Duration in milliseconds", paramNum: 46, size: 2, default: 500, type: "number", min: 200 , max: 5000, unit: "ms"], + +[title: "External Input: Click Control Mode", description: "This Parameter defines how the relay should react when clicking the button connected to the external input. The options are: Click is disabled, Toggle switch: relay inverts state (ON to OFF, OFF to ON), Only On: Relay switches to ON state only, Only Off: Relay switches to OFF state only, Timer: On > Off: Relay output switches to ON state (contacts are closed) then after a specified time switches back to OFF state (contacts are open). The time is specified in 'Relay Timer Mode Duration' below, Timer: Off > On: Relay output switches to OFF state (contacts are open) then after a specified time switches back to On state (contacts are closed). The time is specified in 'Relay Timer Mode Duration' below ", + name: "Selected Click Control Mode", options: [ + 0: "Click is disabled", + 1: "Toggle Switch", + 2: "Only On", + 3: "Only Off", + 4: "Timer: On > Off", + 5: "Timer: Off > On" + ], paramNum: 51, size: 1, default: "1", type: "enum"], + +[title: "Relay Timer Mode Duration", description: "This parameters specify the duration in seconds for the Timer modes for Click Control Mode above. Press the button and the relay output goes to ON/OFF for the specified time then changes back to OFF/ON. If the value is set to “0” the relay output will operate as a short contact (duration is about 0.5 sec)", + name: "Selected Timer Mode Duration in seconds", paramNum: 71, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s"], + +[title: "Retore Relay State", description: "This parameter determines if the last relay state should be restored after power failure or not. This parameter is available on firmware V1.5 or higher", + name: "Selected Mode", options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 66, size: 1, default: "0", type: "enum"], + +[title: "Energy Consumption Meter Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends reports from its energy consumption sensor even if there is no change in the value. This parameter defines the interval between consecutive reports of real time and cumulative energy consumption data to the gateway", + name: "Selected Energy Report Interval in minutes", paramNum: 141, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "Energy Consumption Meter Report", description: "This Parameter determines the change in the load power resulting in the consumption report being sent to the gateway. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Change Percentage", paramNum: 142, size: 1, default: 25, type: "number", min: 0 , max: 50, unit: "%"], + +[title: "Sensors Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends to the gateway reports from its external NTC temperature sensor even if there are not changes in the values. This Parameter defines the interval between consecutive reports", + name: "Selected Energy Report Interval in minutes", paramNum: 143, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "External Temperature Sensor Report Threshold", description: "This Parameter determines the change in temperature level resulting in temperature sensors report being sent to the gateway. The value of this Parameter should be x10 for °C, e.g. for 0.4°C use value 4. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Threshold in °Cx10", paramNum: 144, size: 1, default: 2, type: "number", min: 0 , max: 100, unit: " °Cx10"], + +[title: "Overheat Protection", description: "You can define the maximum limit of temperature, reaching which the device will automatically switch Off the load. Use the value 0 if there is a need to disable this function", + name: "Selected Limit in °C", paramNum: 153, size: 2, default: 60, type: "number", min: 0 , max: 120, unit: " °C"], + +[title: "Over-Load Protection", description: "You can define the maximum power in Watt for connected load. The device will automatically switch off the output if the power consumed by the connected load exceeds this limit. Use the value 0 if there is a need to disable this function.", + name: "Selected Limit in Watts", paramNum: 155, size: 2, default: 3500, type: "number", min: 0 , max: 4000, unit: "W"], + +[title: "Over-Voltage Protection", description: "The device constantly monitors the voltage of your electricity network. You can define the maximum voltage of network exceeding which the device will automatically switch off the output. Use the value 0 if there is a need to disable this function.", + name: "Selected Upper Limit in Volts", paramNum: 156, size: 2, default: 260, type: "number", min: 120 , max: 280, unit: "V"], + +[title: "Voltage Drop Protection", description: "You can define the minimum voltage of your electricity network. If the voltage of the network drops bellow the determined level the device will automatically switch off the output. Use the value 0 if there is a need to disable this function.", + name: "Selected Lower Limit in Volts", paramNum: 157, size: 2, default: 90, type: "number", min: 80 , max: 240, unit: "V"] + +]} From 2bd112a74b37995ab80b19c41797981c7f5cbdd1 Mon Sep 17 00:00:00 2001 From: Sarkis008 <92102906+Sarkis008@users.noreply.github.com> Date: Tue, 7 Dec 2021 01:02:04 +0400 Subject: [PATCH 329/422] DevWs for HELTUN containing containing HE-HLS01 Handler (#76161) * DevWs for HELTUN containing containing HE-HLS01 Handler * Removed device model * Formatting * Added parameters Formmating * Minor Improvement * Reformatting remove HubActions Separated getModeMap * Formmating * changed inClusters as requested * fixed formatting Co-authored-by: Artur Sargsyan --- .../heltun-hls01-thermostat.groovy | 441 ++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 devicetypes/heltun/heltun-hls01-thermostat.src/heltun-hls01-thermostat.groovy diff --git a/devicetypes/heltun/heltun-hls01-thermostat.src/heltun-hls01-thermostat.groovy b/devicetypes/heltun/heltun-hls01-thermostat.src/heltun-hls01-thermostat.groovy new file mode 100644 index 00000000000..f6575d6bc3b --- /dev/null +++ b/devicetypes/heltun/heltun-hls01-thermostat.src/heltun-hls01-thermostat.groovy @@ -0,0 +1,441 @@ +/** + * Copyright 2021 Sarkis Kabrailian + * + * 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: "HELTUN HLS01 Thermostat", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true, ocfDeviceType: "oic.d.thermostat") { + capability "Energy Meter" + capability "Power Meter" + capability "Temperature Measurement" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Operating State" + capability "Voltage Measurement" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0344", prod: "0004", inClusters: "0x42,0x40,0x43", deviceJoinName: "HELTUN Thermostat" //model: "000A" + } + preferences { + input ( + title: "HE-HLS01 | HELTUN High Load Switch", + description: "The user manual document with all technical information is available in support.heltun.com page. In case of technical questions please contact HELTUN Support Team at support@heltun.com", + type: "paragraph", + element: "paragraph" + ) + parameterMap().each { + if (it.title != null) { + input ( + title: "${it.title}", + description: it.description, + type: "paragraph", + element: "paragraph" + ) + } + def unit = it.unit ? it.unit : "" + def defV = it.default as Integer + def defVDescr = it.options ? it.options.get(defV) : "${defV}${unit} - Default Value" + input ( + name: it.name, + title: null, + description: "$defVDescr", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.default, + required: false + ) + } + } +} + + +def updated() { + initialize() +} + +def initialize() { + runIn(3, "checkParam") +} + +def parse(String description) { + def cmd = zwave.parse(description) + if (cmd) { + return zwaveEvent(cmd) + } +} + +def checkParam() { + boolean needConfig = false + parameterMap().each { + if (state."$it.name" == null || state."$it.name".state == "defNotConfigured") { + state."$it.name" = [value: it.default as Integer, state: "defNotConfigured"] + needConfig = true + } + if (settings."$it.name" != null && (state."$it.name".value != settings."$it.name" as Integer || state."$it.name".state == "notConfigured")) { + state."$it.name".value = settings."$it.name" as Integer + state."$it.name".state = "notConfigured" + needConfig = true + } + } + if ( needConfig ) { + configParam() + } +} + +private configParam() { + def cmds = [] + for (parameter in parameterMap()) { + if ( state."$parameter.name"?.value != null && state."$parameter.name"?.state in ["notConfigured", "defNotConfigured"] ) { + cmds << zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$parameter.name".value, parameterNumber: parameter.paramNum, size: parameter.size).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: parameter.paramNum).format() + break + } + } + if (cmds) { + runIn(5, "checkParam") + sendHubCommand(cmds,500) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def parameter = parameterMap().find( {it.paramNum == cmd.parameterNumber } ).name + if (state."$parameter".value == cmd.scaledConfigurationValue) { + state."$parameter".state = "configured" + } + else { + state."$parameter".state = "error" + } + configParam() +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) { + def locaScale = getTemperatureScale() //HubScale + def deviceMode = numToModeMap[cmd.mode.toInteger()] + sendEvent(name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes], value: deviceMode) + //if mode is off -> change stepoint value to 0 + if (cmd.mode == 0) { + sendEvent(name: "heatingSetpoint", value: 0, unit: locaScale) + } + sendHubCommand(zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: cmd.mode.toInteger()).format()) //getSetpoint +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def locaScale = getTemperatureScale() //HubScale + def typeTemperature = 1 + def map = [:] + if (typeTemperature == cmd.sensorType) { + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def deviceTemp = cmd.scaledSensorValue + def scaledTemp = (deviceScale == locaScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + map.name = "temperature" + map.value = scaledTemp + map.unit = locaScale + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + map.name = "energy" + map.value = cmd.scaledMeterValue + map.unit = "kWh" + sendEvent(map) + }else if (cmd.scale == 2) { + map.name = "power" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "W" + sendEvent(map) + }else if (cmd.scale == 4) { + map.name = "voltage" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "V" + sendEvent(map) + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd) { + def state = (cmd.operatingState == 1) ? "heating" : "idle" //DeviceScale + sendEvent(name: "thermostatOperatingState", value: state) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd) { + def locaScale = getTemperatureScale() //HubScale + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def deviceTemp = cmd.scaledValue + def setPoint = (deviceScale == locaScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + def mode = modeToNumMap[device.currentValue("thermostatMode")] + if (mode == 0) {setPoint = 0} + sendEvent(name: "heatingSetpoint", value: setPoint, unit: locaScale) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { + def tSupportedModes = [] + if(cmd.heat) { tSupportedModes << "heat" } + if(cmd.autoChangeover) { tSupportedModes << "autochangeover" } + if(cmd.dryAir) { tSupportedModes << "dryair" } + if(cmd.energySaveHeat) { tSupportedModes << "energysaveheat" } + if(cmd.away) { tSupportedModes << "away" } + if(cmd.off) { tSupportedModes << "off" } + state.supportedModes = tSupportedModes + sendEvent(name: "supportedThermostatModes", value: tSupportedModes, displayed: false) +} + +def setHeatingSetpoint(tValue) { + def cmds = [] + def mode = device.currentValue("thermostatMode") + def currentMode = modeToNumMap[mode] + def temp = state.heatingSetpoint = tValue.toDouble() //temp got fromm the app + def tempInC = (getTemperatureScale() == "F" ? roundC(fahrenheitToCelsius(temp)) : temp) //If not C, Convert to C + cmds << zwave.thermostatSetpointV2.thermostatSetpointSet(setpointType: currentMode, scale: 0, precision: 1, scaledValue: tempInC).format() + + // Sync temp, opState, setPoint + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() + cmds << zwave.thermostatOperatingStateV2.thermostatOperatingStateGet().format() + cmds << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: currentMode).format() + sendHubCommand(cmds) +} + +def setThermostatMode(String value) { + def cmds = [] + cmds << zwave.thermostatModeV2.thermostatModeSet(mode: modeToNumMap[value]).format() + cmds << zwave.thermostatModeV2.thermostatModeGet().format() + cmds << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: modeToNumMap[value]).format() + sendHubCommand(cmds) +} + +def getNumToModeMap() { + [ + 0 : "off", + 1 : "heat", + 8 : "dryair", + 10 : "autochangeover", + 11 : "energysaveheat", + 13 : "away" + ] +} + +def getModeToNumMap() { + [ + "off": 0, + "heat": 1, + "dryair": 8, + "autochangeover": 10, + "energysaveheat": 11, + "away": 13 + ] +} + +def roundC (tempInC) { + return (Math.round(tempInC.toDouble() * 2))/2 +} + +def refresh() { + def cmds = [] + cmds << zwave.thermostatModeV2.thermostatModeGet().format() //get thermostatmode + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() //Temperature + cmds << zwave.meterV3.meterGet(scale: 0).format() //get kWh + cmds << zwave.meterV3.meterGet(scale: 2).format() //get Watts + cmds << zwave.meterV3.meterGet(scale: 4).format() //get Voltage + cmds << zwave.thermostatOperatingStateV2.thermostatOperatingStateGet().format() //get Thermostat Operating State + cmds << zwave.clockV1.clockGet().format() //get clock + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1).format() //get channel association + cmds << zwave.thermostatModeV2.thermostatModeSupportedGet().format() //get supported modes + sendHubCommand(cmds, 1200) + runIn(10, "checkParam") +} + +def ping() { + refresh() +} + +def configure() { + ping() +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) { + def cmds = [] + if (cmd.groupingIdentifier == 1) { + if (cmd.nodeId != [zwaveHubNodeId]) { + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1).format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: zwaveHubNodeId).format() + } + } + if (cmds) { + sendHubCommand(cmds, 1200) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.clockv1.ClockReport cmd) { + def currDate = Calendar.getInstance(location.timeZone) + def time = [hour: currDate.get(Calendar.HOUR_OF_DAY), minute: currDate.get(Calendar.MINUTE), weekday: currDate.get(Calendar.DAY_OF_WEEK)] + if ((time.hour != cmd.hour) || (time.minute != cmd.minute) || (time.weekday != cmd.weekday)){ + sendHubCommand(zwave.clockV1.clockSet(time).format()) + } +} + +def resetEnergyMeter() { + sendHubCommand(zwave.meterV3.meterReset().format()) +} + +def off() { + setThermostatMode("off") +} + +def heat() { + setThermostatMode("heat") +} + +def emergencyHeat() { + setThermostatMode("emergencyHeat") +} + +private parameterMap() {[ +[title: "Relay Output Mode", description: "This Parameter determines the type of load connected to the device relay output. The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 7, size: 1, default: "0", type: "enum"], + +[title: "External Input Mode", description: "This parameter defines how the thermostat should react when pressing the button connected to the external input. The options are: Disabled, Toggle Switch: if the external input is shorted (with Sx or Line) the Thermostat switches to the operating mode selected in the External Input Action bellow and switches to OFF mode when the external input is open, Toggle Switch Reverse: Toggle Switch Reverse” mode: if the external input is shorted the Thermostat switches to OFF mode and switches to the operating mode selected in the External Input Action bellow when the input is open, Momentary Switch: each press of button (shorten of input) will consistently change the mode to the operating mode selected in External Input Action bellow", + name: "Selected External Input Mode", options: [ + 0: "Disabled", + 1: "Toggle Switch", + 2: "Toggle Switch Reverse", + 3: "Momentary Switch" + ], paramNum: 8, size: 1, default: "0", type: "enum"], + +[title: "External Input Action", description: "This parameter allows selection of which Operating Mode the HE-HLS01 should revert to when the external input is shorted.", + name: "Selected External Input Action", options: [ + 1: "Heat", + 2: "Auto Cangeover", + 3: "Dry Air", + 4: "Energy Save Heat", + 5: "Away" + ], paramNum: 9, size: 1, default: "1", type: "enum"], + +[title: "Floor Sensor Resistance", description: "If an external floor NTC temperature sensor is used it is necessary to select the correct resistance value in kiloOhms (kΩ) of the sensor", + name: "Selected Floor Resistance in kΩ", paramNum: 10, size: 1, default: 10, type: "number", min: 1, max: 100, unit: "kΩ"], + +[title: "Temperature Sensor Calibration", description: "This Parameter defines the offset value for floor temperature. This value will be added or subtracted from the floor temperature sensor reading.Through the Z-Wave network the value of this Parameter should be x10, e.g. for 1.5°C set the value 15.", + name: "Selected Temperature Offset in °Cx10", paramNum: 17, size: 1, default: 0, type: "number", min: -100, max: 100, unit: " °Cx10"], + +[title: "Temperature Hysteresis", description: "This Parameter defines the hysteresis value for temperature control. The HE-HLS01 will stabilize the temperature with selected hysteresis. For example, if the SET POINT is set for 25°C and HYSTERESIS is set for 0.5°C the HE-HLS01 will change the state to IDLE when the temperature reaches 25.0°C, but it will change the state to HEATING if the temperature drops lower than 24.5°C.The value of this Parameter should be x10 e.g. for 0.5°C set the value 5.", + name: "Selected Hysteresis in °Cx10", paramNum: 18, size: 1, default: 5, type: "number", min: 2, max: 100, unit: " °Cx10"], + +[title: "Dry Mode Timeout", description: "By choosing Dry Mode, the device will increase the temperature to the selected Set Point and keep it for the time specified in this parameter. A time range of 1 to 720 minutes (12 hours) can be set. As the Dry Time passes, the Thermostat will automatically change to the Mode set in the 'Mode to Switch After Dry Mode Operation Complete' configuration bellow.", + name: "Selected Dry Mode Timeout in minutes", paramNum: 25, size: 2, default: 30, type: "number", min: 1, max: 270, unit: "min"], + +[title: "Mode to Switch After Dry Mode Operation Complete", description: "This Parameter indicates the mode that will be set after Dry Time.", + name: "Selected Mode to Switch", options: [ + 1: "Heat", + 2: "Auto Cangeover", + 4: "Energy Save Heat", + 5: "Away", + 6: "Off" + ], paramNum: 26, size: 1, default: "1", type: "enum"], + +[title: "Schedule Time", description: "Use these Parameters to set the Morning, Day, Evening and Night start times manually for the Temperature Schedule. The value of these Parameters has format HHMM, e.g. for 08:00 use value 0800 (time without a colon). From 00:00 to 23:59 can be selected.", + name: "Selected Morning Start Time", paramNum: 41, size: 2, default: 600, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Day Start Time", paramNum: 42, size: 2, default: 900, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Evening Start Time", paramNum: 43, size: 2, default: 1800, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Night Start Time", paramNum: 44, size: 2, default: 2300, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[title: "Schedule Temperature", description: "Use these Parameters to set the temperature for each day Schedule manually. The value of this Parameter should be x10, e.g., for 22.5°C set value 225. From 1°C (value 10) to 110°C (value 1100) can be selected.", + name: "Monday Morning Temperature in °Cx10", paramNum: 45, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Monday Day Temperature in °Cx10", paramNum: 46, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Monday Evening Temperature in °Cx10", paramNum: 47, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Monday Night Temperature in °Cx10", paramNum: 48, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Tuesday Morning Temperature in °Cx10", paramNum: 49, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Tuesday Day Temperature in °Cx10", paramNum: 50, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Tuesday Evening Temperature in °Cx10", paramNum: 51, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Tuesday Night Temperature in °Cx10", paramNum: 52, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Wednesday Morning Temperature in °Cx10", paramNum: 53, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Wednesday Day Temperature in °Cx10", paramNum: 54, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Wednesday Evening Temperature in °Cx10", paramNum: 55, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Wednesday Night Temperature in °Cx10", paramNum: 56, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Thursday Morning Temperature in °Cx10", paramNum: 57, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Thursday Day Temperature in °Cx10", paramNum: 58, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Thursday Evening Temperature in °Cx10", paramNum: 59, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Thursday Night Temperature in °Cx10", paramNum: 60, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Friday Morning Temperature in °Cx10", paramNum: 61, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Friday Day Temperature in °Cx10", paramNum: 62, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Friday Evening Temperature in °Cx10", paramNum: 63, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Friday Night Temperature in °Cx10", paramNum: 64, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Saturday Morning Temperature in °Cx10", paramNum: 65, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Saturday Day Temperature in °Cx10", paramNum: 66, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Saturday Evening Temperature in °Cx10", paramNum: 67, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Saturday Night Temperature in °Cx10", paramNum: 68, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Sunday Morning Temperature in °Cx10", paramNum: 69, size: 2, default: 240, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Sunday Day Temperature in °Cx10", paramNum: 70, size: 2, default: 200, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Sunday Evening Temperature in °Cx10", paramNum: 71, size: 2, default: 230, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[name: "Sunday Night Temperature in °Cx10", paramNum: 72, size: 2, default: 180, type: "number", min: 10, max: 1100, unit: " °Cx10"], + +[title: "Energy Consumption Meter Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends reports from its energy consumption sensor even if there is no change in the value. This parameter defines the interval between consecutive reports of real time and cumulative energy consumption data to the gateway", + name: "Selected Energy Report Interval in minutes", paramNum: 141, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "Energy Consumption Meter Report", description: "This Parameter determines the change in the load power resulting in the consumption report being sent to the gateway. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Change Percentage", paramNum: 142, size: 1, default: 25, type: "number", min: 0 , max: 50, unit: "%"], + +[title: "Sensors Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends to the gateway reports from its external NTC temperature sensor even if there are not changes in the values. This Parameter defines the interval between consecutive reports", + name: "Selected Energy Report Interval in minutes", paramNum: 143, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "External Temperature Sensor Report Threshold", description: "This Parameter determines the change in temperature level resulting in temperature sensors report being sent to the gateway. The value of this Parameter should be x10 for °C, e.g. for 0.4°C use value 4. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Threshold in °Cx10", paramNum: 144, size: 1, default: 2, type: "number", min: 0 , max: 100, unit: " °Cx10"], + +[title: "Overheat Protection", description: "You can define the maximum limit of temperature, reaching which the device will automatically switch Off the load. Use the value 0 if there is a need to disable this function", + name: "Selected Limit in °C", paramNum: 153, size: 2, default: 60, type: "number", min: 0 , max: 120, unit: " °C"], + +[title: "Over-Load Protection", description: "You can define the maximum power in Watt for connected load. The device will automatically switch off the output if the power consumed by the connected load exceeds this limit. Use the value 0 if there is a need to disable this function.", + name: "Selected Limit in Watts", paramNum: 155, size: 2, default: 3500, type: "number", min: 0 , max: 4000, unit: "W"], + +[title: "Over-Voltage Protection", description: "The device constantly monitors the voltage of your electricity network. You can define the maximum voltage of network exceeding which the device will automatically switch off the output. Use the value 0 if there is a need to disable this function.", + name: "Selected Upper Limit in Volts", paramNum: 156, size: 2, default: 260, type: "number", min: 120 , max: 280, unit: "V"], + +[title: "Voltage Drop Protection", description: "You can define the minimum voltage of your electricity network. If the voltage of the network drops bellow the determined level the device will automatically switch off the output. Use the value 0 if there is a need to disable this function.", + name: "Selected Lower Limit in Volts", paramNum: 157, size: 2, default: 90, type: "number", min: 80 , max: 240, unit: "V"], + +]} \ No newline at end of file From d7d342be832359b3272476e348115cd0d515dc9f Mon Sep 17 00:00:00 2001 From: Sarkis008 <92102906+Sarkis008@users.noreply.github.com> Date: Tue, 7 Dec 2021 01:05:53 +0400 Subject: [PATCH 330/422] DevWs for HELTUN containing containing HE-HT01 Handler X (#76617) * DevWs for HELTUN containing containing HE-HT01 Handler X * child device * Reformatting remove HubActions Separated getModeMap * Formmating * changes in parameters description * fixed formatting Co-authored-by: Artur Sargsyan --- .../he-temperature.src/he-temperature.groovy | 27 + .../heltun-ht01-thermostat.groovy | 539 ++++++++++++++++++ 2 files changed, 566 insertions(+) create mode 100644 devicetypes/heltun/he-temperature.src/he-temperature.groovy create mode 100644 devicetypes/heltun/heltun-ht01-thermostat.src/heltun-ht01-thermostat.groovy diff --git a/devicetypes/heltun/he-temperature.src/he-temperature.groovy b/devicetypes/heltun/he-temperature.src/he-temperature.groovy new file mode 100644 index 00000000000..f5e3458ceb4 --- /dev/null +++ b/devicetypes/heltun/he-temperature.src/he-temperature.groovy @@ -0,0 +1,27 @@ +/** + * Copyright 2021 Sarkis Kabrailian + * + * 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: "HE-TEMPERATURE", namespace: "HELTUN", author: "Sarkis Kabrailian", ocfDeviceType: "oic.d.thermostat") { + capability "Temperature Measurement" + } +} + +def ping() { + parent.refresh() +} + +def refresh() { + parent.refresh() +} + diff --git a/devicetypes/heltun/heltun-ht01-thermostat.src/heltun-ht01-thermostat.groovy b/devicetypes/heltun/heltun-ht01-thermostat.src/heltun-ht01-thermostat.groovy new file mode 100644 index 00000000000..141d5b7e8dd --- /dev/null +++ b/devicetypes/heltun/heltun-ht01-thermostat.src/heltun-ht01-thermostat.groovy @@ -0,0 +1,539 @@ +/** + * Copyright 2021 Sarkis Kabrailian + * + * 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: "HELTUN HT01 Thermostat", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true, ocfDeviceType: "oic.d.thermostat", mcdSync: true) { + capability "Energy Meter" + capability "Power Meter" + capability "Relative Humidity Measurement" + capability "Temperature Measurement" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Operating State" + capability "Illuminance Measurement" + capability "Voltage Measurement" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0344", prod: "0004", model: "0001", deviceJoinName: "HELTUN Thermostat" + } + preferences { + input ( + title: "HE-HT01 | HELTUN Heating Thermostat", + description: "The user manual document with all technical information is available in support.heltun.com page. In case of technical questions please contact HELTUN Support Team at support@heltun.com", + type: "paragraph", + element: "paragraph" + ) + parameterMap().each { + if (it.title != null) { + input ( + title: "${it.title}", + description: it.description, + type: "paragraph", + element: "paragraph" + ) + } + def unit = it.unit ? it.unit : "" + def defV = it.default as Integer + def defVDescr = it.options ? it.options.get(defV) : "${defV}${unit} - Default Value" + input ( + name: it.name, + title: null, + description: "$defVDescr", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.default, + required: false + ) + } + } +} + +private channelNumber(String N) { + N.split(":")[-1] as Integer +} + +def installed() { + state.oldLabel = device.label + def childName = "${device.displayName} Floor Temperature" + def existingChildren = getChildDevices() + def floorTemperatureid = "${device.deviceNetworkId}:${1}" + def childExists = (existingChildren.find {child -> child.getDeviceNetworkId() == floorTemperatureid} != NULL) + if (!childExists) { + addChildDevice("HE-TEMPERATURE", floorTemperatureid, device.hubId,[completedSetup: true, label: childName, isComponent: true, componentName: "FloorTemperature", componentLabel: "FloorTemperature"]) + } +} + +def parse(String description) { + def cmd = zwave.parse(description) + if (cmd) { + return zwaveEvent(cmd) + } +} + +def checkParam() { + boolean needConfig = false + parameterMap().each { + if (state."$it.name" == null || state."$it.name".state == "defNotConfigured") { + state."$it.name" = [value: it.default as Integer, state: "defNotConfigured"] + needConfig = true + } + if (settings."$it.name" != null && (state."$it.name".value != settings."$it.name" as Integer || state."$it.name".state == "notConfigured")) { + state."$it.name".value = settings."$it.name" as Integer + state."$it.name".state = "notConfigured" + needConfig = true + } + } + if ( needConfig ) { + configParam() + } +} + +private configParam() { + def cmds = [] + for (parameter in parameterMap()) { + if ( state."$parameter.name"?.value != null && state."$parameter.name"?.state in ["notConfigured", "defNotConfigured"] ) { + cmds << zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$parameter.name".value, parameterNumber: parameter.paramNum, size: parameter.size).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: parameter.paramNum).format() + break + } + } + if (cmds) { + runIn(5, "checkParam") + sendHubCommand(cmds,500) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def parameter = parameterMap().find( {it.paramNum == cmd.parameterNumber } ).name + if (state."$parameter".value == cmd.scaledConfigurationValue){ + state."$parameter".state = "configured" + } + else { + state."$parameter".state = "error" + } + configParam() +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) { + def locaScale = getTemperatureScale() //HubScale + def deviceMode = numToModeMap[cmd.mode.toInteger()] + sendEvent(name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes], value: deviceMode) + //if mode is off -> change stepoint value to 0 + if (cmd.mode == 0) { + sendEvent(name: "heatingSetpoint", value: 0, unit: locaScale) + } + sendHubCommand(zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: cmd.mode.toInteger()).format()) //getSetpoint +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def map = [:] + def floorTemperature = 24 + def roomTemperature = 1 + def himidity = 5 + def illuminance = 3 + def locaScale = getTemperatureScale() //HubScale + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def child = childDevices?.find {channelNumber(it.deviceNetworkId) == 1 } + if (roomTemperature == cmd.sensorType) { + def deviceTemp = cmd.scaledSensorValue + def scaledTemp = (deviceScale == locaScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + map.name = "temperature" + map.value = scaledTemp + map.unit = locaScale + sendEvent(map) + } else if (floorTemperature == cmd.sensorType) { + def deviceTemp = cmd.scaledSensorValue + def scaledTemp = (deviceScale == locaScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + map.name = "temperature" + map.value = scaledTemp + map.unit = locaScale + child?.sendEvent(map) + } else if (himidity == cmd.sensorType) { + map.name = "humidity" + map.value = cmd.scaledSensorValue.toInteger() + map.unit = "%" + sendEvent(map) + } else if (illuminance == cmd.sensorType) { + map.name = "illuminance" + map.value = cmd.scaledSensorValue + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + map.name = "energy" + map.value = cmd.scaledMeterValue + map.unit = "kWh" + } else if (cmd.scale == 2) { + map.name = "power" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "W" + }else if (cmd.scale == 4) { + map.name = "voltage" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "V" + } + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd) { + def state = (cmd.operatingState == 1) ? "heating" : "idle" + sendEvent(name: "thermostatOperatingState", value: state) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd) { + def locaScale = getTemperatureScale() //HubScale + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def deviceTemp = cmd.scaledValue + def setPoint = (deviceScale == locaScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + def mode = modeToNumMap[device.currentValue("thermostatMode")] + if (mode == 0) {setPoint = 0} + sendEvent(name: "heatingSetpoint", value: setPoint, unit: locaScale) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { + def tSupportedModes = [] + if(cmd.heat) { tSupportedModes << "heat" } + if(cmd.autoChangeover) { tSupportedModes << "autochangeover" } + if(cmd.dryAir) { tSupportedModes << "dryair" } + if(cmd.energySaveHeat) { tSupportedModes << "energysaveheat" } + if(cmd.away) { tSupportedModes << "away" } + if(cmd.off) { tSupportedModes << "off" } + state.supportedModes = tSupportedModes + sendEvent(name: "supportedThermostatModes", value: tSupportedModes, displayed: false) +} + +def setHeatingSetpoint(tValue) { + def cmds = [] + def mode = device.currentValue("thermostatMode") + def currentMode = modeToNumMap[mode] + def temp = state.heatingSetpoint = tValue.toDouble() //temp got fromm the app + def tempInC = (getTemperatureScale() == "F" ? roundC(fahrenheitToCelsius(temp)) : temp) //If not C, Convert to C + cmds << zwave.thermostatSetpointV2.thermostatSetpointSet(setpointType: currentMode, scale: 0, precision: 1, scaledValue: tempInC).format() + // Sync temp, opState, setPoint + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() + cmds << zwave.thermostatOperatingStateV2.thermostatOperatingStateGet().format() + cmds << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: currentMode).format() + sendHubCommand(cmds) +} + +def setThermostatMode(String value) { + def cmds = [] + cmds << zwave.thermostatModeV2.thermostatModeSet(mode: modeToNumMap[value]).format() + cmds << zwave.thermostatModeV2.thermostatModeGet().format() + cmds << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: modeToNumMap[value]).format() + sendHubCommand(cmds) +} + +def getNumToModeMap() { + [ + 0 : "off", + 1 : "heat", + 8 : "dryair", + 10 : "autochangeover", + 11 : "energysaveheat", + 13 : "away" + ] +} + +def getModeToNumMap() { + [ + "off": 0, + "heat": 1, + "dryair": 8, + "autochangeover": 10, + "energysaveheat": 11, + "away": 13 + ] +} + +def roundC (tempInC) { + return (Math.round(tempInC.toDouble() * 2))/2 +} + +def updated() { + def childName = "${device.displayName} Floor Temperature" + if (childDevices && device.label != state.oldLabel) { + childDevices.each {it.setLabel(childName)} + state.oldLabel = device.label + } + initialize() +} + +def initialize() { + runIn(3, "checkParam") +} + +def ping() { + refresh() +} + +def refresh() { + def cmds = [] + cmds << zwave.thermostatModeV2.thermostatModeGet().format() //get thermostatmode + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() //roomTemperature + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:24).format() //floorTemperature + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:3).format() //Humidity + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:5).format() //Illuminance + cmds << zwave.meterV3.meterGet(scale: 0).format() //get kWh + cmds << zwave.meterV3.meterGet(scale: 2).format() //get Watts + cmds << zwave.meterV3.meterGet(scale: 4).format() //get Voltage + cmds << zwave.thermostatOperatingStateV2.thermostatOperatingStateGet().format() //get Thermostat Operating State + cmds << zwave.clockV1.clockGet().format() //get Clock + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1).format() //get channel association + cmds << zwave.thermostatModeV2.thermostatModeSupportedGet().format() //get supported modes + sendHubCommand(cmds, 1200) + runIn(15, "checkParam") +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) { + def cmds = [] + if (cmd.groupingIdentifier == 1) { + if (cmd.nodeId != [1]) { + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1).format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: 1).format() + } + } + if (cmds) { + sendHubCommand(cmds, 1200) + } +} + +def configure() { + ping() +} + +def zwaveEvent(physicalgraph.zwave.commands.clockv1.ClockReport cmd) { + def currDate = Calendar.getInstance(location.timeZone) + def time = [hour: currDate.get(Calendar.HOUR_OF_DAY), minute: currDate.get(Calendar.MINUTE), weekday: currDate.get(Calendar.DAY_OF_WEEK)] + if ((time.hour != cmd.hour) || (time.minute != cmd.minute) || (time.weekday != cmd.weekday)){ + sendHubCommand(zwave.clockV1.clockSet(time).format()) + } +} + +def resetEnergyMeter() { + sendHubCommand(zwave.meterV3.meterReset().format()) +} + +def off() { + setThermostatMode("off") +} + +def heat() { + setThermostatMode("heat") +} + +def emergencyHeat() { + setThermostatMode("emergencyHeat") +} + +private parameterMap() {[ +[title: "Display Brightness Control", description: "The HE-HT01 can adjust its display brightness automatically depending on the illumination of the ambient environment and also allows to control it manually.", + name: "Selected Brightness Level", options: [ + 0: "Auto", + 1: "Level 1 (Lowest)", + 2: "Level 2", + 3: "Level 3", + 4: "Level 4", + 5: "Level 5", + 6: "Level 6", + 7: "Level 7", + 8: "Level 8", + 9: "Level 9", + 10: "Level 10 (Highest)" + ], paramNum: 5, size: 1, default: "0", type: "enum"], + +[title: "Touch Sensor Sensitivity Threshold", description: "This Parameter allows to adjust the Touch Buttons Sensitivity. Note: Setting the sensitivity too high can lead to false touch detection. We recommend not changing this Parameter unless there is a special need to do so.", + name: "Selected Touch Sensitivity", options: [ + 1: "Level 1 (Low sensitivity)", + 2: "Level 2", + 3: "Level 3", + 4: "Level 4", + 5: "Level 5", + 6: "Level 6", + 7: "Level 7", + 8: "Level 8", + 9: "Level 9", + 10: "Level 10 (High sensitivity)" + ], paramNum: 6, size: 1, default: "6", type: "enum"], + +[title: "Relay Output Mode", description: "This Parameter determines the type of load connected to the device relay output. The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 7, size: 1, default: "0", type: "enum"], + +[title: "External Input Mode", description: "This parameter defines how the thermostat should react when pressing the button connected to the external input. The options are: Disabled, Toggle Switch: if the external input is shorted (with Sx or Line) the Thermostat switches to the operating mode selected in the External Input Action bellow and switches to OFF mode when the external input is open, Toggle Switch Reverse: Toggle Switch Reverse” mode: if the external input is shorted the Thermostat switches to OFF mode and switches to the operating mode selected in the External Input Action bellow when the input is open, Momentary Switch: each press of button (shorten of input) will consistently change the mode to the operating mode selected in External Input Action bellow", + name: "Selected External Input Mode", options: [ + 0: "Disabled", + 1: "Toggle Switch", + 2: "Toggle Switch Reverse", + 3: "Momentary Switch" + ], paramNum: 8, size: 1, default: "0", type: "enum"], + +[title: "External Input Action", description: "This parameter allows selection of which Operating Mode the HE-HT01 should revert to when the external input is shorted.", + name: "Selected External Input Action", options: [ + 1: "Heat", + 2: "Auto Cangeover", + 3: "Dry Air", + 4: "Energy Save Heat", + 5: "Away", + 6: "Off" + ], paramNum: 9, size: 1, default: "6", type: "enum"], + +[title: "Source Sensor", description: "1) A – Air sensor: Regulation (heating control) is based on the SET POINT applied to the internal room air temperature sensor. 2) AF – Air sensor plus floor sensor: Regulation is based on SET POINT applied to the internal room temperature sensor but also controlled by the floor temperature sensor ensuring that the floor temperature remains within the floor temperature limits specified bellow. 3) F – Floor sensor: Regulation is based on the SET POINT applied to the external floor temperature sensor. 4) FA – Floor sensor plus air sensor: Regulation is based on SET POINT applied to the external floor sensor but is also controlled by the internal air temperature sensor ensuring that the air temperature remains within the air temperature limits specified bellow. 5) t – Time regulator: Regulation is based on the time settings for heating which will be ON during the (ON time) and OFF during the (OFF Time) specified in the configurations bellow. This cycle will be repeated constantly. 6) tA – Time regulator + Air sensor: Regulation is based on the ON & OFF times specified in the configurations bellow but also controlled by the internal air temperature sensor ensuring that the room temperature remains within the air temperature limits specified bellow. 7) tF – Time regulator + Floor sensor Parameters: Regulation is based on the ON & OFF times specified in the configurations bellow but also controlled by the floor temperature sensor ensuring that the floor temperature remains within the floor temperature limits specified bellow.", + name: "Selected Source Sensor", options: [ + 1: "Air Sensor", + 2: "Air + Floor Sensors", + 3: "Floor Sensor", + 4: "Floor + Air Sensors", + 5: "Time Regulator", + 6: "Time + Air Sensor", + 7: "Time + Floor Sensor" + ], paramNum: 11, size: 1, default: "3", type: "enum"], + +[name: "Air Temperature Minimum in °Cx10", paramNum: 12, size: 2, default: 210, type: "number", min: 10, max: 360, unit: " °Cx10"], + +[name: "Air Temperature Maximum in °Cx10", paramNum: 13, size: 2, default: 270, type: "number", min: 20, max: 370, unit: " °Cx10"], + +[name: "Floor Temperature Minimum in °Cx10", paramNum: 14, size: 2, default: 180, type: "number", min: 10, max: 360, unit: " °Cx10"], + +[name: "Floor Temperature Maximum in °Cx10", paramNum: 15, size: 2, default: 320, type: "number", min: 20, max: 370, unit: " °Cx10"], + +[name: "Time Regulation ON Time in minutes", paramNum: 23, size: 2, default: 30, type: "number", min: 10, max: 240, unit: "min"], + +[name: "Time Regulation OFF Time in minutes", paramNum: 24, size: 2, default: 30, type: "number", min: 20, max: 240, unit: "min"], + +[title: "Floor Sensor Resistance", description: "If an external floor NTC temperature sensor is used it is necessary to select the correct resistance value in kiloOhms (kΩ) of the sensor", + name: "Selected Floor Resistance in kΩ", paramNum: 10, size: 1, default: 10, type: "number", min: 1, max: 100, unit: "kΩ"], + +[title: "Floor Temperature Calibration", description: "This Parameter defines the offset value for floor temperature. This value will be added or subtracted from the floor temperature sensor reading.Through the Z-Wave network the value of this Parameter should be x10, e.g. for 1.5°C set the value 15.", + name: "Selected Temperature Offset in °Cx10", paramNum: 16, size: 1, default: 0, type: "number", min: -100, max: 100, unit: " °Cx10"], + +[title: "Air Temperature Calibration", description: "This Parameter defines the offset value for room air temperature. This value will be added or subtracted from the air temperature sensor reading.Through the Z-Wave network the value of this Parameter should be x10, e.g. for 1.5°C set the value 15.", + name: "Selected Temperature Offset in °Cx10", paramNum: 17, size: 1, default: 0, type: "number", min: -100, max: 100, unit: " °Cx10"], + +[title: "Temperature Hysteresis", description: "This Parameter defines the hysteresis value for temperature control. The HE-HT01 will stabilize the temperature with selected hysteresis. For example, if the SET POINT is set for 25°C and HYSTERESIS is set for 0.5°C the HE-HT01 will change the state to IDLE when the temperature reaches 25.0°C, but it will change the state to HEATING if the temperature drops lower than 24.5°C.The value of this Parameter should be x10 e.g. for 0.5°C set the value 5.", + name: "Selected Hysteresis in °Cx10", paramNum: 18, size: 1, default: 5, type: "number", min: 2, max: 100, unit: " °Cx10"], + +[title: "Dry Time", description: "By choosing Dry Mode, the device will increase the temperature to the selected Set Point and keep it for the time specified in this parameter. A time range of 1 to 720 minutes (12 hours) can be set. As the Dry Time passes, the Thermostat will automatically change to the Mode set in the 'Mode to Switch After Dry Mode Operation Complete' configuration bellow.", + name: "Selected Dry Time in minutes", paramNum: 25, size: 2, default: 30, type: "number", min: 5, max: 90, unit: "min"], + +[title: "Mode to Switch After Dry Mode Operation Complete", description: "This Parameter indicates the mode that will be set after Dry Time.", + name: "Selected Mode to Switch", options: [ + 1: "Heat", + 2: "Auto Cangeover", + 4: "Energy Save Heat", + 5: "Away", + 6: "Off" + ], paramNum: 26, size: 1, default: "1", type: "enum"], + +[title: "Child Lock Restriction Level", description: "This parameter specifies the restriction level of Child Lock feature where it allows you to choose which touch buttons/features of HE-HT01 should be disabled temporarily while the device is locked. Choosing level 1 will lock all the buttons, choosing level 2 will let you change the setpoint and lock the remaining buttons, choosing level 3 will let you change the setpoint and the operating mode, and lock the remaining buttons. This parameter is available on firmware V2.4 or higher", + name: "Selected Restriction Level", options: [ + 1: "level 1 (Strictest)", + 2: "level 2", + 3: "level 3 (least strict)" + ], paramNum: 40, size: 1, default: "1", type: "enum"], + +[title: "Schedule Time", description: "Use these Parameters to set the Morning, Day, Evening and Night start times manually for the Temperature Schedule. The value of these Parameters has format HHMM, e.g. for 08:00 use value 0800 (time without a colon). From 00:00 to 23:59 can be selected.", + name: "Selected Morning Start Time", paramNum: 41, size: 2, default: 600, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Day Start Time", paramNum: 42, size: 2, default: 900, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Evening Start Time", paramNum: 43, size: 2, default: 1800, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Night Start Time", paramNum: 44, size: 2, default: 2300, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[title: "Schedule Temperature", description: "Use these Parameters to set the temperature for each day Schedule manually. The value of this Parameter should be x10, e.g., for 22.5°C set value 225. From 1°C (value 10) to 110°C (value 1100) can be selected.", + name: "Monday Morning Temperature in °Cx10", paramNum: 45, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Monday Day Temperature in °Cx10", paramNum: 46, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Monday Evening Temperature in °Cx10", paramNum: 47, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Monday Night Temperature in °Cx10", paramNum: 48, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Morning Temperature in °Cx10", paramNum: 49, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Day Temperature in °Cx10", paramNum: 50, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Evening Temperature in °Cx10", paramNum: 51, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Night Temperature in °Cx10", paramNum: 52, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Morning Temperature in °Cx10", paramNum: 53, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Day Temperature in °Cx10", paramNum: 54, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Evening Temperature in °Cx10", paramNum: 55, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Night Temperature in °Cx10", paramNum: 56, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Morning Temperature in °Cx10", paramNum: 57, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Day Temperature in °Cx10", paramNum: 58, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Evening Temperature in °Cx10", paramNum: 59, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Night Temperature in °Cx10", paramNum: 60, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Morning Temperature in °Cx10", paramNum: 61, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Day Temperature in °Cx10", paramNum: 62, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Evening Temperature in °Cx10", paramNum: 63, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Night Temperature in °Cx10", paramNum: 64, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Morning Temperature in °Cx10", paramNum: 65, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Day Temperature in °Cx10", paramNum: 66, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Evening Temperature in °Cx10", paramNum: 67, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Night Temperature in °Cx10", paramNum: 68, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Morning Temperature in °Cx10", paramNum: 69, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Day Temperature in °Cx10", paramNum: 70, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Evening Temperature in °Cx10", paramNum: 71, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Night Temperature in °Cx10", paramNum: 72, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[title: "Energy Consumption Meter Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends reports from its energy consumption sensor even if there is no change in the value. This parameter defines the interval between consecutive reports of real time and cumulative energy consumption data to the gateway", + name: "Selected Energy Report Interval in minutes", paramNum: 141, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "Energy Consumption Meter Report", description: "This Parameter determines the change in the load power resulting in the consumption report being sent to the gateway. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Change Percentage", paramNum: 142, size: 1, default: 25, type: "number", min: 0 , max: 50, unit: "%"], + +[title: "Sensors Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends to the gateway reports from its external NTC temperature sensor even if there are not changes in the values. This Parameter defines the interval between consecutive reports", + name: "Selected Energy Report Interval in minutes", paramNum: 143, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "Air & Floor Temperature Sensors Report Threshold", description: "This Parameter determines the change in temperature level (in °C) resulting in temperature sensors report being sent to the gateway. The value of this Parameter should be x10 for °C, e.g. for 0.4°C use value 4. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Temperature Threshold in °Cx10", paramNum: 144, size: 1, default: 2, type: "number", min: 0 , max: 100, unit: " °Cx10"], + +[title: "Humidity Sensor Report Threshold", description: "This Parameter determines the change in humidity level in % resulting in humidity sensors report being sent to the gateway. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Humidity Threshold in %", paramNum: 145, size: 1, default: 2, type: "number", min: 0 , max: 25, unit: "%"], + +[title: "Light Sensor Report Threshold", description: "This Parameter determines the change in the ambient environment illuminance level resulting in a light sensors report being sent to the gateway. From 10% to 99% can be selected. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Light Sensor Threshold in %", paramNum: 146, size: 1, default: 50, type: "number", min: 0 , max: 99, unit: "%"] + +]} \ No newline at end of file From e6c9affb7a15e486a53c6d27478b454ea486a17a Mon Sep 17 00:00:00 2001 From: jwg-123 <51741592+jwg-123@users.noreply.github.com> Date: Tue, 7 Dec 2021 17:39:12 +0800 Subject: [PATCH 331/422] DevWs for www.easyiot.tech containing containing ZigBee RGBW Bulb (#76714) * DevWs for www.easyiot.tech containing containing ZigBee RGBW Bulb Co-authored-by: jiang wegang --- .../smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 17001cf5738..65dadbc811a 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -105,6 +105,8 @@ metadata { fingerprint manufacturer: "Ajax online Ltd", model: "AJ_ZB30_GU10", deviceJoinName: "Ajax Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" // Raw Description: 0B 0104 010D 01 08 0000 0003 0004 0005 0006 0008 0300 1000 00 // Shenzhen C-Lux fingerprint manufacturer: "Shenzhen C-Lux", model: "CL000ZB", deviceJoinName: "C-Lux Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" + // www.easyiot.tech + fingerprint manufacturer: "eWeLight", model: "ZB-CL01", deviceJoinName: "easyiot Light", mnmn: "SmartThings", vid: "generic-rgbw-color-bulb-2000K-6500K" } // UI tile definitions From 385a42c2242fc911283d906c7f4ecaf47f3b62cd Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Thu, 9 Dec 2021 17:53:21 +0900 Subject: [PATCH 332/422] DevWs for SHINA SYSTEM containing containing ZigBee Lock Without Codes (#76888) --- .../zigbee-lock-without-codes.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy index 36626a03291..bc718e0efb3 100644 --- a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy +++ b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy @@ -28,6 +28,7 @@ metadata { fingerprint profileId:"0104, 000A", inClusters:"0000, 0001, 0003, 0009, 0020,0101, 0B05", outclusters:"000A, 0019, 0B05", manufacturer:"Danalock", model:"V3-BTZB", deviceJoinName:"Danalock Door Lock" //Danalock V3 Smart Lock fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0500, 0101", outClusters: "0019", model: "E261-KR0B0Z0-HA", deviceJoinName: "C2O Door Lock", mnmn: "SmartThings", vid: "C2O-ZigBee-Lock" //C2O Lock + fingerprint profileId:"0104", inClusters:"0000, 0001, 0003, 0020,0101", outclusters:"0003,0004, 0019", manufacturer:"ShinaSystem", model:"DLM-300Z", deviceJoinName:"SiHAS Door Lock" //SiHAS Door Lock } From 329411950001a24a6eac6b5dac55749b2b1157ba Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 21 Dec 2021 18:33:03 +0800 Subject: [PATCH 333/422] DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch (#77178) * DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch * Add my fingerprint based on the latest code in the main repository * In the DTH file "Z-Wave Metering Switch", we have added support for anther new product (ZW38M). * delete the fingerprint for ZW38M, we'll add it at the next update. Co-authored-by: Winnie Wen --- .../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 170107bfdba..5fa98c6b5ca 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -53,6 +53,7 @@ metadata { fingerprint mfr: "0154", prod: "0003", model: "000A", deviceJoinName: "POPP Outlet", ocfDeviceType: "oic.d.smartplug" //EU //POPP Smart Outdoor Plug fingerprint mfr: "010F", prod: "1F01", model: "1000", deviceJoinName: "Fibaro Outlet", ocfDeviceType: "oic.d.smartplug" //EU //Fibaro walli Outlet //Fibaro Outlet fingerprint mfr: "0312", prod: "FF00", model: "FF0E", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP21ZP + fingerprint mfr: "0312", prod: "FF00", model: "FF0F", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP22ZP } // simulator metadata From acdd873187705e857cf7010529aa04946bd249df Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 21 Dec 2021 20:06:43 +0900 Subject: [PATCH 334/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#76887) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * Update sihas-multipurpose-sensor.groovy remove unnecessary space. --- .../sihas-multipurpose-sensor.groovy | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index 94ca4fef5ce..d94e8d9a213 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -29,12 +29,13 @@ metadata { capability "Sensor" capability "Contact Sensor" capability "afterguide46998.peopleCounter" + capability "afterguide46998.inOutDirection" fingerprint inClusters: "0000,0001,0003,0020,0400,0402,0405,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "USM-300Z", deviceJoinName: "SiHAS MultiPurpose Sensor", mnmn: "SmartThings", vid: "generic-motion-6" fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2", ocfDeviceType: "x.com.st.d.sensor.motion" fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Temp/Humidity_Sensor", ocfDeviceType: "oic.d.thermostat" fingerprint inClusters: "0000,0001,0003,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "DSM-300Z", deviceJoinName: "SiHAS Contact Sensor", mnmn: "SmartThings", vid: "generic-contact-3", ocfDeviceType: "x.com.st.d.sensor.contact" - fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "15962fd0-22b8-352e-9641-de640d672bb6", ocfDeviceType: "x.com.st.d.sensor.motion" + fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "23d6139c-b108-3467-9ddb-6a177d6cc1df", ocfDeviceType: "x.com.st.d.sensor.motion" } preferences { section { @@ -198,13 +199,26 @@ private Map getContactResult(value) { } private Map getAnalogInputResult(value) { - Float f = Float.intBitsToFloat(value.intValue()) - int pc = f.round(0) - String descriptionText = "${device.displayName} : $pc" + Float fpc = Float.intBitsToFloat(value.intValue()) + def prevInOut = device.currentState('inOutDir')?.value + int pc = ((int)(fpc*10))/10 //people counter + int inout = ((int)(fpc*10).round(0))%10; // inout direction : .1 = in, .2 = out, .0 = ready + if(inout>2) inout = 2 + String inoutString = ( (inout==1) ? "in" : (inout==2) ? "out":"ready") + String descriptionText1 = "${device.displayName} : $pc" + String descriptionText2 = "${device.displayName} : $inoutString" + log.debug "[$fpc] = people: $pc, dir: $inout, $inoutString" + + if((inout != "ready") && (prevInOut == inoutString)) { + sendEvent(name: "inOutDir", value: "ready", displayed: true) + } + + sendEvent(name: "peopleCounter", value: pc, displayed: true, descriptionText: descriptionText1 ) + return [ - name : 'peopleCounter', - value : pc, - descriptionText: descriptionText, + name : 'inOutDir', + value : inoutString, + descriptionText: descriptionText2, translatable : true ] } From c71525492410622f197f3f908bf04983d76eda04 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Wed, 22 Dec 2021 19:36:07 +0800 Subject: [PATCH 335/422] DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch (#77205) * DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch * add support for ZW38M Co-authored-by: Winnie Wen --- .../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 5fa98c6b5ca..102b4f7ba1e 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -54,6 +54,7 @@ metadata { fingerprint mfr: "010F", prod: "1F01", model: "1000", deviceJoinName: "Fibaro Outlet", ocfDeviceType: "oic.d.smartplug" //EU //Fibaro walli Outlet //Fibaro Outlet fingerprint mfr: "0312", prod: "FF00", model: "FF0E", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP21ZP fingerprint mfr: "0312", prod: "FF00", model: "FF0F", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP22ZP + fingerprint mfr: "0312", prod: "FF00", model: "FF11", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Power Meter Plug, ZW38M } // simulator metadata From b2414dc352ccbdd00038366f147708f1d0b3d831 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Thu, 30 Dec 2021 16:13:13 +0800 Subject: [PATCH 336/422] DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch (#77211) * DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Metering Switch * add new product supported for N4003 Co-authored-by: Winnie Wen --- .../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 102b4f7ba1e..5e42d7558a4 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -55,6 +55,7 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF0E", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP21ZP fingerprint mfr: "0312", prod: "FF00", model: "FF0F", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Smart Plug Meter, MP22ZP fingerprint mfr: "0312", prod: "FF00", model: "FF11", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Power Meter Plug, ZW38M + fingerprint mfr: "0312", prod: "AC01", model: "4003", deviceJoinName: "New One Outlet", ocfDeviceType: "oic.d.smartplug" //Mini Power Meter Plug, N4003 } // simulator metadata From fbf4a2dbec5e292f11f5d65441e7d77751658155 Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Tue, 4 Jan 2022 17:42:25 +0800 Subject: [PATCH 337/422] DevWs for CoolKit Technology Co.,Ltd containing containing eWeLink Button (#77263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 啦啦 王 --- devicetypes/smartthings/ikea-button.src/ikea-button.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index b7a48ada477..3b0996944e6 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -33,7 +33,7 @@ metadata { fingerprint manufacturer: "KE", model: "TRADFRI open/close remote", deviceJoinName: "IKEA Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-IKEA_TRADFRI_open/close_remote" // raw description 01 0104 0203 01 07 0000 0001 0003 0009 0020 1000 FC7C 07 0003 0004 0006 0008 0019 0102 1000 //IKEA TRÅDFRI Open/Close Remote fingerprint manufacturer: "SOMFY", model: "Situo 4 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_Situo4_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 fingerprint manufacturer: "SOMFY", model: "Situo 1 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 - fingerprint inClusters: "0000, 0001, 0003", outClusters: "0003, 0006", manufacturer: "eWeLink", model: "WB01", deviceJoinName: "eWeLink Button" //eWeLink Button + fingerprint inClusters: "0000, 0001, 0003", outClusters: "0003, 0006", manufacturer: "eWeLink", model: "WB01", deviceJoinName: "eWeLink Button" //eWeLink Button WB01 fingerprint inClusters: "0000, 0001, 0003, 0020, FC57", outClusters: "0003, 0006, 0019", manufacturer: "eWeLink", model: "SNZB-01P", deviceJoinName: "eWeLink Button" //eWeLink Button } From 1366986462a5699e7d597307b43442e5c50d01d0 Mon Sep 17 00:00:00 2001 From: Tae kyun Lee <64769321+tklee2020@users.noreply.github.com> Date: Wed, 5 Jan 2022 03:37:51 +0900 Subject: [PATCH 338/422] DevWs for eZEX containing containing Zigbee Power Meter (#77221) * DevWs for eZEX containing containing Zigbee Power Meter * re organized indents * modified indent * modified indent * as requested, "import groovy.json.JsonSlurper" has been added Co-authored-by: TK LEE --- .../zigbee-power-meter.groovy | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy index 36b48b6e924..9aa0486afa6 100644 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -1,3 +1,4 @@ + /** * Copyright 2019 SmartThings * @@ -12,6 +13,7 @@ * */ import physicalgraph.zigbee.zcl.DataType +import groovy.json.JsonSlurper metadata { definition (name: "Zigbee Power Meter", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", ocfDeviceType: "x.com.st.d.energymeter", vid: "SmartThings-smartthings-Aeon_Home_Energy_Meter") { @@ -21,6 +23,7 @@ metadata { capability "Health Check" capability "Sensor" capability "Configuration" + capability "Power Consumption Report" fingerprint profileId: "0104", deviceId:"0053", inClusters: "0000, 0003, 0004, 0B04, 0702", outClusters: "0019", manufacturer: "", model: "E240-KR080Z0-HA", deviceJoinName: "Energy Monitor" //Smart Sub-meter(CT Type) fingerprint profileId: "0104", deviceId:"0007", inClusters: "0000,0003,0702", outClusters: "000A", manufacturer: "Develco", model: "ZHEMI101", deviceJoinName: "frient Energy Monitor" // frient External Meter Interface (develco) 02 0104 0007 00 03 0000 0003 0702 01 000A @@ -104,6 +107,36 @@ def parse(String description) { map.name = "energy" map.value = zigbee.convertHexToInt(it.value)/(energyDivisor * 1000) map.unit = "kWh" + + if (isEZEX()) { + def currentEnergy = zigbee.convertHexToInt(it.value) / 1000 + def prevPowerConsumption = device.currentState("powerConsumption")?.value + Map previousMap = prevPowerConsumption ? new groovy.json.JsonSlurper().parseText(prevPowerConsumption) : [:] + def deltaEnergy = calculateDelta(currentEnergy, previousMap) + def currentTimestamp = Calendar.getInstance().timeInMillis + def prevTimestamp = device.currentState("powerConsumption")?.date?.time + if (prevTimestamp == null) prevTimestamp = 0L + def timeDiff = currentTimestamp - prevTimestamp + log.debug "currentTimestamp= $currentTimestamp, prevTimestamp= $prevTimestamp, timeDiff= $timeDiff" + log.debug "deltaEnergy= $deltaEnergy" + if (deltaEnergy < 0) { + Map reportMap = [:] + reportMap["energy"] = currentEnergy + reportMap["deltaEnergy"] = 0 + sendEvent("name": "powerConsumption", "value": reportMap.encodeAsJSON(), displayed: false) + } else { + if (timeDiff >= 15 * 60 * 1000) { + Map reportMap = [:] + reportMap["energy"] = currentEnergy + if (timeDiff < 24 * 60 * 60 * 1000) { + reportMap["deltaEnergy"] = deltaEnergy + } else { + reportMap["deltaEnergy"] = 0 + } + sendEvent("name": "powerConsumption", "value": reportMap.encodeAsJSON(), displayed: false) + } + } + } } } @@ -157,3 +190,17 @@ private Boolean isFrientSensor() { private Boolean isPMM300Z1() { device.getDataValue("model") == "PMM-300Z1" } + +private Boolean isEZEX() { + device.getDataValue("model") == "E240-KR080Z0-HA" +} + +BigDecimal calculateDelta(BigDecimal currentEnergy, Map previousMap) { + if (previousMap?.'energy' == null) { + log.debug "prevEnergy is null" + return 0 + } + BigDecimal lastAccumulated = BigDecimal.valueOf(previousMap['energy']) + log.debug "currentEnergy= $currentEnergy, prevEnergy= $lastAccumulated" + return currentEnergy.subtract(lastAccumulated) +} \ No newline at end of file From 6bad939acebc80ab058f2ec71d6e4da6467782db Mon Sep 17 00:00:00 2001 From: Tae kyun Lee <64769321+tklee2020@users.noreply.github.com> Date: Wed, 5 Jan 2022 03:37:51 +0900 Subject: [PATCH 339/422] DevWs for eZEX containing containing Zigbee Power Meter (#77221) * DevWs for eZEX containing containing Zigbee Power Meter * re organized indents * modified indent * modified indent * as requested, "import groovy.json.JsonSlurper" has been added Co-authored-by: TK LEE --- .../zigbee-power-meter.groovy | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy index 36b48b6e924..9aa0486afa6 100644 --- a/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy +++ b/devicetypes/smartthings/zigbee-power-meter.src/zigbee-power-meter.groovy @@ -1,3 +1,4 @@ + /** * Copyright 2019 SmartThings * @@ -12,6 +13,7 @@ * */ import physicalgraph.zigbee.zcl.DataType +import groovy.json.JsonSlurper metadata { definition (name: "Zigbee Power Meter", namespace: "smartthings", author: "SmartThings", mnmn: "SmartThings", ocfDeviceType: "x.com.st.d.energymeter", vid: "SmartThings-smartthings-Aeon_Home_Energy_Meter") { @@ -21,6 +23,7 @@ metadata { capability "Health Check" capability "Sensor" capability "Configuration" + capability "Power Consumption Report" fingerprint profileId: "0104", deviceId:"0053", inClusters: "0000, 0003, 0004, 0B04, 0702", outClusters: "0019", manufacturer: "", model: "E240-KR080Z0-HA", deviceJoinName: "Energy Monitor" //Smart Sub-meter(CT Type) fingerprint profileId: "0104", deviceId:"0007", inClusters: "0000,0003,0702", outClusters: "000A", manufacturer: "Develco", model: "ZHEMI101", deviceJoinName: "frient Energy Monitor" // frient External Meter Interface (develco) 02 0104 0007 00 03 0000 0003 0702 01 000A @@ -104,6 +107,36 @@ def parse(String description) { map.name = "energy" map.value = zigbee.convertHexToInt(it.value)/(energyDivisor * 1000) map.unit = "kWh" + + if (isEZEX()) { + def currentEnergy = zigbee.convertHexToInt(it.value) / 1000 + def prevPowerConsumption = device.currentState("powerConsumption")?.value + Map previousMap = prevPowerConsumption ? new groovy.json.JsonSlurper().parseText(prevPowerConsumption) : [:] + def deltaEnergy = calculateDelta(currentEnergy, previousMap) + def currentTimestamp = Calendar.getInstance().timeInMillis + def prevTimestamp = device.currentState("powerConsumption")?.date?.time + if (prevTimestamp == null) prevTimestamp = 0L + def timeDiff = currentTimestamp - prevTimestamp + log.debug "currentTimestamp= $currentTimestamp, prevTimestamp= $prevTimestamp, timeDiff= $timeDiff" + log.debug "deltaEnergy= $deltaEnergy" + if (deltaEnergy < 0) { + Map reportMap = [:] + reportMap["energy"] = currentEnergy + reportMap["deltaEnergy"] = 0 + sendEvent("name": "powerConsumption", "value": reportMap.encodeAsJSON(), displayed: false) + } else { + if (timeDiff >= 15 * 60 * 1000) { + Map reportMap = [:] + reportMap["energy"] = currentEnergy + if (timeDiff < 24 * 60 * 60 * 1000) { + reportMap["deltaEnergy"] = deltaEnergy + } else { + reportMap["deltaEnergy"] = 0 + } + sendEvent("name": "powerConsumption", "value": reportMap.encodeAsJSON(), displayed: false) + } + } + } } } @@ -157,3 +190,17 @@ private Boolean isFrientSensor() { private Boolean isPMM300Z1() { device.getDataValue("model") == "PMM-300Z1" } + +private Boolean isEZEX() { + device.getDataValue("model") == "E240-KR080Z0-HA" +} + +BigDecimal calculateDelta(BigDecimal currentEnergy, Map previousMap) { + if (previousMap?.'energy' == null) { + log.debug "prevEnergy is null" + return 0 + } + BigDecimal lastAccumulated = BigDecimal.valueOf(previousMap['energy']) + log.debug "currentEnergy= $currentEnergy, prevEnergy= $lastAccumulated" + return currentEnergy.subtract(lastAccumulated) +} \ No newline at end of file From 54dacf6ed3dbbcee6ff9a915d146c1853a66fd76 Mon Sep 17 00:00:00 2001 From: Tae kyun Lee <64769321+tklee2020@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:37:33 +0900 Subject: [PATCH 340/422] DevWs for eZEX containing containing ZigBee Switch and 1 more (#77207) * DevWs for eZEX containing containing ZigBee Switch and 1 more * Comments are modified Co-authored-by: TK LEE --- .../zigbee-multi-switch.groovy | 36 +++++++++++++++---- .../zigbee-switch.src/zigbee-switch.groovy | 8 +++-- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 04194274d74..e9889fd7c2e 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -27,12 +27,24 @@ metadata { command "childOn", ["string"] command "childOff", ["string"] - // EZEX - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR2N0Z0-HA", deviceJoinName: "eZEX Switch 1" //EZEX Switch 1 - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR3N0Z0-HA", deviceJoinName: "eZEX Switch 1" //EZEX Switch 1 - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR4N0Z0-HA", deviceJoinName: "eZEX Switch 1" //EZEX Switch 1 - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR5N0Z0-HA", deviceJoinName: "eZEX Switch 1" //EZEX Switch 1 - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR6N0Z0-HA", deviceJoinName: "eZEX Switch 1" //EZEX Switch 1 + // eZEX 1st Generation Switches + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR2N0Z0-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR3N0Z0-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR4N0Z0-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR5N0Z0-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR6N0Z0-HA", deviceJoinName: "eZEX Switch 1" + // eZEX 2nd Generation Switches + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR2N0Z1-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR3N0Z1-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR4N0Z1-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR5N0Z1-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR6N0Z1-HA", deviceJoinName: "eZEX Switch 1" + // eZEX 3rd Generation Switches + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR2N0Z2-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR3N0Z2-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR4N0Z2-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR5N0Z2-HA", deviceJoinName: "eZEX Switch 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR6N0Z2-HA", deviceJoinName: "eZEX Switch 1" fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "074b3ffba5a045b7afd94c47079dd553", deviceJoinName: "Orvibo Switch 1" //Orvibo 2 Gang Switch 1 fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "9f76c9f31b4c4a499e3aca0977ac4494", deviceJoinName: "Orvibo Switch 1" //Orvibo 3 Gang Switch 1 @@ -267,6 +279,8 @@ private getChildCount() { case "HY0097": case "HS2SW3L-EFR-3.0": case "E220-KR3N0Z0-HA": + case "E220-KR3N0Z1-HA": + case "E220-KR3N0Z2-HA": case "ZB-SW03": case "JZ-ZB-003": case "PM-S340-ZB": @@ -277,23 +291,31 @@ private getChildCount() { case "HS6SW3A-W-EF-3.0": return 3 case "E220-KR4N0Z0-HA": + case "E220-KR4N0Z1-HA": + case "E220-KR4N0Z2-HA": case "ZB-SW04": case "JZ-ZB-004": case "SBM300Z4": return 4 case "E220-KR5N0Z0-HA": + case "E220-KR5N0Z1-HA": + case "E220-KR5N0Z2-HA": case "ZB-SW05": case "JZ-ZB-005": case "SBM300Z5": return 5 case "E220-KR6N0Z0-HA": + case "E220-KR6N0Z1-HA": + case "E220-KR6N0Z2-HA": case "ZB-SW06": case "JZ-ZB-006": case "SBM300Z6": return 6 case "E220-KR2N0Z0-HA": + case "E220-KR2N0Z1-HA": + case "E220-KR2N0Z2-HA": case "SBM300Z2": default: return 2 } -} +} \ No newline at end of file diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 765ed7cb010..90e3e0d3636 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -35,8 +35,12 @@ metadata { // LELLKI fingerprint profileId: "0104", inClusters: "0000,0003,0004,00005,0006", outClusters: "0000", manufacturer: "LELLKI", model: "JZ-ZB-001", deviceJoinName: "LELLKI Switch" - // EZEX - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR1N0Z0-HA", deviceJoinName: "eZEX Switch" //EZEX Switch + // eZEX 1st Generation Switch + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR1N0Z0-HA", deviceJoinName: "eZEX Switch" + // eZEX 2nd Generation Switch + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR1N0Z1-HA", deviceJoinName: "eZEX Switch" + // eZEX 3rd Generation Switch + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0006", outClusters: "0006, 000A, 0019", model: "E220-KR1N0Z2-HA", deviceJoinName: "eZEX Switch" // GDKES fingerprint profileId: "0104", inClusters: "0000, 0003, 0005, 0004, 0006", manufacturer: "REXENSE", model: "HY0001", deviceJoinName: "GDKES Switch" //GDKES Smart Switch From 83f0e435df54581ce6ed942a2581a53f41d8b301 Mon Sep 17 00:00:00 2001 From: Ryan Greyling Date: Thu, 6 Jan 2022 11:20:33 -0600 Subject: [PATCH 341/422] Cleanup duplicate capabilities Fibaro Heat Controller defines the capability Thermostat Mode twice --- .../fibaro-heat-controller.src/fibaro-heat-controller.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy b/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy index 3822c837b42..c535e6ab08d 100644 --- a/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy +++ b/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy @@ -20,7 +20,6 @@ metadata { capability "Thermostat Heating Setpoint" capability "Health Check" capability "Thermostat" - capability "Thermostat Mode" capability "Temperature Measurement" command "setThermostatSetpointUp" From 495e4c74448c587e2769bbd6384a31c882c71628 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 11 Jan 2022 15:21:15 +0900 Subject: [PATCH 342/422] DevWs for SHINA SYSTEM containing containing ZigBee Lock Without Codes (#77212) * DevWs for SHINA SYSTEM containing containing ZigBee Lock Without Codes * Update zigbee-lock-without-codes.groovy --- .../zigbee-lock-without-codes.groovy | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy index bc718e0efb3..a7dd8ac9ad5 100644 --- a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy +++ b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy @@ -25,10 +25,11 @@ metadata { capability "Battery" capability "Configuration" capability "Health Check" + capability "Contact Sensor" fingerprint profileId:"0104, 000A", inClusters:"0000, 0001, 0003, 0009, 0020,0101, 0B05", outclusters:"000A, 0019, 0B05", manufacturer:"Danalock", model:"V3-BTZB", deviceJoinName:"Danalock Door Lock" //Danalock V3 Smart Lock fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0500, 0101", outClusters: "0019", model: "E261-KR0B0Z0-HA", deviceJoinName: "C2O Door Lock", mnmn: "SmartThings", vid: "C2O-ZigBee-Lock" //C2O Lock - fingerprint profileId:"0104", inClusters:"0000, 0001, 0003, 0020,0101", outclusters:"0003,0004, 0019", manufacturer:"ShinaSystem", model:"DLM-300Z", deviceJoinName:"SiHAS Door Lock" //SiHAS Door Lock + fingerprint profileId:"0104", inClusters:"0000, 0001, 0003, 0020,0101", outclusters:"0003,0004, 0019", manufacturer:"ShinaSystem", model:"DLM-300Z", deviceJoinName:"SiHAS Door Lock", vid:"8019e83a-2ddc-3720-a88c-3cf74186c3ce", mnmn:"SmartThingsCommunity" //SiHAS Door Lock } @@ -69,6 +70,7 @@ private getDOORLOCK_CMD_UNLOCK_DOOR() { 0x01 } private getDOORLOCK_RESPONSE_OPERATION_EVENT() { 0x20 } private getDOORLOCK_RESPONSE_PROGRAMMING_EVENT() { 0x21 } private getPOWER_ATTR_BATTERY_PERCENTAGE_REMAINING() { 0x0021 } +private getDOORLOCK_ATTR_DOORSTATE() { 0x0003 } private getDOORLOCK_ATTR_LOCKSTATE() { 0x0000 } private getIAS_ATTR_ZONE_STATUS() { 0x0002 } @@ -115,6 +117,8 @@ def refresh() { cmds += zigbee.readAttribute(CLUSTER_IAS_ZONE, IAS_ATTR_ZONE_STATUS) } + if (isSiHASLock()) cmds += zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_DOORSTATE) + return cmds } @@ -138,7 +142,7 @@ def initialize() { cmds += zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,DataType.ENUM8, 0, 3600, null) cmds += zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING,DataType.UINT8, 600, 21600, 0x01) cmds += zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) - + if (isSiHASLock()) cmds += zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_DOORSTATE,DataType.ENUM8, 0, 3600, null) cmds += refresh() } @@ -217,6 +221,16 @@ private def parseAttributeResponse(String description) { runIn(1, "delayLockEvent", [overwrite: true, forceForLocallyExecuting: true, data: [map: responseMap]]) return [:] } + } else if (clusterInt == CLUSTER_DOORLOCK && attrInt == DOORLOCK_ATTR_DOORSTATE) { + def value = Integer.parseInt(descMap.value, 16) + responseMap.name = "contact" + if (value == 0) { + responseMap.value = "open" + responseMap.descriptionText = "open state" + } else if (value == 1) { + responseMap.value = "closed" + responseMap.descriptionText = "closed state" + } } else { return null } @@ -306,4 +320,8 @@ private Boolean secondsPast(timestamp, seconds) { private boolean isC2OLock() { device.getDataValue("model") == "E261-KR0B0Z0-HA" -} \ No newline at end of file +} + +private boolean isSiHASLock() { + device.getDataValue("model") == "DLM-300Z" +} From 27bac3c71d6840c4f0435457d7f2fe41100fdfe9 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Wed, 12 Jan 2022 20:00:12 +0800 Subject: [PATCH 343/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug (#77213) Co-authored-by: Winnie Wen --- .../min-smart-plug.src/min-smart-plug.groovy | 96 +++++-------------- 1 file changed, 24 insertions(+), 72 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy index ffb60b0eee4..3b2613d40de 100644 --- a/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy +++ b/devicetypes/sky-nie/min-smart-plug.src/min-smart-plug.groovy @@ -1,7 +1,7 @@ /** - * Min Smart Plug v2.2.0 + * Min Smart Plug v3.0.0 * - * Models: MINOSTON (MP21Z) And Eva Logik (ZW30) / MINOSTON (MS10Z) + * Models: MINOSTON (MP21Z) And New One Mini Smart Plug (N4001) * * Author: * winnie (sky-nie) @@ -10,6 +10,10 @@ * * Changelog: * + * 3.0.0 (09/07/2021) + * - Remove the support for the products of MS10ZS MS12ZS ZW30 ZW30S and ZW30TS, + * they will be independent in another DTH file + * * 2.2.0 (09/22/2021) * - Remove the function related to CentralScene-the function did not achieve the expected effect, * and it can be replaced by the Automation function in the SmartThings APP @@ -79,31 +83,16 @@ metadata { fingerprint mfr: "0312", prod: "C000", model: "C009", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" // old MP21Z fingerprint mfr: "0312", prod: "FF00", model: "FF0C", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //MP21Z Minoston Mini Smart Plug fingerprint mfr: "0312", prod: "AC01", model: "4001", deviceJoinName: "New One Outlet", ocfDeviceType: "oic.d.smartplug" // N4001 New One Mini Smart Plug - fingerprint mfr: "0312", prod: "EE00", model: "EE01", deviceJoinName: "Minoston Switch", ocfDeviceType: "oic.d.switch" //MS10ZS Minoston Smart Switch - fingerprint mfr: "0312", prod: "EE00", model: "EE03", deviceJoinName: "Minoston Switch", ocfDeviceType: "oic.d.switch" //MS12ZS Minoston Smart on/off Toggle Switch - fingerprint mfr: "0312", prod: "A000", model: "A005", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30 - fingerprint mfr: "0312", prod: "BB00", model: "BB01", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30S Evalogik Smart on/off Switch - fingerprint mfr: "0312", prod: "BB00", model: "BB03", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30TS Evalogik Smart on/off Toggle Switch } preferences { - getConfigParamInput(ledModeParam) - getConfigParamInput(autoOffIntervalParam) - getConfigParamInput(autoOnIntervalParam) - getConfigParamInput(powerFailureRecoveryParam) - input "disclaimer", "paragraph", - title: "WARNING", - description: "Configuring for 'Paddle Control'is only valid for the devices with product number of MS10ZS, MS12ZS, ZW30, ZW30S, ZW30TS(one of them)", - required: false - getConfigParamInput(paddleControlParam) - } -} - -private getConfigParamInput(param) { - if (param.range) { - input "configParam${param.num}", "number", title: "${param.name}:", required: false, defaultValue: "${param.value}", range: param.range - } else { - input "configParam${param.num}", "enum", title: "${param.name}:", required: false, defaultValue: "${param.value}", options: param.options + configParams.each { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options + } + } } } @@ -319,42 +308,24 @@ private getConfigParams() { ledModeParam, autoOffIntervalParam, autoOnIntervalParam, - powerFailureRecoveryParam, - paddleControlParam + powerFailureRecoveryParam ] } -private static getPaddleControlOptions() { - return [ - "0":"Normal", - "1":"Reverse", - "2":"Toggle" - ] -} - -private getPaddleControlParam() { - def num = isButtonAvailable()? 1 : 1000 - return getParam(num, "Paddle Control", 1, 0, paddleControlOptions) -} - private getLedModeParam() { - def num = isButtonAvailable()? 2 : 1 - return getParam(num, "LED Indicator Mode", 1, 0, alternativeLedOptions) + return getParam(1, "LED Indicator Mode", 1, 0, alternativeLedOptions) } private getAutoOffIntervalParam() { - def num = isButtonAvailable()? 4 : 2 - return getParam(num, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") + return getParam(2, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") } private getAutoOnIntervalParam() { - def num = isButtonAvailable()? 6 : 4 - return getParam(num, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") + return getParam(4, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") } private getPowerFailureRecoveryParam() { - def num = isButtonAvailable()? 8 : 6 - return getParam(num, "Power Failure Recovery", 1, 0, powerFailureRecoveryOptions) + return getParam(6, "Power Failure Recovery", 1, 2, powerFailureRecoveryOptions) } private getParam(num, name, size, defaultVal, options = null, range = null) { @@ -380,20 +351,11 @@ private static setDefaultOption(options, defaultVal) { } private getAlternativeLedOptions() { - if (isButtonAvailable()) { - return [ - "0":"On When On", - "1":"Off When On", - "2":"Always Off" - ] - } else { - return [ - "0":"Off When On", - "1":"On When On", - "2":"Always Off", - "3":"Always On" - ] - } + return [ + "0":"On When On", + "1":"Off When On", + "2":"Always Off" + ] } private static getPowerFailureRecoveryOptions() { @@ -418,14 +380,4 @@ private logDebug(msg) { private logTrace(msg) { log.trace "$msg" -} - -private isButtonAvailable() { - if (device == null) { - log.error "isButtonAvailable device = null" - return true - } else { - log.debug "isButtonAvailable device.rawDescription = ${device.rawDescription}" - return "${device.rawDescription}".contains("model:EE01") || "${device.rawDescription}".contains("model:EE03") || "${device.rawDescription}".contains("model:A005") || "${device.rawDescription}".contains("model:BB01") || "${device.rawDescription}".contains("model:BB03") - } -} +} \ No newline at end of file From 1f4a292516852ee6203551d65166b588a069cf0f Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Wed, 12 Jan 2022 20:03:01 +0800 Subject: [PATCH 344/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer (#77214) Co-authored-by: Winnie Wen --- .../min-smart-plug-dimmer.groovy | 76 ++++--------------- 1 file changed, 16 insertions(+), 60 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index 1a92fad2b4d..ef33c87f9cc 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -1,5 +1,5 @@ /** - * Min Smart Plug Dimmer v2.2.0 + * Min Smart Plug Dimmer v3.0.0 * * Models: MINOSTON (MP21ZD MP22ZD/ZW39S ZW96SD) * @@ -10,6 +10,10 @@ * * Changelog: * + * 3.0.0 (09/07/2021) + * - Remove the support for the products of MS11ZS MS13ZS ZW31S and ZW31TS, + * they will be independent in another DTH file + * * 2.2.0 (09/22/2021) * - Remove the function related to CentralScene-the function did not achieve the expected effect, * and it can be replaced by the Automation function in the SmartThings APP @@ -102,39 +106,16 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF0D", deviceJoinName: "Minoston Smart Plug Dimmer", ocfDeviceType: "oic.d.smartplug" //MP21ZD fingerprint mfr: "0312", prod: "FF07", model: "FF03", deviceJoinName: "Minoston Outdoor Dimmer", ocfDeviceType: "oic.d.smartplug" //MP22ZD fingerprint mfr: "0312", prod: "AC01", model: "4002", deviceJoinName: "New One Smart Plug Dimmer", ocfDeviceType: "oic.d.smartplug" //N4002 - fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", ocfDeviceType: "oic.d.switch" //MS11ZS Minoston Smart Dimmer Switch - fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", ocfDeviceType: "oic.d.switch" //MS13ZS Minoston Smart Toggle Dimmer Switch - fingerprint mfr: "0312", prod: "BB00", model: "BB02", deviceJoinName: "Evalogik Dimmer Switch", ocfDeviceType: "oic.d.switch" //ZW31S Evalogik Smart Dimmer Switch - fingerprint mfr: "0312", prod: "BB00", model: "BB04", deviceJoinName: "Evalogik Dimmer Switch", ocfDeviceType: "oic.d.switch" //ZW31TS Evalogik Smart Toggle Dimmer Switch } preferences { - getConfigParamInput(ledModeParam) - getConfigParamInput(autoOffIntervalParam) - getConfigParamInput(autoOnIntervalParam) - getConfigParamInput(powerFailureRecoveryParam) - getConfigParamInput(pushDimmingDurationParam) - getConfigParamInput(holdDimmingDurationParam) - getConfigParamInput(minimumBrightnessParam) - input "disclaimer", "paragraph", - title: "WARNING", - description: "Configuring for 'Night Light Settings' is only valid for the devices with product number of MP21ZD、MP22ZD、N4002(one of them)", - required: false - getConfigParamInput(nightLightParam) - input "disclaimer", "paragraph", - title: "WARNING", - description: "Configuring for 'Maximum Brightness' and 'Paddle Control' are only valid for the devices with product number of MS11ZS、MS13ZS、ZW31S、ZW31TS(one of them)", - required: false - getConfigParamInput(maximumBrightnessParam) - getConfigParamInput(paddleControlParam) - } -} - -private getConfigParamInput(param) { - if (param.range) { - input "configParam${param.num}", "number", title: "${param.name}:", required: false, defaultValue: "${param.value}", range: param.range - } else { - input "configParam${param.num}", "enum", title: "${param.name}:", required: false, defaultValue: "${param.value}", options: param.options + configParams.each { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options + } + } } } @@ -317,24 +298,11 @@ private getConfigParams() { powerFailureRecoveryParam, pushDimmingDurationParam, holdDimmingDurationParam, - minimumBrightnessParam, - maximumBrightnessParam, - paddleControlParam - ] -} + minimumBrightnessParam -private static getPaddleControlOptions() { - return [ - "0":"Normal", - "1":"Reverse", - "2":"Toggle" ] } -private getPaddleControlParam() { - return getParam(1, "Paddle Control", 1, 0, paddleControlOptions) -} - private getLedModeParam() { return getParam(2, "LED Indicator Mode", 1, 0, ledModeOptions) } @@ -352,13 +320,11 @@ private getNightLightParam() { } private getPowerFailureRecoveryParam() { - def defaultVal = isButtonAvailable()? 0:2 - return getParam(8, "Power Failure Recovery", 1, defaultVal, powerFailureRecoveryOptions) + return getParam(8, "Power Failure Recovery", 1, 2, powerFailureRecoveryOptions) } private getPushDimmingDurationParam() { - def defaultVal = isButtonAvailable()? 1:2 - return getParam(9, "Push Dimming Duration(0, Disabled; 1 - 10 Seconds)", 1, defaultVal, null, "0..10") + return getParam(9, "Push Dimming Duration(0, Disabled; 1 - 10 Seconds)", 1, 2, null, "0..10") } private getHoldDimmingDurationParam() { @@ -507,14 +473,4 @@ private sendSwitchEvents(rawVal, type) { if (rawVal) { sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") } -} - -private isButtonAvailable() { - if (device == null) { - log.error "isButtonAvailable device = null" - return true - } else { - log.debug "isButtonAvailable device.rawDescription = ${device.rawDescription}" - return "${device.rawDescription}".contains("model:EE02") || "${device.rawDescription}".contains("model:EE04") || "${device.rawDescription}".contains("model:BB02") || "${device.rawDescription}".contains("model:BB04") - } -} +} \ No newline at end of file From 688ecee3f562493c1785d2889ee3a6dc5b33b4cf Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 18 Jan 2022 17:23:18 +0800 Subject: [PATCH 345/422] DevWs for NIE-TECH CO., LTD. containing containing In-Wall Smart Switch (#77215) Co-authored-by: Winnie Wen --- .../in-wall-smart-switch.groovy | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 devicetypes/sky-nie/in-wall-smart-switch.src/in-wall-smart-switch.groovy diff --git a/devicetypes/sky-nie/in-wall-smart-switch.src/in-wall-smart-switch.groovy b/devicetypes/sky-nie/in-wall-smart-switch.src/in-wall-smart-switch.groovy new file mode 100644 index 00000000000..0172d61c52c --- /dev/null +++ b/devicetypes/sky-nie/in-wall-smart-switch.src/in-wall-smart-switch.groovy @@ -0,0 +1,362 @@ +/** + * In-Wall Smart Switch v1.0.0 + * + * Models: MS10ZS/MS12ZS/ZW30/ZW30S/ZW30TS + * + * Author: + * winnie (sky-nie) + * + * Documentation: + * + * Changelog: + * + * 1.0.0 (12/22/2021) + * - Initial Release + * + * Reference: + * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/eva-logik-in-wall-smart-switch.src/eva-logik-in-wall-smart-switch.groovy + * + * 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. + * + */ +import groovy.json.JsonOutput + +metadata { + definition (name: "In-Wall Smart Switch", namespace: "sky-nie", author: "winnie", mnmn: "SmartThings", vid:"generic-switch") { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Configuration" + capability "Refresh" + capability "Health Check" + + attribute "firmwareVersion", "string" + attribute "syncStatus", "string" + + fingerprint mfr: "0312", prod: "EE00", model: "EE01", deviceJoinName: "Minoston Switch", ocfDeviceType: "oic.d.switch" //MS10ZS Minoston Smart Switch + fingerprint mfr: "0312", prod: "EE00", model: "EE03", deviceJoinName: "Minoston Switch", ocfDeviceType: "oic.d.switch" //MS12ZS Minoston Smart on/off Toggle Switch + fingerprint mfr: "0312", prod: "A000", model: "A005", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30 + fingerprint mfr: "0312", prod: "BB00", model: "BB01", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30S Evalogik Smart on/off Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB03", deviceJoinName: "Evalogik Switch", ocfDeviceType: "oic.d.switch" //ZW30TS Evalogik Smart on/off Toggle Switch + } + + preferences { + configParams.each { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options + } + } + } +} + +def installed() { + logDebug "installed()..." + sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +private static def getCheckInterval() { + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + return (60 * 60 * 3) + (5 * 60) +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 5000)) { + state.lastUpdated = new Date().time + logDebug "updated()..." + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + runIn(5, executeConfigureCmds, [overwrite: true]) + } + return [] +} + +def configure() { + logDebug "configure()..." + if (state.resyncAll == null) { + state.resyncAll = true + runIn(8, executeConfigureCmds, [overwrite: true]) + } else { + if (!pendingChanges) { + state.resyncAll = true + } + executeConfigureCmds() + } + return [] +} + +def executeConfigureCmds() { + runIn(6, refreshSyncStatus) + + def cmds = [] + + configParams.each { param -> + def storedVal = getParamStoredValue(param.num) + def paramVal = param.value + if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + + state.resyncAll = false + if (cmds) { + sendHubCommand(cmds, 500) + } + return [] +} + +def ping() { + logDebug "ping()..." + return [ switchBinaryGetCmd() ] +} + +def on() { + logDebug "on()..." + return [ switchBinarySetCmd(0xFF) ] +} + +def off() { + logDebug "off()..." + return [ switchBinarySetCmd(0x00) ] +} + +def refresh() { + logDebug "refresh()..." + refreshSyncStatus() + return [ switchBinaryGetCmd() ] +} + +private switchBinaryGetCmd() { + return secureCmd(zwave.switchBinaryV1.switchBinaryGet()) +} + +private switchBinarySetCmd(val) { + return secureCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: val)) +} + +private secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s") || ("0x98" in device?.rawDescription?.split(" "))) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + log.error("secureCmd exception", ex) + return cmd.format() + } +} + +def parse(String description) { + def result = [] + try { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result += zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + } catch (e) { + log.error "${e}" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + logTrace "SecurityMessageEncapsulation: ${cmd}" + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + def result = [] + if (encapsulatedCmd) { + result += zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + logTrace "ConfigurationReport: ${cmd}" + sendEvent(name: "syncStatus", value: "Syncing...", displayed: false) + runIn(4, refreshSyncStatus) + def param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + def val = cmd.scaledConfigurationValue + logDebug "${param.name}(#${param.num}) = ${val}" + state["configVal${param.num}"] = val + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logTrace "VersionReport: ${cmd}" + def subVersion = String.format("%02d", cmd.applicationSubVersion) + def fullVersion = "${cmd.applicationVersion}.${subVersion}" + sendEvent(name: "firmwareVersion", value: fullVersion) + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + logTrace "BasicReport: ${cmd}" + sendSwitchEvents(cmd.value, "physical") + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + logTrace "SwitchBinaryReport: ${cmd}" + sendSwitchEvents(cmd.value, "digital") + return [] +} + +private sendSwitchEvents(rawVal, type) { + def switchVal = (rawVal == 0xFF) ? "on" : "off" + sendEvent(name: "switch", value: switchVal, displayed: true, type: type) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: $cmd" + return [] +} + +def refreshSyncStatus() { + def changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +private static getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x25: 1, // Switch Binary + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x27: 1, // Switch All + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x8E: 2, // Multi Channel Association + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 + ] +} + +private getPendingChanges() { + return configParams.count { "${it.value}" != "${getParamStoredValue(it.num)}" } +} + +private getParamStoredValue(paramNum) { + return safeToInt(state["configVal${paramNum}"] , null) +} + +private getConfigParams() { + return [ + ledModeParam, + autoOffIntervalParam, + autoOnIntervalParam, + powerFailureRecoveryParam, + paddleControlParam + ] +} + +private static getPaddleControlOptions() { + return [ + "0":"Normal", + "1":"Reverse", + "2":"Toggle" + ] +} + +private getPaddleControlParam() { + return getParam(1, "Paddle Control", 1, 0, paddleControlOptions) +} + +private getLedModeParam() { + return getParam(2, "LED Indicator Mode", 1, 0, alternativeLedOptions) +} + +private getAutoOffIntervalParam() { + return getParam(4, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") +} + +private getAutoOnIntervalParam() { + return getParam(6, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") +} + +private getPowerFailureRecoveryParam() { + return getParam(8, "Power Failure Recovery", 1, 2, powerFailureRecoveryOptions) +} + +private getParam(num, name, size, defaultVal, options = null, range = null) { + def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + def map = [num: num, name: name, size: size, value: val] + if (options) { + map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value + map.options = setDefaultOption(options, defaultVal) + } + if (range) { + map.range = range + } + return map +} + +private static setDefaultOption(options, defaultVal) { + return options?.collectEntries { k, v -> + if ("${k}" == "${defaultVal}") { + v = "${v} [DEFAULT]" + } + ["$k": "$v"] + } +} + +private getAlternativeLedOptions() { + return [ + "0":"Off When On", + "1":"On When On", + "2":"Always Off", + "3":"Always On" + ] +} + +private static getPowerFailureRecoveryOptions() { + return [ + "0":"Turn Off", + "1":"Turn On", + "2":"Restore Last State" + ] +} + +private static safeToInt(val, defaultVal = 0) { + return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal +} + +private static isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +private logDebug(msg) { + log.debug "$msg" +} + +private logTrace(msg) { + log.trace "$msg" +} \ No newline at end of file From 21ab092dd47d3f016c836c8dcef0dcf08894e8ec Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 18 Jan 2022 17:30:03 +0800 Subject: [PATCH 346/422] DevWs for NIE-TECH CO., LTD. containing containing In-Wall Smart Switch Dimmer (#77216) Co-authored-by: Winnie Wen --- .../in-wall-smart-switch-dimmer.groovy | 427 ++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 devicetypes/sky-nie/in-wall-smart-switch-dimmer.src/in-wall-smart-switch-dimmer.groovy diff --git a/devicetypes/sky-nie/in-wall-smart-switch-dimmer.src/in-wall-smart-switch-dimmer.groovy b/devicetypes/sky-nie/in-wall-smart-switch-dimmer.src/in-wall-smart-switch-dimmer.groovy new file mode 100644 index 00000000000..c8d7bb77d01 --- /dev/null +++ b/devicetypes/sky-nie/in-wall-smart-switch-dimmer.src/in-wall-smart-switch-dimmer.groovy @@ -0,0 +1,427 @@ +/** + * In-Wall Smart Switch Dimmer v1.0.0 + * + * Models: MS11ZS/MS13ZS/ZW31S/ZW31TS + * + * Author: + * winnie (sky-nie) + * + * Documentation: + * + * Changelog: + * + * 1.0.0 (12/22/2021) + * - Initial Release + * + * Reference: + * https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/eva-logik-in-wall-smart-dimmer.src/eva-logik-in-wall-smart-dimmer.groovy + * + * 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. + * + */ +import groovy.json.JsonOutput + +metadata { + definition (name: "In-Wall Smart Switch Dimmer", namespace: "sky-nie", author: "winnie", mnmn: "SmartThings", vid:"generic-dimmer") { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Switch Level" + capability "Configuration" + capability "Refresh" + capability "Health Check" + + attribute "firmwareVersion", "string" + attribute "lastCheckIn", "string" + attribute "syncStatus", "string" + + fingerprint mfr: "0312", prod: "0004", model: "EE02", deviceJoinName: "Minoston Dimmer Switch", ocfDeviceType: "oic.d.switch" //MS11ZS Minoston Smart Dimmer Switch + fingerprint mfr: "0312", prod: "EE00", model: "EE04", deviceJoinName: "Minoston Dimmer Switch", ocfDeviceType: "oic.d.switch" //MS13ZS Minoston Smart Toggle Dimmer Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB02", deviceJoinName: "Evalogik Dimmer Switch", ocfDeviceType: "oic.d.switch" //ZW31S Evalogik Smart Dimmer Switch + fingerprint mfr: "0312", prod: "BB00", model: "BB04", deviceJoinName: "Evalogik Dimmer Switch", ocfDeviceType: "oic.d.switch" //ZW31TS Evalogik Smart Toggle Dimmer Switch + } + + preferences { + configParams.each { + if (it.range) { + input "configParam${it.num}", "number", title: "${it.name}:", required: false, defaultValue: "${it.value}", range: it.range + } else { + input "configParam${it.num}", "enum", title: "${it.name}:", required: false, defaultValue: "${it.value}", options: it.options + } + } + } +} + +def ping() { + logDebug "ping()..." + return [ switchMultilevelGetCmd() ] +} + +def refresh() { + logDebug "refresh()..." + refreshSyncStatus() + return [ switchMultilevelGetCmd() ] +} + +private switchMultilevelGetCmd() { + return secureCmd(zwave.switchMultilevelV3.switchMultilevelGet()) +} + +def installed() { + logDebug "installed()..." + sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + +private static def getCheckInterval() { + // These are battery-powered devices, and it's not very critical + // to know whether they're online or not – 12 hrs + return (60 * 60 * 3) + (5 * 60) +} + +def updated() { + if (!isDuplicateCommand(state.lastUpdated, 5000)) { + state.lastUpdated = new Date().time + logDebug "updated()..." + if (device.latestValue("checkInterval") != checkInterval) { + sendEvent(name: "checkInterval", value: checkInterval, displayed: false) + } + runIn(5, executeConfigureCmds, [overwrite: true]) + } + return [] +} + +def configure() { + logDebug "configure()..." + if (state.resyncAll == null) { + state.resyncAll = true + runIn(8, executeConfigureCmds, [overwrite: true]) + } else { + if (!pendingChanges) { + state.resyncAll = true + } + executeConfigureCmds() + } + return [] +} + +def executeConfigureCmds() { + runIn(6, refreshSyncStatus) + + def cmds = [] + + configParams.each { param -> + def storedVal = getParamStoredValue(param.num) + def paramVal = param.value + if (state.resyncAll || ("${storedVal}" != "${paramVal}")) { + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: paramVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + + state.resyncAll = false + if (cmds) { + sendHubCommand(cmds, 500) + } + return [] +} + +def parse(String description) { + def result = [] + try { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + result += zwaveEvent(cmd) + } else { + logDebug "Unable to parse description: $description" + } + sendEvent(name: "lastCheckIn", value: convertToLocalTimeString(new Date()), displayed: false) + } catch (e) { + log.error "$e" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + logTrace "SecurityMessageEncapsulation: ${cmd}" + def encapCmd = cmd.encapsulatedCommand(commandClassVersions) + def result = [] + if (encapCmd) { + result += zwaveEvent(encapCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } + return result +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + logTrace "ConfigurationReport: ${cmd}" + sendEvent(name: "syncStatus", value: "Syncing...", displayed: false) + runIn(4, refreshSyncStatus) + def param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + def val = cmd.scaledConfigurationValue + logDebug "${param.name}(#${param.num}) = ${val}" + state["configParam${param.num}"] = val + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } + return [] +} + +def refreshSyncStatus() { + def changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +def zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: $cmd" + return [] +} + +private secureCmd(cmd) { + try { + if (zwaveInfo?.zw?.contains("s") || ("0x98" in device?.rawDescription?.split(" "))) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } + } catch (ex) { + log.error("secureCmd exception", ex) + return cmd.format() + } +} + +private static getCommandClassVersions() { + [ + 0x20: 1, // Basic + 0x26: 3, // Switch Multilevel + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x71: 3, // Notification + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x8E: 2, // Multi Channel Association + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 + ] +} + +private getPendingChanges() { + return configParams.count { "${it.value}" != "${getParamStoredValue(it.num)}" } +} + +private getParamStoredValue(paramNum) { + return safeToInt(state["configParam${paramNum}"] , null) +} + +// Configuration Parameters +private getConfigParams() { + [ + ledModeParam, + autoOffIntervalParam, + autoOnIntervalParam, + powerFailureRecoveryParam, + pushDimmingDurationParam, + holdDimmingDurationParam, + minimumBrightnessParam, + maximumBrightnessParam, + paddleControlParam + ] +} + +private static getPaddleControlOptions() { + return [ + "0":"Normal", + "1":"Reverse", + "2":"Toggle" + ] +} + +private getPaddleControlParam() { + return getParam(1, "Paddle Control", 1, 0, paddleControlOptions) +} + +private getLedModeParam() { + return getParam(2, "LED Indicator Mode", 1, 0, ledModeOptions) +} + +private getAutoOffIntervalParam() { + return getParam(4, "Auto Turn-Off Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") +} + +private getAutoOnIntervalParam() { + return getParam(6, "Auto Turn-On Timer(0, Disabled; 1 - 65535 minutes)", 4, 0, null, "0..65535") +} + +private getPowerFailureRecoveryParam() { + return getParam(8, "Power Failure Recovery", 1, 2, powerFailureRecoveryOptions) +} + +private getPushDimmingDurationParam() { + return getParam(9, "Push Dimming Duration(0, Disabled; 1 - 10 Seconds)", 1, 1, null, "0..10") +} + +private getHoldDimmingDurationParam() { + return getParam(10, "Hold Dimming Duration(1 - 10 Seconds)", 1, 4, null, "1..10") +} + +private getMinimumBrightnessParam() { + return getParam(11, "Minimum Brightness(0, Disabled; 1 - 99:1% - 99%)", 1, 10, null,"0..99") +} + +private getMaximumBrightnessParam() { + return getParam(12, "Maximum Brightness(0, Disabled; 1 - 99:1% - 99%)", 1, 99, null,"0..99") +} + +private getParam(num, name, size, defaultVal, options = null, range = null) { + def val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + def map = [num: num, name: name, size: size, value: val] + if (options) { + map.valueName = options?.find { k, v -> "${k}" == "${val}" }?.value + map.options = setDefaultOption(options, defaultVal) + } + if (range) { + map.range = range + } + return map +} + +private static setDefaultOption(options, defaultVal) { + return options?.collectEntries { k, v -> + if ("${k}" == "${defaultVal}") { + v = "${v} [DEFAULT]" + } + ["$k": "$v"] + } +} + +private static getLedModeOptions() { + return [ + "0":"Off When On", + "1":"On When On", + "2":"Always Off", + "3":"Always On" + ] +} + +private static getPowerFailureRecoveryOptions() { + return [ + "0":"Turn Off", + "1":"Turn On", + "2":"Restore Last State" + ] +} + +private static validateRange(val, defaultVal, lowVal, highVal) { + val = safeToInt(val, defaultVal) + if (val > highVal) { + return highVal + } else if (val < lowVal) { + return lowVal + } else { + return val + } +} + +private static safeToInt(val, defaultVal = 0) { + return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal +} + +private convertToLocalTimeString(dt) { + def timeZoneId = location?.timeZone?.ID + if (timeZoneId) { + return dt.format("MM/dd/yyyy hh:mm:ss a", TimeZone.getTimeZone(timeZoneId)) + } else { + return "$dt" + } +} + +private static isDuplicateCommand(lastExecuted, allowedMil) { + !lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) +} + +private logDebug(msg) { + log.debug "$msg" +} + +private logTrace(msg) { + log.trace "$msg" +} + +def on() { + logDebug "on()..." + return [ basicSetCmd(0xFF) ] +} + +def off() { + logDebug "off()..." + return [ basicSetCmd(0x00) ] +} + +def setLevel(level) { + logDebug "setLevel($level)..." + return setLevel(level, 1) +} + +def setLevel(level, duration) { + logDebug "setLevel($level, $duration)..." + if (duration > 30) { + duration = 30 + } + return [ switchMultilevelSetCmd(level, duration) ] +} + +private basicSetCmd(val) { + return secureCmd(zwave.basicV1.basicSet(value: val)) +} + +private switchMultilevelSetCmd(level, duration) { + def levelVal = validateRange(level, 99, 0, 99) + def durationVal = validateRange(duration, 1, 0, 100) + return secureCmd(zwave.switchMultilevelV3.switchMultilevelSet(dimmingDuration: durationVal, value: levelVal)) +} + +def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logTrace "VersionReport: ${cmd}" + def subVersion = String.format("%02d", cmd.applicationSubVersion) + def fullVersion = "${cmd.applicationVersion}.${subVersion}" + sendEvent(name: "firmwareVersion", value:fullVersion, displayed: true, type: null) + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + logTrace "BasicReport: ${cmd}" + sendSwitchEvents(cmd.value, "physical") + return [] +} + +def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) { + logTrace "SwitchMultilevelReport: ${cmd}" + sendSwitchEvents(cmd.value, "digital") + return [] +} + +private sendSwitchEvents(rawVal, type) { + def switchVal = rawVal ? "on" : "off" + sendEvent(name: "switch", value:switchVal, displayed: true, type: type) + if (rawVal) { + sendEvent(name: "level", value:rawVal, displayed: true, type: type, unit:"%") + } +} \ No newline at end of file From e67104b8ae753972564ec76ea7d99f7b2a191d28 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 18 Jan 2022 10:11:37 -0800 Subject: [PATCH 347/422] CHAD-7380 Corrects Schlage fingerprint We got a report that this fingerprint had been incorrect for some time. --- devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy index 39af5e53372..c228bbcd676 100644 --- a/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy +++ b/devicetypes/smartthings/zwave-lock.src/zwave-lock.groovy @@ -43,7 +43,7 @@ metadata { //zw:Fs type:4003 mfr:0090 prod:0003 model:0742 ver:4.10 zwv:4.34 lib:03 cc:5E,72,5A,98,73,7A sec:86,80,62,63,85,59,71,70,4E,8B,4C,5D role:07 ff:8300 ui:8300 fingerprint mfr:"0090", prod:"0003", model:"0742", deviceJoinName: "Kwikset Door Lock" //Kwikset Obsidian Lock // Schlage - fingerprint mfr:"003B", prod:"6341", model:"0544", deviceJoinName: "Schlage Door Lock" //Schlage Touchscreen Deadbolt Door Lock + fingerprint mfr:"003B", prod:"6349", model:"5044", deviceJoinName: "Schlage Door Lock" //Schlage Touchscreen Deadbolt Door Lock fingerprint mfr:"003B", prod:"6341", model:"5044", deviceJoinName: "Schlage Door Lock" //Schlage Touchscreen Deadbolt Door Lock fingerprint mfr:"003B", prod:"634B", model:"504C", deviceJoinName: "Schlage Door Lock" //Schlage Connected Keypad Lever Door Lock fingerprint mfr:"003B", prod:"0001", model:"0468", deviceJoinName: "Schlage Door Lock" //BE468ZP //Schlage Connect Smart Deadbolt Door Lock From 7beff203315716e8afc4edf577172ff7bb375cfb Mon Sep 17 00:00:00 2001 From: AltyorFig <95210115+AltyorFig@users.noreply.github.com> Date: Wed, 19 Jan 2022 11:40:50 +0100 Subject: [PATCH 348/422] DevWs for NodOn containing containing NodOn multi Switch (#77465) * DevWs for NodOn containing containing NodOn multi Switch * insert NodOn fingerprint into smartthings / Zigbee Switch * Delete device NodOn multi switch * fix indentations fingerprint * update the last version of zigbee switch and check the indentation of our brand * change deviceJoinName: "NodOn Switch" --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 90e3e0d3636..d64ae59efc6 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -77,6 +77,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0B05", outClusters: "0019", manufacturer: "Leviton", model: "DG15S", deviceJoinName: "Leviton Switch" //Leviton Lumina RF Switch fingerprint manufacturer: "Leviton", model: "DG15A", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-In Switch DG15A, Raw Description: 01 0104 010A 00 06 0000 0003 0004 0005 0006 0B05 01 0019 + // NodOn + fingerprint profileId: "0104", deviceId: "0002", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-1-20", deviceJoinName: "NodOn Switch" + // Orvibo fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "095db3379e414477ba6c2f7e0c6aa026", deviceJoinName: "Orvibo Switch" //Orvibo Smart Switch fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "fdd5fce51a164c7ab73b2f4d8d84c88e", deviceJoinName: "Orvibo Outlet", ocfDeviceType: "oic.d.smartplug" //Orvibo Smart Outlet From a5269f8b26f6840cb3f1622936317d9ff45911c8 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Thu, 20 Jan 2022 17:01:53 +0900 Subject: [PATCH 349/422] DevWs for SHINA SYSTEM containing containing Zigbee Power Meter (#77282) * DevWs for SHINA SYSTEM containing containing Zigbee Power Meter * Update sihas-zigbee-power-meter.groovy fix indentation * Update sihas-zigbee-power-meter.groovy Energy min reporting : 1 -> 5 seconds * Update sihas-zigbee-power-meter.groovy Fix indentations. * Update sihas-zigbee-power-meter.groovy add space after if --- .../sihas-zigbee-power-meter.groovy | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy diff --git a/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy b/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy new file mode 100644 index 00000000000..fd7d4091644 --- /dev/null +++ b/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy @@ -0,0 +1,162 @@ +/** + * Copyright 2022 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. + * + */ +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition (name: "SiHAS Zigbee Power Meter", namespace: "shinasys", author: "SHINA SYSTEM", mnmn: "SmartThingsCommunity", ocfDeviceType: "x.com.st.d.energymeter", vid: "92543bd9-8a3c-3c8a-b43a-036a6a4bea9d") { + capability "Energy Meter" + capability "Power Meter" + capability "Refresh" + capability "Health Check" + capability "Sensor" + capability "Configuration" + capability "Voltage Measurement" + capability "afterguide46998.currentMeasurement" + capability "afterguide46998.frequencyMeasurement" + capability "afterguide46998.powerfactorMeasurement" + capability "Temperature Measurement" + + fingerprint profileId: "0104", manufacturer: "ShinaSystem", model: "PMM-300Z2", deviceJoinName: "SiHAS Energy Monitor" // Single Phase, SIHAS Power Meter 01 0104 0000 01 06 0000 0004 0003 0B04 0702 0402 02 0004 0019 + fingerprint profileId: "0104", manufacturer: "ShinaSystem", model: "PMM-300Z3", deviceJoinName: "SiHAS Energy Monitor" // Three Phase, SIHAS Power Meter 01 0104 0000 01 06 0000 0004 0003 0B04 0702 0402 02 0004 0019 + } +} + +def getATTRIBUTE_READING_INFO_SET() { 0x0000 } +def getATTRIBUTE_HISTORICAL_CONSUMPTION() { 0x0400 } +def getATTRIBUTE_ACTIVE_POWER() { 0x050B } +def getATTRIBUTE_FREQUENCY() { 0x0300 } +def getATTRIBUTE_VOLTAGE() { 0x0505 } +def getATTRIBUTE_CURRENT() { 0x0508 } +def getATTRIBUTE_POWERFACTOR() { 0x0510 } +def getTEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } + +def convertHexToInt24Bit(value) { + int result = zigbee.convertHexToInt(value) + if (result & 0x800000) { + result |= 0xFF000000 + } + return result +} + +def parse(String description) { + log.debug "description is $description" + if (description?.startsWith('temperature:')) { //parse temperature + List result = [] + def map = [:] + map.name = description.split(": ")[0] + map.value = description.split(": ")[1] + map.unit = getTemperatureScale() + log.debug "${device.displayName}: Reported temperature is ${map.value}°$map.unit" + return createEvent(map) + } else { + List result = [] + def descMap = zigbee.parseDescriptionAsMap(description) + log.debug "Desc Map: $descMap" + + List attrData = [[clusterInt: descMap.clusterInt ,attrInt: descMap.attrInt, value: descMap.value, isValidForDataType: descMap.isValidForDataType]] + descMap.additionalAttrs.each { + attrData << [clusterInt: descMap.clusterInt, attrInt: it.attrInt, value: it.value, isValidForDataType: it.isValidForDataType] + } + attrData.each { + def map = [:] + if (it.isValidForDataType && (it.value != null)) { + if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_HISTORICAL_CONSUMPTION) { + log.debug "meter" + map.name = "power" + map.value = convertHexToInt24Bit(it.value)/powerDivisor + map.unit = "W" + } else if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_READING_INFO_SET) { + log.debug "energy" + map.name = "energy" + map.value = zigbee.convertHexToInt(it.value)/energyDivisor + map.unit = "kWh" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_FREQUENCY) { + log.debug "frequency" + map.name = "frequency" + map.value = zigbee.convertHexToInt(it.value)/frequencyDivisor + map.unit = "Hz" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_VOLTAGE) { + log.debug "voltage" + map.name = "voltage" + map.value = zigbee.convertHexToInt(it.value)/voltageDivisor + map.unit = "V" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_CURRENT) { + log.debug "current" + map.name = "current" + map.value = zigbee.convertHexToInt(it.value)/currentDivisor + map.unit = "A" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_POWERFACTOR) { + log.debug "power factor $it.value" + map.name = "powerFactor" + map.value = (byte) zigbee.convertHexToInt(it.value)/powerFactorDivisor + map.unit = "%" + } else if (it.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && it.attrInt == TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) { + log.debug "temperature" + map.name = "temperature" + map.unit = getTemperatureScale() + map.value = zigbee.parseHATemperatureValue("temperature: " + (zigbee.convertHexToInt(it.value)), "temperature: ", tempScale) + log.debug "${device.displayName}: Reported temperature is ${map.value}°$map.unit" + } + } + + if (map) { + result << createEvent(map) + } + log.debug "Parse returned $map" + } + return result + } +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return refresh() +} + +def refresh() { + log.debug "refresh " + zigbee.simpleMeteringPowerRefresh() + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_FREQUENCY) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_VOLTAGE) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_CURRENT) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_POWERFACTOR) + + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) +} + +def configure() { + def configCmds = [] + // this device will send instantaneous demand and current summation delivered every 1 minute + sendEvent(name: "checkInterval", value: 2 * 60 + 10 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + log.debug "Configuring Reporting" + configCmds = zigbee.simpleMeteringPowerConfig() + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 5, 600, 1) + + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_FREQUENCY, DataType.UINT16, 10, 600, 3) + /* 3 unit : 0.3Hz */ + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_VOLTAGE, DataType.UINT16, 5, 600, 3) + /* 3 unit : 0.3V */ + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_CURRENT, DataType.UINT16, 5, 600, 1) + /* 1 unit : 0.01A */ + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_POWERFACTOR, DataType.INT8, 10, 600, 1) + /* 1 unit : 0.1% */ + zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 20, 300, 10 /* 1 uint : 0.1C */) + return configCmds + refresh() +} + +private getActivePowerDivisor() { 1 } +private getPowerDivisor() { 1 } +private getEnergyDivisor() { 1000 } +private getFrequencyDivisor() { 10 } +private getVoltageDivisor() { 10 } +private getCurrentDivisor() { 100 } +private getPowerFactorDivisor() { 1 } From 0efe39641f0eb03f07e8552910f4ca8640fca9c2 Mon Sep 17 00:00:00 2001 From: Sarkis008 <92102906+Sarkis008@users.noreply.github.com> Date: Tue, 25 Jan 2022 22:33:26 +0400 Subject: [PATCH 350/422] DevWs for HELTUN containing containing HELTUN RS01 Switch (#76678) * DevWs for HELTUN containing containing HELTUN RS01 Switch * Child Devices * fixed spacing * fix identation * Changed child device to "smartthings", "Child Button" * Spacing fix * Delete he-button.groovy * Formating * Spacing * Formating * Changing "HE-RELAY" child name to "Heltun Child Relay" Spacing fix * Delete he-relay.groovy Co-authored-by: Artur Sargsyan --- .../heltun-child-relay.groovy | 38 ++ .../heltun-rs01-switch.groovy | 637 ++++++++++++++++++ 2 files changed, 675 insertions(+) create mode 100644 devicetypes/heltun/heltun-child-relay.src/heltun-child-relay.groovy create mode 100644 devicetypes/heltun/heltun-rs01-switch.src/heltun-rs01-switch.groovy diff --git a/devicetypes/heltun/heltun-child-relay.src/heltun-child-relay.groovy b/devicetypes/heltun/heltun-child-relay.src/heltun-child-relay.groovy new file mode 100644 index 00000000000..244c56a3ef6 --- /dev/null +++ b/devicetypes/heltun/heltun-child-relay.src/heltun-child-relay.groovy @@ -0,0 +1,38 @@ +/** + * + * + * Copyright 2021 Sarkis Kabrailian + * + * 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: "Heltun Child Relay", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true, ocfDeviceType: "oic.d.switch") { + capability "Switch" + capability "Power Meter" + capability "Refresh" + capability "Health Check" + } +} + +def ping() { + parent.refresh() +} + +def on() { + parent.childOn(device.deviceNetworkId) +} + +def off() { + parent.childOff(device.deviceNetworkId) +} + +def refresh() { + parent.refresh() +} \ No newline at end of file diff --git a/devicetypes/heltun/heltun-rs01-switch.src/heltun-rs01-switch.groovy b/devicetypes/heltun/heltun-rs01-switch.src/heltun-rs01-switch.groovy new file mode 100644 index 00000000000..15de86ad802 --- /dev/null +++ b/devicetypes/heltun/heltun-rs01-switch.src/heltun-rs01-switch.groovy @@ -0,0 +1,637 @@ +/** + * HELTUN RS01 Switch + * + * Copyright 2021 Sarkis Kabrailian + * + * 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: "HELTUN RS01 Switch", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true, mcdSync: true, ocfDeviceType: "oic.d.switch") { + capability "Switch" + capability "Energy Meter" + capability "Power Meter" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0344", prod: "0004", model: "0009", deviceJoinName: "HELTUN" + } + preferences { + input ( + title: "HE-RS01 | HELTUN Relay Switch", + description: "The user manual document with all technical information is available in support.heltun.com page. In case of technical questions please contact HELTUN Support Team at support@heltun.com", + type: "paragraph", + element: "paragraph" + ) + parameterMap().each { + if (it.title != null) { + input ( + title: "${it.title}", + description: it.description, + type: "paragraph", + element: "paragraph" + ) + } + def unit = it.unit ? it.unit : "" + def defV = it.default as Integer + def defVDescr = it.options ? it.options.get(defV) : "${defV}${unit} - Default Value" + input ( + name: it.name, + title: null, + description: "$defVDescr", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.default, + required: false + ) + } + } +} + +def checkParam() { + boolean needConfig = false + parameterMap().each { + if (state."$it.name" == null || state."$it.name".state == "defNotConfigured") { + state."$it.name" = [value: it.default as Integer, state: "defNotConfigured"] + needConfig = true + } + if (settings."$it.name" != null && (state."$it.name".value != settings."$it.name" as Integer || state."$it.name".state == "notConfigured")) { + state."$it.name".value = settings."$it.name" as Integer + state."$it.name".state = "notConfigured" + needConfig = true + } + } + if ( needConfig ) { + configParam() + } +} + +private configParam() { + def cmds = [] + for (parameter in parameterMap()) { + if ( state."$parameter.name"?.value != null && state."$parameter.name"?.state in ["notConfigured", "defNotConfigured"] ) { + cmds << zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$parameter.name".value, parameterNumber: parameter.paramNum, size: parameter.size).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: parameter.paramNum).format() + break + } + } + if (cmds) { + runIn(5, "checkParam") + sendHubCommand(cmds,500) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def parameter = parameterMap().find( {it.paramNum == cmd.parameterNumber } ).name + if (state."$parameter".value == cmd.scaledConfigurationValue) { + state."$parameter".state = "configured" + } + else { + state."$parameter".state = "error" + } + configParam() +} + +def updated() { + if (childDevices && device.label != state.oldLabel) { + childDevices.each { + def newLabel = getChildName(channelNumber(it.deviceNetworkId)) + it.setLabel(newLabel) + } + state.oldLabel = device.label + } + initialize() +} + +def initialize() { + runIn(3, "checkParam") +} + +def installed() { + def numberOfButtons = 5 + state.oldLabel = device.label + def existingChildren = getChildDevices() + for (i in 1..numberOfButtons) { + def buttonNetworkId = "${device.deviceNetworkId}:${i+10}" + def relayNetworkId = "${device.deviceNetworkId}:${i}" + def childRelayExists = (existingChildren.find {child -> child.getDeviceNetworkId() == relayNetworkId} != NULL) + def childButtonExists = (existingChildren.find {child -> child.getDeviceNetworkId() == buttonNetworkId} != NULL) + if (!childRelayExists) { + addChildDevice("HELTUN", "Heltun Child Relay", relayNetworkId, device.hubId,[completedSetup: true, label: getChildName(i), isComponent: false]) + } + if (!childButtonExists ) { + def child = addChildDevice("smartthings", "Child Button", buttonNetworkId, device.hubId, [completedSetup: true, label: getChildName(i+10), isComponent: true, componentName: "button$i", componentLabel: "Button ${i}"]) + } + } + initialize() +} + +private getChildName(channelNumber) { + if (channelNumber in 1..5) { + return "${device.displayName} " + "${"Switch"} " + "${channelNumber}" + } + else if (channelNumber in 11..16) { + return "${device.displayName} " + "${"Button"} " + "${channelNumber-10}" + } +} + +private channelNumber(String deviceNetworkId) { + deviceNetworkId.split(":")[-1] as Integer +} + +def parse(String description) { + def cmd = zwave.parse(description) + if (cmd) { + return zwaveEvent(cmd) + } +} + +private void setState(value, endpoint = null) { + def map = [ + encap(zwave.basicV1.basicSet(value: value), endpoint), + encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint), + ] + sendHubCommand(map, 500) +} + +private encap(cmd, endpoint) { + if (endpoint) { + zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd).format() + } else { + cmd.format() + } +} + +def on() { + def map = [ + encap(zwave.basicV1.basicSet(value: 0xFF), 0xFF), + encap(zwave.switchBinaryV1.switchBinaryGet(), 1), + encap(zwave.switchBinaryV1.switchBinaryGet(), 2), + encap(zwave.switchBinaryV1.switchBinaryGet(), 3), + encap(zwave.switchBinaryV1.switchBinaryGet(), 4), + encap(zwave.switchBinaryV1.switchBinaryGet(), 5) + ] + sendHubCommand(map, 100) +} + +def off() { + def map = [ + encap(zwave.basicV1.basicSet(value: 0), 0xFF), + encap(zwave.switchBinaryV1.switchBinaryGet(), 1), + encap(zwave.switchBinaryV1.switchBinaryGet(), 2), + encap(zwave.switchBinaryV1.switchBinaryGet(), 3), + encap(zwave.switchBinaryV1.switchBinaryGet(), 4), + encap(zwave.switchBinaryV1.switchBinaryGet(), 5) + ] + sendHubCommand(map, 100) +} + +def childOn(childId) { + setState(0xFF, channelNumber(childId)) +} + +def childOff(childId) { + setState(0, channelNumber(childId)) +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + map.name = "energy" + map.value = cmd.scaledMeterValue + map.unit = "kWh" + sendEvent(map) + } else if (cmd.scale == 2) { + map.name = "power" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "W" + sendEvent(map) + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + def state + def buttonN + switch (cmd.keyAttributes as Integer) { + case 0: + state = "pushed" + buttonN = cmd.sceneNumber + break + case 1: + state = "up" + buttonN = cmd.sceneNumber + break + case 2: + state = "held" + buttonN = cmd.sceneNumber + break + } + if (buttonN) { + def buttonId = buttonN + 10 + def child = childDevices?.find {channelNumber(it.deviceNetworkId) == buttonId } + child?.sendEvent([name: "button", value: state, data: [buttonNumber: 1], isStateChange: true]) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + def endPoint = cmd.sourceEndPoint + def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1]) + def value = encapsulatedCommand.value + def childDevice = childDevices?.find {channelNumber(it.deviceNetworkId) == endPoint } + def corrRelCons = 0 + def corRelParam = 11 + endPoint + def param = parameterMap().find( {it.paramNum == corRelParam } ).name + def paramState = state."$param" + if (paramState){ + corrRelCons = paramState.value + } + if (childDevice) { + childDevice.sendEvent(name: "switch", value: value ? "on" : "off") + if (value) { + sendEvent(name: "switch", value: "on") + childDevice.sendEvent(name: "power", value: corrRelCons, unit: "W") + } else { + childDevice.sendEvent(name: "power", value: 0, unit: "W") + if (!childDevices.any { it.currentValue("switch") == "on" }) { + sendEvent(name: "switch", value: "off") + } + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.clockv1.ClockReport cmd) { + def currDate = Calendar.getInstance(location.timeZone) + def time = [hour: currDate.get(Calendar.HOUR_OF_DAY), minute: currDate.get(Calendar.MINUTE), weekday: currDate.get(Calendar.DAY_OF_WEEK)] + if ((time.hour != cmd.hour) || (time.minute != cmd.minute) || (time.weekday != cmd.weekday)){ + sendHubCommand(zwave.clockV1.clockSet(time).format()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) { + def cmds = [] + if (cmd.groupingIdentifier == 1) { + if (cmd.nodeId != [0, zwaveHubNodeId, 0]) { + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1).format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: [0,zwaveHubNodeId,0]).format() + } + } + if (cmds) { + sendHubCommand(cmds, 1200) + } +} + +def configure() { + refresh() +} + +def refresh() { + def cmds = [] + for (i in 1..5){ + cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), i) + } + cmds << zwave.clockV1.clockGet().format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1).format() + sendHubCommand(cmds, 1200) + runIn(15, "checkParam") +} + +def ping() { + refresh() +} + +def resetEnergyMeter() { + sendHubCommand(zwave.meterV3.meterReset().format()) +} + +private parameterMap() {[ + [ + title: "Relays Output Mode", description: "These Parameters determine the type of loads connected to the device relay outputs. The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", name: "Selected Relay 1 Mode", + options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 7, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Relay 2 Mode", + options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 8, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Relay 3 Mode", + options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 9, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Relay 4 Mode", + options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 10, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Relay 5 Mode", + options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 11, size: 1, default: "0", type: "enum" + ], + [ + title: "Relays Load Power", description: "These parameters are used to specify the loads power that are connected to the device outputs (Relays). Using your connected device’s power consumption specification (see associated owner’s manual), set the load in Watts for the outputs bellow:", + name: "Selected Relay 1 Load Power in Watts", paramNum: 12, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W" + ], + [ + name: "Selected Relay 2 Load Power in Watts", paramNum: 13, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W" + ], + [ + name: "Selected Relay 3 Load Power in Watts", paramNum: 14, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W" + ], + [ + name: "Selected Relay 4 Load Power in Watts", paramNum: 15, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W" + ], + [ + name: "Selected Relay 5 Load Power in Watts", paramNum: 16, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W" + ], + [ + title: "Hold Control Mode for external inputs S1-S5", description: "This Parameter defines how the relay should react while holding the button connected to the corresponding external input. The options are: Hold is disabled, Operate like click, Momentary Switch: When the button is held, the relay output state is ON, as soon as the button is released the relay output state changes to OFF, Reversed Momentary: When the button is held, the relay output state is OFF, as soon as the button is released the relay output state changes to ON, Toggle: When the button is held or released the relay output state will toggle its state (ON to OFF or OFF to ON).", name: "Selected Hold Control Mode for S1", + options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 41, size: 1, default: "2", type: "enum" + ], + [ + name: "Selected Hold Control Mode for S2", + options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 42, size: 1, default: "2", type: "enum" + ], + [ + name: "Selected Hold Control Mode for S3", + options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 43, size: 1, default: "2", type: "enum" + ], + [ + name: "Selected Hold Control Mode for S4", + options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 44, size: 1, default: "2", type: "enum" + ], + [ + name: "Selected Hold Control Mode for S5", + options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 45, size: 1, default: "2", type: "enum" + ], + [ + title: "Hold Mode Duration for External Inputs S1-S5", description: "These Parameters specify the time the device needs to recognize a hold mode when the button connected to an external input is held (key closed). These parameters are available on firmware V1.4 or higher", + name: "Selected Duration for S1 in milliseconds", paramNum: 46, size: 2, default: 500, type: "number", min: 200 , max: 5000, unit: "ms" + ], + [ + name: "Selected Duration for S2 in milliseconds", paramNum: 47, size: 2, default: 500, type: "number", min: 200 , max: 5000, unit: "ms" + ], + [ + name: "Selected Duration for S3 in milliseconds", paramNum: 48, size: 2, default: 500, type: "number", min: 200 , max: 5000, unit: "ms" + ], + [ + name: "Selected Duration for S4 in milliseconds", paramNum: 49, size: 2, default: 500, type: "number", min: 200 , max: 5000, unit: "ms" + ], + [ + name: "Selected Duration for S5 in milliseconds", paramNum: 50, size: 2, default: 500, type: "number", min: 200 , max: 5000, unit: "ms" + ], + [ + title: "Click control mode for external inputs S1-S5", description: "These Parameters defines how the relay should react when clicking the button connected to the corresponding external input. The options are: Click is disabled, Toggle switch: relay inverts state (ON to OFF, OFF to ON), Only On: Relay switches to ON state only, Only Off: Relay switches to OFF state only, Timer: On > Off: Relay output switches to ON state (contacts are closed) then after a specified time switches back to OFF state (contacts are open). The time is specified in 'Relay Timer Mode Duration' below, Timer: Off > On: Relay output switches to OFF state (contacts are open) then after a specified time switches back to On state (contacts are closed). The time is specified in 'Relay Timer Mode Duration' below ", name: "Selected Click Control Mode for S1", + options: [ + 0: "Click is disabled", + 1: "Toggle Switch", + 2: "Only On", + 3: "Only Off", + 4: "Timer: On > Off", + 5: "Timer: Off > On" + ], paramNum: 51, size: 1, default: "1", type: "enum" + ], + [ + name: "Selected Click Control Mode for S2", + options: [ + 0: "Click is disabled", + 1: "Toggle Switch", + 2: "Only On", + 3: "Only Off", + 4: "Timer: On > Off", + 5: "Timer: Off > On" + ], paramNum: 52, size: 1, default: "1", type: "enum" + ], + [ + name: "Selected Click Control Mode for S3", + options: [ + 0: "Click is disabled", + 1: "Toggle Switch", + 2: "Only On", + 3: "Only Off", + 4: "Timer: On > Off", + 5: "Timer: Off > On" + ], paramNum: 53, size: 1, default: "1", type: "enum" + ], + [ + name: "Selected Click Control Mode for S4", options: [ + 0: "Click is disabled", + 1: "Toggle Switch", + 2: "Only On", + 3: "Only Off", + 4: "Timer: On > Off", + 5: "Timer: Off > On" + ], paramNum: 54, size: 1, default: "1", type: "enum" + ], + [ + name: "Selected Click Control Mode for S5", + options: [ + 0: "Click is disabled", + 1: "Toggle Switch", + 2: "Only On", + 3: "Only Off", + 4: "Timer: On > Off", + 5: "Timer: Off > On" + ], paramNum: 55, size: 1, default: "1", type: "enum" + ], + [ + title: "Relays Timer Mode Duration", description: "These parameters specify the duration in seconds for the Timer modes for Click Control Mode above. Press the button and the relay output goes to ON/OFF for the specified time then changes back to OFF/ON. If the value is set to “0” the relay output will operate as a short contact (duration is about 0.5 sec)", + name: "Selected Relay 1 Timer Mode Duration in seconds", paramNum: 71, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s" + ], + [ + name: "Selected Relay 2 Timer Mode Duration in seconds", paramNum: 72, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s" + ], + [ + name: "Selected Relay 3 Timer Mode Duration in seconds", paramNum: 73, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s" + ], + [ + name: "Selected Relay 4 Timer Mode Duration in seconds", paramNum: 74, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s" + ], + [ + name: "Selected Relay 5 Timer Mode Duration in seconds", paramNum: 75, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s" + ], + [ + title: "External Input Number for Relays Output Control", description: "These Parameters defines the relays control source.", name: "Selected Relay 1 Control Source", + options: [ + 0: "Controlled by gateway", + 1: "Controlled by S1", + 2: "Controlled by S2", + 3: "Controlled by S3", + 4: "Controlled by S4", + 5: "Controlled by S5" + ], paramNum: 61, size: 1, default: "1", type: "enum" + ], + [ + name: "Selected Relay 2 Control Source", + options: [ + 0: "Controlled by gateway", + 1: "Controlled by S1", + 2: "Controlled by S2", + 3: "Controlled by S3", + 4: "Controlled by S4", + 5: "Controlled by S5" + ], paramNum: 62, size: 1, default: "2", type: "enum" + ], + [ + name: "Selected Relay 3 Control Source", + options: [ + 0: "Controlled by gateway", + 1: "Controlled by S1", + 2: "Controlled by S2", + 3: "Controlled by S3", + 4: "Controlled by S4", + 5: "Controlled by S5" + ], paramNum: 63, size: 1, default: "3", type: "enum" + ], + [ + name: "Selected Relay 4 Control Source", + options: [ + 0: "Controlled by gateway", + 1: "Controlled by S1", + 2: "Controlled by S2", + 3: "Controlled by S3", + 4: "Controlled by S4", + 5: "Controlled by S5" + ], paramNum: 64, size: 1, default: "4", type: "enum" + ], + [ + name: "Selected Relay 5 Control Source", + options: [ + 0: "Controlled by gateway", + 1: "Controlled by S1", + 2: "Controlled by S2", + 3: "Controlled by S3", + 4: "Controlled by S4", + 5: "Controlled by S5" + ], paramNum: 65, size: 1, default: "5", type: "enum" + ], + [ + title: "Retore Relays State", description: "This parameter determines if the last relay state should be restored after power failure or not. These parameters are available on firmware V1.4 or higher", name: "Selected Mode for Relay 1", + options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 66, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Mode for Relay 2", + options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 67, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Mode for Relay 3", + options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 68, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Mode for Relay 4", + options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 69, size: 1, default: "0", type: "enum" + ], + [ + name: "Selected Mode for Relay 5", + options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 70, size: 1, default: "0", type: "enum" + ], + [ + title: "Relay Inverse Mode", description: "The values in this Parameter specify the relays that will operate in inverse mode. Relays can operate in an inverse mode in two different ways: 1. When the first and the second relays are connected to two different external switches. In this case, after pressing a button, the corresponding relay connected to that button will toggle its state (ON to OFF or OFF to ON), and the other relay will be switched OFF. 2. When two relays are connected to the same external switch. In this case, the relays will operate in roller shutter mode and their behavior will follow these four cycles: a - 1st press of button: the first relay will be switched ON, the second relay will be switched OFF, b - 2nd press of button: both relays will be switched OFF, c - 3rd press of button: the second relay will be switched ON, the first relay will be switched OFF, d - 4th press of button: both relays will be switched OFF. ≡ Note: In this mode, both relays cannot be switched ON at the same time (i.e. simultaneously). ≡ Note: Switching OFF one relay will always operate before switching ON another relay to prevent both relays from being ON at the same time.", name: "Group 1", + options: [ + 0: "Disabled", + 12: "1st & 2nd Relay", + 13: "1st & 3rd Relay", + 14: "1st & 4th Relay", + 15: "1st & 5th Relay", + 23: "2nd & 3rd Relay", + 24: "2nd & 4th Relay", + 25: "2nd & 5th Relay", + 34: "3rd & 4th Relay", + 35: "3rd & 5th Relay", + 45: "4th & 5th Relay" + ], paramNum: 101, size: 1, default: "0", type: "enum" + ], + [ + name: "Group 2", + options: [ + 0: "Disabled", + 12: "1st & 2nd Relay", + 13: "1st & 3rd Relay", + 14: "1st & 4th Relay", + 15: "1st & 5th Relay", + 23: "2nd & 3rd Relay", + 24: "2nd & 4th Relay", + 25: "2nd & 5th Relay", + 34: "3rd & 4th Relay", + 35: "3rd & 5th Relay", + 45: "4th & 5th Relay" + ], paramNum: 102, size: 1, default: "0", type: "enum" + ], + [ + title: "Energy Consumption Meter Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends reports from its energy consumption sensor even if there is no change in the value. This parameter defines the interval between consecutive reports of real time and cumulative energy consumption data to the gateway", + name: "Selected Energy Report Interval in minutes", paramNum: 141, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min" + ], + [ + title: "Control Energy Meter Report", description: "This Parameter determines if the change in the energy meter will result in a report being sent to the gateway. Note: When the device is turning ON, the consumption data will be sent to the gateway once, even if the report is disabled.", name: "Sending Energy Meter Reports", + options: [ + 0: "Disabled", + 1: "Enabled" + ], paramNum: 142, size: 1, default: "1", type: "enum" + ] +]} \ No newline at end of file From 467fbf2218569a39e5c271417358e70f1969fa73 Mon Sep 17 00:00:00 2001 From: mikesip Date: Wed, 26 Jan 2022 10:23:57 -0700 Subject: [PATCH 351/422] Update fibaro-heat-controller.groovy --- .../fibaro-heat-controller.src/fibaro-heat-controller.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy b/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy index c535e6ab08d..6483e7c6683 100644 --- a/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy +++ b/devicetypes/smartthings/fibaro-heat-controller.src/fibaro-heat-controller.groovy @@ -27,6 +27,7 @@ metadata { command "switchMode" fingerprint mfr: "010F", prod: "1301", model: "1000", deviceJoinName: "Fibaro Thermostat" //Fibaro Heat Controller + fingerprint mfr: "010F", prod: "1301", model: "1001", deviceJoinName: "Fibaro Thermostat" //Fibaro Heat Controller } tiles(scale: 2) { @@ -367,4 +368,4 @@ private changeTemperatureSensorStatus(status) { state.isChildOnline = (status == "online") def map = [name: "DeviceWatch-DeviceStatus", value: status] sendEventToChild(map, true) -} \ No newline at end of file +} From 936f000c203c4259ff359cc0aa2543493d553624 Mon Sep 17 00:00:00 2001 From: AltyorFig <95210115+AltyorFig@users.noreply.github.com> Date: Thu, 27 Jan 2022 10:22:48 +0100 Subject: [PATCH 352/422] DevWs for NodOn containing containing NodOn lighting Switch (#77558) * DevWs for NodOn containing containing NodOn lighting Switch --- .../zigbee-multi-switch.src/zigbee-multi-switch.groovy | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index e9889fd7c2e..4be9b697ea0 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -90,6 +90,10 @@ metadata { fingerprint manufacturer: "LELLKI", model: "JZ-ZB-005", deviceJoinName: "LELLKI Switch 1" //LELLKI 5 Gang Switch 1 // Raw Description 01 0104 0100 00 05 0000 0003 0004 0005 0006 01 0000 fingerprint manufacturer: "LELLKI", model: "JZ-ZB-006", deviceJoinName: "LELLKI Switch 1" //LELLKI 6 Gang Switch 1 + + // NodOn + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0007, 0008, FC57", outClusters: "0021", manufacturer: "NodOn", model: "SIN-4-2-20", deviceJoinName: "NodOn Light 1" + // SiHAS Switch (2~6 Gang) fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z2", deviceJoinName: "SiHAS Switch 1" fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z3", deviceJoinName: "SiHAS Switch 1" From 1db5257c626bada1716d95a05e6161d44efe4074 Mon Sep 17 00:00:00 2001 From: AltyorFig <95210115+AltyorFig@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:31:23 +0100 Subject: [PATCH 353/422] DevWs for NodOn containing containing NodOn Roller Shutter (#77656) * DevWs for NodOn containing containing NodOn Roller Shutter * change devicejoinname in NodOn Window Treatment --- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) mode change 100755 => 100644 devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy diff --git a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy old mode 100755 new mode 100644 index 6bb2b707b3a..0bc16af7238 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -27,6 +27,9 @@ metadata { capability "Switch Level" command "pause" + + // NodOn + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0021", manufacturer: "NodOn", model: "SIN-4-RS-20", deviceJoinName: "NodOn Window Treatment" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0102", outClusters: "0019", model: "E2B0-KR000Z0-HA", deviceJoinName: "eZEX Window Treatment" // SY-IoT201-BD //SOMFY Blind Controller/eZEX fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.6", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) @@ -305,4 +308,4 @@ def isSomfy() { device.getDataValue("manufacturer") == "SOMFY" } -private getGLYDEA_MOVE_THRESHOLD() { 3 } +private getGLYDEA_MOVE_THRESHOLD() { 3 } \ No newline at end of file From b358278d3d80f0fdf658162a10ebe5e347981a71 Mon Sep 17 00:00:00 2001 From: "jiangsy@3reality.com" <72915227+jiangshanyang0203@users.noreply.github.com> Date: Sun, 30 Jan 2022 19:34:46 +0800 Subject: [PATCH 354/422] Update Orvibo-Contact-Sensor.groovy add manufacturer == "THIRDREALITY" in order to pair v21 --- .../Orvibo-Contact-Sensor.groovy | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index a33260a3676..0df9da8f9a7 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -36,6 +36,7 @@ metadata { fingerprint manufacturer: "Aurora", model: "DoorSensor50AU", deviceJoinName: "Aurora Open/Closed Sensor" // Raw Description: 01 0104 0402 00 06 0000 0001 0003 0020 0500 0B05 01 0019 //Aurora Smart Door/Window Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001", manufacturer: "HEIMAN", model: "DoorSensor-N", deviceJoinName: "HEIMAN Open/Closed Sensor" //HEIMAN Door Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RDS17BZ", deviceJoinName: "ThirdReality Door Sensor" //ThirdReality Door Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0019", manufacturer: "THIRDREALITY", model: "3RDS17BZ", deviceJoinName: "ThirdReality Door Sensor" //ThirdReality Door Sensor } simulator { @@ -110,7 +111,7 @@ def installed() { log.debug "call installed()" def manufacturer = getDataValue("manufacturer") - if (manufacturer == "Third Reality, Inc") { + if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY" ) { //ThirdReality Door Sensor do not set checkInterval for power-saving. } else { sendEvent(name: "checkInterval", value:20 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) @@ -139,7 +140,7 @@ def refresh() { def configure() { def manufacturer = getDataValue("manufacturer") - if (manufacturer == "Third Reality, Inc") { + if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") { //ThirdReality Door Sensor do not set checkInterval for power-saving. } else if (manufacturer == "eWeLink") { sendEvent(name: "checkInterval", value:2 * 60 * 60 + 5 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"]) @@ -154,7 +155,7 @@ def configure() { cmds = zigbee.enrollResponse() + zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 30, 60 * 5, null) + zigbee.batteryConfig() } else if (manufacturer == "eWeLink" || manufacturer == "HEIMAN") { cmds = zigbee.enrollResponse() + zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 30, 60 * 5, null) + zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, DataType.UINT8, 30, 600, 1) - } else if (manufacturer == "Third Reality, Inc") { + } else if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") { cmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021) } cmds += refresh() @@ -168,7 +169,7 @@ def getBatteryPercentageResult(rawValue) { if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if (manufacturer == "Third Reality, Inc") { + if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From c681d4877f3bc048f3cff51fb9e6a60688714ec1 Mon Sep 17 00:00:00 2001 From: PKacprowiczS <41617389+PKacprowiczS@users.noreply.github.com> Date: Wed, 9 Feb 2022 09:15:06 +0100 Subject: [PATCH 355/422] Allow non-zero power meter values only when device is not idle (#77720) --- .../qubino-flush-thermostat.src/qubino-flush-thermostat.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy index e6a7a539959..372f1852c66 100644 --- a/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy +++ b/devicetypes/smartthings/qubino-flush-thermostat.src/qubino-flush-thermostat.groovy @@ -198,7 +198,8 @@ def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { if (cmd.scale == 0) { createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kWh") } else if (cmd.scale == 2) { - createEvent(name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W") + def powerValue = device.currentValue("thermostatOperatingState") != "idle" ? Math.round(cmd.scaledMeterValue) : 0 + createEvent(name: "power", value: powerValue, unit: "W") } } } From 99c44dd91cc991e70e2ca215619dbc01ec8afdb3 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Wed, 9 Feb 2022 04:31:47 -0500 Subject: [PATCH 356/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZSE44 Temperature | Humidity XS Sensor (#77644) * DevWs for Zooz (The Smartest House) containing containing Zooz ZSE44 Temperature | Humidity XS Sensor * - made requested changes to ZSE44 DTH * Replaced temperatureAlarm custom capability with built-in capability. * made requested changes --- ...se44-temperature-humidity-xs-sensor.groovy | 421 ++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 devicetypes/zooz/zooz-zse44-temperature-humidity-xs-sensor.src/zooz-zse44-temperature-humidity-xs-sensor.groovy diff --git a/devicetypes/zooz/zooz-zse44-temperature-humidity-xs-sensor.src/zooz-zse44-temperature-humidity-xs-sensor.groovy b/devicetypes/zooz/zooz-zse44-temperature-humidity-xs-sensor.src/zooz-zse44-temperature-humidity-xs-sensor.groovy new file mode 100644 index 00000000000..4a7fc7530f4 --- /dev/null +++ b/devicetypes/zooz/zooz-zse44-temperature-humidity-xs-sensor.src/zooz-zse44-temperature-humidity-xs-sensor.groovy @@ -0,0 +1,421 @@ +/* + * Zooz ZSE44 Temperature | Humidity XS Sensor + * + * Changelog: + * + * 2022-02-01 + * - Requested changes + * + * 2022-01-27 + * - Replaced temperatureAlarm custom capability with built-in capability. + * + * 2022-01-26.2 + * - Requested Changes + * + * 2022-01-26 + * - Publication Release + * + * Copyright 2022 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x31: 5, // SensorMultilevel + 0x55: 1, // Transport Service v2 + 0x59: 1, // AssociationGrpInfo v3 + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo v2 + 0x6C: 1, // Supervision + 0x70: 2, // Configuration v4 + 0x71: 3, // Notification v4 + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd v5 + 0x80: 1, // Battery + 0x84: 2, // WakeUp + 0x85: 2, // Association v3 + 0x86: 1, // Version v2 + 0x87: 1, // Indicator v3 + 0x8E: 2, // Multi Channel Association v4 + 0x9F: 1 // Security 2 +] + +@Field static Map configParams = [ + lowBatteryReports: [num:2, title:"Low Battery Reports", size:1, defaultVal:10, options:[10:"10% [DEFAULT]", 20:"20%", 30:"30%", 40:"40%", 50:"50%"]], + tempReportingThreshold: [num:3, title:"Temperature Reporting Threshold", size:1, defaultVal:10, range:"10..100", desc:"10..100 (10 = 1°)"], + tempReportingInterval: [num:16, title:"Temperature Reporting Interval", size:2, defaultVal:240, range:"0..480", desc:"0(disabled), 1..480(minutes)"], + tempUnit: [num:13, title:"Temperature Unit", size:1, defaultVal:1, options:[0:"Celsius", 1:"Fahrenheit [DEFAULT]"]], + tempOffset: [num:14, title:"Temperature Offset", size:1, defaultVal:100, range:"0..200", desc:"0..200 (0: -10°, 100: 0°, 200: +10°)"], + highTempThreshold: [num:5, title:"Heat Alert Temperature", size:1, defaultVal:120, range:"50..120", desc:"50..120(°)"], + lowTempThreshold: [num:7, title:"Freeze Alert Temperature", size:1, defaultVal:10, range:"10..100", desc:"10..100(°)"], + humidityReportingThreshold: [num:4, title:"Humidity Reporting Threshold", size:1, defaultVal:5, range:"1..50", desc:"1..50(%)"], + humidityReportingInterval: [num:17, title:"Humidity Reporting Interval", size:2, defaultVal:240, range:"0..480", desc:"0(disabled), 1..480(minutes)"], + humidityOffset: [num:15, title:"Humidity Offset", size:1, defaultVal:100, range:"0..200", desc:"0..200 (0: -10%, 100: 0%, 200: +10%)"], + highHumidityThreshold: [num:9, title:"High Humidity Alert Level", size:1, defaultVal:0, range:"0..100", desc:"0(disabled), 1..100(%)"], + lowHumidityThreshold: [num:11, title:"Low Humidity Alert Level", size:1, defaultVal:0, range:"0..100", desc:"0(disabled), 1..100(%)"] +] + +@Field static Map temperatureSensor = [sensorType:1, scale:1] +@Field static Map humiditySensor = [sensorType: 5, scale:0] +@Field static Map temperatureAlarm = [name:"temperatureAlarm", notificationType:4, eventValues:[0:"cleared", 2:"heat", 6:"freeze"]] +@Field static Map humidityAlarm = [name:"humidityAlarm", notificationType:16, eventValues:[0:"normal", 2:"high", 6:"low"]] +@Field static int wakeUpInterval = 43200 + +metadata { + definition ( + name: "Zooz ZSE44 Temperature | Humidity XS Sensor", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType:"oic.d.thermostat", + vid: "b68c78d7-bd01-3717-a2ac-d1d55ce5ef73", + mnmn: "SmartThingsCommunity" + ) { + capability "Sensor" + capability "Temperature Measurement" + capability "Relative Humidity Measurement" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Configuration" + capability "platemusic11009.temperatureHumiditySensor" + capability "temperatureAlarm" + capability "platemusic11009.humidityAlarm" + capability "platemusic11009.firmware" + capability "platemusic11009.syncStatus" + + // zw:Ss2a type:0701 mfr:027A prod:7000 model:E004 ver:1.10 zwv:7.13 lib:03 cc:5E,55,9F,6C sec:86,85,8E,59,31,72,5A,87,73,80,71,70,84,7A + fingerprint mfr:"027A", prod:"7000", model:"E004", deviceJoinName: "Zooz Multipurpose Sensor" // Zooz ZSE44 Temperature | Humidity XS Sensor + } + + preferences { + configParams.each { name, param -> + if (param.options) { + input name, "enum", + title: param.title, + description: "Default: ${param.options[param.defaultVal]}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input name, "number", + title: param.title, + description: "${param.desc} - Default: ${param.defaultVal}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + + input "debugLogging", "enum", + title: "Logging:", + description: "Default: Enabled", + required: false, + defaultValue: "1", + options: ["0":"Disabled", "1":"Enabled"] + } +} + +def installed() { + logDebug "installed()..." + state.pendingRefresh = true + initialize() +} + +def updated() { + logDebug "updated()..." + initialize() + + if (pendingChanges) { + logForceWakeupMessage("The setting changes will be sent to the device the next time it wakes up.") + } +} + +void initialize() { + state.debugLoggingEnabled = (safeToInt(settings?.debugLogging, 1) != 0) + + refreshSyncStatus() + + if (device.currentValue("temperatureHumidity") == null) { + state.displayHumidity = " " + state.displayTemperature = " " + sendEvent(name:"temperatureHumidity", value:" ") + } + + if (!device.currentValue("temperatureAlarm")) { + sendEvent(name:"temperatureAlarm", value:"cleared") + } + + if (!device.currentValue("humidityAlarm")) { + sendEvent(name:"humidityAlarm", value:"normal") + } + + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((wakeUpInterval * 2) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } +} + +def refresh() { + logDebug "refresh()..." + refreshSyncStatus() + state.pendingRefresh = true + logForceWakeupMessage("The device will be refreshed the next time it wakes up.") +} + +void logForceWakeupMessage(String msg) { + log.warn "${msg} To force the device to wake up immediately press the action button 4x quickly." +} + +def configure() { + logDebug "configure()..." + sendHubCommand(getRefreshCmds(), 250) +} + +List getRefreshCmds() { + List cmds = [] + + if (state.wakeUpInterval == null) { + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + if (state.pendingRefresh || !device.currentValue("battery")) { + cmds << secureCmd(zwave.batteryV1.batteryGet()) + } + + if (state.pendingRefresh || (device.currentValue("temperature") == null)) { + cmds << secureCmd(zwave.sensorMultilevelV5.sensorMultilevelGet(scale: temperatureSensor.scale, sensorType: temperatureSensor.sensorType)) + } + + if (state.pendingRefresh || (device.currentValue("humidity") == null)) { + cmds << secureCmd(zwave.sensorMultilevelV5.sensorMultilevelGet(scale: humiditySensor.scale, sensorType: humiditySensor.sensorType)) + } + + if (state.pendingRefresh || !device.currentValue("firmwareVersion")) { + cmds << secureCmd(zwave.versionV1.versionGet()) + } + + state.pendingRefresh = false + return cmds +} + +List getConfigureCmds() { + List cmds = [] + + int changes = pendingChanges + if (changes) { + log.warn "Syncing ${changes} Change(s)" + } + + if (state.wakeUpInterval != wakeUpInterval) { + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalSet(seconds: wakeUpInterval, nodeid:zwaveHubNodeId)) + cmds << secureCmd(zwave.wakeUpV2.wakeUpIntervalGet()) + } + + configParams.each { name, param -> + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if (storedVal != settingVal) { + logDebug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: settingVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + return cmds +} + +def ping() { + logDebug "ping()" +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + logDebug "Device Woke Up..." + List cmds = [] + + cmds += getRefreshCmds() + cmds += getConfigureCmds() + + if (!cmds) { + cmds << secureCmd(zwave.batteryV1.batteryGet()) + } + + cmds << secureCmd(zwave.wakeUpV2.wakeUpNoMoreInformation()) + sendHubCommand(cmds, 250) +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) { + logDebug "Wake Up Interval = ${cmd.seconds} seconds" + state.wakeUpInterval = cmd.seconds + refreshSyncStatus() +} + +void zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + Integer val = (cmd.batteryLevel == 0xFF ? 1 : cmd.batteryLevel) + if (val > 100) { + val = 100 + } + logDebug "Battery is ${val}%" + sendEvent(name:"battery", value:val, unit:"%", isStateChange: true) +} + +void zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + logDebug "${cmd}" + switch (cmd.notificationType) { + case temperatureAlarm.notificationType: + sendAlarmEvent(temperatureAlarm, cmd.event) + break + case humidityAlarm.notificationType: + sendAlarmEvent(humidityAlarm, cmd.event) + break + default: + logDebug "${cmd}" + } +} + +void sendAlarmEvent(Map alarm, int notificationEvent) { + String value = alarm.eventValues[notificationEvent] + if (value) { + logDebug "${alarm.name} is ${value}" + sendEvent(name: alarm.name, value: value) + } +} + +void zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + switch (cmd.sensorType) { + case temperatureSensor.sensorType: + def temperature = convertTemperatureIfNeeded(cmd.scaledSensorValue, (cmd.scale ? "F" : "C"), cmd.precision) + sendTemperatureEvent(temperature) + break + case humiditySensor.sensorType: + sendHumidityEvent(cmd.scaledSensorValue) + break + default: + logDebug "Unhandled: ${cmd}" + } +} + +void sendTemperatureEvent(value) { + state.displayTemperature = "${value}°${temperatureScale}" + logDebug "temperature is ${value}°${temperatureScale}" + sendEvent(name: "temperature", value: value, unit: temperatureScale) + sendTemperatureHumidityEvent() +} + +void sendHumidityEvent(value) { + state.displayHumidity = "${safeToInt(value)}%" + logDebug "humidity is ${value}%" + sendEvent(name: "humidity", value: value, unit: "%") + sendTemperatureHumidityEvent() +} + +void sendTemperatureHumidityEvent() { + sendEvent(name: "temperatureHumidity", value: "${state.displayTemperature} | ${state.displayHumidity}", displayed: false) +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + logDebug "${cmd}" + sendEvent(name: "firmwareVersion", value: (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + runIn(4, refreshSyncStatus) + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + int val = cmd.scaledConfigurationValue + + if ((val < 0) && ((name == "humidityOffset") || (name == "tempOffset"))) { + val = (val + 256) + } + + state[name] = val + logDebug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${val}" + } else { + logDebug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + logDebug "Unhandled zwaveEvent: ${cmd}" +} + +void refreshSyncStatus() { + int changes = pendingChanges + sendEvent(name: "syncStatus", value: (changes ? "${changes} Pending Changes" : "Synced"), displayed: false) +} + +Integer getPendingChanges() { + int configChanges = configParams.count { name, param -> + (getSettingVal(name) != getStoredVal(name)) + } + int pendingWakeUpInterval = (state.wakeUpInterval != wakeUpInterval ? 1 : 0) + return (configChanges + pendingWakeUpInterval) +} + +Integer getSettingVal(String name) { + Integer value = safeToInt(settings[name], null) + if ((value == null) && (getStoredVal(name) != null)) { + return configParams[name].defaultVal + } else { + return value + } +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +void logDebug(String msg) { + if (state.debugLoggingEnabled != false) { + log.debug "$msg" + } +} \ No newline at end of file From 3ce2797fde2cdec86809c229ed1a45ecfec803f9 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 15 Feb 2022 09:35:25 +0900 Subject: [PATCH 357/422] DevWs for SHINA SYSTEM containing containing SiHAS Zigbee Metering Plug (#77702) * DevWs for SHINA SYSTEM containing containing SiHAS Zigbee Metering Plug * Update sihas-zigbee-metering-plug.groovy Formatting: comma between clusterInt and attrInt --- .../sihas-zigbee-metering-plug.groovy | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy diff --git a/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy b/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy new file mode 100644 index 00000000000..c400f0255c0 --- /dev/null +++ b/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy @@ -0,0 +1,175 @@ +/** + * Copyright 2022 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. + * + */ +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition (name: "SiHAS Zigbee Metering Plug", namespace: "shinasys", author: "SHINA SYSTEM", mnmn: "SmartThingsCommunity", ocfDeviceType: "oic.d.smartplug", vid: "12d61425-2258-376a-beee-7a69fbc0d9fe") { + capability "Energy Meter" + capability "Power Meter" + capability "Refresh" + capability "Health Check" + capability "Sensor" + capability "Configuration" + capability "Voltage Measurement" + capability "afterguide46998.currentMeasurement" + capability "afterguide46998.frequencyMeasurement" + capability "afterguide46998.powerfactorMeasurement" + capability "Temperature Measurement" + capability "Switch" + + fingerprint profileId: "0104", manufacturer: "ShinaSystem", model: "CCM-300Z2", deviceJoinName: "SiHAS Outlet" // SIHAS Zigbee Metering Plug 01 0104 0000 01 06 0000 0004 0003 0006 0B04 0702 02 0004 0019 + } +} + +def getATTRIBUTE_READING_INFO_SET() { 0x0000 } +def getATTRIBUTE_HISTORICAL_CONSUMPTION() { 0x0400 } +def getATTRIBUTE_ACTIVE_POWER() { 0x050B } +def getATTRIBUTE_FREQUENCY() { 0x0300 } +def getATTRIBUTE_VOLTAGE() { 0x0505 } +def getATTRIBUTE_CURRENT() { 0x0508 } +def getATTRIBUTE_POWERFACTOR() { 0x0510 } +def getTEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE() { 0x0000 } + +def convertHexToInt24Bit(value) { + int result = zigbee.convertHexToInt(value) + if (result & 0x800000) { + result |= 0xFF000000 + } + return result +} + +def parse(String description) { + log.debug "description is $description" + def event = zigbee.getEvent(description) + def descMap = zigbee.parseDescriptionAsMap(description) + + if (event) { + log.info "event enter:$event" + if (event.name == "switch") { + return sendEvent(event) + } else if (event.name == "temperature") { + return sendEvent(event) + } + } + + if (descMap) { + List result = [] + log.debug "Desc Map: $descMap" + + List attrData = [[clusterInt: descMap.clusterInt, attrInt: descMap.attrInt, value: descMap.value, isValidForDataType: descMap.isValidForDataType]] + descMap.additionalAttrs.each { + attrData << [clusterInt: descMap.clusterInt, attrInt: it.attrInt, value: it.value, isValidForDataType: it.isValidForDataType] + } + attrData.each { + def map = [:] + if (it.isValidForDataType && (it.value != null)) { + if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_HISTORICAL_CONSUMPTION) { + log.debug "meter" + map.name = "power" + map.value = convertHexToInt24Bit(it.value)/powerDivisor + map.unit = "W" + } else if (it.clusterInt == zigbee.SIMPLE_METERING_CLUSTER && it.attrInt == ATTRIBUTE_READING_INFO_SET) { + log.debug "energy" + map.name = "energy" + map.value = zigbee.convertHexToInt(it.value)/energyDivisor + map.unit = "kWh" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_FREQUENCY) { + log.debug "frequency" + map.name = "frequency" + map.value = zigbee.convertHexToInt(it.value)/frequencyDivisor + map.unit = "Hz" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_VOLTAGE) { + log.debug "voltage" + map.name = "voltage" + map.value = zigbee.convertHexToInt(it.value)/voltageDivisor + map.unit = "V" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_CURRENT) { + log.debug "current" + map.name = "current" + map.value = zigbee.convertHexToInt(it.value)/currentDivisor + map.unit = "A" + } else if (it.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER && it.attrInt == ATTRIBUTE_POWERFACTOR) { + log.debug "power factor" + map.name = "powerFactor" + map.value = (byte) zigbee.convertHexToInt(it.value)/powerFactorDivisor + map.unit = "%" + } else if (it.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && it.attrInt == TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) { + log.debug "temperature" + map.name = "temperature" + map.unit = getTemperatureScale() + map.value = zigbee.parseHATemperatureValue("temperature: " + (zigbee.convertHexToInt(it.value)), "temperature: ", tempScale) + log.debug "${device.displayName}: Reported temperature is ${map.value}°$map.unit" + } + } + + if (map) { + result << createEvent(map) + } + log.debug "Parse returned $map" + } + return result + } +} + +def off() { + zigbee.off() +} + +def on() { + zigbee.on() +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return refresh() +} + +def refresh() { + log.debug "refresh " + zigbee.onOffRefresh() + + zigbee.simpleMeteringPowerRefresh() + + zigbee.readAttribute(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_FREQUENCY) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_VOLTAGE) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_CURRENT) + + zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_POWERFACTOR) + + zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE) +} + +def configure() { + def configCmds = [] + // this device will send instantaneous demand and current summation delivered every 1 minute + sendEvent(name: "checkInterval", value: 2 * 60 + 10 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + log.debug "Configuring Reporting" + configCmds = zigbee.onOffConfig() + + zigbee.simpleMeteringPowerConfig() + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 5, 600, 1) + + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_FREQUENCY, DataType.UINT16, 10, 600, 3) + /* 3 unit : 0.3Hz */ + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_VOLTAGE, DataType.UINT16, 5, 600, 3) + /* 3 unit : 0.3V */ + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_CURRENT, DataType.UINT16, 5, 600, 1) + /* 1 unit : 0.01A */ + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_POWERFACTOR, DataType.INT8, 10, 600, 1) + /* 1 unit : 0.1% */ + zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 20, 300, 10 /* 1 uint : 0.1C */) + return configCmds + refresh() +} + +private getActivePowerDivisor() { 1 } +private getPowerDivisor() { 1 } +private getEnergyDivisor() { 1000 } +private getFrequencyDivisor() { 10 } +private getVoltageDivisor() { 10 } +private getCurrentDivisor() { 100 } +private getPowerFactorDivisor() { 1 } From cadf20d8b4e94a36ad85895f0db2110baf6454fe Mon Sep 17 00:00:00 2001 From: jahartogsveld <82935890+jahartogsveld@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:11:54 +0100 Subject: [PATCH 358/422] Add Danalock Zigbee 3.0 (#77753) --- devicetypes/smartthings/zigbee-lock.src/README.md | 1 + devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 1 + 2 files changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-lock.src/README.md b/devicetypes/smartthings/zigbee-lock.src/README.md index 8dee77c9b7c..f54fc71a45c 100644 --- a/devicetypes/smartthings/zigbee-lock.src/README.md +++ b/devicetypes/smartthings/zigbee-lock.src/README.md @@ -13,6 +13,7 @@ Works with: * Yale Push Button Deadbolt Lock * Yale Touch Screen Deadbolt Lock * Yale Push Button Lever Lock +* Danalock Door Lock ## Table of contents diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 299840f8888..86d164d91ae 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -49,6 +49,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000202", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YDF40 fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "0700000001", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 fingerprint profileId: "0104", inClusters: "0000,0001,0003,0101", outClusters: "0000,0001,0003,0101", manufacturer: "Datek", model: "ID Lock 150", deviceJoinName: "ID Lock Door Lock" //ID Lock 150 Zigbee Module by Datek + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,0020,0101", outClusters: "0019", manufacturer: "Danalock", model: "V3-BTZBE", deviceJoinName: "Danalock Door Lock" } tiles(scale: 2) { From 0fec6f9b8a7af0586bf50e380656c9f0da8c020c Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Thu, 17 Feb 2022 04:14:43 +0900 Subject: [PATCH 359/422] WWST-7794, Add a DTH code for ABL CPX Lighting(Light with a motion sensor) (#77259) * Create led-cpx-light.groovy First commit * Create led-cpx-light.groovy First commit * Update led-cpx-light.groovy Line aligning * Delete led-cpx-light.groovy Move to other folder * Create README.md First commit * Create led-cpx-motion-sensor-child.groovy First commit * Update led-cpx-motion-sensor-child.groovy Line aligning * Update README.md Add definition of motion sensor capability * Edit codes Edit codes * Edit codes Edit codes * Edit codes Edit codes * Update led-cpx-light.groovy Delete configuration in the refresh function. Delete color temperature naming function. * Update led-cpx-light.groovy Edited conditional statement at line 73 in the parse function. * Update led-cpx-light.groovy Aligning codes. * Update led-cpx-light.groovy Edit try-catch in line 151 * Update led-cpx-light.groovy Edited and aligned codes. * Update led-cpx-motion-sensor-child.groovy Edited and aligned codes. * Update README.md Edited contents. * Update led-cpx-motion-sensor-child.groovy Removed unnecessary spaces. * Update led-cpx-light.groovy Added magic numbers and edited codes. * Update led-cpx-light.groovy Aligned codes. * Update led-cpx-light.groovy Cleaned up codes. * Update led-cpx-light.groovy Edited codes. * Update led-cpx-light.groovy Edited codes * Update zigbee-rgbw-bulb.groovy Changed the "deviceJoinName" in line @43 from "Juno Connect" to "RetroBasics RGBW" by the request of customer. * Update led-cpx-light.groovy Deleted after "runLoaclly". * Update led-cpx-light.groovy Edited codes * Update led-cpx-light.groovy Delete "runlocally:true" and add a rule to the event in parser. * Update led-cpx-light.groovy Aligned codes. * Update led-cpx-light.groovy Edited codes. * Update led-cpx-light.groovy Edited the magic number. * Update led-cpx-light.groovy Added a separate zigbeeMap for child event : zigbeeMap_child in line91. * Update led-cpx-light.groovy Aligned send child event block. --- .../zigbee-motion-sensor-light.src/README.md | 31 ++++ .../led-cpx-light.groovy | 171 ++++++++++++++++++ .../led-cpx-motion-sensor-child.groovy | 39 ++++ .../zigbee-rgbw-bulb.groovy | 2 +- 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 devicetypes/smartthings/zigbee-motion-sensor-light.src/README.md create mode 100644 devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy create mode 100644 devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-motion-sensor-child.groovy diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/README.md b/devicetypes/smartthings/zigbee-motion-sensor-light.src/README.md new file mode 100644 index 00000000000..9be7d475f69 --- /dev/null +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/README.md @@ -0,0 +1,31 @@ +# ZigBee CPX Smart Panel Light + +Cloud Execution + +Works with: + +* ABL Lithonia +* Samsung LED + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) + +## Capabilities + +* **Actuator** - represents that a Device has commands* +* **Color Temperaturer** - It represents color temperature capability measured in degree Kelvin. +* **Configuration** - _configure()_ command called when device is installed or device preferences updated. +* **Health Check** - indicates ability to get device health notifications +* **Refresh** - _refresh()_ command for status updates +* **Switch** - can detect state (possible values: on/off) +* **Switch Level** - represents current light level, usually 0-100 in percent +* **Motion Sensor** - can detect motion + +## Device Health + +Zigbee Bulb with reporting interval of 5 mins. +SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE` + +*__12min__ checkInterval diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy new file mode 100644 index 00000000000..074c8021d35 --- /dev/null +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy @@ -0,0 +1,171 @@ +/** + * Copyright 2022 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. + * + * LED CPX light + * + * Author: SAMSUNG LED + * Date: 2022-01-05 + */ + +metadata { + definition(name: "LED CPX light", namespace: "SAMSUNG LED", author: "SAMSUNG LED") { + + capability "Actuator" + capability "Color Temperature" + capability "Configuration" + capability "Health Check" + capability "Refresh" + capability "Switch" + capability "Switch Level" + + // ABL Lithonia + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Lithonia", model: "ABL-LIGHTSENSOR-Z-001", deviceJoinName: "CPX Smart Panel Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" + + // Samsung LED + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-004", deviceJoinName: "ITM CPX Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" + } + + // UI tile definitions + tiles(scale: 2) { + multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) { + tileAttribute("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#00A0DC", nextState: "turningOff" + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff", nextState: "turningOn" + attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#00A0DC", nextState: "turningOff" + attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff", nextState: "turningOn" + } + tileAttribute("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action: "switch level.setLevel" + } + } + + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh" + } + + controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range: "(2700..6500)") { + state "colorTemperature", action: "color temperature.setColorTemperature" + } + + main(["switch"]) + details(["switch", "switchLevel", "colorTempSliderControl", "refresh"]) + } +} + +private getMOTION_CLUSTER() { 0x0406 } +private getMOTION_STATUS_ATTRIBUTE() { 0x0000 } +private getON_OFF_CLUSTER() { 0x0006 } +private getCONFIGURE_REPORTING_RESPONSE() { 0x07 } +private getON_DATA() { 0x01 } +private getOFF_DATA() { 0x00 } + +def parse(String description) { + def event = zigbee.getEvent(description) + def zigbeeMap = zigbee.parseDescriptionAsMap(description) + + if (event) { + if (zigbeeMap.clusterInt == ON_OFF_CLUSTER && (zigbeeMap.data[0] != ON_DATA || zigbeeMap.data[0] != OFF_DATA)) { + return + } + + if (!(event.name == "level" && event.value == 0)) { + sendEvent(event) + } + } else { + def cluster = zigbee.parse(description) + + if (cluster && cluster.clusterId == ON_OFF_CLUSTER && cluster.command == CONFIGURE_REPORTING_RESPONSE) { + if (cluster.data[0] == 0x00) { + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + } else { + if (zigbeeMap.clusterInt == MOTION_CLUSTER && zigbeeMap.attrInt == MOTION_STATUS_ATTRIBUTE) { + def childDevice = getChildDevices()?.find { + it.device.deviceNetworkId == "${device.deviceNetworkId}:1" + } + def event_child = zigbeeMap.value.endsWith("01") ? createEvent(name: "motion", value: "active") : createEvent(name: "motion", value: "inactive") + childDevice.sendEvent(event_child) + } + } + } +} + +def off() { + zigbee.off() +} + +def on() { + zigbee.on() +} + +def setLevel(value, rate=null) { + zigbee.setLevel(value) +} + +def configure() { + zigbee.configureReporting(MOTION_CLUSTER, MOTION_STATUS_ATTRIBUTE, 0x18, 30, 600, null) + + zigbee.onOffConfig() + + zigbee.levelConfig() + + refresh() +} + +def updated() { + if (!childDevices) { + addChildSensor() + } +} + +def ping() { + return zigbee.levelRefresh() +} + +def refresh() { + zigbee.readAttribute(MOTION_CLUSTER, MOTION_STATUS_ATTRIBUTE) + + zigbee.onOffRefresh() + + zigbee.levelRefresh() + + zigbee.colorTemperatureRefresh() +} + +def setColorTemperature(value) { + value = value as Integer + + zigbee.setColorTemperature(value) + + zigbee.on() + + zigbee.colorTemperatureRefresh() +} + +def installed() { + addChildSensor() +} + +def addChildSensor() { + def componentLabel + def childDevice + + if (device.displayName.endsWith(' Light') || device.displayName.endsWith(' light')) { + componentLabel = "${device.displayName[0..-6]} Motion sensor" + } else { + componentLabel = "$device.displayName Motion sensor" + } + + try { + String dni = "${device.deviceNetworkId}:1" + childDevice = addChildDevice("ITM CPX Motion sensor child", dni, device.hub.id, [completedSetup: true, label: "${componentLabel}", isComponent: false]) + if (childDevice != null) { + childDevice.sendEvent(name: "motion", value: "inactive") + } + } catch (e) { + log.warn "Failed to add ITM Fan Controller - $e" + } + + return childDevice +} diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-motion-sensor-child.groovy b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-motion-sensor-child.groovy new file mode 100644 index 00000000000..00afdb0827e --- /dev/null +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-motion-sensor-child.groovy @@ -0,0 +1,39 @@ +/** + * Copyright 2022 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. + * + * ITM CPX Motion sensor child + * + * Author: SAMSUNG LED + * Date: 2022-01-05 + */ + +metadata { + definition (name: "ITM CPX Motion sensor child", namespace: "SAMSUNG LED", author: "SAMSUNG LED", ocfDeviceType: "x.com.st.d.sensor.motion") { + capability "Motion Sensor" + capability "Refresh" + capability "Sensor" + } + + 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" + } + } + 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", "refresh"]) + } +} diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index 65dadbc811a..8177e505120 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -40,7 +40,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-002", deviceJoinName: "Samsung Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-002" //ITM RGBW // ABL - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-201", deviceJoinName: "Juno Connect", mnmn: "SmartThingsCommunity", vid: "0c0d8ed8-d536-324c-9b80-d4705a55e4df" //E-series + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-201", deviceJoinName: "RetroBasics RGBW", mnmn: "SmartThingsCommunity", vid: "0c0d8ed8-d536-324c-9b80-d4705a55e4df" //E-series // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 1000", outClusters: "0019", deviceId: "010D", manufacturer: "AduroSmart Eria", model: "AD-RGBW3001", deviceJoinName: "Eria Light" //Eria ZigBee RGBW Bulb From 88d12fb86b0beb89fc44fa1a8e0fe6542dd62676 Mon Sep 17 00:00:00 2001 From: jeremelau <38304013+JeremeLau@users.noreply.github.com> Date: Thu, 17 Feb 2022 15:47:38 +0800 Subject: [PATCH 360/422] DevWs for YookSmart containing containing ZigBee Window Shade Battery (#76356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for YookSmart containing containing ZigBee Window Shade Battery * commit battery report * add battery support for yooksmart * version commit * modify for the master * add raw description in the comment * remove useless tiles * restore ‘|| currentLevel == lastLevel’ in report event. But this will cause UI block when customers try to re-open the blinds when they are already open Co-authored-by: Bill Nie --- .../zigbee-window-shade-battery.groovy | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index a67d14754f7..d2bd8427bca 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -34,9 +34,9 @@ metadata { fingerprint manufacturer: "IKEA of Sweden", model: "FYRTUR block-out roller blind", deviceJoinName: "IKEA Window Treatment" // raw description 01 0104 0202 01 09 0000 0001 0003 0004 0005 0020 0102 1000 FC7C 02 0019 1000 //IKEA FYRTUR Blinds // Yookee yooksmart - fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Yookee", model: "D10110", deviceJoinName: "Yookee Window Treatment" - fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "yooksmart", model: "D10110", deviceJoinName: "yooksmart Window Treatment" - + fingerprint manufacturer: "Yookee", model: "D10110", deviceJoinName: "Yookee Window Treatment" // raw description 01 0104 0202 01 07 0000 0001 0003 0004 0005 0020 0102 02 0003 0019 + fingerprint manufacturer: "yooksmart", model: "D10110", deviceJoinName: "yooksmart Window Treatment" // raw description 01 0104 0202 01 07 0000 0001 0003 0004 0005 0020 0102 02 0003 0019 + // SMARTWINGS fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Smartwings", model: "WM25/L-Z", deviceJoinName: "Smartwings Window Treatment" } @@ -178,7 +178,12 @@ def updateFinalState() { } def batteryPercentageEventHandler(batteryLevel) { + log.debug "batteryLevel: ${batteryLevel}" + if (batteryLevel != null) { + if (isYooksmartOrYookee()) { + batteryLevel = batteryLevel >> 1 + } batteryLevel = Math.min(100, Math.max(0, batteryLevel)) sendEvent([name: "battery", value: batteryLevel, unit: "%", descriptionText: "{{ device.displayName }} battery was {{ value }}%"]) } @@ -322,7 +327,7 @@ def shouldInvertLiftPercentage() { } def reportsBatteryPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() || isSmartwings() + return isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() || isSmartwings() } def isIkeaKadrilj() { From f5dd14065097d7fb288b86a04cda0d6215f9f774 Mon Sep 17 00:00:00 2001 From: greens Date: Thu, 24 Feb 2022 12:12:22 -0800 Subject: [PATCH 361/422] Add translation strings that were omitted. --- .../i18n/messages.properties | 580 ++++++++++++------ 1 file changed, 386 insertions(+), 194 deletions(-) diff --git a/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties b/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties index f4577247cf5..5d0b64f8a75 100644 --- a/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/fibaro-smoke-sensor.src/i18n/messages.properties @@ -349,54 +349,6 @@ '''Sound notifications status'''.tr=Sesli bildirimler '''Sound notifications status'''.uk=Звукові сповіщення '''Sound notifications status'''.vi=Thông báo âm thanh -'''24 hours'''.en=24 hours -'''24 hours'''.en-gb=24 hours -'''24 hours'''.en-us=24 hours -'''24 hours'''.en-ca=24 hours -'''24 hours'''.sq=24 orë -'''24 hours'''.ar=٢٤ ساعة -'''24 hours'''.be=24 гадзіны -'''24 hours'''.sr-ba=24 sata -'''24 hours'''.bg=24 часа -'''24 hours'''.ca=24 hores -'''24 hours'''.zh-cn=24 小时 -'''24 hours'''.zh-hk=24 小時 -'''24 hours'''.zh-tw=24 小時 -'''24 hours'''.hr=24 sata -'''24 hours'''.cs=24 hodin -'''24 hours'''.da=24 timer -'''24 hours'''.nl=24 uur -'''24 hours'''.et=24 tundi -'''24 hours'''.fi=24 tuntia -'''24 hours'''.fr=24 heures -'''24 hours'''.fr-ca=24 heures -'''24 hours'''.de=24 Stunden -'''24 hours'''.el=24 ώρες -'''24 hours'''.iw=24 שעות -'''24 hours'''.hi-in=24 घंटे -'''24 hours'''.hu=24 óra -'''24 hours'''.is=Sólarhringur -'''24 hours'''.in=24 jam -'''24 hours'''.it=24 ore -'''24 hours'''.ja=24時間 -'''24 hours'''.ko=24시간 -'''24 hours'''.lv=24 stundas -'''24 hours'''.lt=24 val. -'''24 hours'''.ms=24 jam -'''24 hours'''.no=24 timer -'''24 hours'''.pl=24 godziny -'''24 hours'''.pt=24 horas -'''24 hours'''.ro=24 de ore -'''24 hours'''.ru=24 часа -'''24 hours'''.sr=24 sata -'''24 hours'''.sk=24 hodín -'''24 hours'''.sl=24 ur -'''24 hours'''.es=24 horas -'''24 hours'''.sv=24 timmar -'''24 hours'''.th=24 ชั่วโมง -'''24 hours'''.tr=24 saat -'''24 hours'''.uk=24 години -'''24 hours'''.vi=24 giờ '''Overheat temperature threshold'''.en=Overheat temperature threshold '''Overheat temperature threshold'''.en-gb=Overheat temperature threshold '''Overheat temperature threshold'''.en-us=Overheat temperature threshold @@ -686,54 +638,6 @@ '''To check smoke detection state'''.tr=Duman algılama durumu kontrol ediliyor '''To check smoke detection state'''.uk=Перевірка стану датчика диму '''To check smoke detection state'''.vi=Kiểm tra trạng thái phát hiện khói -'''5 minutes'''.en=5 minutes -'''5 minutes'''.en-gb=5 minutes -'''5 minutes'''.en-us=5 minutes -'''5 minutes'''.en-ca=5 minutes -'''5 minutes'''.sq=5 minuta -'''5 minutes'''.ar=٥ دقائق -'''5 minutes'''.be=5 хвілін -'''5 minutes'''.sr-ba=5 minuta -'''5 minutes'''.bg=5 минути -'''5 minutes'''.ca=5 minuts -'''5 minutes'''.zh-cn=5 分钟 -'''5 minutes'''.zh-hk=5 分鐘 -'''5 minutes'''.zh-tw=5 分鐘 -'''5 minutes'''.hr=5 minuta -'''5 minutes'''.cs=5 minut -'''5 minutes'''.da=5 minutter -'''5 minutes'''.nl=5 minuten -'''5 minutes'''.et=5 minutit -'''5 minutes'''.fi=5 minuuttia -'''5 minutes'''.fr=5 minutes -'''5 minutes'''.fr-ca=5 minutes -'''5 minutes'''.de=5 Minuten -'''5 minutes'''.el=5 λεπτά -'''5 minutes'''.iw=5 דקות -'''5 minutes'''.hi-in=5 मिनट -'''5 minutes'''.hu=5 perc -'''5 minutes'''.is=5 mínútur -'''5 minutes'''.in=5 menit -'''5 minutes'''.it=5 minuti -'''5 minutes'''.ja=5分 -'''5 minutes'''.ko=5분 -'''5 minutes'''.lv=5 minūtes -'''5 minutes'''.lt=5 minutės -'''5 minutes'''.ms=5 minit -'''5 minutes'''.no=5 minutter -'''5 minutes'''.pl=5 minut -'''5 minutes'''.pt=5 minutos -'''5 minutes'''.ro=5 minute -'''5 minutes'''.ru=5 минут -'''5 minutes'''.sr=5 minuta -'''5 minutes'''.sk=5 minút -'''5 minutes'''.sl=5 minut -'''5 minutes'''.es=5 minutos -'''5 minutes'''.sv=5 minuter -'''5 minutes'''.th=5 นาที -'''5 minutes'''.tr=5 dakika -'''5 minutes'''.uk=5 хвилин -'''5 minutes'''.vi=5 phút '''Exceeding temperature threshold'''.en=Temperature threshold exceeded '''Exceeding temperature threshold'''.en-gb=Temperature threshold exceeded '''Exceeding temperature threshold'''.en-us=Temperature threshold exceeded @@ -782,55 +686,6 @@ '''Exceeding temperature threshold'''.tr=Sıcaklık eşiği aşıldı '''Exceeding temperature threshold'''.uk=Перевищено температурний поріг '''Exceeding temperature threshold'''.vi=Đã vượt ngưỡng nhiệt độ -'''30 minutes'''.en=30 minutes -'''30 minutes'''.en-gb=30 minutes -'''30 minutes'''.en-us=30 minutes -'''30 minutes'''.en-ca=30 minutes -'''30 minutes'''.en-ph=30 minutes -'''30 minutes'''.sq=30 minuta -'''30 minutes'''.ar=٣٠ دقيقة -'''30 minutes'''.be=30 хвілін -'''30 minutes'''.sr-ba=30 minuta -'''30 minutes'''.bg=30 минути -'''30 minutes'''.ca=30 minuts -'''30 minutes'''.zh-cn=30 分钟 -'''30 minutes'''.zh-hk=30 分鐘 -'''30 minutes'''.zh-tw=30 分鐘 -'''30 minutes'''.hr=30 minuta -'''30 minutes'''.cs=30 minut -'''30 minutes'''.da=30 minutter -'''30 minutes'''.nl=30 minuten -'''30 minutes'''.et=30 minutit -'''30 minutes'''.fi=30 minuuttia -'''30 minutes'''.fr=30 minutes -'''30 minutes'''.fr-ca=30 minutes -'''30 minutes'''.de=30 Minuten -'''30 minutes'''.el=30 λεπτά -'''30 minutes'''.iw=30 דקות -'''30 minutes'''.hi-in=30 मिनट -'''30 minutes'''.hu=30 perc -'''30 minutes'''.is=30 mínútur -'''30 minutes'''.in=30 menit -'''30 minutes'''.it=30 minuti -'''30 minutes'''.ja=30分 -'''30 minutes'''.ko=30분 -'''30 minutes'''.lv=30 minūtes -'''30 minutes'''.lt=30 minučių -'''30 minutes'''.ms=30 minit -'''30 minutes'''.no=30 minutter -'''30 minutes'''.pl=30 minut -'''30 minutes'''.pt=30 minutos -'''30 minutes'''.ro=30 de minute -'''30 minutes'''.ru=30 минут -'''30 minutes'''.sr=30 minuta -'''30 minutes'''.sk=30 minút -'''30 minutes'''.sl=30 min -'''30 minutes'''.es=30 minutos -'''30 minutes'''.sv=30 minuter -'''30 minutes'''.th=30 นาที -'''30 minutes'''.tr=30 dakika -'''30 minutes'''.uk=30 хвилин -'''30 minutes'''.vi=30 phút '''Instructions'''.en=Getting started '''Instructions'''.en-gb=Getting started '''Instructions'''.en-us=Getting started @@ -1314,55 +1169,6 @@ '''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.tr=Yükleme işleminden sonra, cihazın durumunu ve yapılandırmasını güncellemek için Fibaro Duman Sensörünüzde B tuşuna basın. '''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.uk=Після встановлення натисніть кнопку B на датчику диму Fibaro, щоб оновити стан і конфігурацію пристрою. '''After successful installation, please click B-button at the Fibaro Smoke Sensor to update device status and configuration'''.vi=Sau khi cài đặt, nhấn phím B trên Cảm biến khói Fibaro của bạn để cập nhật trạng thái và cấu hình của thiết bị. -'''1 hour'''.en=1 hour -'''1 hour'''.en-gb=1 hour -'''1 hour'''.en-us=1 hour -'''1 hour'''.en-ca=1 hour -'''1 hour'''.en-ph=1 hour -'''1 hour'''.sq=1 orë -'''1 hour'''.ar=ساعة واحدة -'''1 hour'''.be=1 гадзіна -'''1 hour'''.sr-ba=Jedan sat -'''1 hour'''.bg=1 час -'''1 hour'''.ca=1 hora -'''1 hour'''.zh-cn=1 小时 -'''1 hour'''.zh-hk=1 小時 -'''1 hour'''.zh-tw=1 小時 -'''1 hour'''.hr=1 sat -'''1 hour'''.cs=1 hodina -'''1 hour'''.da=1 time -'''1 hour'''.nl=1 uur -'''1 hour'''.et=1 tund -'''1 hour'''.fi=1 tunti -'''1 hour'''.fr=1 heure -'''1 hour'''.fr-ca=1 heure -'''1 hour'''.de=1 Stunde -'''1 hour'''.el=1 ώρα -'''1 hour'''.iw=שעה אחת -'''1 hour'''.hi-in=1 घंटा -'''1 hour'''.hu=1 óra -'''1 hour'''.is=1 klukkustund -'''1 hour'''.in=1 jam -'''1 hour'''.it=1 ora -'''1 hour'''.ja=1時間 -'''1 hour'''.ko=1시간 -'''1 hour'''.lv=1 stunda -'''1 hour'''.lt=1 val. -'''1 hour'''.ms=1 jam -'''1 hour'''.no=1 time -'''1 hour'''.pl=1 godzina -'''1 hour'''.pt=1 hora -'''1 hour'''.ro=1 oră -'''1 hour'''.ru=1 час -'''1 hour'''.sr=Jedan sat -'''1 hour'''.sk=1 hodina -'''1 hour'''.sl=1 h -'''1 hour'''.es=1 hora -'''1 hour'''.sv=En timme -'''1 hour'''.th=1 ชั่วโมง -'''1 hour'''.tr=1 saat -'''1 hour'''.uk=1 година -'''1 hour'''.vi=1 giờ '''Visual indicator notifications status'''.en=Visual indicator notifications '''Visual indicator notifications status'''.en-gb=Visual indicator notifications '''Visual indicator notifications status'''.en-us=Visual indicator notifications @@ -1460,4 +1266,390 @@ '''All'''.tr=Tümü '''All'''.uk=Усі '''All'''.vi=Tất cả +'''5 minutes'''.en=5 minutes +'''5 minutes'''.en-gb=5 minutes +'''5 minutes'''.en-us=5 minutes +'''5 minutes'''.en-ca=5 minutes +'''5 minutes'''.sq=5 minuta +'''5 minutes'''.ar=٥ دقائق +'''5 minutes'''.be=5 хвілін +'''5 minutes'''.sr-ba=5 minuta +'''5 minutes'''.bg=5 минути +'''5 minutes'''.ca=5 minuts +'''5 minutes'''.zh-cn=5 分钟 +'''5 minutes'''.zh-hk=5 分鐘 +'''5 minutes'''.zh-tw=5 分鐘 +'''5 minutes'''.hr=5 minuta +'''5 minutes'''.cs=5 minut +'''5 minutes'''.da=5 minutter +'''5 minutes'''.nl=5 minuten +'''5 minutes'''.et=5 minutit +'''5 minutes'''.fi=5 minuuttia +'''5 minutes'''.fr=5 minutes +'''5 minutes'''.fr-ca=5 minutes +'''5 minutes'''.de=5 Minuten +'''5 minutes'''.el=5 λεπτά +'''5 minutes'''.iw=5 דקות +'''5 minutes'''.hi-in=5 मिनट +'''5 minutes'''.hu=5 perc +'''5 minutes'''.is=5 mínútur +'''5 minutes'''.in=5 menit +'''5 minutes'''.it=5 minuti +'''5 minutes'''.ja=5分 +'''5 minutes'''.ko=5분 +'''5 minutes'''.lv=5 minūtes +'''5 minutes'''.lt=5 minutės +'''5 minutes'''.ms=5 minit +'''5 minutes'''.no=5 minutter +'''5 minutes'''.pl=5 minut +'''5 minutes'''.pt=5 minutos +'''5 minutes'''.ro=5 minute +'''5 minutes'''.ru=5 минут +'''5 minutes'''.sr=5 minuta +'''5 minutes'''.sk=5 minút +'''5 minutes'''.sl=5 minut +'''5 minutes'''.es=5 minutos +'''5 minutes'''.sv=5 minuter +'''5 minutes'''.th=5 นาที +'''5 minutes'''.tr=5 dakika +'''5 minutes'''.uk=5 хвилин +'''5 minutes'''.vi=5 phút +'''15 minutes'''.en=15 minutes +'''15 minutes'''.en-gb=15 minutes +'''15 minutes'''.en-us=15 minutes +'''15 minutes'''.en-ca=15 minutes +'''15 minutes'''.sq=15 minuta +'''15 minutes'''.ar=١٥ دقيقة +'''15 minutes'''.be=15 хвілін +'''15 minutes'''.sr-ba=15 minuta +'''15 minutes'''.bg=15 минути +'''15 minutes'''.ca=15 minuts +'''15 minutes'''.zh-cn=15 分钟 +'''15 minutes'''.zh-hk=15 分鐘 +'''15 minutes'''.zh-tw=15 分鐘 +'''15 minutes'''.hr=15 minuta +'''15 minutes'''.cs=15 minut +'''15 minutes'''.da=15 minutter +'''15 minutes'''.nl=15 minuten +'''15 minutes'''.et=15 minutit +'''15 minutes'''.fi=15 minuuttia +'''15 minutes'''.fr=15 minutes +'''15 minutes'''.fr-ca=15 minutes +'''15 minutes'''.de=15 Minuten +'''15 minutes'''.el=15 λεπτά +'''15 minutes'''.iw=15 דקות +'''15 minutes'''.hi-in=15 मिनट +'''15 minutes'''.hu=15 perc +'''15 minutes'''.is=15 mínútur +'''15 minutes'''.in=15 menit +'''15 minutes'''.it=15 minuti +'''15 minutes'''.ja=15分 +'''15 minutes'''.ko=15분 +'''15 minutes'''.lv=15 minūtes +'''15 minutes'''.lt=15 min. +'''15 minutes'''.ms=15 minit +'''15 minutes'''.no=15 minutter +'''15 minutes'''.pl=15 minut +'''15 minutes'''.pt=15 minutos +'''15 minutes'''.ro=15 minute +'''15 minutes'''.ru=15 минут +'''15 minutes'''.sr=15 minuta +'''15 minutes'''.sk=15 minút +'''15 minutes'''.sl=15 minut +'''15 minutes'''.es=15 minutos +'''15 minutes'''.sv=15 minuter +'''15 minutes'''.th=15 นาที +'''15 minutes'''.tr=15 dakika +'''15 minutes'''.uk=15 хвилин +'''15 minutes'''.vi=15 phút +'''30 minutes'''.en=30 minutes +'''30 minutes'''.en-gb=30 minutes +'''30 minutes'''.en-us=30 minutes +'''30 minutes'''.en-ca=30 minutes +'''30 minutes'''.en-ph=30 minutes +'''30 minutes'''.sq=30 minuta +'''30 minutes'''.ar=٣٠ دقيقة +'''30 minutes'''.be=30 хвілін +'''30 minutes'''.sr-ba=30 minuta +'''30 minutes'''.bg=30 минути +'''30 minutes'''.ca=30 minuts +'''30 minutes'''.zh-cn=30 分钟 +'''30 minutes'''.zh-hk=30 分鐘 +'''30 minutes'''.zh-tw=30 分鐘 +'''30 minutes'''.hr=30 minuta +'''30 minutes'''.cs=30 minut +'''30 minutes'''.da=30 minutter +'''30 minutes'''.nl=30 minuten +'''30 minutes'''.et=30 minutit +'''30 minutes'''.fi=30 minuuttia +'''30 minutes'''.fr=30 minutes +'''30 minutes'''.fr-ca=30 minutes +'''30 minutes'''.de=30 Minuten +'''30 minutes'''.el=30 λεπτά +'''30 minutes'''.iw=30 דקות +'''30 minutes'''.hi-in=30 मिनट +'''30 minutes'''.hu=30 perc +'''30 minutes'''.is=30 mínútur +'''30 minutes'''.in=30 menit +'''30 minutes'''.it=30 minuti +'''30 minutes'''.ja=30分 +'''30 minutes'''.ko=30분 +'''30 minutes'''.lv=30 minūtes +'''30 minutes'''.lt=30 minučių +'''30 minutes'''.ms=30 minit +'''30 minutes'''.no=30 minutter +'''30 minutes'''.pl=30 minut +'''30 minutes'''.pt=30 minutos +'''30 minutes'''.ro=30 de minute +'''30 minutes'''.ru=30 минут +'''30 minutes'''.sr=30 minuta +'''30 minutes'''.sk=30 minút +'''30 minutes'''.sl=30 min +'''30 minutes'''.es=30 minutos +'''30 minutes'''.sv=30 minuter +'''30 minutes'''.th=30 นาที +'''30 minutes'''.tr=30 dakika +'''30 minutes'''.uk=30 хвилин +'''30 minutes'''.vi=30 phút +'''1 hour'''.en=1 hour +'''1 hour'''.en-gb=1 hour +'''1 hour'''.en-us=1 hour +'''1 hour'''.en-ca=1 hour +'''1 hour'''.en-ph=1 hour +'''1 hour'''.sq=1 orë +'''1 hour'''.ar=ساعة واحدة +'''1 hour'''.be=1 гадзіна +'''1 hour'''.sr-ba=Jedan sat +'''1 hour'''.bg=1 час +'''1 hour'''.ca=1 hora +'''1 hour'''.zh-cn=1 小时 +'''1 hour'''.zh-hk=1 小時 +'''1 hour'''.zh-tw=1 小時 +'''1 hour'''.hr=1 sat +'''1 hour'''.cs=1 hodina +'''1 hour'''.da=1 time +'''1 hour'''.nl=1 uur +'''1 hour'''.et=1 tund +'''1 hour'''.fi=1 tunti +'''1 hour'''.fr=1 heure +'''1 hour'''.fr-ca=1 heure +'''1 hour'''.de=1 Stunde +'''1 hour'''.el=1 ώρα +'''1 hour'''.iw=שעה אחת +'''1 hour'''.hi-in=1 घंटा +'''1 hour'''.hu=1 óra +'''1 hour'''.is=1 klukkustund +'''1 hour'''.in=1 jam +'''1 hour'''.it=1 ora +'''1 hour'''.ja=1時間 +'''1 hour'''.ko=1시간 +'''1 hour'''.lv=1 stunda +'''1 hour'''.lt=1 val. +'''1 hour'''.ms=1 jam +'''1 hour'''.no=1 time +'''1 hour'''.pl=1 godzina +'''1 hour'''.pt=1 hora +'''1 hour'''.ro=1 oră +'''1 hour'''.ru=1 час +'''1 hour'''.sr=Jedan sat +'''1 hour'''.sk=1 hodina +'''1 hour'''.sl=1 h +'''1 hour'''.es=1 hora +'''1 hour'''.sv=En timme +'''1 hour'''.th=1 ชั่วโมง +'''1 hour'''.tr=1 saat +'''1 hour'''.uk=1 година +'''1 hour'''.vi=1 giờ +'''6 hours'''.en=6 hours +'''6 hours'''.en-gb=6 hours +'''6 hours'''.en-us=6 hours +'''6 hours'''.en-ca=6 hours +'''6 hours'''.sq=6 orë +'''6 hours'''.ar=‏‫٦‬ ساعات +'''6 hours'''.be=6 гадзін +'''6 hours'''.sr-ba=6 sati +'''6 hours'''.bg=6 часа +'''6 hours'''.ca=6 hores +'''6 hours'''.zh-cn=6 小时 +'''6 hours'''.zh-hk=6 小時 +'''6 hours'''.zh-tw=6 小時 +'''6 hours'''.hr=6 sati +'''6 hours'''.cs=6 hodin +'''6 hours'''.da=6 timer +'''6 hours'''.nl=6 uur +'''6 hours'''.et=6 tundi +'''6 hours'''.fi=6 tuntia +'''6 hours'''.fr=6 heures +'''6 hours'''.fr-ca=6 heures +'''6 hours'''.de=6 Stunden +'''6 hours'''.el=6 ώρες +'''6 hours'''.iw=6 שעות +'''6 hours'''.hi-in=6 घंटे +'''6 hours'''.hu=6 óra +'''6 hours'''.is=6 klukkustundir +'''6 hours'''.in=6 jam +'''6 hours'''.it=6 ore +'''6 hours'''.ja=6時間 +'''6 hours'''.ko=6시간 +'''6 hours'''.lv=6 stundas +'''6 hours'''.lt=6 val. +'''6 hours'''.ms=6 jam +'''6 hours'''.no=6 timer +'''6 hours'''.pl=6 godzin +'''6 hours'''.pt=6 horas +'''6 hours'''.ro=6 ore +'''6 hours'''.ru=6 часов +'''6 hours'''.sr=6 sati +'''6 hours'''.sk=6 hodín +'''6 hours'''.sl=6 ur +'''6 hours'''.es=6 horas +'''6 hours'''.sv=6 timmar +'''6 hours'''.th=6 ชั่วโมง +'''6 hours'''.tr=6 saat +'''6 hours'''.uk=6 годин +'''6 hours'''.vi=6 giờ +'''12 hours'''.en=12 hours +'''12 hours'''.en-gb=12 hours +'''12 hours'''.en-us=12 hours +'''12 hours'''.en-ca=12 hours +'''12 hours'''.sq=12 orë +'''12 hours'''.ar=‏‫١٢‬ ساعة +'''12 hours'''.be=12 гадзін +'''12 hours'''.sr-ba=12 sati +'''12 hours'''.bg=12 часа +'''12 hours'''.ca=12 hores +'''12 hours'''.zh-cn=12 小时 +'''12 hours'''.zh-hk=12 小時 +'''12 hours'''.zh-tw=12 小時 +'''12 hours'''.hr=12 sati +'''12 hours'''.cs=12 hodin +'''12 hours'''.da=12 timer +'''12 hours'''.nl=12 uur +'''12 hours'''.et=12 tundi +'''12 hours'''.fi=12 tuntia +'''12 hours'''.fr=12 heures +'''12 hours'''.fr-ca=12 heures +'''12 hours'''.de=12 Stunden +'''12 hours'''.el=12 ώρες +'''12 hours'''.iw=12 שעות +'''12 hours'''.hi-in=12 घंटे +'''12 hours'''.hu=12 óra +'''12 hours'''.is=12 klukkustundir +'''12 hours'''.in=12 jam +'''12 hours'''.it=12 ore +'''12 hours'''.ja=12時間 +'''12 hours'''.ko=12시간 +'''12 hours'''.lv=12 stundas +'''12 hours'''.lt=12 val. +'''12 hours'''.ms=12 jam +'''12 hours'''.no=12 timer +'''12 hours'''.pl=12 godzin +'''12 hours'''.pt=12 horas +'''12 hours'''.ro=12 ore +'''12 hours'''.ru=12 часов +'''12 hours'''.sr=12 sati +'''12 hours'''.sk=12 hodín +'''12 hours'''.sl=12 ur +'''12 hours'''.es=12 horas +'''12 hours'''.sv=12 timmar +'''12 hours'''.th=12 ชั่วโมง +'''12 hours'''.tr=12 saat +'''12 hours'''.uk=12 годин +'''12 hours'''.vi=12 giờ +'''18 hours'''.en=18 hours +'''18 hours'''.en-gb=18 hours +'''18 hours'''.en-us=18 hours +'''18 hours'''.en-ca=18 hours +'''18 hours'''.sq=18 orë +'''18 hours'''.ar=١٨ ساعة +'''18 hours'''.be=18 гадзін +'''18 hours'''.sr-ba=18 sati +'''18 hours'''.bg=18 часа +'''18 hours'''.ca=18 hores +'''18 hours'''.zh-cn=18 小时 +'''18 hours'''.zh-hk=18 小時 +'''18 hours'''.zh-tw=18 小時 +'''18 hours'''.hr=18 sati +'''18 hours'''.cs=18 hodin +'''18 hours'''.da=18 timer +'''18 hours'''.nl=18 uur +'''18 hours'''.et=18 tundi +'''18 hours'''.fi=18 tuntia +'''18 hours'''.fr=18 heures +'''18 hours'''.fr-ca=18 heures +'''18 hours'''.de=18 Stunden +'''18 hours'''.el=18 ώρες +'''18 hours'''.iw=18 שעות +'''18 hours'''.hi-in=18 घंटे +'''18 hours'''.hu=18 óra +'''18 hours'''.is=18 klukkustundir +'''18 hours'''.in=18 jam +'''18 hours'''.it=18 ore +'''18 hours'''.ja=18時間 +'''18 hours'''.ko=18시간 +'''18 hours'''.lv=18 stundas +'''18 hours'''.lt=18 val. +'''18 hours'''.ms=18 jam +'''18 hours'''.no=18 timer +'''18 hours'''.pl=18 godzin +'''18 hours'''.pt=18 horas +'''18 hours'''.ro=18 ore +'''18 hours'''.ru=18 часов +'''18 hours'''.sr=18 sati +'''18 hours'''.sk=18 hodín +'''18 hours'''.sl=18 ur +'''18 hours'''.es=18 horas +'''18 hours'''.sv=18 timmar +'''18 hours'''.th=18 ชั่วโมง +'''18 hours'''.tr=18 saat +'''18 hours'''.uk=18 годин +'''18 hours'''.vi=18 giờ +'''24 hours'''.en=24 hours +'''24 hours'''.en-gb=24 hours +'''24 hours'''.en-us=24 hours +'''24 hours'''.en-ca=24 hours +'''24 hours'''.sq=24 orë +'''24 hours'''.ar=٢٤ ساعة +'''24 hours'''.be=24 гадзіны +'''24 hours'''.sr-ba=24 sata +'''24 hours'''.bg=24 часа +'''24 hours'''.ca=24 hores +'''24 hours'''.zh-cn=24 小时 +'''24 hours'''.zh-hk=24 小時 +'''24 hours'''.zh-tw=24 小時 +'''24 hours'''.hr=24 sata +'''24 hours'''.cs=24 hodin +'''24 hours'''.da=24 timer +'''24 hours'''.nl=24 uur +'''24 hours'''.et=24 tundi +'''24 hours'''.fi=24 tuntia +'''24 hours'''.fr=24 heures +'''24 hours'''.fr-ca=24 heures +'''24 hours'''.de=24 Stunden +'''24 hours'''.el=24 ώρες +'''24 hours'''.iw=24 שעות +'''24 hours'''.hi-in=24 घंटे +'''24 hours'''.hu=24 óra +'''24 hours'''.is=Sólarhringur +'''24 hours'''.in=24 jam +'''24 hours'''.it=24 ore +'''24 hours'''.ja=24時間 +'''24 hours'''.ko=24시간 +'''24 hours'''.lv=24 stundas +'''24 hours'''.lt=24 val. +'''24 hours'''.ms=24 jam +'''24 hours'''.no=24 timer +'''24 hours'''.pl=24 godziny +'''24 hours'''.pt=24 horas +'''24 hours'''.ro=24 de ore +'''24 hours'''.ru=24 часа +'''24 hours'''.sr=24 sata +'''24 hours'''.sk=24 hodín +'''24 hours'''.sl=24 ur +'''24 hours'''.es=24 horas +'''24 hours'''.sv=24 timmar +'''24 hours'''.th=24 ชั่วโมง +'''24 hours'''.tr=24 saat +'''24 hours'''.uk=24 години +'''24 hours'''.vi=24 giờ # End of Device Preferences From 3b2ec4eb80592e09c94916070695ecbb0054fbf5 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Fri, 25 Feb 2022 18:12:31 +0900 Subject: [PATCH 362/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#77867) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * Update sihas-multipurpose-sensor.groovy Motion uses a OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE. So, delete unnecessary parts. --- .../sihas-multipurpose-sensor.groovy | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index d94e8d9a213..4c37dba3370 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -132,9 +132,7 @@ private Map translateZoneStatus(ZoneStatus zs) { // Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion if (isDSM300()) { return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getContactResult('open') : getContactResult('closed') - } else { - return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') - } + } } private Map getBatteryResult(rawValue) { @@ -286,19 +284,19 @@ def configure() { configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE, DataType.UINT8, 30, 21600, 0x01/*100mv*1*/) if (isUSM300() || isTSM300()) { - configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 20, 300, 10/*10/100=0.1도*/) - configCmds += zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 20, 300, 40/*10/100=0.4%*/) + configCmds += zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, TEMPERATURE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.INT16, 15, 300, 10/*10/100=0.1도*/) + configCmds += zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, RALATIVE_HUMIDITY_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 15, 300, 40/*10/100=0.4%*/) } if (isUSM300()) { - configCmds += zigbee.configureReporting(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 20, 3600, 10/*10 lux*/) + configCmds += zigbee.configureReporting(ILLUMINANCE_MEASUREMENT_CLUSTER, ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ATTRIBUTE, DataType.UINT16, 15, 3600, 1/*1 lux*/) } if (isUSM300() || isOSM300()) { configCmds += zigbee.configureReporting(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE, DataType.BITMAP8, 1, 600, 1) } - if (isDSM300()) { + if (isDSM300() || isUSM300() || isOSM300()) { configCmds += zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 0, 0xffff, null) } From a5c225a3dcd3b90756c0558ecf0cda224db82dea Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Mon, 28 Feb 2022 13:28:48 +0800 Subject: [PATCH 363/422] Update Orvibo-Contact-Sensor.groovy update thirdreality battery percent report --- .../Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index 0df9da8f9a7..9888fae4fb6 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -166,10 +166,11 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] def manufacturer = getDataValue("manufacturer") + def application = getDataValue("application") if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") { + if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application <= "17") { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From f44d91715734384ea7a55674007031520405903e Mon Sep 17 00:00:00 2001 From: Konrad Date: Tue, 1 Mar 2022 16:56:02 +0100 Subject: [PATCH 364/422] CP-14539 - another fingerprint for Yale Lock YMF40 --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 86d164d91ae..abeb1c082c6 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -48,6 +48,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "iZBModule01", deviceJoinName: "Yale Door Lock" //Yale Locks (YDF30/40, YMF30/40) with old firmware (v.9.0) fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "c700000202", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YDF40 fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "0700000001", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0020,0101", outClusters: "000A,0019", manufacturer: "ASSA ABLOY iRevo", model: "06ffff2027", deviceJoinName: "Yale Door Lock" //Yale Fingerprint Lock YMF40 fingerprint profileId: "0104", inClusters: "0000,0001,0003,0101", outClusters: "0000,0001,0003,0101", manufacturer: "Datek", model: "ID Lock 150", deviceJoinName: "ID Lock Door Lock" //ID Lock 150 Zigbee Module by Datek fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,0020,0101", outClusters: "0019", manufacturer: "Danalock", model: "V3-BTZBE", deviceJoinName: "Danalock Door Lock" } From 0d9c4ad3630739615ae86d21ecdc63414d6d833a Mon Sep 17 00:00:00 2001 From: Sarkis008 <92102906+Sarkis008@users.noreply.github.com> Date: Tue, 1 Mar 2022 23:17:01 +0400 Subject: [PATCH 365/422] DevWs for HELTUN containing containing HELTUN FT01 Fan Coil Thermostat (#77748) * DevWs for HELTUN containing containing HELTUN FT01 Fan Coil Thermostat * Removed logs * Fix typos * Removed unnecessary line * Indentation + formatting * Requested changes Co-authored-by: Artur Sargsyan --- .../heltun-ft01-fan-coil-thermostat.groovy | 565 ++++++++++++++++++ 1 file changed, 565 insertions(+) create mode 100644 devicetypes/heltun/heltun-ft01-fan-coil-thermostat.src/heltun-ft01-fan-coil-thermostat.groovy diff --git a/devicetypes/heltun/heltun-ft01-fan-coil-thermostat.src/heltun-ft01-fan-coil-thermostat.groovy b/devicetypes/heltun/heltun-ft01-fan-coil-thermostat.src/heltun-ft01-fan-coil-thermostat.groovy new file mode 100644 index 00000000000..cd1ffe78681 --- /dev/null +++ b/devicetypes/heltun/heltun-ft01-fan-coil-thermostat.src/heltun-ft01-fan-coil-thermostat.groovy @@ -0,0 +1,565 @@ +/** + * HELTUN FT01 Fan Coil Thermostat + * + * Copyright 2021 Sarkis Kabrailian + * + * 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: "HELTUN FT01 Fan Coil Thermostat", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true, ocfDeviceType: "oic.d.thermostat") { + capability "Energy Meter" + capability "Fan Speed" + capability "Power Meter" + capability "Relative Humidity Measurement" + capability "Temperature Measurement" + capability "Thermostat Heating Setpoint" + capability "Thermostat Mode" + capability "Thermostat Operating State" + capability "Illuminance Measurement" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0344", prod: "0004", model: "0002", deviceJoinName: "HELTUN Thermostat" //Raw Description zw:L type:0806 mfr:0344 prod:0004 model:0002 ver:2.05 zwv:7.11 lib:03 cc:5E,85,59,8E,55,86,72,5A,73,98,9F,6C,81,31,32,70,42,40,43,44,45,87,22,7A + } + preferences { + input ( + title: "HE-FT01 | HELTUN Fan Coil Thermostat", + description: "The user manual document with all technical information is available in support.heltun.com page. In case of technical questions please contact HELTUN Support Team at support@heltun.com", + type: "paragraph", + element: "paragraph" + ) + parameterMap().each { + if (it.title != null) { + input ( + title: "${it.title}", + description: it.description, + type: "paragraph", + element: "paragraph" + ) + } + def unit = it.unit ? it.unit : "" + def defV = it.default as Integer + def defVDescr = it.options ? it.options.get(defV) : "${defV}${unit} - Default Value" + input ( + name: it.name, + title: null, + description: "$defVDescr", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.default, + required: false + ) + } + } +} + +def parse(String description) { + def cmd = zwave.parse(description) + if (cmd) { + return zwaveEvent(cmd) + } +} + +def checkParam() { + boolean needConfig = false + parameterMap().each { + if (state."$it.name" == null || state."$it.name".state == "defNotConfigured") { + state."$it.name" = [value: it.default as Integer, state: "defNotConfigured"] + needConfig = true + } + if (settings."$it.name" != null && (state."$it.name".value != settings."$it.name" as Integer || state."$it.name".state == "notConfigured")) { + state."$it.name".value = settings."$it.name" as Integer + state."$it.name".state = "notConfigured" + needConfig = true + } + } + if ( needConfig ) { + configParam() + } +} + +private configParam() { + def cmds = [] + for (parameter in parameterMap()) { + if ( state."$parameter.name"?.value != null && state."$parameter.name"?.state in ["notConfigured", "defNotConfigured"] ) { + cmds << zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$parameter.name".value, parameterNumber: parameter.paramNum, size: parameter.size).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: parameter.paramNum).format() + break + } + } + if (cmds) { + runIn(5, "checkParam") + sendHubCommand(cmds,500) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def parameter = parameterMap().find( {it.paramNum == cmd.parameterNumber } ).name + if (state."$parameter".value == cmd.scaledConfigurationValue){ + state."$parameter".state = "configured" + } else { + state."$parameter".state = "error" + } + configParam() +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) { + def localScale = getTemperatureScale() //HubScale + def deviceMode = numToModeMap[cmd.mode.toInteger()] + sendEvent(name: "thermostatMode", data:[supportedThermostatModes: state.supportedModes], value: deviceMode) + if (cmd.mode == 0 || cmd.mode == 6) { + sendEvent(name: "heatingSetpoint", value: 0, unit: localScale) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def map = [:] + def roomTemperature = 1 + def humidity = 5 + def illuminance = 3 + def localScale = getTemperatureScale() //HubScale + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + if (roomTemperature == cmd.sensorType) { + def deviceTemp = cmd.scaledSensorValue + def scaledTemp = (deviceScale == localScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + map.name = "temperature" + map.value = scaledTemp + map.unit = localScale + sendEvent(map) + } else if (humidity == cmd.sensorType) { + map.name = "humidity" + map.value = cmd.scaledSensorValue.toInteger() + map.unit = "%" + sendEvent(map) + } else if (illuminance == cmd.sensorType) { + map.name = "illuminance" + map.value = cmd.scaledSensorValue + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + map.name = "energy" + map.value = cmd.scaledMeterValue + map.unit = "kWh" + } else if (cmd.scale == 2) { + map.name = "power" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "W" + } + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev2.ThermostatOperatingStateReport cmd) { + def state = cmd.operatingState.toInteger() + def currentState = opStateMap[state] + sendEvent(name: "thermostatOperatingState", value: currentState) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatfanstatev1.ThermostatFanStateReport cmd) { + def state = cmd.fanOperatingState.toInteger() + def currentState = fanStateMap[state] + sendEvent(name: "thermostatFanState", value: currentState) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) { + def speed = cmd.fanMode.toInteger() + def fanSpeed = fanModeToSpeedMap[speed] + if (cmd.off) { + fanSpeed = 0 + } + sendEvent(name: "fanSpeed", value: fanSpeed) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd) { + def localScale = getTemperatureScale() //HubScale + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def deviceTemp = cmd.scaledValue + def setPoint = (deviceScale == localScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + def mode = modeToNumMap[device.currentValue("thermostatMode")] + if (mode == 0 || mode == 6) { + setPoint = 0 + } + sendEvent(name: "heatingSetpoint", value: setPoint, unit: localScale) +} + +def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) { + def tSupportedModes = [] + if(cmd.heat) { tSupportedModes << "heat" } + if(cmd.cool) { tSupportedModes << "cool" } + if(cmd.auto) { tSupportedModes << "auto" } + if(cmd.fanOnly) { tSupportedModes << "fanonly" } + if(cmd.autoChangeover) { tSupportedModes << "autochangeover" } + if(cmd.energySaveHeat) { tSupportedModes << "energysaveheat" } + if(cmd.energySaveCool) { tSupportedModes << "energysavecool" } + if(cmd.off) { tSupportedModes << "off" } + state.supportedModes = tSupportedModes + sendEvent(name: "supportedThermostatModes", value: tSupportedModes, displayed: false) +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) { + def cmds = [] + if (cmd.groupingIdentifier == 1) { + if (cmd.nodeId != [0, zwaveHubNodeId, 0]) { + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1).format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: [0,zwaveHubNodeId,0]).format() + } + } + if (cmds) { + sendHubCommand(cmds, 1200) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.clockv1.ClockReport cmd) { + def currDate = new Date().toCalendar() + def time = [hour: currDate.get(Calendar.HOUR_OF_DAY), minute: currDate.get(Calendar.MINUTE), weekday: currDate.get(Calendar.DAY_OF_WEEK)] + if ((time.hour != cmd.hour) || (time.minute != cmd.minute) || (time.weekday != cmd.weekday)) { + sendHubCommand(zwave.clockV1.clockSet(time).format()) + } +} + +def setHeatingSetpoint(tValue) { + def cmds = [] + def mode = device.currentValue("thermostatMode") + def currentMode = modeToNumMap[mode] + def temp = state.heatingSetpoint = tValue.toDouble() //temp got fromm the app + def tempInC = (getTemperatureScale() == "F" ? roundC(fahrenheitToCelsius(temp)) : temp) //If not C, Convert to C + cmds << zwave.thermostatSetpointV2.thermostatSetpointSet(setpointType: currentMode, scale: 0, precision: 1, scaledValue: tempInC).format() + // Sync temp, opState, setPoint, fanSpeed + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() + cmds << zwave.thermostatOperatingStateV2.thermostatOperatingStateGet().format() + cmds << zwave.thermostatFanModeV3.thermostatFanModeGet() + cmds << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: currentMode).format() + sendHubCommand(cmds) +} + +def setFanSpeed(speed) { + def cmds = [] + boolean fanState = false + if (speed == 0) { + fanState = true + cmds << zwave.thermostatFanModeV3.thermostatFanModeSet(off: fanState) + } else { + def fanSpeed = fanSpeedToModeMap[speed] + cmds << zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanSpeed, off: fanState) + } + cmds << zwave.thermostatFanModeV3.thermostatFanModeGet() + sendHubCommand(cmds) +} + +def setThermostatMode(String value) { + def cmds = [] + cmds << zwave.thermostatModeV2.thermostatModeSet(mode: modeToNumMap[value]).format() + cmds << zwave.thermostatModeV2.thermostatModeGet().format() + cmds << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: modeToNumMap[value]).format() + sendHubCommand(cmds) +} + +def getNumToModeMap() { + [ + 0 : "off", + 1 : "heat", + 2: "cool", + 3 : "auto", + 6 : "fanonly", + 10 : "autochangeover", + 11 : "energysaveheat", + 12 : "energysavecool" + ] +} + +def getModeToNumMap() { + [ + "off": 0, + "heat": 1, + "cool": 2, + "auto": 3, + "fanonly": 6, + "autochangeover": 10, + "energysaveheat": 11, + "energysavecool": 12 + ] +} + +def getOpStateMap() { + [ + 0 : "idle", + 1 : "heating", + 2 : "cooling", + 3 : "fan only" + ] +} + +def getFanStateMap() { + [ + 0 : "idle", + 1 : "running", + 2 : "running high", + 3 : "running medium" + ] +} + +def getFanModeToSpeedMap() { + [ + 0 : 1, //Fan Auto Low > Speed Low + 1 : 1, //Fan Low > Speed Low + 2 : 4, //Fan Auto High > Speed High + 3 : 3, //Fan High > Speed Max + 4 : 2, //Fan Auto mendium > Speed Medium + 5 : 2 //Fan medium > Speed Medium + ] +} + +def getFanSpeedToModeMap() { + [ + 1 : 1, //Speed Low > Fan Low + 2 : 5, //Speed Medium > Fan Medium + 3 : 3, //Speed High > Fan Auto High + 4 : 2 //Speed Max > Fan High + ] +} + +def roundC (tempInC) { + return (Math.round(tempInC.toDouble() * 2))/2 +} + +def updated() { + initialize() +} + +def initialize() { + runIn(3, "checkParam") +} + +def ping() { + refresh() +} + +def refresh() { + def cmds = [] + cmds << zwave.thermostatModeV2.thermostatModeGet().format() //get thermostatmode + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1).format() //roomTemperature + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:3).format() //Humidity + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:5).format() //Illuminance + cmds << zwave.meterV3.meterGet(scale: 0).format() //get kWh + cmds << zwave.meterV3.meterGet(scale: 2).format() //get Watts + cmds << zwave.thermostatOperatingStateV2.thermostatOperatingStateGet().format() //get Thermostat Operating State + cmds << zwave.clockV1.clockGet().format() //get Clock + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1).format() //get channel association + cmds << zwave.thermostatModeV2.thermostatModeSupportedGet().format() //get supported modes + cmds << zwave.thermostatFanModeV3.thermostatFanModeGet() //get fanMode + sendHubCommand(cmds, 1200) + runIn(15, "checkParam") +} + +def configure() { + ping() +} + +def resetEnergyMeter() { + sendHubCommand(zwave.meterV3.meterReset().format()) +} + +def off() { + setThermostatMode("off") +} + +def heat() { + setThermostatMode("heat") +} + +def cool() { + setThermostatMode("cool") +} + +def auto() { + setThermostatMode("auto") +} + +private parameterMap() {[ +[title: "Display Brightness Control", description: "The HE-FT01 can adjust its display brightness automatically depending on the illumination of the ambient environment and also allows to control it manually.", + name: "Selected Brightness Level", options: [ + 0: "Auto", + 1: "Level 1 (Lowest)", + 2: "Level 2", + 3: "Level 3", + 4: "Level 4", + 5: "Level 5", + 6: "Level 6", + 7: "Level 7", + 8: "Level 8", + 9: "Level 9", + 10: "Level 10 (Highest)" + ], paramNum: 5, size: 1, default: "0", type: "enum"], + +[title: "Touch Sensor Sensitivity Threshold", description: "This Parameter allows to adjust the Touch Buttons Sensitivity. Note: Setting the sensitivity too high can lead to false touch detection. We recommend not changing this Parameter unless there is a special need to do so.", + name: "Selected Touch Sensitivity", options: [ + 1: "Level 1 (Low sensitivity)", + 2: "Level 2", + 3: "Level 3", + 4: "Level 4", + 5: "Level 5", + 6: "Level 6", + 7: "Level 7", + 8: "Level 8", + 9: "Level 9", + 10: "Level 10 (High sensitivity)" + ], paramNum: 6, size: 1, default: "6", type: "enum"], + +[title: "Fan Relay Output Mode", description: "This Parameter determines the type of load connected to the device fan relay relay outputs (OUT-1, OUT-2, OUT-3). The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 7, size: 1, default: "0", type: "enum"], + +[title: "Heater Relay Output Mode", description: "This Parameter determines the type of load connected to the device heater relay output (OUT-4). The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 8, size: 1, default: "0", type: "enum"], + +[title: "Cooler Relay Output Mode", description: "This Parameter determines the type of load connected to the device cooler relay output (OUT-5). The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 9, size: 1, default: "0", type: "enum"], + +[title: "Heating State Fan Control", description: "This parameter determines if fan should be enabled or disabled in heating mode. If fan is enabled (normal operation), one of the outputs OUT-1, OUT-2, OUT-3 will be ON depending on the selected fan speed. If fan is disabled, in heating state, only OUT-4 will be ON and OUT-1, OUT-2, OUT-3 will always remain OFF", + name: "Selected Mode", options: [ + 0: "Fan Disabled", + 1: "Fan Enabled" + ], paramNum: 10, size: 1, default: "1", type: "enum"], + +[title: "Cooling State Fan Control", description: "This parameter determines if fan should be enabled or disabled in cooling mode. If fan is enabled (normal operation), one of the outputs OUT-1, OUT-2, OUT-3 will be ON depending on the selected fan speed. If fan is disabled, in cooling state, only OUT-4 will be ON and OUT-1, OUT-2, OUT-3 will always remain OFF", + name: "Selected Mode", options: [ + 0: "Fan Disabled", + 1: "Fan Enabled" + ], paramNum: 11, size: 1, default: "1", type: "enum"], + +[title: "Relays Load Power", description: "These parameters are used to specify the loads power that are connected to the device outputs (Relays). Using your connected device’s power consumption specification (see associated owner’s manual), set the load in Watts for the outputs bellow:", + name: "Selected Fan Low Speed Load Power in Watts", paramNum: 12, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + +[name: "Selected Fan Medium Speed Load Power in Watts", paramNum: 13, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + +[name: "Selected Fan High Speed Load Power in Watts", paramNum: 14, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + +[name: "Selected Heating Load Power in Watts", paramNum: 15, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + +[name: "Selected Cooling Load Power in Watts", paramNum: 16, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + +[title: "Air Temperature Calibration", description: "This Parameter defines the offset value for room air temperature. This value will be added or subtracted from the air temperature sensor reading.Through the Z-Wave network the value of this Parameter should be x10, e.g. for 1.5°C set the value 15.", + name: "Selected Temperature Offset in °Cx10", paramNum: 17, size: 1, default: 0, type: "number", min: -100, max: 100, unit: " °Cx10"], + +[title: "Temperature Hysteresis", description: "This Parameter defines the hysteresis value for temperature control. The HE-FT01 will stabilize the temperature with selected hysteresis. For example, if the SET POINT is set for 25°C and HYSTERESIS is set for 0.5°C the HE-FT01 will change the state to IDLE if the temperature reaches 25.0°C. It will change the state to HEATING if the temperature becomes lower than 24.5°C, and will change the state to COOLING if the temperature rises beyond 25.5°C.The value of this Parameter should be x10 e.g. for 0.5°C set the value 5.", + name: "Selected Hysteresis in °Cx10", paramNum: 18, size: 1, default: 5, type: "number", min: 2, max: 100, unit: " °Cx10"], + +[title: "TIME mode operation", description: "This Parameter determines the Climate Mode (Heating or Cooling) in which HE-FT01 will operates when the TIME mode is selected", + name: "Selected Mode", options: [ + 1: "Heating & Cooling", + 2: "Heating", + 3: "Cooling" + ], paramNum: 23, size: 1, default: "1", type: "enum"], + +[title: "Schedule Time", description: "Use these Parameters to set the Morning, Day, Evening and Night start times manually for the Temperature Schedule. The value of these Parameters has format HHMM, e.g. for 08:00 use value 0800 (time without a colon). From 00:00 to 23:59 can be selected.", + name: "Selected Morning Start Time", paramNum: 41, size: 2, default: 600, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Day Start Time", paramNum: 42, size: 2, default: 900, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Evening Start Time", paramNum: 43, size: 2, default: 1800, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[name: "Selected Night Start Time", paramNum: 44, size: 2, default: 2300, type: "number", min: 0, max: 2359, unit: " HHMM"], + +[title: "Schedule Temperature", description: "Use these Parameters to set the temperature for each day Schedule manually. The value of this Parameter should be x10, e.g., for 22.5°C set value 225. From 1°C (value 10) to 110°C (value 1100) can be selected.", + name: "Monday Morning Temperature in °Cx10", paramNum: 45, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Monday Day Temperature in °Cx10", paramNum: 46, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Monday Evening Temperature in °Cx10", paramNum: 47, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Monday Night Temperature in °Cx10", paramNum: 48, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Morning Temperature in °Cx10", paramNum: 49, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Day Temperature in °Cx10", paramNum: 50, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Evening Temperature in °Cx10", paramNum: 51, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Tuesday Night Temperature in °Cx10", paramNum: 52, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Morning Temperature in °Cx10", paramNum: 53, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Day Temperature in °Cx10", paramNum: 54, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Evening Temperature in °Cx10", paramNum: 55, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Wednesday Night Temperature in °Cx10", paramNum: 56, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Morning Temperature in °Cx10", paramNum: 57, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Day Temperature in °Cx10", paramNum: 58, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Evening Temperature in °Cx10", paramNum: 59, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Thursday Night Temperature in °Cx10", paramNum: 60, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Morning Temperature in °Cx10", paramNum: 61, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Day Temperature in °Cx10", paramNum: 62, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Evening Temperature in °Cx10", paramNum: 63, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Friday Night Temperature in °Cx10", paramNum: 64, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Morning Temperature in °Cx10", paramNum: 65, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Day Temperature in °Cx10", paramNum: 66, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Evening Temperature in °Cx10", paramNum: 67, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Saturday Night Temperature in °Cx10", paramNum: 68, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Morning Temperature in °Cx10", paramNum: 69, size: 2, default: 240, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Day Temperature in °Cx10", paramNum: 70, size: 2, default: 200, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Evening Temperature in °Cx10", paramNum: 71, size: 2, default: 230, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[name: "Sunday Night Temperature in °Cx10", paramNum: 72, size: 2, default: 180, type: "number", min: 10, max: 370, unit: " °Cx10"], + +[title: "Energy Consumption Meter Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends reports from its energy consumption sensor even if there is no change in the value. This parameter defines the interval between consecutive reports of real time and cumulative energy consumption data to the gateway", + name: "Selected Energy Report Interval in minutes", paramNum: 141, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "Control Energy Meter Report", description: "This Parameter determines if the change in the energy meter will result in a report being sent to the gateway. Note: When the device is turning ON, the consumption data will be sent to the gateway once, even if the report is disabled.", + name: "Sending Energy Meter Reports", options: [ + 0: "Disabled", + 1: "Enabled" + ], paramNum: 142, size: 1, default: "1", type: "enum"], + +[title: "Sensors Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends to the gateway reports from its external NTC temperature sensor even if there are not changes in the values. This Parameter defines the interval between consecutive reports", + name: "Selected Energy Report Interval in minutes", paramNum: 143, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + +[title: "Air & Floor Temperature Sensors Report Threshold", description: "This Parameter determines the change in temperature level (in °C) resulting in temperature sensors report being sent to the gateway. The value of this Parameter should be x10 for °C, e.g. for 0.4°C use value 4. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Temperature Threshold in °Cx10", paramNum: 144, size: 1, default: 2, type: "number", min: 0 , max: 100, unit: " °Cx10"], + +[title: "Humidity Sensor Report Threshold", description: "This Parameter determines the change in humidity level in % resulting in humidity sensors report being sent to the gateway. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Humidity Threshold in %", paramNum: 145, size: 1, default: 2, type: "number", min: 0 , max: 25, unit: "%"], + +[title: "Light Sensor Report Threshold", description: "This Parameter determines the change in the ambient environment illuminance level resulting in a light sensors report being sent to the gateway. From 10% to 99% can be selected. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Light Sensor Threshold in %", paramNum: 146, size: 1, default: 50, type: "number", min: 0 , max: 99, unit: "%"] + +]} \ No newline at end of file From 6c071836a322df969cbdf7512240b8d22e8cb33b Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Mon, 7 Mar 2022 05:42:25 -0500 Subject: [PATCH 366/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZEN52 Double Relay (#77899) * DevWs for Zooz (The Smartest House) containing containing Zooz ZEN52 Double Relay * Zooz Child Switch Button for Zooz ZEN52 Double Relay * Zooz ZEN52 Double Relay changes --- .../zooz-child-switch-button.groovy | 61 +++ .../zooz-zen52-double-relay.groovy | 437 ++++++++++++++++++ 2 files changed, 498 insertions(+) create mode 100644 devicetypes/zooz/zooz-child-switch-button.src/zooz-child-switch-button.groovy create mode 100644 devicetypes/zooz/zooz-zen52-double-relay.src/zooz-zen52-double-relay.groovy diff --git a/devicetypes/zooz/zooz-child-switch-button.src/zooz-child-switch-button.groovy b/devicetypes/zooz/zooz-child-switch-button.src/zooz-child-switch-button.groovy new file mode 100644 index 00000000000..496a5bdbb2e --- /dev/null +++ b/devicetypes/zooz/zooz-child-switch-button.src/zooz-child-switch-button.groovy @@ -0,0 +1,61 @@ +/* + * Zooz Child Switch Button + * + * Changelog: + * + * 2022-03-02 + * - Publication Release + * + * Copyright 2022 Zooz + * + * 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: "Zooz Child Switch Button", + namespace: "Zooz", + author: "Kevin LaFramboise (krlaframboise)", + ocfDeviceType: "oic.d.light", + mnmn: "SmartThingsCommunity", + vid: "29d51c12-bb47-3d95-ad2e-831656ed20a8" + ) { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Button" + capability "Refresh" + } + + preferences() {} +} + +def parse(String description) { + return [] +} + +def on() { + log.debug "on()..." + parent.childOn(device.deviceNetworkId) +} + +def off() { + log.debug "off()..." + parent.childOff(device.deviceNetworkId) +} + +def refresh() { + log.debug "refresh()..." + parent.childRefresh(device.deviceNetworkId) +} \ No newline at end of file diff --git a/devicetypes/zooz/zooz-zen52-double-relay.src/zooz-zen52-double-relay.groovy b/devicetypes/zooz/zooz-zen52-double-relay.src/zooz-zen52-double-relay.groovy new file mode 100644 index 00000000000..ef3eb55c4b7 --- /dev/null +++ b/devicetypes/zooz/zooz-zen52-double-relay.src/zooz-zen52-double-relay.groovy @@ -0,0 +1,437 @@ +/* + * Zooz ZEN52 Double Relay + * + * Changelog: + * + * 2022-03-02.2 + * - Removed central scene setting + * 2022-03-02 + * - Publication Release + * + * Copyright 2022 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x20: 1, // Basic + 0x22: 1, // ApplicationStatus + 0x25: 1, // SwitchBinary + 0x55: 1, // TransportService + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5B: 1, // CentralScene + 0x5E: 2, // ZwaveplusInfo + 0x60: 3, // MultiChannel + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd + 0x85: 2, // Association + 0x86: 1, // Version + 0x87: 2, // Indicator (3) + 0x8E: 2, // MultiChannelAssociation + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 +] + +@Field static int supervisionCC = 108 +@Field static int btnPushed = 0 +@Field static int btnReleased = 1 +@Field static int btnHeld = 2 +@Field static int mainEndpoint = 0 +@Field static List relayEndpoints = [1, 2] +@Field static List supportedButtonValues = ["pushed","held","pushed_2x","pushed_3x","pushed_4x","pushed_5x"] + +@Field static Map configParams = [ + ledIndicator: [num:2, title:"Led indicator", size:1, defaultVal:1, options:[0:"Disabled", 1:"Enabled"]], + relay1AutoOff: [num:3, title:"Relay 1 Auto Off Timer", size:2, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(timer unit)"], + relay1AutoOn: [num:4, title:"Relay 1 Auto On Timer", size:2, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(timer unit)"], + relay1TimerUnit: [num:7, title:"Relay 1 Timer Unit", size:1, defaultVal:1, options:[1:"Minutes", 2:"Seconds"]], + relay1StatusAfterPowerFailure: [num:14, title:"Relay 1 Status After Power Failure", size:1, defaultVal:2, options:[0:"Forced off", 1:"Forced on", 2:"Restore previous state"]], + relay1LoadControl: [num:17, title:"Relay 1 Load Control", size:1, defaultVal:1, options:[0:"Disable Switch/ Enable Z-Wave", 1:"Enable Switch and Z-Wave", 2:"Disable Switch and Z-Wave"]], + relay1SwitchType: [num:20, title:"Relay 1 Switch Type", size:1, defaultVal:2, options:[0:"Toggle Switch", 1:"Momentary Light Switch", 2:"Toggle Up On/Down Off", 3:"3-way Impulse Control", 4:"Garage Door Mode"]], + relay1ImpulseDuration: [num:22, title:"Relay 1 Impulse Duration for 3-way", size:1, defaultVal:10, range:"2..200", desc:"2..200"], + relay2AutoOff: [num:5, title:"Relay 2 Auto Off Timer", size:2, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(timer unit)"], + relay2AutoOn: [num:6, title:"Relay 2 Auto On Timer", size:2, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(timer unit)"], + relay2TimerUnit: [num:8, title:"Relay 2 Timer Unit", size:1, defaultVal:1, options:[1:"Minutes", 2:"Seconds"]], + relay2StatusAfterPowerFailure: [num:15, title:"Relay 2 Status After Power Failure", size:1, defaultVal:2, options:[0:"Forced off", 1:"Forced on", 2:"Restore previous state"]], + relay2LoadControl: [num:18, title:"Relay 2 Load Control", size:1, defaultVal:1, options:[0:"Disable Switch/ Enable Z-Wave", 1:"Enable Switch and Z-Wave", 2:"Disable Switch and Z-Wave"]], + relay2SwitchType: [num:21, title:"Relay 2 Switch Type", size:1, defaultVal:2, options:[0:"Toggle Switch", 1:"Momentary Light Switch", 2:"Toggle Up On/Down Off", 3:"3-way Impulse Control", 4:"Garage Door Mode"]], + relay2ImpulseDuration: [num:23, title:"Relay 2 Impulse Duration for 3-way", size:1, defaultVal:10, range:"2..200", desc:"2..200"] +] + +metadata { + definition ( + name: "Zooz ZEN52 Double Relay", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType: "oic.d.light", + mnmn: "SmartThings", + vid: "generic-switch" + ) { + capability "Actuator" + capability "Switch" + capability "Refresh" + capability "Health Check" + + // zw:Ls2a type:1000 mfr:027A prod:0104 model:0202 ver:1.11 zwv:7.15 lib:03 cc:5E,55,9F,6C,22 sec:25,70,85,59,8E,86,72,5A,73,7A,60,5B,87 epc:2 + fingerprint mfr: "027A", prod: "0104", model: "0202", deviceJoinName: "Zooz Switch" // Zooz ZEN52 Double Relay + } + + preferences { + configParams.each { name, param -> + if (param.options) { + input name, "enum", + title: param.title, + description: "Default: ${param.options[param.defaultVal]}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input name, "number", + title: param.title, + description: "${param.desc} - Default: ${param.defaultVal}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + } +} + +def installed() { + log.debug "installed()..." + initialize() + state.firstConfig = true +} + +def updated() { + log.debug "updated()..." + initialize() + + if (!state.firstConfig) { + configure() + } else { + state.firstConfig = false + } +} + +void initialize() { + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((60 * 60 * 3) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + relayEndpoints.each { endpoint -> + if (!findChildByEndpoint(endpoint)) { + String dni = buildChildDNI(endpoint) + def child + try { + child = createChildDevice(endpoint, dni, "Zooz", "Zooz Child Switch Button") + child.sendEvent(name: "supportedButtonValues", value: supportedButtonValues.encodeAsJSON(), displayed: false) + child.sendEvent(name:"numberOfButtons", value:1, displayed:false) + sendButtonEvent(child, "pushed") + } catch(e) { + log.warn "${e}" + } + + if (child) { + childRefresh(child.deviceNetworkId) + } + } + } +} + +def createChildDevice(int endpoint, String dni, String dthNamespace, String dthName) { + return addChildDevice( + dthNamespace, + dthName, + dni, + device.getHub().getId(), + [ + completedSetup: true, + label: "Zooz Switch ${endpoint}", + isComponent: false + ] + ) +} + +def configure() { + log.debug "configure()..." + List cmds = [] + + if (device.currentValue("switch") == null) { + cmds << switchBinaryGetCmd(mainEndpoint) + } + + configParams.each { name, param -> + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if (storedVal != settingVal) { + log.debug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: settingVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + if (cmds) { + sendHubCommand(cmds, 500) + } +} + +def on() { + log.debug "on()..." + executeOnOffCmds(0xFF, mainEndpoint) +} + +def off() { + log.debug "off()..." + executeOnOffCmds(0x00, mainEndpoint) +} + +void childOn(String dni) { + executeOnOffCmds(0xFF, getEndpointFromDNI(dni)) +} + +void childOff(String dni) { + executeOnOffCmds(0x00, getEndpointFromDNI(dni)) +} + +void executeOnOffCmds(int value, endpoint) { + List cmds = [ + multiChannelCmdEncapCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: value), endpoint) + ] + + // Workaround for unreliable automatic reports. + if (endpoint == mainEndpoint) { + cmds += getRefreshRelaysCmds() + } else { + cmds << switchBinaryGetCmd(endpoint) + } + + sendHubCommand(cmds) +} + +List getRefreshRelaysCmds() { + List cmds = [] + relayEndpoints.each { endpoint -> + cmds << switchBinaryGetCmd(endpoint) + } + return cmds +} + +def ping() { + log.debug "ping()..." + return [ switchBinaryGetCmd(mainEndpoint) ] +} + +def refresh() { + log.debug "refresh()..." + sendHubCommand(getRefreshRelaysCmds(), 500) +} + +void childRefresh(String dni) { + sendHubCommand([ + switchBinaryGetCmd(getEndpointFromDNI(dni)) + ]) +} + +String switchBinaryGetCmd(int endpoint) { + return multiChannelCmdEncapCmd(zwave.switchBinaryV1.switchBinaryGet(), endpoint) +} + +String multiChannelCmdEncapCmd(cmd, int endpoint=0) { + if (endpoint) { + return secureCmd(zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd)) + } else { + return secureCmd(cmd) + } +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) { + // Workaround that was added to all SmartThings Multichannel DTHs. + if ((cmd.commandClass == supervisionCC) && (cmd.parameter.size >= 4)) { // Supervision encapsulated Message + // Supervision header is 4 bytes long, two bytes dropped here are the latter two bytes of the supervision header + cmd.parameter = cmd.parameter.drop(2) + // Updated Command Class/Command now with the remaining bytes + cmd.commandClass = cmd.parameter[0] + cmd.command = cmd.parameter[1] + cmd.parameter = cmd.parameter.drop(2) + } + + def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint) + } else { + log.debug "Unable to get encapsulated command: $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + int val = cmd.scaledConfigurationValue + + if (val < 0) { + // device uses signed values + val = (val + Math.pow(256, cmd.size)) + } + + state[name] = val + log.debug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${val}" + } else { + log.debug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, endpoint=0) { + log.debug "${cmd} (${endpoint})" + sendSwitchEvent(cmd.value, endpoint) +} + +void zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint=0) { + log.debug "${cmd} (${endpoint})" + sendSwitchEvent(cmd.value, endpoint) +} + +void sendSwitchEvent(int rawValue, int endpoint) { + String value = (rawValue ? "on" : "off") + if (endpoint == mainEndpoint) { + log.debug("Switch is ${value}") + sendEvent(name: "switch", value: value) + } else { + def child = findChildByEndpoint(endpoint) + if (child) { + log.debug("${child.displayName} switch is ${value}") + child.sendEvent(name: "switch", value: value) + } else { + log.warn "Child device for endpoint ${endpoint} does not exist" + } + + // Workaround for device not sending reports for main endpoint for physical or z-wave control. + if (device.currentValue("switch") != value) { + sendHubCommand([switchBinaryGetCmd(mainEndpoint)]) + } + } +} + +void zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + if (state.lastSequenceNumber != cmd.sequenceNumber) { + state.lastSequenceNumber = cmd.sequenceNumber + + int endpoint = cmd.sceneNumber + String value + + switch (cmd.keyAttributes){ + case btnPushed: + value = "pushed" + break + case btnReleased: + log.debug "Button Value 'released' is not supported by SmartThings" + break + case btnHeld: + value = "held" + break + default: + value = "pushed_${cmd.keyAttributes - 1}x" + } + + if (value) { + sendButtonEvent(findChildByEndpoint(endpoint), value) + } + } +} + +void sendButtonEvent(child, String value) { + if (child) { + log.debug "${child.displayName} button ${value}" + child.sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) + } +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Unhandled zwaveEvent: $cmd" +} + +Integer getSettingVal(String name) { + Integer value = safeToInt(settings[name], null) + if ((value == null) && (getStoredVal(name) != null)) { + return configParams[name].defaultVal + } else { + return value + } +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +def findChildByEndpoint(int endpoint) { + String dni = buildChildDNI(endpoint) + return childDevices?.find { it.deviceNetworkId == dni } +} + +String buildChildDNI(int endpoint) { + return "${device.deviceNetworkId}:${endpoint}" +} + +int getEndpointFromDNI(String dni) { + if (dni?.contains(":")) { + String lastChar = dni.reverse().take(1) + return safeToInt(lastChar, mainEndpoint) + } else { + return mainEndpoint + } +} \ No newline at end of file From 9a2fc20fc28fea843c2e0f7834b46f867e08e658 Mon Sep 17 00:00:00 2001 From: AltyorFig <95210115+AltyorFig@users.noreply.github.com> Date: Mon, 7 Mar 2022 15:53:11 +0100 Subject: [PATCH 367/422] DevWs for NodOn containing containing NodOn lighting Switch and 2 more (#77876) * DevWs for NodOn containing containing NodOn lighting Switch and 2 more * add space outcluster --- .../zigbee-multi-switch.src/zigbee-multi-switch.groovy | 3 ++- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 7 +++++-- .../zigbee-window-shade.src/zigbee-window-shade.groovy | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 4be9b697ea0..47d8af4f804 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -92,7 +92,8 @@ metadata { fingerprint manufacturer: "LELLKI", model: "JZ-ZB-006", deviceJoinName: "LELLKI Switch 1" //LELLKI 6 Gang Switch 1 // NodOn - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0007, 0008, FC57", outClusters: "0021", manufacturer: "NodOn", model: "SIN-4-2-20", deviceJoinName: "NodOn Light 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0007, 0008, 1000, FC57", outClusters: "0003, 0006, 0019", manufacturer: "NodOn", model: "SIN-4-2-20", deviceJoinName: "NodOn Light 1" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0007, 0008, 1000, FC57", outClusters: "0003, 0006, 0019", manufacturer: "NodOn", model: "SIN-4-2-20_PRO", deviceJoinName: "NodOn Light 1" // SiHAS Switch (2~6 Gang) fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z2", deviceJoinName: "SiHAS Switch 1" diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index d64ae59efc6..b0293c12ea5 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -78,7 +78,8 @@ metadata { fingerprint manufacturer: "Leviton", model: "DG15A", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Zigbee Plug-In Switch DG15A, Raw Description: 01 0104 010A 00 06 0000 0003 0004 0005 0006 0B05 01 0019 // NodOn - fingerprint profileId: "0104", deviceId: "0002", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-1-20", deviceJoinName: "NodOn Switch" + fingerprint profileId: "0104", deviceId: "0002", inClusters: "0000, 0003, 0004, 0005, 0006, 0007, 1000, FC57", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-1-20", deviceJoinName: "NodOn Switch" + fingerprint profileId: "0104", deviceId: "0002", inClusters: "0000, 0003, 0004, 0005, 0006, 0007, 1000, FC57", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-1-20_PRO", deviceJoinName: "NodOn Switch" // Orvibo fingerprint profileId: "0104", inClusters: "0000, 0005, 0004, 0006", outClusters: "0000", manufacturer: "ORVIBO", model: "095db3379e414477ba6c2f7e0c6aa026", deviceJoinName: "Orvibo Switch" //Orvibo Smart Switch @@ -198,4 +199,6 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() -} \ No newline at end of file +} + + 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 0bc16af7238..e580330b3fc 100644 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -29,8 +29,9 @@ metadata { command "pause" // NodOn - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0021", manufacturer: "NodOn", model: "SIN-4-RS-20", deviceJoinName: "NodOn Window Treatment" - + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-RS-20", deviceJoinName: "NodOn Window Treatment" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-RS-20_PRO", deviceJoinName: "NodOn Window Treatment" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0102", outClusters: "0019", model: "E2B0-KR000Z0-HA", deviceJoinName: "eZEX Window Treatment" // SY-IoT201-BD //SOMFY Blind Controller/eZEX fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.6", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.8", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) From ebd2a4a9e4d75052bd7aae10a9c88486a1402b0f Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Tue, 8 Mar 2022 08:55:13 +0900 Subject: [PATCH 368/422] Update led-cpx-light.groovy Added an ocf device type. Added initial dimming level. --- .../zigbee-motion-sensor-light.src/led-cpx-light.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy index 074c8021d35..5af6d858682 100644 --- a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy @@ -17,7 +17,7 @@ */ metadata { - definition(name: "LED CPX light", namespace: "SAMSUNG LED", author: "SAMSUNG LED") { + definition(name: "LED CPX light", namespace: "SAMSUNG LED", author: "SAMSUNG LED", ocfDeviceType: "oic.d.light") { capability "Actuator" capability "Color Temperature" @@ -144,7 +144,10 @@ def setColorTemperature(value) { } def installed() { - addChildSensor() + if ((device.currentState("level")?.value == null) || (device.currentState("level")?.value == 0)) { + sendEvent(name: "level", value: 100) + } + addChildSensor() } def addChildSensor() { From f4c921a6c048126b2e56569db4e337aab6156ffe Mon Sep 17 00:00:00 2001 From: dsobierajsk Date: Wed, 9 Mar 2022 14:21:13 +0100 Subject: [PATCH 369/422] ICP-13805 Added YMF40 to bad models which reports battery incorrectly --- devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index abeb1c082c6..2121d9d531a 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -1135,7 +1135,7 @@ def isYaleLock() { } def isYaleFingerprintLock() { - return "ASSA ABLOY iRevo" == device.getDataValue("manufacturer") && ("iZBModule01" || "c700000202" || "0700000001" == device.getDataValue("model")) + return "ASSA ABLOY iRevo" == device.getDataValue("manufacturer") && ("iZBModule01" || "c700000202" || "0700000001" || "06ffff2027" == device.getDataValue("model")) } /** @@ -1150,7 +1150,8 @@ def reportsBatteryIncorrectly() { "YRD210 PB DB", "YRD220/240 TSDB", "YRL210 PB LL", - "c700000202" //YDF40 + "c700000202", //YDF40 + "06ffff2027" //YMF40 ] return device.getDataValue("model") in badModels } From 5bcf02236755e2881c37a1a0dd2a4d52e5d6e527 Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Thu, 10 Mar 2022 17:04:44 -0500 Subject: [PATCH 370/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZEN51 Dry Contact Relay (#77898) * DevWs for Zooz (The Smartest House) containing containing Zooz ZEN51 Dry Contact Relay * Zooz ZEN51 Dry Contact Relay changes * Zooz ZEN51 Dry Contact Relay changes --- .../zooz-zen51-dry-contact-relay.groovy | 300 ++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 devicetypes/zooz/zooz-zen51-dry-contact-relay.src/zooz-zen51-dry-contact-relay.groovy diff --git a/devicetypes/zooz/zooz-zen51-dry-contact-relay.src/zooz-zen51-dry-contact-relay.groovy b/devicetypes/zooz/zooz-zen51-dry-contact-relay.src/zooz-zen51-dry-contact-relay.groovy new file mode 100644 index 00000000000..faa8fe2c45f --- /dev/null +++ b/devicetypes/zooz/zooz-zen51-dry-contact-relay.src/zooz-zen51-dry-contact-relay.groovy @@ -0,0 +1,300 @@ +/* + * Zooz ZEN51 Dry Contact Relay + * + * Changelog: + * + * 2022-03-09 + * - requested change. + * 2022-03-02 + * - Removed central scene setting + * 2022-03-01 + * - Publication Release + * + * Copyright 2022 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x20: 1, // Basic + 0x22: 1, // ApplicationStatus + 0x25: 1, // SwitchBinary + 0x55: 1, // TransportService + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5B: 1, // CentralScene + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd + 0x85: 2, // Association + 0x86: 1, // Version + 0x87: 2, // Indicator (3) + 0x8E: 2, // MultiChannelAssociation + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 +] + +@Field static int btnPushed = 0 +@Field static int btnReleased = 1 +@Field static int btnHeld = 2 +@Field static List supportedButtonValues = ["pushed","held","pushed_2x","pushed_3x","pushed_4x","pushed_5x"] + +@Field static Map configParams = [ + ledIndicator: [num:1, title:"Led indicator", size:1, defaultVal:1, options:[0:"Disabled", 1:"Enabled"]], + autoOff: [num:2, title:"Auto Off Timer", size:2, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(timer unit)"], + autoOn: [num:3, title:"Auto On Timer", size:2, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(timer unit)"], + timerUnit: [num:10, title:"Timer Unit", size:1, defaultVal:1, options:[1:"Minutes", 2:"Seconds"]], + statusAfterPowerFailure: [num:4, title:"On/Off Status After Power Failure", size:1, defaultVal:2, options:[0:"Forced off", 1:"Forced on", 2:"Restore previous state"]], + loadControl: [num:6, title:"Load Control", size:1, defaultVal:1, options:[0:"Disable Switch/ Enable Z-Wave", 1:"Enable Switch and Z-Wave", 2:"Disable Switch and Z-Wave"]], + switchType: [num:7, title:"Switch Type", size:1, defaultVal:2, options:[0:"Toggle Switch", 1:"Momentary Light Switch", 2:"Toggle Up On/Down Off", 3:"3-way Impulse Control", 4:"Garage Door Mode"]], + relayBehavior: [num:9, title:"Relay Type Behavior", size:1, defaultVal:0, options:[0:"Normally Open (NO)", 1:"Normally Closed (NC)"]], + impulseDuration: [num:11, title:"Impulse Duration for 3-way", size:1, defaultVal:10, range:"2..200", desc:"2..200 (seconds)"] +] + +metadata { + definition ( + name: "Zooz ZEN51 Dry Contact Relay", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType: "oic.d.light", + mnmn: "SmartThingsCommunity", + vid: "d4bdecb2-4374-3c96-aceb-24223399fe5f" + ) { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Button" + capability "Refresh" + capability "Health Check" + + // zw:Ls2a type:1000 mfr:027A prod:0104 model:0201 ver:1.24 zwv:7.15 lib:03 cc:5E,55,9F,6C,22 sec:25,70,85,59,8E,86,72,5A,73,7A,5B,87 + fingerprint mfr: "027A", prod: "0104", model: "0201", deviceJoinName: "Zooz Switch" // Zooz ZEN51 Dry Contact Relay + } + + preferences { + configParams.each { name, param -> + if (param.options) { + input name, "enum", + title: param.title, + description: "Default: ${param.options[param.defaultVal]}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input name, "number", + title: param.title, + description: "${param.desc} - Default: ${param.defaultVal}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + } +} + +def installed() { + log.debug "installed()..." + initialize() + state.firstConfig = true +} + +def updated() { + log.debug "updated()..." + initialize() + + if (!state.firstConfig) { + configure() + } else { + state.firstConfig = false + } +} + +void initialize() { + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((60 * 60 * 3) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + if (!device.currentValue("numberOfButtons")) { + sendEvent(name:"numberOfButtons", value:1, displayed:false) + sendEvent(name: "supportedButtonValues", value: supportedButtonValues.encodeAsJSON(), displayed: false) + sendButtonEvent("pushed") + } +} + +def configure() { + log.debug "configure()..." + List cmds = [] + + if (device.currentValue("switch") == null) { + cmds << secureCmd(zwave.switchBinaryV1.switchBinaryGet()) + } + + configParams.each { name, param -> + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if (storedVal != settingVal) { + log.debug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: settingVal)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + if (cmds) { + sendHubCommand(cmds, 500) + } +} + +def on() { + log.debug "on()..." + return [ secureCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF)) ] +} + +def off() { + log.debug "off()..." + return [ secureCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00)) ] +} + +def ping() { + log.debug "ping()..." + return [ secureCmd(zwave.switchBinaryV1.switchBinaryGet()) ] +} + +def refresh() { + log.debug "refresh()..." + sendHubCommand([ secureCmd(zwave.switchBinaryV1.switchBinaryGet()) ]) +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + int val = cmd.scaledConfigurationValue + + if (val < 0) { + // device uses signed values + val = (val + Math.pow(256, cmd.size)) + } + + state[name] = val + log.debug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${val}" + } else { + log.debug "Parameter #${cmd.parameterNumber} = ${cmd.scaledConfigurationValue}" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + log.debug "${cmd}" + sendSwitchEvent(cmd.value) +} + +void zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + log.debug "${cmd}" + sendSwitchEvent(cmd.value) +} + +void sendSwitchEvent(int rawValue) { + String value = (rawValue ? "on" : "off") + log.debug("Switch is ${value}") + sendEvent(name: "switch", value: value) +} + +void zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + if (state.lastSequenceNumber != cmd.sequenceNumber) { + state.lastSequenceNumber = cmd.sequenceNumber + + String value + switch (cmd.keyAttributes){ + case btnPushed: + value = "pushed" + break + case btnReleased: + // value = released" + log.debug "Button Value 'released' is not supported by SmartThings" + break + case btnHeld: + value = "held" + break + default: + value = "pushed_${cmd.keyAttributes - 1}x" + } + + if (value) { + sendButtonEvent(value) + } + } +} + +void sendButtonEvent(String value) { + log.debug "button ${value}" + sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Unhandled zwaveEvent: $cmd" +} + +Integer getSettingVal(String name) { + Integer value = safeToInt(settings[name], null) + if ((value == null) && (getStoredVal(name) != null)) { + return configParams[name].defaultVal + } else { + return value + } +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} \ No newline at end of file From 810faf5771956d8d48b2d1da37c3fdfb3f2ac17a Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Thu, 10 Mar 2022 17:05:07 -0500 Subject: [PATCH 371/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZSE11 Q Sensor (#77897) * DevWs for Zooz (The Smartest House) containing containing Zooz ZSE11 Q Sensor * Zooz ZSE11 Q Sensor changes * Zooz ZSE11 Q Sensor changes --- .../zooz-zse11-q-sensor.groovy | 429 ++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 devicetypes/zooz/zooz-zse11-q-sensor.src/zooz-zse11-q-sensor.groovy diff --git a/devicetypes/zooz/zooz-zse11-q-sensor.src/zooz-zse11-q-sensor.groovy b/devicetypes/zooz/zooz-zse11-q-sensor.src/zooz-zse11-q-sensor.groovy new file mode 100644 index 00000000000..ea4f04ea16e --- /dev/null +++ b/devicetypes/zooz/zooz-zse11-q-sensor.src/zooz-zse11-q-sensor.groovy @@ -0,0 +1,429 @@ +/* + * Zooz ZSE11 Q Sensor + * + * Changelog: + * + * 2022-03-09 + * - Requested changes + * 2022-03-02 + * - Requested changes + * 2022-03-01 + * - Publication Release + * + * Copyright 2022 Zooz + * + * 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. + * +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x30: 2, // SensorBinary + 0x31: 5, // SensorMultilevel + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x71: 3, // Notification + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x7A: 2, // FirmwareUpdateMd + 0x80: 1, // Battery + 0x84: 2, // WakeUp + 0x85: 2, // Association + 0x86: 1, // Version + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 +] + +@Field static String batteryCC = "80" +@Field static int homeSecurity = 7 +@Field static int homeSecurityTamper = 3 +@Field static int tempSensorType = 1 +@Field static int lightSensorType = 3 +@Field static int humiditySensorType = 5 +@Field static int motionSensorType = 12 + +metadata { + definition ( + name: "Zooz ZSE11 Q Sensor", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType: "x.com.st.d.sensor.motion", + mnmn: "SmartThingsCommunity", + vid: "42067896-6424-3a34-b753-b87d8c92262f" + ) { + capability "Sensor" + capability "Motion Sensor" + capability "Tamper Alert" + capability "Temperature Measurement" + capability "Illuminance Measurement" + capability "Relative Humidity Measurement" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "Power Source" + + // zw:Ss2 type:0701 mfr:027A prod:0200 model:0006 ver:1.09 zwv:6.04 lib:03 cc:5E,6C,55,98,9F sec:86,72,71,59,85,80,84,73,30,31,70,5A,7A + fingerprint mfr:"027A", prod:"0200", model:"0006", deviceJoinName: "Zooz Multipurpose Sensor" // Zooz ZSE11 Q Sensor (EU) + // zw:Ss2 type:0701 mfr:027A prod:0201 model:0006 ver:1.09 zwv:6.04 lib:03 cc:5E,6C,55,98,9F sec:86,72,71,59,85,80,84,73,30,31,70,5A,7A + fingerprint mfr:"027A", prod:"0201", model:"0006", deviceJoinName: "Zooz Multipurpose Sensor" // Zooz ZSE11 Q Sensor (US) + // zw:Ss2 type:0701 mfr:027A prod:0202 model:0006 ver:1.09 zwv:6.04 lib:03 cc:5E,6C,55,98,9F sec:86,72,71,59,85,80,84,73,30,31,70,5A,7A + fingerprint mfr:"027A", prod:"0202", model:"0006", deviceJoinName: "Zooz Multipurpose Sensor" // Zooz ZSE11 Q Sensor (AU) + } + + preferences { + configParams.each { param -> + if (param.options) { + input "configParam${param.num}", "enum", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input "configParam${param.num}", "number", + title: "${param.name}:", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + + input "tempOffset", "decimal", + title: "Temperature Offset:", + required: false, + defaultValue: 0, + range: "-50..50" + + input "humidityOffset", "number", + title: "Humidity Offset:", + required: false, + defaultValue: 0, + range: "-50..50" + + input "lightOffset", "number", + title: "Light Offset:", + required: false, + defaultValue: 0, + range: "-20000..20000" + } +} + +def installed() { + log.debug "installed()..." + state.firstConfig = true + initialize() +} + +def updated() { + log.debug "updated()..." + + initialize() + + if (!state.firstConfig) { + if (device.currentValue("powerSource") == "battery") { + logForceWakeupMessage("Configuration changes will be sent to the device the next time it wakes up.") + } else { + sendHubCommand(getConfigCmds()) + } + } else { + sendHubCommand(getRefreshCmds()) + state.firstConfig = false + } +} + +void initialize() { + if (!device.currentValue("checkInterval")) { + sendEvent(name: "checkInterval", value: ((60 * 60 * 24) + (60 * 5)), displayed: false, data:[protocol: "zwave", hubHardwareId: device.hub.hardwareID]) + } + + if (!device.currentValue("tamper")) { + sendEvent(name: "tamper", value: "clear") + } + + if (device.currentValue("powerSource") == null) { + boolean hasBatteryCC = ((zwaveInfo?.cc?.find { it.toString() == batteryCC }) || (zwaveInfo?.sec?.find { it.toString() == batteryCC })) + + String powerSource = (hasBatteryCC ? "battery" : "dc") + sendEvent(name: "powerSource", value: powerSource) + + if (powerSource == "dc") { + sendEvent(name: "battery", value: 100, unit: "%") + } + } + + sendTempEvent(state.reportedTemp) + sendLightEvent(state.reportedLight) + sendHumidityEvent(state.reportedHumidity) +} + +def configure() { + log.debug "configure()..." + sendHubCommand(getConfigCmds(), 200) +} + +List getConfigCmds() { + List cmds = [] + + configParams.each { param -> + def storedVal = safeToInt(state["configVal${param.num}"] , null) + if ("${storedVal}" != "${param.value}") { + log.debug "Changing ${param.name}(#${param.num}) from ${storedVal} to ${param.value}" + cmds << secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: param.size, scaledConfigurationValue: param.value)) + cmds << secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) + } + } + return cmds +} + +def refresh() { + log.debug "refresh()..." + + if (device.currentValue("tamper") != "clear") { + sendEvent(name:"tamper", value:"clear") + } + + if (device.currentValue("powerSource") == "battery") { + state.pendingRefresh = true + logForceWakeupMessage("The sensor values will be requested the next time the device wakes up.") + } else { + sendHubCommand(getRefreshCmds()) + } +} + +void logForceWakeupMessage(msg) { + log.debug "${msg} You can force the device to wake up immediately by holding the z-button for 3 seconds." +} + +List getRefreshCmds() { + return [ + secureCmd(zwave.sensorBinaryV2.sensorBinaryGet(sensorType: motionSensorType)), + sensorMultilevelGetCmd(tempSensorType), + sensorMultilevelGetCmd(lightSensorType), + sensorMultilevelGetCmd(humiditySensorType), + batteryGetCmd() + ] +} + +String sensorMultilevelGetCmd(sensorType) { + def scale = (sensorType == tempSensorType ? 0 : 1) + return secureCmd(zwave.sensorMultilevelV5.sensorMultilevelGet(scale: scale, sensorType: sensorType)) +} + +String batteryGetCmd() { + return secureCmd(zwave.batteryV1.batteryGet()) +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) { + log.debug "Device Woke Up" + List cmds = [] + + if (state.pendingRefresh) { + state.pendingRefresh = false + cmds += getRefreshCmds() + } + + cmds += getConfigCmds() + + if (!cmds) { + cmds << batteryGetCmd() + } + + cmds << secureCmd(zwave.wakeUpV1.wakeUpNoMoreInformation()) + sendHubCommand(cmds) +} + +void zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) { + int val = (cmd.batteryLevel == 0xFF ? 1 : cmd.batteryLevel) + val = Math.min(Math.max(1, val), 100) + + if (device.currentValue("powerSource") != "battery") { + log.debug "powerSource is battery" + sendEvent(name:"powerSource", value:"battery") + } + + log.debug "battery is ${val}%" + sendEvent(name:"battery", value:val, unit:"%", isStateChange:true) +} + +void zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + switch (cmd.sensorType) { + case tempSensorType: + def temp = convertTemperatureIfNeeded(cmd.scaledSensorValue, (cmd.scale ? "F" : "C"), cmd.precision) + sendTempEvent(temp) + break + case lightSensorType: + sendLightEvent(cmd.scaledSensorValue) + break + case humiditySensorType: + sendHumidityEvent(cmd.scaledSensorValue) + break + default: + log.debug "Unhandled: ${cmd}" + } +} + +void sendTempEvent(reportedVal) { + reportedVal = safeToDec(reportedVal) + state.reportedTemp = reportedVal + + def adjVal = (safeToDec(settings?.tempOffset) + reportedVal) + log.debug "temperature is ${adjVal}°${temperatureScale}" + sendEvent(name:"temperature", value:adjVal, unit:temperatureScale) +} + +void sendLightEvent(reportedVal) { + reportedVal = safeToInt(reportedVal) + if (reportedVal < 0) { + // workaround for bug in original firmware + reportedVal = (reportedVal + 65536) + } + state.reportedLight = reportedVal + + def adjVal = (safeToInt(settings?.lightOffset) + reportedVal) + if (adjVal < 0) adjVal = 0 + log.debug "illuminance is ${adjVal}lux" + sendEvent(name:"illuminance", value:adjVal, unit:"lux") +} + +void sendHumidityEvent(reportedVal) { + reportedVal = safeToInt(reportedVal) + state.reportedHumidity = reportedVal + + def adjVal = (safeToInt(settings?.humidityOffset) + reportedVal) + adjVal = Math.min(Math.max(0, adjVal), 100) + log.debug "humidity is ${adjVal}%" + sendEvent(name:"humidity", value:adjVal, unit:"%") +} + +void zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) { + if (cmd.notificationType == homeSecurity) { + if ((cmd.event == homeSecurityTamper) || (cmd.eventParameter[0] == homeSecurityTamper)) { + String value = (cmd.event ? "detected" : "clear") + log.debug "tamper is ${value}" + sendEvent(name:"tamper", value:value) + } + } +} + +void zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { + if (cmd.sensorType == motionSensorType) { + String value = (cmd.sensorValue ? "active" : "inactive") + log.debug "motion is ${value}" + sendEvent(name:"motion", value:value) + } +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + def param = configParams.find { it.num == cmd.parameterNumber } + if (param) { + def val = cmd.scaledConfigurationValue + log.debug "${param.name}(#${param.num}) = ${val}" + state["configVal${param.num}"] = val + } +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Ignored Command: $cmd" +} + +List getConfigParams() { + [ + motionSensitivityParam, + motionResetParam, + motionLedParam, + reportingFrequencyParam, + temperatureThresholdParam, + humidityThresholdParam, + lightThresholdParam + ] +} + +Map getMotionSensitivityParam() { + return getParam(12, "Motion Sensitivity", 1, 6, [0:"Motion Disabled", 1:"1 - Least Sensitive", 2:"2", 3:"3", 4:"4", 5:"5", 6:"6 [DEFAULT]", 7:"7", 8:"8 - Most Sensitive"]) +} + +Map getMotionResetParam() { + return getParam(13, "Motion Clear Time (10-3600 Seconds)", 2, 30, null, "10..3600") +} + +Map getMotionLedParam() { + return getParam(19, "Motion LED", 1, 1, [0:"Disabled", 1:"Enabled [DEFAULT]"]) +} + +Map getReportingFrequencyParam() { + return getParam(172, "Minimum Reporting Frequency (1-774 Hours)", 2, 4, null, "1..744") +} + +Map getTemperatureThresholdParam() { + return getParam(183, "Temperature Reporting Threshold (1-144°F)", 2, 1, null, "1..144") +} + +Map getHumidityThresholdParam() { + return getParam(184, "Humidity Reporting Threshold (0:No Reports, 1-80%)", 1, 5, null, "0..80") +} + +Map getLightThresholdParam() { + return getParam(185, "Light Reporting Threshold (0:No Reports, 1-30000 lux)", 2, 50, null, "0..30000") +} + +Map getParam(Integer num, String name, Integer size, Integer defaultVal, Map options, range=null) { + Integer val = safeToInt((settings ? settings["configParam${num}"] : null), defaultVal) + + return [num: num, name: name, size: size, defaultVal: defaultVal, value: val, options: options, range: range] +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} + +BigDecimal safeToDec(val, BigDecimal defaultVal=0) { + return "${val}"?.isBigDecimal() ? "${val}".toBigDecimal() : defaultVal +} \ No newline at end of file From 688e0253cc3d895166ad33cc9482132cc9895538 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 15 Mar 2022 00:03:49 +0800 Subject: [PATCH 372/422] DevWs for NIE-TECH CO., LTD. containing containing ZigBee Switch (#77938) * DevWs for NIE-TECH CO., LTD. containing containing ZigBee Switch * Except the modifications, others remain the same as the master. * Except the modifications, others remain the same as the master. Co-authored-by: Winnie Wen --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index b0293c12ea5..6814c50d000 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -32,6 +32,9 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "eWeLink", model: "SA-003-Zigbee", deviceJoinName: "eWeLink Outlet", ocfDeviceType: "oic.d.smartplug" //eWeLink SmartPlug (SA-003) fingerprint profileId: "0104", inClusters: "0000,0003,0004,00005,0006", outClusters: "0000", manufacturer: "eWeLink", model: "ZB-SW01", deviceJoinName: "eWeLink Switch" + // Minoston + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0000", manufacturer: "Minoston", model: "ZB36S", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Minoston SmartPlug + // LELLKI fingerprint profileId: "0104", inClusters: "0000,0003,0004,00005,0006", outClusters: "0000", manufacturer: "LELLKI", model: "JZ-ZB-001", deviceJoinName: "LELLKI Switch" From be1aeca6d181369e542dac671e7e57acee25fa65 Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 14 Mar 2022 10:43:58 -0700 Subject: [PATCH 373/422] Revert "Update Orvibo-Contact-Sensor.groovy" This reverts commit a5c225a3dcd3b90756c0558ecf0cda224db82dea. --- .../Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index 9888fae4fb6..0df9da8f9a7 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -166,11 +166,10 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] def manufacturer = getDataValue("manufacturer") - def application = getDataValue("application") if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application <= "17") { + if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From 45cdecd145623ec2a5ddfe6fc496893eaeb492c3 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 15 Mar 2022 17:31:09 +0900 Subject: [PATCH 374/422] DevWs for SHINA SYSTEM containing containing Zigbee Power Meter (#77947) --- .../sihas-zigbee-power-meter.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy b/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy index fd7d4091644..b64146158b3 100644 --- a/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy +++ b/devicetypes/shinasys/sihas-zigbee-power-meter.src/sihas-zigbee-power-meter.groovy @@ -143,7 +143,7 @@ def configure() { sendEvent(name: "checkInterval", value: 2 * 60 + 10 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting" - configCmds = zigbee.simpleMeteringPowerConfig() + + configCmds = zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_HISTORICAL_CONSUMPTION, DataType.INT24, 5, 600, 1) + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 5, 600, 1) + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_FREQUENCY, DataType.UINT16, 10, 600, 3) + /* 3 unit : 0.3Hz */ zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_VOLTAGE, DataType.UINT16, 5, 600, 3) + /* 3 unit : 0.3V */ @@ -159,4 +159,4 @@ private getEnergyDivisor() { 1000 } private getFrequencyDivisor() { 10 } private getVoltageDivisor() { 10 } private getCurrentDivisor() { 100 } -private getPowerFactorDivisor() { 1 } +private getPowerFactorDivisor() { 1 } \ No newline at end of file From eb59c8c6bc1a64b1861c420a3d7d3ba8cc733f25 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 15 Mar 2022 18:08:31 +0900 Subject: [PATCH 375/422] DevWs for SHINA SYSTEM containing containing SiHAS Zigbee Metering Plug (#77948) --- .../sihas-zigbee-metering-plug.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy b/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy index c400f0255c0..6d291988590 100644 --- a/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy +++ b/devicetypes/shinasys/sihas-zigbee-metering-plug.src/sihas-zigbee-metering-plug.groovy @@ -156,7 +156,7 @@ def configure() { log.debug "Configuring Reporting" configCmds = zigbee.onOffConfig() + - zigbee.simpleMeteringPowerConfig() + + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_HISTORICAL_CONSUMPTION, DataType.INT24, 5, 600, 1) + zigbee.configureReporting(zigbee.SIMPLE_METERING_CLUSTER, ATTRIBUTE_READING_INFO_SET, DataType.UINT48, 5, 600, 1) + zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_FREQUENCY, DataType.UINT16, 10, 600, 3) + /* 3 unit : 0.3Hz */ zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, ATTRIBUTE_VOLTAGE, DataType.UINT16, 5, 600, 3) + /* 3 unit : 0.3V */ @@ -172,4 +172,4 @@ private getEnergyDivisor() { 1000 } private getFrequencyDivisor() { 10 } private getVoltageDivisor() { 10 } private getCurrentDivisor() { 100 } -private getPowerFactorDivisor() { 1 } +private getPowerFactorDivisor() { 1 } \ No newline at end of file From ac64e68a990c93ee1dc10d9edf953e6f20663ba8 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 15 Mar 2022 18:58:17 +0900 Subject: [PATCH 376/422] DevWs for SHINA SYSTEM containing containing Zigbee Multi Button (#77951) --- .../zigbee-multi-button.src/zigbee-multi-button.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy index 628bd4486ca..c829563ca9b 100644 --- a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy +++ b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy @@ -37,11 +37,11 @@ metadata { fingerprint inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FCCC, 1000", outClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, FCCC, 1000", manufacturer: "ADUROLIGHT", model: "ADUROLIGHT_CSC", deviceJoinName: "Eria Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Eria scene button switch V2.0 fingerprint inClusters: "0000, 0003, 0008, FCCC, 1000", outClusters: "0003, 0004, 0006, 0008, FCCC, 1000", manufacturer: "AduroSmart Eria", model: "Adurolight_NCC", deviceJoinName: "Eria Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Eria dimming button switch V2.1 fingerprint inClusters: "0000, 0003, 0008, FCCC, 1000", outClusters: "0003, 0004, 0006, 0008, FCCC, 1000", manufacturer: "ADUROLIGHT", model: "Adurolight_NCC", deviceJoinName: "Eria Remote Control", mnmn: "SmartThings", vid: "generic-4-button" //Eria dimming button switch V2.0 - fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "MSM-300Z", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" - fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "BSM-300Z", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" - fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB1", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" - fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB2", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" - fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB3", deviceJoinName: "SiHAS Remote Control", mnmn: "0Ar2", vid: "ST_9639674b-8026-4f61-9579-585cd0fe1fad" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "MSM-300Z", deviceJoinName: "SiHAS Remote Control", mnmn: "SmartThingsCommunity", vid: "b18d7e4e-3775-3606-85a6-14b63cd8a0e3" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "BSM-300Z", deviceJoinName: "SiHAS Remote Control", mnmn: "SmartThingsCommunity", vid: "0b6ace5f-e2d8-3e34-9b2a-5662bc9e20e1" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB1", deviceJoinName: "SiHAS Remote Control", mnmn: "SmartThingsCommunity", vid: "0b6ace5f-e2d8-3e34-9b2a-5662bc9e20e1" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB2", deviceJoinName: "SiHAS Remote Control", mnmn: "SmartThingsCommunity", vid: "57bb4dc5-40ef-335f-8e60-cc63190cc73b" + fingerprint inClusters: "0000,0001,0003,0020", outClusters: "0003,0004,0006,0019", manufacturer: "ShinaSystem", model: "SBM300ZB3", deviceJoinName: "SiHAS Remote Control", mnmn: "SmartThingsCommunity", vid: "f3f3ab0e-82f5-36dd-839f-a048e1a3f8f9" } tiles { From 53a1598159573de9a608e8b142e42a7c6065898b Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Tue, 15 Mar 2022 22:42:13 +0800 Subject: [PATCH 377/422] Update orvibo-Moisture-sensor.groovy (#77886) * Update orvibo-Moisture-sensor.groovy update thirdreality battery percent report Update * update orvibo-Moisture-sensor.groovy update orvibo-Moisture-sensor.groovy * update orvibo-moisture-sensor.groovy update orvibo-moisture-sensor.groovy --- .../orvibo-Moisture-Sensor.groovy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy index f7addb7e9f6..c2efdb53720 100644 --- a/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy +++ b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy @@ -35,6 +35,7 @@ metadata { fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001, 0009", outClusters: "0019", manufacturer: "HEIMAN", model: "da2edf1ded0d44e1815d06f45ce02029", deviceJoinName: "Orvibo Water Leak Sensor" fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001", manufacturer: "HEIMAN", model: "WaterSensor-N", deviceJoinName: "HEIMAN Water Leak Sensor" //HEIMAN Water Leakage Sensor (HS3WL-E) fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0006,0019", manufacturer:"Third Reality, Inc", model:"3RWS18BZ", deviceJoinName: "ThirdReality Water Leak Sensor" //ThirdReality WaterLeak Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0006,0019", manufacturer:"THIRDREALITY", model:"3RWS18BZ", deviceJoinName: "ThirdReality Water Leak Sensor" //ThirdReality WaterLeak Sensor } simulator { @@ -156,11 +157,12 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage" def result = [:] def manufacturer = getDataValue("manufacturer") + def application = getDataValue("application").toInteger() if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if (manufacturer == "Third Reality, Inc") { + if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application <= 17) { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) @@ -170,4 +172,4 @@ def getBatteryPercentageResult(rawValue) { log.debug "${device.displayName} battery was ${result.value}%" result -} \ No newline at end of file +} From 478d5b378c2204bc520047c8c1235ab7b408fc2c Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Tue, 15 Mar 2022 22:42:58 +0800 Subject: [PATCH 378/422] Update zigbee-motion-detector.groovy (#77885) * Update zigbee-motion-detector.groovy update thirdreality battery percent report * update zigbee-motion-detector.groovy update zigbee-motion-detector.groovy * update zigbee-motion-detector.groovy update zigbee-motion-detector.groovy --- .../zigbee-motion-detector.src/zigbee-motion-detector.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy index 14003b028af..2cc8d98d1ce 100644 --- a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy +++ b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy @@ -32,6 +32,7 @@ metadata { fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000,0003,0500,0001,FFFF", manufacturer: "Megaman", model: "PS601/z1", deviceJoinName: "INGENIUM Motion Sensor" //INGENIUM ZB PIR Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0003, 0500, 0001", outClusters: "0019", manufacturer: "HEIMAN", model: "PIRSensor-N", deviceJoinName: "HEIMAN Motion Sensor" //HEIMAN Motion Sensor fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RMS16BZ", deviceJoinName: "ThirdReality Motion Sensor" //ThirdReality Motion Sensor + fingerprint profileId: "0104", deviceId: "0402", inClusters: "0000, 0001, 0500", outClusters: "0019", manufacturer: "THIRDREALITY", model: "3RMS16BZ", deviceJoinName: "ThirdReality Motion Sensor" //ThirdReality Motion Sensor } simulator { status "active": "zone status 0x0001 -- extended status 0x00" @@ -128,11 +129,12 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] def manufacturer = getDataValue("manufacturer") + def application = getDataValue("application").toInteger() if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if (manufacturer == "Third Reality, Inc") { + if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application <= 17) { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From 5edcea2ff97ab7f885fb5714c7a323cb8a0cb0e5 Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Tue, 15 Mar 2022 22:43:52 +0800 Subject: [PATCH 379/422] Update smartsense-button.groovy (#77934) * Update smartsense-button.groovy Update smartsense-button.groovy * update smartsense-button.groovy update smartsense-button.groovy * update smartsense-button.groovy update smartsense-button.groovy --- .../smartsense-button.src/smartsense-button.groovy | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy index 209c08ad376..c301d5c7a6f 100755 --- a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy +++ b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy @@ -28,6 +28,7 @@ metadata { capability "Sensor" fingerprint inClusters: "0000,0001,0003,0020,0402,0500", outClusters: "0019", manufacturer: "Samjin", model: "button", deviceJoinName: "Button" + fingerprint inClusters: "0000,0001,0012", outClusters: "0006,0008,0019", manufacturer: "Third Reality, Inc", model: "3RSB22BZ", deviceJoinName: "ThirdReality Smart Button" } simulator { @@ -136,6 +137,8 @@ def parse(String description) { } } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) { map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value))) + } else if ( descMap.clusterInt == 0x0012 ) { + map = translateMultiStatus(descMap.value) } } } else if (map.name == "temperature") { @@ -174,6 +177,16 @@ private Map translateZoneStatus(ZoneStatus zs) { } else { } } +private Map translateMultiStatus(String value) { + if (value == "0002" ) { + return getButtonResult('double') + } else if (value == "0001" ) { + return getButtonResult('pushed') + } else if (value == "0000"){ + return getButtonResult('held') + } else {} +} + private Map getBatteryResult(rawValue) { log.debug "Battery rawValue = ${rawValue}" def linkText = getLinkText(device) From 3d3d88c9748b8b4826433023acb170811e6bf5af Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:45:31 +0800 Subject: [PATCH 380/422] update zigbee-motion-detector.groovy update zigbee-motion-detector.groovy --- .../zigbee-motion-detector.src/zigbee-motion-detector.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy index 2cc8d98d1ce..846e3722bdf 100644 --- a/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy +++ b/devicetypes/smartthings/zigbee-motion-detector.src/zigbee-motion-detector.groovy @@ -129,12 +129,12 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] def manufacturer = getDataValue("manufacturer") - def application = getDataValue("application").toInteger() + def application = getDataValue("application") if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application <= 17) { + if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application.toInteger() <= 17) { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From 316637bd334a9f10f85d86d437b7e447fb61f5ac Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:48:35 +0800 Subject: [PATCH 381/422] update orvibo-moisture-sensor.groovy update orvibo-moisture-sensor.groovy --- .../orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy index c2efdb53720..fe909265b8f 100644 --- a/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy +++ b/devicetypes/smartthings/orvibo-Moisture-Sensor.src/orvibo-Moisture-Sensor.groovy @@ -157,12 +157,12 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage" def result = [:] def manufacturer = getDataValue("manufacturer") - def application = getDataValue("application").toInteger() + def application = getDataValue("application") if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application <= 17) { + if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application.toInteger() <= 17) { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From 72b6baa41a2ee91494770a6bc98448f63cf20a63 Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:52:54 +0800 Subject: [PATCH 382/422] update orvibo-contact-sensor.groovy update orvibo-contact-sensor.groovy --- .../Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy index 0df9da8f9a7..04d568956e8 100755 --- a/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy +++ b/devicetypes/smartthings/Orvibo-Contact-Sensor.src/Orvibo-Contact-Sensor.groovy @@ -166,10 +166,12 @@ def getBatteryPercentageResult(rawValue) { log.debug "Battery Percentage rawValue = ${rawValue} -> ${rawValue / 2}%" def result = [:] def manufacturer = getDataValue("manufacturer") + def application = getDataValue("application") + if (0 <= rawValue && rawValue <= 200) { result.name = 'battery' result.translatable = true - if (manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") { + if ((manufacturer == "Third Reality, Inc" || manufacturer == "THIRDREALITY") && application.toInteger() <= 17) { result.value = Math.round(rawValue) } else { result.value = Math.round(rawValue / 2) From 979628a4770c3f47700cf3019f1e8dbc923b8ebf Mon Sep 17 00:00:00 2001 From: Sarkis008 <92102906+Sarkis008@users.noreply.github.com> Date: Wed, 16 Mar 2022 17:23:58 +0400 Subject: [PATCH 383/422] DevWs for HELTUN containing HELTUN TPS05 Switch (#77846) * Create heltun-tps05-switch.groovy * Fix typos Change child device names * initial * initial * Indentation * Delete heltun-child-backlight.groovy * Requested changes * Requested changes * Requested changes 2 * Requested changes 3 * Indentation * Requested changed 4 * Identation 2 * identation fix Co-authored-by: Artur Sargsyan --- .../heltun-tps05-switch.groovy | 724 ++++++++++++++++++ 1 file changed, 724 insertions(+) create mode 100644 devicetypes/heltun/heltun-tps05-switch.src/heltun-tps05-switch.groovy diff --git a/devicetypes/heltun/heltun-tps05-switch.src/heltun-tps05-switch.groovy b/devicetypes/heltun/heltun-tps05-switch.src/heltun-tps05-switch.groovy new file mode 100644 index 00000000000..c86d131d699 --- /dev/null +++ b/devicetypes/heltun/heltun-tps05-switch.src/heltun-tps05-switch.groovy @@ -0,0 +1,724 @@ +/** + * HELTUN TPS05 Switch + * + * Copyright 2022 Sarkis Kabrailian + * + * 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. + */ + +import groovy.transform.Field + +@Field static int roomTemperature = 1 +@Field static int humidity = 5 +@Field static int illuminance = 3 + +metadata { + definition (name: "HELTUN TPS05 Switch", namespace: "HELTUN", author: "Sarkis Kabrailian", cstHandler: true, mcdSync: true ) { + capability "Temperature Measurement" + capability "Illuminance Measurement" + capability "Relative Humidity Measurement" + capability "Energy Meter" + capability "Power Meter" + capability "Configuration" + capability "Health Check" + capability "Refresh" + + fingerprint mfr: "0344", prod: "0004", model: "0003", deviceJoinName: "HELTUN Panel" + } + preferences { + input ( + title: "HE-TPS05 | HELTUN Touch Panel Switch", + description: "The user manual document with all technical information is available in support.heltun.com page. In case of technical questions please contact HELTUN Support Team at support@heltun.com", + type: "paragraph", + element: "paragraph" + ) + parameterMap().each { + if (it.title != null) { + input ( + title: "${it.title}", + description: it.description, + type: "paragraph", + element: "paragraph" + ) + } + def unit = it.unit ? it.unit : "" + def defV = it.default as Integer + def defVDescr = it.options ? it.options.get(defV) : "${defV}${unit} - Default Value" + input ( + name: it.name, + title: null, + description: "$defVDescr", + type: it.type, + options: it.options, + range: (it.min != null && it.max != null) ? "${it.min}..${it.max}" : null, + defaultValue: it.default, + required: false + ) + } + } +} + +def checkParam() { + boolean needConfig = false + parameterMap().each { + if (state."$it.name" == null || state."$it.name".state == "defNotConfigured") { + state."$it.name" = [value: it.default as Integer, state: "defNotConfigured"] + needConfig = true + } + if (settings."$it.name" != null && (state."$it.name".value != settings."$it.name" as Integer || state."$it.name".state == "notConfigured")) { + state."$it.name".value = settings."$it.name" as Integer + state."$it.name".state = "notConfigured" + needConfig = true + } + } + if (needConfig) { + configParam() + } +} + +private configParam() { + def cmds = [] + for (parameter in parameterMap()) { + if (state."$parameter.name"?.value != null && state."$parameter.name"?.state in ["notConfigured", "defNotConfigured"] ) { + cmds << zwave.configurationV2.configurationSet(scaledConfigurationValue: state."$parameter.name".value, parameterNumber: parameter.paramNum, size: parameter.size).format() + cmds << zwave.configurationV2.configurationGet(parameterNumber: parameter.paramNum).format() + break + } + } + if (cmds) { + runIn(5, "checkParam") + sendHubCommand(cmds,500) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) { + def map = [:] + def localScale = getTemperatureScale() //HubScale + def deviceScale = (cmd.scale == 1) ? "F" : "C" //DeviceScale + def child = childDevices?.find {channelNumber(it.deviceNetworkId) == 1 } + if (roomTemperature == cmd.sensorType) { + def deviceTemp = cmd.scaledSensorValue + def scaledTemp = (deviceScale == localScale) ? deviceTemp : (deviceScale == "F" ? roundC(fahrenheitToCelsius(deviceTemp)) : celsiusToFahrenheit(deviceTemp).toDouble().round(0).toInteger()) + map.name = "temperature" + map.value = scaledTemp + map.unit = localScale + sendEvent(map) + } else if (humidity == cmd.sensorType) { + map.name = "humidity" + map.value = cmd.scaledSensorValue.toInteger() + map.unit = "%" + sendEvent(map) + } else if (illuminance == cmd.sensorType) { + map.name = "illuminance" + map.value = cmd.scaledSensorValue + sendEvent(map) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) { + def parameter = parameterMap().find( {it.paramNum == cmd.parameterNumber } ).name + if (state."$parameter".value == cmd.scaledConfigurationValue){ + state."$parameter".state = "configured" + } else { + state."$parameter".state = "error" + } + configParam() +} + +def updated() { + if (childDevices && device.label != state.oldLabel) { + childDevices.each { + def newLabel = getChildName(channelNumber(it.deviceNetworkId)) + it.setLabel(newLabel) + } + state.oldLabel = device.label + } + initialize() +} + +def initialize() { + runIn(3, "checkParam") +} + +def installed() { + def numberOfButtons = 5 + state.numberOfButtons = numberOfButtons + def existingChildren = getChildDevices() + for (i in 1..numberOfButtons) { + def buttonNetworkId = "${device.deviceNetworkId}:${i+2*numberOfButtons}" + def relayNetworkId = "${device.deviceNetworkId}:${i+numberOfButtons}" + def backlightNetworkId = "${device.deviceNetworkId}:${i}" + def childRelayExists = (existingChildren.find {child -> child.getDeviceNetworkId() == relayNetworkId} != NULL) + def childButtonExists = (existingChildren.find {child -> child.getDeviceNetworkId() == buttonNetworkId} != NULL) + def childBacklightExists = (existingChildren.find {child -> child.getDeviceNetworkId() == backlightNetworkId} != NULL) + if (!childBacklightExists ) { + addChildDevice("smartthings","Child Switch", backlightNetworkId, device.hubId, [completedSetup: true, label: getChildName(i), isComponent: false]) + } + if (!childRelayExists) { + addChildDevice("HELTUN", "Heltun Child Relay", relayNetworkId, device.hubId,[completedSetup: true, label: getChildName(i+numberOfButtons), isComponent: false]) + } + if (!childButtonExists ) { + addChildDevice("smartthings", "Child Button", buttonNetworkId, device.hubId, [completedSetup: true, label: getChildName(i+2*numberOfButtons), isComponent: true, componentName: "button$i", componentLabel: "Button ${i}"]) + } + } + initialize() +} + +private getChildName(channelNumber) { + def prefix = device.displayName + if (prefix == "HELTUN Panel") { + prefix = "HELTUN" + } + def numberOfButtons = state.numberOfButtons + if (channelNumber in 1..numberOfButtons) { + return "${prefix} " + "${"Backlight"} " + "${channelNumber}" + } + else if (channelNumber in (numberOfButtons+1)..(2*numberOfButtons)){ + return "${prefix} " + "${"Switch"} " + "${channelNumber-numberOfButtons}" + } + else if (channelNumber in (2*numberOfButtons+1)..(3*numberOfButtons)){ + return "${prefix} " + "${"Button"} " + "${channelNumber-numberOfButtons*2}" + } +} + +private channelNumber(String deviceNetworkId) { + deviceNetworkId.split(":")[-1] as Integer +} + +def parse(String description) { + def cmd = zwave.parse(description) + if (cmd) { + return zwaveEvent(cmd) + } +} + +private void setState(value, endpoint = null) { + def cmds = [ + encap(zwave.basicV1.basicSet(value: value), endpoint), + encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint), + ] + sendHubCommand(cmds, 500) +} + +private encap(cmd, endpoint) { + if (endpoint) { + zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd).format() + } else { + cmd.format() + } +} + +def childOn(childId) { + setState(0xFF, channelNumber(childId)) +} + +def childOff(childId) { + setState(0x00, channelNumber(childId)) +} + +def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) { + def map = [:] + if (cmd.meterType == 1) { + if (cmd.scale == 0) { + map.name = "energy" + map.value = cmd.scaledMeterValue + map.unit = "kWh" + sendEvent(map) + } else if (cmd.scale == 2) { + map.name = "power" + map.value = Math.round(cmd.scaledMeterValue) + map.unit = "W" + sendEvent(map) + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd) { + log.info cmd + def numberOfButtons = state.numberOfButtons + def state + def buttonN = cmd.sceneNumber + switch (cmd.keyAttributes as Integer) { + case 0: + state = "pushed" + break + case 1: + state = "up" + break + case 2: + state = "held" + break + } + if (buttonN) { + def buttonId = buttonN + numberOfButtons * 2 + def child = childDevices?.find {channelNumber(it.deviceNetworkId) == buttonId } + child?.sendEvent([name: "button", value: state, data: [buttonNumber: 1], isStateChange: true]) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep = null) { + def encapsulatedCommand = cmd.encapsulatedCommand() + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) { + def numberOfButtons = state.numberOfButtons + def value = cmd.value + def childDevice = childDevices?.find {channelNumber(it.deviceNetworkId) == ep } + def corrRelCons = 0 + def corRelParam = 11 + ep - numberOfButtons + if (ep in numberOfButtons..(2*numberOfButtons)) { + def param = parameterMap().find( {it.paramNum == corRelParam } ).name + def paramState = state."$param" + if (paramState) { + corrRelCons = paramState.value + } + } + if (childDevice) { + childDevice.sendEvent(name: "switch", value: value ? "on" : "off") + if (value) { + sendEvent(name: "switch", value: "on") + childDevice.sendEvent(name: "power", value: corrRelCons, unit: "W") + } else { + childDevice.sendEvent(name: "power", value: 0, unit: "W") + if (!childDevices.any { it.currentValue("switch") == "on" }) { + sendEvent(name: "switch", value: "off") + } + } + } +} + +def zwaveEvent(physicalgraph.zwave.commands.clockv1.ClockReport cmd) { + def currDate = Calendar.getInstance(location.timeZone) + def time = [hour: currDate.get(Calendar.HOUR_OF_DAY), minute: currDate.get(Calendar.MINUTE), weekday: currDate.get(Calendar.DAY_OF_WEEK)] + if ((time.hour != cmd.hour) || (time.minute != cmd.minute) || (time.weekday != cmd.weekday)) { + sendHubCommand(zwave.clockV1.clockSet(time).format()) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelassociationv2.MultiChannelAssociationReport cmd) { + def cmds = [] + if (cmd.groupingIdentifier == 1) { + if (cmd.nodeId != [0, zwaveHubNodeId, 0]) { + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationRemove(groupingIdentifier: 1).format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationSet(groupingIdentifier: 1, nodeId: [0,zwaveHubNodeId,0]).format() + } + } + if (cmds) { + sendHubCommand(cmds, 1200) + } +} + +def configure() { + refresh() +} + +def getRefreshCommands() { + def numberOfButtons = state.numberOfButtons + def cmds = [] + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:roomTemperature).format() + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:humidity).format() + cmds << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:illuminance).format() + for (i in 1..(2 * numberOfButtons)) { + cmds << encap(zwave.switchBinaryV1.switchBinaryGet(), i) + } + return cmds +} + +def refresh() { + def cmds = getRefreshCommands() + cmds << zwave.clockV1.clockGet().format() + cmds << zwave.multiChannelAssociationV2.multiChannelAssociationGet(groupingIdentifier: 1).format() + sendHubCommand(cmds, 1200) + runIn(15, "checkParam") +} + +def ping() { + def cmds = getRefreshCommands() + sendHubCommand(cmds, 1200) +} + +def resetEnergyMeter() { + sendHubCommand(zwave.meterV3.meterReset().format()) +} + +private parameterMap() {[ + [title: "Relays Output Mode", description: "These Parameters determine the type of loads connected to the device relay outputs. " + + "The output type can be NO – normal open (no contact/voltage switch the load OFF) or NC - normal close (output is contacted / there is a voltage to switch the load OFF)", + name: "Selected Relay 1 Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 7, size: 1, default: "0", type: "enum"], + + [name: "Selected Relay 2 Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 8, size: 1, default: "0", type: "enum"], + + [name: "Selected Relay 3 Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 9, size: 1, default: "0", type: "enum"], + + [name: "Selected Relay 4 Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 10, size: 1, default: "0", type: "enum"], + + [name: "Selected Relay 5 Mode", options: [ + 0: "NO - Normal Open", + 1: "NC - Normal Close" + ], paramNum: 11, size: 1, default: "0", type: "enum"], + + [title: "Relays Load Power", description: "These parameters are used to specify the loads power that are connected to the device outputs (Relays). " + + "Using your connected device’s power consumption specification (see associated owner’s manual), set the load in Watts for the outputs bellow:", + name: "Selected Relay 1 Load Power in Watts", paramNum: 12, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + + [name: "Selected Relay 2 Load Power in Watts", paramNum: 13, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + + [name: "Selected Relay 3 Load Power in Watts", paramNum: 14, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + + [name: "Selected Relay 4 Load Power in Watts", paramNum: 15, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + + [name: "Selected Relay 5 Load Power in Watts", paramNum: 16, size: 2, default: 0, type: "number", min: 0, max: 1100, unit: "W"], + + [title: "Air Temperature Calibration", description: "This Parameter defines the offset value for room air temperature. " + + "This value will be added or subtracted from the air temperature sensor reading.Through the Z-Wave network the value of this Parameter should be x10, e.g. for 1.5°C set the value 15.", + name: "Selected Temperature Offset in °Cx10", paramNum: 17, size: 1, default: 0, type: "number", min: -100, max: 100, unit: " °Cx10"], + + [title: "Touch Sensor Sensitivity Threshold", description: "This Parameter allows to adjust the Touch Buttons Sensitivity. " + + "Note: Setting the sensitivity too high can lead to false touch detection. We recommend not changing this Parameter unless there is a special need to do so.", + name: "Selected Touch Sensitivity", options: [ + 1: "Level 1 (Low sensitivity)", + 2: "Level 2", + 3: "Level 3", + 4: "Level 4", + 5: "Level 5", + 6: "Level 6", + 7: "Level 7", + 8: "Level 8", + 9: "Level 9", + 10: "Level 10 (High sensitivity)" + ], paramNum: 6, size: 1, default: "6", type: "enum"], + + [title: "Brightness Control", description: "The HE-TPS05 can adjust its display brightness automatically depending on the illumination of the ambient environment and also allows to control it manually.", + name: "Selected Brightness Level", options: [ + 0: "Auto", + 1: "Level 1 (Lowest)", + 2: "Level 2", + 3: "Level 3", + 4: "Level 4", + 5: "Level 5", + 6: "Level 6", + 7: "Level 7", + 8: "Level 8", + 9: "Level 9", + 10: "Level 10 (Highest)" + ], paramNum: 5, size: 1, default: "0", type: "enum"], + + [title: "Buttons Backlight Color", description: "This parameter defines backlights active state color", + name: "Selected Active State Color", options: [ + 0: "Red", + 1: "Blue" + ], paramNum: 30, size: 1, default: "1", type: "enum"], + + [title: "Buttons Backlight Control Source", description: "This parameter defines the buttons backlight control source", + name: "Backlight 1", options: [ + 0: "Disabled", + 1: "Controlled by Touch Button", + 2: "Controlled by Gateway" + ], paramNum: 31, size: 1, default: "1", type: "enum"], + + [name: "Backlight 2", options: [ + 0: "Disabled", + 1: "Controlled by Touch Button", + 2: "Controlled by Gateway" + ], paramNum: 32, size: 1, default: "1", type: "enum"], + + [name: "Backlight 3", options: [ + 0: "Disabled", + 1: "Controlled by Touch Button", + 2: "Controlled by Gateway" + ], paramNum: 33, size: 1, default: "1", type: "enum"], + + [name: "Backlight 4", options: [ + 0: "Disabled", + 1: "Controlled by Touch Button", + 2: "Controlled by Gateway" + ], paramNum: 34, size: 1, default: "1", type: "enum"], + + [name: "Backlight 5", options: [ + 0: "Disabled", + 1: "Controlled by Touch Button", + 2: "Controlled by Gateway" + ], paramNum: 35, size: 1, default: "1", type: "enum"], + + [title: "Buttons Hold Control Mode", description: "This Parameter defines how the relay should react while holding the corresponding button. The options are: " + + "Hold is disabled, Operate like click, " + + "Momentary Switch: When the button is held, the relay output state is ON, as soon as the button is released the relay output state changes to OFF, " + + "Reversed Momentary: When the button is held, the relay output state is OFF, as soon as the button is released the relay output state changes to ON, " + + "Toggle: When the button is held or released the relay output state will toggle its state (ON to OFF or OFF to ON).", + name: "Selected Hold Control Mode for Button 1", options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 41, size: 1, default: "2", type: "enum"], + + [name: "Selected Hold Control Mode for Button 2", options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 42, size: 1, default: "2", type: "enum"], + + [name: "Selected Hold Control Mode for Button 3", options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 43, size: 1, default: "2", type: "enum"], + + [name: "Selected Hold Control Mode for Button 4", options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 44, size: 1, default: "2", type: "enum"], + + [name: "Selected Hold Control Mode for Button 5", options: [ + 0: "Hold is disabled", + 1: "Operate like click", + 2: "Momentary Switch", + 3: "Reversed Momentary", + 4: "Toggle" + ], paramNum: 45, size: 1, default: "2", type: "enum"], + + [title: "Buttons Click Control Mode", description: "These Parameters defines how the relay should react when clicking the corresponding button. The options are: " + + "Click is disabled, Toggle Switch (Relay): relay inverts state (ON to OFF, OFF to ON) according to the relay state, " + + "Toggle Switch (Backlight): relay inverts state (ON to OFF, OFF to ON) according to the button backlight state, " + + "Only On: Relay switches to ON state only, " + + "Only Off: Relay switches to OFF state only, " + + "Timer: On > Off: Relay output switches to ON state (contacts are closed) then after a specified time switches back to OFF state (contacts are open). The time is specified in 'Relay Timer Mode Duration' below, " + + "Timer: Off > On: Relay output switches to OFF state (contacts are open) then after a specified time switches back to On state (contacts are closed). The time is specified in 'Relay Timer Mode Duration' below ", + name: "Selected Click Control Mode for Button 1", options: [ + 0: "Click is disabled", + 1: "Toggle Switch (Relay)", + 2: "Toggle Switch (Backlight)", + 3: "Only On", + 4: "Only Off", + 5: "Timer: On > Off", + 6: "Timer: Off > On" + ], paramNum: 51, size: 1, default: "1", type: "enum"], + + [name: "Selected Click Control Mode for Button 2", options: [ + 0: "Click is disabled", + 1: "Toggle Switch (Relay)", + 2: "Toggle Switch (Backlight)", + 3: "Only On", + 4: "Only Off", + 5: "Timer: On > Off", + 6: "Timer: Off > On" + ], paramNum: 52, size: 1, default: "1", type: "enum"], + + [name: "Selected Click Control Mode for Button 3", options: [ + 0: "Click is disabled", + 1: "Toggle Switch (Relay)", + 2: "Toggle Switch (Backlight)", + 3: "Only On", + 4: "Only Off", + 5: "Timer: On > Off", + 6: "Timer: Off > On" + ], paramNum: 53, size: 1, default: "1", type: "enum"], + + [name: "Selected Click Control Mode for Button 4", options: [ + 0: "Click is disabled", + 1: "Toggle Switch (Relay)", + 2: "Toggle Switch (Backlight)", + 3: "Only On", + 4: "Only Off", + 5: "Timer: On > Off", + 6: "Timer: Off > On" + ], paramNum: 54, size: 1, default: "1", type: "enum"], + + [name: "Selected Click Control Mode for Button 5", options: [ + 0: "Click is disabled", + 1: "Toggle Switch (Relay)", + 2: "Toggle Switch (Backlight)", + 3: "Only On", + 4: "Only Off", + 5: "Timer: On > Off", + 6: "Timer: Off > On" + ], paramNum: 55, size: 1, default: "1", type: "enum"], + + [title: "Button Number for Relays Output Control", description: "This parameter defines the relays control source", + name: "Selected Relay 1 Control Source", options: [ + 0: "Controlled by Gateway", + 1: "Touch Button 1 (Top Left)", + 2: "Touch Button 2 (Top Right)", + 3: "Touch Button 3 (Bottom Left)", + 4: "Touch Button 4 (Bottom Right)", + 5: "Touch Button 5 (Center)" + ], paramNum: 61, size: 1, default: "1", type: "enum"], + + [name: "Selected Relay 1 Control Source", options: [ + 0: "Controlled by Gateway", + 1: "Touch Button 1 (Top Left)", + 2: "Touch Button 2 (Top Right)", + 3: "Touch Button 3 (Bottom Left)", + 4: "Touch Button 4 (Bottom Right)", + 5: "Touch Button 5 (Center)" + ], paramNum: 61, size: 1, default: "1", type: "enum"], + + [name: "Selected Relay 2 Control Source", options: [ + 0: "Controlled by Gateway", + 1: "Touch Button 1 (Top Left)", + 2: "Touch Button 2 (Top Right)", + 3: "Touch Button 3 (Bottom Left)", + 4: "Touch Button 4 (Bottom Right)", + 5: "Touch Button 5 (Center)" + ], paramNum: 62, size: 1, default: "2", type: "enum"], + + [name: "Selected Relay 3 Control Source", options: [ + 0: "Controlled by Gateway", + 1: "Touch Button 1 (Top Left)", + 2: "Touch Button 2 (Top Right)", + 3: "Touch Button 3 (Bottom Left)", + 4: "Touch Button 4 (Bottom Right)", + 5: "Touch Button 5 (Center)" + ], paramNum: 63, size: 1, default: "3", type: "enum"], + + [name: "Selected Relay 4 Control Source", options: [ + 0: "Controlled by Gateway", + 1: "Touch Button 1 (Top Left)", + 2: "Touch Button 2 (Top Right)", + 3: "Touch Button 3 (Bottom Left)", + 4: "Touch Button 4 (Bottom Right)", + 5: "Touch Button 5 (Center)" + ], paramNum: 64, size: 1, default: "4", type: "enum"], + + [name: "Selected Relay 5 Control Source", options: [ + 0: "Controlled by Gateway", + 1: "Touch Button 1 (Top Left)", + 2: "Touch Button 2 (Top Right)", + 3: "Touch Button 3 (Bottom Left)", + 4: "Touch Button 4 (Bottom Right)", + 5: "Touch Button 5 (Center)" + ], paramNum: 65, size: 1, default: "5", type: "enum"], + + [title: "Relays Timer Mode Duration", description: "These parameters specify the duration in seconds for the Timer modes for Click Control Mode above. " + + "Press the button and the relay output goes to ON/OFF for the specified time then changes back to OFF/ON. " + + "If the value is set to “0” the relay output will operate as a short contact (duration is about 0.5 sec)", + name: "Selected Relay 1 Timer Mode Duration in seconds", paramNum: 71, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s"], + + [name: "Selected Relay 2 Timer Mode Duration in seconds", paramNum: 72, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s"], + + [name: "Selected Relay 3 Timer Mode Duration in seconds", paramNum: 73, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s"], + + [name: "Selected Relay 4 Timer Mode Duration in seconds", paramNum: 74, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s"], + + [name: "Selected Relay 5 Timer Mode Duration in seconds", paramNum: 75, size: 2, default: 0, type: "number", min: 0 , max: 43200, unit: "s"], + + [title: "Retore Relays State", description: "This parameter determines if the last relay state should be restored after power failure or not. " + + "These parameters are available on firmware V2.4 or higher", + name: "Selected Mode for Relay 1", options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 66, size: 1, default: "0", type: "enum"], + + [name: "Selected Mode for Relay 2", options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 67, size: 1, default: "0", type: "enum"], + + [name: "Selected Mode for Relay 3", options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 68, size: 1, default: "0", type: "enum"], + + [name: "Selected Mode for Relay 4", options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 69, size: 1, default: "0", type: "enum"], + + [name: "Selected Mode for Relay 5", options: [ + 0: "Relay Off After Power Failure", + 1: "Restore Last State" + ], paramNum: 70, size: 1, default: "0", type: "enum"], + + [title: "Relay Inverse Mode", description: "The values in this Parameter specify the relays that will operate in inverse mode. Relays can operate in an inverse mode in two different ways: " + + "1. When the first and the second relays are connected to two different external switches. In this case, after pressing a button, the corresponding relay connected to that button will toggle its state (ON to OFF or OFF to ON), and the other relay will be switched OFF. " + + "2. When two relays are connected to the same external switch. In this case, the relays will operate in roller shutter mode and their behavior will follow these four cycles: " + + "a - 1st press of button: the first relay will be switched ON, the second relay will be switched OFF, " + + "b - 2nd press of button: both relays will be switched OFF, " + + "c - 3rd press of button: the second relay will be switched ON, the first relay will be switched OFF, " + + "d - 4th press of button: both relays will be switched OFF. " + + "≡ Note: In this mode, both relays cannot be switched ON at the same time (i.e. simultaneously). " + + "≡ Note: Switching OFF one relay will always operate before switching ON another relay to prevent both relays from being ON at the same time.", + name: "Group 1", options: [ + 0: "Disabled", + 12: "1st & 2nd Relay", + 13: "1st & 3rd Relay", + 14: "1st & 4th Relay", + 15: "1st & 5th Relay", + 23: "2nd & 3rd Relay", + 24: "2nd & 4th Relay", + 25: "2nd & 5th Relay", + 34: "3rd & 4th Relay", + 35: "3rd & 5th Relay", + 45: "4th & 5th Relay" + ], paramNum: 101, size: 1, default: "0", type: "enum"], + + [name: "Group 2", options: [ + 0: "Disabled", + 12: "1st & 2nd Relay", + 13: "1st & 3rd Relay", + 14: "1st & 4th Relay", + 15: "1st & 5th Relay", + 23: "2nd & 3rd Relay", + 24: "2nd & 4th Relay", + 25: "2nd & 5th Relay", + 34: "3rd & 4th Relay", + 35: "3rd & 5th Relay", + 45: "4th & 5th Relay" + ], paramNum: 102, size: 1, default: "0", type: "enum"], + + [title: "Energy Consumption Meter Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends reports from its energy consumption sensor even if there is no change in the value. " + + "This parameter defines the interval between consecutive reports of real time and cumulative energy consumption data to the gateway", + name: "Selected Energy Report Interval in minutes", paramNum: 141, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + + [title: "Control Energy Meter Report", description: "This Parameter determines if the change in the energy meter will result in a report being sent to the gateway. " + + "Note: When the device is turning ON, the consumption data will be sent to the gateway once, even if the report is disabled.", + name: "Sending Energy Meter Reports", options: [ + 0: "Disabled", + 1: "Enabled" + ], paramNum: 142, size: 1, default: "1", type: "enum"], + + [title: "Sensors Consecutive Report Interval", description: "When the device is connected to the gateway, it periodically sends to the gateway reports from its external " + + "NTC temperature sensor even if there are not changes in the values. This Parameter defines the interval between consecutive reports", + name: "Selected Energy Report Interval in minutes", paramNum: 143, size: 1, default: 10, type: "number", min: 1 , max: 120, unit: "min"], + + [title: "Air & Floor Temperature Sensors Report Threshold", description: "This Parameter determines the change in temperature level (in °C) resulting in temperature sensors " + + "report being sent to the gateway. The value of this Parameter should be x10 for °C, e.g. for 0.4°C use value 4. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Temperature Threshold in °Cx10", paramNum: 144, size: 1, default: 2, type: "number", min: 0 , max: 100, unit: " °Cx10"], + + [title: "Humidity Sensor Report Threshold", description: "This Parameter determines the change in humidity level in % resulting in humidity sensors " + + "report being sent to the gateway. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Humidity Threshold in %", paramNum: 145, size: 1, default: 2, type: "number", min: 0 , max: 25, unit: "%"], + + [title: "Light Sensor Report Threshold", description: "This Parameter determines the change in the ambient environment illuminance level resulting in a light sensors report " + + "being sent to the gateway. From 10% to 99% can be selected. Use the value 0 if there is a need to stop sending the reports.", + name: "Selected Light Sensor Threshold in %", paramNum: 146, size: 1, default: 50, type: "number", min: 0 , max: 99, unit: "%"] + +]} From 9301f9395ed972efcd1b9c47edb59f36448a35aa Mon Sep 17 00:00:00 2001 From: aguilbault-Sinope <54598043+aguilbault-Sinope@users.noreply.github.com> Date: Wed, 16 Mar 2022 10:04:16 -0400 Subject: [PATCH 384/422] integrate dm2500zb and dm2550zb in the same file (#77964) --- .../dm2500zb-dm2550zb-sinope-dimmer.groovy} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename devicetypes/sinope-technologies/{dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy => dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy} (97%) diff --git a/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy b/devicetypes/sinope-technologies/dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy similarity index 97% rename from devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy rename to devicetypes/sinope-technologies/dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy index 028431906cb..cf7093a9a7e 100644 --- a/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy +++ b/devicetypes/sinope-technologies/dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy @@ -18,7 +18,7 @@ preferences { } metadata { - definition (name: "DM2500ZB Sinope Dimmer", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.switch") + definition (name: "DM2500ZB-DM2550ZB Sinope Dimmer", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.switch") { capability "Actuator" capability "Configuration" @@ -30,6 +30,7 @@ metadata { attribute "swBuild","string"// earliers versions of the DM2500ZB does not support the minimal intensity. theses dimmers can be identified by their swBuild under the value 106 fingerprint manufacturer: "Sinope Technologies", model: "DM2500ZB", deviceJoinName: "Sinope Dimmer Switch" //DM2500ZB + fingerprint manufacturer: "Sinope Technologies", model: "DM2550ZB", deviceJoinName: "Sinope Dimmer Switch" //DM2550ZB } tiles(scale: 2) From 03238c199aecd8b7689a171893ee475e3e880bb6 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Thu, 17 Mar 2022 09:28:37 +0900 Subject: [PATCH 385/422] Update led-cpx-light.groovy Edited vids. --- .../zigbee-motion-sensor-light.src/led-cpx-light.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy index 5af6d858682..451e1ece4bb 100644 --- a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy @@ -28,10 +28,10 @@ metadata { capability "Switch Level" // ABL Lithonia - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Lithonia", model: "ABL-LIGHTSENSOR-Z-001", deviceJoinName: "CPX Smart Panel Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Lithonia", model: "ABL-LIGHTSENSOR-Z-001", deviceJoinName: "CPX Smart Panel Light", mnmn: "Samsung Electronics", vid: "ABL-LIGHTSENSOR-Z-001" // Samsung LED - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-004", deviceJoinName: "ITM CPX Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-001" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Samsung Electronics", model: "SAMSUNG-ITM-Z-004", deviceJoinName: "ITM CPX Light", mnmn: "Samsung Electronics", vid: "SAMSUNG-ITM-Z-004" } // UI tile definitions From 4a2cd6ad143180dd6d7b884be0d1d7807faadaca Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Thu, 17 Mar 2022 16:49:10 +0800 Subject: [PATCH 386/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Switch (#77930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Switch * Update zigbee-switch.groovy Co-authored-by: 啦啦 王 --- devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 6814c50d000..1b354241f1b 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -105,6 +105,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "1000", manufacturer: "SONOFF", model: "01MINIZB", deviceJoinName: "SONOFF 01MINIZB" //01MINIZB fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, FC57", outClusters: "0019", manufacturer: "SONOFF", model: "S26R2ZB", deviceJoinName: "SONOFF Plug", ocfDeviceType: "oic.d.smartplug" //SONOFF S26R2 Plug fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, FC57", outClusters: "0019", manufacturer: "SONOFF", model: "S40LITE", deviceJoinName: "SONOFF Plug", ocfDeviceType: "oic.d.smartplug" //SONOFF S40Lite Plug + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0020, FC57", outClusters: "0019", manufacturer: "SONOFF", model: "ZBMINI-L", deviceJoinName: "SONOFF Switch" //SONOFF ZBMINI-L // Terncy fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0019", manufacturer: "", model: "TERNCY-LS01", deviceJoinName: "Terncy Switch" //Terncy Smart Light Socket @@ -203,5 +204,3 @@ def configure() { log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() } - - From 80e9a24563017b198ae0f60430452e2475a4423c Mon Sep 17 00:00:00 2001 From: KevinTSH <89558926+KevinTSH@users.noreply.github.com> Date: Fri, 18 Mar 2022 03:34:55 -0400 Subject: [PATCH 387/422] DevWs for Zooz (The Smartest House) containing containing Zooz ZEN32 Scene Controller (#77946) * DevWs for Zooz (The Smartest House) containing containing Zooz ZEN32 Scene Controller * Zooz ZEN32 Scene Controller * Zooz ZEN32 Scene Controller --- .../zooz-zen32-scene-controller-button.groovy | 91 ++++ .../zooz-zen32-scene-controller.groovy | 457 ++++++++++++++++++ 2 files changed, 548 insertions(+) create mode 100644 devicetypes/zooz/zooz-zen32-scene-controller-button.src/zooz-zen32-scene-controller-button.groovy create mode 100644 devicetypes/zooz/zooz-zen32-scene-controller.src/zooz-zen32-scene-controller.groovy diff --git a/devicetypes/zooz/zooz-zen32-scene-controller-button.src/zooz-zen32-scene-controller-button.groovy b/devicetypes/zooz/zooz-zen32-scene-controller-button.src/zooz-zen32-scene-controller-button.groovy new file mode 100644 index 00000000000..660cb0d2e61 --- /dev/null +++ b/devicetypes/zooz/zooz-zen32-scene-controller-button.src/zooz-zen32-scene-controller-button.groovy @@ -0,0 +1,91 @@ +/* +* Zooz ZEN32 Scene Controller Button +* +* Changelog: +* +* 2022-03-17 +* - Requested changes +* 2022-02-27 +* - Publication Release +* +* Copyright 2022 Zooz +* +* 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: "Zooz ZEN32 Scene Controller Button", + namespace: "Zooz", + author: "Kevin LaFramboise (krlaframboise)", + ocfDeviceType: "x.com.st.d.remotecontroller", + mnmn: "SmartThingsCommunity", + vid: "63601248-c681-3458-b5d6-ab1f482b2d71" + ) { + capability "Sensor" + capability "Button" + capability "Refresh" + capability "platemusic11009.zoozLedColor" + capability "platemusic11009.zoozLedBrightness" + capability "platemusic11009.zoozLedMode" + } + + preferences() {} +} + +def parse(String description) { + log.debug "parse(${description})..." + return [] +} + +def installed() { + log.debug "installed()..." + initialize() +} + +def updated() { + log.debug "updated().." + initialize() +} + +void initialize() { + if (!device.currentValue("numberOfButtons")) { + sendEvent(name: "numberOfButtons", value: 1) + sendEvent(name: "supportedButtonValues", value: ["pushed", "held", "pushed_2x", "pushed_3x", "pushed_4x", "pushed_5x"].encodeAsJSON()) + sendEvent(name: "button", value: "pushed", data: [buttonNumber: 1]) + sendEvent(name: "ledMode", value: "onWhenOff") + sendEvent(name: "ledBrightness", value: "medium") + sendEvent(name: "ledColor", value: "white") + } +} + +def refresh() { + log.debug "refresh()..." + parent.childRefresh(device.deviceNetworkId) +} + +def setLedMode(mode) { + log.debug "setLedMode(${mode})..." + parent.childSetLedMode(device.deviceNetworkId, mode) +} + +def setLedColor(color) { + log.debug "setLedColor(${color})..." + parent.childSetLedColor(device.deviceNetworkId, color) +} + +def setLedBrightness(brightness) { + log.debug "setLedBrightness(${brightness})..." + parent.childSetLedBrightness(device.deviceNetworkId, brightness) +} \ No newline at end of file diff --git a/devicetypes/zooz/zooz-zen32-scene-controller.src/zooz-zen32-scene-controller.groovy b/devicetypes/zooz/zooz-zen32-scene-controller.src/zooz-zen32-scene-controller.groovy new file mode 100644 index 00000000000..3317d20a50e --- /dev/null +++ b/devicetypes/zooz/zooz-zen32-scene-controller.src/zooz-zen32-scene-controller.groovy @@ -0,0 +1,457 @@ +/* +* Zooz ZEN32 Scene Controller +* +* Changelog: +* +* 2022-03-17 +* - Requested changes +* 2022-02-27 +* - Publication Release +* +* Copyright 2022 Zooz +* +* 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. +* +*/ + +import groovy.transform.Field + +@Field static Map commandClassVersions = [ + 0x20: 1, // Basic + 0x25: 1, // Switch Binary + 0x55: 1, // Transport Service + 0x59: 1, // AssociationGrpInfo + 0x5A: 1, // DeviceResetLocally + 0x5B: 1, // CentralScene (3) + 0x5E: 2, // ZwaveplusInfo + 0x6C: 1, // Supervision + 0x70: 1, // Configuration + 0x7A: 2, // FirmwareUpdateMd + 0x72: 2, // ManufacturerSpecific + 0x73: 1, // Powerlevel + 0x85: 2, // Association + 0x86: 1, // Version (2) + 0x87: 1, // Indicator + 0x8E: 2, // Multi Channel Association + 0x98: 1, // Security S0 + 0x9F: 1 // Security S2 +] + +@Field static Map configParams = [ + autoOffTimer: [num:16, title:"Auto Turn-Off Timer (Minutes)", size:4, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(minutes)"], + autoOnTimer: [num:17, title:"Auto Turn-On Timer (Minutes)", size:4, defaultVal:0, range:"0..65535", desc:"0(disabled), 1..65535(minutes)"], + statusAfterPowerFailure: [num:18, title:"On Off Status After Power Failure", defaultVal:0, options:[0:"Restore previous state", 1:"Forced off", 2:"Forced on"]], + relayLoadControl: [num:19, title:"Relay Load Control", defaultVal:1, options:[1:"Enable Switch and Z-Wave", 0:"Disable Switch/ Enable Z-Wave", 2:"Disable Switch and Z-Wave"]], + disabledRelayBehavior: [num:20, title:"Disabled Relay Load Control Behavior", defaultVal:0, options:[0:"Reports Status / Changes LED", 1:"Doesn't Report Status / Change LED"]], + threeWaySwitchType: [num:21, title:"3-Way Switch Type", defaultVal:0, options:[0:"Toggle On/Off Switch", 1:"Momentary Switch (ZAC99)"]] +] + +@Field static List buttons = [ + [btnNum: 1, params:[ledMode:[num:2], ledColor:[num:7], ledBrightness:[num:12]]], + [btnNum: 2, params:[ledMode:[num:3], ledColor:[num:8], ledBrightness:[num:13]]], + [btnNum: 3, params:[ledMode:[num:4], ledColor:[num:9], ledBrightness:[num:14]]], + [btnNum: 4, params:[ledMode:[num:5], ledColor:[num:10], ledBrightness:[num:15]]], + [btnNum: 5, params:[ledMode:[num:1], ledColor:[num:6], ledBrightness:[num:11]]] +] + +@Field static Map ledParamOptions = [ + ledMode:[0:"onWhenOff", 1:"onWhenOn", 2:"alwaysOff", 3:"alwaysOn"], + ledColor:[0:"white", 1:"blue", 2:"green", 3:"red"], + ledBrightness:[0:"bright", 1:"medium", 2:"low"] +] + +@Field static int btnPushed = 0 +@Field static int btnReleased = 1 +@Field static int btnHeld = 2 + +metadata { + definition ( + name: "Zooz ZEN32 Scene Controller", + namespace: "Zooz", + author: "Kevin LaFramboise (@krlaframboise)", + ocfDeviceType: "oic.d.switch", + mnmn: "SmartThingsCommunity", + vid: "a0e5a3b8-4dc2-3616-87d1-58a520a2dc52" + ) { + capability "Actuator" + capability "Sensor" + capability "Switch" + capability "Light" + capability "Configuration" + capability "Refresh" + capability "Health Check" + capability "Button" + capability "platemusic11009.firmware" + + // zw:Ls2a type:1000 mfr:027A prod:7000 model:A008 ver:1.01 zwv:7.13 lib:03 cc:5E,55,9F,6C sec:86,25,70,20,5B,85,8E,59,72,5A,73,87,7A + fingerprint mfr:"027A", prod:"7000", model: "A008", deviceJoinName:"Zooz Switch" // Zooz ZEN32 Scene Controller + } + + preferences { + configParams.each { name, param -> + if (param.options) { + input name, "enum", + title: param.title, + description: "Default: ${param.options[param.defaultVal]}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + options: param.options + } else if (param.range) { + input name, "number", + title: param.title, + description: "${param.desc} - Default: ${param.defaultVal}", + required: false, + displayDuringSetup: false, + defaultValue: param.defaultVal, + range: param.range + } + } + } +} + +def installed() { + log.debug "installed()..." + initialize() + state.firstRun = true +} + +def updated() { + log.debug "updated()..." + initialize() + + if (!state.firstRun) { + executeConfigure() + } else { + state.firstRun = false + } +} + +void initialize() { + if (!device.currentValue("checkInterval")) { + sendEvent([name: "checkInterval", value: ((60 * 60 * 3) + (5 * 60)), displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]]) + } + + buttons.each { btn -> + if (!findChildByButton(btn)) { + addChildButton(btn) + } + } +} + +void addChildButton(Map btn) { + log.debug "Creating Button ${btn.btnNum}" + try { + addChildDevice( + "Zooz", + "Zooz ZEN32 Scene Controller Button", + "${device.deviceNetworkId}:${btn.btnNum}", + device.getHub().getId(), + [ + completedSetup: true, + label: "Zooz Button ${btn.btnNum}", + isComponent: false + ] + ) + } catch(Exception e) { + log.warn "${e}" + } +} + +void executeConfigure() { + List cmds = [] + + if (!device.currentValue("switch")) { + cmds << switchBinaryGetCmd() + } + + if (!device.currentValue("firmwareVersion")) { + cmds << versionGetCmd() + } + + configParams.each { name, param -> + Integer storedVal = getStoredVal(name) + Integer settingVal = getSettingVal(name) + if ((storedVal == null) || (storedVal != settingVal)) { + if (settingVal != null) { + log.debug "Changing ${param.title}(#${param.num}) from ${storedVal} to ${settingVal}" + cmds << configSetCmd(param, settingVal) + } + cmds << configGetCmd(param) + } + } + + if (cmds) { + sendHubCommand(cmds, 100) + } +} + +Integer getSettingVal(String name) { + Integer value = safeToInt(settings[name], null) + if ((value == null) && (getStoredVal(name) != null)) { + return configParams[name].defaultVal + } else { + return value + } +} + +Integer getStoredVal(String name) { + return safeToInt(state[name], null) +} + +def ping() { + log.debug "ping()..." + return [ switchBinaryGetCmd() ] +} + +def on() { + log.debug "on()..." + return [ switchBinarySetCmd(0xFF) ] +} + +def off() { + log.debug "off()..." + return [ switchBinarySetCmd(0x00) ] +} + +def refresh() { + log.debug "refresh()..." + List cmds = [ + switchBinaryGetCmd(), + versionGetCmd() + ] + + buttons.each { btn -> + btn.params.each { name, param -> + cmds << configGetCmd(param) + } + } + sendHubCommand(cmds) +} + +void childRefresh(String dni) { + log.debug "childRefresh(${dni})..." + Map btn = findButtonByDNI(dni) + if (btn) { + List cmds = [] + btn.params.each { name, param -> + cmds << configGetCmd(param) + } + sendHubCommand(cmds) + } +} + +void childSetLedMode(String dni, String mode) { + log.debug "childSetLedMode(${dni}, ${mode})..." + Map btn = findButtonByDNI(dni) + if (btn) { + mode = mode?.toLowerCase()?.trim() + Integer value = ledParamOptions.ledMode.find { it.value.toLowerCase() == mode }?.key + + if (value != null) { + sendConfigCmds(btn.params.ledMode, value) + } else { + log.warn "${mode} is not a valid LED Mode" + } + } +} + +void childSetLedColor(String dni, String color) { + log.debug "childSetLedColor(${dni}, ${color})..." + Map btn = findButtonByDNI(dni) + if (btn) { + color = color?.toLowerCase()?.trim() + Integer value = ledParamOptions.ledColor.find { it.value.toLowerCase() == color }?.key + + if (value != null) { + sendConfigCmds(btn.params.ledColor, value) + } else { + log.warn "${color} is not a valid LED Color" + } + } +} + +void childSetLedBrightness(String dni, String brightness) { + log.debug "childSetLedBrightness(${dni}, ${brightness})..." + Map btn = findButtonByDNI(dni) + if (btn) { + brightness = brightness?.toLowerCase()?.trim() + Integer value = ledParamOptions.ledBrightness.find { it.value == brightness }?.key + + if (value != null) { + sendConfigCmds(btn.params.ledBrightness, value) + } else { + log.warn "${brightness} is not a valid LED Brightness" + } + } +} + +void sendConfigCmds(Map param, int value) { + sendHubCommand([ + configSetCmd(param, value), + configGetCmd(param) + ]) +} + +String versionGetCmd() { + return secureCmd(zwave.versionV1.versionGet()) +} + +String switchBinaryGetCmd() { + return secureCmd(zwave.switchBinaryV1.switchBinaryGet()) +} + +String switchBinarySetCmd(val) { + return secureCmd(zwave.switchBinaryV1.switchBinarySet(switchValue: val)) +} + +String configSetCmd(Map param, int value) { + int size = (param.size ?: 1) + return secureCmd(zwave.configurationV1.configurationSet(parameterNumber: param.num, size: size, scaledConfigurationValue: value)) +} + +String configGetCmd(Map param) { + return secureCmd(zwave.configurationV1.configurationGet(parameterNumber: param.num)) +} + +String secureCmd(cmd) { + if (zwaveInfo?.zw?.contains("s")) { + return zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format() + } else { + return cmd.format() + } +} + +def parse(String description) { + def cmd = zwave.parse(description, commandClassVersions) + if (cmd) { + zwaveEvent(cmd) + } else { + log.warn "Unable to parse: $description" + } + return [] +} + +void zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) { + def encapsulatedCmd = cmd.encapsulatedCommand(commandClassVersions) + if (encapsulatedCmd) { + zwaveEvent(encapsulatedCmd) + } else { + log.warn "Unable to extract encapsulated cmd from $cmd" + } +} + +void zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) { + int value = cmd.scaledConfigurationValue + String name = configParams.find { name, param -> param.num == cmd.parameterNumber }?.key + if (name) { + state[name] = value + log.debug "${configParams[name]?.title}(#${configParams[name]?.num}) = ${value}" + } else { + handleLedEvent(cmd.parameterNumber, value) + } +} + +void handleLedEvent(int paramNum, int configVal) { + buttons.each { btn -> + String name = btn.params.find { it.value.num == paramNum}?.key + if (name) { + String value = ledParamOptions[name].get(configVal) + if (value) { + log.debug "Button ${btn.btnNum} ${name} is ${value}" + findChildByButton(btn)?.sendEvent(name: name, value: value) + } + } + } +} + +void zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) { + log.debug "${cmd}" + sendEvent(name: "firmwareVersion", value: (cmd.applicationVersion + (cmd.applicationSubVersion / 100))) +} + +void zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { + sendSwitchEvent(cmd.value) +} + +void zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) { + sendSwitchEvent(cmd.value) +} + +void sendSwitchEvent(rawVal) { + String value = (rawVal ? "on" : "off") + log.debug "switch is ${value}" + sendEvent(name: "switch", value: value) +} + +void zwaveEvent(physicalgraph.zwave.commands.centralscenev1.CentralSceneNotification cmd){ + if (state.lastSequenceNumber != cmd.sequenceNumber) { + state.lastSequenceNumber = cmd.sequenceNumber + + Map btn = findButtonByNum(cmd.sceneNumber) + if (btn) { + String value + switch (cmd.keyAttributes){ + case btnPushed: + value = "pushed" + break + case btnReleased: + log.debug "Button Value 'released' is not supported by SmartThings" + break + case btnHeld: + value = "held" + break + default: + value = "pushed_${cmd.keyAttributes - 1}x" + } + + if (value) { + log.debug "button ${btn.btnNum} ${value}" + findChildByButton(btn)?.sendEvent(name: "button", value: value, data:[buttonNumber: 1], isStateChange: true) + } + } else { + log.debug "Scene ${cmd.sceneNumber} is not a valid Button Number" + } + } +} + +void zwaveEvent(physicalgraph.zwave.Command cmd) { + log.debug "Unhandled zwaveEvent: $cmd" +} + +def findChildByButton(Map btn) { + return childDevices?.find { btn == findButtonByDNI(it.deviceNetworkId) } +} + +Map findButtonByDNI(String dni) { + Integer btnNum = safeToInt("${dni}".reverse().take(1), null) + if (btnNum) { + return findButtonByNum(btnNum) + } else { + log.warn "${dni} is not a valid Button DNI" + } +} + +Map findButtonByNum(Integer btnNum) { + return buttons.find { it.btnNum == btnNum } +} + +Integer safeToInt(val, Integer defaultVal=0) { + if ("${val}"?.isInteger()) { + return "${val}".toInteger() + } else if ("${val}".isDouble()) { + return "${val}".toDouble()?.round() + } else { + return defaultVal + } +} \ No newline at end of file From c69e449a51b41f53144a389c49ddd2c768f2dfc5 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Mon, 21 Mar 2022 16:31:46 +0800 Subject: [PATCH 388/422] DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Switch Generic (#77973) * DevWs for NIE-TECH CO., LTD. containing containing Z-Wave Switch Generic * Add a new product for Minoston Mini Outdoor Smart Plug * The deleted code is repeated with line 57:the fingerprint was added long long ago but the product is not managed by developer workspace. Co-authored-by: Winnie Wen --- .../zwave-switch-generic.src/zwave-switch-generic.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy b/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy index c9727f3a775..e109fb88f56 100644 --- a/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy +++ b/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy @@ -59,7 +59,7 @@ metadata { fingerprint mfr: "0312", prod: "FF00", model: "FF01", deviceJoinName: "Minoston Outlet", ocfDeviceType: "oic.d.smartplug" //Minoston on/off Toggle Switch fingerprint mfr: "0312", prod: "C000", model: "C003", deviceJoinName: "Evalogik Outlet", ocfDeviceType: "oic.d.smartplug" //Evalogik Outdoor Smart Plug fingerprint mfr: "0312", prod: "FF00", model: "FF03", deviceJoinName: "Minoston Switch" //Minoston Smart On/Off Switch - fingerprint mfr: "0312", prod: "C000", model: "CO05", deviceJoinName: "Evalogik Outlet", ocfDeviceType: "oic.d.smartplug" //Evalogik Mini Outdoor Smart Plug + fingerprint mfr: "0312", prod: "C000", model: "C005", deviceJoinName: "Evalogik Outlet", ocfDeviceType: "oic.d.smartplug" //Evalogik Mini Outdoor Smart Plug fingerprint mfr: "031E", prod: "0004", model: "0001", deviceJoinName: "Inovelli Switch" //Inovelli Switch fingerprint mfr: "001D", prod: "0037", model: "0002", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Tamper Resistant Outlet ZW15R fingerprint mfr: "0371", prod: "0103", model: "0026", deviceJoinName: "Aeotec Wall Switch" //Aeotec illumino Wall Switch From 3b46e8cde5ab7ca4d52b2498c33fab734124b016 Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Wed, 23 Mar 2022 01:25:42 +0800 Subject: [PATCH 389/422] Update zigbee-window-shade.groovy (#77868) * Update zigbee-window-shade.groovy add third reality smart blind * update zigbee-window-shade.groovy update zigbee-window-shade.groovy --- .../zigbee-window-shade.groovy | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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 e580330b3fc..4fc994cb38e 100644 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -40,6 +40,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Glydea Ultra Curtain", deviceJoinName: "Somfy Window Treatment" //Somfy Glydea Ultra fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Sonesse 30 WF Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 30 Zigbee LI-ION Pack fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0020, 0102", outClusters: "0003", manufacturer: "SOMFY", model: "Sonesse 40 Roller", deviceJoinName: "Somfy Window Treatment" // Somfy Sonesse 40 + fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Third Reality, Inc", model: "3RSB015BZ", deviceJoinName: "ThirdReality smart Blind" // ThirdReality } preferences { @@ -148,7 +149,11 @@ def levelEventHandler(currentLevel) { sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) if (currentLevel == 0 || currentLevel == 100) { - sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") + if (device.getDataValue("manufacturer") == "Third Reality, Inc"){ + sendEvent(name: "windowShade", value: currentLevel == 0 ? "open" : "closed") + } else { + sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") + } } else { if (priorLevel < currentLevel) { sendEvent([name:"windowShade", value: "opening"]) @@ -217,10 +222,14 @@ def pause() { log.info "pause()" def currentShadeStatus = device.currentValue("windowShade") - if (currentShadeStatus == "open" || currentShadeStatus == "closed") { - sendEvent(name: "windowShade", value: currentShadeStatus) + if (device.getDataValue("manufacturer") == "Third Reality, Inc") { + zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) } else { - zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) + if (currentShadeStatus == "open" || currentShadeStatus == "closed") { + sendEvent(name: "windowShade", value: currentShadeStatus) + } else { + zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) + } } } @@ -309,4 +318,4 @@ def isSomfy() { device.getDataValue("manufacturer") == "SOMFY" } -private getGLYDEA_MOVE_THRESHOLD() { 3 } \ No newline at end of file +private getGLYDEA_MOVE_THRESHOLD() { 3 } From 1ecb021216bd3372f5bb5420210b8efe3b88ba6a Mon Sep 17 00:00:00 2001 From: Aeotec-ccheng <63321041+Aeotec-ccheng@users.noreply.github.com> Date: Thu, 24 Mar 2022 02:27:06 -0700 Subject: [PATCH 390/422] DevWs for Aeotec Group GmbH containing containing Z-Wave Switch Generic (#77999) * DevWs for Aeotec Group GmbH containing containing Z-Wave Switch Generic * Fixed line 62 Co-authored-by: Christopher Cheng --- .../zwave-switch-generic.src/zwave-switch-generic.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy b/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy index e109fb88f56..4226bac8b81 100644 --- a/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy +++ b/devicetypes/smartthings/zwave-switch-generic.src/zwave-switch-generic.groovy @@ -63,6 +63,9 @@ metadata { fingerprint mfr: "031E", prod: "0004", model: "0001", deviceJoinName: "Inovelli Switch" //Inovelli Switch fingerprint mfr: "001D", prod: "0037", model: "0002", deviceJoinName: "Leviton Outlet", ocfDeviceType: "oic.d.smartplug" //Leviton Tamper Resistant Outlet ZW15R fingerprint mfr: "0371", prod: "0103", model: "0026", deviceJoinName: "Aeotec Wall Switch" //Aeotec illumino Wall Switch + fingerprint mfr: "0371", prod: "0003", model: "002A", deviceJoinName: "Aeotec Switch" //Aeotec Outdoor Smart Plug EU + fingerprint mfr: "0371", prod: "0103", model: "002A", deviceJoinName: "Aeotec Switch" //Aeotec Outdoor Smart Plug US + fingerprint mfr: "0371", prod: "0203", model: "002A", deviceJoinName: "Aeotec Switch" //Aeotec Outdoor Smart Plug AU } // simulator metadata From f75fe15e90fd945793d66b79322bf4c7c62da5c4 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Mon, 28 Mar 2022 17:00:25 +0900 Subject: [PATCH 391/422] Update led-cpx-light.groovy Added a light type capability in the code. --- .../zigbee-motion-sensor-light.src/led-cpx-light.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy index 451e1ece4bb..cda93258309 100644 --- a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy @@ -26,6 +26,7 @@ metadata { capability "Refresh" capability "Switch" capability "Switch Level" + capability "Light" // ABL Lithonia fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0406", outClusters: "0019", manufacturer: "Lithonia", model: "ABL-LIGHTSENSOR-Z-001", deviceJoinName: "CPX Smart Panel Light", mnmn: "Samsung Electronics", vid: "ABL-LIGHTSENSOR-Z-001" From d340b1ba7be82e71566dfde1cc170551e689f624 Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Tue, 29 Mar 2022 05:25:31 +0800 Subject: [PATCH 392/422] update smartsense-temp-humodity-sensor.groovy (#78003) * update smartsense-temp-humodity-sensor.groovy update smartsense-temp-humodity-sensor.groovy * update smartsense-temp-humidity-sensor.groovy update smartsense-temp-humidity-sensor.groovy * update update --- .../smartsense-temp-humidity-sensor.groovy | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 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 543d629fc53..af41fd23c1a 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 @@ -38,6 +38,9 @@ metadata { //eWeLink fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0402, 0405", outClusters: "0003", manufacturer: "eWeLink", model: "TH01", deviceJoinName: "eWeLink Multipurpose Sensor" fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0020, 0402, 0405, FC57", outClusters: "0003, 0019", manufacturer: "eWeLink", model: "SNZB-02P", deviceJoinName: "eWeLink Multipurpose Sensor" + + //Third Reality + fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0402,0405", outClusters: "0019", manufacturer:"Third Reality, Inc", model:"3RTS20BZ", deviceJoinName: "ThirdReality Thermal & Humidity Sensor" } simulator { @@ -174,7 +177,7 @@ def refresh() { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0021, [destEndpoint: 0x01])+ zigbee.readAttribute(0x0402, 0x0000, [destEndpoint: 0x01])+ zigbee.readAttribute(0x0405, 0x0000, [destEndpoint: 0x02]) - } else if (isFrientSensor()) { + } else if (isFrientSensor() || isThirdReality()) { return zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020)+ zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000)+ zigbee.readAttribute(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000) @@ -205,7 +208,7 @@ def configure() { 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 if (isFrientSensor()) { + } else if (isFrientSensor() || isThirdReality()) { return refresh() + zigbee.configureReporting(zigbee.RELATIVE_HUMIDITY_CLUSTER, 0x0000, DataType.UINT16, 60, 600, 1*100) + zigbee.configureReporting(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000, DataType.INT16, 60, 600, 0xA) + @@ -231,3 +234,7 @@ private Boolean isFrientSensor() { private Boolean isEWeLink() { device.getDataValue("manufacturer") == "eWeLink" } + +private Boolean isThirdReality() { + device.getDataValue("manufacturer") == "Third Reality, Inc" +} From ad257ff39bb835e4699e666317de567fe62521a2 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 29 Mar 2022 11:19:39 -0700 Subject: [PATCH 393/422] Revert "integrate dm2500zb and dm2550zb in the same file (#77964)" This reverts commit 9301f9395ed972efcd1b9c47edb59f36448a35aa. --- .../dm2500zb-sinope-dimmer.groovy} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename devicetypes/sinope-technologies/{dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy => dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy} (97%) diff --git a/devicetypes/sinope-technologies/dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy b/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy similarity index 97% rename from devicetypes/sinope-technologies/dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy rename to devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy index cf7093a9a7e..028431906cb 100644 --- a/devicetypes/sinope-technologies/dm2500zb-dm2550zb-sinope-dimmer.src/dm2500zb-dm2550zb-sinope-dimmer.groovy +++ b/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy @@ -18,7 +18,7 @@ preferences { } metadata { - definition (name: "DM2500ZB-DM2550ZB Sinope Dimmer", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.switch") + definition (name: "DM2500ZB Sinope Dimmer", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.switch") { capability "Actuator" capability "Configuration" @@ -30,7 +30,6 @@ metadata { attribute "swBuild","string"// earliers versions of the DM2500ZB does not support the minimal intensity. theses dimmers can be identified by their swBuild under the value 106 fingerprint manufacturer: "Sinope Technologies", model: "DM2500ZB", deviceJoinName: "Sinope Dimmer Switch" //DM2500ZB - fingerprint manufacturer: "Sinope Technologies", model: "DM2550ZB", deviceJoinName: "Sinope Dimmer Switch" //DM2550ZB } tiles(scale: 2) From 06c6546f0aac4856b5cf00e377a541ed98829ca5 Mon Sep 17 00:00:00 2001 From: greens Date: Tue, 29 Mar 2022 11:20:55 -0700 Subject: [PATCH 394/422] Add DM2550ZB fingerprint --- .../dm2500zb-sinope-dimmer.groovy | 225 +++++++++--------- 1 file changed, 113 insertions(+), 112 deletions(-) diff --git a/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy b/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy index 028431906cb..a412eeec34e 100644 --- a/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy +++ b/devicetypes/sinope-technologies/dm2500zb-sinope-dimmer.src/dm2500zb-sinope-dimmer.groovy @@ -3,18 +3,18 @@ Copyright Sinopé Technologies 1.3.0 SVN-571 * 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. + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. **/ preferences { - input("MinimalIntensityParam", "number", title:"Light bulb minimal intensity (1..10) (default: blank)", range:"1..10", description:"optional") + input("MinimalIntensityParam", "number", title:"Light bulb minimal intensity (1..10) (default: blank)", range:"1..10", description:"optional") // when the is at a low value, some bulbs may flicker for some technical reasons. to prevent that behaviour. writting this parameter will increase the minimal value // of the dimmer's become a little bit higher so the load doesn't start flickering when the level is low. input("LedIntensityParam", "number", title:"Indicator light intensity (1..100) (default: blank)", range:"1..100", description:"optional") - input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - // input("logFilter", "number", title: "Trace level", range: "1..5", - // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") + // input("logFilter", "number", title: "Trace level", range: "1..5", + // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") } metadata { @@ -26,13 +26,14 @@ metadata { capability "Switch" capability "Switch Level" capability "Health Check" - + attribute "swBuild","string"// earliers versions of the DM2500ZB does not support the minimal intensity. theses dimmers can be identified by their swBuild under the value 106 - + fingerprint manufacturer: "Sinope Technologies", model: "DM2500ZB", deviceJoinName: "Sinope Dimmer Switch" //DM2500ZB + fingerprint manufacturer: "Sinope Technologies", model: "DM2550ZB", deviceJoinName: "Sinope Dimmer Switch" //DM2550ZB } - tiles(scale: 2) + tiles(scale: 2) { multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) { @@ -43,13 +44,13 @@ metadata { attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" } - tileAttribute ("device.level", key: "SLIDER_CONTROL") + tileAttribute ("device.level", key: "SLIDER_CONTROL") { attributeState "level", action:"switch level.setLevel" } } - standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) + standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } @@ -63,26 +64,26 @@ def parse(String description) traceEvent(settings.logFilter, "description is $description", settings.trace, get_LOG_DEBUG()) def event = zigbee.getEvent(description) traceEvent(settings.logFilter, "Event = $event", settings.trace, get_LOG_DEBUG()) - + if(event) { if (event.name=="level" && event.value==0) {} - else { + else { traceEvent(settings.logFilter, "send event : $event", settings.trace, get_LOG_DEBUG()) - sendEvent(event) + sendEvent(event) sendEvent(name: "checkInterval", value: 300, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - } + } } else { traceEvent(settings.logFilter, "DID NOT PARSE MESSAGE for description", settings.trace, get_LOG_WARN()) - if (description?.startsWith("read attr -")) + if (description?.startsWith("read attr -")) { def descMap = zigbee.parseDescriptionAsMap(description) def result = [] result += createCustomMap(descMap) - // In the possibility of multiple attributes being reported in the same message, all the attributes will be in the same description. the first attribute will be in the fields regularly used, + // In the possibility of multiple attributes being reported in the same message, all the attributes will be in the same description. the first attribute will be in the fields regularly used, // but the otter attributes will be in the "additionalAttrs". they should all be treated in the following part. if(descMap.additionalAttrs) { @@ -104,18 +105,18 @@ def parse(String description) private def parseDescriptionAsMap(description) { traceEvent(settings.logFilter, "parsing MAP ...", settings.trace, get_LOG_DEBUG()) - (description - "read attr - ").split(",").inject([:]) + (description - "read attr - ").split(",").inject([:]) { - map, param -> - def nameAndValue = param.split(":") - map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] - } + map, param -> + def nameAndValue = param.split(":") + map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] + } } private def createCustomMap(descMap) { def result = null - def map = [:] + def map = [:] if(descMap.cluster == "0000" && descMap.attrId == "0001") { @@ -128,11 +129,11 @@ private def createCustomMap(descMap) } def updated() { - - if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 2000) + + if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 2000) { - state.updatedLastRanAt = now() - + state.updatedLastRanAt = now() + def cmds = [] if(checkSoftVersion() == true) { @@ -140,7 +141,7 @@ def updated() { def Time = getTiming(MinLight) traceEvent(settings.logFilter, "Set timing to: $Time", settings.trace, get_LOG_DEBUG()) cmds += zigbee.writeAttribute(0xff01, 0x0055, 0x21, Time) - + } else { @@ -157,11 +158,11 @@ def updated() { } sendZigbeeCommands(cmds) - } - else { + } + else { traceEvent(settings.logFilter, "updated(): Ran within last 2 seconds so aborting", settings.trace, get_LOG_TRACE()) - } - + } + } def off() @@ -174,7 +175,7 @@ def on() zigbee.on() } -def setLevel(level) +def setLevel(level) { traceEvent(settings.logFilter, "setLevel value = $level", settings.trace, get_LOG_DEBUG()) zigbee.setLevel(level,0) @@ -184,19 +185,19 @@ def setLevel(level) * PING is used by Device-Watch in attempt to reach the Device * */ def ping() { - return zigbee.onOffRefresh() + return zigbee.onOffRefresh() } def refresh() { - def cmds = [] + def cmds = [] cmds += zigbee.readAttribute(0x0006, 0x0000) //read on/off cmds += zigbee.readAttribute(0x0008, 0x0000) //read level cmds += zigbee.readAttribute(0x0000, 0x0001) //read software version cmds += zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 599, null) //configure reporting on/off cmds += zigbee.configureReporting(0x0008, 0x0000, 0x20, 3, 602, 0x01) //configure reporting level if(checkSoftVersion() == true){//if the minimal intensity is supported - cmds += zigbee.writeAttribute(0xff01, 0x0055, 0x21, getTiming((MinimalIntensityParam)?MinimalIntensityParam.toInteger():0)) + cmds += zigbee.writeAttribute(0xff01, 0x0055, 0x21, getTiming((MinimalIntensityParam)?MinimalIntensityParam.toInteger():0)) } return sendZigbeeCommands(cmds) } @@ -206,14 +207,14 @@ def configure() traceEvent(settings.logFilter, "Configuring Reporting and Bindings", settings.trace, get_LOG_DEBUG()) //allow 30 minutes without reveiving any on/off report - sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) return zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 599, null) + //configure reporting on/off zigbee.configureReporting(0x0008, 0x0000, 0x20, 3, 602, 0x01) + //configure reporting level zigbee.readAttribute(0x0006, 0x0000) + //read on/off zigbee.readAttribute(0x0008, 0x0000) + //read level zigbee.readAttribute(0x0000, 0x0001) //read software version - + } //-- Check Settings --------------------------------------------------------------------------------------- @@ -221,57 +222,57 @@ def configure() private int getTiming(def setting) {//getTiming is used to get the minimal time associated with the parameter "minimalIntensityParam" - def Timing - switch(setting) - { - case(1): - Timing = 100 - break; - case(2): - Timing = 250 - break; - case(3): - Timing = 500 - break; - case(4): - Timing = 750 - break; - case(5): - Timing = 1000 - break; - case(6): - Timing = 1250 - break; - case(7): - Timing = 1500 - break; - case(8): - Timing = 1750 - break; - case(9): - Timing = 2000 - break; - case(10): - Timing = 2250 - break; - default: - Timing = 600 - break; - } + def Timing + switch(setting) + { + case(1): + Timing = 100 + break; + case(2): + Timing = 250 + break; + case(3): + Timing = 500 + break; + case(4): + Timing = 750 + break; + case(5): + Timing = 1000 + break; + case(6): + Timing = 1250 + break; + case(7): + Timing = 1500 + break; + case(8): + Timing = 1750 + break; + case(9): + Timing = 2000 + break; + case(10): + Timing = 2250 + break; + default: + Timing = 600 + break; + } return Timing } private boolean checkSoftVersion() { - def version + def version def versionMin = "106" //the first version to support the minimal intensity is the version 106 def Build = device.currentState("swBuild")?.value traceEvent(settings.logFilter, "soft version: $Build", settings.trace, get_LOG_DEBUG()) - + if(Build > versionMin)//if the version is under 107, the minimal light intensity is not supported. { traceEvent(settings.logFilter, "intensity supported", settings.trace, get_LOG_DEBUG()) - version = true + version = true } else { @@ -283,54 +284,54 @@ private boolean checkSoftVersion() private void sendZigbeeCommands(cmds, delay = 1000) { - cmds.removeAll { it.startsWith("delay") } - // convert each command into a HubAction - cmds = cmds.collect { new physicalgraph.device.HubAction(it) } - sendHubCommand(cmds, delay) + cmds.removeAll { it.startsWith("delay") } + // convert each command into a HubAction + cmds = cmds.collect { new physicalgraph.device.HubAction(it) } + sendHubCommand(cmds, delay) } private int get_LOG_ERROR() { - return 1 + return 1 } private int get_LOG_WARN() { - return 2 + return 2 } private int get_LOG_INFO() { - return 3 + return 3 } private int get_LOG_DEBUG() { - return 4 + return 4 } private int get_LOG_TRACE() { - return 5 + return 5 } def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMessage = true) { - int LOG_ERROR = get_LOG_ERROR() - int LOG_WARN = get_LOG_WARN() - int LOG_INFO = get_LOG_INFO() - int LOG_DEBUG = get_LOG_DEBUG() - int LOG_TRACE = get_LOG_TRACE() - - if (displayEvent || traceLevel < 4) { - switch (traceLevel) { - case LOG_ERROR: - log.error "${message}" - break - case LOG_WARN: - log.warn "${message}" - break - case LOG_INFO: - log.info "${message}" - break - case LOG_TRACE: - log.trace "${message}" - break - case LOG_DEBUG: - default: - log.debug "${message}" - break - } - } + int LOG_ERROR = get_LOG_ERROR() + int LOG_WARN = get_LOG_WARN() + int LOG_INFO = get_LOG_INFO() + int LOG_DEBUG = get_LOG_DEBUG() + int LOG_TRACE = get_LOG_TRACE() + + if (displayEvent || traceLevel < 4) { + switch (traceLevel) { + case LOG_ERROR: + log.error "${message}" + break + case LOG_WARN: + log.warn "${message}" + break + case LOG_INFO: + log.info "${message}" + break + case LOG_TRACE: + log.trace "${message}" + break + case LOG_DEBUG: + default: + log.debug "${message}" + break + } + } } \ No newline at end of file From 2b20026fa18e130747836db65dcccf87b970c099 Mon Sep 17 00:00:00 2001 From: RaihaPark <74279632+RaihaPark@users.noreply.github.com> Date: Fri, 1 Apr 2022 12:15:30 +0900 Subject: [PATCH 395/422] Added a health check code By the reason for WWST certification failed, I added a health check code in the configure function. --- .../zigbee-motion-sensor-light.src/led-cpx-light.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy index cda93258309..da18e3d7ac6 100644 --- a/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy +++ b/devicetypes/smartthings/zigbee-motion-sensor-light.src/led-cpx-light.groovy @@ -113,6 +113,8 @@ def setLevel(value, rate=null) { } def configure() { + sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + zigbee.configureReporting(MOTION_CLUSTER, MOTION_STATUS_ATTRIBUTE, 0x18, 30, 600, null) + zigbee.onOffConfig() + zigbee.levelConfig() + From 34748fd5f877c926cab6857ce31ad0179282d92c Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:34:10 +0900 Subject: [PATCH 396/422] DevWs for SHINA SYSTEM containing containing Zigbee Multi Button (#78059) --- .../zigbee-multi-button.src/zigbee-multi-button.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy index c829563ca9b..78b7dabe35e 100644 --- a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy +++ b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy @@ -183,6 +183,8 @@ def configure() { if (isHeimanButton()) cmds += zigbee.writeAttribute(0x0000, 0x0012, DataType.BOOLEAN, 0x01) + addHubToGroup(0x000F) + addHubToGroup(0x0010) + addHubToGroup(0x0011) + addHubToGroup(0x0012) + if (isShinaButton()) + cmds += addHubToGroup(0x0000) return cmds } From 55f0754837783518f351becb936c6a183b2e44eb Mon Sep 17 00:00:00 2001 From: Focalcrest-Madi <58587489+Focalcrest-Madi@users.noreply.github.com> Date: Tue, 12 Apr 2022 17:23:29 +0800 Subject: [PATCH 397/422] DevWs for Focalcrest containing containing ZigBee Switch (#78069) * DevWs for Focalcrest containing containing ZigBee Switch * Fix: replace 4 spaces to 1 tab * Fix: Update other fingerprints * Fix: Add an empty line * Fix: replace 4 spaces to 1 tab --- .../smartthings/zigbee-switch.src/zigbee-switch.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 1b354241f1b..e197fbf58e0 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -133,6 +133,11 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "Focalcrest", model: "SRB01", deviceJoinName: "Focalcrest Switch" // In-Wall Relay Switch fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "EVVR", model: "SRB01A", deviceJoinName: "Evvr Switch" // Evvr IRS fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "EVVR", model: "SRB02A", deviceJoinName: "Evvr Switch" // Evvr IRS Lite + + // Evvr + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0019", outClusters: "0019", manufacturer: "Evvr", model: "SRB01", deviceJoinName: "Evvr Switch" // Evvr IRS + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "Evvr", model: "SRB01A", deviceJoinName: "Evvr Switch" // Evvr IRS + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006", outClusters: "0019", manufacturer: "Evvr", model: "SRB02A", deviceJoinName: "Evvr Switch" // Evvr IRS Lite // SiHAS Switch fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z1", deviceJoinName: "SiHAS Switch" From 7e677d5b0c88094bbc54a57820f46f0935805d08 Mon Sep 17 00:00:00 2001 From: sky-nie <54890556+sky-nie@users.noreply.github.com> Date: Tue, 19 Apr 2022 17:10:16 +0800 Subject: [PATCH 398/422] DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer (#78091) * DevWs for NIE-TECH CO., LTD. containing containing Min Smart Plug Dimmer * the default value restore as the previos version. Co-authored-by: Winnie Wen --- .../min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy index ef33c87f9cc..db2f2525cc4 100644 --- a/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy +++ b/devicetypes/sky-nie/min-smart-plug-dimmer.src/min-smart-plug-dimmer.groovy @@ -298,8 +298,8 @@ private getConfigParams() { powerFailureRecoveryParam, pushDimmingDurationParam, holdDimmingDurationParam, - minimumBrightnessParam - + minimumBrightnessParam, + maximumBrightnessParam, ] } From ff9f798f9c806c1900ee9b1dda2c179c8ff2e68d Mon Sep 17 00:00:00 2001 From: mingwei0827 <38943109+mingwei0827@users.noreply.github.com> Date: Mon, 25 Apr 2022 16:14:23 +0800 Subject: [PATCH 399/422] DevWs for CoolKit Technology Co.,Ltd containing containing ZigBee Window Shade Battery (#78108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 啦啦 王 --- .../zigbee-window-shade-battery.groovy | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy index d2bd8427bca..2e6522f25f9 100644 --- a/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy +++ b/devicetypes/smartthings/zigbee-window-shade-battery.src/zigbee-window-shade-battery.groovy @@ -39,6 +39,9 @@ metadata { // SMARTWINGS fingerprint inClusters: "0000,0001,0003,0004,0005,0102", outClusters: "0019", manufacturer: "Smartwings", model: "WM25/L-Z", deviceJoinName: "Smartwings Window Treatment" + + // SONOFF + fingerprint inClusters: "0000,0001,0003,0004,0020,0102,fc57", outClusters: "0019", manufacturer: "SONOFF", model: "ZBCurtain", deviceJoinName: "SONOFF Window Treatment" } preferences { @@ -319,15 +322,15 @@ private List readDeviceBindingTable() { } def supportsLiftPercentage() { - isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() || isSmartwings() + isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() || isSmartwings() || isSonoff() } def shouldInvertLiftPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() || isSmartwings() + return isIkeaKadrilj() || isIkeaFyrtur() || isSmartwings() || isSonoff() } def reportsBatteryPercentage() { - return isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() || isSmartwings() + return isIkeaKadrilj() || isIkeaFyrtur() || isYooksmartOrYookee() || isSmartwings() || isSonoff() } def isIkeaKadrilj() { @@ -344,4 +347,8 @@ def isYooksmartOrYookee() { def isSmartwings() { device.getDataValue("model") == "WM25/L-Z" +} + +def isSonoff() { + device.getDataValue("manufacturer") == "SONOFF" } \ No newline at end of file From 3299947ca57b7273ac12f5916641bdc21f839a4d Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 9 May 2022 14:52:10 -0700 Subject: [PATCH 400/422] BUG-4775 Only prevent sending pause for Somfy shades --- .../zigbee-window-shade.groovy | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) 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 4fc994cb38e..3282841927a 100644 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -27,11 +27,11 @@ metadata { capability "Switch Level" command "pause" - + // NodOn - fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-RS-20", deviceJoinName: "NodOn Window Treatment" + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-RS-20", deviceJoinName: "NodOn Window Treatment" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "0019", manufacturer: "NodOn", model: "SIN-4-RS-20_PRO", deviceJoinName: "NodOn Window Treatment" - + fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0102", outClusters: "0019", model: "E2B0-KR000Z0-HA", deviceJoinName: "eZEX Window Treatment" // SY-IoT201-BD //SOMFY Blind Controller/eZEX fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.6", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0102", outClusters: "000A", manufacturer: "Feibit Co.Ltd", model: "FTB56-ZT218AK1.8", deviceJoinName: "Wistar Window Treatment" //Wistar Curtain Motor(CMJ) @@ -149,10 +149,10 @@ def levelEventHandler(currentLevel) { sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) if (currentLevel == 0 || currentLevel == 100) { - if (device.getDataValue("manufacturer") == "Third Reality, Inc"){ - sendEvent(name: "windowShade", value: currentLevel == 0 ? "open" : "closed") + if (device.getDataValue("manufacturer") == "Third Reality, Inc"){ + sendEvent(name: "windowShade", value: currentLevel == 0 ? "open" : "closed") } else { - sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") + sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } } else { if (priorLevel < currentLevel) { @@ -222,14 +222,10 @@ def pause() { log.info "pause()" def currentShadeStatus = device.currentValue("windowShade") - if (device.getDataValue("manufacturer") == "Third Reality, Inc") { - zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) + if (isSomfy() && (currentShadeStatus == "open" || currentShadeStatus == "closed")) { + sendEvent(name: "windowShade", value: currentShadeStatus) } else { - if (currentShadeStatus == "open" || currentShadeStatus == "closed") { - sendEvent(name: "windowShade", value: currentShadeStatus) - } else { - zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) - } + zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE) } } From f0c28a6fae961876d1240ba99a12403388a979e2 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Mon, 16 May 2022 16:53:51 +0900 Subject: [PATCH 401/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#78264) --- .../sihas-multipurpose-sensor.groovy | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index 4c37dba3370..db0c960a844 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -147,31 +147,13 @@ private Map getBatteryResult(rawValue) { def maxVolts = 3.2 if (isDSM300()) maxVolts = 3.1 - - // Get the current battery percentage as a multiplier 0 - 1 - def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0 - // Find the corresponding voltage from our range - curValVolts = curValVolts * (maxVolts - minVolts) + minVolts - // Round to the nearest 10th of a volt - curValVolts = Math.round(10 * curValVolts) / 10.0 - - // Only update the battery reading if we don't have a last reading, - // OR we have received the same reading twice in a row - // OR we don't currently have a battery reading - // OR the value we just received is at least 2 steps off from the last reported value - if (state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) { - def pct = (volts - minVolts) / (maxVolts - minVolts) - def roundedPct = Math.round(pct * 100) - if (roundedPct <= 0) - roundedPct = 1 - result.value = Math.min(100, roundedPct) - } else { - // Don't update as we want to smooth the battery values, but do report the last battery state for record keeping purposes - result.value = device.currentState("battery").value - } - - result.descriptionText = "${device.displayName} battery was ${result.value}%" - state.lastVolts = volts + + def pct = (volts - minVolts) / (maxVolts - minVolts) + def roundedPct = Math.round(pct * 100) + if (roundedPct <= 0) + roundedPct = 1 + result.value = Math.min(100, roundedPct) + result.descriptionText = "${device.displayName} battery was ${result.value}%" } return result } @@ -254,7 +236,6 @@ def refresh() { } if (isDSM300()) { - refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) refreshCmds += zigbee.enrollResponse() } @@ -325,4 +306,4 @@ private Boolean isDSM300() { private Boolean isCSM300() { device.getDataValue("model") == "CSM-300Z" -} +} \ No newline at end of file From a81d68df78675e30e31f9f15b0158d67be7da2ff Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 17 May 2022 19:45:36 +0900 Subject: [PATCH 402/422] DevWs for SHINA SYSTEM containing containing Zigbee Multi Button (#78282) --- .../zigbee-multi-button.src/zigbee-multi-button.groovy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy index 78b7dabe35e..63d9a46a978 100644 --- a/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy +++ b/devicetypes/smartthings/zigbee-multi-button.src/zigbee-multi-button.groovy @@ -157,6 +157,9 @@ def getBatteryPercentageResult(rawValue) { def minVolts = 2.1 def maxVolts = 3.0 def pct = (volts - minVolts) / (maxVolts - minVolts) + if(pct <= 0) { + pct = 0.01 + } result.value = Math.min(100, (int)(pct * 100)) def linkText = getLinkText(device) result.descriptionText = "${linkText} battery was ${result.value}%" From d7e7a8ca1d381c676b4fa5dea10246b3479aa9f8 Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Wed, 18 May 2022 11:34:52 +0800 Subject: [PATCH 403/422] UPDATE update model id --- .../smartsense-temp-humidity-sensor.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 af41fd23c1a..9b23cc87390 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 @@ -40,7 +40,7 @@ metadata { fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0020, 0402, 0405, FC57", outClusters: "0003, 0019", manufacturer: "eWeLink", model: "SNZB-02P", deviceJoinName: "eWeLink Multipurpose Sensor" //Third Reality - fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0402,0405", outClusters: "0019", manufacturer:"Third Reality, Inc", model:"3RTS20BZ", deviceJoinName: "ThirdReality Thermal & Humidity Sensor" + fingerprint profileId: "0104", deviceId: "0302", inClusters: "0000,0001,0402,0405", outClusters: "0019", manufacturer:"Third Reality, Inc", model:"3RTHS24BZ", deviceJoinName: "ThirdReality Thermal & Humidity Sensor" } simulator { From ab054dd6c7e2231ee610935c15252cff37ce6a1d Mon Sep 17 00:00:00 2001 From: aguilbault-Sinope <54598043+aguilbault-Sinope@users.noreply.github.com> Date: Thu, 19 May 2022 08:33:21 -0400 Subject: [PATCH 404/422] User27228209 16 (#78292) * DevWs for Sinope Technologies containing containing VA4200WZ-VA4200ZB Sinope Valve * correction spacing * Update va4200wz-va4200zb-sinope-valve.groovy remove commented code * Update va4200wz-va4200zb-sinope-valve.groovy spacing * Update va4200wz-va4200zb-sinope-valve.groovy spacing --- .../va4200wz-va4200zb-sinope-valve.groovy | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy index 160c2a03334..722b8118437 100644 --- a/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy +++ b/devicetypes/sinope-technologies/va4200wz-va4200zb-sinope-valve.src/va4200wz-va4200zb-sinope-valve.groovy @@ -11,9 +11,7 @@ import physicalgraph.zigbee.zcl.DataType metadata { preferences { - input("trace", "bool", title: "Trace", description: "Set it to true to enable tracing") - // input("logFilter", "number", title: "Trace level", range: "1..5", - // description: "1= ERROR only, 2= <1+WARNING>, 3= <2+INFO>, 4= <3+DEBUG>, 5= <4+TRACE>") + input("trace", "bool", title: "Trace (Only for debugging)", description: "Set it to true to enable tracing") } definition (name: "VA4200WZ-VA4200ZB Sinope Valve", namespace: "Sinope Technologies", author: "Sinope Technologies", ocfDeviceType: "oic.d.watervalve") { @@ -140,7 +138,7 @@ def parse(String description) { } } } - + return result } @@ -204,7 +202,6 @@ private Map getBatteryResult(rawValue) { def result = [:] result.name = 'battery' result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" - result.value = convertVoltToPercent(rawValue) return result } @@ -308,7 +305,7 @@ private def convertVoltToPercent(value) { } } -def traceEvent(logFilter, message, displayEvent = false, traceLevel = 4, sendMessage = true) { +def traceEvent(logFilter, message, displayEvent = true, traceLevel, sendMessage = true) { int LOG_ERROR = get_LOG_ERROR() int LOG_WARN = get_LOG_WARN() int LOG_INFO = get_LOG_INFO() From 636e6e639bc66cf5a8d74edd6f1488080259ec32 Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 23 May 2022 12:49:01 -0700 Subject: [PATCH 405/422] Remove bintray from sources list and update artifactory url --- build.gradle | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 200bc83c86c..7f4927c3ea5 100644 --- a/build.gradle +++ b/build.gradle @@ -13,26 +13,24 @@ buildscript { } repositories { mavenLocal() - jcenter() maven { credentials { username smartThingsArtifactoryUserName password smartThingsArtifactoryPassword } - url "https://smartthings.jfrog.io/smartthings/libs-release-local" + url "https://smartthings.jfrog.io/smartthings/libs-release" } } } repositories { mavenLocal() - jcenter() maven { credentials { username smartThingsArtifactoryUserName password smartThingsArtifactoryPassword } - url "https://smartthings.jfrog.io/smartthings/libs-release-local" + url "https://smartthings.jfrog.io/smartthings/libs-release" } } From 9c617262a2dbe7f309612290c550bdeed6dc23c2 Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 23 May 2022 12:49:01 -0700 Subject: [PATCH 406/422] Remove bintray from sources list and update artifactory url --- build.gradle | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 200bc83c86c..7f4927c3ea5 100644 --- a/build.gradle +++ b/build.gradle @@ -13,26 +13,24 @@ buildscript { } repositories { mavenLocal() - jcenter() maven { credentials { username smartThingsArtifactoryUserName password smartThingsArtifactoryPassword } - url "https://smartthings.jfrog.io/smartthings/libs-release-local" + url "https://smartthings.jfrog.io/smartthings/libs-release" } } } repositories { mavenLocal() - jcenter() maven { credentials { username smartThingsArtifactoryUserName password smartThingsArtifactoryPassword } - url "https://smartthings.jfrog.io/smartthings/libs-release-local" + url "https://smartthings.jfrog.io/smartthings/libs-release" } } From 5b7573f3fdfe3faa4d561200cb762d87da27cb62 Mon Sep 17 00:00:00 2001 From: greens Date: Mon, 23 May 2022 13:29:19 -0700 Subject: [PATCH 407/422] HCS-3029 Add ocfDeviceType to Zigbe Lock Without Codes --- .../zigbee-lock-without-codes.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy index a7dd8ac9ad5..4775d00de82 100644 --- a/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy +++ b/devicetypes/smartthings/zigbee-lock-without-codes.src/zigbee-lock-without-codes.groovy @@ -12,12 +12,12 @@ * for the specific language governing permissions and limitations under the License. * */ - + import physicalgraph.zigbee.zcl.DataType import physicalgraph.zigbee.clusters.iaszone.ZoneStatus metadata { - definition (name:"ZigBee Lock Without Codes", namespace:"smartthings", author:"SmartThings", vid:"generic-lock-2", mnmn:"SmartThings", runLocally:true, minHubCoreVersion:'000.022.00013', executeCommandsLocally:true) { + definition (name:"ZigBee Lock Without Codes", namespace:"smartthings", author:"SmartThings", vid:"generic-lock-2", mnmn:"SmartThings", runLocally:true, minHubCoreVersion:'000.022.00013', executeCommandsLocally:true, ocfDeviceType: "oic.d.smartlock") { capability "Actuator" capability "Lock" capability "Refresh" @@ -139,10 +139,10 @@ def initialize() { cmds += zigbee.enrollResponse() cmds += zigbee.configureReporting(CLUSTER_IAS_ZONE, IAS_ATTR_ZONE_STATUS, DataType.BITMAP16, 30, 60*5, null) } else { - cmds += zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,DataType.ENUM8, 0, 3600, null) + cmds += zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,DataType.ENUM8, 0, 3600, null) cmds += zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING,DataType.UINT8, 600, 21600, 0x01) cmds += zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) - if (isSiHASLock()) cmds += zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_DOORSTATE,DataType.ENUM8, 0, 3600, null) + if (isSiHASLock()) cmds += zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_DOORSTATE,DataType.ENUM8, 0, 3600, null) cmds += refresh() } @@ -193,7 +193,7 @@ private def parseAttributeResponse(String description) { responseMap.value = Math.round(Integer.parseInt(descMap.value, 16) / 2) responseMap.descriptionText = "Battery is at ${responseMap.value}%" } - + } else if (clusterInt == CLUSTER_DOORLOCK && attrInt == DOORLOCK_ATTR_LOCKSTATE) { def value = Integer.parseInt(descMap.value, 16) responseMap.name = "lock" From 558068c9abac466961549de1892084aa4670f063 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Tue, 24 May 2022 15:57:26 +0900 Subject: [PATCH 408/422] DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor (#78311) --- .../sihas-multipurpose-sensor.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index db0c960a844..3191342733d 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -146,7 +146,7 @@ private Map getBatteryResult(rawValue) { def minVolts = 2.3 def maxVolts = 3.2 - if (isDSM300()) maxVolts = 3.1 + if (isDSM300()) maxVolts = 3.0 def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) From 019a5272af794b2f2d2cfa73b33203f82004a2c5 Mon Sep 17 00:00:00 2001 From: AltyorFig <95210115+AltyorFig@users.noreply.github.com> Date: Mon, 20 Jun 2022 15:11:52 +0200 Subject: [PATCH 409/422] DevWs for NodOn containing containing NodOn Roller Shutter (#78396) * DevWs for NodOn containing containing NodOn Roller Shutter * format file --- .../zigbee-window-shade.groovy | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) 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 3282841927a..00eab9a46ef 100644 --- a/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy +++ b/devicetypes/smartthings/zigbee-window-shade.src/zigbee-window-shade.groovy @@ -149,17 +149,25 @@ def levelEventHandler(currentLevel) { sendEvent(name: "level", value: currentLevel, unit: "%", displayed: false) if (currentLevel == 0 || currentLevel == 100) { - if (device.getDataValue("manufacturer") == "Third Reality, Inc"){ + if (device.getDataValue("manufacturer") == "Third Reality, Inc" || device.getDataValue("manufacturer") == "NodOn"){ sendEvent(name: "windowShade", value: currentLevel == 0 ? "open" : "closed") } else { sendEvent(name: "windowShade", value: currentLevel == 0 ? "closed" : "open") } } else { - if (priorLevel < currentLevel) { - sendEvent([name:"windowShade", value: "opening"]) - } else if (priorLevel > currentLevel) { - sendEvent([name:"windowShade", value: "closing"]) - } + if (device.getDataValue("manufacturer") == "NodOn"){ + if (priorLevel < currentLevel) { + sendEvent([name:"windowShade", value: "closing"]) + } else if (priorLevel > currentLevel) { + sendEvent([name:"windowShade", value: "opening"]) + } + } else { + if (priorLevel < currentLevel) { + sendEvent([name:"windowShade", value: "opening"]) + } else if (priorLevel > currentLevel) { + sendEvent([name:"windowShade", value: "closing"]) + } + } runIn(1, "updateFinalState", [overwrite:true]) } } From 7dbb5803d67fc1dd23c8c74f97643b6939518aea Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Fri, 24 Jun 2022 19:03:42 +0800 Subject: [PATCH 410/422] HEIMAN translate issue (#78425) --- .../smartthings/zigbee-scene-keypad.src/i18n/messages.properties | 1 + .../smartthings/zigbee-switch.src/i18n/messages.properties | 1 + 2 files changed, 2 insertions(+) diff --git a/devicetypes/smartthings/zigbee-scene-keypad.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-scene-keypad.src/i18n/messages.properties index 9d741777bbf..8190dbde3e6 100644 --- a/devicetypes/smartthings/zigbee-scene-keypad.src/i18n/messages.properties +++ b/devicetypes/smartthings/zigbee-scene-keypad.src/i18n/messages.properties @@ -15,6 +15,7 @@ # Chinese '''HEIMAN Remote Control'''.zh-cn=海曼情景开关 '''HEIMAN Scene Keypad'''.zh-cn=海曼情景开关 +'''HEIMAN Scene Panel'''.zh-cn=海曼情景开关 '''ORVIBO Remote Control'''.zh-cn=欧瑞博情景开关 '''ORVIBO Scene Keypad'''.zh-cn=欧瑞博情景开关 '''GDKES Remote Control'''.zh-cn=粤奇胜情景开关 diff --git a/devicetypes/smartthings/zigbee-switch.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-switch.src/i18n/messages.properties index 7179338dfc1..7433cef0364 100755 --- a/devicetypes/smartthings/zigbee-switch.src/i18n/messages.properties +++ b/devicetypes/smartthings/zigbee-switch.src/i18n/messages.properties @@ -29,3 +29,4 @@ '''HONYAR Smart Switch'''.zh-cn=鸿雁智能墙面开关(一开) '''HEIMAN Switch'''.zh-cn=海曼智能墙面开关(一开) '''HEIMAN Smart Switch'''.zh-cn=海曼智能墙面开关(一开) +'''HEIMAN Outlet'''.zh-cn=海曼智能插座 \ No newline at end of file From 3cc8efa81eee0ac4c2a9ed420e1a6c25f88dedf0 Mon Sep 17 00:00:00 2001 From: Doczillar <66080719+Doczillar@users.noreply.github.com> Date: Wed, 6 Jul 2022 13:27:11 -0500 Subject: [PATCH 411/422] Updating README with new Edge info (#78603) * Updating README with new Edge info * Update README.md --- README.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7dc22a4722a..9224d6806c0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ -# SmartThings Public GitHub Repo +# Welcome to the SmartThings Public GitHub Repo -An official list of SmartApps and Device Types from SmartThings. +This repo contains development code for SmartApps and Groovy DTHs (Dynamic Type Handlers). -Here are some links to help you get started coding right away: +Here are some links to help you get started: -* [GitHub-specific Documentation](http://docs.smartthings.com/en/latest/tools-and-ide/github-integration.html) -* [Full Documentation](http://docs.smartthings.com) -* [IDE & Simulator](http://ide.smartthings.com) +* [Developer Documentation](https://developer-preview.smartthings.com) +* [Developer Workspace](https://smartthings.developer.samsung.com/workspace) * [Community Forums](http://community.smartthings.com) -Follow us on the web: - -* Twitter: http://twitter.com/smartthingsdev -* Facebook: http://facebook.com/smartthingsdevelopers +> SmartThings Edge Device Drivers are the new method for integrating Hub Connected Devices into the SmartThings Platform. With the launch of SmartThings Edge, we are taking some events that would have happened in the Cloud and moving them to the SmartThings Hub. SmartThings Edge uses Lua-based device drivers and our Rules API to control and automate devices connected directly to a SmartThings Hub. This includes Zigbee, Z-Wave, and LAN devices as well as automations triggered by timers and other Hub Connected devices using drivers. In the future, this will expand to include more protocols and features, like the new Matter standard. +> To learn more about SmartThings Edge, visit [Get Started with SmartThings Edge](https://developer-preview.smartthings.com/docs/devices/hub-connected/get-started). From 4c68f433a90fe1742d75592442fc88645c0c853f Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Wed, 20 Jul 2022 03:22:17 +0900 Subject: [PATCH 412/422] Devws for shina system containin 114 (#78712) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * CSM-300Z update --- .../sihas-multipurpose-sensor.groovy | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy index 3191342733d..8ac40de5eb3 100644 --- a/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy +++ b/devicetypes/shinasys/sihas-multipurpose-sensor.src/sihas-multipurpose-sensor.groovy @@ -28,14 +28,15 @@ metadata { capability "Health Check" capability "Sensor" capability "Contact Sensor" - capability "afterguide46998.peopleCounter" - capability "afterguide46998.inOutDirection" + capability "afterguide46998.peopleCounterV2" + capability "afterguide46998.inOutDirectionV2" + capability "Momentary" fingerprint inClusters: "0000,0001,0003,0020,0400,0402,0405,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "USM-300Z", deviceJoinName: "SiHAS MultiPurpose Sensor", mnmn: "SmartThings", vid: "generic-motion-6" fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "OSM-300Z", deviceJoinName: "SiHAS Motion Sensor", mnmn: "SmartThings", vid: "generic-motion-2", ocfDeviceType: "x.com.st.d.sensor.motion" fingerprint inClusters: "0000,0003,0402,0001,0405", outClusters: "0004,0003,0019", manufacturer: "ShinaSystem", model: "TSM-300Z", deviceJoinName: "SiHAS Temperature/Humidity Sensor", mnmn: "SmartThings", vid: "SmartThings-smartthings-SmartSense_Temp/Humidity_Sensor", ocfDeviceType: "oic.d.thermostat" fingerprint inClusters: "0000,0001,0003,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "DSM-300Z", deviceJoinName: "SiHAS Contact Sensor", mnmn: "SmartThings", vid: "generic-contact-3", ocfDeviceType: "x.com.st.d.sensor.contact" - fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "23d6139c-b108-3467-9ddb-6a177d6cc1df", ocfDeviceType: "x.com.st.d.sensor.motion" + fingerprint inClusters: "0000,0001,0003,000C,0020,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "CSM-300Z", deviceJoinName: "SiHAS People Counter", mnmn: "SmartThingsCommunity", vid: "c924b630-4647-39d6-897e-7597acededd7", ocfDeviceType: "x.com.st.d.sensor.motion" } preferences { section { @@ -143,11 +144,12 @@ private Map getBatteryResult(rawValue) { if (!(rawValue == 0 || rawValue == 255)) { result.name = 'battery' result.translatable = true - def minVolts = 2.3 - def maxVolts = 3.2 - + def minVolts = 2.2 + def maxVolts = 3.1 + if (isDSM300()) maxVolts = 3.0 - + if (isCSM300()) minVolts = 1.9 + def pct = (volts - minVolts) / (maxVolts - minVolts) def roundedPct = Math.round(pct * 100) if (roundedPct <= 0) @@ -189,18 +191,21 @@ private Map getAnalogInputResult(value) { String descriptionText2 = "${device.displayName} : $inoutString" log.debug "[$fpc] = people: $pc, dir: $inout, $inoutString" - if((inout != "ready") && (prevInOut == inoutString)) { + String motionActive = pc ? "active" : "inactive" + sendEvent(name: "motion", value: motionActive, displayed: true, isStateChange: false) + + if((inoutString != "ready") && (prevInOut == inoutString)) { sendEvent(name: "inOutDir", value: "ready", displayed: true) } - sendEvent(name: "peopleCounter", value: pc, displayed: true, descriptionText: descriptionText1 ) - + sendEvent(name: "inOutDir", value: inoutString, displayed: true, descriptionText: descriptionText2) return [ - name : 'inOutDir', - value : inoutString, - descriptionText: descriptionText2, + name : 'peopleCounter', + value : pc, + descriptionText: descriptionText1, translatable : true ] + } def setPeopleCounter(peoplecounter) { @@ -209,6 +214,9 @@ def setPeopleCounter(peoplecounter) { zigbee.writeAttribute(ANALOG_INPUT_BASIC_CLUSTER, ANALOG_INPUT_BASIC_PRESENT_VALUE_ATTRIBUTE, DataType.FLOAT4, pc) } +def push() { + setPeopleCounter(0) +} /** * PING is used by Device-Watch in attempt to reach the Device * */ From 3817703e791a132eb359e77cbcb414d431fb8538 Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Wed, 20 Jul 2022 03:22:53 +0900 Subject: [PATCH 413/422] Devws for shina system containin 70 (#78726) * DevWs for SHINA SYSTEM containing containing ZigBee Multi Switch * Move SiHAS 1gang switch to another switch DTH. * Add SiHAS Switch * Add SiHAS Switch * add blank line * update * update ISM300Z * Update zigbee-switch.groovy Recover from erroneous deletion. * Update zigbee-switch.groovy Recover from erroneous deletion. * Update zigbee-switch.groovy add blank * Update zigbee-switch.groovy update blank --- .../zigbee-multi-switch.src/zigbee-multi-switch.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy index 47d8af4f804..a103546cbcc 100644 --- a/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy +++ b/devicetypes/smartthings/zigbee-multi-switch.src/zigbee-multi-switch.groovy @@ -101,6 +101,7 @@ metadata { fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z4", deviceJoinName: "SiHAS Switch 1" fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z5", deviceJoinName: "SiHAS Switch 1" fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "SBM300Z6", deviceJoinName: "SiHAS Switch 1" + fingerprint inClusters: "0000, 0003, 0006, 0019, ", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "ISM300Z3", deviceJoinName: "SiHAS Switch 1" } // simulator metadata simulator { @@ -294,6 +295,7 @@ private getChildCount() { case "ST-S350-ZB": case "SBM300Z3": case "HS6SW3A-W-EF-3.0": + case "ISM300Z3": return 3 case "E220-KR4N0Z0-HA": case "E220-KR4N0Z1-HA": @@ -323,4 +325,4 @@ private getChildCount() { default: return 2 } -} \ No newline at end of file +} From b5e9884815c8ffd8183c26fd9cc59da19a53994b Mon Sep 17 00:00:00 2001 From: ADUROSMART ERIA <52692745+adurosmart@users.noreply.github.com> Date: Thu, 21 Jul 2022 05:11:56 +0800 Subject: [PATCH 414/422] Dev fix dimmer plug189 (#78346) * update parseAduroSmartButtonMessage function Some customers reported that the buttons 1 and 4 are not sensitive, so restore the zigbee.ONOFF_CLUSTER event trigger of button 1 and button 4, which will increase the success rate of event reporting after the button is pressed * fix dimmer plug Identify errors Co-authored-by: Andy Yi Co-authored-by: DESKTOP-NHN41KC\linkl --- devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy | 3 ++- .../zigbee-switch-power.src/zigbee-switch-power.groovy | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index 79de2e21568..7aa8aa2920e 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -27,7 +27,8 @@ metadata { // AduroSmart fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019", deviceId: "0101", manufacturer: "AduroSmart Eria", model: "AD-DimmableLight3001", deviceJoinName: "Eria Light" //Eria ZigBee Dimmable Bulb - + fingerprint profileId: "0104", deviceId: "0101", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BDP3001", deviceJoinName: "Eria Switch" //Eria Zigbee Dimmable Plug + // Aurora fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0019", manufacturer: "Aurora", model: "LCBulb01UK", deviceJoinName: "AOne Dimmer Switch", ocfDeviceType: "oic.d.switch" //Aurora AOne Control Dimmer (120w) fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0003", manufacturer: "Aurora", model: "Dimmer", deviceJoinName: "AOne Dimmer Switch", ocfDeviceType: "oic.d.switch" //Aurora AOne Control Dimmer (320w) diff --git a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy index 15563dc1217..479998275b8 100644 --- a/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy +++ b/devicetypes/smartthings/zigbee-switch-power.src/zigbee-switch-power.groovy @@ -53,7 +53,7 @@ metadata { //AduroSmart fingerprint profileId: "0104", deviceId: "0051", inClusters: "0000, 0003, 0004, 0005, 0006, 0B04, 1000, 0702", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "AD-SmartPlug3001", deviceJoinName: "Eria Switch" //Eria Zigbee Smart Plug fingerprint profileId: "0104", deviceId: "010A", inClusters: "0000, 0003, 0004, 0005, 0006, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BPU3", deviceJoinName: "Eria Switch" //Eria Zigbee On/Off Plug - fingerprint profileId: "0104", deviceId: "0101", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 1000", outClusters: "0019", manufacturer: "AduroSmart Eria", model: "BDP3001", deviceJoinName: "Eria Switch" //Eria Zigbee Dimmable Plug + } tiles(scale: 2) { From 2a3c40e8c800dd28508b0d1247d7b2f797ebf225 Mon Sep 17 00:00:00 2001 From: weihuan1111 <99949392+weihuan1111@users.noreply.github.com> Date: Fri, 22 Jul 2022 05:06:18 +0800 Subject: [PATCH 415/422] Move Third Reality Smart Button (#78743) * update * Update ikea-button.groovy * update * update * Update ikea-button.groovy * Update ikea-button.groovy --- .../ikea-button.src/ikea-button.groovy | 27 +++++++++++++++---- .../smartsense-button.groovy | 15 +---------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy index 3b0996944e6..4ea0a27ce44 100644 --- a/devicetypes/smartthings/ikea-button.src/ikea-button.groovy +++ b/devicetypes/smartthings/ikea-button.src/ikea-button.groovy @@ -35,6 +35,7 @@ metadata { fingerprint manufacturer: "SOMFY", model: "Situo 1 Zigbee", deviceJoinName: "SOMFY Remote Control", mnmn: "SmartThings", vid: "SmartThings-smartthings-Somfy_open/close_remote" // raw description 01 0104 0203 00 02 0000 0003 04 0003 0005 0006 0102 fingerprint inClusters: "0000, 0001, 0003", outClusters: "0003, 0006", manufacturer: "eWeLink", model: "WB01", deviceJoinName: "eWeLink Button" //eWeLink Button WB01 fingerprint inClusters: "0000, 0001, 0003, 0020, FC57", outClusters: "0003, 0006, 0019", manufacturer: "eWeLink", model: "SNZB-01P", deviceJoinName: "eWeLink Button" //eWeLink Button + fingerprint inClusters: "0000,0001,0012", outClusters: "0006,0008,0019", manufacturer: "Third Reality, Inc", model: "3RSB22BZ", deviceJoinName: "ThirdReality Smart Button" } tiles { @@ -199,7 +200,7 @@ def installed() { if (isIkeaOpenCloseRemote() || isSomfy()) { supportedButtons = ["pushed"] - } else if (isEWeLink()) { + } else if (isEWeLink() || isThirdReality()) { supportedButtons = ["pushed", "held", "double"] } else { supportedButtons = ["pushed", "held"] @@ -269,9 +270,10 @@ def parse(String description) { } else if (descMap.clusterInt == CLUSTER_SCENES || descMap.clusterInt == zigbee.ONOFF_CLUSTER || descMap.clusterInt == zigbee.LEVEL_CONTROL_CLUSTER || - descMap.clusterInt == CLUSTER_WINDOW_COVERING) { + descMap.clusterInt == CLUSTER_WINDOW_COVERING || + descMap.clusterInt == 0x0012) { event = getButtonEvent(descMap) - } + } } def result = [] @@ -395,7 +397,18 @@ private Map getButtonEvent(Map descMap) { buttonState = "pushed" } } - } + } else if (isThirdReality()) { + if (descMap.clusterInt == 0x0012) { + buttonNumber = 1 + if (descMap.value == "0002") { + buttonState = "double" + } else if (descMap.value == "0001") { + buttonState = "pushed" + } else if (descMap.value == "0000") { + buttonState = "held" + } + } + } if (buttonNumber != 0) { // Create old style @@ -461,4 +474,8 @@ private List addHubToGroup(Integer groupAddr) { private List readDeviceBindingTable() { ["zdo mgmt-bind 0x${device.deviceNetworkId} 0", "delay 200"] -} \ No newline at end of file +} + +private boolean isThirdReality() { + device.getDataValue("manufacturer") == "Third Reality, Inc" +} diff --git a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy index c301d5c7a6f..fa24592d589 100755 --- a/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy +++ b/devicetypes/smartthings/smartsense-button.src/smartsense-button.groovy @@ -28,7 +28,6 @@ metadata { capability "Sensor" fingerprint inClusters: "0000,0001,0003,0020,0402,0500", outClusters: "0019", manufacturer: "Samjin", model: "button", deviceJoinName: "Button" - fingerprint inClusters: "0000,0001,0012", outClusters: "0006,0008,0019", manufacturer: "Third Reality, Inc", model: "3RSB22BZ", deviceJoinName: "ThirdReality Smart Button" } simulator { @@ -137,9 +136,7 @@ def parse(String description) { } } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap?.value) { map = translateZoneStatus(new ZoneStatus(zigbee.convertToInt(descMap?.value))) - } else if ( descMap.clusterInt == 0x0012 ) { - map = translateMultiStatus(descMap.value) - } + } } } else if (map.name == "temperature") { if (tempOffset) { @@ -177,16 +174,6 @@ private Map translateZoneStatus(ZoneStatus zs) { } else { } } -private Map translateMultiStatus(String value) { - if (value == "0002" ) { - return getButtonResult('double') - } else if (value == "0001" ) { - return getButtonResult('pushed') - } else if (value == "0000"){ - return getButtonResult('held') - } else {} -} - private Map getBatteryResult(rawValue) { log.debug "Battery rawValue = ${rawValue}" def linkText = getLinkText(device) From caf4a38f6048be7cbdc4b7b822ef4d90e27cf1cd Mon Sep 17 00:00:00 2001 From: greens Date: Wed, 3 Aug 2022 16:27:52 -0700 Subject: [PATCH 416/422] CHAD-8882 Send supportedButtonValues for zigbee-button should bring devices backed by this DTH more in line with the capability definition. --- .../smartthings/zigbee-button.src/zigbee-button.groovy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/devicetypes/smartthings/zigbee-button.src/zigbee-button.groovy b/devicetypes/smartthings/zigbee-button.src/zigbee-button.groovy index aa74ac7193a..87edffd4152 100755 --- a/devicetypes/smartthings/zigbee-button.src/zigbee-button.groovy +++ b/devicetypes/smartthings/zigbee-button.src/zigbee-button.groovy @@ -22,7 +22,7 @@ metadata { capability "Actuator" capability "Battery" capability "Button" - capability "Holdable Button" + capability "Holdable Button" capability "Configuration" capability "Refresh" capability "Sensor" @@ -147,7 +147,7 @@ private Map parseNonIasButtonMessage(Map descMap){ button = 2 break } - + getButtonResult("release", button) } } @@ -191,6 +191,9 @@ def refresh() { def configure() { log.debug "Configuring Reporting, IAS CIE, and Bindings." + if (!device.currentState("supportedButtonValues")) { + sendEvent(name: "supportedButtonValues", value: JsonOutput.toJson(["pushed", "held"]), displayed: false) + } def cmds = [] if (device.getDataValue("model") == "3450-L") { cmds << [ From 0c7cf684359a476d0877d98ea56c33dd9cba5fcd Mon Sep 17 00:00:00 2001 From: shinasys <71238736+shinasys@users.noreply.github.com> Date: Wed, 10 Aug 2022 03:19:26 +0900 Subject: [PATCH 417/422] Devws for shina system containin 114 (#78787) * DevWs for SHINA SYSTEM containing containing SiHas Multipurpose Sensor * CSM-300Z update * SiHAS Dual Motion Sensor : new * Update sihas-dual-motion-sensor.groovy The reporting time has been changed. * Update sihas-dual-motion-sensor.groovy rollback * Update sihas-dual-motion-sensor.groovy Convert indentation to Tabs. --- .../sihas-dual-motion-sensor.groovy | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 devicetypes/shinasys/sihas-dual-motion-sensor.src/sihas-dual-motion-sensor.groovy diff --git a/devicetypes/shinasys/sihas-dual-motion-sensor.src/sihas-dual-motion-sensor.groovy b/devicetypes/shinasys/sihas-dual-motion-sensor.src/sihas-dual-motion-sensor.groovy new file mode 100644 index 00000000000..fb1036fc782 --- /dev/null +++ b/devicetypes/shinasys/sihas-dual-motion-sensor.src/sihas-dual-motion-sensor.groovy @@ -0,0 +1,205 @@ +/* + * Copyright 2022 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. + */ +import physicalgraph.zigbee.clusters.iaszone.ZoneStatus +import physicalgraph.zigbee.zcl.DataType + +metadata { + definition (name: "SiHAS Dual Motion Sensor", namespace: "shinasys", author: "SHINA SYSTEM", mnmn: "SmartThingsCommunity", vid: "868a0fcc-ae46-3a1b-9315-e342007bb3a9", ocfDeviceType: "x.com.st.d.sensor.motion") { + capability "Motion Sensor" + capability "Configuration" + capability "Battery" + capability "Refresh" + capability "Health Check" + capability "afterguide46998.dualMotionInSensor" + capability "afterguide46998.dualMotionOutSensor" + + attribute "motionInterval","number" + + fingerprint inClusters: "0000,0001,0003,0020,0406,0500", outClusters: "0003,0004,0019", manufacturer: "ShinaSystem", model: "DMS-300Z", deviceJoinName: "SiHAS Dual Motion Sensor" + } + preferences { + section { + input "motionInterval", "number", title: "Motion Interval", description: "What is the re-sensing time (seconds) after the motion sensor is detected.", range: "1..100", defaultValue: 5, required: true, displayDuringSetup: true + } + } +} + +private getOCCUPANCY_SENSING_CLUSTER() { 0x0406 } +private getPOWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE() { 0x0020 } +private getOCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE() { 0x0000 } +private getOCCUPIED_TO_UNOCCUPIED_DELAY_ATTRIBUTE() { 0x0010 } + +private List collectAttributes(Map descMap) { + List descMaps = new ArrayList() + descMaps.add(descMap) + if (descMap.additionalAttrs) { + descMaps.addAll(descMap.additionalAttrs) + } + return descMaps +} + +def parse(String description) { + log.debug "Parsing message from device: $description" + + Map map = zigbee.getEvent(description) + if (!map) { + if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } else { + Map descMap = zigbee.parseDescriptionAsMap(description) + if (descMap?.clusterInt == zigbee.POWER_CONFIGURATION_CLUSTER && descMap.commandInt != 0x07 && descMap.value) { + List descMaps = collectAttributes(descMap) + def battMap = descMaps.find { it.attrInt == POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE } + if (battMap) { + map = getBatteryResult(Integer.parseInt(battMap.value, 16)) + } + } else if (descMap?.clusterInt == zigbee.IAS_ZONE_CLUSTER && descMap.attrInt == zigbee.ATTRIBUTE_IAS_ZONE_STATUS && descMap.commandInt != 0x07) { + def zs = new ZoneStatus(zigbee.convertToInt(descMap.value, 10)) + map = translateZoneStatus(zs) + } else if (descMap?.clusterInt == OCCUPANCY_SENSING_CLUSTER && descMap.attrInt == OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE && descMap?.value) { + def inMotion = descMap.value == "01" ? "active" : "inactive" + def outMotion = device.latestState('motionOut')?.value + sendDualMotionResult("motionIn", inMotion) + map = (inMotion == "active" || outMotion == "active") ? getMotionResult('active') : getMotionResult('inactive') + } else if (descMap?.clusterInt == OCCUPANCY_SENSING_CLUSTER && descMap.attrInt == OCCUPIED_TO_UNOCCUPIED_DELAY_ATTRIBUTE && descMap?.value) { + def interval = zigbee.convertToInt(descMap.value, 10) + log.debug "interval = [$interval]" + map = [name:'motionInterval',value: interval] + } + } + } + + def result = map ? createEvent(map) : [:] + + if (description?.startsWith('enroll request')) { + List cmds = zigbee.enrollResponse() + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + log.debug "result: $result" + return result +} + +private Map parseIasMessage(String description) { + ZoneStatus zs = zigbee.parseZoneStatus(description) + translateZoneStatus(zs) +} + +private Map translateZoneStatus(ZoneStatus zs) { + def inMotion = device.latestState('motionIn')?.value + def outMotion = (zs.isAlarm1Set() || zs.isAlarm2Set()) ? "active" : "inactive" + sendDualMotionResult("motionOut", outMotion) + return (inMotion == "active" || outMotion == "active") ? getMotionResult('active') : getMotionResult('inactive') +} + +private Map getBatteryResult(rawValue) { + def linkText = getLinkText(device) + def result = [:] + def volts = rawValue / 10 + + if (!(rawValue == 0 || rawValue == 255)) { + result.name = 'battery' + result.translatable = true + def minVolts = 2.2 + def maxVolts = 3.1 + + def pct = (volts - minVolts) / (maxVolts - minVolts) + def roundedPct = Math.round(pct * 100) + if (roundedPct <= 0) + roundedPct = 1 + result.value = Math.min(100, roundedPct) + result.descriptionText = "${device.displayName} battery was ${result.value}%" + } + return result +} + +private sendDualMotionResult(name, value) { + String descriptionText = value == 'active' ? "${device.displayName} ${name} detected motion" : "${device.displayName} ${name} has stopped" + log.debug "$name = $value: $descriptionText" + + sendEvent(name: name, value: value, descriptionText: descriptionText,translatable : true) +} + +private Map getMotionResult(value) { + String descriptionText = value == 'active' ? "${device.displayName} detected motion" : "${device.displayName} motion has stopped" + return [ + name : 'motion', + value : value, + descriptionText: descriptionText, + translatable : true + ] +} + +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) +} + +def updated() { + log.debug "device updated $motionInterval" + + //set reportingInterval = 0 to trigger update + if (isMotionIntervalChange()) { + sendEvent(name: "motionInterval", value: getMotionReportInterval(), descriptionText: "Motion interval set to ${getMotionReportInterval()} seconds") + sendHubCommand(zigbee.writeAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPIED_TO_UNOCCUPIED_DELAY_ATTRIBUTE, DataType.UINT16, getMotionReportInterval()), 1) + } +} + +//has interval been updated +def isMotionIntervalChange() { + log.debug "isMotionIntervalChange ${getMotionReportInterval()} <- ${device.latestValue("motionInterval")}" + return (getMotionReportInterval() != device.latestValue("motionInterval")) +} + +//settings default interval +def getMotionReportInterval() { + return (motionInterval != null ? motionInterval : 5) +} + +def refresh() { + def refreshCmds = [] + + refreshCmds += zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE) + + refreshCmds += zigbee.readAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE) + refreshCmds += zigbee.readAttribute(OCCUPANCY_SENSING_CLUSTER, OCCUPIED_TO_UNOCCUPIED_DELAY_ATTRIBUTE) + refreshCmds += zigbee.readAttribute(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS) + refreshCmds += zigbee.enrollResponse() + return refreshCmds +} + +def configure() { + def configCmds = [] + + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) + sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + + // temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity + // battery minReport 30 seconds, maxReportTime 6 hrs by default + // humidity minReportTime 30 seconds, maxReportTime 60 min + // illuminance minReportTime 30 seconds, maxReportTime 60 min + // occupancy sensing minReportTime 10 seconds, maxReportTime 60 min + // ex) zigbee.configureReporting(0x0001, 0x0020, DataType.UINT8, 600, 21600, 0x01) + // This is for cluster 0x0001 (power cluster), attribute 0x0021 (battery level), whose type is UINT8, + // the minimum time between reports is 10 minutes (600 seconds) and the maximum time between reports is 6 hours (21600 seconds), + // and the amount of change needed to trigger a report is 1 unit (0x01). + configCmds += zigbee.configureReporting(zigbee.POWER_CONFIGURATION_CLUSTER, POWER_CONFIGURATION_BATTERY_VOLTAGE_ATTRIBUTE, DataType.UINT8, 30, 21600, 0x01/*100mv*1*/) + + configCmds += zigbee.configureReporting(OCCUPANCY_SENSING_CLUSTER, OCCUPANCY_SENSING_OCCUPANCY_ATTRIBUTE, DataType.BITMAP8, 1, 600, 1) + configCmds += zigbee.configureReporting(zigbee.IAS_ZONE_CLUSTER, zigbee.ATTRIBUTE_IAS_ZONE_STATUS, DataType.BITMAP16, 0, 0xffff, null) + return configCmds + refresh() +} From 78747fa5a3b42a0c801f1095f8f7521da968ba42 Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Mon, 5 Sep 2022 18:26:06 +0800 Subject: [PATCH 418/422] WWST-7986, WWST-7980 (#78929) * DG Light * DG Light * DG Light --- .../i18n/messages.properties | 16 ++++++++++++++++ .../zigbee-white-color-temperature-bulb.groovy | 5 +++++ 2 files changed, 21 insertions(+) create mode 100644 devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/i18n/messages.properties diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/i18n/messages.properties new file mode 100644 index 00000000000..7a99c7e68fa --- /dev/null +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/i18n/messages.properties @@ -0,0 +1,16 @@ +# 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. + +# Chinese +'''DG Light'''.zh-cn=DG智能灯 diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index b2e1aca9909..c6b9462439b 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -34,6 +34,11 @@ metadata { // Generic fingerprint profileId: "0104", deviceId: "010C", inClusters: "0006, 0008, 0300", deviceJoinName: "Light" //Generic Color Temperature Light + // DuraGreen + fingerprint profileId: "0104", deviceId: "010C", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0003, 0019", manufacturer: "DURAGREEN", model: "DG-CW-02", deviceJoinName: "DG Light" //DuraGreen Track Light + fingerprint profileId: "0104", deviceId: "010C", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0003, 0019", manufacturer: "DURAGREEN", model: "DG-CW-01", deviceJoinName: "DG Light" //DuraGreen LED Strip + fingerprint profileId: "0104", deviceId: "010C", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0003, 0019", manufacturer: "DURAGREEN", model: "DG-CCT-01", deviceJoinName: "DG Light" //DuraGreen Down Light + // ABL fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Samsung Electronics", model: "ABL-LIGHT-Z-001", deviceJoinName: "Juno Connect", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" //Wafer fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300", outClusters: "0019", manufacturer: "Juno", model: "ABL-LIGHT-Z-001", deviceJoinName: "Juno Connect", mnmn: "Samsung Electronics", vid: "ABL-LIGHT-Z-001" From 28d1f9f5264c9ad112c686b08ecfecd39701eee6 Mon Sep 17 00:00:00 2001 From: LUZhanchang <86645710+LUZhanchang@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:50:07 +0800 Subject: [PATCH 419/422] Wwst 8008 (#79403) * Totem door lock * Totem door lock * modify device join name * modify device join name --- .../zigbee-lock.src/i18n/messages.properties | 16 ++++++++++++++++ .../zigbee-lock.src/zigbee-lock.groovy | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 devicetypes/smartthings/zigbee-lock.src/i18n/messages.properties diff --git a/devicetypes/smartthings/zigbee-lock.src/i18n/messages.properties b/devicetypes/smartthings/zigbee-lock.src/i18n/messages.properties new file mode 100644 index 00000000000..c2b1244f979 --- /dev/null +++ b/devicetypes/smartthings/zigbee-lock.src/i18n/messages.properties @@ -0,0 +1,16 @@ +# 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. + +# Chinese +'''TOTEM Door Lock'''.zh-cn=TOTEM智能门锁 diff --git a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy index 2121d9d531a..09e9b55abe3 100644 --- a/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy +++ b/devicetypes/smartthings/zigbee-lock.src/zigbee-lock.groovy @@ -28,6 +28,8 @@ metadata { capability "Health Check" fingerprint profileId: "0104", inClusters: "0000,0001,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Door Lock" //Yale Touch Screen Deadbolt Lock + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0101", outClusters: "0019", manufacturer: "TOTEM", model: "H60/H90", deviceJoinName: "TOTEM Door Lock" //TOTEM Door lock + fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,000A,0101", outClusters: "0019", manufacturer: "TOTEM", model: "P30", deviceJoinName: "TOTEM Door Lock" //TOTEM Door lock fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL220 TS LL", deviceJoinName: "Yale Door Lock" //Yale Touch Screen Lever Lock fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Door Lock" //Yale Push Button Deadbolt Lock fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Door Lock" //Yale Touch Screen Deadbolt Lock From 5550e3d6b345a73fb3ce7c8992ca3d0d7ee648e8 Mon Sep 17 00:00:00 2001 From: greens Date: Fri, 20 Jan 2023 12:14:09 -0800 Subject: [PATCH 420/422] Update Circle deploy script with new S3 security --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3dbfd1df712..2f93e152ab8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,19 +19,19 @@ jobs: <<: *defaults steps: - checkout - - run: ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_DEV" + - run: ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_DEV" -PawsAccessKey="$S3_IAM_PREPROD_USERNAME" -PawsSecretKey="$S3_IAM_PREPROD_PASSWORD" - run: ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL" --stacktrace deploy-stage: <<: *defaults steps: - checkout - - run: ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_STAGE" + - run: ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_STAGE" -PawsAccessKey="$S3_IAM_PREPROD_USERNAME" -PawsSecretKey="$S3_IAM_PREPROD_PASSWORD" - run: ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL_STAGE" --stacktrace deploy-accept: <<: *defaults steps: - checkout - - run: ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_ACCEPT" + - run: ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_ACCEPT" -PawsAccessKey="$S3_IAM_ACCEPTANCE_USERNAME" -PawsSecretKey="$S3_IAM_ACCEPTANCE_PASSWORD" - run: ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL_ACCEPT" --stacktrace workflows: version: 2 From b4c0cd2b1823226ac022056a940341d50a35f529 Mon Sep 17 00:00:00 2001 From: Ryan Greyling Date: Mon, 12 Jun 2023 14:54:31 -0500 Subject: [PATCH 421/422] PRT-1088-Vietnam-Copyright --- .../smart-care-daily-routine.groovy | 16 ++++++++-------- .../smart-care-detect-motion.groovy | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/smartapps/smartthings/smart-care-daily-routine.src/smart-care-daily-routine.groovy b/smartapps/smartthings/smart-care-daily-routine.src/smart-care-daily-routine.groovy index fa2ac9c3db2..c5f77cdaf8b 100644 --- a/smartapps/smartthings/smart-care-daily-routine.src/smart-care-daily-routine.groovy +++ b/smartapps/smartthings/smart-care-daily-routine.src/smart-care-daily-routine.groovy @@ -35,10 +35,10 @@ preferences { } def disclaimerPage() { - def disclaimerText = "SMARTTHINGS INC. SMART CARE SUPPLEMENTAL TERMS AND DISCLAIMER\n" + - "SmartThings Inc. is not an emergency medical response service of any kind and does not provide " + + def disclaimerText = "Samsung Electronics Co., LTD. SMART CARE SUPPLEMENTAL TERMS AND DISCLAIMER\n" + + "Samsung Electronics Co., LTD. is not an emergency medical response service of any kind and does not provide " + "medical or health-related advice, which should be obtained from qualified medical personnel. " + - "SmartThings Inc., the contents of the app (such as text, graphics, images, videos, data and "+ + "Samsung Electronics Co., LTD., the contents of the app (such as text, graphics, images, videos, data and "+ "information contained therein) and such materials obtained from third parties are provided for " + "information purposes only and are not substitutes for professional medical advice, diagnosis, " + "examination, or treatment by a health care provider. If you think you or a loved one has a medical " + @@ -52,12 +52,12 @@ def disclaimerPage() { "avoid, or delay obtaining medical or health-related advice " + "relating to treatment or standard of care because of information contained in or transmitted through the app. "+ "RELIANCE ON ANY INFORMATION PROVIDED BY THE APP OR OTHER THIRD-PARTY PLATFORMS IS SOLELY AT YOUR OWN RISK.\n\n" + - "While SmartThings Inc. strives to make the information on the app as timely and accurate as possible, " + - "SmartThings Inc. makes no claims, promises, or guarantees about the accuracy, completeness, " + - "or adequacy of the content or information on the app. SmartThings Inc. expressly disclaims liability for any errors "+ + "While Samsung Electronics Co., LTD. strives to make the information on the app as timely and accurate as possible, " + + "Samsung Electronics Co., LTD. makes no claims, promises, or guarantees about the accuracy, completeness, " + + "or adequacy of the content or information on the app. Samsung Electronics Co., LTD. expressly disclaims liability for any errors "+ "and omissions in content or for the availability of content on the app. " + - "SmartThings Inc. will not be liable for any losses, injuries, or damages arising from the display " + - "or use of content on the app. SMARTTHINGS INC., ITS OFFICERS, " + + "Samsung Electronics Co., LTD. will not be liable for any losses, injuries, or damages arising from the display " + + "or use of content on the app. Samsung Electronics Co., LTD., ITS OFFICERS, " + "EMPLOYEES AND AGENTS DO NOT ACCEPT LIABILITY HOWEVER ARISING, INCLUDING LIABILITY FOR NEGLIGENCE, " + "FOR ANY LOSS RESULTING FROM THE USE OF OR RELIANCE UPON THE INFORMATION AND/OR SERVICES AT ANY TIME." diff --git a/smartapps/smartthings/smart-care-detect-motion.src/smart-care-detect-motion.groovy b/smartapps/smartthings/smart-care-detect-motion.src/smart-care-detect-motion.groovy index f51655a97fb..0742f45ca1a 100644 --- a/smartapps/smartthings/smart-care-detect-motion.src/smart-care-detect-motion.groovy +++ b/smartapps/smartthings/smart-care-detect-motion.src/smart-care-detect-motion.groovy @@ -32,10 +32,10 @@ preferences { } def disclaimerPage() { - def disclaimerText = "SMARTTHINGS INC. SMART CARE SUPPLEMENTAL TERMS AND DISCLAIMER\n" + - "SmartThings Inc. is not an emergency medical response service of any kind and does not provide " + + def disclaimerText = "Samsung Electronics Co., LTD. SMART CARE SUPPLEMENTAL TERMS AND DISCLAIMER\n" + + "Samsung Electronics Co., LTD. is not an emergency medical response service of any kind and does not provide " + "medical or health-related advice, which should be obtained from qualified medical personnel. " + - "SmartThings Inc., the contents of the app (such as text, graphics, images, videos, data and "+ + "Samsung Electronics Co., LTD., the contents of the app (such as text, graphics, images, videos, data and "+ "information contained therein) and such materials obtained from third parties are provided for " + "information purposes only and are not substitutes for professional medical advice, diagnosis, " + "examination, or treatment by a health care provider. If you think you or a loved one has a medical " + @@ -49,12 +49,12 @@ def disclaimerPage() { "avoid, or delay obtaining medical or health-related advice " + "relating to treatment or standard of care because of information contained in or transmitted through the app. "+ "RELIANCE ON ANY INFORMATION PROVIDED BY THE APP OR OTHER THIRD-PARTY PLATFORMS IS SOLELY AT YOUR OWN RISK.\n\n" + - "While SmartThings Inc. strives to make the information on the app as timely and accurate as possible, " + - "SmartThings Inc. makes no claims, promises, or guarantees about the accuracy, completeness, " + - "or adequacy of the content or information on the app. SmartThings Inc. expressly disclaims liability for any errors "+ + "While Samsung Electronics Co., LTD. strives to make the information on the app as timely and accurate as possible, " + + "Samsung Electronics Co., LTD. makes no claims, promises, or guarantees about the accuracy, completeness, " + + "or adequacy of the content or information on the app. Samsung Electronics Co., LTD. expressly disclaims liability for any errors "+ "and omissions in content or for the availability of content on the app. " + - "SmartThings Inc. will not be liable for any losses, injuries, or damages arising from the display " + - "or use of content on the app. SMARTTHINGS INC., ITS OFFICERS, " + + "Samsung Electronics Co., LTD. will not be liable for any losses, injuries, or damages arising from the display " + + "or use of content on the app. Samsung Electronics Co., LTD., ITS OFFICERS, " + "EMPLOYEES AND AGENTS DO NOT ACCEPT LIABILITY HOWEVER ARISING, INCLUDING LIABILITY FOR NEGLIGENCE, " + "FOR ANY LOSS RESULTING FROM THE USE OF OR RELIANCE UPON THE INFORMATION AND/OR SERVICES AT ANY TIME." From 34b7178fddc413066f550dd58e6cedeebdcc795a Mon Sep 17 00:00:00 2001 From: Saul Flores Date: Wed, 21 Jun 2023 17:09:13 -0600 Subject: [PATCH 422/422] docs: update copyright company name --- .../tile-ux/tile-basic-carousel.src/tile-basic-carousel.groovy | 2 +- .../tile-basic-colorwheel.src/tile-basic-colorwheel.groovy | 2 +- .../tile-ux/tile-basic-presence.src/tile-basic-presence.groovy | 2 +- .../tile-ux/tile-basic-slider.src/tile-basic-slider.groovy | 2 +- .../tile-ux/tile-basic-standard.src/tile-basic-standard.groovy | 2 +- .../tile-ux/tile-basic-value.src/tile-basic-value.groovy | 2 +- .../tile-multiattribute-generic.groovy | 2 +- .../tile-multiattribute-lighting.groovy | 2 +- .../tile-multiattribute-mediaplayer.groovy | 2 +- .../tile-multiattribute-thermostat.groovy | 2 +- .../tile-multiattribute-videoplayer.groovy | 2 +- smartapps/smartthings/mood-cube.src/mood-cube.groovy | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/devicetypes/smartthings/tile-ux/tile-basic-carousel.src/tile-basic-carousel.groovy b/devicetypes/smartthings/tile-ux/tile-basic-carousel.src/tile-basic-carousel.groovy index 0af22a23af4..04667d32bfe 100644 --- a/devicetypes/smartthings/tile-ux/tile-basic-carousel.src/tile-basic-carousel.groovy +++ b/devicetypes/smartthings/tile-ux/tile-basic-carousel.src/tile-basic-carousel.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-basic-colorwheel.src/tile-basic-colorwheel.groovy b/devicetypes/smartthings/tile-ux/tile-basic-colorwheel.src/tile-basic-colorwheel.groovy index 102f9e3f294..4a7ce00814f 100644 --- a/devicetypes/smartthings/tile-ux/tile-basic-colorwheel.src/tile-basic-colorwheel.groovy +++ b/devicetypes/smartthings/tile-ux/tile-basic-colorwheel.src/tile-basic-colorwheel.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-basic-presence.src/tile-basic-presence.groovy b/devicetypes/smartthings/tile-ux/tile-basic-presence.src/tile-basic-presence.groovy index 392866c2fe3..c62687368a8 100644 --- a/devicetypes/smartthings/tile-ux/tile-basic-presence.src/tile-basic-presence.groovy +++ b/devicetypes/smartthings/tile-ux/tile-basic-presence.src/tile-basic-presence.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-basic-slider.src/tile-basic-slider.groovy b/devicetypes/smartthings/tile-ux/tile-basic-slider.src/tile-basic-slider.groovy index 68872b512f1..c1f4cf55ea5 100644 --- a/devicetypes/smartthings/tile-ux/tile-basic-slider.src/tile-basic-slider.groovy +++ b/devicetypes/smartthings/tile-ux/tile-basic-slider.src/tile-basic-slider.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-basic-standard.src/tile-basic-standard.groovy b/devicetypes/smartthings/tile-ux/tile-basic-standard.src/tile-basic-standard.groovy index 21fcea6c544..05db6ff7810 100644 --- a/devicetypes/smartthings/tile-ux/tile-basic-standard.src/tile-basic-standard.groovy +++ b/devicetypes/smartthings/tile-ux/tile-basic-standard.src/tile-basic-standard.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-basic-value.src/tile-basic-value.groovy b/devicetypes/smartthings/tile-ux/tile-basic-value.src/tile-basic-value.groovy index e1efb8a1dd9..e1f32441e82 100644 --- a/devicetypes/smartthings/tile-ux/tile-basic-value.src/tile-basic-value.groovy +++ b/devicetypes/smartthings/tile-ux/tile-basic-value.src/tile-basic-value.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-multiattribute-generic.src/tile-multiattribute-generic.groovy b/devicetypes/smartthings/tile-ux/tile-multiattribute-generic.src/tile-multiattribute-generic.groovy index 6ba0e1c3e8f..a4eaa34b322 100644 --- a/devicetypes/smartthings/tile-ux/tile-multiattribute-generic.src/tile-multiattribute-generic.groovy +++ b/devicetypes/smartthings/tile-ux/tile-multiattribute-generic.src/tile-multiattribute-generic.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-multiattribute-lighting.src/tile-multiattribute-lighting.groovy b/devicetypes/smartthings/tile-ux/tile-multiattribute-lighting.src/tile-multiattribute-lighting.groovy index 81e9121c657..1533a5d15e3 100644 --- a/devicetypes/smartthings/tile-ux/tile-multiattribute-lighting.src/tile-multiattribute-lighting.groovy +++ b/devicetypes/smartthings/tile-ux/tile-multiattribute-lighting.src/tile-multiattribute-lighting.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-multiattribute-mediaplayer.src/tile-multiattribute-mediaplayer.groovy b/devicetypes/smartthings/tile-ux/tile-multiattribute-mediaplayer.src/tile-multiattribute-mediaplayer.groovy index 2ad41f42b87..56759a27388 100644 --- a/devicetypes/smartthings/tile-ux/tile-multiattribute-mediaplayer.src/tile-multiattribute-mediaplayer.groovy +++ b/devicetypes/smartthings/tile-ux/tile-multiattribute-mediaplayer.src/tile-multiattribute-mediaplayer.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-multiattribute-thermostat.src/tile-multiattribute-thermostat.groovy b/devicetypes/smartthings/tile-ux/tile-multiattribute-thermostat.src/tile-multiattribute-thermostat.groovy index a784ac57da9..4889190b35a 100644 --- a/devicetypes/smartthings/tile-ux/tile-multiattribute-thermostat.src/tile-multiattribute-thermostat.groovy +++ b/devicetypes/smartthings/tile-ux/tile-multiattribute-thermostat.src/tile-multiattribute-thermostat.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/devicetypes/smartthings/tile-ux/tile-multiattribute-videoplayer.src/tile-multiattribute-videoplayer.groovy b/devicetypes/smartthings/tile-ux/tile-multiattribute-videoplayer.src/tile-multiattribute-videoplayer.groovy index c6dc80da0ff..7e684c578ed 100644 --- a/devicetypes/smartthings/tile-ux/tile-multiattribute-videoplayer.src/tile-multiattribute-videoplayer.groovy +++ b/devicetypes/smartthings/tile-ux/tile-multiattribute-videoplayer.src/tile-multiattribute-videoplayer.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2016 SmartThings, Inc. + * Copyright 2016 Samsung Electronics Co., LTD. * * 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: diff --git a/smartapps/smartthings/mood-cube.src/mood-cube.groovy b/smartapps/smartthings/mood-cube.src/mood-cube.groovy index 1c7b6cf5806..bef5bf06f47 100644 --- a/smartapps/smartthings/mood-cube.src/mood-cube.groovy +++ b/smartapps/smartthings/mood-cube.src/mood-cube.groovy @@ -1,7 +1,7 @@ /** * Mood Cube * - * Copyright 2014 SmartThings, Inc. + * Copyright 2014 Samsung Electronics Co., LTD. * * 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: