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 1/8] [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 2/8] =?UTF-8?q?issue=20fix=20:=20ICP-12604:=20Supported=20?= =?UTF-8?q?temperature=20values=20=E2=80=8B=E2=80=8Bfor=20the=20following?= =?UTF-8?q?=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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] =?UTF-8?q?CHAD-5172=20Send=20follow-ups=20to=20verify?= =?UTF-8?q?=20that=20window=20shade=20level=20changes=20o=E2=80=A6=20(#362?= =?UTF-8?q?79)?= 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 8/8] 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())) } }