From 514ca31592cc45d7913634f8f7bbfd4515fa9199 Mon Sep 17 00:00:00 2001 From: Rafael de Medeiros Borja Gomes Date: Fri, 14 Aug 2020 15:58:06 -0400 Subject: [PATCH 1/5] Fix for child device - Multiple event messages in history - Blinking light after changing dimmer --- .../king-of-fans-zigbee-fan-controller.groovy | 4 +-- ...-fans-zigbee-fan-light-child-device.groovy | 26 +++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy index aafeb93..9d47e86 100644 --- a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy +++ b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy @@ -94,7 +94,7 @@ def parse(String description) { it.device.deviceNetworkId == "${device.deviceNetworkId}-Light" } event.displayed = true - event.isStateChange = true + event.isStateChange = childDevice.isStateChange(event) // TODO find right event childDevice.createAndSendEvent(event) @@ -499,7 +499,7 @@ def refresh(physicalgraph.device.cache.DeviceDTO child=null) { log.info "refresh($child) called " return zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(0x0202, 0x0000) + zigbee.readAttribute(0x0006, 0x0000) + - zigbee.readAttribute(0x0202, 0x0000) + zigbee.readAttribute(0x0202, 0x0001) + zigbee.readAttribute(0x0006, 0x0001) + zigbee.readAttribute(0x0006, 0x0000) + + zigbee.readAttribute(0x0202, 0x0000) + zigbee.readAttribute(0x0202, 0x0001) + /* zigbee.readAttribute(0x0006, 0x0001) + zigbee.readAttribute(0x0006, 0x0000) + */ zigbee.readAttribute(0x0008, 0x0004) + zigbee.readAttribute(0x0008, 0x0004) } diff --git a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy index ac85411..82ab7ce 100644 --- a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy +++ b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy @@ -77,12 +77,34 @@ void refresh() { def createAndSendEvent(map) { log.debug "child[ ${device.deviceNetworkId} ].createAndSendEvent($map)" - results.each { name, value -> - sendEvent(name: name, value: value, displayed: true, isStateChange: true) // check if displayed: true, isStateChange: true is needed + results.each { name, value -> + NEVER_CALLED + sendEvent(name: name, value: value, displayed: true, isStateChange: true) // check if displayed: true, isStateChange: true is needed } return null } +/** + * Returns true if event map values differs from devices values. + */ +def isStateChange(event) { + def eventChange = true; + switch(event['name']) { + case "switch": + eventChange = (event['value'] != null && event['value'] != device.currentValue("switch")) + break + case "level": + eventChange = (event['value'] != null && event['value'] != device.currentValue("level")) + break + default: + log.debug "[isStateChange] Unknown event: ${event}" + break; + } + + log.debug "[isStateChange] This is a state change? (${event} vs ${device.currentValue('level')} and ${device.currentValue('switch')})? ${eventChange}" + return eventChange +} + def parse(description) { log.debug "PARSE IN Child: $description" } From bad121317e981a783b2935b919e1bfa890035f3c Mon Sep 17 00:00:00 2001 From: Rafael Borja <5069188+rafaelborja@users.noreply.github.com> Date: Fri, 14 Aug 2020 16:07:58 -0400 Subject: [PATCH 2/5] Validation scenario for version V.02 --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index cecd01d..b72350f 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,15 @@ To control lights device child, simply use the command "Ok Google, set Fan **Lig ### Validation scenario +#### V.01 This device handler was tested using Samsung Connect Home Pro (Smartthings V2 Hub) with Firmware version 000.027.00010 in a set up with +10 Zigbee devices. I used two King Of Fans, Inc. model HDC52EastwindFan at the same network. Google assistant commands were validated using Google Home app v 2.19.1.18 running under Android Oreo, Google Nest mini running firmware 191160 and Google Nest Hub running firmware 191160. All commands were tested using US-English language set. +#### V.02 +This device handler was tested using SmartThings WiFi, Plume US (Smartthings V3 Hub) with Firmware version 000.029.00009 in a set up with +10 Zigbee devices. I used two King Of Fans, Inc. model HDC52EastwindFan at the same network. +Google assistant commands were validated using Google Home app v 2.26.1.8 running under Android Oreo, Google Nest mini running firmware 191160 and Google Nest Hub running firmware 191160. All commands were tested using US-English language set. + + ### Known issues - Random delays to update child device. - Excess message logging and events generated From ba070c4de397ad7b754dc0f029d19b04047f93a0 Mon Sep 17 00:00:00 2001 From: Rafael Borja <5069188+rafaelborja@users.noreply.github.com> Date: Mon, 17 Aug 2020 10:29:21 -0400 Subject: [PATCH 3/5] Fix for legacy preference of dimmer as fan control --- .../king-of-fans-zigbee-fan-controller.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy index 9d47e86..726a720 100644 --- a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy +++ b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy @@ -509,7 +509,8 @@ def refresh(physicalgraph.device.cache.DeviceDTO child=null) { def useDimmerAsFanControl() { log.trace("useDimmerAsFanControl(): ${settings.dimmerControl}") - return settings.dimmerControl == "Fan" + // Check fro dimmerAsFanControl == 1 for legacy reasons. TODO - Remove in future versions + return settings.dimmerControl == "Fan" || dimmerAsFanControl == 1 } /** @@ -558,4 +559,4 @@ def medium() { def high() { setLevel(99) -} \ No newline at end of file +} From 8adde9439b8c60fcf77f92cc23aeef2889d1426b Mon Sep 17 00:00:00 2001 From: Rafael de Medeiros Borja Gomes Date: Tue, 18 Aug 2020 00:12:22 -0400 Subject: [PATCH 4/5] Minor clean up and logs - Removed createAndSendEvent as it was executing no action - Changes on logging --- ...-of-fans-zigbee-fan-light-child-device.groovy | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy index 82ab7ce..8e8f148 100644 --- a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy +++ b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-light-child-device.src/king-of-fans-zigbee-fan-light-child-device.groovy @@ -74,20 +74,12 @@ void refresh() { parent.childRefresh(device.deviceNetworkId) } - -def createAndSendEvent(map) { - log.debug "child[ ${device.deviceNetworkId} ].createAndSendEvent($map)" - results.each { name, value -> - NEVER_CALLED - sendEvent(name: name, value: value, displayed: true, isStateChange: true) // check if displayed: true, isStateChange: true is needed - } - return null -} - /** * Returns true if event map values differs from devices values. */ def isStateChange(event) { + log.trace "[isStateChange](${eventChange})" + def eventChange = true; switch(event['name']) { case "switch": @@ -101,8 +93,8 @@ def isStateChange(event) { break; } - log.debug "[isStateChange] This is a state change? (${event} vs ${device.currentValue('level')} and ${device.currentValue('switch')})? ${eventChange}" - return eventChange + log.trace "[isStateChange] ${eventChange}" + return eventChange } def parse(description) { From 8f2afd70daff718cbe80fc0995994d8691a74fa3 Mon Sep 17 00:00:00 2001 From: Rafael de Medeiros Borja Gomes Date: Tue, 18 Aug 2020 00:19:05 -0400 Subject: [PATCH 5/5] Fix for duplicated events - Detected issue with on/off button on remote control - Removed unnecessary childDevice.createAndSendEvent(event) - Removing duplicated and unnecessary events on refresh() --- .../king-of-fans-zigbee-fan-controller.groovy | 47 ++++++------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy index 726a720..6c774f7 100644 --- a/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy +++ b/devicetypes/rafaelborja/king-of-fans-zigbee-fan-controller.src/king-of-fans-zigbee-fan-controller.groovy @@ -82,7 +82,10 @@ metadata { def parse(String description) { log.info "parse($description)" + // TODO description 'on/off: 1' or 'on/off: 0' can represent both light on/of or power on/off on remote control. After this event fan speed and state must be retrieved and updated def event = zigbee.getEvent(description) + + if (event) { // "Sample 0104 0006 01 01 0000 00 D42D 00 00 0000 01 01 010086" @@ -96,11 +99,7 @@ def parse(String description) { event.displayed = true event.isStateChange = childDevice.isStateChange(event) - // TODO find right event - childDevice.createAndSendEvent(event) - // childDevice.createAndSendEvent(description) childDevice.sendEvent(event) - // TODO not needed? childDevice.parse(description) //send light events to light child device and update lightBrightness attribute if(event.value != "on" && event.value != "off" && !useDimmerAsFanControl()) { @@ -315,13 +314,6 @@ def off (physicalgraph.device.cache.DeviceDTO child) { def childDevice = getChildDevices()?.find { it.device.deviceNetworkId == "${device.deviceNetworkId}-Light" } - if (childDevice) { - log.debug "Sending event to child $childDevice" - log.debug childDevice - childDevice.createAndSendEvent(name: "switch", value: "off", displayed: true, isStateChange: true) // childDevice.sendEvent(name: "device.switch", value: "off", displayed: true, isStateChange: true) + - // childDevice.sendEvent(name: "switch", value: "off", displayed: true, isStateChange: true) + - // childDevice.createAndSendEvent(name: "switch", value: "off", displayed: true, isStateChange: true) - } lightOff(getEndpoint(child)) } @@ -331,14 +323,6 @@ def on (physicalgraph.device.cache.DeviceDTO child) { def childDevice = getChildDevices()?.find { it.device.deviceNetworkId == "${device.deviceNetworkId}-Light" - } - if (childDevice) { - log.debug "Sending event to child $childDevice" - log.debug childDevice - - childDevice.createAndSendEvent(name: "switch", value: "on", displayed: true, isStateChange: true) // childDevice.sendEvent(name: "device.switch", value: "on", displayed: true, isStateChange: true) + - //childDevice.sendEvent(name: "switch", value: "on", displayed: true, isStateChange: true) + - } lightOn(getEndpoint(child)) @@ -388,7 +372,6 @@ def lightOn(String dni) { log.debug childDevice childDevice.sendEvent(name: "device.switch", value: "on", displayed: true, isStateChange: true) childDevice.sendEvent(name: "switch", value: "on", displayed: true, isStateChange: true) - childDevice.createEvent(childDevice.createAndSendEvent(name: "switch", value: "on", displayed: true, isStateChange: true )) } return zigbee.on() @@ -408,7 +391,6 @@ def lightOff(String id) { log.debug childDevice childDevice.sendEvent(name: "device.switch", value: "off", displayed: true, isStateChange: true) childDevice.sendEvent(name: "switch", value: "off", displayed: true, isStateChange: true) - childDevice.createEvent(childDevice.createAndSendEvent(name: "switch", value: "off", displayed: true, isStateChange: true )) } return zigbee.off() @@ -416,7 +398,6 @@ def lightOff(String id) { } - void childOn(String dni) { log.debug "childOn(String ${dni})" lightOn(null) @@ -441,11 +422,8 @@ def lightLevel(val) { if (childDevice) { log.debug "Sending event to child" log.debug childDevice - //childDevice.sendEvent(name: "device.value", value: val) - // childDevice.sendEvent(name: "device.switch", value: "on", isStatusChange: true) childDevice.sendEvent(name: "switch", value: isDeviceOn? "on": "off", isStatusChange: true, display: true) childDevice.sendEvent(name: "value", value: val, isStatusChange: true, display: true) - childDevice.createEvent(childDevice.createAndSendEvent(name: "level", value: value, isStatusChange: true, display: true)) } } @@ -498,9 +476,15 @@ def ping() { def refresh(physicalgraph.device.cache.DeviceDTO child=null) { log.info "refresh($child) called " - return zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(0x0202, 0x0000) + zigbee.readAttribute(0x0006, 0x0000) + - zigbee.readAttribute(0x0202, 0x0000) + zigbee.readAttribute(0x0202, 0x0001) + /* zigbee.readAttribute(0x0006, 0x0001) + zigbee.readAttribute(0x0006, 0x0000) + */ - zigbee.readAttribute(0x0008, 0x0004) + zigbee.readAttribute(0x0008, 0x0004) + log.trace "That's tthe refresh: ${zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(0x0202, 0x0000) + zigbee.readAttribute(0x0006, 0x0000) + zigbee.readAttribute(0x0202, 0x0001) + zigbee.readAttribute(0x0008, 0x0004)}" + return zigbee.onOffRefresh() /* same as zigbee.readAttribute(0x0006, 0x0000) + */ + + zigbee.levelRefresh() + + zigbee.readAttribute(0x0202, 0x0000) + + zigbee.readAttribute(0x0006, 0x0000) + + zigbee.readAttribute(0x0202, 0x0001) + + zigbee.readAttribute(0x0008, 0x0004) + // + zigbee.readAttribute(0x0006, 0x0001) TODO Confirm it was the cause of blinking light + } /** @@ -508,8 +492,7 @@ def refresh(physicalgraph.device.cache.DeviceDTO child=null) { */ def useDimmerAsFanControl() { log.trace("useDimmerAsFanControl(): ${settings.dimmerControl}") - - // Check fro dimmerAsFanControl == 1 for legacy reasons. TODO - Remove in future versions + // Check for dimmerAsFanControl == 1 for legacy reasons. TODO - Remove in future versions return settings.dimmerControl == "Fan" || dimmerAsFanControl == 1 } @@ -539,8 +522,6 @@ def dimmerLevelToSpeed(dimmerLevel) { } - - def raiseFanSpeed() { setFanSpeed(Math.min((device.currentValue("fanSpeed") as Integer) + 1, 3)) } @@ -559,4 +540,4 @@ def medium() { def high() { setLevel(99) -} +} \ No newline at end of file