From 144c1d1a43dcf3d5d2f4fe9f368401327273bde9 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Tue, 9 Apr 2024 10:23:15 +0300 Subject: [PATCH 01/26] Add WS/CT implementation --- ikea-zigbee-drivers/Aqara_DCM-K01.groovy | 10 +- ikea-zigbee-drivers/CHANGELOG.md | 4 + ..._ICPSHC24.groovy => Ikea_DIM-Light.groovy} | 231 ++--- ikea-zigbee-drivers/Ikea_E1603.groovy | 20 +- ikea-zigbee-drivers/Ikea_E1743.groovy | 10 +- ikea-zigbee-drivers/Ikea_E1745.groovy | 10 +- ikea-zigbee-drivers/Ikea_E1766.groovy | 10 +- ikea-zigbee-drivers/Ikea_E1810.groovy | 10 +- ikea-zigbee-drivers/Ikea_E1812.groovy | 14 +- ikea-zigbee-drivers/Ikea_E1836.groovy | 16 +- ikea-zigbee-drivers/Ikea_E2002.groovy | 14 +- ikea-zigbee-drivers/Ikea_E2006.groovy | 14 +- ikea-zigbee-drivers/Ikea_E2013.groovy | 10 +- ikea-zigbee-drivers/Ikea_E2112.groovy | 10 +- ikea-zigbee-drivers/Ikea_E2123.groovy | 14 +- ikea-zigbee-drivers/Ikea_E2134.groovy | 10 +- ikea-zigbee-drivers/Ikea_E2201.groovy | 10 +- ikea-zigbee-drivers/Ikea_E2202.groovy | 10 +- ikea-zigbee-drivers/Ikea_E2204.groovy | 16 +- ikea-zigbee-drivers/Ikea_E2213.groovy | 10 +- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 863 ++++++++++++++++++ ikea-zigbee-drivers/Legrand_741811.groovy | 16 +- ikea-zigbee-drivers/Makefile | 2 +- ikea-zigbee-drivers/Philips_RDM001.groovy | 10 +- ikea-zigbee-drivers/Philips_RWL022.groovy | 10 +- ikea-zigbee-drivers/README.md | 122 ++- ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy | 10 +- ikea-zigbee-drivers/img/Ikea_CWS-Light.webp | Bin 0 -> 8218 bytes ikea-zigbee-drivers/img/Ikea_DIM-Light.webp | Bin 0 -> 7372 bytes ikea-zigbee-drivers/img/Ikea_WS-Light.webp | Bin 0 -> 7036 bytes ikea-zigbee-drivers/packageManifest.json | 54 +- ikea-zigbee-drivers/src/blueprint.groovy | 8 +- .../src/capabilities/Brightness.groovy | 189 ++-- .../src/capabilities/ColorTemperature.groovy | 155 ++++ .../src/capabilities/Switch.groovy | 6 +- ikea-zigbee-drivers/src/common.yaml | 2 +- .../config.yaml | 21 +- .../src/devices/Ikea_WS-Light/config.yaml | 30 + 38 files changed, 1473 insertions(+), 478 deletions(-) rename ikea-zigbee-drivers/{Ikea_ICPSHC24.groovy => Ikea_DIM-Light.groovy} (82%) create mode 100644 ikea-zigbee-drivers/Ikea_WS-Light.groovy create mode 100644 ikea-zigbee-drivers/img/Ikea_CWS-Light.webp create mode 100644 ikea-zigbee-drivers/img/Ikea_DIM-Light.webp create mode 100644 ikea-zigbee-drivers/img/Ikea_WS-Light.webp create mode 100644 ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy rename ikea-zigbee-drivers/src/devices/{Ikea_ICPSHC24 => Ikea_DIM-Light}/config.yaml (78%) create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml diff --git a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy index 3c5839b..ff81aba 100644 --- a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy +++ b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'Aqara Dual Relay Module T2 (DCM-K01)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.MultiRelay import com.hubitat.app.ChildDeviceWrapper @@ -38,8 +38,7 @@ metadata { capability 'PushableButton' capability 'HealthCheck' - // For firmware: Unknown - fingerprint profileId:'0104', endpointId:'01', inClusters:'0B04,0702,0005,0004,0003,0012,0000,0006,FCC0', outClusters:'0019,000A', model:'lumi.switch.acn047', manufacturer:'Aqara' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0B04,0702,0005,0004,0003,0012,0000,0006,FCC0', outClusters:'0019,000A', model:'lumi.switch.acn047', manufacturer:'Aqara' // For firmware: Unknown // Attributes for devices.Aqara_DCM-K01 attribute 'powerOutageCount', 'number' @@ -56,7 +55,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- Aqara Dual Relay Module T2 (DCM-K01) v4.1.0
+ Aqara Dual Relay Module T2 (DCM-K01) v5.0.0
device details
community page
@@ -781,6 +780,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index b01df57..2cde98a 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [5.0.0] - 2024-04-?? +- Add driver for Dimmable Lights, including LED drivers +- Add driver for White Spectrum Lights (CT) + ## [4.1.0] - 2024-04-06 ### Added diff --git a/ikea-zigbee-drivers/Ikea_ICPSHC24.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy similarity index 82% rename from ikea-zigbee-drivers/Ikea_ICPSHC24.groovy rename to ikea-zigbee-drivers/Ikea_DIM-Light.groovy index bef356f..13afc5a 100644 --- a/ikea-zigbee-drivers/Ikea_ICPSHC24.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -1,13 +1,13 @@ /** - * IKEA Tradfri LED Driver (ICPSHC24) + * IKEA Dimmable Light * * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ */ import groovy.transform.CompileStatic import groovy.transform.Field -@Field static final String DRIVER_NAME = 'IKEA Tradfri LED Driver (ICPSHC24)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_NAME = 'IKEA Dimmable Light' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -23,7 +23,7 @@ import groovy.time.TimeCategory ] metadata { - definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_ICPSHC24.groovy') { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_DIM-Light.groovy') { capability 'Configuration' capability 'Refresh' capability 'Actuator' @@ -33,17 +33,10 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 10EU-IL-1/1.2.245 (117C-4101-12245572) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - - // For firmware: 10EU-IL-1/2.3.086 (117C-4101-23086631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - - // For firmware: 30EU-IL-2/1.0.002 (117C-4109-00010002) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' - - // For firmware: 30-IL44-1/1.0.021 (117C-4104-00010021) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // Type 10EU-IL-1 (Tradfri LED Driver 10W): 1.2.245 (117C-4101-12245572) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // Type 10EU-IL-1 (Tradfri LED Driver 10W): 2.3.086 (117C-4101-23086631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // Type 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // Type 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -51,11 +44,10 @@ metadata { // Commands for capability.Switch command 'toggle' - command 'onWithTimedOff', [[name:'On time*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] // Commands for capability.Brightness - command 'levelUp' - command 'levelDown' + command 'shiftLevel', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] // Commands for capability.FirmwareUpdate command 'updateFirmware' @@ -64,10 +56,10 @@ metadata { input( name: 'helpInfo', type: 'hidden', title: ''' -
- IKEA Tradfri LED Driver (ICPSHC24) v4.1.0
+
+ IKEA Dimmable Light v5.0.0
@@ -105,32 +97,42 @@ metadata { // Inputs for capability.Brightness input( name: 'levelStep', type: 'enum', - title: 'Brightness up/down step', - description: 'Level adjust when using the levelUp/levelDown commands.', + title: 'Brightness up/down shift', + description: 'Brightness +/- adjust for the shiftLevel() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true + ) + input( + name: 'levelChangeRate', type: 'enum', + title: 'Brightness change rate', + description: 'Brightness +/- adjust for the startLevelChange() command.', options: [ - '1': '1%', - '2': '2%', - '5': '5%', - '10': '10%', - '20': '20%', - '25': '25%', - '33': '33%' + '10': '10% / sec - from 0% to 100% in 10 seconds', + '20': '20% / sec - from 0% to 100% in 5 seconds', + '33': '33% / sec - from 0% to 100% in 3 seconds', + '50': '50% / secs - from 0% to 100% in 2 seconds', + '100': '100% / sec - from 0% to 100% in 1 seconds', ], defaultValue: '20', required: true ) input( - name: 'startLevelChangeRate', type: 'enum', - title: 'Brightness change rate', - description: 'The rate of brightness change when using the startLevelChange() command.', + name: 'transitionTime', type: 'enum', + title: 'Brightness transition time', + description: 'Time taken to move to/from the target brightness when device is turned On/Off.', options: [ - '10': '10% / second : from 0% to 100% in 10 seconds', - '20': '20% / second : from 0% to 100% in 5 seconds', - '33': '33% / second : from 0% to 100% in 3 seconds', - '50': '50% / seconds : from 0% to 100% in 2 seconds', - '100': '100% / second : from 0% to 100% in 1 seconds', + '0': 'Instant', + '5': '0.5 seconds', + '10': '1 second', + '15': '1.5 seconds', + '20': '2 seconds', + '30': '3 seconds', + '40': '4 seconds', + '50': '5 seconds', + '100': '10 seconds' ], - defaultValue: '20', + defaultValue: '5', required: true ) input( @@ -155,28 +157,10 @@ metadata { required: true ) } - input( - name: 'transitionTime', type: 'enum', - title: 'On/Off transition time', - description: 'Time taken to move to/from the target brightness when device is turned On/Off.', - options: [ - '0': 'Instant', - '5': '0.5 seconds', - '10': '1 second', - '15': '1.5 seconds', - '20': '2 seconds', - '30': '3 seconds', - '40': '4 seconds', - '50': '5 seconds', - '100': '10 seconds' - ], - defaultValue: '5', - required: true - ) input( name: 'prestaging', type: 'bool', title: 'Pre-staging', - description: 'Set the brightness level without turning On the device (for later use).', + description: 'Set brightness level without turning On the device (for later use).', defaultValue: false, required: true ) @@ -232,11 +216,11 @@ List updated(boolean auto = false) { } log_info "🛠️ levelStep = ${levelStep}%" - if (startLevelChangeRate == null) { - startLevelChangeRate = '20' - device.updateSetting 'startLevelChangeRate', [value:startLevelChangeRate, type:'enum'] + if (levelChangeRate == null) { + levelChangeRate = '20' + device.updateSetting 'levelChangeRate', [value:levelChangeRate, type:'enum'] } - log_info "🛠️ startLevelChangeRate = ${startLevelChangeRate}% / second" + log_info "🛠️ levelChangeRate = ${levelChangeRate}% / second" if (turnOnBehavior == null) { turnOnBehavior = 'RESTORE_PREVIOUS_LEVEL' @@ -244,10 +228,11 @@ List updated(boolean auto = false) { } log_info "🛠️ turnOnBehavior = ${turnOnBehavior}" if (turnOnBehavior == 'FIXED_VALUE') { - Integer lvl = onLevelValue == null ? 50 : onLevelValue.intValue() - device.updateSetting 'onLevelValue', [value:lvl, type:'number'] - log_info "🛠️ onLevelValue = ${lvl}%" - applyOnLevel(lvl) + Integer onLevelValue = onLevelValue == null ? 50 : onLevelValue.intValue() + device.updateSetting 'onLevelValue', [value:onLevelValue, type:'number'] + log_info "🛠️ onLevelValue = ${onLevelValue}%" + Integer lvl = onLevelValue * 2.54 + utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) } else { log_debug 'Disabling OnLevel (0xFF)' cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) @@ -266,6 +251,9 @@ List updated(boolean auto = false) { } log_info "🛠️ prestaging = ${prestaging}" + // If prestaging is true, enable update of brightness without the need for the device to be turned On + cmds += zigbee.writeAttribute(0x0008, 0x000F, 0x18, prestaging ? 0x01 : 0x00) + // Preferences for capability.HealthCheck schedule HEALTH_CHECK.schedule, 'healthCheck' @@ -336,7 +324,6 @@ void configure(boolean auto = false) { cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes // Configuration for capability.Brightness - sendEvent name:'level', value:'100', type:'digital', descriptionText:'Brightness initialized to 100%' cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) @@ -401,77 +388,39 @@ void toggle() { void onWithTimedOff(BigDecimal onTime = 1) { Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) log_debug 'Sending OnWithTimedOff command' - - String payload = "00 ${zigbee.swapOctets(zigbee.convertToHexString(delay * 10, 4))} 0000" + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) } // Implementation for capability.Brightness +void setLevel(BigDecimal level, BigDecimal duration = 0) { + Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) + log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" + Integer lvl = newLevel * 2.54 + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String command = prestaging == false ? '04' : '00' + String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) +} void startLevelChange(String direction) { - log_debug "Starting brightness change ${direction}wards with a rate of ${startLevelChangeRate}% / second" - + log_debug "Starting brightness level change ${direction}wards with a rate of ${levelChangeRate}% / second" Integer mode = direction == 'up' ? 0x00 : 0x01 - Integer rate = Integer.parseInt(startLevelChangeRate) * 2.54 - String payload = "${zigbee.convertToHexString(mode, 2)} ${zigbee.convertToHexString(rate, 2)}" + Integer rate = Integer.parseInt(levelChangeRate) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload rate, 2}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114301 ${payload}}"]) } void stopLevelChange() { - log_debug 'Stopping brightness change' + log_debug 'Stopping brightness level change' utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114303}"]) } -void levelUp() { - log_debug "Moving brightness up by ${levelStep}%" - +void shiftLevel(String direction) { + log_debug "Shifting brightness level ${direction} by ${levelStep}%" + Integer mode = direction == 'up' ? 0x00 : 0x01 Integer stepSize = Integer.parseInt(levelStep) * 2.54 - Integer dur = 0 - - String payload = "${zigbee.convertToHexString(0x00, 2)} ${zigbee.convertToHexString(stepSize, 2)} ${zigbee.swapOctets(zigbee.convertToHexString(dur, 4))}" + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 2} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) } -void levelDown() { - log_debug "Moving brightness down by ${levelStep}%" - - Integer stepSize = Integer.parseInt(levelStep) * 2.54 - Integer dur = 0 - - String payload = "${zigbee.convertToHexString(0x01, 2)} ${zigbee.convertToHexString(stepSize, 2)} ${zigbee.swapOctets(zigbee.convertToHexString(dur, 4))}" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) -} -void setLevel(BigDecimal level, BigDecimal duration = 0) { - Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) - log_debug "Setting brightness to ${newLevel}% during ${duration} seconds" - - // Device is On: use the Move To Level command - if (device.currentValue('switch', true) == 'on' || prestaging == false) { - Integer lvl = newLevel * 2.54 - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min - String command = prestaging == false ? '04' : '00' - String payload = "${zigbee.convertToHexString(lvl, 2)} ${zigbee.swapOctets(zigbee.convertToHexString(dur, 4))}" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) - return - } - - // Device is Off and onLevel is set to a fixed value: ignore command - if (turnOnBehavior == 'FIXED_VALUE') { - log_info 'Ignoring Set Level command because the device is turned Off and "Turn On behavior" preference is set to "Always start with the same fixed brightness"' - return - } - - // Device is Off: keep the device turned Off, use the OnLevel attribute - log_debug('Device is turned Off so we pre-stage brightness level for when the device will be turned On') - applyOnLevel newLevel - utils_sendEvent(name:'level', value:newLevel, descriptionText:"Brightness is ${newLevel}%", type:'digital', isStateChange:true) -} -void applyOnLevel(Integer level) { - Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) - log_debug "Setting Turn On brightness to ${newLevel}%" - Integer lvl = newLevel * 2.54 - utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) -} -private void turnOnCallback(String switchState) { - // Device was just turned on: Read the value of the OnLevel attribute to sync/update its value - if (switchState == 'on') utils_sendZigbeeCommands zigbee.readAttribute(0x0008, 0x0011) -} // Implementation for capability.HealthCheck void ping() { @@ -556,10 +505,6 @@ void parse(String description) { String newState = msg.value == '00' ? 'off' : 'on' utils_sendEvent name:'switch', value:newState, descriptionText:"Was turned ${newState}", type:type - // Execute the configured callback: turnOnCallback - if (device.currentValue('switch', true) != newState) { - turnOnCallback(newState) - } utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "OnOff=${newState}" return @@ -591,33 +536,14 @@ void parse(String description) { // Report/Read Attributes Reponse: CurrentLevel case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: - Integer newLevel = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) - utils_sendEvent name:'level', value:newLevel, descriptionText:"Brightness is ${newLevel}%", type:'digital' - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value}" - return - - // Read Attributes Reponse: OnLevel - // This value is read immediately after the device is turned On - // @see turnOnCallback() - case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0011] }: - Integer onLevel = msg.value == '00' ? 0 : Integer.parseInt(msg.value, 16) * 100 / 254 - - // Clear OnLevel attribute value (if previously set) - if (turnOnBehavior != 'FIXED_VALUE' && msg.value != 'FF') { - setLevel device.currentValue('level', true) - log_debug 'Disabling OnLevel (0xFF)' - utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) - return - } - - // Set current level to OnLevel - if (turnOnBehavior == 'FIXED_VALUE') setLevel(onLevel) - utils_processedZclMessage 'Read Attributes Response', "OnLevel=${msg.value}" + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) + utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return // Other events that we expect but are not usefull for capability.Brightness behavior case { contains it, [clusterInt:0x0008, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=level, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) return @@ -800,6 +726,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1603.groovy b/ikea-zigbee-drivers/Ikea_E1603.groovy index 3ba1cf1..5aff4c3 100644 --- a/ikea-zigbee-drivers/Ikea_E1603.groovy +++ b/ikea-zigbee-drivers/Ikea_E1603.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tradfri Control Outlet (E1603)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -32,11 +32,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 2.0.024 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' - - // For firmware: 2.3.089 (117C-1101-23089631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.0.024 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.3.089 (117C-1101-23089631) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -44,7 +41,7 @@ metadata { // Commands for capability.Switch command 'toggle' - command 'onWithTimedOff', [[name:'On time*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] // Commands for capability.FirmwareUpdate command 'updateFirmware' @@ -54,7 +51,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tradfri Control Outlet (E1603) v4.1.0
+ IKEA Tradfri Control Outlet (E1603) v5.0.0
device details
community page
@@ -262,8 +259,8 @@ void toggle() { void onWithTimedOff(BigDecimal onTime = 1) { Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) log_debug 'Sending OnWithTimedOff command' - - String payload = "00 ${zigbee.swapOctets(zigbee.convertToHexString(delay * 10, 4))} 0000" + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) } @@ -553,6 +550,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index 15d327e..dcc55d5 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tradfri On/Off Switch (E1743)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -34,8 +34,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -49,7 +48,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tradfri On/Off Switch (E1743) v4.1.0
+ IKEA Tradfri On/Off Switch (E1743) v5.0.0
device details
community page
@@ -616,6 +615,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1745.groovy b/ikea-zigbee-drivers/Ikea_E1745.groovy index 9ab284d..69d7f37 100644 --- a/ikea-zigbee-drivers/Ikea_E1745.groovy +++ b/ikea-zigbee-drivers/Ikea_E1745.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tradfri Motion Sensor (E1745)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -32,8 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 24.4.5 (117C-11C8-24040005) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'TRADFRI motion sensor', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'TRADFRI motion sensor', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C8-24040005) // Attributes for devices.Ikea_E1745 attribute 'requestedBrightness', 'number' // Syncs with the brightness option on device (◐/⭘) @@ -51,7 +50,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tradfri Motion Sensor (E1745) v4.1.0
+ IKEA Tradfri Motion Sensor (E1745) v5.0.0
device details
community page
@@ -669,6 +668,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1766.groovy b/ikea-zigbee-drivers/Ikea_E1766.groovy index 5a04b21..d2b9b4b 100644 --- a/ikea-zigbee-drivers/Ikea_E1766.groovy +++ b/ikea-zigbee-drivers/Ikea_E1766.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tradfri Open/Close Remote (E1766)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -34,8 +34,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 24.4.6 (117C-11C6-24040006) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -49,7 +48,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tradfri Open/Close Remote (E1766) v4.1.0
+ IKEA Tradfri Open/Close Remote (E1766) v5.0.0
device details
community page
@@ -607,6 +606,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index cb2af92..d0c5db1 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tradfri Remote Control (E1810)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -37,8 +37,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 24.4.5 (117C-11C1-24040005) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C1-24040005) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -52,7 +51,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tradfri Remote Control (E1810) v4.1.0
+ IKEA Tradfri Remote Control (E1810) v5.0.0
device details
community page
@@ -645,6 +644,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1812.groovy b/ikea-zigbee-drivers/Ikea_E1812.groovy index b880043..271d924 100644 --- a/ikea-zigbee-drivers/Ikea_E1812.groovy +++ b/ikea-zigbee-drivers/Ikea_E1812.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tradfri Shortcut Button (E1812)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -34,11 +34,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 2.3.015 (117C-11C6-23015631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' - - // For firmware: 24.4.6 (117C-11C6-24040006) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 2.3.015 (117C-11C6-23015631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -52,7 +49,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tradfri Shortcut Button (E1812) v4.1.0
+ IKEA Tradfri Shortcut Button (E1812) v5.0.0
device details
community page
@@ -632,6 +629,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E1836.groovy b/ikea-zigbee-drivers/Ikea_E1836.groovy index 81df1f5..32668bf 100644 --- a/ikea-zigbee-drivers/Ikea_E1836.groovy +++ b/ikea-zigbee-drivers/Ikea_E1836.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Askvader On/Off Switch (E1836)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -32,8 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 1.0.002 (117C-110D-00010002) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'ASKVADER on/off switch', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'ASKVADER on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 1.0.002 (117C-110D-00010002) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -41,7 +40,7 @@ metadata { // Commands for capability.Switch command 'toggle' - command 'onWithTimedOff', [[name:'On time*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] // Commands for capability.FirmwareUpdate command 'updateFirmware' @@ -51,7 +50,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Askvader On/Off Switch (E1836) v4.1.0
+ IKEA Askvader On/Off Switch (E1836) v5.0.0
device details
community page
@@ -259,8 +258,8 @@ void toggle() { void onWithTimedOff(BigDecimal onTime = 1) { Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) log_debug 'Sending OnWithTimedOff command' - - String payload = "00 ${zigbee.swapOctets(zigbee.convertToHexString(delay * 10, 4))} 0000" + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) } @@ -550,6 +549,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index a8af855..135ef4e 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Styrbar Remote Control N2 (E2002)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -36,11 +36,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 1.0.024 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' - - // For firmware: 2.4.5 (117C-11CB-02040005) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.024 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 2.4.5 (117C-11CB-02040005) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -54,7 +51,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Styrbar Remote Control N2 (E2002) v4.1.0
+ IKEA Styrbar Remote Control N2 (E2002) v5.0.0
device details
community page
@@ -656,6 +653,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2006.groovy b/ikea-zigbee-drivers/Ikea_E2006.groovy index aa5f1f7..2953cc8 100644 --- a/ikea-zigbee-drivers/Ikea_E2006.groovy +++ b/ikea-zigbee-drivers/Ikea_E2006.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Starkvind Air Purifier (E2006)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for devices.Ikea_E2006 @Field static final List SUPPORTED_FAN_SPEEDS = [ @@ -35,11 +35,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 1.0.033 (117C-110C-00010033) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' - - // For firmware: 1.1.001 (117C-110C-00011001) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7C,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.0.033 (117C-110C-00010033) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7C,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.1.001 (117C-110C-00011001) // Attributes for devices.Ikea_E2006 attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive groups', 'unhealthy', 'hazardous'] @@ -63,7 +60,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Starkvind Air Purifier (E2006) v4.1.0
+ IKEA Starkvind Air Purifier (E2006) v5.0.0
device details
community page
@@ -712,6 +709,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2013.groovy b/ikea-zigbee-drivers/Ikea_E2013.groovy index 8b526f7..43d0c52 100644 --- a/ikea-zigbee-drivers/Ikea_E2013.groovy +++ b/ikea-zigbee-drivers/Ikea_E2013.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Parasoll Door/Window Sensor (E2013)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.IAS import hubitat.zigbee.clusters.iaszone.ZoneStatus @@ -35,8 +35,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 1.0.19 (117C-3277-01000019) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'PARASOLL Door/Window Sensor', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'PARASOLL Door/Window Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.19 (117C-3277-01000019) // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -53,7 +52,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Parasoll Door/Window Sensor (E2013) v4.1.0
+ IKEA Parasoll Door/Window Sensor (E2013) v5.0.0
device details
community page
@@ -684,6 +683,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2112.groovy b/ikea-zigbee-drivers/Ikea_E2112.groovy index 7dbdc99..3bde206 100644 --- a/ikea-zigbee-drivers/Ikea_E2112.groovy +++ b/ikea-zigbee-drivers/Ikea_E2112.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Vindstyrka Air Quality Sensor (E2112)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -28,8 +28,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 1.0.010 (117C-110F-00010010) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0402,0405,FC57,FC7C,042A,FC7E', outClusters:'0003,0019,0020,0202', model:'VINDSTYRKA', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0402,0405,FC57,FC7C,042A,FC7E', outClusters:'0003,0019,0020,0202', model:'VINDSTYRKA', manufacturer:'IKEA of Sweden' // For firmware: 1.0.010 (117C-110F-00010010) // Attributes for E2112.FineParticulateMatter attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive groups', 'unhealthy', 'hazardous'] @@ -50,7 +49,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Vindstyrka Air Quality Sensor (E2112) v4.1.0
+ IKEA Vindstyrka Air Quality Sensor (E2112) v5.0.0
device details
community page
@@ -537,6 +536,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2123.groovy b/ikea-zigbee-drivers/Ikea_E2123.groovy index 23b8c54..de098e0 100644 --- a/ikea-zigbee-drivers/Ikea_E2123.groovy +++ b/ikea-zigbee-drivers/Ikea_E2123.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Symfonisk Sound Remote Gen2 (E2123)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -45,11 +45,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 1.0.012 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' - - // For firmware: 1.0.35 (117C-110E-01000035) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.012 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.35 (117C-110E-01000035) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -63,7 +60,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Symfonisk Sound Remote Gen2 (E2123) v4.1.0
+ IKEA Symfonisk Sound Remote Gen2 (E2123) v5.0.0
device details
community page
@@ -736,6 +733,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index d9969d0..29e3dce 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Vallhorn Motion Sensor (E2134)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -33,8 +33,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 1.0.57 (117C-1938-01000057) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.57 (117C-1938-01000057) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -48,7 +47,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Vallhorn Motion Sensor (E2134) v4.1.0
+ IKEA Vallhorn Motion Sensor (E2134) v5.0.0
device details
community page
@@ -640,6 +639,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2201.groovy b/ikea-zigbee-drivers/Ikea_E2201.groovy index 378dd74..a052063 100644 --- a/ikea-zigbee-drivers/Ikea_E2201.groovy +++ b/ikea-zigbee-drivers/Ikea_E2201.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Rodret Dimmer (E2201)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -39,8 +39,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 1.0.47 (117C-11CD-01000047) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' // For firmware: 1.0.47 (117C-11CD-01000047) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -54,7 +53,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Rodret Dimmer (E2201) v4.1.0
+ IKEA Rodret Dimmer (E2201) v5.0.0
device details
community page
@@ -657,6 +656,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index 7f841d7..72ee6dc 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Badring Water Leakage Sensor (E2202)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.IAS import hubitat.zigbee.clusters.iaszone.ZoneStatus @@ -30,8 +30,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 1.0.7 (117C-24D4-01000007) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0500,0B05,FC7C,FC81', outClusters:'0003,0004,0019', model:'BADRING Water Leakage Sensor', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0500,0B05,FC7C,FC81', outClusters:'0003,0004,0019', model:'BADRING Water Leakage Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.7 (117C-24D4-01000007) // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -48,7 +47,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Badring Water Leakage Sensor (E2202) v4.1.0
+ IKEA Badring Water Leakage Sensor (E2202) v5.0.0
device details
community page
@@ -508,6 +507,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2204.groovy b/ikea-zigbee-drivers/Ikea_E2204.groovy index 6e717aa..b3b4a7e 100644 --- a/ikea-zigbee-drivers/Ikea_E2204.groovy +++ b/ikea-zigbee-drivers/Ikea_E2204.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Tretakt Smart Plug (E2204)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -32,8 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 2.4.4 (117C-1100-02040004) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57,FC7C,FC85', outClusters:'0019', model:'TRETAKT Smart plug', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57,FC7C,FC85', outClusters:'0019', model:'TRETAKT Smart plug', manufacturer:'IKEA of Sweden' // For firmware: 2.4.4 (117C-1100-02040004) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -41,7 +40,7 @@ metadata { // Commands for capability.Switch command 'toggle' - command 'onWithTimedOff', [[name:'On time*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] // Commands for capability.FirmwareUpdate command 'updateFirmware' @@ -51,7 +50,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Tretakt Smart Plug (E2204) v4.1.0
+ IKEA Tretakt Smart Plug (E2204) v5.0.0
device details
community page
@@ -292,8 +291,8 @@ void toggle() { void onWithTimedOff(BigDecimal onTime = 1) { Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) log_debug 'Sending OnWithTimedOff command' - - String payload = "00 ${zigbee.swapOctets(zigbee.convertToHexString(delay * 10, 4))} 0000" + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) } @@ -604,6 +603,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_E2213.groovy b/ikea-zigbee-drivers/Ikea_E2213.groovy index 3a51fcc..5e81d0d 100644 --- a/ikea-zigbee-drivers/Ikea_E2213.groovy +++ b/ikea-zigbee-drivers/Ikea_E2213.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Somrig Shortcut Button (E2213)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -35,8 +35,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 1.0.20 (117C-3B08-01000020) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' // For firmware: 1.0.20 (117C-3B08-01000020) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -50,7 +49,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- IKEA Somrig Shortcut Button (E2213) v4.1.0
+ IKEA Somrig Shortcut Button (E2213) v5.0.0
device details
community page
@@ -516,6 +515,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy new file mode 100644 index 0000000..8e3d3b7 --- /dev/null +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -0,0 +1,863 @@ +/** + * IKEA White Spectrum Light + * + * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ + */ +import groovy.transform.CompileStatic +import groovy.transform.Field + +@Field static final String DRIVER_NAME = 'IKEA White Spectrum Light' +@Field static final String DRIVER_VERSION = '5.0.0' + +// Fields for capability.HealthCheck +import groovy.time.TimeCategory + +@Field static final Map HEALTH_CHECK = [ + 'schedule': '0 0 0/1 ? * * *', // Health will be checked using this cron schedule + 'thereshold': '3600' // When checking, mark the device as offline if no Zigbee message was received in the last 3600 seconds +] + +// Fields for capability.ZigbeeGroups +@Field static final Map GROUPS = [ + '9900':'Alfa', '9901':'Bravo', '9902':'Charlie', '9903':'Delta', '9904':'Echo', '9905':'Foxtrot', '9906':'Golf', '9907':'Hotel', '9908':'India', '9909':'Juliett', '990A':'Kilo', '990B':'Lima', '990C':'Mike', '990D':'November', '990E':'Oscar', '990F':'Papa', '9910':'Quebec', '9911':'Romeo', '9912':'Sierra', '9913':'Tango', '9914':'Uniform', '9915':'Victor', '9916':'Whiskey', '9917':'Xray', '9918':'Yankee', '9919':'Zulu' +] + +metadata { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_WS-Light.groovy') { + capability 'Configuration' + capability 'Refresh' + capability 'Actuator' + capability 'Switch' + capability 'ColorTemperature' + capability 'ColorMode' + capability 'ChangeLevel' + capability 'SwitchLevel' + capability 'HealthCheck' + capability 'PowerSource' + + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // Type LED2101G4: 1.1.003 (117C-2204-00011003) + + // Attributes for capability.HealthCheck + attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] + } + + // Commands for capability.Switch + command 'toggle' + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + + // Commands for capability.ColorTemperature + command 'startColorTemperatureChange', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + command 'stopColorTemperatureChange' + command 'shiftColorTemperature', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + + // Commands for capability.Brightness + command 'shiftLevel', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + + // Commands for capability.FirmwareUpdate + command 'updateFirmware' + + preferences { + input( + name: 'helpInfo', type: 'hidden', + title: ''' +
+ IKEA White Spectrum Light v5.0.0
+ +
+ ''' + ) + input( + name: 'logLevel', type: 'enum', + title: 'Log verbosity', + description: 'Select what type of messages appear in the "Logs" section.', + options: [ + '1': 'Debug - log everything', + '2': 'Info - log important events', + '3': 'Warning - log events that require attention', + '4': 'Error - log errors' + ], + defaultValue: '1', + required: true + ) + + // Inputs for capability.Switch + input( + name: 'powerOnBehavior', + type: 'enum', + title: 'Power On behaviour', + description: 'Select what happens after a power outage.', + options: [ + 'TURN_POWER_ON': 'Turn power On', + 'TURN_POWER_OFF': 'Turn power Off', + 'RESTORE_PREVIOUS_STATE': 'Restore previous state' + ], + defaultValue: 'RESTORE_PREVIOUS_STATE', + required: true + ) + + // Inputs for capability.ColorTemperature + input( + name: 'colorTemperatureStep', type: 'enum', + title: 'Color Temperature up/down shift', + description: 'Color Temperature +/- adjust for the shiftColorTemperature() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true + ) + input( + name: 'colorTemperatureChangeRate', type: 'enum', + title: 'Color Temperature change rate', + description: 'Color Temperature +/- adjust for the startColorTemperatureChange() command.', + options: [ + '10': '10% / sec - from hot to cold in 10 seconds', + '20': '20% / sec - from hot to cold in 5 seconds', + '33': '33% / sec - from hot to cold in 3 seconds', + '50': '50% / secs - from hot to cold in 2 seconds', + '100': '100% / sec - from hot to cold in 1 seconds', + ], + defaultValue: '20', + required: true + ) + + // Inputs for capability.Brightness + input( + name: 'levelStep', type: 'enum', + title: 'Brightness up/down shift', + description: 'Brightness +/- adjust for the shiftLevel() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true + ) + input( + name: 'levelChangeRate', type: 'enum', + title: 'Brightness change rate', + description: 'Brightness +/- adjust for the startLevelChange() command.', + options: [ + '10': '10% / sec - from 0% to 100% in 10 seconds', + '20': '20% / sec - from 0% to 100% in 5 seconds', + '33': '33% / sec - from 0% to 100% in 3 seconds', + '50': '50% / secs - from 0% to 100% in 2 seconds', + '100': '100% / sec - from 0% to 100% in 1 seconds', + ], + defaultValue: '20', + required: true + ) + input( + name: 'transitionTime', type: 'enum', + title: 'Brightness transition time', + description: 'Time taken to move to/from the target brightness when device is turned On/Off.', + options: [ + '0': 'Instant', + '5': '0.5 seconds', + '10': '1 second', + '15': '1.5 seconds', + '20': '2 seconds', + '30': '3 seconds', + '40': '4 seconds', + '50': '5 seconds', + '100': '10 seconds' + ], + defaultValue: '5', + required: true + ) + input( + name: 'turnOnBehavior', type: 'enum', + title: 'Turn On behavior', + description: 'Select what happens when the device is turned On.', + options: [ + 'RESTORE_PREVIOUS_LEVEL': 'Restore previous brightness', + 'FIXED_VALUE': 'Always start with the same fixed brightness' + ], + defaultValue: 'RESTORE_PREVIOUS_LEVEL', + required: true + ) + if (turnOnBehavior == 'FIXED_VALUE') { + input( + name: 'onLevelValue', + type: 'number', + title: 'Fixed brightness value', + description: 'Range 1..100', + defaultValue: 50, + range: '1..100', + required: true + ) + } + input( + name: 'prestaging', type: 'bool', + title: 'Pre-staging', + description: 'Set brightness level without turning On the device (for later use).', + defaultValue: false, + required: true + ) + + // Inputs for capability.ZigbeeBindings + input( + name: 'joinGroup', type: 'enum', + title: 'Join a Zigbee group', + description: 'Select a Zigbee group you want to join.', + options: ['0000':'❌ Leave all Zigbee groups', '----':'- - - -'] + GROUPS, + defaultValue: '----', + required: false + ) + } +} + +// =================================================================================================================== +// Implement default methods +// =================================================================================================================== + +// Called when the device is first added +void installed() { + log_warn 'Installing device ...' + log_warn '[IMPORTANT] For battery-powered devices, make sure that you keep your device as close as you can (less than 2inch / 5cm) to your Hubitat hub for at least 30 seconds. Otherwise the device will successfully pair but it won\'t work properly!' +} + +// Called when the "Save Preferences" button is clicked +List updated(boolean auto = false) { + log_info "Saving preferences${auto ? ' (auto)' : ''} ..." + List cmds = [] + + unschedule() + + if (logLevel == null) { + logLevel = '1' + device.updateSetting 'logLevel', [value:logLevel, type:'enum'] + } + if (logLevel == '1') runIn 1800, 'logsOff' + log_info "🛠️ logLevel = ${['1':'Debug', '2':'Info', '3':'Warning', '4':'Error'].get(logLevel)}" + + // Preferences for capability.Switch + if (powerOnBehavior == null) { + powerOnBehavior = 'RESTORE_PREVIOUS_STATE' + device.updateSetting 'powerOnBehavior', [value:powerOnBehavior, type:'enum'] + } + log_info "🛠️ powerOnBehavior = ${powerOnBehavior}" + cmds += zigbee.writeAttribute(0x0006, 0x4003, 0x30, powerOnBehavior == 'TURN_POWER_OFF' ? 0x00 : (powerOnBehavior == 'TURN_POWER_ON' ? 0x01 : 0xFF)) + + // Preferences for capability.ColorTemperature + if (colorTemperatureStep == null) { + colorTemperatureStep = '20' + device.updateSetting 'colorTemperatureStep', [value:colorTemperatureStep, type:'enum'] + } + log_info "🛠️ colorTemperatureStep = ${colorTemperatureStep}%" + + if (colorTemperatureChangeRate == null) { + colorTemperatureChangeRate = '20' + device.updateSetting 'colorTemperatureChangeRate', [value:colorTemperatureChangeRate, type:'enum'] + } + log_info "🛠️ colorTemperatureChangeRate = ${colorTemperatureChangeRate}% / second" + + // Regardless of prestaging, enable update of color temperature without the need for the device to be turned On + cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) + + // Preferences for capability.Brightness + if (levelStep == null) { + levelStep = '20' + device.updateSetting 'levelStep', [value:levelStep, type:'enum'] + } + log_info "🛠️ levelStep = ${levelStep}%" + + if (levelChangeRate == null) { + levelChangeRate = '20' + device.updateSetting 'levelChangeRate', [value:levelChangeRate, type:'enum'] + } + log_info "🛠️ levelChangeRate = ${levelChangeRate}% / second" + + if (turnOnBehavior == null) { + turnOnBehavior = 'RESTORE_PREVIOUS_LEVEL' + device.updateSetting 'turnOnBehavior', [value:turnOnBehavior, type:'enum'] + } + log_info "🛠️ turnOnBehavior = ${turnOnBehavior}" + if (turnOnBehavior == 'FIXED_VALUE') { + Integer onLevelValue = onLevelValue == null ? 50 : onLevelValue.intValue() + device.updateSetting 'onLevelValue', [value:onLevelValue, type:'number'] + log_info "🛠️ onLevelValue = ${onLevelValue}%" + Integer lvl = onLevelValue * 2.54 + utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) + } else { + log_debug 'Disabling OnLevel (0xFF)' + cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) + } + + if (transitionTime == null) { + transitionTime = '5' + device.updateSetting 'transitionTime', [value:transitionTime, type:'enum'] + } + log_info "🛠️ transitionTime = ${Integer.parseInt(transitionTime) / 10} second(s)" + cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(transitionTime)) + + if (prestaging == null) { + prestaging = false + device.updateSetting 'prestaging', [value:prestaging, type:'bool'] + } + log_info "🛠️ prestaging = ${prestaging}" + + // If prestaging is true, enable update of brightness without the need for the device to be turned On + cmds += zigbee.writeAttribute(0x0008, 0x000F, 0x18, prestaging ? 0x01 : 0x00) + + // Preferences for capability.HealthCheck + schedule HEALTH_CHECK.schedule, 'healthCheck' + + // Preferences for capability.ZigbeeGroups + if (joinGroup != null && joinGroup != '----') { + if (joinGroup == '0000') { + log_info '🛠️ Leaving all Zigbee groups' + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + } else { + String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') + log_info "🛠️ Joining group: ${joinGroup} (${groupName})" + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group + } + + device.updateSetting 'joinGroup', [value:'----', type:'enum'] + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + } + + if (auto) return cmds + utils_sendZigbeeCommands cmds + return [] +} + +// =================================================================================================================== +// Capabilities helpers +// =================================================================================================================== + +// Handler method for scheduled job to disable debug logging +void logsOff() { + log_info '⏲️ Automatically reverting log level to "Info"' + device.updateSetting 'logLevel', [value:'2', type:'enum'] +} + +// Helpers for capability.HealthCheck +void healthCheck() { + log_debug '⏲️ Automatically running health check' + String healthStatus = state.lastRx == 0 || state.lastRx == null ? 'unknown' : (now() - state.lastRx < Integer.parseInt(HEALTH_CHECK.thereshold) * 1000 ? 'online' : 'offline') + utils_sendEvent name:'healthStatus', value:healthStatus, type:'physical', descriptionText:"Health status is ${healthStatus}" +} + +// =================================================================================================================== +// Implement Capabilities +// =================================================================================================================== + +// capability.Configuration +// Note: This method is also called when the device is initially installed +void configure(boolean auto = false) { + log_warn "Configuring device${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Configure" button immediately after pushing any button on the device in order to first wake it up!' + } + + // Apply preferences first + List cmds = [] + cmds += updated true + + // Clear data (keep firmwareMT information though) + device.data*.key.each { if (it != 'firmwareMT') device.removeDataValue it } + + // Clear state + state.clear() + state.lastTx = 0 + state.lastRx = 0 + state.lastCx = DRIVER_VERSION + + // Configuration for capability.Switch + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes + + // Configuration for capability.ColorTemperature + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0000 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) + state.minMireds = 200 // Will be updated in refresh() + state.maxMireds = 600 // Will be updated in refresh() + + // Configuration for capability.Brightness + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) + + // Configuration for capability.HealthCheck + sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' + sendEvent name:'checkInterval', value:3600, unit:'second', descriptionText:'Health check interval is 3600 seconds' + + // Configuration for capability.PowerSource + sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + + // Query Basic cluster attributes + cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID + cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier + cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + utils_sendZigbeeCommands cmds + + log_info 'Configuration done; refreshing device current state in 7 seconds ...' + runIn 7, 'refresh', [data:true] +} +private void autoConfigure() { + log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" + configure true +} + +// capability.Refresh +void refresh(boolean auto = false) { + log_warn "Refreshing device state${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Refresh" button immediately after pushing any button on the device in order to first wake it up!' + } + + List cmds = [] + + // Refresh for capability.Switch + cmds += zigbee.readAttribute(0x0006, 0x0000) // OnOff + cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior + + // Refresh for capability.ColorTemperature + cmds += zigbee.readAttribute(0x0300, 0x0008) // ColorMode + cmds += zigbee.readAttribute(0x0300, 0x0007) // ColorTemperatureMireds + cmds += zigbee.readAttribute(0x0300, 0x400B) // ColorTemperaturePhysicalMinMireds + cmds += zigbee.readAttribute(0x0300, 0x400C) // ColorTemperaturePhysicalMaxMireds + + // Refresh for capability.Brightness + cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel + + // Refresh for capability.ZigbeeGroups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + utils_sendZigbeeCommands cmds +} + +// Implementation for capability.Switch +void on() { + log_debug 'Sending On command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114301}"]) +} +void off() { + log_debug 'Sending Off command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114300}"]) +} + +void toggle() { + log_debug 'Sending Toggle command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114302}"]) +} + +void onWithTimedOff(BigDecimal onTime = 1) { + Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) + log_debug 'Sending OnWithTimedOff command' + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) +} + +// Implementation for capability.ColorTemperature +void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, BigDecimal duration = 0) { + Integer mireds = Math.round(1000000 / colorTemperature) + mireds = mireds < state.minMireds ? state.minMireds : (mireds > state.maxMireds ? state.maxMireds : mireds) + Integer newColorTemperature = Math.round(1000000 / mireds) + log_debug "Setting color temperature to ${newColorTemperature}k (${mireds} mireds) during ${duration} seconds" + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) + if (level > 0 && duration == 0) setLevel level, duration +} +void startColorTemperatureChange(String direction) { + log_debug "Starting color temperature change ${direction}wards with a rate of ${colorTemperatureChangeRate}% / second" + Integer mode = direction == 'up' ? 0x03 : 0x01 + Integer changeRate = (state.maxMireds - state.minMireds) * Integer.parseInt(colorTemperatureChangeRate) / 100 + String payload = "${utils_payload mode, 2} ${utils_payload changeRate, 4} ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434B ${payload}}"]) +} +void stopColorTemperatureChange() { + log_debug 'Stopping color temperature change' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114347 00 00}"]) +} +void shiftColorTemperature(String direction) { + log_debug "Shifting color temperature ${direction} by ${colorTemperatureStep}%" + Integer mode = direction == 'up' ? 0x03 : 0x01 + Integer stepSize = (state.maxMireds - state.minMireds) * Integer.parseInt(colorTemperatureStep) / 100 + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 4} 0000 ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434C ${payload}}"]) +} + +// Implementation for capability.Brightness +void setLevel(BigDecimal level, BigDecimal duration = 0) { + Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) + log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" + Integer lvl = newLevel * 2.54 + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String command = prestaging == false ? '04' : '00' + String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) +} +void startLevelChange(String direction) { + log_debug "Starting brightness level change ${direction}wards with a rate of ${levelChangeRate}% / second" + Integer mode = direction == 'up' ? 0x00 : 0x01 + Integer rate = Integer.parseInt(levelChangeRate) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload rate, 2}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114301 ${payload}}"]) +} +void stopLevelChange() { + log_debug 'Stopping brightness level change' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114303}"]) +} +void shiftLevel(String direction) { + log_debug "Shifting brightness level ${direction} by ${levelStep}%" + Integer mode = direction == 'up' ? 0x00 : 0x01 + Integer stepSize = Integer.parseInt(levelStep) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 2} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) +} + +// Implementation for capability.HealthCheck +void ping() { + log_warn 'ping ...' + utils_sendZigbeeCommands(zigbee.readAttribute(0x0000, 0x0000)) + log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' + runIn 5, 'pingExecute' +} + +void pingExecute() { + if (state.lastRx == 0) { + log_info 'Did not sent any messages since it was last configured' + return + } + + Date now = new Date(Math.round(now() / 1000) * 1000) + Date lastRx = new Date(Math.round(state.lastRx / 1000) * 1000) + String lastRxAgo = TimeCategory.minus(now, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Sent last message at ${lastRx.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${lastRxAgo} ago)" + + Date thereshold = new Date(Math.round(state.lastRx / 1000 + Integer.parseInt(HEALTH_CHECK.thereshold)) * 1000) + String theresholdAgo = TimeCategory.minus(thereshold, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received for ${theresholdAgo} (hardcoded)" + + String offlineMarkAgo = TimeCategory.minus(thereshold, now).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received until ${thereshold.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${offlineMarkAgo} from now)" +} + +// Implementation for capability.FirmwareUpdate +void updateFirmware() { + log_info 'Looking for firmware updates ...' + if (device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Update Firmware" button immediately after pushing any button on the device in order to first wake it up!' + } + utils_sendZigbeeCommands zigbee.updateFirmware() +} + +// =================================================================================================================== +// Handle incoming Zigbee messages +// =================================================================================================================== + +void parse(String description) { + log_debug "description=[${description}]" + + // Auto-Configure device: configure() was not called for this driver version + if (state.lastCx != DRIVER_VERSION) { + state.lastCx = DRIVER_VERSION + runInMillis 1500, 'autoConfigure' + } + + // Extract msg + Map msg = [:] + if (description.startsWith('zone status')) msg += [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] + if (description.startsWith('enroll request')) msg += [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] + + msg += zigbee.parseDescriptionAsMap description + if (msg.containsKey('endpoint')) msg.endpointInt = Integer.parseInt(msg.endpoint, 16) + if (msg.containsKey('sourceEndpoint')) msg.endpointInt = Integer.parseInt(msg.sourceEndpoint, 16) + if (msg.containsKey('cluster')) msg.clusterInt = Integer.parseInt(msg.cluster, 16) + if (msg.containsKey('command')) msg.commandInt = Integer.parseInt(msg.command, 16) + log_debug "msg=[${msg}]" + + state.lastRx = now() + + // Parse for capability.HealthCheck + if (device.currentValue('healthStatus', true) != 'online') { + utils_sendEvent name:'healthStatus', value:'online', type:'digital', descriptionText:'Health status changed to online' + } + + // If we sent a Zigbee command in the last 3 seconds, we assume that this Zigbee event is a consequence of this driver doing something + // Therefore, we mark this event as "digital" + String type = state.containsKey('lastTx') && (now() - state.lastTx < 3000) ? 'digital' : 'physical' + + switch (msg) { + + // Events for capability.Switch + // =================================================================================================================== + + // Report/Read Attributes: OnOff + case { contains it, [clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String newState = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'switch', value:newState, descriptionText:"Was turned ${newState}", type:type + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "OnOff=${newState}" + return + + // Read Attributes Response: powerOnBehavior + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x4003] }: + String newValue = '' + switch (Integer.parseInt(msg.value, 16)) { + case 0x00: newValue = 'TURN_POWER_OFF'; break + case 0x01: newValue = 'TURN_POWER_ON'; break + case 0xFF: newValue = 'RESTORE_PREVIOUS_STATE'; break + default: log_warn "Received unexpected attribute value: PowerOnBehavior=${msg.value}"; return + } + powerOnBehavior = newValue + device.updateSetting 'powerOnBehavior', [value:newValue, type:'enum'] + utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" + return + + // Other events that we expect but are not usefull for capability.Switch behavior + case { contains it, [clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" + return + case { contains it, [clusterInt:0x0006, commandInt:0x04] }: // Write Attribute Response + case { contains it, [clusterInt:0x0006, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command + return + + // Events for capability.ColorTemperature + // =================================================================================================================== + + // Report/Read Attributes Reponse: ColorTemperatureMireds + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0007] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0007] }: + Integer mireds = Integer.parseInt "${msg.value}", 16 + Integer colorTemperature = Math.round(1000000 / mireds) + String colorName = convertTemperatureToGenericColorName colorTemperature + utils_sendEvent name:'colorTemperature', value:colorTemperature, descriptionText:"Color temperature is ${colorTemperature}K", type:'digital' + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${msg.value}(${mireds} mireds, ${colorTemperature}K, ${colorName})" + return + + // Report/Read Attributes Reponse: ColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: + String colorMode = msg.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorMode=${msg.value}" + return + + // Read Attributes Reponse: ColorTemperaturePhysicalMinMireds + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: + state.minMireds = Integer.parseInt "${msg.value}", 16 + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + return + + // Read Attributes Reponse: ColorTemperaturePhysicalMaxMireds + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400C] }: + state.maxMireds = Integer.parseInt "${msg.value}", 16 + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + return + + // Other events that we expect but are not usefull for capability.ColorTemperature behavior + case { contains it, [clusterInt:0x0300, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" + return + case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) + return + + // Events for capability.Brightness + // =================================================================================================================== + + // Report/Read Attributes Reponse: CurrentLevel + case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) + utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" + return + + // Other events that we expect but are not usefull for capability.Brightness behavior + case { contains it, [clusterInt:0x0008, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" + return + case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) + return + + // Events for capability.HealthCheck + // =================================================================================================================== + + case { contains it, [clusterInt:0x0000, attrInt:0x0000] }: + log_warn '... pong' + return + + // Configuration for capability.PowerSource + // =================================================================================================================== + + // Read Attributes Reponse: PowerSource + case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: + String powerSource = 'unknown' + + // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } + switch (msg.value) { + case '01': + case '02': + case '05': + case '06': + powerSource = 'mains' + break + case '03': + powerSource = 'battery' + break + case '04': + powerSource = 'dc' + break + } + utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" + utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" + return + + // Events for capability.ZigbeeGroups + // =================================================================================================================== + + // Get Group Membership Response Command + case { contains it, [clusterInt:0x0004, commandInt:0x02, direction:'01'] }: + Integer count = Integer.parseInt msg.data[1], 16 + Set groupNames = [] + for (int pos = 0; pos < count; pos++) { + String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" + String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + log_debug "Found group membership: ${groupName}" + groupNames.add groupName + } + state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + if (state.joinGrp.size() == 0) state.remove 'joinGrp' + log_info "Current group membership: ${groupNames ?: 'None'}" + return + + // Add Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // Leave Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Handle common messages (e.g.: received during pairing when we query the device for information) + // --------------------------------------------------------------------------------------------------------------- + + // Device_annce: Welcome back! let's sync state. + case { contains it, [endpointInt:0x00, clusterInt:0x0013, commandInt:0x00] }: + log_warn 'Rejoined the Zigbee mesh; refreshing device state in 3 seconds ...' + runIn 3, 'refresh' + return + + // Report/Read Attributes Response (Basic cluster) + case { contains it, [clusterInt:0x0000, commandInt:0x01] }: + case { contains it, [clusterInt:0x0000, commandInt:0x0A] }: + utils_zigbeeDataValue(msg.attrInt, msg.value) + msg.additionalAttrs?.each { utils_zigbeeDataValue(it.attrInt, it.value) } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "cluster=0x${msg.cluster}, attribute=0x${msg.attrId}, value=${msg.value}" + return + + // Mgmt_Leave_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8034, commandInt:0x00] }: + log_warn 'Device is leaving the Zigbee mesh. See you later, Aligator!' + return + + // Ignore the following Zigbee messages + case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) + case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response + case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command + utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + return + + case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8004, commandInt:0x00] }: // ZDP: Simple_Desc_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8005, commandInt:0x00] }: // ZDP: Active_EP_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x0006, commandInt:0x00] }: // ZDP: MatchDescriptorRequest + case { contains it, [endpointInt:0x00, clusterInt:0x801F, commandInt:0x00] }: // ZDP: Parent_annce_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8021, commandInt:0x00] }: // ZDP: Mgmt_Bind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8022, commandInt:0x00] }: // ZDP: Mgmt_Unbind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify + utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Unexpected Zigbee message + // --------------------------------------------------------------------------------------------------------------- + default: + log_error "Sent unexpected Zigbee message: description=${description}, msg=${msg}" + } +} + +// =================================================================================================================== +// Logging helpers (something like this should be part of the SDK and not implemented by each driver) +// =================================================================================================================== + +private void log_debug(String message) { + if (logLevel == '1') log.debug "${device.displayName} ${message.uncapitalize()}" +} +private void log_info(String message) { + if (logLevel <= '2') log.info "${device.displayName} ${message.uncapitalize()}" +} +private void log_warn(String message) { + if (logLevel <= '3') log.warn "${device.displayName} ${message.uncapitalize()}" +} +private void log_error(String message) { + log.error "${device.displayName} ${message.uncapitalize()}" +} + +// =================================================================================================================== +// Helper methods (keep them simple, keep them dumb) +// =================================================================================================================== + +private void utils_sendZigbeeCommands(List cmds) { + if (cmds.empty) return + List send = delayBetween(cmds.findAll { !it.startsWith('delay') }, 1000) + log_debug "◀ Sending Zigbee messages: ${send}" + state.lastTx = now() + sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) +} +private void utils_sendEvent(Map event) { + if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + log_info "${event.descriptionText} [${event.type}]" + } else { + log_debug "${event.descriptionText} [${event.type}]" + } + sendEvent event +} +private void utils_dataValue(String key, String value) { + if (value == null || value == '') return + log_debug "Update data value: ${key}=${value}" + updateDataValue key, value +} +private void utils_zigbeeDataValue(Integer attrInt, String value) { + switch (attrInt) { + case 0x0001: utils_dataValue 'application', value; return + case 0x0003: utils_dataValue 'hwVersion', value; return + case 0x0004: utils_dataValue 'manufacturer', value; return + case 0x000A: utils_dataValue 'type', "${value ? (value.split('') as List).collate(2).collect { "${Integer.parseInt(it.join(), 16) as char}" }.join() : ''}"; return + case 0x0005: utils_dataValue 'model', value; return + case 0x4000: utils_dataValue 'softwareBuild', value; return + } +} +private void utils_processedZclMessage(String type, String details) { + log_debug "▶ Processed ZCL message: type=${type}, ${details}" +} +private void utils_processedZdpMessage(String type, String details) { + log_debug "▶ Processed ZDO message: type=${type}, ${details}" +} +private String utils_payload(String value) { + return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') +} +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} + +// switch/case syntactic sugar +@CompileStatic private boolean contains(Map msg, Map spec) { + return msg.keySet().containsAll(spec.keySet()) && spec.every { it.value == msg[it.key] } +} diff --git a/ikea-zigbee-drivers/Legrand_741811.groovy b/ikea-zigbee-drivers/Legrand_741811.groovy index 4d74130..d04e280 100644 --- a/ikea-zigbee-drivers/Legrand_741811.groovy +++ b/ikea-zigbee-drivers/Legrand_741811.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'Legrand Connected Outlet (741811)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -32,8 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - // For firmware: 003e (1021-0011-003E4203) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // For firmware: 003e (1021-0011-003E4203) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -41,7 +40,7 @@ metadata { // Commands for capability.Switch command 'toggle' - command 'onWithTimedOff', [[name:'On time*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] // Commands for capability.FirmwareUpdate command 'updateFirmware' @@ -51,7 +50,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- Legrand Connected Outlet (741811) v4.1.0
+ Legrand Connected Outlet (741811) v5.0.0
device details
community page
@@ -302,8 +301,8 @@ void toggle() { void onWithTimedOff(BigDecimal onTime = 1) { Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) log_debug 'Sending OnWithTimedOff command' - - String payload = "00 ${zigbee.swapOctets(zigbee.convertToHexString(delay * 10, 4))} 0000" + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) } @@ -636,6 +635,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Makefile b/ikea-zigbee-drivers/Makefile index 4027671..0f31057 100644 --- a/ikea-zigbee-drivers/Makefile +++ b/ikea-zigbee-drivers/Makefile @@ -18,7 +18,7 @@ $(info ------------------------------------) .PHONY: all clean -all: Aqara_DCM-K01.groovy Ikea_E1603.groovy Ikea_E1743.groovy Ikea_E1745.groovy Ikea_E1766.groovy Ikea_E1810.groovy Ikea_E1812.groovy Ikea_E1836.groovy Ikea_E2002.groovy Ikea_E2006.groovy Ikea_E2013.groovy Ikea_E2112.groovy Ikea_E2123.groovy Ikea_E2134.groovy Ikea_E2201.groovy Ikea_E2202.groovy Ikea_E2204.groovy Ikea_E2213.groovy Ikea_ICPSHC24.groovy Legrand_741811.groovy Philips_RDM001.groovy Philips_RWL022.groovy Swann_SWO-KEF1PA.groovy +all: Aqara_DCM-K01.groovy Ikea_DIM-Lights.groovy Ikea_E1603.groovy Ikea_E1743.groovy Ikea_E1745.groovy Ikea_E1766.groovy Ikea_E1810.groovy Ikea_E1812.groovy Ikea_E1836.groovy Ikea_E2002.groovy Ikea_E2006.groovy Ikea_E2013.groovy Ikea_E2112.groovy Ikea_E2123.groovy Ikea_E2134.groovy Ikea_E2201.groovy Ikea_E2202.groovy Ikea_E2204.groovy Ikea_E2213.groovy Ikea_WS-Lights.groovy Legrand_741811.groovy Philips_RDM001.groovy Philips_RWL022.groovy Swann_SWO-KEF1PA.groovy %.groovy: src/devices/%/config.yaml src/common.yaml src/blueprint.groovy tools/yaml-merge$(POSTFIX) src/common.yaml $< | tools/mustache$(POSTFIX) src/blueprint.groovy > $@ diff --git a/ikea-zigbee-drivers/Philips_RDM001.groovy b/ikea-zigbee-drivers/Philips_RDM001.groovy index a6bb4f4..8ec149f 100644 --- a/ikea-zigbee-drivers/Philips_RDM001.groovy +++ b/ikea-zigbee-drivers/Philips_RDM001.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'Philips Hue Wall Switch Module (RDM001)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for devices.Philips_RDM001 @Field static final Map RDM001_SWITCH_STYLE = [ @@ -47,8 +47,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 1.0.5 (100B-011C-0000041A) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' // For firmware: 1.0.5 (100B-011C-0000041A) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -62,7 +61,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- Philips Hue Wall Switch Module (RDM001) v4.1.0
+ Philips Hue Wall Switch Module (RDM001) v5.0.0
device details
community page
@@ -722,6 +721,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/Philips_RWL022.groovy b/ikea-zigbee-drivers/Philips_RWL022.groovy index a7460e3..2f26456 100644 --- a/ikea-zigbee-drivers/Philips_RWL022.groovy +++ b/ikea-zigbee-drivers/Philips_RWL022.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'Philips Hue Dimmer Switch (RWL022)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -41,8 +41,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - // For firmware: 2.45.2_hF4400CA (100B-0119-02002D02) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' // For firmware: 2.45.2_hF4400CA (100B-0119-02002D02) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -56,7 +55,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- Philips Hue Dimmer Switch (RWL022) v4.1.0
+ Philips Hue Dimmer Switch (RWL022) v5.0.0
device details
community page
@@ -664,6 +663,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index cea0617..2ae9220 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -3,27 +3,34 @@ This document provides instructions on how to install and use custom drivers for These drivers enable advanced features and functionalities for the following devices: -* [Askvader On/Off Switch (E1836)](#askvader-onoff-switch-e1836) -* [Badring Water Leakage Sensor (E2202)](#badring-water-leakage-sensor-e2202) -* [LED Driver (ICPSHC24)](#led-driver-icpshc24) -* [Parasoll Door/Window Sensor (E2013)](#parasoll-doorwindow-sensor-e2013) +Remotes: * [Rodret Dimmer (E2201)](#rodret-dimmer-e2201) * [Somrig Shortcut Button (E2213)](#somrig-shortcut-button-e2213) -* [Starkvind Air Purifier (E2006)](#starkvind-air-purifier-e2006) * [Styrbar Remote Control N2 (E2002)](#styrbar-remote-control-n2-e2002) * [Symfonisk Sound Remote Gen2 (E2123)](#symfonisk-sound-remote-gen2-e2123) -* [Tradfri Control Outlet (E1603, E1706)](#tradfri-control-outlet-e1603-e1706) -* [Tradfri Motion Sensor (E1745)](#tradfri-motion-sensor-e1745) * [Tradfri On/Off Switch (E1743)](#tradfri-onoff-switch-e1743) * [Tradfri Open/Close Remote (E1766)](#tradfri-openclose-remote-e1766) * [Tradfri Remote Control (E1810)](#tradfri-remote-control-e1810) * [Tradfri Shortcut Button (E1812)](#tradfri-shortcut-button-e1812) -* [Tretakt Smart Plug (E2204)](#tretakt-smart-plug-e2204) + +Sensors: +* [Badring Water Leakage Sensor (E2202)](#badring-water-leakage-sensor-e2202) +* [Parasoll Door/Window Sensor (E2013)](#parasoll-doorwindow-sensor-e2013) +* [Tradfri Motion Sensor (E1745)](#tradfri-motion-sensor-e1745) * [Vallhorn Motion Sensor (E2134)](#vallhorn-motion-sensor-e2134) * [Vindstyrka Air Quality Sensor (E2112)](#vindstyrka-air-quality-sensor-e2112) -Advanced features: -* [Zigbee Bindings](Zigbee_Bindings.md) +Outlets: +* [Askvader On/Off Switch (E1836)](#askvader-onoff-switch-e1836) +* [Tradfri Control Outlet (E1603, E1706)](#tradfri-control-outlet-e1603-e1706) +* [Tretakt Smart Plug (E2204)](#tretakt-smart-plug-e2204) + +Lights: +* [Dimmable Light](#dimmable-light) +* [White Spectrum Light](#white-spectrum-light) + +Other: +* [Starkvind Air Purifier (E2006)](#starkvind-air-purifier-e2006) Devices from other vendors (not in HPM): * [Aqara Dual Relay Module T2 (DCM-K01)](#aqara-dual-relay-module-t2-dcm-k01) @@ -32,6 +39,10 @@ Devices from other vendors (not in HPM): * [Philips Hue Dimmer Switch (RWL022)](#philips-hue-dimmer-switch-rwl022) * [Swann One Key Fob (SWO-KEF1PA)](#swann-one-key-fob-swo-kef1pa) +Advanced features: +* [Zigbee Bindings](Zigbee_Bindings.md) + + ## Installation There are two ways to install the drivers: using Hubitat Package Manager (HPM) or manually importing the driver code. @@ -112,42 +123,6 @@ Below you can find the details of each device, including the features and pairin 1. Close the device battery compartiment. 1. You're all set! Enjoy using your Badring Water Leakage Sensor. -### LED Driver (ICPSHC24) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `603.426.56`|`503.561.87`|`104.747.72` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_ICPSHC24.groovy` | -| Tested firmwares | 10EU-IL-1: `1.2.245` (10W) | -|| 30EU-IL-2: `1.0.002` (30W) | -|| 30-IL44-1: `1.0.021` (Silverglans, 30W) | -| Since version | `3.1.0` | - -#### Features -* Commands: On, Off, Toggle, On with Timed Off -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Brightness control: Set brightness level, Start/Stop level change, Level up/down -* Configure brightness level when turned on (Always the same value, Restore last level) -* Can set the brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. -* Health status: online / offline -* Refresh switch state on demand -* Can be member of Zigbee groups - -#### Known Issues -* Smaller (10W) drivers do not honor the Power On Behavior. - -#### Pairing Instructions -1. Have a light attached to the LED Driver device. -1. Find the small reset hole on the device and make sure you have at hand a pin that can fit the reset hole (e.g.: a paper clip or SIM card eject pin). -1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. -1. Plug the device back into an outlet. -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Insert the pin into the reset hole and press it for at least 5 seconds; the attached light will blink then stay on. -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. You're all set! Enjoy using your LED Driver. - - ### Parasoll Door/Window Sensor (E2013) | Parameter | Details | @@ -589,6 +564,61 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your Vindstyrka Air Quality Sensor. +### Dimmable Light + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_DIM-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* Commands: On, Off, Toggle, On with Timed Off +* Brightness control: Set brightness level, Start/Stop brightness level change, Step brightness level up/down +* Configure what happens after a power outage (Power On, Power Off, Restore previous state) +* Configure brightness level when turned on (Always the same fixed value, Restore last level) +* Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. +* Health status: online / offline +* Refresh switch state on demand +* Can be member of Zigbee groups + +#### Tested devices +* 10EU-IL-1: Tradfri LED Driver 10W +* 30EU-IL-2: Tradfri LED Driver 30W +* 30-IL44-1: Silverglans LED Driver 30W + +### White Spectrum Light + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_WS-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* Commands: On, Off, Toggle, On with Timed Off +* Brightness control: Set brightness level, Start/Stop brightness level change, Step brightness level up/down +* Color temperature (CT) control: Set color temperature, Start/Stop color temperature change, Step color temperature up/down +* Configure what happens after a power outage (Power On, Power Off, Restore previous state) +* Configure brightness level when turned on (Always the same fixed value, Restore last level) +* Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. +* Pre-staging: Set color temperature when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color temperature. +* Health status: online / offline +* Refresh switch state on demand +* Can be member of Zigbee groups + +#### Tested devices +* LED2101G4: Tradfri Bulb E14 WS Globe 470lm +* LED1949C5: Tradfri Bulb E14 WS Candle Opal 470lm + +#### Pairing Instructions +1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Put the light in pairing mode. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your White Spectrum Light. + + ### Aqara Dual Relay Module T2 (DCM-K01) | Parameter | Details | diff --git a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy index 5b08e20..59ec256 100644 --- a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy +++ b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy @@ -7,7 +7,7 @@ import groovy.transform.CompileStatic import groovy.transform.Field @Field static final String DRIVER_NAME = 'Swann One Key Fob (SWO-KEF1PA)' -@Field static final String DRIVER_VERSION = '4.1.0' +@Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.IAS import hubitat.zigbee.clusters.iaszone.ZoneStatus @@ -36,8 +36,7 @@ metadata { capability 'HealthCheck' capability 'PushableButton' - // For firmware: TBD - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0001,0500,0000', outClusters:'0003,0501', model:'SWO-KEF1PA', manufacturer:'SwannONe' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0001,0500,0000', outClusters:'0003,0501', model:'SWO-KEF1PA', manufacturer:'SwannONe' // For firmware: TBD // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -54,7 +53,7 @@ metadata { name: 'helpInfo', type: 'hidden', title: '''
- Swann One Key Fob (SWO-KEF1PA) v4.1.0
+ Swann One Key Fob (SWO-KEF1PA) v5.0.0
device details
community page
@@ -525,6 +524,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/img/Ikea_CWS-Light.webp b/ikea-zigbee-drivers/img/Ikea_CWS-Light.webp new file mode 100644 index 0000000000000000000000000000000000000000..c3f554dc940a967329e7c5502d0097127cdc4865 GIT binary patch literal 8218 zcmV+#Am!guNk&EzAOHYYMM6+kP&il$0000G0002<0RZy>06|PpNFf9O009p${}B;_ zj%#a~BncX|Ffq$yDa_7bXTu_90hq0^Fi{Kf8JU%Q-}{KjoF5~i{}W*TPxLRM{8P^# z^z=(hSwY>~B_sBsNVoXq-gP<0R z2dcyxfjSQXEhZ0Bh&jSEt_(A{$e|Fs2dR7sr1KHT6mS?M7$B-dg6I+iF+?1~67c|q zL?b|x$pBr3tlSiGawHtYic}P9Qdz7^k(HWaPKv~XP?3v5RW1p2IT9KGR%QWs83GI< z1tE$wge=k#NMt}*i3Q;$2r>v1j3`hsl0d`A0t16A-vn`d3&isgXkaTSk*%U6wuVA% z<0>52x{AP52sSd+%g9XqGRX4G>+n2FSs~z{EwNN>4Jos9Bw`s*mSaM>0}-^>78#1R zij^1|Rz6S1B8CBG_a>OzTVVb;1Rk~uTHLB==`;;3Z*?>}&49Bz6VB}{IDZ&|4`(TO zaiij;voyTC(edakLxFv5DsV?x3j9$>VK_!Bx2Pkz$%(C23;vKMEx*-zh8 zew}ROB9pBKt744Mk3y{q%K3=5Z*p4_%S@ z_%uEFN!LS9nx6atsCv=|py-KDs-E}@CTVH z+(Cv4dyuZe9HeQ;FI5fcMidS4C8Hr6WhwASnF`!%LxDX?S746P@bX5*OJ^x~al>3r z*jaF1o7CWzzfAVyghsM-of6fhV;VnR5H0bwONgpq6rS)?FD5rYt9 z7J!!-09K|07}+Y9go<1gGIBv`iaDt+W~7@`7AsOw%t!^fDdgn3kdbdP8KB5S0JB6q z!Vqx?UBn<(i3DLJ0>%_@7+t_%R=xyc7F$4>Y!;<)WrJF{B8-_#9;Y)I+` z8NGiK{rSWEpC|xUP&gpU8vpr?43{ST-I_YR`}Ko2gjWMvP;X#PDf=UM;4dHC#C zfEVMR)PK}(Hh>SSAN0LmW|j5d=KX*;8|Vl6H_E_cF7n)+-94(-5RHUZ zcH5GTN@xHXTenIA=k|?%&-u=Gr}>mr>6h$hP~}e7^2{0Zru5uD+Ah#2+V!x2U70DG?X_A73Mg(NdldZL^E-ej`7&G$(F8N%)$M3sTqVaCDvp}rp9)9x z+P^$*_n|hkAWjxjo>tA_(AoCer0nBgR|PIOGrxBZTXum{u|E0H{bCOmn1e+c`!wmJ zhYtKEufz+F7tGS53k6#9w|9SKgE%#X#?}yxtXw)Nf6BIfCy`<>x?z1KP)zP;gS3fN z78mRHL+wm$wIN!IiXPQ#2u8k6JM^z8ofl!0rAn^CL|v{bVcwH!AGn_3$8y-!44J}{ z4})+h=UPkElV|sX{3U2bU#)7G4eyJpiu7fH$E)2Z-#Smd2|gEV#p&X3J-R2VWD3R} z;dw>wAu*UHiJ^oE2MHMzC-0pn-#Kab$fUAXW|5Urc9hwLZo4KN%cz+U#RSFpvQ~Jj z6mSaUFq03323)I5CQqmA{C#&MD=l3#Ox(CEX;kGZG$ZVgjsi12!rJ-j$vHVs#eMN zE{+znhLgGqf+5VbeX9hQ@?|@Yik=NQJnj(4H@aR4?!~Dj?yo{-n2;T^D=6p%_C_@9 z4@Ke>ro%j~80MXn&fV^M~EG1OteK`wl8Wn00_rivCLan+J`|XIDKPj_Veq8G;Yo+WlMjU^ z9|}x96qtM|F!(e80RHJ{0000000A)MGL9fH&pJgwEX#n^8uAU)Sr)|z6 zEC!*G_rltpXHX(HHQRmd$vXSaCa2v4N83PuwMR2N57X%4xv>aFMasS{EV-p17rZ%x zf{daG;%d|lcIMSV`llAFtI5G~X%Plxvd;3(ndTp7ptt}C6jAad`TM0}0M8>5-(eUx zCS2NWSp-sWKzi0BXP)OD?(7E%tM@*pi_tnuvRhLcB5g-H?N+m!iFQ##o?^%!$!%b?+W#`qK$L6j;@Nu$t_`lpqTB z0^FXsb-SN(v~u^?0m1}BfT=46bqYH{x?#-?Bl8OLJk$Y0PWWT70HqY1sGHXX-kNQU zr->!RcB9SqR{f+CsaeV1@1P+L2jg2f4eBj~Z_wLA48KU6ibZ zr%)w2iOs#@Y2BWm*UI8Xg}mbi=-pdzEGBjq83upP{&H^6z=&`nl1FRFZM)D+B{(!F zyH$Y}UQMqG*?_*|1RN}egTlL|nixh!fQ4{3f=*Y6W~e`Z6Envr-uNjIstib>>OR?p zw5VJfsa*7OoSO^t)4mlcaj)vP5yQ5_hN(s{{JrWLMdw^cb6inoA zofX4U;bw9bl9zgW;%;9euF4kMuM)Ovex>gKOLEki4i9?} zG49ujuEDkZtxl~5^F2JRGUkV)!rh*KHOi0D4#dBCuI?G%p!Rd_Q6RIB6p4gv6$)A$ z&i@vP?Ksg;&OT(|5($PlV#5xzW|#9u7KNb!Ai8w=X8O&|>4EaRMNi2TZ7Z050NgU* zO6d2Gb~l6d1Vp9}KvTA1@=Z1BBYP$nM&tEMa5}|3pEh|`N{>?jKJ5=-%%{Bmp-Y0yGQ&D-`SUX+fSBNq_#`%+;q=9@pS5=ERj7P&2Z#j>aRib` zD}3=qFP>d)?D}yMaP$2_8z(T#izjg|g~IoNvbM?uT&?^#??%6Af70D_f(UpF(q_hb zcn$RkBCI;1GMP`Rl&p_XNJ04d&6a)as3n!yf%@@3;1mrtDWsJ=g(AHuDS6EEBvhfd z(pZ?1C7N%RMef8Bz7T^~N6^UY#@1j1xpb8Yx=$RazCVq+vppgHgcCk9UnU8W%`(Cs z$UF=t^1SW{a8}}Zj~h+uC-)}T1QxF*=Gr9o`LfYRU>hgb?=hVQ&Nr+t+&bVK8-4<1?l@EVH)^OSk`jXAOw)Nw{kP=(}C%bd9+*dp%ksa^m@B4Wm z`IGB@gf{0fhNU+Fp+-?W5G<1$UblsOurY&+ydZ>jqOKzUFAHJ1 z0(_5^UsDrV$)iq0e1lzvGmHZBt%ojR+Ao{A;3W%;egnHl{)hG~n~}iC)WjH6nBoc& zCfcA!q6XygoGIKAGOcUg3yFF1nthkhi*qUHY)m59H)6o&Y`g=f?9PXQB!7(XK4!4X zw4x+t>hFoTU)ZO4CE(cCkiKF_|E0X0Z|ngK(kw4%U2qE_b~V_?tfe>&^4v&~AK&Fc zDexB}9_M?jzyT&E7eq-Au0JLk@8JTtq3vz_YFHOval`!X_y>$we{+Cj;uXlvBuQGl z7N5h;1j+UE(S1&WX97Y&#Z43tD~**rV_Ckz>bi`}emk%WR2TH&E{oZs-*&gxhF79x zOS5)WkTRIWLU?ZP7Mbv+pcN_?NiP=fk(}5JE4R=IbVWrJ?328nFVm5BF1_3k2GlVa zPLMUh76r3jhvs8dI#V!rX!sOcvK_rJTWb=z{cFc0LdJmx!QL~PxPsvGcovplk$5ic zYw+>dioUn~$3*@;CORya=O*jXE#edA`6;WKp&}E$dO$x59}i*@8_V^khP*mgZajl< z%Ev=<8~O;$rll#NL4z@2iU#&?ZmN^)q7*~_2g3Nfp0xC+1SRGw+Py4JDkzGOy92Vz zt9F{+b|ZK7Bq;ugyxRm;AVt47DK`%C5khjL0R;&7?oxqfG_j z$(C0*iAOMT;bo?`wbJ&b{Yfpj3!%r0f7T=kMEZ2iv zFRzSmhzk5d1^|`=7d>8-ebD3Nyt_iPY>8`4ga|#*Bn*h|ZUB2~{w!PdN8vq?pInxF zUo60PJBeqPvPpUqx_YIPNm2VvzSD!g?Bh^6QtdSbB{7M;`DDRHE}%%WeKQpMG6jXk zR!!cYWX3?oAOS|%>66yY$pVW1?8uOtxy6ct_KC$ox5lH-#_8>{BL(>!=Y zaTVaSWJg3KP=n3j50aKZs7~gMo^JEbg)8#LkQO%QZ zV_*t@7D#tO4w`@f5T|p`T%ZkGrA>%=uQ+7U2Pwcr+>MAID7ai>PrBKhb+FHlQv^|4 zPA$v#%(ukymJB*S4f%}dm^pFePD2l_Eq|G0V>n9m5`dLS zdb0~=Pdj5cmskKp@if*~;nheV1E$*Te~Est1N&HF7rW6w3*)-ZU?GbvHjxz*8eIPh z$H_QU#uT*E~e&Ci|{WuCi^R?Hq%}^efV02;j!e&s&PLTZDJvioE~&7 zk7jdYmiAvHp`&sD3ViK0>lZWN&)nls;*iRRd1}S?rP*Uk&oZH%)*e7dT9O6=c28W` zfFlx`w%e;?4O;KO7ut1}yca+6R#FG=$Li>9&taI8s z)hLCuQ%Vc+ujjf%zuDX2pD9(A-#SRcU5drSqt_uZJ2hdEvJ|=ISd{hFuiX%~_ny+T z4Y=TTMpbio1CNlsB(l=7`tq0qUJ{Ye$2J&@ii#bT91;o`77MDd~5wg%9L_`xF_qjE%I_(v25{CiZF&z;2OIFU&DV)pjS^<3vKHxysjCrt&`TEm8DJr_E`bRX3k#928T*hJF znQwfbQYXLMDikcr);TG-7v z-<{FaQbg~-Py3QMy%xw!bxM3X0Bs^7e5Vi^$+E<{dV6~$oWy1f`x0-u@ZAsVN(5>d zmP{>lVSUZG4uprm z2K?-)f-iC!;+nQ?hYMK|!mej*N;T4k2}X*{Hd5LTeZ<VH{yim4~Z2gZvydQos~EeA|zveonr!TjzCiP9+U+FBK~y zATnJZ=LfQvi8h>vzq0rHHS=dhv@b{uqrv1wAz#h<&azjbs+L!=X}V!u@(kW+1_Rbw zEs>3sbJdkw%K(jzrwF2Gg59fhwdoP1;cEyp39a=HbXI0vZTj(MXWJeq#L=<+QGMYN zg6QD=|AxD~?8@~)|GEj)&ai7+tF**N^K=cueAD|{?5z%Y_p*oP+1a_j|n|tLlkErXp6HP}@d~J|C zmFc=fSI7eDp0&BT{GGzjMCWSBa#sG8{T(kY4+Jj1xD#!qv8fIX8+|_>C9AqU(XwO* zs#wAWyC;RUw{S_DX7@H#nYkoO)K#Ul8!X_x<`8^-C5oe!0ROidb2|Awb?cx?7)A(N zG{UC${}>jit^LR)d;l@!hE_VTsne2*Z~X?_k!)!Ku}xBYiC3=ISWp`h-b`_*lBUZJ z>X4CH(xf*H0Kly7)A_BNa25S<^$q)9@y8VX&t87IG-Z8&2IMp*uPxAf*i<- zqrZWY_~-RwT*=UcV*xc4R0d`PM;u>}Q#*?7}X$dB9J@*e>Iw7b3d z;43W=i!lkuP8Vc4I`9=ExnRvPjqD}L>EsL4|3DrWYa#uYcp1)x*O2T02ocp{u5NT_ zrO{KoiE1PhwIt^I?H0C2nk7z_XEX4xSuX-gefI7IhPiMsbkI|I^qPk(| z*&tv0BrG=NXHulM)47Z=SwPJ4kA%O*AN__$6;a{8PFZ2~2Xzqu$NxUkJ{dOa!v#&{ z>Xg%!0+vzs7@RnY#Iw)|MK5}{V7J`2#p*Zl&laWBo6~|jG}A7IJ6+Km!xW2jy9eVS zuM{Boz610u+qXoLFE}O>zLMPzs84#5E!)9U%|=b(Y*d5OwJj?Wb4GMdjo(!_|B$s+ zo0L=5c)N;7JuKp(s6%)xwQ!{z03_g{8yFn$Ik>FR?KVkz^+GrE0Xd;kAA6_*4J~Y} z_yqcHKu!04fD25&Wyq*^9vmC%%f7nOzO#K=N#b zCfy~*<-!X~#utr3jT9A)Ch(inNO4`DI?N8xEL@hyWWeTwEcHaAybM>n@z2Ns)Az4| zRpY_VVNIpZ15$ijpAG@er2^hFA*}3|)Z!-3#|?0q&HsRT1Ga6#l+OTfsA|WL-1)BB{3uDa# z9_o1+Ra*lTLK75a`WGmW3#KrhT~p~K@vlKS?c)9fP_2bK3sYrIy|(u~*Y;8}-_2*BsedvIy#{n(`H>ZQil2<%q>G$uux)r-`-*B9QFD#(PS5+xcY!t8 zXOUVg;l}aRonR%|*TMgCgT93?JmSlN*ik=8k=7VOSg3HD>ICQ{?f!*P)l$ctovdEj z?QPf+*`vHTFbdOjX@5!0X*@wdE!Th202Sxup0I;FvlE4AZ^d}CG&Tbei%XE*RFA(; z(Awj2oQU`!kg4f`QE6i)$`zycZ86Ej_4%8NaCLOhbE3CUpXDG!2%5}Qm5sq;!Y0|_y@Vl1#^9aSbCkN|>1k$rvXXMfOb>r>DhqJXxqTtPev2){zpcp7_{af!Jv8MUD3C&GL4xXlu*3M z17I{kv=4$%wp*6(*hV#0>HaW^_3fmuPu8_fA$8Z>@`i-x9uBnOO58aLGj>3QQQ@1t zAT3&eJmk7eTJZq{y3pFU9`%yM%2DMHYfY+HS#6K&Kpew8%KJ+nbZ_T#!d-GOfz^2D z-~q=Y!%77ZCTU?!e{;o?=8dZDq!%BT2}QJ6#jP=)#AZYRV<`-b2nIs}XiHtjX@&>w z#9uAgxF+5!rY%8K4rDeB?^5f4drIf|QxWX3wgG?_itSTBS#>XkTMv>U(^Btvbg;32 M00000000000N-`NNB{r; literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/img/Ikea_DIM-Light.webp b/ikea-zigbee-drivers/img/Ikea_DIM-Light.webp new file mode 100644 index 0000000000000000000000000000000000000000..c775c89feccb44b6eb8afd25a82e33efdcac3bb4 GIT binary patch literal 7372 zcmV;-95drmNk&G*8~^}UMM6+kP&il$0000G0002<0RZy>06|PpNFf9O009p${}B;_ zj%#a~BncX|Ffq$yDa_7bXTu_90hq0^Fi{Kf8JU%Q-}{KjoF5~i{}W*TPxLRM{8P^# z^z=(hSwY>~B_sBsNVoXq-gP<0R z2dcyxfjSQXEhZ0Bh&jSEt_(A{$e|Fs2dR7sr1KHT6mS?M7$B-dg6I+iF+?1~67c|q zL?b|x$pBr3tlSiGawHtYic}P9Qdz7^k(HWaPKv~XP?3v5RW1p2IT9KGR%QWs83GI< z1tE$wge=k#NMt}*i3Q;$2r>v1j3`hsl0d`A0t16A-vn`d3&isgXkaTSk*%U6wuVA% z<0>52x{AP52sSd+%g9XqGRX4G>+n2FSs~z{EwNN>4Jos9Bw`s*mSaM>0}-^>78#1R zij^1|Rz6S1B8CBG_a>OzTVVb;1Rk~uTHLB==`;;3Z*?>}&49Bz6VB}{IDZ&|4`(TO zaiij;voyTC(edakLxFv5DsV?x3j9$>VK_!Bx2Pkz$%(C23;vKMEx*-zh8 zew}ROB9pBKt744Mk3y{q%K3=5Z*p4_%S@ z_%uEFN!LS9nx6atsCv=|py-KDs-E}@CTVH z+(Cv4dyuZe9HeQ;FI5fcMidS4C8Hr6WhwASnF`!%LxDX?S746P@bX5*OJ^x~al>3r z*jaF1o7CWzzfAVyghsM-of6fhV;VnR5H0bwONgpq6rS)?FD5rYt9 z7J!!-09K|07}+Y9go<1gGIBv`iaDt+W~7@`7AsOw%t!^fDdgn3kdbdP8KB5S0JB6q z!Vqx?UBn<(i3DLJ0>%_@7+t_%R=xyc7F$4>Y!;<)WrJF{B8-_#9;Y)I+` z8NGiK{rSWEpC|xUP&gob7ytmUtN@(>D)a&L0X~sPp-UyCqoJgB$pEkt31@EnM)=*+ z12w$w{?~k#!u22AxuH3$rJiM!Y3v`>nb!0j;`h?e%!i!^roZuCpdZuvn>`NtU@{f` z1O9i_XNSL}{^9E1R!?L3V7*7+tnGFuz$5Y>>fh_Psz3+T|3Gi3^KKN)@jgZ!Ol&QmgcW27ZReR))0-XB{2EXv2WqBgpx4ZkE*tkdmbWoAwyR7 z!X9RZnLu||M;zM15w(M{0kA=bNiS2b`_6CQHpJlyL6yuyDn60mnw<22pbNZtkyoC# z?ll5D^R-vDFulUHJws3fTxsg-8gv($da$+DoYU<^04=^4wyOUASxWuL;UJ9=x*i&u ziyr8bRg;-yy8{}?9>inYr)_r+W|jimI2DrKZ^4C;IGa$R4kQCPwjL@S;bMnV)m$&3 zvW>4?4%rU7V49C+(E*FHmPFs$eK2fS81h5R0R_X{oJ9Bww4;qciEEWceZeyVcC^RY zPd}%(x2_R!6ZDX7eMzEj|fG&sr_56 zqQL})ZLroS5j!3s7*8ya;LKK14}Gz%Ad0-yooKuT$#{a^PtazkWD>E>Oe6U3-@TC_9SDs z%q?8nA^c@zQkH8fo;_@3Y>Sqd-X98s1)N~v-Aeaa=_|I0(`!`*gE@F%2Tt~=*-_?6 zm0&6jK%!PD$^FBz;ugs^4)Hr3(F`^>NQD1YUcJy_dLu_8l?|vxKq1SyP8IQQD87=F zM0EN?+o5;^PX`SfEI_VZZ~!63W-kRO;QG+B56-zP({xjxKJ8mkpZsL z-8&udsq{eFZ{yie_2YGeMssDW4A5;mE?Bzo+QHXlqYs-+C`5@$CCErsunXtu$J@9l zlJF;iQ^2#hpltyiJ!t)jwS-Y9*8rgmow`t=yRj>GXUUu@4CWI|r%)1_VjF1nEM0_D z&cCs+$gA@mNRlyQ4~v@=ta|Z7x$<#&&}YJ-5R*$#R`3^q$w+iVX(%onHqsP#U~mvW zon#96tH?8JWJ>9nsgzr_&-%(|I}p_=e3!N=#lt=o27DbO97uhKtbjV_*5D2s59YEXTqS*g+gbiL7xhPJ{1OhDh&8k8Stnx z;ZSG6umJw~hoAre0009jA`4q{8CHzO-R`?CL8s`9)n=&O)12BDJWu}NR3;-58yza= zv8mjczZxaFf`kJEWNy#4><39BP>DwH+Ym^F;In>0rw7aq0~U=D3{%KU#4R2^*FkcEmrLO-8OzR$pvFyY15?(FCG+Pa=J^8>dUZ~!Z$gH~c4^im5b2#J%#A7ij zGwkRB3!;rUS)5-c;#xg+-_U0u_=e}<34a0Nrii;ddX;nRTpxSrAoVNKK{lohb!rGl z>P+hqd{BC1AL^|1I08%U{(-uUjC;V2oR{nqffT`_$!V04N${zhMmf75Gs;tdnO1k& z18v0sCsvae7~|MUahuCZ?M-n5j@46Qr}|~LOfLF(^A@=xfET6-(0OqCN9bE*Sbr>s zq+uy+1J#e$l1Fj$PWe-AIgayQq#K?2#>B@^0f$nn8!3m~)6pyX{q|$OyX&8|>7Vg$6)pN; z!zb8h({>P$LJwlTtR|=k~wKU3P(-qOSxn@U=Pp5OX3=j=|2( zf#Ufd`-b{)2BhnYCug9H4Cu*jD9ASY4{-#5m@pxT1H8;T*%{C$JBaLOic?gCP<0{wv=}lt zvh5KQmPf%P3jub80kYG6y@T+u@5KE3>IN;m$insVTeqgoKySq-(r`buY@GvqqyZ`d z`oH7`mLnQkvJ2ov3=A~4jAlT#IFJp+zNqw~0A#eo;c>4_zY>k zv}WQxtHmzpV|*ecr$2Yti2#^8GG{!WSR9T`nfSe2k_d$jwb;#5YCvB83Lb?YdZd2xEJJ{08^NUGo2dVQXgohufOH6!Myg^oOx)WS9c)wFe?n^Y3Tkuy(l zHn6f8!Lw%Og@m?i3Jlv^0P4ZK>Ie=0Y|TvsBJ3-3 zNmtQMu>~1vzf+tD**%i3+M2sV{5}Bi=wIZ=&mp5%_^-+puLhU5Z)pP9;xT-;r*0!| zc|@3pDNm~|uq}DZG3dk5jxKD7&TKH5mO?j@Q5?EB7^Bq3b^#>8Se(X0oq z?Hw!dt9ZqSzG=mLelRrd*{f@#cs2byB?i!{XqLCMR^V3J6CXn@*EjaGYtL?%{Ua zC8=9JqL)zH=m0E!Vmjv#UHhC)sIy5vwEkorfcyopV+9dD#0=viVlhk>p;;{SX!#;bscwXV`?eyZyjt$&OT>-UJJFOS{Cw&>o`IY_r z_(Xh~GBOpbgZx-czDLi6-ak!8wD3~2c}ZS+XC8xU%&i+e{})R7W|LKtQulwvn>4$5 zk2kCVYjg2W6&M$4`bXS)Zg7)^0P1#Yd{rW00Pqkrv(Her2G$>=e2lW7-1tH|DtrUk zWD>s0AYLd5)^o{IViAYStYAK;ci(h;!M9gsQOH z&;^Kqn};tZha>YymAD4@pJsEHJ>l2#L{S?qO~EUV1HR&K14kpypp;r52L9p8`PG0= zc&%eRI{DGi9P)&{vd>8`#aU0Es))a1Ht>FKzY>N>TRd8}@!1vvrcH}Vl44zu0n>0W z+oOZOpbJ6I>%*CR693P^3PeS&BVoKF&F_V5#{?tqiGoOu%ykoE)ulFX;||}$>j4kv zTud7g9ni*YeK%ot~aGcQhDT zf@K|{cO-ucZZxa}G}={5qBs;Cw7?!A^AXrC44=f4o(+dU^-~^`fii{}h&kQf{ z0Pezr_cSgce&{G)uH<7?p9=zm>^*_Z9RKUai`DPro)15i7Z40$TsIAQ+n|u!i+CvF zmNyf|H^?Kwn*ZT5;O-4phb}s%ujW=c%@UGN-?FufgkVrY@HQ4`0+pf5IGkO={5VeR z37BAj6Rlz84tq`x=4C_C14wm3%4s=Y*-}u+G$t0Od*Pb!Mo$V1Z9X<#xqM-OvZ8Lwl9L;t!gXu_Hs$+$8OVmPI%4aG~LY4IvsFs5HMKxvBlxNy9U_Y?GN~= z5*GNRmI45Kw9Mcpo+KGQ^a21ZXzZ%E*hN%2?|UURY@~c(O+zNZ7PoDhlmN}W+r9`y zAU6Zd+{|rR4T8X*_+@!+j>$voe`Ccahn^IPE9vyg3K9Vp3{lywtlIz~N)h`dAw7@; zAgET7&d$B`G{To`?2U1^hS=p?@^-b2%2lVulyaVgPhf+TUshuc?r%$lxdkKnLjQgp z-yYEl$z)VNc@OnSQ+qQS@kc}=Fy#-B37R{Z?*2?CDEkv$I}f;>RhzRAt3&onPGn;4 zZHy4ww*3BanU=ZzQ@ zUkK3+!`3x&VOf__r-sde>0w>aRw!tGN-iRVj!N~)nnA~?ii@J|tyUMt(Om$aF`E4Z zsISPCgct;y(oNbc5xfQijZVdY&+$)4(bemoX4*7(nSPvs5b?(HG9un)iWDH4)P zl9yW0>f+Le?6Q4j46`6R#$#4NWJ6irU>28oz2YvQZ%=x#OejVZi-VBI$Na#bPrnOK zBpXHQ1Qg&BV`r?4jBx)LY>DPmrt&{=U6qjT2R|W|S-h(jcKr6!uv2zJm`<>HlT$<*n4Klk7wxcWQOpLgjw0HFwtNc!y~SbCAr2Q8vxaUHmS z{2+PY_aNZ^7}E32n-S~nwZ(qg^Q7e<0|>LH|D={|X+Jrigz&v5MM!3ZJ}v#{4{Q>)CBqf2L`;4dry@utJUsoO zj$hV++NQRZCXt3swj)R1tiz&E*5zUR)(agozoQ^9C^1V)HS}y<2&g*>)+w}qxbrG` zUw%w~Co1Hpvp(WchD_trS|0iP01fMpsBuz5ESggwt-m2%u57e{W;Ig@FU7gxGJqvV zOp=yKXxw-&^NYQ{wS)vaB(@5ggr1PH)TXQyyRX)OG}vP>tl|72Z77h!3Ro}- zLQOp;XGD4&a$zoGl^lJKd-|C=+5t9C@c;4Cg1mB$hQvuQkI7|yUP=&VT`!FxnudSybZVE-V(B*!ZfSf!(O-Q>gwT5|`OU&%8pA_uRCzepgs z8e4)BKE5~&_KJyE_Ao1#*KI<{XD9#w00PEeR3FyBqa7@I8i>c(t-Pr;WV2AT_OVdF z6jCgKY~{4H&VU>S!)qg|Gl_+``vT$vg*{nemB&Ma4SP4x#uqu$d6mq3{8x zu;kKb+{D?@FAe`rZOc>&+?fWOo%DH%@E@G?jFNgbjP9x<{Y(rx5y#55-P!sZeKUfM z61|eI*2jf?WBz^-=%%NI09M-9TC7Ag`Z{X<$CX9LViuyizqOQ*kHz}p*wWPFi`c{L z!?U)Q8DqQFAuO}Ew}x=kHT(&nNb2|i?RSwbP1G&Bwgzbb)M=QMSz+M**~+xvNDpyr zI@W^l4PIzl0elDen$vEwFtcN^Mb5Huw=UQ%o$ocvSK$dBfLcoVm?J~Z6!5upoR$A) z0oyWwzc|k92I92%dDy?t)l1Wy>YVSdNtIpftqsGphDSBc7X@1O$Bkg#tloDs$4Z?M zaiWY{(yr|o4^(kWbc{kWB$6!B91Y6-Z^W@^&h?v~Vy?yx2N=F)_!(N?(H{XJjB|%) zOJ&>$AQ+uMF?rumfB*mm`=&bYy}~kO^d9KWW zc-|+*FP4JM$75i}uR#zJ+SSxfwf!6V{*6r~8(to_U^l`f0G|DDHk_%`?SOHk8#9|p z+6yb8Ef(z%?xOBc+^mkZ0XD_YN7dFK2CFhi-K$boqIh*G=!~5M|D;iVws7N;O*D{= z|30jyMy);(O8Z!ZF(5!}wx>)%8CC{dajG7Z!nfXS(rU|%r|QFlH#Tk%8UZ<;jQ+A7 z`1NsxoS*uY*JSn2>czOY>s9>}`@000000002jc@d}p literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/img/Ikea_WS-Light.webp b/ikea-zigbee-drivers/img/Ikea_WS-Light.webp new file mode 100644 index 0000000000000000000000000000000000000000..9de06f7ede120c524d4d5efb8cc7360d5da24aa0 GIT binary patch literal 7036 zcmaKwWl$VGwDuR5;_j}C`vS$?p~ce|Zm$N&HWF7GQo zesr3P3z!LmqJx)j)|P#4!O~%j!5$&Mv6WZn%@sK~C-mScajKA(vHX?L@GryTx% zk@oghP0eWWRBhVhaliIKq1l=3ysETx^_-+>$v*a6+;-VfJhEc|OZa|DhS*wNyC|lV zBn7Q*oFYmL7lDff_kE)1s9+P8K=)J`ULRU*C`RQC{uGfX850>@oqY5u0WzOF)hNLg zAWoi9I%^tW7T>T-NJ}P6U!pc z``AKvT=~ws5{Axj6KUr$w9?b0Kho=2k^l{p^{kTiuBP)cRt>H2mA^|T4s30t4((sH z4s@Y3@eEJA#61aRZoszf-B)gGg*z*tw-Q$gqq-${{m^%72j5#CUp&;Vt9F0G*w<|&B{L5r%JwIfr9SYIz zsDx;@k3%fmWBXdNA5we{=2IwDfAKvEfo=Y~qMlN3_k--RJvTd*ZS5r*7`Q7Fj~SBD zG)h(JG{vKbeWpl+eZ^csPi4n`sRYK{b&-pbP&KejTe-YeoKXAkoySZyw~Rc^Av@r0 z)WW?p0~>!z!jMCD-IR^af&kioLW{!_0CdblWIu ziHc<(xhjLJ7?^XTiHxw`9U5&O1y1^r?fa91Yrn~}_>dR7I8*ZKLyp|HfvVR3yfj~m zJc#lQ#*+B`oKDe*q-Gmak%*T}##$DXgkZ%i7MtmcZd6Gpj~|0)OA?t!4$!YgBKr!= zi%m8q;`f$uP8O}@wy%Wfl5o7!nNvpTVTVF{GcrB+$xEl?0iWgHj^cTtn~l@yCm~xg zAx{3w7PDbX{Xt}F5))~#D@m1?U5s%zLn>IE=92*V?kHi@ob{D*(F7f78oC`z%$Re? z*U09^$UB$W(!3&chsjCYw>R5)I$IXg`Z2KQQLT!$29LqimA2wg@_`7#XV@6LgbnbI z9dw+e|JYFrC_(_i-I)wq9ON@=fxYeY(<0=&WDKF>SPX!UHSyw0uL<(G8WCCF-wta4c)4aUgl{8^def zco?{#hj0;S7$kEs;_%|g6`XwGuLiYZIs`R&1*oDPoD(ge{z8)wm0^i&*BN$4vRZL; zvcYq;ShZ-M%I_xf`jm4ZHFAy}ZkTXdc2Ip}l#B=e(Ny)B8E8|`$>6vA5=`?(?NG7k zk^9V>hj~S21v&qVjywx)Su^#w;a=+l>ZFQQDu>r?k?mWB_C!;znuhDFmCRWut!)+k3FKRV?iuJtA?L#8EZL$=Qjd z-u=!VLhMakvoax&I1v;4S6Cz99Ba7?kV9hZ7rkR*4XLaEYakyJT;=$2Hd*50j#fVYEnv4ovA+PA`} zTSZiMtx;WRH+25;&o%j*SZueG_jem*`4_c02dR~;HBLxw7J4$Ry!roP2vk+aVByc~ zDy0)Ot%)bXBE$)vs)?F5f_?OZ>W&uv^Bmu2U3t*NZH4#3XB2vz@;x2s7v=N2B_`E0ASP$SYgN+lRu6{_ zwJ}C&evE+)ek~n%6H3?zZ2b`9lpVAZ+pQzRQNxtYb!BWnDrBhC?AlQELv#V{W8R{b ztc)NCX6>Lf9GR-`8Y$vYBmY!!11WWCr$B?Z{5~@<5|y5xKV=>t7NScKyJcjq#Ai@J zfw4mC*3^#Z31_N2t8N@aSgMlk!YN?}6ppgInwDvoYGXyYmWvi3^H4$Yy-&#PsTJt< zRt~kbJ~xtBtnDD-G$G`6Q(SY6>t-TjMJjC_w--lZ2OC$JJ-j&uwch54LUJBBYV45w zx30uU?>0-i>8aqMIUiAuA!!aKEit^Ua_Pdp77iNRe0P4&eu_ouWh z%9MD#mc$jDhi?ZBPRXnw{e<8;0X1}QtSGLIL$N5n=Y5aZ3@~UjL$Pv|yeMj(X{b$+ZF{y8#W&mK>hZ$`TE@qX3!UN(t7ek=L8o-WQ> z=cN?eGJ4xc!dX4WE=ei=pwpH^M-*ww{5NtwdMq}D&OK44)CRcl@DM|TxTUUHDGO?O zgXNjIHR+o87~(-5L$2z?ou3FBiqEM0XXh6c(T-zpVA&z|O|O0WUFrOm3?kximN;M< zT0v-nq6I8lKv^s2NvI=SqCkW;v&m=c51Z5-9!a>6@qzURlHW)ioHvi4D9Ggfhmt)r zlWXxz%aw-D!br}^=pT4QGw7``Pt)}K@8LZ+n&+2|QkF(4Rq9HnUUc}oG;(frH zeLHBDcXTgdzCY=f46Z!|)!W|hmm~1zjggIremmp#o{%T*%rcZgs67Aa*sGY0hm59$ z+9u$-B>3I-o4Y5%8^sz{SrDHH)f8Ht0tX}#?AaJY-%%wh<}lAC83Hgd%C5$$6BbPR z9oA@2_Gc#~Xh>3=8ZYMreSHQ3VqE1=R(nRMJz$UF(%^CCka#O98?DA=J zppM_Sh@8X3^5*H%T-{B8c{yS7s?)2s1$s>V)ITSlTAfGIhF0}AjMhJ^ z#rlDDlyWgwdk%blZDBja@`^hO zehP2&BfuFO;hz9FK~B=vRcc={$i`CxBpMoxG-3rt{M{2t6=;jPE++3knY`rQwj^=j zUy6lrL0c1)LWwv!C6|%SK8YTq?q)&}0Rx%?txA40>}S-O4Z49hu+Q49&$9Q4IxHNS zO=-!#>?LI#MUlON#xX7?rKENh|o$9Vo)ag_hZS&lc z7%!8;yJN4ihNZ?-e#J2kwrW$-(oj2Z5DC9|nfmb_E25|PndcoqcR2TiZ1$Ppz7^ZTa_XQY#Qr!|A&;K3|o5 z$J*}N;@CoeIprba6OgB6Hg%`;<0V^^a85=1b8q5d4BpM zUF^Gus4Ieu#jYCai14at3k1GFlV&z zU9$j0^0uj|FNG@>6l4eO{P3Ze2E)UnG$Z;!bbi?KNmOAzoTC&`q3@2Li-!LU^!YKY zo%ik0bJ2X;7h3;n5EyD4SFn$%NKxc@-UUi@b|Tw)Ggeq{s6bVTHshe+<4#;pB4BLX zcbHU%1;Y#0Ccs5Q+4?d8h^S>DnM>|@AuztL%>ym5Jg&EowY5)}a?MbUdTOpc0ul=) z1g&Nj?p0qa#0GA2TOZkaJ~h_vER+>eU-G$dIoeU<4_=<(Bkl`|OG;F~eW#%B3@GSL z5SB}1{g;#iyJ*Vx(K!$~(kHHuxvg&p6qmF(P`m;Zaq6?4LcC?e!nbd0jzd|G%Q{^m z3I%-S=>6k0CRp>Z_$J4Jo9qS9N{z8*w^D)f=+)&18B;1WOO5a9iu*5#^ygB=ypg2& zT}gy0bDe$xjhHptq?{$hNr`4Tsohtc?6&o=i?A^FP=kg+e<-0Z_x1<+d<$^&206=g zNFO_W^28O5!UR%7!>^&nqm!DlX1;gd?*e&vtl~y{SCdh{Y<*Odn02 zJ@gHV{EeQ6|7(7Gupk;GCzgXr^AH>C-z zZV&};MYAu6WqUx`_D+6$ejAPV~0KEqJz(pm7&(ZC(rD_T(8Zf=DS5%eY|$beaTA|UK&O}#7^nT z)H|B*wj-0)T>QuwusIzvAt=UMkjs`S)We)!Qxe=mxiFnX8&d!Pic1*X4RvA-ebK2^ zjr~1qh*bjICiqyOH8G!7aylYxocD*!lL9gPbxT)Z+qkhXIaa3zR^%Y)M16JpHR@+V z9npvsJzB_X_fH2F-&)qPv|EnQtf_VM=LU9?s?-I6*R1Yi4dST}K!GV{-^GEBJCV-HS&VOvu75;6oiOOs@n zmhoJF5nKH5tllmjvQnII_zGwqy1fct@t6EJKvec z=B>>nU)y4j+C=939XOX;){=KiVYsqtfrAp`xAtDaQ zMh>LQf%nD!tyB9B0+#!yDS%kYxIiRAhny76j*(O+0S4~TcJ-`rYktsB-m@?MlexRbwdAh;~ zQ#+q5MOn<$-;nX({=noufn2+NV*Vo)(H&V_5K`zLO4zJZedZ7@p+m5$a36i~OXCCO z&2w+S)3a>cwO%v==gCzd7k4fySt(Ptvhr%~T_H83S#??}ET;LJJmsQbz0&NEEt@Nu zxcUdT5L{U@Wjnu3f2Ly=A+^75;?YvsD2axflj*VteyAUg~Ju|u&ad2<1Z+6M8n^( z@p9b>YXrtG%`T4CBZ}!rFTxlr3dJ*Wo|hrtf)u6cJ;uz**yfO-sdOwivGJT3niDsX zY(~T6xr%-fZ6oz(CspoKO@HqDLjK+}8aGyBz4^Gy7=TtEW?7$E&r&0Kh)+LTPOwDr zgdJ(|XRS|&4&hc_F}$^&o6>s=y6_{TbK`DOZiO^AbB#p6JVDZ4kncyoM|K)R8yyg7 z@P3^4S3OJyC+vMXSHMxr6Ln%x%SQi~$Slq(tOg(t6%icAe;_Sq#V7h=2QT1nW>e`F zhsQ_zpobR!2>Fo1j^9M52{%cMaR{;QZ*c+*+P#n8|E&$%dnvTA9Xwmp3qt+%E9Lxx zJLkzv96g6Uh#*CH`@CV9Z|k~B+0minHAio`Qe=VjE48z;9S2*Nm`W#yA*)+dwDXwG zt{@E~-Z~}YWt&P)qY#Q~!N4$I8nf4R3t90DSp-|u5Mut4Wlk^fS~?)ln5lfNdEsu;fud@iG(;kgrQX64)&w7aS2}4G`fy6)UvK!$L z+4YkpopbhQx@RZV;w=FvxGQXqEbG1-;7LD=5nDsY!o8_)J@lxtUWvPO`kioEQb?L+ zBH^-^_{Oi-iUqC{gyk(SEWQ>3M4~ouy`}7RK+j1B4?O!XbJw`MaL;rQMuojo6(l<9 z5LDnPu)%13Is3gL5Bj?L=Jw4wGEn$FSUNqK<^n&zE%?2j-p&gO&w8*ViF?swUlb+b zIb{^3wolW%-u`_o2szBqkMxN1+S&aMT67|boT6kKLJ*^#CLN?YSr<%G)Dm#kU0WIG zeI3?E1J&Ih@Y}TIJoW8Zk!;zDH{`0PTNa6R;cfZgH_G9OVz$aRWJ-pVsiV>8o3Z;! z>Ss{&yDS|J91XAH6|odfz%Sq>f_dEz9>E&PAC9W^BSzc^jeMcBJg8rWt3sOvzrl44 z6cU!5wbPAX4G})9iFxTVGrHHFY29pWsNFSF`JUSA2tOkJ4Hq~4O$LWOOkgy$RDuQ~ zcz^f@n6tfu?=NVErhfj)nmPEZnyR}cD4cGeEN&eGgQD=q9B=Iv`PM$iYJ~u%-f4Ay z&hT}?YJ)kM6Kh>Q4;HZY?>(d`$tb}aG&JS*NF`_Avp&$E-p6zdaUUv$q`SmwK z#EU)P4xkY@C!+q4T%mS9+g_O3$QOuPpi}%7ZV_~zimw{za}$ffMo=}gF+r-FBsfPt zk-x+Hjd8Srht13Z`TaDWlP)E>$l=*GPNj6@zoG-@cu7zM`x`p3s&vwG&lZk$Ysg-T z2;7ayKp_onF%Rd@YV^}6-!O{PxO;TM1A+N<*`m~p=4-F)n|vT`zH4K zd9^J!@hoEbU;XOZEQGg(00b#oKfP~)o>^=&_acvB6JMpHqYOK|TBNL%Y0OBJvAd-r z6!M!@ajoK?d#~AB-GPa6gdZ=p`MVyR^NZ(uV*8)>JqJDF?Ek@hq-&F93uC>>PQPk9 Q-X0EQB>bV!Z literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 670bd30..455e1ae 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,10 +1,10 @@ { "packageName": "IKEA Zigbee drivers", - "version": "4.1.0", - "releaseNotes": "v4.1.0\n - Ikea E2204: Add option to disable LED indicators on the device, ensuring total darkness\n - Legrand 741811: Add option to configure LED indicators: always On, always Off, follow device power state", + "version": "5.0.0", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", - "dateReleased": "2024-04-06", + "dateReleased": "2024-04-??", "documentationLink": "https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/", "communityLink": "https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853", "payPalUrl": "https://www.buymeacoffee.com/dandanache", @@ -17,7 +17,7 @@ "name": "IKEA Askvader On/Off Switch (E1836)", "description": "Zigbee driver for Askvader On/Off Switch (E1836)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1836.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1836.groovy", "required": false }, { @@ -25,15 +25,23 @@ "name": "IKEA Badring Water Leakage Sensor (E2202)", "description": "Zigbee driver for IKEA Badring Water Leakage Sensor (E2202)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2202.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2202.groovy", "required": false }, { "id" : "97ee5c2b-9c74-43f7-a149-adbfbad48da5", - "name": "IKEA Tradfri LED Driver (ICPSHC24)", - "description": "Zigbee driver for IKEA Tradfri LED Driver (ICPSHC24)", + "name": "IKEA Dimmable Light", + "description": "Zigbee driver for IKEA Dimmable Lights", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_ICPSHC24.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_DIM-Lights.groovy", + "required": false + }, + { + "id" : "23ef8cf5-29bd-481b-a045-4e71078153e2", + "name": "IKEA White Spectrum Light", + "description": "Zigbee driver for IKEA White Spectrum Lights", + "namespace": "dandanache", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_WS-Light.groovy", "required": false }, { @@ -41,7 +49,7 @@ "name": "IKEA Parasoll Door/Window Sensor (E2013)", "description": "Zigbee driver for IKEA Parasoll Door/Window Sensor (E2013)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2013.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2013.groovy", "required": false }, { @@ -49,7 +57,7 @@ "name": "IKEA Rodret Dimmer (E2201)", "description": "Zigbee driver for IKEA Rodret Dimmer (E2201)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2201.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2201.groovy", "required": false }, { @@ -57,7 +65,7 @@ "name": "IKEA Somrig Shortcut Button (E2213)", "description": "Zigbee driver for IKEA Somrig Shortcut Button (E2213)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2213.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2213.groovy", "required": false }, { @@ -65,7 +73,7 @@ "name": "IKEA Starkvind Air Purifier (E2006)", "description": "Zigbee driver for IKEA Starkvind Air Purifier (E2006)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2006.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2006.groovy", "required": false }, { @@ -73,7 +81,7 @@ "name": "IKEA Styrbar Remote Control N2 (E2002)", "description": "Zigbee driver for IKEA Styrbar Remote Control N2 (E2002)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2002.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2002.groovy", "required": false }, { @@ -81,7 +89,7 @@ "name": "IKEA Symfonisk Sound Remote Gen2 (E2123)", "description": "Zigbee driver for IKEA Symfonisk Sound Remote Gen2 (E2123)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2123.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2123.groovy", "required": false }, { @@ -89,7 +97,7 @@ "name": "IKEA Tradfri Control Outlet (E1603)", "description": "Zigbee driver for IKEA Tradfri Control Outlet (E1603)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1603.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1603.groovy", "required": false }, { @@ -97,7 +105,7 @@ "name": "IKEA Tradfri Motion Sensor (E1745)", "description": "Zigbee driver for IKEA Tradfri Motion Sensor (E1745)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1745.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1745.groovy", "required": false }, { @@ -105,7 +113,7 @@ "name": "IKEA Tradfri On/Off Switch (E1743)", "description": "Zigbee driver for IKEA Tradfri On/Off Switch (E1743)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1743.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1743.groovy", "required": false }, { @@ -113,7 +121,7 @@ "name": "IKEA Tradfri Open/Close Remote (E1766)", "description": "Zigbee driver for IKEA Tradfri Open/Close Remote (E1766)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1766.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1766.groovy", "required": false }, { @@ -121,7 +129,7 @@ "name": "IKEA Tradfri Remote Control (E1810)", "description": "Zigbee driver for IKEA Tradfri Remote Control (E1810)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1810.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1810.groovy", "required": false }, { @@ -129,7 +137,7 @@ "name": "IKEA Tradfri Shortcut Button (E1812)", "description": "Zigbee driver for IKEA Tradfri Shortcut Button (E1812)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E1812.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E1812.groovy", "required": false }, { @@ -137,7 +145,7 @@ "name": "Tretakt Smart Plug (E2204)", "description": "Zigbee driver for Tretakt Smart Plug (E2204)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2204.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2204.groovy", "required": false }, { @@ -145,7 +153,7 @@ "name": "IKEA Vallhorn Motion Sensor (E2134)", "description": "Zigbee driver for IKEA Vallhorn Motion Sensor (E2134)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2134.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2134.groovy", "required": false }, { @@ -153,7 +161,7 @@ "name": "IKEA Vindstyrka Air Quality Sensor (E2112)", "description": "Zigbee driver for IKEA Vindstyrka Air Quality Sensor (E2112)", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_4.1.0/ikea-zigbee-drivers/Ikea_E2112.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2112.groovy", "required": false } ] diff --git a/ikea-zigbee-drivers/src/blueprint.groovy b/ikea-zigbee-drivers/src/blueprint.groovy index f65f1f8..513727d 100644 --- a/ikea-zigbee-drivers/src/blueprint.groovy +++ b/ikea-zigbee-drivers/src/blueprint.groovy @@ -24,10 +24,9 @@ metadata { {{> file@definition }} {{/ file }} {{/ device.capabilities }} - {{# device.fingerprints }} - // For firmware: {{ firmwares }} - {{{ value }}} + {{# device.fingerprints }} + {{{ value }}} // {{# type }}Type {{ type }}{{/ type }}{{^ type }}For firmware{{/ type }}: {{ firmwares }} {{/ device.fingerprints }} {{# device.capabilities }} {{> file@attributes }} @@ -324,6 +323,9 @@ private void utils_processedZdpMessage(String type, String details) { private String utils_payload(String value) { return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') } +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} // switch/case syntactic sugar @CompileStatic private boolean contains(Map msg, Map spec) { diff --git a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy index 24afa9f..2e40792 100644 --- a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy @@ -9,32 +9,42 @@ capability 'SwitchLevel' // Inputs for capability.Brightness input( name: 'levelStep', type: 'enum', - title: 'Brightness up/down step', - description: 'Level adjust when using the levelUp/levelDown commands.', + title: 'Brightness up/down shift', + description: 'Brightness +/- adjust for the shiftLevel() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true +) +input( + name: 'levelChangeRate', type: 'enum', + title: 'Brightness change rate', + description: 'Brightness +/- adjust for the startLevelChange() command.', options: [ - '1': '1%', - '2': '2%', - '5': '5%', - '10': '10%', - '20': '20%', - '25': '25%', - '33': '33%' + '10': '10% / sec - from 0% to 100% in 10 seconds', + '20': '20% / sec - from 0% to 100% in 5 seconds', + '33': '33% / sec - from 0% to 100% in 3 seconds', + '50': '50% / secs - from 0% to 100% in 2 seconds', + '100': '100% / sec - from 0% to 100% in 1 seconds', ], defaultValue: '20', required: true ) input( - name: 'startLevelChangeRate', type: 'enum', - title: 'Brightness change rate', - description: 'The rate of brightness change when using the startLevelChange() command.', + name: 'transitionTime', type: 'enum', + title: 'Brightness transition time', + description: 'Time taken to move to/from the target brightness when device is turned On/Off.', options: [ - '10': '10% / second : from 0% to 100% in 10 seconds', - '20': '20% / second : from 0% to 100% in 5 seconds', - '33': '33% / second : from 0% to 100% in 3 seconds', - '50': '50% / seconds : from 0% to 100% in 2 seconds', - '100': '100% / second : from 0% to 100% in 1 seconds', + '0': 'Instant', + '5': '0.5 seconds', + '10': '1 second', + '15': '1.5 seconds', + '20': '2 seconds', + '30': '3 seconds', + '40': '4 seconds', + '50': '5 seconds', + '100': '10 seconds' ], - defaultValue: '20', + defaultValue: '5', required: true ) input( @@ -59,28 +69,10 @@ if (turnOnBehavior == 'FIXED_VALUE') { required: true ) } -input( - name: 'transitionTime', type: 'enum', - title: 'On/Off transition time', - description: 'Time taken to move to/from the target brightness when device is turned On/Off.', - options: [ - '0': 'Instant', - '5': '0.5 seconds', - '10': '1 second', - '15': '1.5 seconds', - '20': '2 seconds', - '30': '3 seconds', - '40': '4 seconds', - '50': '5 seconds', - '100': '10 seconds' - ], - defaultValue: '5', - required: true -) input( name: 'prestaging', type: 'bool', title: 'Pre-staging', - description: 'Set the brightness level without turning On the device (for later use).', + description: 'Set brightness level without turning On the device (for later use).', defaultValue: false, required: true ) @@ -89,78 +81,39 @@ input( {{# @commands }} // Commands for capability.Brightness -command 'levelUp' -command 'levelDown' +command 'shiftLevel', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] {{/ @commands }} {{!--------------------------------------------------------------------------}} {{# @implementation }} // Implementation for capability.Brightness +void setLevel(BigDecimal level, BigDecimal duration = 0) { + Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) + log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" + Integer lvl = newLevel * 2.54 + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String command = prestaging == false ? '04' : '00' + String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) +} void startLevelChange(String direction) { - log_debug "Starting brightness change ${direction}wards with a rate of ${startLevelChangeRate}% / second" - + log_debug "Starting brightness level change ${direction}wards with a rate of ${levelChangeRate}% / second" Integer mode = direction == 'up' ? 0x00 : 0x01 - Integer rate = Integer.parseInt(startLevelChangeRate) * 2.54 - String payload = "${zigbee.convertToHexString(mode, 2)} ${zigbee.convertToHexString(rate, 2)}" + Integer rate = Integer.parseInt(levelChangeRate) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload rate, 2}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114301 ${payload}}"]) } void stopLevelChange() { - log_debug 'Stopping brightness change' + log_debug 'Stopping brightness level change' utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114303}"]) } -void levelUp() { - log_debug "Moving brightness up by ${levelStep}%" - - Integer stepSize = Integer.parseInt(levelStep) * 2.54 - Integer dur = 0 - - String payload = "${zigbee.convertToHexString(0x00, 2)} ${zigbee.convertToHexString(stepSize, 2)} ${zigbee.swapOctets(zigbee.convertToHexString(dur, 4))}" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) -} -void levelDown() { - log_debug "Moving brightness down by ${levelStep}%" - +void shiftLevel(String direction) { + log_debug "Shifting brightness level ${direction} by ${levelStep}%" + Integer mode = direction == 'up' ? 0x00 : 0x01 Integer stepSize = Integer.parseInt(levelStep) * 2.54 - Integer dur = 0 - - String payload = "${zigbee.convertToHexString(0x01, 2)} ${zigbee.convertToHexString(stepSize, 2)} ${zigbee.swapOctets(zigbee.convertToHexString(dur, 4))}" + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 2} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) } -void setLevel(BigDecimal level, BigDecimal duration = 0) { - Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) - log_debug "Setting brightness to ${newLevel}% during ${duration} seconds" - - // Device is On: use the Move To Level command - if (device.currentValue('switch', true) == 'on' || prestaging == false) { - Integer lvl = newLevel * 2.54 - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min - String command = prestaging == false ? '04' : '00' - String payload = "${zigbee.convertToHexString(lvl, 2)} ${zigbee.swapOctets(zigbee.convertToHexString(dur, 4))}" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) - return - } - - // Device is Off and onLevel is set to a fixed value: ignore command - if (turnOnBehavior == 'FIXED_VALUE') { - log_info 'Ignoring Set Level command because the device is turned Off and "Turn On behavior" preference is set to "Always start with the same fixed brightness"' - return - } - - // Device is Off: keep the device turned Off, use the OnLevel attribute - log_debug('Device is turned Off so we pre-stage brightness level for when the device will be turned On') - applyOnLevel newLevel - utils_sendEvent(name:'level', value:newLevel, descriptionText:"Brightness is ${newLevel}%", type:'digital', isStateChange:true) -} -void applyOnLevel(Integer level) { - Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) - log_debug "Setting Turn On brightness to ${newLevel}%" - Integer lvl = newLevel * 2.54 - utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) -} -private void turnOnCallback(String switchState) { - // Device was just turned on: Read the value of the OnLevel attribute to sync/update its value - if (switchState == 'on') utils_sendZigbeeCommands zigbee.readAttribute(0x0008, 0x0011) -} {{/ @implementation }} {{!--------------------------------------------------------------------------}} {{# @updated }} @@ -172,11 +125,11 @@ if (levelStep == null) { } log_info "🛠️ levelStep = ${levelStep}%" -if (startLevelChangeRate == null) { - startLevelChangeRate = '20' - device.updateSetting 'startLevelChangeRate', [value:startLevelChangeRate, type:'enum'] +if (levelChangeRate == null) { + levelChangeRate = '20' + device.updateSetting 'levelChangeRate', [value:levelChangeRate, type:'enum'] } -log_info "🛠️ startLevelChangeRate = ${startLevelChangeRate}% / second" +log_info "🛠️ levelChangeRate = ${levelChangeRate}% / second" if (turnOnBehavior == null) { turnOnBehavior = 'RESTORE_PREVIOUS_LEVEL' @@ -184,10 +137,11 @@ if (turnOnBehavior == null) { } log_info "🛠️ turnOnBehavior = ${turnOnBehavior}" if (turnOnBehavior == 'FIXED_VALUE') { - Integer lvl = onLevelValue == null ? 50 : onLevelValue.intValue() - device.updateSetting 'onLevelValue', [value:lvl, type:'number'] - log_info "🛠️ onLevelValue = ${lvl}%" - applyOnLevel(lvl) + Integer onLevelValue = onLevelValue == null ? 50 : onLevelValue.intValue() + device.updateSetting 'onLevelValue', [value:onLevelValue, type:'number'] + log_info "🛠️ onLevelValue = ${onLevelValue}%" + Integer lvl = onLevelValue * 2.54 + utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) } else { log_debug 'Disabling OnLevel (0xFF)' cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) @@ -205,12 +159,14 @@ if (prestaging == null) { device.updateSetting 'prestaging', [value:prestaging, type:'bool'] } log_info "🛠️ prestaging = ${prestaging}" + +// If prestaging is true, enable update of brightness without the need for the device to be turned On +cmds += zigbee.writeAttribute(0x0008, 0x000F, 0x18, prestaging ? 0x01 : 0x00) {{/ @updated }} {{!--------------------------------------------------------------------------}} {{# @configure }} // Configuration for capability.Brightness -sendEvent name:'level', value:'100', type:'digital', descriptionText:'Brightness initialized to 100%' cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) {{/ @configure }} @@ -229,33 +185,14 @@ cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel // Report/Read Attributes Reponse: CurrentLevel case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: - Integer newLevel = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) - utils_sendEvent name:'level', value:newLevel, descriptionText:"Brightness is ${newLevel}%", type:'digital' - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value}" - return - -// Read Attributes Reponse: OnLevel -// This value is read immediately after the device is turned On -// @see turnOnCallback() -case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0011] }: - Integer onLevel = msg.value == '00' ? 0 : Integer.parseInt(msg.value, 16) * 100 / 254 - - // Clear OnLevel attribute value (if previously set) - if (turnOnBehavior != 'FIXED_VALUE' && msg.value != 'FF') { - setLevel device.currentValue('level', true) - log_debug 'Disabling OnLevel (0xFF)' - utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) - return - } - - // Set current level to OnLevel - if (turnOnBehavior == 'FIXED_VALUE') setLevel(onLevel) - utils_processedZclMessage 'Read Attributes Response', "OnLevel=${msg.value}" + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) + utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return // Other events that we expect but are not usefull for capability.Brightness behavior case { contains it, [clusterInt:0x0008, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=level, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) return diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy new file mode 100644 index 0000000..8746cc9 --- /dev/null +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -0,0 +1,155 @@ +{{!--------------------------------------------------------------------------}} +{{# @definition }} +capability 'ColorTemperature' +capability 'ColorMode' +{{/ @definition }} +{{!--------------------------------------------------------------------------}} +{{# @inputs }} + +// Inputs for capability.ColorTemperature +input( + name: 'colorTemperatureStep', type: 'enum', + title: 'Color Temperature up/down shift', + description: 'Color Temperature +/- adjust for the shiftColorTemperature() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true +) +input( + name: 'colorTemperatureChangeRate', type: 'enum', + title: 'Color Temperature change rate', + description: 'Color Temperature +/- adjust for the startColorTemperatureChange() command.', + options: [ + '10': '10% / sec - from hot to cold in 10 seconds', + '20': '20% / sec - from hot to cold in 5 seconds', + '33': '33% / sec - from hot to cold in 3 seconds', + '50': '50% / secs - from hot to cold in 2 seconds', + '100': '100% / sec - from hot to cold in 1 seconds', + ], + defaultValue: '20', + required: true +) +{{/ @inputs }} +{{!--------------------------------------------------------------------------}} +{{# @commands }} + +// Commands for capability.ColorTemperature +command 'startColorTemperatureChange', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] +command 'stopColorTemperatureChange' +command 'shiftColorTemperature', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] +{{/ @commands }} +{{!--------------------------------------------------------------------------}} +{{# @implementation }} + +// Implementation for capability.ColorTemperature +void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, BigDecimal duration = 0) { + Integer mireds = Math.round(1000000 / colorTemperature) + mireds = mireds < state.minMireds ? state.minMireds : (mireds > state.maxMireds ? state.maxMireds : mireds) + Integer newColorTemperature = Math.round(1000000 / mireds) + log_debug "Setting color temperature to ${newColorTemperature}k (${mireds} mireds) during ${duration} seconds" + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) + if (level > 0 && duration == 0) setLevel level, duration +} +void startColorTemperatureChange(String direction) { + log_debug "Starting color temperature change ${direction}wards with a rate of ${colorTemperatureChangeRate}% / second" + Integer mode = direction == 'up' ? 0x03 : 0x01 + Integer changeRate = (state.maxMireds - state.minMireds) * Integer.parseInt(colorTemperatureChangeRate) / 100 + String payload = "${utils_payload mode, 2} ${utils_payload changeRate, 4} ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434B ${payload}}"]) +} +void stopColorTemperatureChange() { + log_debug 'Stopping color temperature change' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114347 00 00}"]) +} +void shiftColorTemperature(String direction) { + log_debug "Shifting color temperature ${direction} by ${colorTemperatureStep}%" + Integer mode = direction == 'up' ? 0x03 : 0x01 + Integer stepSize = (state.maxMireds - state.minMireds) * Integer.parseInt(colorTemperatureStep) / 100 + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 4} 0000 ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434C ${payload}}"]) +} +{{/ @implementation }} +{{!--------------------------------------------------------------------------}} +{{# @updated }} + +// Preferences for capability.ColorTemperature +if (colorTemperatureStep == null) { + colorTemperatureStep = '20' + device.updateSetting 'colorTemperatureStep', [value:colorTemperatureStep, type:'enum'] +} +log_info "🛠️ colorTemperatureStep = ${colorTemperatureStep}%" + +if (colorTemperatureChangeRate == null) { + colorTemperatureChangeRate = '20' + device.updateSetting 'colorTemperatureChangeRate', [value:colorTemperatureChangeRate, type:'enum'] +} +log_info "🛠️ colorTemperatureChangeRate = ${colorTemperatureChangeRate}% / second" + +// Regardless of prestaging, enable update of color temperature without the need for the device to be turned On +cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) +{{/ @updated }} +{{!--------------------------------------------------------------------------}} +{{# @configure }} + +// Configuration for capability.ColorTemperature +cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0000 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) +state.minMireds = 200 // Will be updated in refresh() +state.maxMireds = 600 // Will be updated in refresh() +{{/ @configure }} +{{!--------------------------------------------------------------------------}} +{{# @refresh }} + +// Refresh for capability.ColorTemperature +cmds += zigbee.readAttribute(0x0300, 0x0008) // ColorMode +cmds += zigbee.readAttribute(0x0300, 0x0007) // ColorTemperatureMireds +cmds += zigbee.readAttribute(0x0300, 0x400B) // ColorTemperaturePhysicalMinMireds +cmds += zigbee.readAttribute(0x0300, 0x400C) // ColorTemperaturePhysicalMaxMireds +{{/ @refresh }} +{{!--------------------------------------------------------------------------}} +{{# @events }} + +// Events for capability.ColorTemperature +// =================================================================================================================== + +// Report/Read Attributes Reponse: ColorTemperatureMireds +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0007] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0007] }: + Integer mireds = Integer.parseInt "${msg.value}", 16 + Integer colorTemperature = Math.round(1000000 / mireds) + String colorName = convertTemperatureToGenericColorName colorTemperature + utils_sendEvent name:'colorTemperature', value:colorTemperature, descriptionText:"Color temperature is ${colorTemperature}K", type:'digital' + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${msg.value}(${mireds} mireds, ${colorTemperature}K, ${colorName})" + return + +// Report/Read Attributes Reponse: ColorMode +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: + String colorMode = msg.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorMode=${msg.value}" + return + +// Read Attributes Reponse: ColorTemperaturePhysicalMinMireds +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: + state.minMireds = Integer.parseInt "${msg.value}", 16 + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + return + +// Read Attributes Reponse: ColorTemperaturePhysicalMaxMireds +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400C] }: + state.maxMireds = Integer.parseInt "${msg.value}", 16 + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + return + +// Other events that we expect but are not usefull for capability.ColorTemperature behavior +case { contains it, [clusterInt:0x0300, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" + return +case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) + return +{{/ @events }} +{{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/Switch.groovy b/ikea-zigbee-drivers/src/capabilities/Switch.groovy index 3178807..b4e29c9 100644 --- a/ikea-zigbee-drivers/src/capabilities/Switch.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Switch.groovy @@ -29,7 +29,7 @@ input( // Commands for capability.Switch command 'toggle' {{# params.onWithTimedOff }} -command 'onWithTimedOff', [[name:'On time*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] +command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] {{/ params.onWithTimedOff }} {{/ @commands }} {{!--------------------------------------------------------------------------}} @@ -54,8 +54,8 @@ void toggle() { void onWithTimedOff(BigDecimal onTime = 1) { Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) log_debug 'Sending OnWithTimedOff command' - - String payload = "00 ${zigbee.swapOctets(zigbee.convertToHexString(delay * 10, 4))} 0000" + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) } {{/ params.onWithTimedOff }} diff --git a/ikea-zigbee-drivers/src/common.yaml b/ikea-zigbee-drivers/src/common.yaml index b9671f7..47bcd75 100644 --- a/ikea-zigbee-drivers/src/common.yaml +++ b/ikea-zigbee-drivers/src/common.yaml @@ -1,4 +1,4 @@ driver: namespace: dandanache author: Dan Danache - version: 4.1.0 + version: 5.0.0 diff --git a/ikea-zigbee-drivers/src/devices/Ikea_ICPSHC24/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml similarity index 78% rename from ikea-zigbee-drivers/src/devices/Ikea_ICPSHC24/config.yaml rename to ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml index 5fd5c1d..ccd98c8 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_ICPSHC24/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml @@ -1,21 +1,25 @@ device: - model: IKEA Tradfri LED Driver (ICPSHC24) - importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_ICPSHC24.groovy - image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_ICPSHC24.webp + model: IKEA Dimmable Light + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_DIM-Light.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_DIM-Light.webp links: - name: device details - url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#led-driver-icpshc24 + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#dimmable-light - name: community page url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: - - firmwares: 10EU-IL-1/1.2.245 (117C-4101-12245572) + - type: 10EU-IL-1 (Tradfri LED Driver 10W) + firmwares: 1.2.245 (117C-4101-12245572) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - - firmwares: 10EU-IL-1/2.3.086 (117C-4101-23086631) + - type: 10EU-IL-1 (Tradfri LED Driver 10W) + firmwares: 2.3.086 (117C-4101-23086631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - - firmwares: 30EU-IL-2/1.0.002 (117C-4109-00010002) + - type: 30EU-IL-2 (Tradfri LED Driver 30W) + firmwares: 1.0.002 (117C-4109-00010002) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' - - firmwares: 30-IL44-1/1.0.021 (117C-4104-00010021) + - type: 30-IL44-1 (Silverglans LED Driver 30W) + firmwares: 1.0.021 (117C-4104-00010021) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' capabilities: @@ -23,7 +27,6 @@ device: params: powerOnBehavior: true onWithTimedOff: true - callback: turnOnCallback - file: src/capabilities/Brightness.groovy - file: src/capabilities/HealthCheck.groovy params: diff --git a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml new file mode 100644 index 0000000..c8f3a60 --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml @@ -0,0 +1,30 @@ +device: + model: IKEA White Spectrum Light + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_WS-Light.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_WS-Light.webp + links: + - name: device details + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#ws-light + - name: community page + url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 + + fingerprints: + - type: LED2101G4, LED1949C5 + firmwares: 1.1.003 (117C-2204-00011003) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' + + capabilities: + - file: src/capabilities/Switch.groovy + params: + powerOnBehavior: true + onWithTimedOff: true + - file: src/capabilities/ColorTemperature.groovy + - file: src/capabilities/Brightness.groovy + - file: src/capabilities/HealthCheck.groovy + params: + schedule: 0 0 0/1 ? * * * + checkInterval: 3600 # every hour + thereshold: 3600 # report device as offline if no message was received in the last 60 minutes (device should report On/Off status every 10 minutes) + - file: src/capabilities/PowerSource.groovy + - file: src/capabilities/ZigbeeGroups.groovy + - file: src/capabilities/FirmwareUpdate.groovy From 47f6d71df47bb3e9ec5a51855cea9dd0cb0ff4a3 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Fri, 12 Apr 2024 14:47:32 +0300 Subject: [PATCH 02/26] Add driver for Color White Spectrum lights (RGB+CT) --- .groovylintrc.json | 5 +- hub-a-dashery-app/hub-a-dashery.html | 2317 +++++++++-------- ikea-zigbee-drivers/CHANGELOG.md | 10 +- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 991 +++++++ ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 2 +- ikea-zigbee-drivers/Ikea_E2134.groovy | 12 +- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 73 +- ikea-zigbee-drivers/README.md | 44 +- ikea-zigbee-drivers/packageManifest.json | 10 +- .../src/capabilities/Brightness.groovy | 2 +- .../src/capabilities/ColorControl.groovy | 130 + .../src/capabilities/ColorTemperature.groovy | 69 +- .../src/devices/Ikea_CWS-Light/config.yaml | 31 + .../src/devices/Ikea_E2134/E2134.groovy | 15 +- 14 files changed, 2528 insertions(+), 1183 deletions(-) create mode 100644 ikea-zigbee-drivers/Ikea_CWS-Light.groovy create mode 100644 ikea-zigbee-drivers/src/capabilities/ColorControl.groovy create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml diff --git a/.groovylintrc.json b/.groovylintrc.json index bc5d986..787c5ac 100644 --- a/.groovylintrc.json +++ b/.groovylintrc.json @@ -7,6 +7,9 @@ "CompileStatic": { "enabled": false }, + "DuplicateListLiteral": { + "enabled": false + }, "DuplicateMapLiteral": { "enabled": false }, @@ -41,4 +44,4 @@ "enabled": false } } -} \ No newline at end of file +} diff --git a/hub-a-dashery-app/hub-a-dashery.html b/hub-a-dashery-app/hub-a-dashery.html index 571f068..02a2c76 100644 --- a/hub-a-dashery-app/hub-a-dashery.html +++ b/hub-a-dashery-app/hub-a-dashery.html @@ -1,1191 +1,1270 @@ - - Hub-a-Dashery - v1.3.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - + + + + + + + + + + + + + + + + + + + + +
+ + - + + diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 2cde98a..5206c5b 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -6,8 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [5.0.0] - 2024-04-?? -- Add driver for Dimmable Lights, including LED drivers -- Add driver for White Spectrum Lights (CT) + +### Added +- Add driver for Dimmable Lights devices, including LED drivers +- Add driver for White Spectrum Lights devices (CT) +- Add driver for Color White Spectrum Lights devices (RGB+CT) + +### Changed +- Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver ## [4.1.0] - 2024-04-06 diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy new file mode 100644 index 0000000..96b9ecd --- /dev/null +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -0,0 +1,991 @@ +/** + * IKEA Color White Spectrum Light + * + * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ + */ +import groovy.transform.CompileStatic +import groovy.transform.Field + +@Field static final String DRIVER_NAME = 'IKEA Color White Spectrum Light' +@Field static final String DRIVER_VERSION = '5.0.0' + +// Fields for capability.HealthCheck +import groovy.time.TimeCategory + +@Field static final Map HEALTH_CHECK = [ + 'schedule': '0 0 0/1 ? * * *', // Health will be checked using this cron schedule + 'thereshold': '3600' // When checking, mark the device as offline if no Zigbee message was received in the last 3600 seconds +] + +// Fields for capability.ZigbeeGroups +@Field static final Map GROUPS = [ + '9900':'Alfa', '9901':'Bravo', '9902':'Charlie', '9903':'Delta', '9904':'Echo', '9905':'Foxtrot', '9906':'Golf', '9907':'Hotel', '9908':'India', '9909':'Juliett', '990A':'Kilo', '990B':'Lima', '990C':'Mike', '990D':'November', '990E':'Oscar', '990F':'Papa', '9910':'Quebec', '9911':'Romeo', '9912':'Sierra', '9913':'Tango', '9914':'Uniform', '9915':'Victor', '9916':'Whiskey', '9917':'Xray', '9918':'Yankee', '9919':'Zulu' +] + +metadata { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_CWS-Light.groovy') { + capability 'Configuration' + capability 'Refresh' + capability 'Actuator' + capability 'Switch' + capability 'ColorControl' + capability 'ColorTemperature' + capability 'ColorMode' + capability 'ChangeLevel' + capability 'SwitchLevel' + capability 'HealthCheck' + capability 'PowerSource' + + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // Type L2112: 1.1.10 (117C-2804-01010010) + + // Attributes for capability.HealthCheck + attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] + } + + // Commands for capability.Switch + command 'toggle' + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + + // Commands for capability.ColorControl + + // Commands for capability.ColorTemperature + command 'startColorTemperatureChange', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + command 'stopColorTemperatureChange' + command 'shiftColorTemperature', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + + // Commands for capability.Brightness + command 'shiftLevel', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + + // Commands for capability.FirmwareUpdate + command 'updateFirmware' + + preferences { + input( + name: 'helpInfo', type: 'hidden', + title: ''' +
+ IKEA Color White Spectrum Light v5.0.0
+ +
+ ''' + ) + input( + name: 'logLevel', type: 'enum', + title: 'Log verbosity', + description: 'Select what type of messages appear in the "Logs" section.', + options: [ + '1': 'Debug - log everything', + '2': 'Info - log important events', + '3': 'Warning - log events that require attention', + '4': 'Error - log errors' + ], + defaultValue: '1', + required: true + ) + + // Inputs for capability.Switch + input( + name: 'powerOnBehavior', + type: 'enum', + title: 'Power On behaviour', + description: 'Select what happens after a power outage.', + options: [ + 'TURN_POWER_ON': 'Turn power On', + 'TURN_POWER_OFF': 'Turn power Off', + 'RESTORE_PREVIOUS_STATE': 'Restore previous state' + ], + defaultValue: 'RESTORE_PREVIOUS_STATE', + required: true + ) + + // Inputs for capability.ColorControl + + // Inputs for capability.ColorTemperature + input( + name: 'colorTemperatureStep', type: 'enum', + title: 'Color Temperature up/down shift', + description: 'Color Temperature +/- adjust for the shiftColorTemperature() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true + ) + input( + name: 'colorTemperatureChangeRate', type: 'enum', + title: 'Color Temperature change rate', + description: 'Color Temperature +/- adjust for the startColorTemperatureChange() command.', + options: [ + '10': '10% / sec - from hot to cold in 10 seconds', + '20': '20% / sec - from hot to cold in 5 seconds', + '33': '33% / sec - from hot to cold in 3 seconds', + '50': '50% / secs - from hot to cold in 2 seconds', + '100': '100% / sec - from hot to cold in 1 seconds', + ], + defaultValue: '20', + required: true + ) + + // Inputs for capability.Brightness + input( + name: 'levelStep', type: 'enum', + title: 'Brightness up/down shift', + description: 'Brightness +/- adjust for the shiftLevel() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true + ) + input( + name: 'levelChangeRate', type: 'enum', + title: 'Brightness change rate', + description: 'Brightness +/- adjust for the startLevelChange() command.', + options: [ + '10': '10% / sec - from 0% to 100% in 10 seconds', + '20': '20% / sec - from 0% to 100% in 5 seconds', + '33': '33% / sec - from 0% to 100% in 3 seconds', + '50': '50% / secs - from 0% to 100% in 2 seconds', + '100': '100% / sec - from 0% to 100% in 1 seconds', + ], + defaultValue: '20', + required: true + ) + input( + name: 'transitionTime', type: 'enum', + title: 'Brightness transition time', + description: 'Time taken to move to/from the target brightness when device is turned On/Off.', + options: [ + '0': 'Instant', + '5': '0.5 seconds', + '10': '1 second', + '15': '1.5 seconds', + '20': '2 seconds', + '30': '3 seconds', + '40': '4 seconds', + '50': '5 seconds', + '100': '10 seconds' + ], + defaultValue: '5', + required: true + ) + input( + name: 'turnOnBehavior', type: 'enum', + title: 'Turn On behavior', + description: 'Select what happens when the device is turned On.', + options: [ + 'RESTORE_PREVIOUS_LEVEL': 'Restore previous brightness', + 'FIXED_VALUE': 'Always start with the same fixed brightness' + ], + defaultValue: 'RESTORE_PREVIOUS_LEVEL', + required: true + ) + if (turnOnBehavior == 'FIXED_VALUE') { + input( + name: 'onLevelValue', + type: 'number', + title: 'Fixed brightness value', + description: 'Range 1..100', + defaultValue: 50, + range: '1..100', + required: true + ) + } + input( + name: 'prestaging', type: 'bool', + title: 'Pre-staging', + description: 'Set brightness level without turning On the device (for later use).', + defaultValue: false, + required: true + ) + + // Inputs for capability.ZigbeeBindings + input( + name: 'joinGroup', type: 'enum', + title: 'Join a Zigbee group', + description: 'Select a Zigbee group you want to join.', + options: ['0000':'❌ Leave all Zigbee groups', '----':'- - - -'] + GROUPS, + defaultValue: '----', + required: false + ) + } +} + +// =================================================================================================================== +// Implement default methods +// =================================================================================================================== + +// Called when the device is first added +void installed() { + log_warn 'Installing device ...' + log_warn '[IMPORTANT] For battery-powered devices, make sure that you keep your device as close as you can (less than 2inch / 5cm) to your Hubitat hub for at least 30 seconds. Otherwise the device will successfully pair but it won\'t work properly!' +} + +// Called when the "Save Preferences" button is clicked +List updated(boolean auto = false) { + log_info "Saving preferences${auto ? ' (auto)' : ''} ..." + List cmds = [] + + unschedule() + + if (logLevel == null) { + logLevel = '1' + device.updateSetting 'logLevel', [value:logLevel, type:'enum'] + } + if (logLevel == '1') runIn 1800, 'logsOff' + log_info "🛠️ logLevel = ${['1':'Debug', '2':'Info', '3':'Warning', '4':'Error'].get(logLevel)}" + + // Preferences for capability.Switch + if (powerOnBehavior == null) { + powerOnBehavior = 'RESTORE_PREVIOUS_STATE' + device.updateSetting 'powerOnBehavior', [value:powerOnBehavior, type:'enum'] + } + log_info "🛠️ powerOnBehavior = ${powerOnBehavior}" + cmds += zigbee.writeAttribute(0x0006, 0x4003, 0x30, powerOnBehavior == 'TURN_POWER_OFF' ? 0x00 : (powerOnBehavior == 'TURN_POWER_ON' ? 0x01 : 0xFF)) + + // Preferences for capability.ColorControl + cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) + + // Preferences for capability.ColorTemperature + if (colorTemperatureStep == null) { + colorTemperatureStep = '20' + device.updateSetting 'colorTemperatureStep', [value:colorTemperatureStep, type:'enum'] + } + log_info "🛠️ colorTemperatureStep = ${colorTemperatureStep}%" + + if (colorTemperatureChangeRate == null) { + colorTemperatureChangeRate = '20' + device.updateSetting 'colorTemperatureChangeRate', [value:colorTemperatureChangeRate, type:'enum'] + } + log_info "🛠️ colorTemperatureChangeRate = ${colorTemperatureChangeRate}% / second" + + // Regardless of prestaging, enable update of color temperature without the need for the device to be turned On + cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) + + // Preferences for capability.Brightness + if (levelStep == null) { + levelStep = '20' + device.updateSetting 'levelStep', [value:levelStep, type:'enum'] + } + log_info "🛠️ levelStep = ${levelStep}%" + + if (levelChangeRate == null) { + levelChangeRate = '20' + device.updateSetting 'levelChangeRate', [value:levelChangeRate, type:'enum'] + } + log_info "🛠️ levelChangeRate = ${levelChangeRate}% / second" + + if (turnOnBehavior == null) { + turnOnBehavior = 'RESTORE_PREVIOUS_LEVEL' + device.updateSetting 'turnOnBehavior', [value:turnOnBehavior, type:'enum'] + } + log_info "🛠️ turnOnBehavior = ${turnOnBehavior}" + if (turnOnBehavior == 'FIXED_VALUE') { + Integer onLevelValue = onLevelValue == null ? 50 : onLevelValue.intValue() + device.updateSetting 'onLevelValue', [value:onLevelValue, type:'number'] + log_info "🛠️ onLevelValue = ${onLevelValue}%" + Integer lvl = onLevelValue * 2.54 + utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) + } else { + log_debug 'Disabling OnLevel (0xFF)' + cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) + } + + if (transitionTime == null) { + transitionTime = '5' + device.updateSetting 'transitionTime', [value:transitionTime, type:'enum'] + } + log_info "🛠️ transitionTime = ${Integer.parseInt(transitionTime) / 10} second(s)" + cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(transitionTime)) + + if (prestaging == null) { + prestaging = false + device.updateSetting 'prestaging', [value:prestaging, type:'bool'] + } + log_info "🛠️ prestaging = ${prestaging}" + + // If prestaging is true, enable update of brightness without the need for the device to be turned On + cmds += zigbee.writeAttribute(0x0008, 0x000F, 0x18, prestaging ? 0x01 : 0x00) + + // Preferences for capability.HealthCheck + schedule HEALTH_CHECK.schedule, 'healthCheck' + + // Preferences for capability.ZigbeeGroups + if (joinGroup != null && joinGroup != '----') { + if (joinGroup == '0000') { + log_info '🛠️ Leaving all Zigbee groups' + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + } else { + String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') + log_info "🛠️ Joining group: ${joinGroup} (${groupName})" + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group + } + + device.updateSetting 'joinGroup', [value:'----', type:'enum'] + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + } + + if (auto) return cmds + utils_sendZigbeeCommands cmds + return [] +} + +// =================================================================================================================== +// Capabilities helpers +// =================================================================================================================== + +// Handler method for scheduled job to disable debug logging +void logsOff() { + log_info '⏲️ Automatically reverting log level to "Info"' + device.updateSetting 'logLevel', [value:'2', type:'enum'] +} + +// Helpers for capability.HealthCheck +void healthCheck() { + log_debug '⏲️ Automatically running health check' + String healthStatus = state.lastRx == 0 || state.lastRx == null ? 'unknown' : (now() - state.lastRx < Integer.parseInt(HEALTH_CHECK.thereshold) * 1000 ? 'online' : 'offline') + utils_sendEvent name:'healthStatus', value:healthStatus, type:'physical', descriptionText:"Health status is ${healthStatus}" +} + +// =================================================================================================================== +// Implement Capabilities +// =================================================================================================================== + +// capability.Configuration +// Note: This method is also called when the device is initially installed +void configure(boolean auto = false) { + log_warn "Configuring device${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Configure" button immediately after pushing any button on the device in order to first wake it up!' + } + + // Apply preferences first + List cmds = [] + cmds += updated true + + // Clear data (keep firmwareMT information though) + device.data*.key.each { if (it != 'firmwareMT') device.removeDataValue it } + + // Clear state + state.clear() + state.lastTx = 0 + state.lastRx = 0 + state.lastCx = DRIVER_VERSION + + // Configuration for capability.Switch + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes + + // Configuration for capability.ColorControl + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {01} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1) + + // Configuration for capability.ColorTemperature + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0000 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) + state.minMireds = 200 // Will be updated in refresh() + state.maxMireds = 600 // Will be updated in refresh() + + // Configuration for capability.Brightness + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) + + // Configuration for capability.HealthCheck + sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' + sendEvent name:'checkInterval', value:3600, unit:'second', descriptionText:'Health check interval is 3600 seconds' + + // Configuration for capability.PowerSource + sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + + // Query Basic cluster attributes + cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID + cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier + cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + utils_sendZigbeeCommands cmds + + log_info 'Configuration done; refreshing device current state in 7 seconds ...' + runIn 7, 'refresh', [data:true] +} +private void autoConfigure() { + log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" + configure true +} + +// capability.Refresh +void refresh(boolean auto = false) { + log_warn "Refreshing device state${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Refresh" button immediately after pushing any button on the device in order to first wake it up!' + } + + List cmds = [] + + // Refresh for capability.Switch + cmds += zigbee.readAttribute(0x0006, 0x0000) // OnOff + cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior + + // Refresh for capability.ColorControl + cmds += zigbee.readAttribute(0x0300, [0x0000, 0x0001, 0x0008]) // CurrentHue, CurrentSaturation, ColorMode + + // Refresh for capability.ColorTemperature + cmds += zigbee.readAttribute(0x0300, [0x0007, 0x0008]) // ColorTemperatureMireds, ColorMode + cmds += zigbee.readAttribute(0x0300, [0x400B, 0x400C]) // ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds + + // Refresh for capability.Brightness + cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel + + // Refresh for capability.ZigbeeGroups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + utils_sendZigbeeCommands cmds +} + +// Implementation for capability.Switch +void on() { + log_debug 'Sending On command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114301}"]) +} +void off() { + log_debug 'Sending Off command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114300}"]) +} + +void toggle() { + log_debug 'Sending Toggle command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114302}"]) +} + +void onWithTimedOff(BigDecimal onTime = 1) { + Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) + log_debug 'Sending OnWithTimedOff command' + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) +} + +// Implementation for capability.ColorControl +void setColor(Map colormap) { + Integer newHue = colormap.hue > 100 ? 100 : (colormap.hue < 0 ? 0 : colormap.hue) + Integer newSaturation = colormap.saturation > 100 ? 100 : (colormap.saturation < 0 ? 0 : colormap.saturation) + Integer newLevel = colormap.level > 100 ? 100 : (colormap.level < 0 ? 0 : colormap.level) + log_debug "Setting color to hue=${newHue}, saturation=${newSaturation}, level=${newLevel}" + newHue = Math.round(newHue * 2.54) + newSaturation = Math.round(newSaturation * 2.54) + String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + setLevel newLevel +} +void setHue(BigDecimal hue) { + Integer newHue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + log_debug "Setting color hue to ${newHue}%" + newHue = Math.round(newHue * 2.54) + String payload = "${utils_payload newHue, 2} 00 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue +} +void setSaturation(BigDecimal saturation) { + Integer newSaturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) + log_debug "Setting color saturation to ${newSaturation}%" + newSaturation = Math.round(newSaturation * 2.54) + String payload = "${utils_payload newSaturation, 2} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation +} +private void processMultipleColorAttributes(Map msg, String type) { + Map attributes = [:] + attributes[msg.attrInt] = msg.value + msg.additionalAttrs?.each { attributes[Integer.parseInt(it.attrId, 16)] = it.value } + + Integer hue = -1 + Integer saturation = -1 + String colorMode = null + attributes.each { + switch (it.key) { + case 0x0000: + hue = Math.round(Integer.parseInt(it.value, 16) / 2.54) + hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + break + + case 0x0001: + saturation = Math.round(Integer.parseInt(it.value, 16) / 2.54) + saturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) + break + + case 0x0008: + case 0x4001: + colorMode = it.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type + break + } + } + + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type + if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type + + // Update colorName, if the case + if ("${colorMode ?: device.currentValue('colorMode', true)}" == 'RGB') { + Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) + Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) + String colorName = convertHueToGenericColorName colorHue, colorSaturation + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + } + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" +} + +// Implementation for capability.ColorTemperature +void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, BigDecimal duration = 0) { + Integer mireds = Math.round(1000000 / colorTemperature) + mireds = mireds < state.minMireds ? state.minMireds : (mireds > state.maxMireds ? state.maxMireds : mireds) + Integer newColorTemperature = Math.round(1000000 / mireds) + log_debug "Setting color temperature to ${newColorTemperature}k (${mireds} mireds) during ${duration} seconds" + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) + if (level > 0 && duration == 0) setLevel level, duration +} +void startColorTemperatureChange(String direction) { + log_debug "Starting color temperature change ${direction}wards with a rate of ${colorTemperatureChangeRate}% / second" + Integer mode = direction == 'up' ? 0x03 : 0x01 + Integer changeRate = (state.maxMireds - state.minMireds) * Integer.parseInt(colorTemperatureChangeRate) / 100 + String payload = "${utils_payload mode, 2} ${utils_payload changeRate, 4} ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434B ${payload}}"]) +} +void stopColorTemperatureChange() { + log_debug 'Stopping color temperature change' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114347 00 00}"]) +} +void shiftColorTemperature(String direction) { + log_debug "Shifting color temperature ${direction} by ${colorTemperatureStep}%" + Integer mode = direction == 'up' ? 0x03 : 0x01 + Integer stepSize = (state.maxMireds - state.minMireds) * Integer.parseInt(colorTemperatureStep) / 100 + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 4} 0000 ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434C ${payload}}"]) +} +private void processMultipleColorTemperatureAttributes(Map msg, String type) { + Map attributes = [:] + attributes[msg.attrInt] = msg.value + msg.additionalAttrs?.each { attributes[Integer.parseInt(it.attrId, 16)] = it.value } + + Integer mireds = -1 + Integer temperature = -1 + String colorMode = null + String colorName = null + attributes.each { + switch (it.key) { + case 0x0007: + mireds = Integer.parseInt it.value, 16 + temperature = Math.round(1000000 / mireds) + break + + case 0x0008: + case 0x4001: + colorMode = it.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type + break + } + } + + if (temperature >= 0) utils_sendEvent name:'colorTemperature', value:temperature, descriptionText:"Color temperature is ${temperature}K", type:type + + // Update colorName, if the case + if ("${colorMode ?: device.currentValue('colorMode', true)}" == 'CT') { + Integer colorTemperature = temperature >= 0 ? temperature : device.currentValue('colorTemperature', true) + colorName = convertTemperatureToGenericColorName colorTemperature + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + } + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${mireds} (${temperature}K), ${colorName}), ColorMode=${colorMode}" +} + +// Implementation for capability.Brightness +void setLevel(BigDecimal level, BigDecimal duration = 0) { + Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) + log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" + Integer lvl = newLevel * 2.54 + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String command = prestaging == false ? '04' : '00' + String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) +} +void startLevelChange(String direction) { + log_debug "Starting brightness level change ${direction}wards with a rate of ${levelChangeRate}% / second" + Integer mode = direction == 'up' ? 0x00 : 0x01 + Integer rate = Integer.parseInt(levelChangeRate) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload rate, 2}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114301 ${payload}}"]) +} +void stopLevelChange() { + log_debug 'Stopping brightness level change' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114303}"]) +} +void shiftLevel(String direction) { + log_debug "Shifting brightness level ${direction} by ${levelStep}%" + Integer mode = direction == 'up' ? 0x00 : 0x01 + Integer stepSize = Integer.parseInt(levelStep) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 2} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) +} + +// Implementation for capability.HealthCheck +void ping() { + log_warn 'ping ...' + utils_sendZigbeeCommands(zigbee.readAttribute(0x0000, 0x0000)) + log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' + runIn 5, 'pingExecute' +} + +void pingExecute() { + if (state.lastRx == 0) { + log_info 'Did not sent any messages since it was last configured' + return + } + + Date now = new Date(Math.round(now() / 1000) * 1000) + Date lastRx = new Date(Math.round(state.lastRx / 1000) * 1000) + String lastRxAgo = TimeCategory.minus(now, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Sent last message at ${lastRx.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${lastRxAgo} ago)" + + Date thereshold = new Date(Math.round(state.lastRx / 1000 + Integer.parseInt(HEALTH_CHECK.thereshold)) * 1000) + String theresholdAgo = TimeCategory.minus(thereshold, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received for ${theresholdAgo} (hardcoded)" + + String offlineMarkAgo = TimeCategory.minus(thereshold, now).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received until ${thereshold.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${offlineMarkAgo} from now)" +} + +// Implementation for capability.FirmwareUpdate +void updateFirmware() { + log_info 'Looking for firmware updates ...' + if (device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Update Firmware" button immediately after pushing any button on the device in order to first wake it up!' + } + utils_sendZigbeeCommands zigbee.updateFirmware() +} + +// =================================================================================================================== +// Handle incoming Zigbee messages +// =================================================================================================================== + +void parse(String description) { + log_debug "description=[${description}]" + + // Auto-Configure device: configure() was not called for this driver version + if (state.lastCx != DRIVER_VERSION) { + state.lastCx = DRIVER_VERSION + runInMillis 1500, 'autoConfigure' + } + + // Extract msg + Map msg = [:] + if (description.startsWith('zone status')) msg += [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] + if (description.startsWith('enroll request')) msg += [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] + + msg += zigbee.parseDescriptionAsMap description + if (msg.containsKey('endpoint')) msg.endpointInt = Integer.parseInt(msg.endpoint, 16) + if (msg.containsKey('sourceEndpoint')) msg.endpointInt = Integer.parseInt(msg.sourceEndpoint, 16) + if (msg.containsKey('cluster')) msg.clusterInt = Integer.parseInt(msg.cluster, 16) + if (msg.containsKey('command')) msg.commandInt = Integer.parseInt(msg.command, 16) + log_debug "msg=[${msg}]" + + state.lastRx = now() + + // Parse for capability.HealthCheck + if (device.currentValue('healthStatus', true) != 'online') { + utils_sendEvent name:'healthStatus', value:'online', type:'digital', descriptionText:'Health status changed to online' + } + + // If we sent a Zigbee command in the last 3 seconds, we assume that this Zigbee event is a consequence of this driver doing something + // Therefore, we mark this event as "digital" + String type = state.containsKey('lastTx') && (now() - state.lastTx < 3000) ? 'digital' : 'physical' + + switch (msg) { + + // Events for capability.Switch + // =================================================================================================================== + + // Report/Read Attributes: OnOff + case { contains it, [clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String newState = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'switch', value:newState, descriptionText:"Was turned ${newState}", type:type + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "OnOff=${newState}" + return + + // Read Attributes Response: powerOnBehavior + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x4003] }: + String newValue = '' + switch (Integer.parseInt(msg.value, 16)) { + case 0x00: newValue = 'TURN_POWER_OFF'; break + case 0x01: newValue = 'TURN_POWER_ON'; break + case 0xFF: newValue = 'RESTORE_PREVIOUS_STATE'; break + default: log_warn "Received unexpected attribute value: PowerOnBehavior=${msg.value}"; return + } + powerOnBehavior = newValue + device.updateSetting 'powerOnBehavior', [value:newValue, type:'enum'] + utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" + return + + // Other events that we expect but are not usefull for capability.Switch behavior + case { contains it, [clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" + return + case { contains it, [clusterInt:0x0006, commandInt:0x04] }: // Write Attribute Response + case { contains it, [clusterInt:0x0006, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command + return + + // Events for capability.ColorControl + // =================================================================================================================== + + // Report/Read Attributes Reponse: CurrentHue + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0000] }: + + // Report/Read Attributes Reponse: CurrentSaturation + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0001] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0001] }: + + // Report/Read Attributes Reponse: ColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: + + // Report/Read Attributes Reponse: EnhancedColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + processMultipleColorAttributes msg, type + return + + + // Events for capability.ColorTemperature + // =================================================================================================================== + + // Report/Read Attributes Reponse: ColorTemperatureMireds + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0007] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0007] }: + + // Report/Read Attributes Reponse: ColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: + + // Report/Read Attributes Reponse: EnhancedColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + processMultipleColorTemperatureAttributes msg, type + return + + // Read Attributes Reponse: ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: + state.minMireds = Integer.parseInt msg.value, 16 + msg.additionalAttrs?.each { if (it.attrId == '400C') state.maxMireds = Integer.parseInt it.value, 16 } + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.minMireds)}K), ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + return + + // Other events that we expect but are not usefull for capability.ColorTemperature behavior + case { contains it, [clusterInt:0x0300, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" + return + case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) + return + + // Events for capability.Brightness + // =================================================================================================================== + + // Report/Read Attributes Reponse: CurrentLevel + case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) / 2.54) + utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" + return + + // Other events that we expect but are not usefull for capability.Brightness behavior + case { contains it, [clusterInt:0x0008, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" + return + case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) + return + + // Events for capability.HealthCheck + // =================================================================================================================== + + case { contains it, [clusterInt:0x0000, attrInt:0x0000] }: + log_warn '... pong' + return + + // Configuration for capability.PowerSource + // =================================================================================================================== + + // Read Attributes Reponse: PowerSource + case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: + String powerSource = 'unknown' + + // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } + switch (msg.value) { + case '01': + case '02': + case '05': + case '06': + powerSource = 'mains' + break + case '03': + powerSource = 'battery' + break + case '04': + powerSource = 'dc' + break + } + utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" + utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" + return + + // Events for capability.ZigbeeGroups + // =================================================================================================================== + + // Get Group Membership Response Command + case { contains it, [clusterInt:0x0004, commandInt:0x02, direction:'01'] }: + Integer count = Integer.parseInt msg.data[1], 16 + Set groupNames = [] + for (int pos = 0; pos < count; pos++) { + String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" + String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + log_debug "Found group membership: ${groupName}" + groupNames.add groupName + } + state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + if (state.joinGrp.size() == 0) state.remove 'joinGrp' + log_info "Current group membership: ${groupNames ?: 'None'}" + return + + // Add Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // Leave Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Handle common messages (e.g.: received during pairing when we query the device for information) + // --------------------------------------------------------------------------------------------------------------- + + // Device_annce: Welcome back! let's sync state. + case { contains it, [endpointInt:0x00, clusterInt:0x0013, commandInt:0x00] }: + log_warn 'Rejoined the Zigbee mesh; refreshing device state in 3 seconds ...' + runIn 3, 'refresh' + return + + // Report/Read Attributes Response (Basic cluster) + case { contains it, [clusterInt:0x0000, commandInt:0x01] }: + case { contains it, [clusterInt:0x0000, commandInt:0x0A] }: + utils_zigbeeDataValue(msg.attrInt, msg.value) + msg.additionalAttrs?.each { utils_zigbeeDataValue(it.attrInt, it.value) } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "cluster=0x${msg.cluster}, attribute=0x${msg.attrId}, value=${msg.value}" + return + + // Mgmt_Leave_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8034, commandInt:0x00] }: + log_warn 'Device is leaving the Zigbee mesh. See you later, Aligator!' + return + + // Ignore the following Zigbee messages + case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) + case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response + case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command + utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + return + + case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8004, commandInt:0x00] }: // ZDP: Simple_Desc_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8005, commandInt:0x00] }: // ZDP: Active_EP_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x0006, commandInt:0x00] }: // ZDP: MatchDescriptorRequest + case { contains it, [endpointInt:0x00, clusterInt:0x801F, commandInt:0x00] }: // ZDP: Parent_annce_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8021, commandInt:0x00] }: // ZDP: Mgmt_Bind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8022, commandInt:0x00] }: // ZDP: Mgmt_Unbind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify + utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Unexpected Zigbee message + // --------------------------------------------------------------------------------------------------------------- + default: + log_error "Sent unexpected Zigbee message: description=${description}, msg=${msg}" + } +} + +// =================================================================================================================== +// Logging helpers (something like this should be part of the SDK and not implemented by each driver) +// =================================================================================================================== + +private void log_debug(String message) { + if (logLevel == '1') log.debug "${device.displayName} ${message.uncapitalize()}" +} +private void log_info(String message) { + if (logLevel <= '2') log.info "${device.displayName} ${message.uncapitalize()}" +} +private void log_warn(String message) { + if (logLevel <= '3') log.warn "${device.displayName} ${message.uncapitalize()}" +} +private void log_error(String message) { + log.error "${device.displayName} ${message.uncapitalize()}" +} + +// =================================================================================================================== +// Helper methods (keep them simple, keep them dumb) +// =================================================================================================================== + +private void utils_sendZigbeeCommands(List cmds) { + if (cmds.empty) return + List send = delayBetween(cmds.findAll { !it.startsWith('delay') }, 1000) + log_debug "◀ Sending Zigbee messages: ${send}" + state.lastTx = now() + sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) +} +private void utils_sendEvent(Map event) { + if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + log_info "${event.descriptionText} [${event.type}]" + } else { + log_debug "${event.descriptionText} [${event.type}]" + } + sendEvent event +} +private void utils_dataValue(String key, String value) { + if (value == null || value == '') return + log_debug "Update data value: ${key}=${value}" + updateDataValue key, value +} +private void utils_zigbeeDataValue(Integer attrInt, String value) { + switch (attrInt) { + case 0x0001: utils_dataValue 'application', value; return + case 0x0003: utils_dataValue 'hwVersion', value; return + case 0x0004: utils_dataValue 'manufacturer', value; return + case 0x000A: utils_dataValue 'type', "${value ? (value.split('') as List).collate(2).collect { "${Integer.parseInt(it.join(), 16) as char}" }.join() : ''}"; return + case 0x0005: utils_dataValue 'model', value; return + case 0x4000: utils_dataValue 'softwareBuild', value; return + } +} +private void utils_processedZclMessage(String type, String details) { + log_debug "▶ Processed ZCL message: type=${type}, ${details}" +} +private void utils_processedZdpMessage(String type, String details) { + log_debug "▶ Processed ZDO message: type=${type}, ${details}" +} +private String utils_payload(String value) { + return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') +} +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} + +// switch/case syntactic sugar +@CompileStatic private boolean contains(Map msg, Map spec) { + return msg.keySet().containsAll(spec.keySet()) && spec.every { it.value == msg[it.key] } +} diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index 13afc5a..b2fdb9d 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -536,7 +536,7 @@ void parse(String description) { // Report/Read Attributes Reponse: CurrentLevel case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: - Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) / 2.54) utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index 29e3dce..62e1e30 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -190,11 +190,11 @@ void configure(boolean auto = false) { state.lastCx = DRIVER_VERSION // Configuration for devices.Ikea_E2134 - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0400 {${device.zigbeeId}} {}" // Illuminance Measurement cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0400 0x0000 0x18 0x0000 0x4650 {01} {}" // Report Illuminance/MeasuredValue (uint16) at least every 5 hours (Δ = 0) + cmds += "zdo bind 0x${device.deviceNetworkId} 0x03 0x01 0x0400 {${device.zigbeeId}} {}" // Illuminance Measurement cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x03 0x0400 0x0000 0x18 0x0000 0x4650 {01} {}" // Report Illuminance/MeasuredValue (uint16) at least every 5 hours (Δ = 0) - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0406 {${device.zigbeeId}} {}" // Occupancy Sensing cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0406 0x0000 0x21 0x0000 0x4650 {01} {}" // Report Illuminance/MeasuredValue (uint16) at least every 5 hours (Δ = 0) + cmds += "zdo bind 0x${device.deviceNetworkId} 0x02 0x01 0x0406 {${device.zigbeeId}} {}" // Occupancy Sensing cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x02 0x0406 0x0000 0x21 0x0000 0x4650 {01} {}" // Report Occupancy/MeasuredValue (uint16) at least every 5 hours (Δ = 0) // Configuration for capability.Battery cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0001 {${device.zigbeeId}} {}" // Power Configuration cluster @@ -231,6 +231,10 @@ void refresh(boolean auto = false) { List cmds = [] + // Refresh for devices.Ikea_E2134 + cmds += zigbee.readAttribute(0x0406, 0x0000, [destEndpoint:0x02]) // Occupancy/MeasuredValue + cmds += zigbee.readAttribute(0x0400, 0x0000, [destEndpoint:0x03]) // Illuminance/MeasuredValue + // Refresh for capability.Battery cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index 8e3d3b7..db139d3 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -35,7 +35,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // Type LED2101G4: 1.1.003 (117C-2204-00011003) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // Type LED2101G4, LED1949C5: 1.1.003 (117C-2204-00011003) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -413,10 +413,8 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior // Refresh for capability.ColorTemperature - cmds += zigbee.readAttribute(0x0300, 0x0008) // ColorMode - cmds += zigbee.readAttribute(0x0300, 0x0007) // ColorTemperatureMireds - cmds += zigbee.readAttribute(0x0300, 0x400B) // ColorTemperaturePhysicalMinMireds - cmds += zigbee.readAttribute(0x0300, 0x400C) // ColorTemperaturePhysicalMaxMireds + cmds += zigbee.readAttribute(0x0300, [0x0007, 0x0008]) // ColorTemperatureMireds, ColorMode + cmds += zigbee.readAttribute(0x0300, [0x400B, 0x400C]) // ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds // Refresh for capability.Brightness cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel @@ -478,6 +476,41 @@ void shiftColorTemperature(String direction) { String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 4} 0000 ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434C ${payload}}"]) } +private void processMultipleColorTemperatureAttributes(Map msg, String type) { + Map attributes = [:] + attributes[msg.attrInt] = msg.value + msg.additionalAttrs?.each { attributes[Integer.parseInt(it.attrId, 16)] = it.value } + + Integer mireds = -1 + Integer temperature = -1 + String colorMode = null + String colorName = null + attributes.each { + switch (it.key) { + case 0x0007: + mireds = Integer.parseInt it.value, 16 + temperature = Math.round(1000000 / mireds) + break + + case 0x0008: + case 0x4001: + colorMode = it.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type + break + } + } + + if (temperature >= 0) utils_sendEvent name:'colorTemperature', value:temperature, descriptionText:"Color temperature is ${temperature}K", type:type + + // Update colorName, if the case + if ("${colorMode ?: device.currentValue('colorMode', true)}" == 'CT') { + Integer colorTemperature = temperature >= 0 ? temperature : device.currentValue('colorTemperature', true) + colorName = convertTemperatureToGenericColorName colorTemperature + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + } + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${mireds} (${temperature}K), ${colorName}), ColorMode=${colorMode}" +} // Implementation for capability.Brightness void setLevel(BigDecimal level, BigDecimal duration = 0) { @@ -622,32 +655,22 @@ void parse(String description) { // Report/Read Attributes Reponse: ColorTemperatureMireds case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0007] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0007] }: - Integer mireds = Integer.parseInt "${msg.value}", 16 - Integer colorTemperature = Math.round(1000000 / mireds) - String colorName = convertTemperatureToGenericColorName colorTemperature - utils_sendEvent name:'colorTemperature', value:colorTemperature, descriptionText:"Color temperature is ${colorTemperature}K", type:'digital' - utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:'digital' - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${msg.value}(${mireds} mireds, ${colorTemperature}K, ${colorName})" - return // Report/Read Attributes Reponse: ColorMode case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: - String colorMode = msg.value == '02' ? 'CT' : 'RGB' - utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:'digital' - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorMode=${msg.value}" - return - // Read Attributes Reponse: ColorTemperaturePhysicalMinMireds - case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: - state.minMireds = Integer.parseInt "${msg.value}", 16 - utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + // Report/Read Attributes Reponse: EnhancedColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + processMultipleColorTemperatureAttributes msg, type return - // Read Attributes Reponse: ColorTemperaturePhysicalMaxMireds - case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400C] }: - state.maxMireds = Integer.parseInt "${msg.value}", 16 - utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" + // Read Attributes Reponse: ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: + state.minMireds = Integer.parseInt msg.value, 16 + msg.additionalAttrs?.each { if (it.attrId == '400C') state.maxMireds = Integer.parseInt it.value, 16 } + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.minMireds)}K), ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" return // Other events that we expect but are not usefull for capability.ColorTemperature behavior @@ -663,7 +686,7 @@ void parse(String description) { // Report/Read Attributes Reponse: CurrentLevel case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: - Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) / 2.54) utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 2ae9220..1c0fa43 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -28,6 +28,7 @@ Outlets: Lights: * [Dimmable Light](#dimmable-light) * [White Spectrum Light](#white-spectrum-light) +* [Color White Spectrum Light](#color-white-spectrum-light) Other: * [Starkvind Air Purifier (E2006)](#starkvind-air-purifier-e2006) @@ -579,7 +580,7 @@ Below you can find the details of each device, including the features and pairin * Configure brightness level when turned on (Always the same fixed value, Restore last level) * Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. * Health status: online / offline -* Refresh switch state on demand +* Refresh light state on demand * Can be member of Zigbee groups #### Tested devices @@ -604,9 +605,12 @@ Below you can find the details of each device, including the features and pairin * Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. * Pre-staging: Set color temperature when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color temperature. * Health status: online / offline -* Refresh switch state on demand +* Refresh light state on demand * Can be member of Zigbee groups +#### Known Issues +* Color temperature and brightness level can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. + #### Tested devices * LED2101G4: Tradfri Bulb E14 WS Globe 470lm * LED1949C5: Tradfri Bulb E14 WS Candle Opal 470lm @@ -619,6 +623,42 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your White Spectrum Light. +### Color White Spectrum Light + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_CWS-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* Commands: On, Off, Toggle, On with Timed Off +* Brightness control: Set brightness level, Start/Stop brightness level change, Step brightness level up/down +* Color temperature (CT) control: Set color temperature, Start/Stop color temperature change, Step color temperature up/down +* Color (RGB) control: Set color hue and/or saturation, display color name and colo mode +* Configure what happens after a power outage (Power On, Power Off, Restore previous state) +* Configure brightness level when turned on (Always the same fixed value, Restore last level) +* Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. +* Pre-staging: Set color temperature when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color temperature. +* Pre-staging: Set color when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color. +* Health status: online / offline +* Refresh light state on demand +* Can be member of Zigbee groups + +#### Known Issues +* Color temperature and brightness level can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. + +#### Tested devices +* L2112: Ormanas LED Strip + +#### Pairing Instructions +1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Put the light in pairing mode. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your White Spectrum Light. + + ### Aqara Dual Relay Module T2 (DCM-K01) | Parameter | Details | diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 455e1ae..47a95e6 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-04-??", @@ -44,6 +44,14 @@ "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_WS-Light.groovy", "required": false }, + { + "id" : "6e9663e0-c76b-42e3-bbd4-01657e23adba", + "name": "IKEA Color White Spectrum Light", + "description": "Zigbee driver for IKEA Color White Spectrum Lights", + "namespace": "dandanache", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_CWS-Light.groovy", + "required": false + }, { "id" : "d5e1cf42-b933-466b-b95b-cd0c338db077", "name": "IKEA Parasoll Door/Window Sensor (E2013)", diff --git a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy index 2e40792..201098c 100644 --- a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy @@ -185,7 +185,7 @@ cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel // Report/Read Attributes Reponse: CurrentLevel case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: - Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) * 100 / 254) + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) / 2.54) utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy new file mode 100644 index 0000000..9eb3b71 --- /dev/null +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -0,0 +1,130 @@ +{{!--------------------------------------------------------------------------}} +{{# @definition }} +capability 'ColorControl' +{{/ @definition }} +{{!--------------------------------------------------------------------------}} +{{# @inputs }} + +// Inputs for capability.ColorControl +{{/ @inputs }} +{{!--------------------------------------------------------------------------}} +{{# @commands }} + +// Commands for capability.ColorControl +{{/ @commands }} +{{!--------------------------------------------------------------------------}} +{{# @implementation }} + +// Implementation for capability.ColorControl +void setColor(Map colormap) { + Integer newHue = colormap.hue > 100 ? 100 : (colormap.hue < 0 ? 0 : colormap.hue) + Integer newSaturation = colormap.saturation > 100 ? 100 : (colormap.saturation < 0 ? 0 : colormap.saturation) + Integer newLevel = colormap.level > 100 ? 100 : (colormap.level < 0 ? 0 : colormap.level) + log_debug "Setting color to hue=${newHue}, saturation=${newSaturation}, level=${newLevel}" + newHue = Math.round(newHue * 2.54) + newSaturation = Math.round(newSaturation * 2.54) + String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + setLevel newLevel +} +void setHue(BigDecimal hue) { + Integer newHue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + log_debug "Setting color hue to ${newHue}%" + newHue = Math.round(newHue * 2.54) + String payload = "${utils_payload newHue, 2} 00 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue +} +void setSaturation(BigDecimal saturation) { + Integer newSaturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) + log_debug "Setting color saturation to ${newSaturation}%" + newSaturation = Math.round(newSaturation * 2.54) + String payload = "${utils_payload newSaturation, 2} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation +} +private void processMultipleColorAttributes(Map msg, String type) { + Map attributes = [:] + attributes[msg.attrInt] = msg.value + msg.additionalAttrs?.each { attributes[Integer.parseInt(it.attrId, 16)] = it.value } + + Integer hue = -1 + Integer saturation = -1 + String colorMode = null + attributes.each { + switch (it.key) { + case 0x0000: + hue = Math.round(Integer.parseInt(it.value, 16) / 2.54) + hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + break + + case 0x0001: + saturation = Math.round(Integer.parseInt(it.value, 16) / 2.54) + saturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) + break + + case 0x0008: + case 0x4001: + colorMode = it.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type + break + } + } + + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type + if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type + + // Update colorName, if the case + if ("${colorMode ?: device.currentValue('colorMode', true)}" == 'RGB') { + Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) + Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) + String colorName = convertHueToGenericColorName colorHue, colorSaturation + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + } + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" +} +{{/ @implementation }} +{{!--------------------------------------------------------------------------}} +{{# @updated }} + +// Preferences for capability.ColorControl +cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) +{{/ @updated }} +{{!--------------------------------------------------------------------------}} +{{# @configure }} + +// Configuration for capability.ColorControl +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {01} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1) +{{/ @configure }} +{{!--------------------------------------------------------------------------}} +{{# @refresh }} + +// Refresh for capability.ColorControl +cmds += zigbee.readAttribute(0x0300, [0x0000, 0x0001, 0x0008]) // CurrentHue, CurrentSaturation, ColorMode +{{/ @refresh }} +{{!--------------------------------------------------------------------------}} +{{# @events }} + +// Events for capability.ColorControl +// =================================================================================================================== + +// Report/Read Attributes Reponse: CurrentHue +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0000] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0000] }: + +// Report/Read Attributes Reponse: CurrentSaturation +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0001] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0001] }: + +// Report/Read Attributes Reponse: ColorMode +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: + +// Report/Read Attributes Reponse: EnhancedColorMode +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + processMultipleColorAttributes msg, type + return + +{{/ @events }} +{{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy index 8746cc9..81c61e8 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -70,6 +70,41 @@ void shiftColorTemperature(String direction) { String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 4} 0000 ${utils_payload state.minMireds, 4} ${utils_payload state.maxMireds, 4} 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11434C ${payload}}"]) } +private void processMultipleColorTemperatureAttributes(Map msg, String type) { + Map attributes = [:] + attributes[msg.attrInt] = msg.value + msg.additionalAttrs?.each { attributes[Integer.parseInt(it.attrId, 16)] = it.value } + + Integer mireds = -1 + Integer temperature = -1 + String colorMode = null + String colorName = null + attributes.each { + switch (it.key) { + case 0x0007: + mireds = Integer.parseInt it.value, 16 + temperature = Math.round(1000000 / mireds) + break + + case 0x0008: + case 0x4001: + colorMode = it.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type + break + } + } + + if (temperature >= 0) utils_sendEvent name:'colorTemperature', value:temperature, descriptionText:"Color temperature is ${temperature}K", type:type + + // Update colorName, if the case + if ("${colorMode ?: device.currentValue('colorMode', true)}" == 'CT') { + Integer colorTemperature = temperature >= 0 ? temperature : device.currentValue('colorTemperature', true) + colorName = convertTemperatureToGenericColorName colorTemperature + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + } + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${mireds} (${temperature}K), ${colorName}), ColorMode=${colorMode}" +} {{/ @implementation }} {{!--------------------------------------------------------------------------}} {{# @updated }} @@ -103,10 +138,8 @@ state.maxMireds = 600 // Will be updated in refresh() {{# @refresh }} // Refresh for capability.ColorTemperature -cmds += zigbee.readAttribute(0x0300, 0x0008) // ColorMode -cmds += zigbee.readAttribute(0x0300, 0x0007) // ColorTemperatureMireds -cmds += zigbee.readAttribute(0x0300, 0x400B) // ColorTemperaturePhysicalMinMireds -cmds += zigbee.readAttribute(0x0300, 0x400C) // ColorTemperaturePhysicalMaxMireds +cmds += zigbee.readAttribute(0x0300, [0x0007, 0x0008]) // ColorTemperatureMireds, ColorMode +cmds += zigbee.readAttribute(0x0300, [0x400B, 0x400C]) // ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds {{/ @refresh }} {{!--------------------------------------------------------------------------}} {{# @events }} @@ -117,32 +150,22 @@ cmds += zigbee.readAttribute(0x0300, 0x400C) // ColorTemperaturePhysicalMaxMired // Report/Read Attributes Reponse: ColorTemperatureMireds case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0007] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0007] }: - Integer mireds = Integer.parseInt "${msg.value}", 16 - Integer colorTemperature = Math.round(1000000 / mireds) - String colorName = convertTemperatureToGenericColorName colorTemperature - utils_sendEvent name:'colorTemperature', value:colorTemperature, descriptionText:"Color temperature is ${colorTemperature}K", type:'digital' - utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:'digital' - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorTemperatureMireds=${msg.value}(${mireds} mireds, ${colorTemperature}K, ${colorName})" - return // Report/Read Attributes Reponse: ColorMode case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: - String colorMode = msg.value == '02' ? 'CT' : 'RGB' - utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:'digital' - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ColorMode=${msg.value}" - return -// Read Attributes Reponse: ColorTemperaturePhysicalMinMireds -case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: - state.minMireds = Integer.parseInt "${msg.value}", 16 - utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" +// Report/Read Attributes Reponse: EnhancedColorMode +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + processMultipleColorTemperatureAttributes msg, type return -// Read Attributes Reponse: ColorTemperaturePhysicalMaxMireds -case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400C] }: - state.maxMireds = Integer.parseInt "${msg.value}", 16 - utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" +// Read Attributes Reponse: ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds +case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: + state.minMireds = Integer.parseInt msg.value, 16 + msg.additionalAttrs?.each { if (it.attrId == '400C') state.maxMireds = Integer.parseInt it.value, 16 } + utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.minMireds)}K), ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" return // Other events that we expect but are not usefull for capability.ColorTemperature behavior diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml new file mode 100644 index 0000000..7891e3a --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -0,0 +1,31 @@ +device: + model: IKEA Color White Spectrum Light + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_CWS-Light.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_CWS-Light.webp + links: + - name: device details + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#cws-light + - name: community page + url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 + + fingerprints: + - type: L2112 + firmwares: 1.1.10 (117C-2804-01010010) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' + + capabilities: + - file: src/capabilities/Switch.groovy + params: + powerOnBehavior: true + onWithTimedOff: true + - file: src/capabilities/ColorControl.groovy + - file: src/capabilities/ColorTemperature.groovy + - file: src/capabilities/Brightness.groovy + - file: src/capabilities/HealthCheck.groovy + params: + schedule: 0 0 0/1 ? * * * + checkInterval: 3600 # every hour + thereshold: 3600 # report device as offline if no message was received in the last 60 minutes (device should report On/Off status every 10 minutes) + - file: src/capabilities/PowerSource.groovy + - file: src/capabilities/ZigbeeGroups.groovy + - file: src/capabilities/FirmwareUpdate.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2134/E2134.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2134/E2134.groovy index 3d414ec..b5fd24f 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2134/E2134.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2134/E2134.groovy @@ -2,13 +2,20 @@ {{# @configure }} // Configuration for devices.Ikea_E2134 -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0400 {${device.zigbeeId}} {}" // Illuminance Measurement cluster -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0400 0x0000 0x18 0x0000 0x4650 {01} {}" // Report Illuminance/MeasuredValue (uint16) at least every 5 hours (Δ = 0) +cmds += "zdo bind 0x${device.deviceNetworkId} 0x03 0x01 0x0400 {${device.zigbeeId}} {}" // Illuminance Measurement cluster +cmds += "he cr 0x${device.deviceNetworkId} 0x03 0x0400 0x0000 0x18 0x0000 0x4650 {01} {}" // Report Illuminance/MeasuredValue (uint16) at least every 5 hours (Δ = 0) -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0406 {${device.zigbeeId}} {}" // Occupancy Sensing cluster -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0406 0x0000 0x21 0x0000 0x4650 {01} {}" // Report Illuminance/MeasuredValue (uint16) at least every 5 hours (Δ = 0) +cmds += "zdo bind 0x${device.deviceNetworkId} 0x02 0x01 0x0406 {${device.zigbeeId}} {}" // Occupancy Sensing cluster +cmds += "he cr 0x${device.deviceNetworkId} 0x02 0x0406 0x0000 0x21 0x0000 0x4650 {01} {}" // Report Occupancy/MeasuredValue (uint16) at least every 5 hours (Δ = 0) {{/ @configure }} {{!--------------------------------------------------------------------------}} +{{# @refresh }} + +// Refresh for devices.Ikea_E2134 +cmds += zigbee.readAttribute(0x0406, 0x0000, [destEndpoint:0x02]) // Occupancy/MeasuredValue +cmds += zigbee.readAttribute(0x0400, 0x0000, [destEndpoint:0x03]) // Illuminance/MeasuredValue +{{/ @refresh }} +{{!--------------------------------------------------------------------------}} {{# @events }} // Events for devices.Ikea_E2134 From b9297040c64f0082c15deadb4fd9b0e2950dc089 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 14 Apr 2024 12:42:04 +0300 Subject: [PATCH 03/26] Small fixes --- ikea-zigbee-drivers/Aqara_DCM-K01.groovy | 10 +-- ikea-zigbee-drivers/CHANGELOG.md | 3 + ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 68 ++++++------------- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 55 ++++++--------- ikea-zigbee-drivers/Ikea_E1603.groovy | 47 ++++--------- ikea-zigbee-drivers/Ikea_E1743.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E1745.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E1766.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E1810.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E1812.groovy | 28 +++----- ikea-zigbee-drivers/Ikea_E1836.groovy | 45 ++++-------- ikea-zigbee-drivers/Ikea_E2002.groovy | 28 +++----- ikea-zigbee-drivers/Ikea_E2006.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E2013.groovy | 30 +++----- ikea-zigbee-drivers/Ikea_E2112.groovy | 24 ++----- ikea-zigbee-drivers/Ikea_E2123.groovy | 28 +++----- ikea-zigbee-drivers/Ikea_E2134.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E2201.groovy | 26 ++----- ikea-zigbee-drivers/Ikea_E2202.groovy | 28 +++----- ikea-zigbee-drivers/Ikea_E2204.groovy | 45 ++++-------- ikea-zigbee-drivers/Ikea_E2213.groovy | 24 ++----- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 55 ++++++--------- ikea-zigbee-drivers/Legrand_741811.groovy | 45 ++++-------- ikea-zigbee-drivers/Makefile | 2 +- ikea-zigbee-drivers/Philips_RDM001.groovy | 26 ++----- ikea-zigbee-drivers/Philips_RWL022.groovy | 26 ++----- ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy | 14 ++-- ikea-zigbee-drivers/packageManifest.json | 18 ++--- ikea-zigbee-drivers/src/blueprint.groovy | 9 +-- .../src/capabilities/Brightness.groovy | 4 +- .../src/capabilities/ColorControl.groovy | 20 +----- .../src/capabilities/ColorTemperature.groovy | 5 +- .../src/capabilities/HealthCheck.groovy | 1 - .../src/capabilities/IAS.groovy | 4 +- .../src/capabilities/PowerSource.groovy | 14 ++-- .../src/capabilities/Switch.groovy | 6 +- .../src/capabilities/ZigbeeBindings.groovy | 2 +- .../src/capabilities/ZigbeeGroups.groovy | 15 ++-- .../src/devices/Ikea_WS-Light/config.yaml | 6 +- 39 files changed, 280 insertions(+), 637 deletions(-) diff --git a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy index ff81aba..0f3d6f0 100644 --- a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy +++ b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy @@ -38,7 +38,7 @@ metadata { capability 'PushableButton' capability 'HealthCheck' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0B04,0702,0005,0004,0003,0012,0000,0006,FCC0', outClusters:'0019,000A', model:'lumi.switch.acn047', manufacturer:'Aqara' // For firmware: Unknown + fingerprint profileId:'0104', endpointId:'01', inClusters:'0B04,0702,0005,0004,0003,0012,0000,0006,FCC0', outClusters:'0019,000A', model:'lumi.switch.acn047', manufacturer:'Aqara' // For firmware: Unknown // Attributes for devices.Aqara_DCM-K01 attribute 'powerOutageCount', 'number' @@ -67,12 +67,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -394,7 +389,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 5206c5b..0a2e5ea 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver +### Fixed +- E2134: Fix bindings and reporting to use the correct endpoints - `@alexandruy2k` + ## [4.1.0] - 2024-04-06 ### Added diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 96b9ecd..09ec72b 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -36,7 +36,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // Type L2112: 1.1.10 (117C-2804-01010010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -46,8 +46,6 @@ metadata { command 'toggle' command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] - // Commands for capability.ColorControl - // Commands for capability.ColorTemperature command 'startColorTemperatureChange', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] command 'stopColorTemperatureChange' @@ -76,12 +74,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -92,17 +85,11 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) - // Inputs for capability.ColorControl - // Inputs for capability.ColorTemperature input( name: 'colorTemperatureStep', type: 'enum', @@ -313,15 +300,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -395,7 +381,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -435,7 +421,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -471,7 +457,7 @@ void setColor(Map colormap) { newHue = Math.round(newHue * 2.54) newSaturation = Math.round(newSaturation * 2.54) String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation setLevel newLevel } void setHue(BigDecimal hue) { @@ -479,14 +465,14 @@ void setHue(BigDecimal hue) { log_debug "Setting color hue to ${newHue}%" newHue = Math.round(newHue * 2.54) String payload = "${utils_payload newHue, 2} 00 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue } void setSaturation(BigDecimal saturation) { Integer newSaturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) log_debug "Setting color saturation to ${newSaturation}%" newSaturation = Math.round(newSaturation * 2.54) String payload = "${utils_payload newSaturation, 2} 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] @@ -502,12 +488,10 @@ private void processMultipleColorAttributes(Map msg, String type) { hue = Math.round(Integer.parseInt(it.value, 16) / 2.54) hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) break - case 0x0001: saturation = Math.round(Integer.parseInt(it.value, 16) / 2.54) saturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) break - case 0x0008: case 0x4001: colorMode = it.value == '02' ? 'CT' : 'RGB' @@ -526,7 +510,6 @@ private void processMultipleColorAttributes(Map msg, String type) { String colorName = convertHueToGenericColorName colorHue, colorSaturation utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type } - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" } @@ -536,7 +519,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big mireds = mireds < state.minMireds ? state.minMireds : (mireds > state.maxMireds ? state.maxMireds : mireds) Integer newColorTemperature = Math.round(1000000 / mireds) log_debug "Setting color temperature to ${newColorTemperature}k (${mireds} mireds) during ${duration} seconds" - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) if (level > 0 && duration == 0) setLevel level, duration @@ -574,7 +557,6 @@ private void processMultipleColorTemperatureAttributes(Map msg, String type) { mireds = Integer.parseInt it.value, 16 temperature = Math.round(1000000 / mireds) break - case 0x0008: case 0x4001: colorMode = it.value == '02' ? 'CT' : 'RGB' @@ -600,7 +582,7 @@ void setLevel(BigDecimal level, BigDecimal duration = 0) { Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" Integer lvl = newLevel * 2.54 - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String command = prestaging == false ? '04' : '00' String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) @@ -631,7 +613,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -753,7 +734,6 @@ void parse(String description) { processMultipleColorAttributes msg, type return - // Events for capability.ColorTemperature // =================================================================================================================== @@ -782,7 +762,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0300, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" return - case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) + case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.Brightness @@ -800,7 +780,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return - case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) + case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.HealthCheck @@ -819,18 +799,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -845,11 +819,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -858,7 +832,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -866,7 +840,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index b2fdb9d..c424c25 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -33,10 +33,10 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // Type 10EU-IL-1 (Tradfri LED Driver 10W): 1.2.245 (117C-4101-12245572) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // Type 10EU-IL-1 (Tradfri LED Driver 10W): 2.3.086 (117C-4101-23086631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // Type 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // Type 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 1.2.245 (117C-4101-12245572) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 2.3.086 (117C-4101-23086631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -69,12 +69,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -85,11 +80,7 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) @@ -261,15 +252,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -333,7 +323,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -366,7 +356,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -398,7 +388,7 @@ void setLevel(BigDecimal level, BigDecimal duration = 0) { Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" Integer lvl = newLevel * 2.54 - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String command = prestaging == false ? '04' : '00' String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) @@ -429,7 +419,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -545,7 +534,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return - case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) + case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.HealthCheck @@ -564,18 +553,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -590,11 +573,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -603,7 +586,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -611,7 +594,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Ikea_E1603.groovy b/ikea-zigbee-drivers/Ikea_E1603.groovy index 5aff4c3..325a3c2 100644 --- a/ikea-zigbee-drivers/Ikea_E1603.groovy +++ b/ikea-zigbee-drivers/Ikea_E1603.groovy @@ -32,8 +32,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.0.024 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.3.089 (117C-1101-23089631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.0.024 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.3.089 (117C-1101-23089631) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -63,12 +63,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -79,11 +74,7 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) @@ -139,15 +130,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -207,7 +197,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -237,7 +227,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -271,7 +261,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -388,18 +377,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -414,11 +397,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -427,7 +410,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -435,7 +418,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index dcc55d5..b82d1c0 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -34,7 +34,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -60,12 +60,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -185,7 +180,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -218,7 +213,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -229,7 +224,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -409,18 +403,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E1745.groovy b/ikea-zigbee-drivers/Ikea_E1745.groovy index 69d7f37..809d3c4 100644 --- a/ikea-zigbee-drivers/Ikea_E1745.groovy +++ b/ikea-zigbee-drivers/Ikea_E1745.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'TRADFRI motion sensor', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C8-24040005) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'TRADFRI motion sensor', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C8-24040005) // Attributes for devices.Ikea_E1745 attribute 'requestedBrightness', 'number' // Syncs with the brightness option on device (◐/⭘) @@ -62,12 +62,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -247,7 +242,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -276,7 +271,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -293,7 +288,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -447,18 +441,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E1766.groovy b/ikea-zigbee-drivers/Ikea_E1766.groovy index d2b9b4b..ce0271a 100644 --- a/ikea-zigbee-drivers/Ikea_E1766.groovy +++ b/ikea-zigbee-drivers/Ikea_E1766.groovy @@ -34,7 +34,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -60,12 +60,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -183,7 +178,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -216,7 +211,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -227,7 +222,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -400,18 +394,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index d0c5db1..2001cbb 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -37,7 +37,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C1-24040005) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C1-24040005) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -63,12 +63,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -189,7 +184,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -222,7 +217,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -233,7 +228,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -438,18 +432,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E1812.groovy b/ikea-zigbee-drivers/Ikea_E1812.groovy index 271d924..949339b 100644 --- a/ikea-zigbee-drivers/Ikea_E1812.groovy +++ b/ikea-zigbee-drivers/Ikea_E1812.groovy @@ -34,8 +34,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 2.3.015 (117C-11C6-23015631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 2.3.015 (117C-11C6-23015631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -61,12 +61,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -185,7 +180,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -218,7 +213,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -239,7 +234,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -423,18 +417,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E1836.groovy b/ikea-zigbee-drivers/Ikea_E1836.groovy index 32668bf..8090522 100644 --- a/ikea-zigbee-drivers/Ikea_E1836.groovy +++ b/ikea-zigbee-drivers/Ikea_E1836.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'ASKVADER on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 1.0.002 (117C-110D-00010002) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'ASKVADER on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 1.0.002 (117C-110D-00010002) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -62,12 +62,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -78,11 +73,7 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) @@ -138,15 +129,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -206,7 +196,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -236,7 +226,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -270,7 +260,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -387,18 +376,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -413,11 +396,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -426,7 +409,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -434,7 +417,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index 135ef4e..c9473b8 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -36,8 +36,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.024 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 2.4.5 (117C-11CB-02040005) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.024 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 2.4.5 (117C-11CB-02040005) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -63,12 +63,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -189,7 +184,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -222,7 +217,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -233,7 +228,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -447,18 +441,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2006.groovy b/ikea-zigbee-drivers/Ikea_E2006.groovy index 2953cc8..8e63553 100644 --- a/ikea-zigbee-drivers/Ikea_E2006.groovy +++ b/ikea-zigbee-drivers/Ikea_E2006.groovy @@ -35,8 +35,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.0.033 (117C-110C-00010033) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7C,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.1.001 (117C-110C-00011001) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.0.033 (117C-110C-00010033) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7C,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.1.001 (117C-110C-00011001) // Attributes for devices.Ikea_E2006 attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive groups', 'unhealthy', 'hazardous'] @@ -72,12 +72,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -249,7 +244,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -384,7 +379,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -581,18 +575,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2013.groovy b/ikea-zigbee-drivers/Ikea_E2013.groovy index 43d0c52..c01e5b8 100644 --- a/ikea-zigbee-drivers/Ikea_E2013.groovy +++ b/ikea-zigbee-drivers/Ikea_E2013.groovy @@ -35,7 +35,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'PARASOLL Door/Window Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.19 (117C-3277-01000019) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'PARASOLL Door/Window Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.19 (117C-3277-01000019) // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -64,12 +64,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -227,7 +222,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -262,7 +257,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -273,7 +268,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -394,8 +388,8 @@ void parse(String description) { case { contains it, [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] }: Integer ep0500 = 0x02 utils_sendZigbeeCommands([ - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload ]) utils_processedZclMessage 'Enroll Request', "description=${description}" return @@ -462,18 +456,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2112.groovy b/ikea-zigbee-drivers/Ikea_E2112.groovy index 3bde206..32be62b 100644 --- a/ikea-zigbee-drivers/Ikea_E2112.groovy +++ b/ikea-zigbee-drivers/Ikea_E2112.groovy @@ -28,7 +28,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0402,0405,FC57,FC7C,042A,FC7E', outClusters:'0003,0019,0020,0202', model:'VINDSTYRKA', manufacturer:'IKEA of Sweden' // For firmware: 1.0.010 (117C-110F-00010010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0402,0405,FC57,FC7C,042A,FC7E', outClusters:'0003,0019,0020,0202', model:'VINDSTYRKA', manufacturer:'IKEA of Sweden' // For firmware: 1.0.010 (117C-110F-00010010) // Attributes for E2112.FineParticulateMatter attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive groups', 'unhealthy', 'hazardous'] @@ -61,12 +61,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -169,7 +164,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -230,7 +225,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -408,18 +402,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2123.groovy b/ikea-zigbee-drivers/Ikea_E2123.groovy index de098e0..ff2a4b0 100644 --- a/ikea-zigbee-drivers/Ikea_E2123.groovy +++ b/ikea-zigbee-drivers/Ikea_E2123.groovy @@ -45,8 +45,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.012 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.35 (117C-110E-01000035) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.012 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.35 (117C-110E-01000035) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -72,12 +72,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -219,7 +214,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -252,7 +247,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -273,7 +268,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -512,18 +506,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index 62e1e30..89806ff 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -33,7 +33,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.57 (117C-1938-01000057) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.57 (117C-1938-01000057) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -59,12 +59,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -206,7 +201,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -239,7 +234,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -250,7 +245,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -422,18 +416,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2201.groovy b/ikea-zigbee-drivers/Ikea_E2201.groovy index a052063..7c68061 100644 --- a/ikea-zigbee-drivers/Ikea_E2201.groovy +++ b/ikea-zigbee-drivers/Ikea_E2201.groovy @@ -39,7 +39,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' // For firmware: 1.0.47 (117C-11CD-01000047) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' // For firmware: 1.0.47 (117C-11CD-01000047) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -65,12 +65,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -211,7 +206,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -244,7 +239,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -255,7 +250,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -435,18 +429,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index 72ee6dc..83eb47e 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -30,7 +30,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0500,0B05,FC7C,FC81', outClusters:'0003,0004,0019', model:'BADRING Water Leakage Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.7 (117C-24D4-01000007) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0500,0B05,FC7C,FC81', outClusters:'0003,0004,0019', model:'BADRING Water Leakage Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.7 (117C-24D4-01000007) // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -59,12 +59,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -162,7 +157,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -205,7 +200,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -311,8 +305,8 @@ void parse(String description) { case { contains it, [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] }: Integer ep0500 = 0x01 utils_sendZigbeeCommands([ - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload ]) utils_processedZclMessage 'Enroll Request', "description=${description}" return @@ -379,18 +373,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_E2204.groovy b/ikea-zigbee-drivers/Ikea_E2204.groovy index b3b4a7e..d8c10ed 100644 --- a/ikea-zigbee-drivers/Ikea_E2204.groovy +++ b/ikea-zigbee-drivers/Ikea_E2204.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57,FC7C,FC85', outClusters:'0019', model:'TRETAKT Smart plug', manufacturer:'IKEA of Sweden' // For firmware: 2.4.4 (117C-1100-02040004) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57,FC7C,FC85', outClusters:'0019', model:'TRETAKT Smart plug', manufacturer:'IKEA of Sweden' // For firmware: 2.4.4 (117C-1100-02040004) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -62,12 +62,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -78,11 +73,7 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) @@ -167,15 +158,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -235,7 +225,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -269,7 +259,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0xFC85, 0x0001, [mfgCode:'0x117C'] ) // DarkMode // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -303,7 +293,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -441,18 +430,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -467,11 +450,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -480,7 +463,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -488,7 +471,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Ikea_E2213.groovy b/ikea-zigbee-drivers/Ikea_E2213.groovy index 5e81d0d..c7b9b12 100644 --- a/ikea-zigbee-drivers/Ikea_E2213.groovy +++ b/ikea-zigbee-drivers/Ikea_E2213.groovy @@ -35,7 +35,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' // For firmware: 1.0.20 (117C-3B08-01000020) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' // For firmware: 1.0.20 (117C-3B08-01000020) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -61,12 +61,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -161,7 +156,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -212,7 +207,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -387,18 +381,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index db139d3..aaa9430 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -35,7 +35,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // Type LED2101G4, LED1949C5: 1.1.003 (117C-2204-00011003) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // LED2101G4: 1.1.003 (117C-2204-00011003) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' // LED1949C5: 1.1.003 (117C-2204-00011003) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -73,12 +74,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -89,11 +85,7 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) @@ -305,15 +297,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -383,7 +374,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -420,7 +411,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -453,7 +444,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big mireds = mireds < state.minMireds ? state.minMireds : (mireds > state.maxMireds ? state.maxMireds : mireds) Integer newColorTemperature = Math.round(1000000 / mireds) log_debug "Setting color temperature to ${newColorTemperature}k (${mireds} mireds) during ${duration} seconds" - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) if (level > 0 && duration == 0) setLevel level, duration @@ -491,7 +482,6 @@ private void processMultipleColorTemperatureAttributes(Map msg, String type) { mireds = Integer.parseInt it.value, 16 temperature = Math.round(1000000 / mireds) break - case 0x0008: case 0x4001: colorMode = it.value == '02' ? 'CT' : 'RGB' @@ -517,7 +507,7 @@ void setLevel(BigDecimal level, BigDecimal duration = 0) { Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" Integer lvl = newLevel * 2.54 - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String command = prestaging == false ? '04' : '00' String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) @@ -548,7 +538,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -677,7 +666,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0300, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" return - case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) + case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.Brightness @@ -695,7 +684,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return - case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) + case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.HealthCheck @@ -714,18 +703,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -740,11 +723,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -753,7 +736,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -761,7 +744,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Legrand_741811.groovy b/ikea-zigbee-drivers/Legrand_741811.groovy index d04e280..8ffc4f8 100644 --- a/ikea-zigbee-drivers/Legrand_741811.groovy +++ b/ikea-zigbee-drivers/Legrand_741811.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // For firmware: 003e (1021-0011-003E4203) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // For firmware: 003e (1021-0011-003E4203) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -62,12 +62,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -78,11 +73,7 @@ metadata { type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) @@ -172,15 +163,14 @@ List updated(boolean auto = false) { if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } if (auto) return cmds @@ -244,7 +234,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Query Basic cluster attributes cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID @@ -279,7 +269,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0B04, 0x050B) // ActivePower // Refresh for capability.ZigbeeGroups - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership utils_sendZigbeeCommands cmds } @@ -313,7 +303,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -473,18 +462,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" @@ -499,11 +482,11 @@ void parse(String description) { Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -512,7 +495,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -520,7 +503,7 @@ void parse(String description) { case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return diff --git a/ikea-zigbee-drivers/Makefile b/ikea-zigbee-drivers/Makefile index 0f31057..8bc03c7 100644 --- a/ikea-zigbee-drivers/Makefile +++ b/ikea-zigbee-drivers/Makefile @@ -18,7 +18,7 @@ $(info ------------------------------------) .PHONY: all clean -all: Aqara_DCM-K01.groovy Ikea_DIM-Lights.groovy Ikea_E1603.groovy Ikea_E1743.groovy Ikea_E1745.groovy Ikea_E1766.groovy Ikea_E1810.groovy Ikea_E1812.groovy Ikea_E1836.groovy Ikea_E2002.groovy Ikea_E2006.groovy Ikea_E2013.groovy Ikea_E2112.groovy Ikea_E2123.groovy Ikea_E2134.groovy Ikea_E2201.groovy Ikea_E2202.groovy Ikea_E2204.groovy Ikea_E2213.groovy Ikea_WS-Lights.groovy Legrand_741811.groovy Philips_RDM001.groovy Philips_RWL022.groovy Swann_SWO-KEF1PA.groovy +all: Aqara_DCM-K01.groovy Ikea_CWS-Lights.groovy Ikea_DIM-Lights.groovy Ikea_E1603.groovy Ikea_E1743.groovy Ikea_E1745.groovy Ikea_E1766.groovy Ikea_E1810.groovy Ikea_E1812.groovy Ikea_E1836.groovy Ikea_E2002.groovy Ikea_E2006.groovy Ikea_E2013.groovy Ikea_E2112.groovy Ikea_E2123.groovy Ikea_E2134.groovy Ikea_E2201.groovy Ikea_E2202.groovy Ikea_E2204.groovy Ikea_E2213.groovy Ikea_WS-Lights.groovy Legrand_741811.groovy Philips_RDM001.groovy Philips_RWL022.groovy Swann_SWO-KEF1PA.groovy %.groovy: src/devices/%/config.yaml src/common.yaml src/blueprint.groovy tools/yaml-merge$(POSTFIX) src/common.yaml $< | tools/mustache$(POSTFIX) src/blueprint.groovy > $@ diff --git a/ikea-zigbee-drivers/Philips_RDM001.groovy b/ikea-zigbee-drivers/Philips_RDM001.groovy index 8ec149f..a455793 100644 --- a/ikea-zigbee-drivers/Philips_RDM001.groovy +++ b/ikea-zigbee-drivers/Philips_RDM001.groovy @@ -47,7 +47,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' // For firmware: 1.0.5 (100B-011C-0000041A) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' // For firmware: 1.0.5 (100B-011C-0000041A) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -73,12 +73,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -248,7 +243,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -281,7 +276,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -292,7 +287,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -500,18 +494,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Philips_RWL022.groovy b/ikea-zigbee-drivers/Philips_RWL022.groovy index 2f26456..892fb8c 100644 --- a/ikea-zigbee-drivers/Philips_RWL022.groovy +++ b/ikea-zigbee-drivers/Philips_RWL022.groovy @@ -41,7 +41,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' // For firmware: 2.45.2_hF4400CA (100B-0119-02002D02) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' // For firmware: 2.45.2_hF4400CA (100B-0119-02002D02) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -67,12 +67,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -218,7 +213,7 @@ void configure(boolean auto = false) { // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' - cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource // Configuration for capability.PushableButton Integer numberOfButtons = BUTTONS.count { true } @@ -251,7 +246,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage // Refresh for capability.ZigbeeBindings - cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table utils_sendZigbeeCommands cmds } @@ -262,7 +257,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -442,18 +436,12 @@ void parse(String description) { // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy index 59ec256..4f7bef8 100644 --- a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy +++ b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy @@ -36,7 +36,7 @@ metadata { capability 'HealthCheck' capability 'PushableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0001,0500,0000', outClusters:'0003,0501', model:'SWO-KEF1PA', manufacturer:'SwannONe' // For firmware: TBD + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0001,0500,0000', outClusters:'0003,0501', model:'SWO-KEF1PA', manufacturer:'SwannONe' // For firmware: TBD // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -65,12 +65,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) @@ -214,7 +209,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' @@ -354,8 +348,8 @@ void parse(String description) { case { contains it, [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] }: Integer ep0500 = 0x01 utils_sendZigbeeCommands([ - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload ]) utils_processedZclMessage 'Enroll Request', "description=${description}" return diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 47a95e6..882d454 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - E2134: Fix bindings and reporting to use the correct endpoints - @alexandruy2k", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-04-??", @@ -28,6 +28,14 @@ "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2202.groovy", "required": false }, + { + "id" : "6e9663e0-c76b-42e3-bbd4-01657e23adba", + "name": "IKEA Color White Spectrum Light", + "description": "Zigbee driver for IKEA Color White Spectrum Lights", + "namespace": "dandanache", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_CWS-Light.groovy", + "required": false + }, { "id" : "97ee5c2b-9c74-43f7-a149-adbfbad48da5", "name": "IKEA Dimmable Light", @@ -44,14 +52,6 @@ "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_WS-Light.groovy", "required": false }, - { - "id" : "6e9663e0-c76b-42e3-bbd4-01657e23adba", - "name": "IKEA Color White Spectrum Light", - "description": "Zigbee driver for IKEA Color White Spectrum Lights", - "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_CWS-Light.groovy", - "required": false - }, { "id" : "d5e1cf42-b933-466b-b95b-cd0c338db077", "name": "IKEA Parasoll Door/Window Sensor (E2013)", diff --git a/ikea-zigbee-drivers/src/blueprint.groovy b/ikea-zigbee-drivers/src/blueprint.groovy index 513727d..523002f 100644 --- a/ikea-zigbee-drivers/src/blueprint.groovy +++ b/ikea-zigbee-drivers/src/blueprint.groovy @@ -26,7 +26,7 @@ metadata { {{/ device.capabilities }} {{# device.fingerprints }} - {{{ value }}} // {{# type }}Type {{ type }}{{/ type }}{{^ type }}For firmware{{/ type }}: {{ firmwares }} + {{{ value }}} // {{# type }}{{ type }}{{/ type }}{{^ type }}For firmware{{/ type }}: {{ firmwares }} {{/ device.fingerprints }} {{# device.capabilities }} {{> file@attributes }} @@ -54,12 +54,7 @@ metadata { name: 'logLevel', type: 'enum', title: 'Log verbosity', description: 'Select what type of messages appear in the "Logs" section.', - options: [ - '1': 'Debug - log everything', - '2': 'Info - log important events', - '3': 'Warning - log events that require attention', - '4': 'Error - log errors' - ], + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], defaultValue: '1', required: true ) diff --git a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy index 201098c..13c5a44 100644 --- a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy @@ -91,7 +91,7 @@ void setLevel(BigDecimal level, BigDecimal duration = 0) { Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" Integer lvl = newLevel * 2.54 - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String command = prestaging == false ? '04' : '00' String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) @@ -194,7 +194,7 @@ case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return -case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) +case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index 9eb3b71..29ac032 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -3,16 +3,6 @@ capability 'ColorControl' {{/ @definition }} {{!--------------------------------------------------------------------------}} -{{# @inputs }} - -// Inputs for capability.ColorControl -{{/ @inputs }} -{{!--------------------------------------------------------------------------}} -{{# @commands }} - -// Commands for capability.ColorControl -{{/ @commands }} -{{!--------------------------------------------------------------------------}} {{# @implementation }} // Implementation for capability.ColorControl @@ -24,7 +14,7 @@ void setColor(Map colormap) { newHue = Math.round(newHue * 2.54) newSaturation = Math.round(newSaturation * 2.54) String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation setLevel newLevel } void setHue(BigDecimal hue) { @@ -32,14 +22,14 @@ void setHue(BigDecimal hue) { log_debug "Setting color hue to ${newHue}%" newHue = Math.round(newHue * 2.54) String payload = "${utils_payload newHue, 2} 00 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue } void setSaturation(BigDecimal saturation) { Integer newSaturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) log_debug "Setting color saturation to ${newSaturation}%" newSaturation = Math.round(newSaturation * 2.54) String payload = "${utils_payload newSaturation, 2} 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] @@ -55,12 +45,10 @@ private void processMultipleColorAttributes(Map msg, String type) { hue = Math.round(Integer.parseInt(it.value, 16) / 2.54) hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) break - case 0x0001: saturation = Math.round(Integer.parseInt(it.value, 16) / 2.54) saturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) break - case 0x0008: case 0x4001: colorMode = it.value == '02' ? 'CT' : 'RGB' @@ -79,7 +67,6 @@ private void processMultipleColorAttributes(Map msg, String type) { String colorName = convertHueToGenericColorName colorHue, colorSaturation utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type } - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" } {{/ @implementation }} @@ -125,6 +112,5 @@ case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: processMultipleColorAttributes msg, type return - {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy index 81c61e8..10e90a6 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -47,7 +47,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big mireds = mireds < state.minMireds ? state.minMireds : (mireds > state.maxMireds ? state.maxMireds : mireds) Integer newColorTemperature = Math.round(1000000 / mireds) log_debug "Setting color temperature to ${newColorTemperature}k (${mireds} mireds) during ${duration} seconds" - Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) if (level > 0 && duration == 0) setLevel level, duration @@ -85,7 +85,6 @@ private void processMultipleColorTemperatureAttributes(Map msg, String type) { mireds = Integer.parseInt it.value, 16 temperature = Math.round(1000000 / mireds) break - case 0x0008: case 0x4001: colorMode = it.value == '02' ? 'CT' : 'RGB' @@ -172,7 +171,7 @@ case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: case { contains it, [clusterInt:0x0300, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" return -case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) +case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/HealthCheck.groovy b/ikea-zigbee-drivers/src/capabilities/HealthCheck.groovy index 5605e68..1766131 100644 --- a/ikea-zigbee-drivers/src/capabilities/HealthCheck.groovy +++ b/ikea-zigbee-drivers/src/capabilities/HealthCheck.groovy @@ -52,7 +52,6 @@ void ping() { log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' runIn 5, 'pingExecute' } - void pingExecute() { if (state.lastRx == 0) { log_info 'Did not sent any messages since it was last configured' diff --git a/ikea-zigbee-drivers/src/capabilities/IAS.groovy b/ikea-zigbee-drivers/src/capabilities/IAS.groovy index 4b5a9fd..3fcbbbd 100644 --- a/ikea-zigbee-drivers/src/capabilities/IAS.groovy +++ b/ikea-zigbee-drivers/src/capabilities/IAS.groovy @@ -55,8 +55,8 @@ case { contains it, [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] case { contains it, [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] }: Integer ep0500 = {{# params.endpoint }}{{params.endpoint}}{{/ params.endpoint }}{{^ params.endpoint }}0x01{{/ params.endpoint }} utils_sendZigbeeCommands([ - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 - "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload ]) utils_processedZclMessage 'Enroll Request', "description=${description}" return diff --git a/ikea-zigbee-drivers/src/capabilities/PowerSource.groovy b/ikea-zigbee-drivers/src/capabilities/PowerSource.groovy index 89031e3..b9d0bf8 100644 --- a/ikea-zigbee-drivers/src/capabilities/PowerSource.groovy +++ b/ikea-zigbee-drivers/src/capabilities/PowerSource.groovy @@ -7,7 +7,7 @@ capability 'PowerSource' // Configuration for capability.PowerSource sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' -cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource +cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource {{/ @configure }} {{!--------------------------------------------------------------------------}} {{# @events }} @@ -21,18 +21,12 @@ case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } switch (msg.value) { - case '01': - case '02': - case '05': - case '06': - powerSource = 'mains' - break + case ['01', '02', '05', '06']: + powerSource = 'mains'; break case '03': - powerSource = 'battery' - break + powerSource = 'battery'; break case '04': powerSource = 'dc' - break } utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" diff --git a/ikea-zigbee-drivers/src/capabilities/Switch.groovy b/ikea-zigbee-drivers/src/capabilities/Switch.groovy index b4e29c9..78346a9 100644 --- a/ikea-zigbee-drivers/src/capabilities/Switch.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Switch.groovy @@ -13,11 +13,7 @@ input( type: 'enum', title: 'Power On behaviour', description: 'Select what happens after a power outage.', - options: [ - 'TURN_POWER_ON': 'Turn power On', - 'TURN_POWER_OFF': 'Turn power Off', - 'RESTORE_PREVIOUS_STATE': 'Restore previous state' - ], + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], defaultValue: 'RESTORE_PREVIOUS_STATE', required: true ) diff --git a/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy b/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy index 1829591..8f2026c 100644 --- a/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy @@ -88,7 +88,7 @@ if (controlGroup != null && controlGroup != '----') { {{# @refresh }} // Refresh for capability.ZigbeeBindings -cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table +cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table {{/ @refresh }} {{!--------------------------------------------------------------------------}} {{# @events }} diff --git a/ikea-zigbee-drivers/src/capabilities/ZigbeeGroups.groovy b/ikea-zigbee-drivers/src/capabilities/ZigbeeGroups.groovy index a0f61e1..e3c9193 100644 --- a/ikea-zigbee-drivers/src/capabilities/ZigbeeGroups.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ZigbeeGroups.groovy @@ -26,22 +26,21 @@ input( if (joinGroup != null && joinGroup != '----') { if (joinGroup == '0000') { log_info '🛠️ Leaving all Zigbee groups' - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups } else { String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') log_info "🛠️ Joining group: ${joinGroup} (${groupName})" cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group } - device.updateSetting 'joinGroup', [value:'----', type:'enum'] - cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership } {{/ @updated }} {{!--------------------------------------------------------------------------}} {{# @refresh }} // Refresh for capability.ZigbeeGroups -cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership +cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership {{/ @refresh }} {{!--------------------------------------------------------------------------}} {{# @events }} @@ -55,11 +54,11 @@ case { contains it, [clusterInt:0x0004, commandInt:0x02, direction:'01'] }: Set groupNames = [] for (int pos = 0; pos < count; pos++) { String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" log_debug "Found group membership: ${groupName}" groupNames.add groupName } - state.joinGrp = groupNames.findAll { !it.startsWith('Unknown') } + state.joinGrp = groupNames if (state.joinGrp.size() == 0) state.remove 'joinGrp' log_info "Current group membership: ${groupNames ?: 'None'}" return @@ -68,7 +67,7 @@ case { contains it, [clusterInt:0x0004, commandInt:0x02, direction:'01'] }: case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return @@ -76,7 +75,7 @@ case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') String groupId = "${msg.data[2]}${msg.data[1]}" - String groupName = GROUPS.getOrDefault(groupId, "Unknown (${groupId})") + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" return {{/ @events }} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml index c8f3a60..7f40313 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml @@ -9,10 +9,14 @@ device: url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: - - type: LED2101G4, LED1949C5 + - type: LED2101G4 firmwares: 1.1.003 (117C-2204-00011003) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' + - type: LED1949C5 + firmwares: 1.1.003 (117C-2204-00011003) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' + capabilities: - file: src/capabilities/Switch.groovy params: From 0ae270d7ac281ed6c03672d89028e7e24f1d5b93 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 14 Apr 2024 21:36:26 +0300 Subject: [PATCH 04/26] Add fingerprint for LED2111G6 --- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 1 + ikea-zigbee-drivers/README.md | 1 + ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml | 3 +++ ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml | 1 - 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 09ec72b..3ed2cd5 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -37,6 +37,7 @@ metadata { capability 'PowerSource' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 1c0fa43..6c5e28b 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -650,6 +650,7 @@ Below you can find the details of each device, including the features and pairin #### Tested devices * L2112: Ormanas LED Strip +* LED2111G6: Tradfri Bulb E14 CWS Globe 806lm #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index 7891e3a..67771d1 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -12,6 +12,9 @@ device: - type: L2112 firmwares: 1.1.10 (117C-2804-01010010) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' + - type: LED2111G6 + firmwares: 1.0.38 (117C-2805-01000038) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml index 7f40313..8b80637 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml @@ -12,7 +12,6 @@ device: - type: LED2101G4 firmwares: 1.1.003 (117C-2204-00011003) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' - - type: LED1949C5 firmwares: 1.1.003 (117C-2204-00011003) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' From 4d693f9ea8451074559d454f091840c7c2ae8513 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Mon, 15 Apr 2024 22:39:12 +0300 Subject: [PATCH 05/26] E2123: Fix bindings to use the correct endpoints Add fingerprint for Legrand Mobile Outlet Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - `@BorrisTheCat` --- ikea-zigbee-drivers/Aqara_DCM-K01.groovy | 22 +++++++++------- ikea-zigbee-drivers/CHANGELOG.md | 4 ++- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 16 +++++++++--- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 5 ++-- ikea-zigbee-drivers/Ikea_E1603.groovy | 7 ++--- ikea-zigbee-drivers/Ikea_E1743.groovy | 9 +++++-- ikea-zigbee-drivers/Ikea_E1745.groovy | 6 +++-- ikea-zigbee-drivers/Ikea_E1766.groovy | 9 +++++-- ikea-zigbee-drivers/Ikea_E1810.groovy | 9 +++++-- ikea-zigbee-drivers/Ikea_E1812.groovy | 12 ++++++--- ikea-zigbee-drivers/Ikea_E1836.groovy | 5 ++-- ikea-zigbee-drivers/Ikea_E2002.groovy | 12 ++++++--- ikea-zigbee-drivers/Ikea_E2006.groovy | 11 +++++--- ikea-zigbee-drivers/Ikea_E2013.groovy | 8 +++--- ikea-zigbee-drivers/Ikea_E2112.groovy | 19 +++++++------- ikea-zigbee-drivers/Ikea_E2123.groovy | 22 ++++++++++------ ikea-zigbee-drivers/Ikea_E2134.groovy | 6 +++-- ikea-zigbee-drivers/Ikea_E2201.groovy | 9 +++++-- ikea-zigbee-drivers/Ikea_E2202.groovy | 7 ++--- ikea-zigbee-drivers/Ikea_E2204.groovy | 5 ++-- ikea-zigbee-drivers/Ikea_E2213.groovy | 9 +++++-- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 7 ++--- ikea-zigbee-drivers/Legrand_741811.groovy | 26 ++++++++++--------- ikea-zigbee-drivers/Philips_RDM001.groovy | 11 +++++--- ikea-zigbee-drivers/Philips_RWL022.groovy | 11 +++++--- ikea-zigbee-drivers/README.md | 3 +++ ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy | 8 +++--- ikea-zigbee-drivers/packageManifest.json | 2 +- ikea-zigbee-drivers/src/blueprint.groovy | 3 ++- .../src/capabilities/Battery.groovy | 2 +- .../src/capabilities/Brightness.groovy | 2 +- .../src/capabilities/ColorControl.groovy | 9 +++++++ .../src/capabilities/ColorTemperature.groovy | 2 +- .../capabilities/DoubleTapableButton.groovy | 1 + .../src/capabilities/EnergyMeter.groovy | 2 +- .../src/capabilities/HoldableButton.groovy | 1 + .../src/capabilities/IAS.groovy | 2 +- .../src/capabilities/MultiRelay.groovy | 4 +-- .../src/capabilities/PowerMeter.groovy | 4 +-- .../src/capabilities/PushableButton.groovy | 1 + .../src/capabilities/RelativeHumidity.groovy | 4 +-- .../src/capabilities/ReleasableButton.groovy | 1 + .../src/capabilities/Switch.groovy | 2 +- .../src/capabilities/Temperature.groovy | 4 +-- .../src/capabilities/ZigbeeBindings.groovy | 1 + .../src/devices/Aqara_DCM-K01/DCM-K01.groovy | 8 +++--- .../src/devices/Ikea_E2002/E2002.groovy | 1 - .../src/devices/Ikea_E2006/E2006.groovy | 6 +++-- .../Ikea_E2112/FineParticulateMatter.groovy | 4 +-- .../src/devices/Ikea_E2112/VocIndex.groovy | 4 +-- .../src/devices/Ikea_E2123/E2123.groovy | 10 +++---- .../src/devices/Legrand_741811/741811.groovy | 16 ++++++------ .../src/devices/Legrand_741811/config.yaml | 2 ++ .../src/devices/Philips_RDM001/RDM001.groovy | 2 +- .../src/devices/Philips_RWL022/RWL022.groovy | 2 +- 55 files changed, 243 insertions(+), 137 deletions(-) diff --git a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy index 0f3d6f0..07e92a4 100644 --- a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy +++ b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy @@ -38,7 +38,7 @@ metadata { capability 'PushableButton' capability 'HealthCheck' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0B04,0702,0005,0004,0003,0012,0000,0006,FCC0', outClusters:'0019,000A', model:'lumi.switch.acn047', manufacturer:'Aqara' // For firmware: Unknown + fingerprint profileId:'0104', endpointId:'01', inClusters:'0B04,0702,0005,0004,0003,0012,0000,0006,FCC0', outClusters:'0019,000A', model:'lumi.switch.acn047', manufacturer:'Aqara' // Firmware: Unknown // Attributes for devices.Aqara_DCM-K01 attribute 'powerOutageCount', 'number' @@ -316,6 +316,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -373,6 +374,7 @@ void componentRefresh(ChildDeviceWrapper childDevice) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -544,7 +546,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Temperature=${temperature}, PowerOutageCount=${powerOutageCount}, SoftwareBuild=${softwareBuild}, Energy=${energy}kWh, Voltage=${voltage}V, Power=${power}W, Amperage=${amperage}A" return - // Other events that we expect but are not usefull for devices.Aqara_DCM-K01 behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0xFCC0, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=LumiSpecific, data=${msg.data}" return @@ -555,7 +557,7 @@ void parse(String description) { utils_processedZclMessage 'Report Attributes Response', "OperationMode=${msg.value}, Switch=${msg.endpoint}" return case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x000A] }: - utils_processedZclMessage 'Report Attributes Response', "switchType=${msg.value}" + utils_processedZclMessage 'Report Attributes Response', "SwitchType=${msg.value}" return case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x02D0] }: utils_processedZclMessage 'Report Attributes Response', "Interlock=${msg.value}" @@ -564,9 +566,9 @@ void parse(String description) { utils_processedZclMessage 'Report Attributes Response', "RelayMode=${msg.value}" return case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x00EB] }: - utils_processedZclMessage 'Report Attributes Response', "pulseDuration=${msg.value}" + utils_processedZclMessage 'Report Attributes Response', "PulseDuration=${msg.value}" return - case { contains it, [clusterInt:0xFCC0, commandInt:0x04] }: // Write Attribute Response + case { contains it, [clusterInt:0xFCC0, commandInt:0x04] }: // Write Attribute Response return // Events for capability.PowerMeter @@ -598,11 +600,11 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerDivisor=${msg.value}" return - // Other events that we expect but are not usefull for capability.PowerMeter behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0B04, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ActivePower, data=${msg.data}" return - case { contains it, [clusterInt:0x0B04, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command + case { contains it, [clusterInt:0x0B04, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Response return // Events for capability.EnergyMeter @@ -628,7 +630,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "EnergyDivisor=${msg.value}" return - // Other events that we expect but are not usefull for capability.PowerMeter behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0702, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentSummation, data=${msg.data}" return @@ -651,9 +653,9 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Module=${moduleNumber}, Switch=${newState}" return - // Other events that we expect but are not usefull for capability.MultiRelay behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=switch, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return // Events for capability.HealthCheck diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 0a2e5ea..031260a 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -16,7 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver ### Fixed -- E2134: Fix bindings and reporting to use the correct endpoints - `@alexandruy2k` +- E2134, E2123: Fix bindings to use the correct endpoints - `@alexandruy2k` +- Fix errors when pressing a dashboard tile assigned to "push" one of the buttons - `@OldChicagoPete` +- Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - `@BorrisTheCat` ## [4.1.0] - 2024-04-06 diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 3ed2cd5..785660d 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -393,6 +393,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -459,6 +460,7 @@ void setColor(Map colormap) { newSaturation = Math.round(newSaturation * 2.54) String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + /* groovylint-disable-next-line UnnecessarySetter */ setLevel newLevel } void setHue(BigDecimal hue) { @@ -706,7 +708,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return @@ -735,6 +737,14 @@ void parse(String description) { processMultipleColorAttributes msg, type return + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0300, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "data=${msg.data}" + return + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0003] }: // Report Attribute Current X + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0004] }: // Report Attribute Current Y + return + // Events for capability.ColorTemperature // =================================================================================================================== @@ -759,7 +769,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.minMireds)}K), ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" return - // Other events that we expect but are not usefull for capability.ColorTemperature behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0300, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" return @@ -777,7 +787,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return - // Other events that we expect but are not usefull for capability.Brightness behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index c424c25..69a17b6 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -334,6 +334,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -511,7 +512,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return @@ -530,7 +531,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return - // Other events that we expect but are not usefull for capability.Brightness behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1603.groovy b/ikea-zigbee-drivers/Ikea_E1603.groovy index 325a3c2..d296ab5 100644 --- a/ikea-zigbee-drivers/Ikea_E1603.groovy +++ b/ikea-zigbee-drivers/Ikea_E1603.groovy @@ -32,8 +32,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.0.024 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // For firmware: 2.3.089 (117C-1101-23089631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // Firmware: 2.0.024 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019,0020,1000', model:'TRADFRI control outlet', manufacturer:'IKEA of Sweden' // Firmware: 2.3.089 (117C-1101-23089631) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -208,6 +208,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -353,7 +354,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index b82d1c0..8d6e829 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -34,7 +34,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' // Firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -195,6 +195,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -244,6 +245,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -254,6 +256,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -264,6 +267,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -283,6 +287,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -382,7 +387,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1745.groovy b/ikea-zigbee-drivers/Ikea_E1745.groovy index 809d3c4..a2e96b0 100644 --- a/ikea-zigbee-drivers/Ikea_E1745.groovy +++ b/ikea-zigbee-drivers/Ikea_E1745.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'TRADFRI motion sensor', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C8-24040005) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'TRADFRI motion sensor', manufacturer:'IKEA of Sweden' // Firmware: 24.4.5 (117C-11C8-24040005) // Attributes for devices.Ikea_E1745 attribute 'requestedBrightness', 'number' // Syncs with the brightness option on device (◐/⭘) @@ -253,6 +253,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -326,6 +327,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -420,7 +422,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1766.groovy b/ikea-zigbee-drivers/Ikea_E1766.groovy index ce0271a..bb2a676 100644 --- a/ikea-zigbee-drivers/Ikea_E1766.groovy +++ b/ikea-zigbee-drivers/Ikea_E1766.groovy @@ -34,7 +34,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' // Firmware: 24.4.6 (117C-11C6-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -193,6 +193,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -242,6 +243,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -252,6 +254,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -262,6 +265,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -281,6 +285,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -373,7 +378,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index 2001cbb..4ef4e4e 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -37,7 +37,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' // For firmware: 24.4.5 (117C-11C1-24040005) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' // Firmware: 24.4.5 (117C-11C1-24040005) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -199,6 +199,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -248,6 +249,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -258,6 +260,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -268,6 +271,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -287,6 +291,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -411,7 +416,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1812.groovy b/ikea-zigbee-drivers/Ikea_E1812.groovy index 949339b..8c66960 100644 --- a/ikea-zigbee-drivers/Ikea_E1812.groovy +++ b/ikea-zigbee-drivers/Ikea_E1812.groovy @@ -34,8 +34,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 2.3.015 (117C-11C6-23015631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // For firmware: 24.4.6 (117C-11C6-24040006) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // Firmware: 2.3.015 (117C-11C6-23015631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // Firmware: 24.4.6 (117C-11C6-24040006) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -195,6 +195,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -218,6 +219,7 @@ void refresh(boolean auto = false) { } // Implementation for capability.DoubleTapableButton +void doubleTap(String buttonNumber) { doubleTap Integer.parseInt(buttonNumber) } void doubleTap(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -254,6 +256,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -264,6 +267,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -274,6 +278,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -293,6 +298,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -396,7 +402,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E1836.groovy b/ikea-zigbee-drivers/Ikea_E1836.groovy index 8090522..8740df9 100644 --- a/ikea-zigbee-drivers/Ikea_E1836.groovy +++ b/ikea-zigbee-drivers/Ikea_E1836.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'ASKVADER on/off switch', manufacturer:'IKEA of Sweden' // For firmware: 1.0.002 (117C-110D-00010002) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'ASKVADER on/off switch', manufacturer:'IKEA of Sweden' // Firmware: 1.0.002 (117C-110D-00010002) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -207,6 +207,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -352,7 +353,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index c9473b8..c86cc4e 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -36,8 +36,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.024 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // For firmware: 2.4.5 (117C-11CB-02040005) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // Firmware: 1.0.024 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // Firmware: 2.4.5 (117C-11CB-02040005) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -199,6 +199,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -248,6 +249,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -258,6 +260,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -268,6 +271,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -287,6 +291,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -368,7 +373,6 @@ void parse(String description) { List button = msg.data[0] == '00' ? BUTTONS.NEXT : BUTTONS.PREV utils_sendEvent name:'pushed', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was pushed" return - /* Holding the PREV and NEXT buttons works in a weird way: @@ -420,7 +424,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2006.groovy b/ikea-zigbee-drivers/Ikea_E2006.groovy index 8e63553..bc96a4b 100644 --- a/ikea-zigbee-drivers/Ikea_E2006.groovy +++ b/ikea-zigbee-drivers/Ikea_E2006.groovy @@ -35,8 +35,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.0.033 (117C-110C-00010033) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7C,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // For firmware: 1.1.001 (117C-110C-00011001) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // Firmware: 1.0.033 (117C-110C-00010033) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0202,FC57,FC7C,FC7D', outClusters:'0019,0400,042A', model:'STARKVIND Air purifier table', manufacturer:'IKEA of Sweden' // Firmware: 1.1.001 (117C-110C-00011001) // Attributes for devices.Ikea_E2006 attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive groups', 'unhealthy', 'hazardous'] @@ -255,6 +255,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -554,9 +555,11 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ChildLock=${msg.value}" return - // Other events that we expect but are not usefull for devices.E2006 behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0xFC7D, commandInt:0x04] }: // Write Attribute Response (0x04) - case { contains it, [clusterInt:0xFC7D, commandInt:0x07] }: // Configure Reporting Response + return + case { contains it, [clusterInt:0xFC7D, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "data=${msg.data}" return // Events for capability.HealthCheck diff --git a/ikea-zigbee-drivers/Ikea_E2013.groovy b/ikea-zigbee-drivers/Ikea_E2013.groovy index c01e5b8..72b547f 100644 --- a/ikea-zigbee-drivers/Ikea_E2013.groovy +++ b/ikea-zigbee-drivers/Ikea_E2013.groovy @@ -35,7 +35,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'PARASOLL Door/Window Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.19 (117C-3277-01000019) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'PARASOLL Door/Window Sensor', manufacturer:'IKEA of Sweden' // Firmware: 1.0.19 (117C-3277-01000019) // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -233,6 +233,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -297,6 +298,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -406,7 +408,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "ZoneType=${msg.value}" return - // Other events that we expect but are not usefull for capability.IAS behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0500, commandInt:0x04, isClusterSpecific:false] }: utils_processedZclMessage 'Write Attribute Response', "attribute=IAS_CIE_Address, ZoneType=${msg.data}" return @@ -435,7 +437,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2112.groovy b/ikea-zigbee-drivers/Ikea_E2112.groovy index 32be62b..7797400 100644 --- a/ikea-zigbee-drivers/Ikea_E2112.groovy +++ b/ikea-zigbee-drivers/Ikea_E2112.groovy @@ -28,7 +28,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0402,0405,FC57,FC7C,042A,FC7E', outClusters:'0003,0019,0020,0202', model:'VINDSTYRKA', manufacturer:'IKEA of Sweden' // For firmware: 1.0.010 (117C-110F-00010010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0402,0405,FC57,FC7C,042A,FC7E', outClusters:'0003,0019,0020,0202', model:'VINDSTYRKA', manufacturer:'IKEA of Sweden' // Firmware: 1.0.010 (117C-110F-00010010) // Attributes for E2112.FineParticulateMatter attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive groups', 'unhealthy', 'hazardous'] @@ -175,6 +175,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -312,9 +313,9 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "PM25Measurement=${pm25} μg/m³" return - // Other events that we expect but are not usefull for E2112.FineParticulateMatter behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x042A, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=pm25, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=PM25, data=${msg.data}" return // Events for E2112.VocIndex @@ -335,9 +336,9 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "VocIndex=${msg.value}" return - // Other events that we expect but are not usefull for E2112.VocIndex behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0xFC7E, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=vocIndex, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=VocIndex, data=${msg.data}" return // Events for capability.Temperature @@ -358,9 +359,9 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Temperature=${msg.value}" return - // Other events that we expect but are not usefull for capability.Temperature behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0402, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=temperature, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=Temperature, data=${msg.data}" return // Events for capability.RelativeHumidity @@ -381,9 +382,9 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "RelativeHumidity=${msg.value}" return - // Other events that we expect but are not usefull for capability.RelativeHumidity behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0405, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=humidity, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=RelativeHumidity, data=${msg.data}" return // Events for capability.HealthCheck diff --git a/ikea-zigbee-drivers/Ikea_E2123.groovy b/ikea-zigbee-drivers/Ikea_E2123.groovy index ff2a4b0..1007f51 100644 --- a/ikea-zigbee-drivers/Ikea_E2123.groovy +++ b/ikea-zigbee-drivers/Ikea_E2123.groovy @@ -45,8 +45,8 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.012 - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // For firmware: 1.0.35 (117C-110E-01000035) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // Firmware: 1.0.012 + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // Firmware: 1.0.35 (117C-110E-01000035) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -198,11 +198,11 @@ void configure(boolean auto = false) { state.lastCx = DRIVER_VERSION // Configuration for devices.Ikea_E2123 - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0xFC7F {${device.zigbeeId}} {}" // Unknown 64639 cluster --> For firmware 1.0.012 - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 - cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 + cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster + cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster + cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0xFC7F {${device.zigbeeId}} {}" // Unknown 64639 cluster --> For firmware 1.0.012 + cmds += "zdo bind 0x${device.deviceNetworkId} 0x02 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 + cmds += "zdo bind 0x${device.deviceNetworkId} 0x03 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 // Configuration for capability.Battery cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0001 {${device.zigbeeId}} {}" // Power Configuration cluster @@ -229,6 +229,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -252,6 +253,7 @@ void refresh(boolean auto = false) { } // Implementation for capability.DoubleTapableButton +void doubleTap(String buttonNumber) { doubleTap Integer.parseInt(buttonNumber) } void doubleTap(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -288,6 +290,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -298,6 +301,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -308,6 +312,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -327,6 +332,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -485,7 +491,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index 89806ff..2b97e7e 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -33,7 +33,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.57 (117C-1938-01000057) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' // Firmware: 1.0.57 (117C-1938-01000057) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -212,6 +212,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -274,6 +275,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -395,7 +397,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2201.groovy b/ikea-zigbee-drivers/Ikea_E2201.groovy index 7c68061..45c3283 100644 --- a/ikea-zigbee-drivers/Ikea_E2201.groovy +++ b/ikea-zigbee-drivers/Ikea_E2201.groovy @@ -39,7 +39,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' // For firmware: 1.0.47 (117C-11CD-01000047) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' // Firmware: 1.0.47 (117C-11CD-01000047) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -221,6 +221,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -270,6 +271,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -280,6 +282,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -290,6 +293,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -309,6 +313,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -408,7 +413,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index 83eb47e..8c42002 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -30,7 +30,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0500,0B05,FC7C,FC81', outClusters:'0003,0004,0019', model:'BADRING Water Leakage Sensor', manufacturer:'IKEA of Sweden' // For firmware: 1.0.7 (117C-24D4-01000007) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0500,0B05,FC7C,FC81', outClusters:'0003,0004,0019', model:'BADRING Water Leakage Sensor', manufacturer:'IKEA of Sweden' // Firmware: 1.0.7 (117C-24D4-01000007) // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -168,6 +168,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -323,7 +324,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "ZoneType=${msg.value}" return - // Other events that we expect but are not usefull for capability.IAS behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0500, commandInt:0x04, isClusterSpecific:false] }: utils_processedZclMessage 'Write Attribute Response', "attribute=IAS_CIE_Address, ZoneType=${msg.data}" return @@ -352,7 +353,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2204.groovy b/ikea-zigbee-drivers/Ikea_E2204.groovy index d8c10ed..939d181 100644 --- a/ikea-zigbee-drivers/Ikea_E2204.groovy +++ b/ikea-zigbee-drivers/Ikea_E2204.groovy @@ -32,7 +32,7 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57,FC7C,FC85', outClusters:'0019', model:'TRETAKT Smart plug', manufacturer:'IKEA of Sweden' // For firmware: 2.4.4 (117C-1100-02040004) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57,FC7C,FC85', outClusters:'0019', model:'TRETAKT Smart plug', manufacturer:'IKEA of Sweden' // Firmware: 2.4.4 (117C-1100-02040004) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -236,6 +236,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -385,7 +386,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_E2213.groovy b/ikea-zigbee-drivers/Ikea_E2213.groovy index c7b9b12..33ba0db 100644 --- a/ikea-zigbee-drivers/Ikea_E2213.groovy +++ b/ikea-zigbee-drivers/Ikea_E2213.groovy @@ -35,7 +35,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' // For firmware: 1.0.20 (117C-3B08-01000020) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' // Firmware: 1.0.20 (117C-3B08-01000020) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -171,6 +171,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -191,6 +192,7 @@ void refresh(boolean auto = false) { } // Implementation for capability.DoubleTapableButton +void doubleTap(String buttonNumber) { doubleTap Integer.parseInt(buttonNumber) } void doubleTap(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -227,6 +229,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -237,6 +240,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -247,6 +251,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -360,7 +365,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index aaa9430..751ad77 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -385,6 +385,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -630,7 +631,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return @@ -662,7 +663,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.minMireds)}K), ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" return - // Other events that we expect but are not usefull for capability.ColorTemperature behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0300, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" return @@ -680,7 +681,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return - // Other events that we expect but are not usefull for capability.Brightness behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Legrand_741811.groovy b/ikea-zigbee-drivers/Legrand_741811.groovy index 8ffc4f8..4d50c99 100644 --- a/ikea-zigbee-drivers/Legrand_741811.groovy +++ b/ikea-zigbee-drivers/Legrand_741811.groovy @@ -32,7 +32,8 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // For firmware: 003e (1021-0011-003E4203) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // Firmware: 003e (1021-0011-003E4203) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006,000F', outClusters:'0006,0000,FC01,0005,0019', model:' Mobile outlet', manufacturer:' Legrand' // Firmware: 002f // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -84,8 +85,8 @@ metadata { title: 'LED mode', description: 'Select how the LED indicator behaves.', options: [ - 'ALWAYS_ON': 'Always On - LED remains lit at all times, making it easy to find in the dark', - 'ALWAYS_OFF': 'Always Off - LED remains off, ensuring total darkness', + 'ALWAYS_ON': 'Always On - LED remains lit at all times, making it easy to find in the dark', + 'ALWAYS_OFF': 'Always Off - LED remains off, ensuring total darkness', 'OUTLET_STATUS': 'Outlet status - LED indicates the power state of the outlet' ], defaultValue: 'OUTLET_STATUS', @@ -144,16 +145,16 @@ List updated(boolean auto = false) { log_info "🛠️ ledMode = ${ledMode}" switch (ledMode) { case 'ALWAYS_ON': - cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x01, [mfgCode: '0x1021']) // Write LED Mode attribute - cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x01, [mfgCode: '0x1021']) // Write LED Mode attributes + cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x01, [mfgCode: '0x1021']) + cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x01, [mfgCode: '0x1021']) break case 'ALWAYS_OFF': - cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x00, [mfgCode: '0x1021']) // Write LED Mode attribute - cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x00, [mfgCode: '0x1021']) // Write LED Mode attributes + cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x00, [mfgCode: '0x1021']) + cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x00, [mfgCode: '0x1021']) break default: - cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x01, [mfgCode: '0x1021']) // Write LED Mode attribute - cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x00, [mfgCode: '0x1021']) // Write LED Mode attributes + cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x00, [mfgCode: '0x1021']) + cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x01, [mfgCode: '0x1021']) } // Preferences for capability.HealthCheck @@ -245,6 +246,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -395,7 +397,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" return - // Other events that we expect but are not usefull for capability.Switch behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return @@ -439,11 +441,11 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "PowerDivisor=${msg.value}" return - // Other events that we expect but are not usefull for capability.PowerMeter behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0B04, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ActivePower, data=${msg.data}" return - case { contains it, [clusterInt:0x0B04, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command + case { contains it, [clusterInt:0x0B04, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Response return // Events for capability.HealthCheck diff --git a/ikea-zigbee-drivers/Philips_RDM001.groovy b/ikea-zigbee-drivers/Philips_RDM001.groovy index a455793..b1f57c8 100644 --- a/ikea-zigbee-drivers/Philips_RDM001.groovy +++ b/ikea-zigbee-drivers/Philips_RDM001.groovy @@ -47,7 +47,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' // For firmware: 1.0.5 (100B-011C-0000041A) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' // Firmware: 1.0.5 (100B-011C-0000041A) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -258,6 +258,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -307,6 +308,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -317,6 +319,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -327,6 +330,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -346,6 +350,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -446,7 +451,7 @@ void parse(String description) { return // Other events that we expect but are not usefull - case { contains it, [clusterInt:0x0000, commandInt:0x07] }: // ConfigureReportingResponse + case { contains it, [clusterInt:0x0000, commandInt:0x07] }: // Configure Reporting Response return // Events for capability.Battery @@ -473,7 +478,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/Philips_RWL022.groovy b/ikea-zigbee-drivers/Philips_RWL022.groovy index 892fb8c..6d73064 100644 --- a/ikea-zigbee-drivers/Philips_RWL022.groovy +++ b/ikea-zigbee-drivers/Philips_RWL022.groovy @@ -41,7 +41,7 @@ metadata { capability 'PushableButton' capability 'ReleasableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' // For firmware: 2.45.2_hF4400CA (100B-0119-02002D02) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' // Firmware: 2.45.2_hF4400CA (100B-0119-02002D02) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -228,6 +228,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -277,6 +278,7 @@ void pingExecute() { } // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -287,6 +289,7 @@ void hold(BigDecimal buttonNumber) { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -297,6 +300,7 @@ void push(BigDecimal buttonNumber) { } // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -316,6 +320,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } @@ -388,7 +393,7 @@ void parse(String description) { // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0000, commandInt:0x04, isClusterSpecific:false] }: - utils_processedZclMessage 'Write Attribute Response', 'attribute=Philips magic attribute' + utils_processedZclMessage 'Write Attribute Response', 'attribute=Philips Magic Attribute' return // Events for capability.Battery @@ -415,7 +420,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 6c5e28b..7116d64 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -293,6 +293,9 @@ Below you can find the details of each device, including the features and pairin * Directly control Zigbee devices: On/Off * Directly control Zigbee groups: On/Off +#### Known Issues +* Old firmware versions (below 1.0.35) does not send the release event for Button 6 (•) and Button 7 (••) + #### Pairing Instructions 1. Open the battery compartiment and you should see the small pair button (🔗). 1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. diff --git a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy index 4f7bef8..225b340 100644 --- a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy +++ b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy @@ -36,7 +36,7 @@ metadata { capability 'HealthCheck' capability 'PushableButton' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0001,0500,0000', outClusters:'0003,0501', model:'SWO-KEF1PA', manufacturer:'SwannONe' // For firmware: TBD + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0001,0500,0000', outClusters:'0003,0501', model:'SWO-KEF1PA', manufacturer:'SwannONe' // Firmware: TBD // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] @@ -177,6 +177,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true @@ -229,6 +230,7 @@ void pingExecute() { } // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { @@ -366,7 +368,7 @@ void parse(String description) { utils_processedZclMessage 'Read Attributes Response', "ZoneType=${msg.value}" return - // Other events that we expect but are not usefull for capability.IAS behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0500, commandInt:0x04, isClusterSpecific:false] }: utils_processedZclMessage 'Write Attribute Response', "attribute=IAS_CIE_Address, ZoneType=${msg.data}" return @@ -395,7 +397,7 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return - // Other events that we expect but are not usefull for capability.Battery behavior + // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 882d454..1697e6b 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - E2134: Fix bindings and reporting to use the correct endpoints - @alexandruy2k", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-04-??", diff --git a/ikea-zigbee-drivers/src/blueprint.groovy b/ikea-zigbee-drivers/src/blueprint.groovy index 523002f..6c59995 100644 --- a/ikea-zigbee-drivers/src/blueprint.groovy +++ b/ikea-zigbee-drivers/src/blueprint.groovy @@ -26,7 +26,7 @@ metadata { {{/ device.capabilities }} {{# device.fingerprints }} - {{{ value }}} // {{# type }}{{ type }}{{/ type }}{{^ type }}For firmware{{/ type }}: {{ firmwares }} + {{{ value }}} // {{# type }}{{ type }}{{/ type }}{{^ type }}Firmware{{/ type }}: {{ firmwares }} {{/ device.fingerprints }} {{# device.capabilities }} {{> file@attributes }} @@ -146,6 +146,7 @@ void configure(boolean auto = false) { log_info 'Configuration done; refreshing device current state in 7 seconds ...' runIn 7, 'refresh', [data:true] } +/* groovylint-disable-next-line UnusedPrivateMethod */ private void autoConfigure() { log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" configure true diff --git a/ikea-zigbee-drivers/src/capabilities/Battery.groovy b/ikea-zigbee-drivers/src/capabilities/Battery.groovy index e2ded03..1d9ac12 100644 --- a/ikea-zigbee-drivers/src/capabilities/Battery.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Battery.groovy @@ -44,7 +44,7 @@ case { contains it, [clusterInt:0x0001, commandInt:0x01] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return -// Other events that we expect but are not usefull for capability.Battery behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0001, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy index 13c5a44..8fe9632 100644 --- a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy @@ -190,7 +190,7 @@ case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" return -// Other events that we expect but are not usefull for capability.Brightness behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0008, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index 29ac032..cf7da9d 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -15,6 +15,7 @@ void setColor(Map colormap) { newSaturation = Math.round(newSaturation * 2.54) String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + /* groovylint-disable-next-line UnnecessarySetter */ setLevel newLevel } void setHue(BigDecimal hue) { @@ -112,5 +113,13 @@ case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: processMultipleColorAttributes msg, type return + +// Other events that we expect but are not usefull +case { contains it, [clusterInt:0x0300, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "data=${msg.data}" + return +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0003] }: // Report Attribute Current X +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0004] }: // Report Attribute Current Y + return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy index 10e90a6..852861d 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -167,7 +167,7 @@ case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x400B] }: utils_processedZclMessage 'Read Attributes Response', "ColorTemperaturePhysicalMinMireds=${msg.value} (${state.minMireds} mireds, ${Math.round(1000000 / state.minMireds)}K), ColorTemperaturePhysicalMaxMireds=${msg.value} (${state.maxMireds} mireds, ${Math.round(1000000 / state.maxMireds)}K)" return -// Other events that we expect but are not usefull for capability.ColorTemperature behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0300, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ColorTemperatureMireds, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/src/capabilities/DoubleTapableButton.groovy b/ikea-zigbee-drivers/src/capabilities/DoubleTapableButton.groovy index bd2e96c..5b6e682 100644 --- a/ikea-zigbee-drivers/src/capabilities/DoubleTapableButton.groovy +++ b/ikea-zigbee-drivers/src/capabilities/DoubleTapableButton.groovy @@ -6,6 +6,7 @@ capability 'DoubleTapableButton' {{# @implementation }} // Implementation for capability.DoubleTapableButton +void doubleTap(String buttonNumber) { doubleTap Integer.parseInt(buttonNumber) } void doubleTap(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { diff --git a/ikea-zigbee-drivers/src/capabilities/EnergyMeter.groovy b/ikea-zigbee-drivers/src/capabilities/EnergyMeter.groovy index 601f917..20704c8 100644 --- a/ikea-zigbee-drivers/src/capabilities/EnergyMeter.groovy +++ b/ikea-zigbee-drivers/src/capabilities/EnergyMeter.groovy @@ -43,7 +43,7 @@ case { contains it, [clusterInt:0x0702, commandInt:0x01, attrInt:0x0302] }: utils_processedZclMessage 'Read Attributes Response', "EnergyDivisor=${msg.value}" return -// Other events that we expect but are not usefull for capability.PowerMeter behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0702, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentSummation, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/src/capabilities/HoldableButton.groovy b/ikea-zigbee-drivers/src/capabilities/HoldableButton.groovy index 86fef93..fcd6ff8 100644 --- a/ikea-zigbee-drivers/src/capabilities/HoldableButton.groovy +++ b/ikea-zigbee-drivers/src/capabilities/HoldableButton.groovy @@ -6,6 +6,7 @@ capability 'HoldableButton' {{# @implementation }} // Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } void hold(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { diff --git a/ikea-zigbee-drivers/src/capabilities/IAS.groovy b/ikea-zigbee-drivers/src/capabilities/IAS.groovy index 3fcbbbd..834d276 100644 --- a/ikea-zigbee-drivers/src/capabilities/IAS.groovy +++ b/ikea-zigbee-drivers/src/capabilities/IAS.groovy @@ -73,7 +73,7 @@ case { contains it, [clusterInt:0x0500, commandInt:0x01, attrInt:0x0001] }: utils_processedZclMessage 'Read Attributes Response', "ZoneType=${msg.value}" return -// Other events that we expect but are not usefull for capability.IAS behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0500, commandInt:0x04, isClusterSpecific:false] }: utils_processedZclMessage 'Write Attribute Response', "attribute=IAS_CIE_Address, ZoneType=${msg.data}" return diff --git a/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy b/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy index c1c4464..a267db9 100644 --- a/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy +++ b/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy @@ -75,9 +75,9 @@ case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Module=${moduleNumber}, Switch=${newState}" return -// Other events that we expect but are not usefull for capability.MultiRelay behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=switch, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/PowerMeter.groovy b/ikea-zigbee-drivers/src/capabilities/PowerMeter.groovy index 0b7c73d..6bf168d 100644 --- a/ikea-zigbee-drivers/src/capabilities/PowerMeter.groovy +++ b/ikea-zigbee-drivers/src/capabilities/PowerMeter.groovy @@ -49,11 +49,11 @@ case { contains it, [clusterInt:0x0B04, commandInt:0x01, attrInt:0x0605] }: utils_processedZclMessage 'Read Attributes Response', "PowerDivisor=${msg.value}" return -// Other events that we expect but are not usefull for capability.PowerMeter behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0B04, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=ActivePower, data=${msg.data}" return -case { contains it, [clusterInt:0x0B04, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command +case { contains it, [clusterInt:0x0B04, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Response return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/PushableButton.groovy b/ikea-zigbee-drivers/src/capabilities/PushableButton.groovy index cd0a149..6d2ca65 100644 --- a/ikea-zigbee-drivers/src/capabilities/PushableButton.groovy +++ b/ikea-zigbee-drivers/src/capabilities/PushableButton.groovy @@ -23,6 +23,7 @@ sendEvent name:'numberOfButtons', value:numberOfButtons, descriptionText:"Number {{# @implementation }} // Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } void push(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { diff --git a/ikea-zigbee-drivers/src/capabilities/RelativeHumidity.groovy b/ikea-zigbee-drivers/src/capabilities/RelativeHumidity.groovy index 988ccd3..fe751f9 100644 --- a/ikea-zigbee-drivers/src/capabilities/RelativeHumidity.groovy +++ b/ikea-zigbee-drivers/src/capabilities/RelativeHumidity.groovy @@ -36,9 +36,9 @@ case { contains it, [clusterInt:0x0405, commandInt:0x01, attrInt:0x0000] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "RelativeHumidity=${msg.value}" return -// Other events that we expect but are not usefull for capability.RelativeHumidity behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0405, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=humidity, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=RelativeHumidity, data=${msg.data}" return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ReleasableButton.groovy b/ikea-zigbee-drivers/src/capabilities/ReleasableButton.groovy index 2bb8ee3..10e10d2 100644 --- a/ikea-zigbee-drivers/src/capabilities/ReleasableButton.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ReleasableButton.groovy @@ -6,6 +6,7 @@ capability 'ReleasableButton' {{# @implementation }} // Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } void release(BigDecimal buttonNumber) { String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) if (buttonName == null) { diff --git a/ikea-zigbee-drivers/src/capabilities/Switch.groovy b/ikea-zigbee-drivers/src/capabilities/Switch.groovy index 78346a9..f16c2d1 100644 --- a/ikea-zigbee-drivers/src/capabilities/Switch.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Switch.groovy @@ -122,7 +122,7 @@ case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x4003] }: return {{/ params.powerOnBehavior }} -// Other events that we expect but are not usefull for capability.Switch behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0006, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" return diff --git a/ikea-zigbee-drivers/src/capabilities/Temperature.groovy b/ikea-zigbee-drivers/src/capabilities/Temperature.groovy index 0ff65c6..1d63d3a 100644 --- a/ikea-zigbee-drivers/src/capabilities/Temperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Temperature.groovy @@ -36,9 +36,9 @@ case { contains it, [clusterInt:0x0402, commandInt:0x01, attrInt:0x0000] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Temperature=${msg.value}" return -// Other events that we expect but are not usefull for capability.Temperature behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x0402, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=temperature, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=Temperature, data=${msg.data}" return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy b/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy index 8f2026c..4ba6d27 100644 --- a/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ZigbeeBindings.groovy @@ -44,6 +44,7 @@ private Map retrieveSwitchDevices() { .sort { it.name } .collectEntries { [(it.zigbeeId): it.name] } } + /* groovylint-disable-next-line CatchException */ } catch (Exception ex) { return ['ZZZZ': "Exception: ${ex}"] } diff --git a/ikea-zigbee-drivers/src/devices/Aqara_DCM-K01/DCM-K01.groovy b/ikea-zigbee-drivers/src/devices/Aqara_DCM-K01/DCM-K01.groovy index cc52f6d..de9fe1f 100644 --- a/ikea-zigbee-drivers/src/devices/Aqara_DCM-K01/DCM-K01.groovy +++ b/ikea-zigbee-drivers/src/devices/Aqara_DCM-K01/DCM-K01.groovy @@ -252,7 +252,7 @@ case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x00F7] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Temperature=${temperature}, PowerOutageCount=${powerOutageCount}, SoftwareBuild=${softwareBuild}, Energy=${energy}kWh, Voltage=${voltage}V, Power=${power}W, Amperage=${amperage}A" return -// Other events that we expect but are not usefull for devices.Aqara_DCM-K01 behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0xFCC0, commandInt:0x07] }: utils_processedZclMessage 'Configure Reporting Response', "attribute=LumiSpecific, data=${msg.data}" return @@ -263,7 +263,7 @@ case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x0200] }: utils_processedZclMessage 'Report Attributes Response', "OperationMode=${msg.value}, Switch=${msg.endpoint}" return case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x000A] }: - utils_processedZclMessage 'Report Attributes Response', "switchType=${msg.value}" + utils_processedZclMessage 'Report Attributes Response', "SwitchType=${msg.value}" return case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x02D0] }: utils_processedZclMessage 'Report Attributes Response', "Interlock=${msg.value}" @@ -272,9 +272,9 @@ case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x0289] }: utils_processedZclMessage 'Report Attributes Response', "RelayMode=${msg.value}" return case { contains it, [clusterInt:0xFCC0, commandInt:0x0A, attrInt:0x00EB] }: - utils_processedZclMessage 'Report Attributes Response', "pulseDuration=${msg.value}" + utils_processedZclMessage 'Report Attributes Response', "PulseDuration=${msg.value}" return -case { contains it, [clusterInt:0xFCC0, commandInt:0x04] }: // Write Attribute Response +case { contains it, [clusterInt:0xFCC0, commandInt:0x04] }: // Write Attribute Response return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2002/E2002.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2002/E2002.groovy index 248b10b..341511c 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2002/E2002.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2002/E2002.groovy @@ -38,7 +38,6 @@ case { contains it, [clusterInt:0x0005, commandInt:0x07] }: List button = msg.data[0] == '00' ? BUTTONS.NEXT : BUTTONS.PREV utils_sendEvent name:'pushed', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was pushed" return - /* Holding the PREV and NEXT buttons works in a weird way: diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy index efe5bd5..37188ec 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy @@ -342,9 +342,11 @@ case { contains it, [clusterInt:0xFC7D, commandInt:0x01, attrInt:0x0005] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ChildLock=${msg.value}" return -// Other events that we expect but are not usefull for devices.E2006 behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0xFC7D, commandInt:0x04] }: // Write Attribute Response (0x04) -case { contains it, [clusterInt:0xFC7D, commandInt:0x07] }: // Configure Reporting Response + return +case { contains it, [clusterInt:0xFC7D, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "data=${msg.data}" return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2112/FineParticulateMatter.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2112/FineParticulateMatter.groovy index 6803edc..b7c34da 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2112/FineParticulateMatter.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2112/FineParticulateMatter.groovy @@ -64,9 +64,9 @@ case { contains it, [clusterInt:0x042A, commandInt:0x01, attrInt:0x0000] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "PM25Measurement=${pm25} μg/m³" return -// Other events that we expect but are not usefull for E2112.FineParticulateMatter behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0x042A, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=pm25, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=PM25, data=${msg.data}" return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2112/VocIndex.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2112/VocIndex.groovy index 9d74fae..68b63f1 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2112/VocIndex.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2112/VocIndex.groovy @@ -38,9 +38,9 @@ case { contains it, [clusterInt:0xFC7E, commandInt:0x01, attrInt:0x0000] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "VocIndex=${msg.value}" return -// Other events that we expect but are not usefull for E2112.VocIndex behavior +// Other events that we expect but are not usefull case { contains it, [clusterInt:0xFC7E, commandInt:0x07] }: - utils_processedZclMessage 'Configure Reporting Response', "attribute=vocIndex, data=${msg.data}" + utils_processedZclMessage 'Configure Reporting Response', "attribute=VocIndex, data=${msg.data}" return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2123/E2123.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2123/E2123.groovy index adec8b6..1784572 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2123/E2123.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2123/E2123.groovy @@ -2,11 +2,11 @@ {{# @configure }} // Configuration for devices.Ikea_E2123 -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0xFC7F {${device.zigbeeId}} {}" // Unknown 64639 cluster --> For firmware 1.0.012 -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 -cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 +cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster +cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster +cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0xFC7F {${device.zigbeeId}} {}" // Unknown 64639 cluster --> For firmware 1.0.012 +cmds += "zdo bind 0x${device.deviceNetworkId} 0x02 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 +cmds += "zdo bind 0x${device.deviceNetworkId} 0x03 0x01 0xFC80 {${device.zigbeeId}} {}" // Heiman - Specific Scenes cluster --> For firmware 1.0.35 {{/ @configure }} {{!--------------------------------------------------------------------------}} {{# @events }} diff --git a/ikea-zigbee-drivers/src/devices/Legrand_741811/741811.groovy b/ikea-zigbee-drivers/src/devices/Legrand_741811/741811.groovy index 5cbd629..071d82c 100644 --- a/ikea-zigbee-drivers/src/devices/Legrand_741811/741811.groovy +++ b/ikea-zigbee-drivers/src/devices/Legrand_741811/741811.groovy @@ -7,8 +7,8 @@ input( title: 'LED mode', description: 'Select how the LED indicator behaves.', options: [ - 'ALWAYS_ON': 'Always On - LED remains lit at all times, making it easy to find in the dark', - 'ALWAYS_OFF': 'Always Off - LED remains off, ensuring total darkness', + 'ALWAYS_ON': 'Always On - LED remains lit at all times, making it easy to find in the dark', + 'ALWAYS_OFF': 'Always Off - LED remains off, ensuring total darkness', 'OUTLET_STATUS': 'Outlet status - LED indicates the power state of the outlet' ], defaultValue: 'OUTLET_STATUS', @@ -26,16 +26,16 @@ if (ledMode == null) { log_info "🛠️ ledMode = ${ledMode}" switch (ledMode) { case 'ALWAYS_ON': - cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x01, [mfgCode: '0x1021']) // Write LED Mode attribute - cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x01, [mfgCode: '0x1021']) // Write LED Mode attributes + cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x01, [mfgCode: '0x1021']) + cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x01, [mfgCode: '0x1021']) break case 'ALWAYS_OFF': - cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x00, [mfgCode: '0x1021']) // Write LED Mode attribute - cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x00, [mfgCode: '0x1021']) // Write LED Mode attributes + cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x00, [mfgCode: '0x1021']) + cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x00, [mfgCode: '0x1021']) break default: - cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x01, [mfgCode: '0x1021']) // Write LED Mode attribute - cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x00, [mfgCode: '0x1021']) // Write LED Mode attributes + cmds += zigbee.writeAttribute(0xFC01, 0x0001, 0x10, 0x00, [mfgCode: '0x1021']) + cmds += zigbee.writeAttribute(0xFC01, 0x0002, 0x10, 0x01, [mfgCode: '0x1021']) } {{/ @updated }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml b/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml index d430ad5..bff1e32 100644 --- a/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml @@ -11,6 +11,8 @@ device: fingerprints: - firmwares: 003e (1021-0011-003E4203) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' + - firmwares: 002f + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006,000F', outClusters:'0006,0000,FC01,0005,0019', model:' Mobile outlet', manufacturer:' Legrand' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Philips_RDM001/RDM001.groovy b/ikea-zigbee-drivers/src/devices/Philips_RDM001/RDM001.groovy index 14be9fc..41d7f81 100644 --- a/ikea-zigbee-drivers/src/devices/Philips_RDM001/RDM001.groovy +++ b/ikea-zigbee-drivers/src/devices/Philips_RDM001/RDM001.groovy @@ -102,7 +102,7 @@ case { contains it, [endpointInt:0x01, clusterInt:0x0000, commandInt:0x0A, attrI return // Other events that we expect but are not usefull -case { contains it, [clusterInt:0x0000, commandInt:0x07] }: // ConfigureReportingResponse +case { contains it, [clusterInt:0x0000, commandInt:0x07] }: // Configure Reporting Response return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Philips_RWL022/RWL022.groovy b/ikea-zigbee-drivers/src/devices/Philips_RWL022/RWL022.groovy index 6d2c30c..ea0d03c 100644 --- a/ikea-zigbee-drivers/src/devices/Philips_RWL022/RWL022.groovy +++ b/ikea-zigbee-drivers/src/devices/Philips_RWL022/RWL022.groovy @@ -33,7 +33,7 @@ case { contains it, [clusterInt:0xFC00, commandInt:0x00] }: // Other events that we expect but are not usefull case { contains it, [clusterInt:0x0000, commandInt:0x04, isClusterSpecific:false] }: - utils_processedZclMessage 'Write Attribute Response', 'attribute=Philips magic attribute' + utils_processedZclMessage 'Write Attribute Response', 'attribute=Philips Magic Attribute' return {{/ @events }} {{!--------------------------------------------------------------------------}} From 9bfe71a0716bb813b128345372485a597910878d Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Mon, 15 Apr 2024 23:24:03 +0300 Subject: [PATCH 06/26] Add Scenes support using Zigbee Bindings for E1810 and E2002 --- ikea-zigbee-drivers/CHANGELOG.md | 1 + ikea-zigbee-drivers/Ikea_E1810.groovy | 3 ++- ikea-zigbee-drivers/Ikea_E2002.groovy | 3 ++- ikea-zigbee-drivers/README.md | 4 ++-- ikea-zigbee-drivers/packageManifest.json | 2 +- ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml | 2 +- ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 031260a..5400808 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add driver for Dimmable Lights devices, including LED drivers - Add driver for White Spectrum Lights devices (CT) - Add driver for Color White Spectrum Lights devices (RGB+CT) +- Add Scenes support using Zigbee Bindings for E1810 and E2002 ### Changed - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index 4ef4e4e..968589c 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -113,8 +113,9 @@ List updated(boolean auto = false) { log_info '🛠️ Clearing all device bindings' state.stopControlling = 'devices' } else { - log_info "🛠️ Adding binding to device #${controlDevice} for clusters [0x0006 0x0008]" + log_info "🛠️ Adding binding to device #${controlDevice} for clusters [0x0005 0x0006 0x0008]" + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0005'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0005 cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0006'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0006 cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0008'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0008 } diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index c86cc4e..e819657 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -113,8 +113,9 @@ List updated(boolean auto = false) { log_info '🛠️ Clearing all device bindings' state.stopControlling = 'devices' } else { - log_info "🛠️ Adding binding to device #${controlDevice} for clusters [0x0006 0x0008]" + log_info "🛠️ Adding binding to device #${controlDevice} for clusters [0x0005 0x0006 0x0008]" + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0005'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0005 cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0006'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0006 cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0008'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0008 } diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 7116d64..b43edcf 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -257,7 +257,7 @@ Below you can find the details of each device, including the features and pairin * Button Release events for: Button 1 (🔆), Button 2 (🔅) * Battery report: % * Health status: online / offline -* Directly control Zigbee devices: On/Off and Brightness +* Directly control Zigbee devices: On/Off, Brightness and Scenes #### Known Issues * The Hold / Release events don't work correctly on the Next and Prev buttons. @@ -441,7 +441,7 @@ Below you can find the details of each device, including the features and pairin * Button 2 (🔆) and Button 3 (🔅) act as a switch level (0 - 100%) * Battery report: % * Health status: online / offline -* Directly control Zigbee devices: On/Off and Brightness +* Directly control Zigbee devices: On/Off, Brightness and Scenes #### Known Issues diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 1697e6b..c1a3c66 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-04-??", diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml index 9f2e08e..1bc952d 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml @@ -35,5 +35,5 @@ device: - file: src/capabilities/ReleasableButton.groovy - file: src/capabilities/ZigbeeBindings.groovy params: - clusters: ['0x0006', '0x0008'] + clusters: ['0x0005', '0x0006', '0x0008'] - file: src/capabilities/FirmwareUpdate.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml index 07e7c1b..8e21f6e 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml @@ -36,5 +36,5 @@ device: - file: src/capabilities/ReleasableButton.groovy - file: src/capabilities/ZigbeeBindings.groovy params: - clusters: ['0x0006', '0x0008'] + clusters: ['0x0005', '0x0006', '0x0008'] - file: src/capabilities/FirmwareUpdate.groovy From fd23e6d5f6713eb06412005c8b31617fbf521b47 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Tue, 23 Apr 2024 21:50:54 +0300 Subject: [PATCH 07/26] Put all devices in "identifying mode" while `configure()` is running - `@UncleAlias` --- ikea-zigbee-drivers/Aqara_DCM-K01.groovy | 17 +- ikea-zigbee-drivers/CHANGELOG.md | 1 + ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 41 +- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 9 +- ikea-zigbee-drivers/Ikea_E1603.groovy | 8 +- ikea-zigbee-drivers/Ikea_E1743.groovy | 8 +- ikea-zigbee-drivers/Ikea_E1745.groovy | 8 +- ikea-zigbee-drivers/Ikea_E1766.groovy | 8 +- ikea-zigbee-drivers/Ikea_E1810.groovy | 8 +- ikea-zigbee-drivers/Ikea_E1812.groovy | 8 +- ikea-zigbee-drivers/Ikea_E1836.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2002.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2006.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2013.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2112.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2123.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2134.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2201.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2202.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2204.groovy | 8 +- ikea-zigbee-drivers/Ikea_E2213.groovy | 8 +- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 9 +- ikea-zigbee-drivers/Legrand_741811.groovy | 11 +- ikea-zigbee-drivers/Philips_RDM001.groovy | 8 +- ikea-zigbee-drivers/Philips_RWL022.groovy | 8 +- .../Schneider_CCTFR6600.groovy | 591 ++++++++++++++++++ ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy | 8 +- .../img/Schneider_CCTFR6600.webp | Bin 0 -> 4006 bytes ikea-zigbee-drivers/packageManifest.json | 2 +- ikea-zigbee-drivers/src/blueprint.groovy | 8 +- .../src/capabilities/Brightness.groovy | 1 + .../src/capabilities/ColorControl.groovy | 38 +- .../src/capabilities/MultiRelay.groovy | 9 +- .../src/devices/Legrand_741811/config.yaml | 6 +- .../Schneider_CCTFR6600/CCTFR6600.groovy | 240 +++++++ .../devices/Schneider_CCTFR6600/config.yaml | 22 + 36 files changed, 1071 insertions(+), 94 deletions(-) create mode 100644 ikea-zigbee-drivers/Schneider_CCTFR6600.groovy create mode 100644 ikea-zigbee-drivers/img/Schneider_CCTFR6600.webp create mode 100644 ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/CCTFR6600.groovy create mode 100644 ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/config.yaml diff --git a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy index 07e92a4..13f519c 100644 --- a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy +++ b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy @@ -11,6 +11,7 @@ import groovy.transform.Field // Fields for capability.MultiRelay import com.hubitat.app.ChildDeviceWrapper +import com.hubitat.app.DeviceWrapper // Fields for capability.PushableButton @Field static final Map> BUTTONS = [ @@ -268,7 +269,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -311,6 +312,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -356,19 +358,19 @@ private ChildDeviceWrapper fetchChildDevice(Integer moduleNumber) { return childDevice ?: addChildDevice('hubitat', 'Generic Component Switch', "${device.deviceNetworkId}-${moduleNumber}", [name:"${device.displayName} - Relay L${moduleNumber}", label:"Relay L${moduleNumber}", isComponent:true]) } -void componentOff(ChildDeviceWrapper childDevice) { +void componentOff(DeviceWrapper childDevice) { log_debug "▲ Received Off request from ${childDevice.displayName}" Integer endpointInt = Integer.parseInt(childDevice.deviceNetworkId.split('-')[1]) utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${endpointInt} 0x0006 {014300}"]) } -void componentOn(ChildDeviceWrapper childDevice) { +void componentOn(DeviceWrapper childDevice) { log_debug "▲ Received On request from ${childDevice.displayName}" Integer endpointInt = Integer.parseInt(childDevice.deviceNetworkId.split('-')[1]) utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${endpointInt} 0x0006 {014301}"]) } -void componentRefresh(ChildDeviceWrapper childDevice) { +void componentRefresh(DeviceWrapper childDevice) { log_debug "▲ Received Refresh request from ${childDevice.displayName}" refresh() } @@ -650,7 +652,7 @@ void parse(String description) { childDevice.parse([[name:'switch', value:newState, descriptionText:"${childDevice.displayName} was turned ${newState}", type:type]]) } - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Module=${moduleNumber}, Switch=${newState}" + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Relay=${moduleNumber}, Switch=${newState}" return // Other events that we expect but are not usefull @@ -692,7 +694,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -705,7 +708,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 5400808..ae199ef 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add driver for White Spectrum Lights devices (CT) - Add driver for Color White Spectrum Lights devices (RGB+CT) - Add Scenes support using Zigbee Bindings for E1810 and E2002 +- Put all devices in "identifying mode" while `configure()` is running - `@UncleAlias` ### Changed - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 785660d..5f0a82f 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -9,6 +9,12 @@ import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA Color White Spectrum Light' @Field static final String DRIVER_VERSION = '5.0.0' +// Fields for capability.ColorControl + +@Field static final Map COLOR_LOOP_SPEED = [ + 'switft':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 +] + // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -31,6 +37,7 @@ metadata { capability 'ColorControl' capability 'ColorTemperature' capability 'ColorMode' + capability 'Light' capability 'ChangeLevel' capability 'SwitchLevel' capability 'HealthCheck' @@ -47,6 +54,10 @@ metadata { command 'toggle' command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + // Commands for capability.ColorControl + command 'startColorLoop', [[name:'Speed*', type:'ENUM', constraints: COLOR_LOOP_SPEED.keySet()]] + command 'stopColorLoop' + // Commands for capability.ColorTemperature command 'startColorTemperatureChange', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] command 'stopColorTemperatureChange' @@ -346,7 +357,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -363,8 +374,9 @@ void configure(boolean auto = false) { cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes // Configuration for capability.ColorControl - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1) - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {01} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x4000 0x21 0x0002 0xFFFE {CB0C} {}" // Report EnhancedCurrentHue (uint16) at most every 2 seconds (Δ = 5%) // Configuration for capability.ColorTemperature cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster @@ -388,6 +400,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -477,6 +490,17 @@ void setSaturation(BigDecimal saturation) { String payload = "${utils_payload newSaturation, 2} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } +void startColorLoop(String speed) { + Integer seconds = COLOR_LOOP_SPEED[speed] + log_debug "Starting color loop with ${seconds} seconds / loop" + String payload = "0F 01 01 ${utils_payload seconds, 4} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set +} +void stopColorLoop() { + log_debug "Stopping color loop" + String payload = "0F 00 01 0000 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set +} private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] attributes[msg.attrInt] = msg.value @@ -500,6 +524,9 @@ private void processMultipleColorAttributes(Map msg, String type) { colorMode = it.value == '02' ? 'CT' : 'RGB' utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type break + case 0x4000: + hue = Math.round(Integer.parseInt(it.value, 16) / 655.34) + hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) } } @@ -734,6 +761,9 @@ void parse(String description) { // Report/Read Attributes Reponse: EnhancedColorMode case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + + // Report Attributes Reponse: EnhancedCurrentHue + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4000] }: processMultipleColorAttributes msg, type return @@ -882,7 +912,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -895,7 +926,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index 69a17b6..9aa4595 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -28,6 +28,7 @@ metadata { capability 'Refresh' capability 'Actuator' capability 'Switch' + capability 'Light' capability 'ChangeLevel' capability 'SwitchLevel' capability 'HealthCheck' @@ -297,7 +298,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -329,6 +330,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -626,7 +628,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -639,7 +642,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1603.groovy b/ikea-zigbee-drivers/Ikea_E1603.groovy index d296ab5..5295bc3 100644 --- a/ikea-zigbee-drivers/Ikea_E1603.groovy +++ b/ikea-zigbee-drivers/Ikea_E1603.groovy @@ -175,7 +175,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -203,6 +203,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -450,7 +451,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -463,7 +465,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index 8d6e829..2d7b8ae 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -154,7 +154,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -190,6 +190,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -524,7 +525,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -537,7 +539,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1745.groovy b/ikea-zigbee-drivers/Ikea_E1745.groovy index a2e96b0..49c9064 100644 --- a/ikea-zigbee-drivers/Ikea_E1745.groovy +++ b/ikea-zigbee-drivers/Ikea_E1745.groovy @@ -217,7 +217,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -248,6 +248,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -574,7 +575,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -587,7 +589,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1766.groovy b/ikea-zigbee-drivers/Ikea_E1766.groovy index bb2a676..1a57560 100644 --- a/ikea-zigbee-drivers/Ikea_E1766.groovy +++ b/ikea-zigbee-drivers/Ikea_E1766.groovy @@ -153,7 +153,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -188,6 +188,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -515,7 +516,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -528,7 +530,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index 968589c..8f6b2f2 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -158,7 +158,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -195,6 +195,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -554,7 +555,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -567,7 +569,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1812.groovy b/ikea-zigbee-drivers/Ikea_E1812.groovy index 8c66960..bf3c6d2 100644 --- a/ikea-zigbee-drivers/Ikea_E1812.groovy +++ b/ikea-zigbee-drivers/Ikea_E1812.groovy @@ -154,7 +154,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -190,6 +190,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -539,7 +540,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -552,7 +554,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E1836.groovy b/ikea-zigbee-drivers/Ikea_E1836.groovy index 8740df9..18dcf99 100644 --- a/ikea-zigbee-drivers/Ikea_E1836.groovy +++ b/ikea-zigbee-drivers/Ikea_E1836.groovy @@ -174,7 +174,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -202,6 +202,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -449,7 +450,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -462,7 +464,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index e819657..3e8c149 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -158,7 +158,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -195,6 +195,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -562,7 +563,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -575,7 +577,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2006.groovy b/ikea-zigbee-drivers/Ikea_E2006.groovy index bc96a4b..e3c98c4 100644 --- a/ikea-zigbee-drivers/Ikea_E2006.groovy +++ b/ikea-zigbee-drivers/Ikea_E2006.groovy @@ -212,7 +212,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -250,6 +250,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -616,7 +617,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -629,7 +631,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2013.groovy b/ikea-zigbee-drivers/Ikea_E2013.groovy index 72b547f..ac25227 100644 --- a/ikea-zigbee-drivers/Ikea_E2013.groovy +++ b/ikea-zigbee-drivers/Ikea_E2013.groovy @@ -193,7 +193,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -228,6 +228,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -589,7 +590,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -602,7 +604,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2112.groovy b/ikea-zigbee-drivers/Ikea_E2112.groovy index 7797400..5496f27 100644 --- a/ikea-zigbee-drivers/Ikea_E2112.groovy +++ b/ikea-zigbee-drivers/Ikea_E2112.groovy @@ -130,7 +130,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -170,6 +170,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -441,7 +442,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -454,7 +456,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2123.groovy b/ikea-zigbee-drivers/Ikea_E2123.groovy index 1007f51..d78294b 100644 --- a/ikea-zigbee-drivers/Ikea_E2123.groovy +++ b/ikea-zigbee-drivers/Ikea_E2123.groovy @@ -185,7 +185,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -224,6 +224,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -643,7 +644,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -656,7 +658,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index 2b97e7e..ce07b15 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -172,7 +172,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -207,6 +207,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -549,7 +550,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -562,7 +564,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2201.groovy b/ikea-zigbee-drivers/Ikea_E2201.groovy index 45c3283..a1d9a68 100644 --- a/ikea-zigbee-drivers/Ikea_E2201.groovy +++ b/ikea-zigbee-drivers/Ikea_E2201.groovy @@ -180,7 +180,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -216,6 +216,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -565,7 +566,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -578,7 +580,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index 8c42002..127e70f 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -128,7 +128,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -163,6 +163,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -412,7 +413,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -425,7 +427,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2204.groovy b/ikea-zigbee-drivers/Ikea_E2204.groovy index 939d181..56f07f4 100644 --- a/ikea-zigbee-drivers/Ikea_E2204.groovy +++ b/ikea-zigbee-drivers/Ikea_E2204.groovy @@ -203,7 +203,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -231,6 +231,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -503,7 +504,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -516,7 +518,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_E2213.groovy b/ikea-zigbee-drivers/Ikea_E2213.groovy index 33ba0db..d2c1f2b 100644 --- a/ikea-zigbee-drivers/Ikea_E2213.groovy +++ b/ikea-zigbee-drivers/Ikea_E2213.groovy @@ -130,7 +130,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -166,6 +166,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -424,7 +425,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -437,7 +439,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index 751ad77..4947354 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -30,6 +30,7 @@ metadata { capability 'Switch' capability 'ColorTemperature' capability 'ColorMode' + capability 'Light' capability 'ChangeLevel' capability 'SwitchLevel' capability 'HealthCheck' @@ -342,7 +343,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -380,6 +381,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -776,7 +778,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -789,7 +792,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Legrand_741811.groovy b/ikea-zigbee-drivers/Legrand_741811.groovy index 4d50c99..72ce4ce 100644 --- a/ikea-zigbee-drivers/Legrand_741811.groovy +++ b/ikea-zigbee-drivers/Legrand_741811.groovy @@ -32,8 +32,9 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // Firmware: 003e (1021-0011-003E4203) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006,000F', outClusters:'0006,0000,FC01,0005,0019', model:' Mobile outlet', manufacturer:' Legrand' // Firmware: 002f + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // Firmware: 003e (1021-0011-003E4203) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' // Firmware: 0053 (1021-0011-005343FF) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -209,7 +210,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -241,6 +242,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -536,7 +538,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -549,7 +552,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Philips_RDM001.groovy b/ikea-zigbee-drivers/Philips_RDM001.groovy index b1f57c8..7d8f4d2 100644 --- a/ikea-zigbee-drivers/Philips_RDM001.groovy +++ b/ikea-zigbee-drivers/Philips_RDM001.groovy @@ -210,7 +210,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -253,6 +253,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -630,7 +631,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -643,7 +645,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Philips_RWL022.groovy b/ikea-zigbee-drivers/Philips_RWL022.groovy index 6d73064..b0a3795 100644 --- a/ikea-zigbee-drivers/Philips_RWL022.groovy +++ b/ikea-zigbee-drivers/Philips_RWL022.groovy @@ -182,7 +182,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -223,6 +223,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -572,7 +573,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -585,7 +587,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy b/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy new file mode 100644 index 0000000..c25ce08 --- /dev/null +++ b/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy @@ -0,0 +1,591 @@ +/** + * Schneider Wiser UFH (CCTFR6600) + * + * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ + */ +import groovy.transform.CompileStatic +import groovy.transform.Field + +@Field static final String DRIVER_NAME = 'Schneider Wiser UFH (CCTFR6600)' +@Field static final String DRIVER_VERSION = '5.0.0' + +// Fields for capability.HealthCheck +import groovy.time.TimeCategory + +@Field static final Map HEALTH_CHECK = [ + 'schedule': '0 0 0/1 ? * * *', // Health will be checked using this cron schedule + 'thereshold': '3600' // When checking, mark the device as offline if no Zigbee message was received in the last 3600 seconds +] + +// Fields for devices.Schneider_CCTFR6600 +@Field static final List ZONES = [ + 'zone_1', 'zone_2', 'zone_3', 'zone_4', 'zone_5', 'zone_6' +] + +metadata { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy') { + capability 'Configuration' + capability 'Refresh' + capability 'HealthCheck' + capability 'PowerSource' + capability 'Actuator' + capability 'SignalStrength' + + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0006,0201,0B05,FE03,FF16', outClusters:'0003,0019', model:'UFH', manufacturer:'Schneider Electric' // Firmware: 9ae667b (105E-0A00-00007D00) + + // Attributes for capability.HealthCheck + attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] + + // Attributes for devices.Schneider_CCTFR6600 + attribute 'zone_1', 'enum', ['on', 'off'] + attribute 'zone_2', 'enum', ['on', 'off'] + attribute 'zone_3', 'enum', ['on', 'off'] + attribute 'zone_4', 'enum', ['on', 'off'] + attribute 'zone_5', 'enum', ['on', 'off'] + attribute 'zone_6', 'enum', ['on', 'off'] + attribute 'pump', 'enum', ['on', 'off'] + attribute 'boiler', 'enum', ['on', 'off'] + attribute 'debug', 'string' + } + + // Commands for devices.Schneider_CCTFR6600 + command 'on', [[name:'Zone*', type:'ENUM', description:'Zone to turn On', constraints:ZONES]] + command 'off', [[name:'Zone*', type:'ENUM', description:'Zone to turn Off', constraints:ZONES]] + command 'exec', [[name:'Zigbee command', description:'Enter raw command to execute (e.g. for toggle on/off: he raw .addr 0x01 0x01 0x0006 {114302})', type:'STRING']] + + preferences { + input( + name: 'helpInfo', type: 'hidden', + title: ''' +
+ Schneider Wiser UFH (CCTFR6600) v5.0.0
+ +
+ ''' + ) + input( + name: 'logLevel', type: 'enum', + title: 'Log verbosity', + description: 'Select what type of messages appear in the "Logs" section.', + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], + defaultValue: '1', + required: true + ) + } +} + +// =================================================================================================================== +// Implement default methods +// =================================================================================================================== + +// Called when the device is first added +void installed() { + log_warn 'Installing device ...' + log_warn '[IMPORTANT] For battery-powered devices, make sure that you keep your device as close as you can (less than 2inch / 5cm) to your Hubitat hub for at least 30 seconds. Otherwise the device will successfully pair but it won\'t work properly!' +} + +// Called when the "Save Preferences" button is clicked +List updated(boolean auto = false) { + log_info "Saving preferences${auto ? ' (auto)' : ''} ..." + List cmds = [] + + unschedule() + + if (logLevel == null) { + logLevel = '1' + device.updateSetting 'logLevel', [value:logLevel, type:'enum'] + } + if (logLevel == '1') runIn 1800, 'logsOff' + log_info "🛠️ logLevel = ${['1':'Debug', '2':'Info', '3':'Warning', '4':'Error'].get(logLevel)}" + + // Preferences for capability.HealthCheck + schedule HEALTH_CHECK.schedule, 'healthCheck' + + if (auto) return cmds + utils_sendZigbeeCommands cmds + return [] +} + +// =================================================================================================================== +// Capabilities helpers +// =================================================================================================================== + +// Handler method for scheduled job to disable debug logging +void logsOff() { + log_info '⏲️ Automatically reverting log level to "Info"' + device.updateSetting 'logLevel', [value:'2', type:'enum'] +} + +// Helpers for capability.HealthCheck +void healthCheck() { + log_debug '⏲️ Automatically running health check' + String healthStatus = state.lastRx == 0 || state.lastRx == null ? 'unknown' : (now() - state.lastRx < Integer.parseInt(HEALTH_CHECK.thereshold) * 1000 ? 'online' : 'offline') + utils_sendEvent name:'healthStatus', value:healthStatus, type:'physical', descriptionText:"Health status is ${healthStatus}" +} + +// =================================================================================================================== +// Implement Capabilities +// =================================================================================================================== + +// capability.Configuration +// Note: This method is also called when the device is initially installed +void configure(boolean auto = false) { + log_warn "Configuring device${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Configure" button immediately after pushing any button on the device in order to first wake it up!' + } + + // Apply preferences first + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] + cmds += updated true + + // Clear data (keep firmwareMT information though) + device.data*.key.each { if (it != 'firmwareMT') device.removeDataValue it } + + // Clear state + state.clear() + state.lastTx = 0 + state.lastRx = 0 + state.lastCx = DRIVER_VERSION + + // Configuration for capability.HealthCheck + sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' + sendEvent name:'checkInterval', value:3600, unit:'second', descriptionText:'Health check interval is 3600 seconds' + + // Configuration for capability.PowerSource + sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + + // Configuration for Schneider_CCTFR6600.Switch + ZONES.each { off it } + + cmds += "zdo bind 0x${device.deviceNetworkId} 0x07 0x01 0x0006 {${device.zigbeeId}} {}" // Pump endpoint + //cmds += "he cr 0x${device.deviceNetworkId} 0x07 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report pump status at least every 10 minutes + + cmds += "zdo bind 0x${device.deviceNetworkId} 0x08 0x01 0x0006 {${device.zigbeeId}} {}" // Boiler endpoint + //cmds += "he cr 0x${device.deviceNetworkId} 0x08 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report boiler status at least every 10 minutes + + ZONES.each { + cmds += "he cr 0x${device.deviceNetworkId} 0x0${it.substring(5)} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report zones[1-6] status at least every 10 minutes + } + + // Query Basic cluster attributes + cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID + cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier + cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" + utils_sendZigbeeCommands cmds + + log_info 'Configuration done; refreshing device current state in 7 seconds ...' + runIn 7, 'refresh', [data:true] +} +/* groovylint-disable-next-line UnusedPrivateMethod */ +private void autoConfigure() { + log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" + configure true +} + +// capability.Refresh +void refresh(boolean auto = false) { + log_warn "Refreshing device state${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Refresh" button immediately after pushing any button on the device in order to first wake it up!' + } + + List cmds = [] + + // Refresh for devices.Schneider_CCTFR6600 + cmds += zigbee.readAttribute(0xFE03, 0x0020, [mfgCode: '0x105E']) // Wiser Debug Info + + ZONES.each { cmds += zigbee.readAttribute(0x0006, 0x0000, [destEndpoint:Integer.parseInt(it.substring(5))]) } // Zones[1-6] OnOff + cmds += zigbee.readAttribute(0x0006, 0x0000, [destEndpoint:0x07]) // Pump OnOff + cmds += zigbee.readAttribute(0x0006, 0x0000, [destEndpoint:0x08]) // Boiler OnOff + utils_sendZigbeeCommands cmds +} + +// Implementation for capability.HealthCheck +void ping() { + log_warn 'ping ...' + utils_sendZigbeeCommands(zigbee.readAttribute(0x0000, 0x0000)) + log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' + runIn 5, 'pingExecute' +} +void pingExecute() { + if (state.lastRx == 0) { + log_info 'Did not sent any messages since it was last configured' + return + } + + Date now = new Date(Math.round(now() / 1000) * 1000) + Date lastRx = new Date(Math.round(state.lastRx / 1000) * 1000) + String lastRxAgo = TimeCategory.minus(now, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Sent last message at ${lastRx.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${lastRxAgo} ago)" + + Date thereshold = new Date(Math.round(state.lastRx / 1000 + Integer.parseInt(HEALTH_CHECK.thereshold)) * 1000) + String theresholdAgo = TimeCategory.minus(thereshold, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received for ${theresholdAgo} (hardcoded)" + + String offlineMarkAgo = TimeCategory.minus(thereshold, now).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received until ${thereshold.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${offlineMarkAgo} from now)" +} + +// Implementation for devices.Schneider_CCTFR6600 +void on(String zone) { + if (!ZONES.contains(zone)) { + log_error "Invalid zone: ${zone}. Available zones: ${ZONES}" + return + } + utils_sendEvent name:zone, value:'on', descriptionText:"Zone ${zone} was turned on", type:'digital' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${zone.substring(5)} 0x0006 {014301}"]) +} + +void off(String zone) { + if (!ZONES.contains(zone)) { + log_error "Invalid zone: ${zone}. Available zones: ${ZONES}" + return + } + utils_sendEvent name:zone, value:'off', descriptionText:"Zone ${zone} was turned off", type:'digital' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${zone.substring(5)} 0x0006 {014300}"]) +} + +void exec(String command) { + log_info "Exec: ${command}" + String cmd = command.replace '.addr', "0x${device.deviceNetworkId}" + utils_sendZigbeeCommands([cmd]) +} + +private String attrValue(Integer attr, Integer type, Integer value, Integer bytes) { + return "${utils_payload attr, 4}00${utils_payload type, 2}${utils_payload value, bytes}" +} + +// =================================================================================================================== +// Handle incoming Zigbee messages +// =================================================================================================================== + +void parse(String description) { + log_debug "description=[${description}]" + + // Auto-Configure device: configure() was not called for this driver version + if (state.lastCx != DRIVER_VERSION) { + state.lastCx = DRIVER_VERSION + runInMillis 1500, 'autoConfigure' + } + + // Extract msg + Map msg = [:] + if (description.startsWith('zone status')) msg += [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] + if (description.startsWith('enroll request')) msg += [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] + + msg += zigbee.parseDescriptionAsMap description + if (msg.containsKey('endpoint')) msg.endpointInt = Integer.parseInt(msg.endpoint, 16) + if (msg.containsKey('sourceEndpoint')) msg.endpointInt = Integer.parseInt(msg.sourceEndpoint, 16) + if (msg.containsKey('cluster')) msg.clusterInt = Integer.parseInt(msg.cluster, 16) + if (msg.containsKey('command')) msg.commandInt = Integer.parseInt(msg.command, 16) + log_debug "msg=[${msg}]" + + state.lastRx = now() + + // Parse for capability.HealthCheck + if (device.currentValue('healthStatus', true) != 'online') { + utils_sendEvent name:'healthStatus', value:'online', type:'digital', descriptionText:'Health status changed to online' + } + + // If we sent a Zigbee command in the last 3 seconds, we assume that this Zigbee event is a consequence of this driver doing something + // Therefore, we mark this event as "digital" + String type = state.containsKey('lastTx') && (now() - state.lastTx < 3000) ? 'digital' : 'physical' + + switch (msg) { + + // Events for capability.HealthCheck + // =================================================================================================================== + + case { contains it, [clusterInt:0x0000, attrInt:0x0000] }: + log_warn '... pong' + return + + // Configuration for capability.PowerSource + // =================================================================================================================== + + // Read Attributes Reponse: PowerSource + case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: + String powerSource = 'unknown' + + // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } + switch (msg.value) { + case ['01', '02', '05', '06']: + powerSource = 'mains'; break + case '03': + powerSource = 'battery'; break + case '04': + powerSource = 'dc' + } + utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" + utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" + return + + // Events for devices.Schneider_CCTFR6600 + // =================================================================================================================== + + // Read Attributes: ZCL Version + case { contains it, [clusterInt:0x0000, commandInt:0x00, data:['00', '00']] }: + Integer frameControl = 0x08 + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = attrValue(0x0000, 0x20, 0x03, 2) // ZCL Version = 0x03 + + // Send response only once every 5 minutes + //if (Calendar.getInstance().get(Calendar.MINUTE) % 5 == 0) + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0000 {${utils_payload frameControl, 2}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes (health check)', "endpoint=${msg.sourceEndpoint}, cluster=Basic, attr=0000 (ZCL Version)" + return + + // ▶ Processed ZCL message: type=Read Attributes, endpoint=03, manufacturer=0000, cluster=0006, attrs=[0000] + case { contains it, [clusterInt:0x0006, commandInt:0x00, data:['00', '00']] }: + String zone = "zone_${Integer.parseInt msg.sourceEndpoint, 16}" + boolean status = device.currentValue(zone, true) == 'on' + + Integer frameControl = Integer.parseInt('00001000', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = attrValue(0x0000, 0x10, status ? 0x01 : 0x00, 2) + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${msg.sourceEndpoint} 0x0006 {${utils_payload frameControl, 2}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=OnOff, attr=0000 (OnOff)" + return + + // ▶ Processed ZCL message: type=Read Attributes, endpoint=03, manufacturer=105E, cluster=0006, attrs=[E002] + case { contains it, [clusterInt:0x0006, commandInt:0x00, data:['02', 'E0']] }: + Integer frameControl = Integer.parseInt('00001100', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = attrValue(0xE002, 0x10, 0x00, 2) + Integer mfgCode = 0x105E + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${msg.sourceEndpoint} 0x0006 {${utils_payload frameControl, 2}${utils_payload mfgCode, 4}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=OnOff, attr=E002 (Unknown)" + return + + // ▶ Processed ZCL message: type=Read Attributes, endpoint=03, manufacturer=105E, cluster=FF16, attrs=[0000, 0001, 0002, 0010, 0011, 0012, 0030] + case { contains it, [clusterInt:0xFF16, commandInt:0x00, data:['00', '00', '01', '00', '02', '00', '10', '00', '11', '00', '12', '00', '30', '00']] }: + Integer frameControl = Integer.parseInt('00001100', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = '' + payload += attrValue(0x0000, 0x20, 0xC8, 2) + ' ' // 0x0000 : 0x64 = 100 (uint8) + payload += '010086 ' // 0x0001 : UNSUPPORTED_ATTRIBUTE + payload += '020086 ' // 0x0002 : UNSUPPORTED_ATTRIBUTE + payload += attrValue(0x0010, 0x21, 0x04B0, 4) + ' ' // 0x0010 : 0258 = 600 (uint16) + payload += attrValue(0x0011, 0x21, 0x0258, 4) + ' ' // 0x0011 : 012C = 300 (uint16) + payload += attrValue(0x0012, 0x21, 0x1C20, 4) + ' ' // 0x0012 : 0E10 = 3600 (uint16) + payload += attrValue(0x0030, 0x20, 0x42, 2) // 0x0030 : 0x21 = 33 (uint8) + + Integer mfgCode = 0x105E + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${msg.sourceEndpoint} 0xFF16 {${utils_payload frameControl, 2}${utils_payload mfgCode, 4}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=FF16, attrs=[0000, 0001, 0002, 0010, 0011, 0012, 0030]" + return + + // ▶ Processed ZCL message: type=Read Attributes, endpoint=01, manufacturer=0000, cluster=0201, attrs=[001C, 0015, 0016, 0017, 0018] + case { contains it, [clusterInt:0x0201, commandInt:0x00, data:['1C', '00', '15', '00', '16', '00', '17', '00', '18', '00']] }: + Integer frameControl = Integer.parseInt('00001100', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = '' + payload += attrValue(0x001C, 0x30, 0x04, 2) + ' ' // 0x0000 : 0x03 = Cool (enum8) | 0x00:Off, 0x01:Auto, 0x03:Cool, 0x04:Heat, 0x05:Emergency heating + // | 0x06:Precooling, 0x07:Fan only, 0x08:Dry, 0x09:Sleep + payload += attrValue(0x0015, 0x29, 0x954D, 4) + ' ' // 0x0015 : 954D = -54.53°C (int16) | Min Heat Setpoint Limit + payload += attrValue(0x0016, 0x29, 0x7FFF, 4) + ' ' // 0x0016 : 7FFF = 327.67°C (int16) | Max Heat Setpoint Limit + payload += attrValue(0x0017, 0x29, 0x954D, 4) + ' ' // 0x0017 : 954D = -54.53°C (int16) | Min Cool Setpoint Limit + payload += attrValue(0x0038, 0x29, 0x7FFF, 4) // 0x0018 : 7FFF = 327.67°C (int16) | Max Cool Setpoint Limit + + Integer mfgCode = 0x105E + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0201 {${utils_payload frameControl, 2}${utils_payload mfgCode, 4}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=Thermostat, attrs=[001C, 0015, 0016, 0017, 0018]" + return + + // Report/Read Attributes: Pump + case { contains it, [endpointInt:0x07, clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [endpointInt:0x07, clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String pump = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'pump', value:pump, descriptionText:"Pump was turned ${pump}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Pump=${pump}" + return + + // Report/Read Attributes: Boiler + case { contains it, [endpointInt:0x08, clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [endpointInt:0x08, clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String boiler = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'boiler', value:boiler, descriptionText:"Boiler was turned ${boiler}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Boiler=${boiler}" + return + + // Read Attributes: Zone 1-6 + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String zone = "zone_${msg.endpointInt}" + String status = msg.value == '01' ? 'on' : 'off' + utils_sendEvent name:zone, value:status, descriptionText:"Zone ${zone} is ${status}", type:'digital' + utils_processedZclMessage 'Read Attributes Response', "${zone}=${status}" + return + + // Read Attributes Reponse: Debug Info + case { contains it, [clusterInt:0xFE03, commandInt:0x01, attrInt:0x0020] }: + case { contains it, [clusterInt:0xFE03, commandInt:0x0A, attrInt:0x0020] }: + utils_sendEvent name:'debug', value:msg.value, descriptionText:"Debug info is ${msg.value}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "WiserDebugInfo=${msg.value}" + return + + // Report Attributes: LastMessageLQI + case { contains it, [clusterInt:0x0B05, commandInt:0x0A, attrInt:0x011C] }: + Integer lqi = Integer.parseInt msg.value, 16 + utils_sendEvent name:'lqi', value:lqi, descriptionText:"Signal LQI is ${lqi}", type:'physical' + msg.additionalAttrs.each { + if (it.attrId == '011D') { + Integer rssi = Integer.parseInt it.value, 16 + utils_sendEvent name:'rssi', value:rssi, descriptionText:"Signal RSSI is ${rssi}", type:'physical' + utils_processedZclMessage 'Report Attributes Response', "Diagnostics/LastMessageRSSI=${it.value}" + } + } + utils_processedZclMessage 'Report Attributes Response', "Diagnostics/LastMessageLQI=${msg.value}" + return + + // Report Unhandled Attributes + case { contains it, [commandInt:0x0A] }: + List attrs = ["${msg.attrId}=${msg.value} (${msg.encoding})"] + msg.additionalAttrs?.each { attrs += "${it.attrId}=${it.value} (${it.encoding})" } + utils_processedZclMessage '👿 Report Attributes', "endpoint=${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=${msg.manufacturerId ?: '0000'}, cluster=${msg.clusterId ?: msg.cluster}, attrs=${attrs}" + return + + // Read Unhandled Attributes + case { contains it, [commandInt:0x00] }: + List attrs = msg.data.collate(2).collect { "${it.reverse().join()}" } + utils_processedZclMessage '👿 Read Attributes', "endpoint=${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=${msg.manufacturerId ?: '0000'}, cluster=${msg.clusterId ?: msg.cluster}, attrs=${attrs}" + return + + // Other events that we expect but are not usefull + case { contains it, [endpointInt:0x07, clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=Pump, data=${msg.data}" + return + case { contains it, [endpointInt:0x08, clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=Boiler, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Handle common messages (e.g.: received during pairing when we query the device for information) + // --------------------------------------------------------------------------------------------------------------- + + // Device_annce: Welcome back! let's sync state. + case { contains it, [endpointInt:0x00, clusterInt:0x0013, commandInt:0x00] }: + log_warn 'Rejoined the Zigbee mesh; refreshing device state in 3 seconds ...' + runIn 3, 'refresh' + return + + // Report/Read Attributes Response (Basic cluster) + case { contains it, [clusterInt:0x0000, commandInt:0x01] }: + case { contains it, [clusterInt:0x0000, commandInt:0x0A] }: + utils_zigbeeDataValue(msg.attrInt, msg.value) + msg.additionalAttrs?.each { utils_zigbeeDataValue(it.attrInt, it.value) } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "cluster=0x${msg.cluster}, attribute=0x${msg.attrId}, value=${msg.value}" + return + + // Mgmt_Leave_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8034, commandInt:0x00] }: + log_warn 'Device is leaving the Zigbee mesh. See you later, Aligator!' + return + + // Ignore the following Zigbee messages + case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) + case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response + case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8004, commandInt:0x00] }: // ZDP: Simple_Desc_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8005, commandInt:0x00] }: // ZDP: Active_EP_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x0006, commandInt:0x00] }: // ZDP: MatchDescriptorRequest + case { contains it, [endpointInt:0x00, clusterInt:0x801F, commandInt:0x00] }: // ZDP: Parent_annce_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8021, commandInt:0x00] }: // ZDP: Mgmt_Bind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8022, commandInt:0x00] }: // ZDP: Mgmt_Unbind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Unexpected Zigbee message + // --------------------------------------------------------------------------------------------------------------- + default: + log_error "Sent unexpected Zigbee message: description=${description}, msg=${msg}" + } +} + +// =================================================================================================================== +// Logging helpers (something like this should be part of the SDK and not implemented by each driver) +// =================================================================================================================== + +private void log_debug(String message) { + if (logLevel == '1') log.debug "${device.displayName} ${message.uncapitalize()}" +} +private void log_info(String message) { + if (logLevel <= '2') log.info "${device.displayName} ${message.uncapitalize()}" +} +private void log_warn(String message) { + if (logLevel <= '3') log.warn "${device.displayName} ${message.uncapitalize()}" +} +private void log_error(String message) { + log.error "${device.displayName} ${message.uncapitalize()}" +} + +// =================================================================================================================== +// Helper methods (keep them simple, keep them dumb) +// =================================================================================================================== + +private void utils_sendZigbeeCommands(List cmds) { + if (cmds.empty) return + List send = delayBetween(cmds.findAll { !it.startsWith('delay') }, 1000) + log_debug "◀ Sending Zigbee messages: ${send}" + state.lastTx = now() + sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) +} +private void utils_sendEvent(Map event) { + if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + log_info "${event.descriptionText} [${event.type}]" + } else { + log_debug "${event.descriptionText} [${event.type}]" + } + sendEvent event +} +private void utils_dataValue(String key, String value) { + if (value == null || value == '') return + log_debug "Update data value: ${key}=${value}" + updateDataValue key, value +} +private void utils_zigbeeDataValue(Integer attrInt, String value) { + switch (attrInt) { + case 0x0001: utils_dataValue 'application', value; return + case 0x0003: utils_dataValue 'hwVersion', value; return + case 0x0004: utils_dataValue 'manufacturer', value; return + case 0x000A: utils_dataValue 'type', "${value ? (value.split('') as List).collate(2).collect { "${Integer.parseInt(it.join(), 16) as char}" }.join() : ''}"; return + case 0x0005: utils_dataValue 'model', value; return + case 0x4000: utils_dataValue 'softwareBuild', value; return + } +} +private void utils_processedZclMessage(String type, String details) { + log_debug "▶ Processed ZCL message: type=${type}, ${details}" +} +private void utils_processedZdpMessage(String type, String details) { + log_debug "▶ Processed ZDO message: type=${type}, ${details}" +} +private String utils_payload(String value) { + return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') +} +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} + +// switch/case syntactic sugar +@CompileStatic private boolean contains(Map msg, Map spec) { + return msg.keySet().containsAll(spec.keySet()) && spec.every { it.value == msg[it.key] } +} diff --git a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy index 225b340..a02f4dd 100644 --- a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy +++ b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy @@ -134,7 +134,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -172,6 +172,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -436,7 +437,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -449,7 +451,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/img/Schneider_CCTFR6600.webp b/ikea-zigbee-drivers/img/Schneider_CCTFR6600.webp new file mode 100644 index 0000000000000000000000000000000000000000..75e11d6a58a13f8cc5934a17dcb26b56dd206dea GIT binary patch literal 4006 zcmd5;Wl$83_T43fB^D4?x=|JcmIi?z-3`*+-5t^(-3zF6cS(qpv@{~+vNQ_P5-!dD zdHXO}z<-sLs-`01 z2>?JyDd&omJZd+;e!WJ>P=Y4y^Vh{S>K8Onzbh;)a;2`|3|qX#k(5;htd*aM)qbl= za0im@p~Dj1g_rpt+Z_)Kolnk_P7+;_?XF0+SJwiLjnBI5knQ3U$jeo4lv(YwsaR2! z_&87zi&6&j7*FcxzU3|84o!0PG-bk2|E;JnPQ`932@mi)`r+~m`u-#geY^gjblnL? zhF9ir6dE9uN`IO6kDv)_DmzJ;sRF(pV^-Ih}Yt`;fWO{=WDI%6RFXcl}#tkaWaPjJdf>d%g+kg(W+M z2vuyM^s7pkY6E5O&Q4>PvkmAsmX@M--tV~AkNZd;r?|+8Y6q&*Mu|^5Gsu)%zoz2S zaTvR9_V+LW-Mo|G6kIEiSAVb0N0yNhq-Mr0)}Clm?y z((SI!Iq!L0^BQ(Fbw@DB)a_9I^ALPFWqpwCNgGKf5N~YQbu!c0u$I=3na<~IK0xSs z53{E|H#KU2b3|6nE<@{eQhXy*wRlw(8A!ZjJ*m|&`Am@zl|;C?EZOaG&_>%F=)1@J z3SOsDmnxo)A8J1VQ)(THs6nYjO*yHiiqXx$ALH^li6O(DB^Ea(QFg;m0VU25; zT%ni0ZeD%88ugsC71zLhyH~8vX+|^Xms$X9pPLR2LCc&EbRLi@2rxs)T@lQ%CfbAL z&NMEz;OOS5HY3(==|`y`TN&N9Ewb3Zxb{6~aV>v$rHwM79bF++bs#+c25BQ9ARWoF zY#Ehn6RE37I3Q;!#&>bv$bWy>(>D3;&EARde(T<9 zeIWR4$6}ZZY`qM9VQy&>$k)BlE(4vvzvidFAEFf7BZ6BDy{|{n12&K8$?w1=(|PHb z_B%WsUED!uMoGUX(*^7!$|UoPk6kMme{^la{}S)7P>Ne7Ga776+ngUq;r4ehOB9Hu zNUk^E$KnbVerh;XyYZWj&i#?$$%X~+MHx6f*=SV2^sr4`Kmx9urLQwQ*OH2+>3Fd8 zQNmbZ0!&qNUx`f++a$$f2<8M$w%`>KFPl`T^y`vT=Bg%rNdFacoHD%@)-F-m%MhA~ zFVz-#B9S7&sw5@|Qqo5vCW#xYMnO;85B~?Rf1M)<#yF2CCB7T>u+<>_#VgB=lEsCf zL}bB-)iPBrtlD5c++fH6VjDY}Eto($U+gKv;~hqhrpic7dyQxZ9uygI`NzsgTy;7a zgUJu(v~jvI;yYKXyvjad$iP=zOJzF$xQ)YjP%-gYb_|0ZitLG$x$g+*ZHofWZ|Y>v zG0I(xYIv!8kx5yewau>-LQAsg{7vj6cFoF%wLprIyuIbzSlgtJ@7c2#7F1t1@n)R| zMTAfL5;Ij+C@V`M;LLMrr3?H|2-V~=--Eo&{q?O+qT%I|s!25bmnv0JCGCUsT-e>^ z{?t#mte)n|Q1LTBO$YXXxAh`<`UYFi7{ zmMhbyaqC=o>7D8my6&tp!={K$eCqJ@+Tm!wN2H{fZ&QBtb}NhA|4v-jaXN91*fAjv zI%qLbH|X=SCeBHtqrS57=2C5|0-nqHyE1~)Tp@-w)$y;wx6@j&Nbx0h~ zKj!w{?H9}m{au(a(Wus(J=UJ6Duci1R-6eD@ESs?Nm)NPfeFZ7ucO!R&{O}#Fz(%H z0gs-?P=E^ne6t7S1KBTt7eMiJ#bVX+GUk$^k8vGMPeG1u=<}X51>$OzfsRJy0&jG0 z>^1X2NL2__{)N+#;|8M9*Z7_>qR(_GG_cOzibB$ zvu|fbx%RMIhehkd`8vsZ$gK~{7x#GTUZ1>efhRwy?@RH9>1a8)wQD_EGMsnApO(A% zzuN7)=w1okkUm4EtZ_Jn?&?E!sqv00#;tD1Y4KkJ2UIqqIW^b8*&_#O+6hg_;E9{RM1I&_x4=&Zx>4!N;lb-LN+Z6tg*g^h0V67yNA$nRV&m=?i_XT3vCAO zTQ6@LedDOuJiPnXzugQfU1eho1;Anrx$Abu)&WAFv(9ItC{*=q| zK%XV2_FPCm#l-A!fZ7T9Y>m86wh$pAhq+TU;%V)2&VhBC5|6Z-7YO(>860>N-i;J) zXrE9xKmNb>h^^3Zgga^d5N1!J^6u#%J`MZwA@+j|754mhEfAK@kWM8ERR_C?xXgBb zm8HPNa_TsmJbOQ;wv3*>`Tabz$N!K?=Zoudl>HbV{|&59cSi}g=dSQR9*O7K)v9*m)qUFc{de!m1}Zwf?5hZ&9}F9hH)AW~ z2QtJ^1d-aPi{} zNJJTL=L9;jm8FBRZ`EYJR#1wP?F3)fvjH3?XnSADM&azv{}Ju&N5S8F9*%-RJCIw7 z10VVIW&t0)dl&aN;bkG#lF(>S4q{|!6ngVkt`EyI(fBw)0zX%Xefb?*Y&3<7v97kf zxjSh(hwc}3rIfDgH`Z%%bJX6es8fuFY(M*OFkVCxAgUU!_5F{+9mVen_f~PveZCQ- zfSXnI3?hqukz3aUP5qo znA|Ra6)<;(HJJ<$HM*{b*R87!xiJ08PNwsj4^?{63g-3{oJkV}6MNs|>TZ%Vg2GQy z0Ny>&uGmY!Vb$9yB*a}Vb**Rocxyu87&sCDP^$@?_gY{&78}>~rfvf*(khFcwdjre zqT))h9s0tLtAIhN-loAs{f1|9GF_X-`VdYYyFDSN90@$k!x(a-Zyy7DR{Lex>&Ln~ z{*3kOI5915_7@1}-(uP0fEqrgTWlSxS6tuJ^;x(rm%A~ifqUG?aX)|>S6i-3?D(h# zsxxXBGu)!LFBSKbUP_3K6p7GLJa5R^&jri|RO~{@a=NL<)v=V{K>)(JgajIAyUS)iVR;t$pG=T(98uw9mv( zx+(DIuI?P9CEi~b`UKnMJk%rphWxX_Y&G6`_f`ZPfV=4so)|w6$~|Bszm4!pot4+S zu^micEsx(e+HIW;hHZ5L=!%1C#CI+vZ1c|{stjO{kcKz~M8oY+f30aAiy8oE{cZl?-a z6YJ}$c?L7N)?{^*4&~SAduW4@rN42(4=oA8X!7sMODV3ajiOnv!$#)O;25VKG|`8} zmCbe{to~a6w7_Osv0@SO(`h=gRn{s9C62Kv8h9DgmM)bjd0N4@N@e>hd2Cyr--Fw% zr@Yp)Kg345q}j3fVn{{v8{@;?9o literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index c1a3c66..c3061bc 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-04-??", diff --git a/ikea-zigbee-drivers/src/blueprint.groovy b/ikea-zigbee-drivers/src/blueprint.groovy index 6c59995..fbd197c 100644 --- a/ikea-zigbee-drivers/src/blueprint.groovy +++ b/ikea-zigbee-drivers/src/blueprint.groovy @@ -122,7 +122,7 @@ void configure(boolean auto = false) { } // Apply preferences first - List cmds = [] + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] cmds += updated true // Clear data (keep firmwareMT information though) @@ -141,6 +141,7 @@ void configure(boolean auto = false) { cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" utils_sendZigbeeCommands cmds log_info 'Configuration done; refreshing device current state in 7 seconds ...' @@ -235,7 +236,8 @@ void parse(String description) { case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command - utils_processedZclMessage 'Ignored', "endpoint=${msg.endpoint}, cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp @@ -248,7 +250,7 @@ void parse(String description) { case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify - utils_processedZdpMessage 'Ignored', "cluster=0x${msg.clusterId}, command=0x${msg.command}, data=${msg.data}" + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" return // --------------------------------------------------------------------------------------------------------------- diff --git a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy index 8fe9632..8bd11e0 100644 --- a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy @@ -1,5 +1,6 @@ {{!--------------------------------------------------------------------------}} {{# @definition }} +capability 'Light' capability 'ChangeLevel' capability 'SwitchLevel' {{/ @definition }} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index cf7da9d..328e85e 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -3,6 +3,22 @@ capability 'ColorControl' {{/ @definition }} {{!--------------------------------------------------------------------------}} +{{# @fields }} + +// Fields for capability.ColorControl + +@Field static final Map COLOR_LOOP_SPEED = [ + 'switft':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 +] +{{/ @fields }} +{{!--------------------------------------------------------------------------}} +{{# @commands }} + +// Commands for capability.ColorControl +command 'startColorLoop', [[name:'Speed*', type:'ENUM', constraints: COLOR_LOOP_SPEED.keySet()]] +command 'stopColorLoop' +{{/ @commands }} +{{!--------------------------------------------------------------------------}} {{# @implementation }} // Implementation for capability.ColorControl @@ -32,6 +48,17 @@ void setSaturation(BigDecimal saturation) { String payload = "${utils_payload newSaturation, 2} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } +void startColorLoop(String speed) { + Integer seconds = COLOR_LOOP_SPEED[speed] + log_debug "Starting color loop with ${seconds} seconds / loop" + String payload = "0F 01 01 ${utils_payload seconds, 4} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set +} +void stopColorLoop() { + log_debug "Stopping color loop" + String payload = "0F 00 01 0000 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set +} private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] attributes[msg.attrInt] = msg.value @@ -55,6 +82,9 @@ private void processMultipleColorAttributes(Map msg, String type) { colorMode = it.value == '02' ? 'CT' : 'RGB' utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type break + case 0x4000: + hue = Math.round(Integer.parseInt(it.value, 16) / 655.34) + hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) } } @@ -81,8 +111,9 @@ cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) {{# @configure }} // Configuration for capability.ColorControl -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1) -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {01} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x4000 0x21 0x0002 0xFFFE {CB0C} {}" // Report EnhancedCurrentHue (uint16) at most every 2 seconds (Δ = 5%) {{/ @configure }} {{!--------------------------------------------------------------------------}} {{# @refresh }} @@ -111,6 +142,9 @@ case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: // Report/Read Attributes Reponse: EnhancedColorMode case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + +// Report Attributes Reponse: EnhancedCurrentHue +case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4000] }: processMultipleColorAttributes msg, type return diff --git a/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy b/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy index a267db9..36b990f 100644 --- a/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy +++ b/ikea-zigbee-drivers/src/capabilities/MultiRelay.groovy @@ -7,6 +7,7 @@ capability 'Actuator' // Fields for capability.MultiRelay import com.hubitat.app.ChildDeviceWrapper +import com.hubitat.app.DeviceWrapper {{/ @fields }} {{!--------------------------------------------------------------------------}} {{# @implementation }} @@ -17,19 +18,19 @@ private ChildDeviceWrapper fetchChildDevice(Integer moduleNumber) { return childDevice ?: addChildDevice('hubitat', 'Generic Component Switch', "${device.deviceNetworkId}-${moduleNumber}", [name:"${device.displayName} - Relay L${moduleNumber}", label:"Relay L${moduleNumber}", isComponent:true]) } -void componentOff(ChildDeviceWrapper childDevice) { +void componentOff(DeviceWrapper childDevice) { log_debug "▲ Received Off request from ${childDevice.displayName}" Integer endpointInt = Integer.parseInt(childDevice.deviceNetworkId.split('-')[1]) utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${endpointInt} 0x0006 {014300}"]) } -void componentOn(ChildDeviceWrapper childDevice) { +void componentOn(DeviceWrapper childDevice) { log_debug "▲ Received On request from ${childDevice.displayName}" Integer endpointInt = Integer.parseInt(childDevice.deviceNetworkId.split('-')[1]) utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${endpointInt} 0x0006 {014301}"]) } -void componentRefresh(ChildDeviceWrapper childDevice) { +void componentRefresh(DeviceWrapper childDevice) { log_debug "▲ Received Refresh request from ${childDevice.displayName}" refresh() } @@ -72,7 +73,7 @@ case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: childDevice.parse([[name:'switch', value:newState, descriptionText:"${childDevice.displayName} was turned ${newState}", type:type]]) } - utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Module=${moduleNumber}, Switch=${newState}" + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Relay=${moduleNumber}, Switch=${newState}" return // Other events that we expect but are not usefull diff --git a/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml b/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml index bff1e32..7744b2c 100644 --- a/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Legrand_741811/config.yaml @@ -9,10 +9,12 @@ device: url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: - - firmwares: 003e (1021-0011-003E4203) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' - firmwares: 002f value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006,000F', outClusters:'0006,0000,FC01,0005,0019', model:' Mobile outlet', manufacturer:' Legrand' + - firmwares: 003e (1021-0011-003E4203) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' + - firmwares: 0053 (1021-0011-005343FF) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,FC01,0B04,0006', outClusters:'0006,0000,FC01,0005,0019', model:' Connected outlet', manufacturer:' Legrand' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/CCTFR6600.groovy b/ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/CCTFR6600.groovy new file mode 100644 index 0000000..0d86fbd --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/CCTFR6600.groovy @@ -0,0 +1,240 @@ +{{!--------------------------------------------------------------------------}} +{{# @definition }} +capability 'Actuator' +capability 'SignalStrength' +{{/ @definition }} +{{!--------------------------------------------------------------------------}} +{{# @fields }} + +// Fields for devices.Schneider_CCTFR6600 +@Field static final List ZONES = [ + 'zone_1', 'zone_2', 'zone_3', 'zone_4', 'zone_5', 'zone_6' +] +{{/ @fields }} +{{!--------------------------------------------------------------------------}} +{{# @attributes }} + +// Attributes for devices.Schneider_CCTFR6600 +attribute 'zone_1', 'enum', ['on', 'off'] +attribute 'zone_2', 'enum', ['on', 'off'] +attribute 'zone_3', 'enum', ['on', 'off'] +attribute 'zone_4', 'enum', ['on', 'off'] +attribute 'zone_5', 'enum', ['on', 'off'] +attribute 'zone_6', 'enum', ['on', 'off'] +attribute 'pump', 'enum', ['on', 'off'] +attribute 'boiler', 'enum', ['on', 'off'] +attribute 'debug', 'string' +{{/ @attributes }} +{{!--------------------------------------------------------------------------}} +{{# @commands }} + +// Commands for devices.Schneider_CCTFR6600 +command 'on', [[name:'Zone*', type:'ENUM', description:'Zone to turn On', constraints:ZONES]] +command 'off', [[name:'Zone*', type:'ENUM', description:'Zone to turn Off', constraints:ZONES]] +command 'exec', [[name:'Zigbee command', description:'Enter raw command to execute (e.g. for toggle on/off: he raw .addr 0x01 0x01 0x0006 {114302})', type:'STRING']] +{{/ @commands }} +{{!--------------------------------------------------------------------------}} +{{# @configure }} + +// Configuration for Schneider_CCTFR6600.Switch +ZONES.each { off it } + +cmds += "zdo bind 0x${device.deviceNetworkId} 0x07 0x01 0x0006 {${device.zigbeeId}} {}" // Pump endpoint +//cmds += "he cr 0x${device.deviceNetworkId} 0x07 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report pump status at least every 10 minutes + +cmds += "zdo bind 0x${device.deviceNetworkId} 0x08 0x01 0x0006 {${device.zigbeeId}} {}" // Boiler endpoint +//cmds += "he cr 0x${device.deviceNetworkId} 0x08 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report boiler status at least every 10 minutes + +ZONES.each { + cmds += "he cr 0x${device.deviceNetworkId} 0x0${it.substring(5)} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report zones[1-6] status at least every 10 minutes +} +{{/ @configure }} +{{!--------------------------------------------------------------------------}} +{{# @refresh }} + +// Refresh for devices.Schneider_CCTFR6600 +cmds += zigbee.readAttribute(0xFE03, 0x0020, [mfgCode: '0x105E']) // Wiser Debug Info + +ZONES.each { cmds += zigbee.readAttribute(0x0006, 0x0000, [destEndpoint:Integer.parseInt(it.substring(5))]) } // Zones[1-6] OnOff +cmds += zigbee.readAttribute(0x0006, 0x0000, [destEndpoint:0x07]) // Pump OnOff +cmds += zigbee.readAttribute(0x0006, 0x0000, [destEndpoint:0x08]) // Boiler OnOff +{{/ @refresh }} +{{!--------------------------------------------------------------------------}} +{{# @implementation }} + +// Implementation for devices.Schneider_CCTFR6600 +void on(String zone) { + if (!ZONES.contains(zone)) { + log_error "Invalid zone: ${zone}. Available zones: ${ZONES}" + return + } + utils_sendEvent name:zone, value:'on', descriptionText:"Zone ${zone} was turned on", type:'digital' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${zone.substring(5)} 0x0006 {014301}"]) +} + +void off(String zone) { + if (!ZONES.contains(zone)) { + log_error "Invalid zone: ${zone}. Available zones: ${ZONES}" + return + } + utils_sendEvent name:zone, value:'off', descriptionText:"Zone ${zone} was turned off", type:'digital' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x0${zone.substring(5)} 0x0006 {014300}"]) +} + +void exec(String command) { + log_info "Exec: ${command}" + String cmd = command.replace '.addr', "0x${device.deviceNetworkId}" + utils_sendZigbeeCommands([cmd]) +} + +private String attrValue(Integer attr, Integer type, Integer value, Integer bytes) { + return "${utils_payload attr, 4}00${utils_payload type, 2}${utils_payload value, bytes}" +} +{{/ @implementation }} +{{!--------------------------------------------------------------------------}} +{{# @events }} + +// Events for devices.Schneider_CCTFR6600 +// =================================================================================================================== + +// Read Attributes: ZCL Version +case { contains it, [clusterInt:0x0000, commandInt:0x00, data:['00', '00']] }: + Integer frameControl = 0x08 + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = attrValue(0x0000, 0x20, 0x03, 2) // ZCL Version = 0x03 + + // Send response only once every 5 minutes + //if (Calendar.getInstance().get(Calendar.MINUTE) % 5 == 0) + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0000 {${utils_payload frameControl, 2}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes (health check)', "endpoint=${msg.sourceEndpoint}, cluster=Basic, attr=0000 (ZCL Version)" + return + +// ▶ Processed ZCL message: type=Read Attributes, endpoint=03, manufacturer=0000, cluster=0006, attrs=[0000] +case { contains it, [clusterInt:0x0006, commandInt:0x00, data:['00', '00']] }: + String zone = "zone_${Integer.parseInt msg.sourceEndpoint, 16}" + boolean status = device.currentValue(zone, true) == 'on' + + Integer frameControl = Integer.parseInt('00001000', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = attrValue(0x0000, 0x10, status ? 0x01 : 0x00, 2) + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${msg.sourceEndpoint} 0x0006 {${utils_payload frameControl, 2}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=OnOff, attr=0000 (OnOff)" + return + +// ▶ Processed ZCL message: type=Read Attributes, endpoint=03, manufacturer=105E, cluster=0006, attrs=[E002] +case { contains it, [clusterInt:0x0006, commandInt:0x00, data:['02', 'E0']] }: + Integer frameControl = Integer.parseInt('00001100', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = attrValue(0xE002, 0x10, 0x00, 2) + Integer mfgCode = 0x105E + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${msg.sourceEndpoint} 0x0006 {${utils_payload frameControl, 2}${utils_payload mfgCode, 4}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=OnOff, attr=E002 (Unknown)" + return + +// ▶ Processed ZCL message: type=Read Attributes, endpoint=03, manufacturer=105E, cluster=FF16, attrs=[0000, 0001, 0002, 0010, 0011, 0012, 0030] +case { contains it, [clusterInt:0xFF16, commandInt:0x00, data:['00', '00', '01', '00', '02', '00', '10', '00', '11', '00', '12', '00', '30', '00']] }: + Integer frameControl = Integer.parseInt('00001100', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = '' + payload += attrValue(0x0000, 0x20, 0xC8, 2) + ' ' // 0x0000 : 0x64 = 100 (uint8) + payload += '010086 ' // 0x0001 : UNSUPPORTED_ATTRIBUTE + payload += '020086 ' // 0x0002 : UNSUPPORTED_ATTRIBUTE + payload += attrValue(0x0010, 0x21, 0x04B0, 4) + ' ' // 0x0010 : 0258 = 600 (uint16) + payload += attrValue(0x0011, 0x21, 0x0258, 4) + ' ' // 0x0011 : 012C = 300 (uint16) + payload += attrValue(0x0012, 0x21, 0x1C20, 4) + ' ' // 0x0012 : 0E10 = 3600 (uint16) + payload += attrValue(0x0030, 0x20, 0x42, 2) // 0x0030 : 0x21 = 33 (uint8) + + Integer mfgCode = 0x105E + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${msg.sourceEndpoint} 0xFF16 {${utils_payload frameControl, 2}${utils_payload mfgCode, 4}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=FF16, attrs=[0000, 0001, 0002, 0010, 0011, 0012, 0030]" + return + +// ▶ Processed ZCL message: type=Read Attributes, endpoint=01, manufacturer=0000, cluster=0201, attrs=[001C, 0015, 0016, 0017, 0018] +case { contains it, [clusterInt:0x0201, commandInt:0x00, data:['1C', '00', '15', '00', '16', '00', '17', '00', '18', '00']] }: + Integer frameControl = Integer.parseInt('00001100', 2) + Integer txSeq = 0x00 + Integer command = 0x01 // Read Attributes Response + String payload = '' + payload += attrValue(0x001C, 0x30, 0x04, 2) + ' ' // 0x0000 : 0x03 = Cool (enum8) | 0x00:Off, 0x01:Auto, 0x03:Cool, 0x04:Heat, 0x05:Emergency heating + // | 0x06:Precooling, 0x07:Fan only, 0x08:Dry, 0x09:Sleep + payload += attrValue(0x0015, 0x29, 0x954D, 4) + ' ' // 0x0015 : 954D = -54.53°C (int16) | Min Heat Setpoint Limit + payload += attrValue(0x0016, 0x29, 0x7FFF, 4) + ' ' // 0x0016 : 7FFF = 327.67°C (int16) | Max Heat Setpoint Limit + payload += attrValue(0x0017, 0x29, 0x954D, 4) + ' ' // 0x0017 : 954D = -54.53°C (int16) | Min Cool Setpoint Limit + payload += attrValue(0x0038, 0x29, 0x7FFF, 4) // 0x0018 : 7FFF = 327.67°C (int16) | Max Cool Setpoint Limit + + Integer mfgCode = 0x105E + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0201 {${utils_payload frameControl, 2}${utils_payload mfgCode, 4}${utils_payload txSeq, 2}${utils_payload command, 2} ${payload}}"]) + utils_processedZclMessage '😊 Read Attributes', "endpoint=${msg.sourceEndpoint}, cluster=Thermostat, attrs=[001C, 0015, 0016, 0017, 0018]" + return + +// Report/Read Attributes: Pump +case { contains it, [endpointInt:0x07, clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: +case { contains it, [endpointInt:0x07, clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String pump = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'pump', value:pump, descriptionText:"Pump was turned ${pump}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Pump=${pump}" + return + +// Report/Read Attributes: Boiler +case { contains it, [endpointInt:0x08, clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: +case { contains it, [endpointInt:0x08, clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String boiler = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'boiler', value:boiler, descriptionText:"Boiler was turned ${boiler}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "Boiler=${boiler}" + return + +// Read Attributes: Zone 1-6 +case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String zone = "zone_${msg.endpointInt}" + String status = msg.value == '01' ? 'on' : 'off' + utils_sendEvent name:zone, value:status, descriptionText:"Zone ${zone} is ${status}", type:'digital' + utils_processedZclMessage 'Read Attributes Response', "${zone}=${status}" + return + +// Read Attributes Reponse: Debug Info +case { contains it, [clusterInt:0xFE03, commandInt:0x01, attrInt:0x0020] }: +case { contains it, [clusterInt:0xFE03, commandInt:0x0A, attrInt:0x0020] }: + utils_sendEvent name:'debug', value:msg.value, descriptionText:"Debug info is ${msg.value}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "WiserDebugInfo=${msg.value}" + return + +// Report Attributes: LastMessageLQI +case { contains it, [clusterInt:0x0B05, commandInt:0x0A, attrInt:0x011C] }: + Integer lqi = Integer.parseInt msg.value, 16 + utils_sendEvent name:'lqi', value:lqi, descriptionText:"Signal LQI is ${lqi}", type:'physical' + msg.additionalAttrs.each { + if (it.attrId == '011D') { + Integer rssi = Integer.parseInt it.value, 16 + utils_sendEvent name:'rssi', value:rssi, descriptionText:"Signal RSSI is ${rssi}", type:'physical' + utils_processedZclMessage 'Report Attributes Response', "Diagnostics/LastMessageRSSI=${it.value}" + } + } + utils_processedZclMessage 'Report Attributes Response', "Diagnostics/LastMessageLQI=${msg.value}" + return + +// Report Unhandled Attributes +case { contains it, [commandInt:0x0A] }: + List attrs = ["${msg.attrId}=${msg.value} (${msg.encoding})"] + msg.additionalAttrs?.each { attrs += "${it.attrId}=${it.value} (${it.encoding})" } + utils_processedZclMessage '👿 Report Attributes', "endpoint=${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=${msg.manufacturerId ?: '0000'}, cluster=${msg.clusterId ?: msg.cluster}, attrs=${attrs}" + return + +// Read Unhandled Attributes +case { contains it, [commandInt:0x00] }: + List attrs = msg.data.collate(2).collect { "${it.reverse().join()}" } + utils_processedZclMessage '👿 Read Attributes', "endpoint=${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=${msg.manufacturerId ?: '0000'}, cluster=${msg.clusterId ?: msg.cluster}, attrs=${attrs}" + return + +// Other events that we expect but are not usefull +case { contains it, [endpointInt:0x07, clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=Pump, data=${msg.data}" + return +case { contains it, [endpointInt:0x08, clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=Boiler, data=${msg.data}" + return +{{/ @events }} +{{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/config.yaml b/ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/config.yaml new file mode 100644 index 0000000..ec2330a --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Schneider_CCTFR6600/config.yaml @@ -0,0 +1,22 @@ +device: + model: Schneider Wiser UFH (CCTFR6600) + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Schneider_CCTFR6600.webp + links: + - name: device details + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#schneider-wiser-ufh-cctrf6600 + - name: community page + url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 + + fingerprints: + - firmwares: 9ae667b (105E-0A00-00007D00) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0006,0201,0B05,FE03,FF16', outClusters:'0003,0019', model:'UFH', manufacturer:'Schneider Electric' + + capabilities: + - file: src/capabilities/HealthCheck.groovy + params: + schedule: 0 0 0/1 ? * * * + checkInterval: 3600 # every hour + thereshold: 3600 # report device as offline if no message was received in the last 60 minutes (device should report On/Off status every 10 minutes) + - file: src/capabilities/PowerSource.groovy + - file: src/devices/Schneider_CCTFR6600/CCTFR6600.groovy From d7c98d8cd99fb99f6aa3ae4083b18ad4bc3d3284 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Wed, 24 Apr 2024 00:31:38 +0300 Subject: [PATCH 08/26] Add fingerprint for LED1925G6 --- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 1 + ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 5f0a82f..58689e4 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -45,6 +45,7 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' // LED1925G6: 1.0.021 (117C-2802-10021655) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index 67771d1..c9432e5 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -12,9 +12,12 @@ device: - type: L2112 firmwares: 1.1.10 (117C-2804-01010010) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' - - type: LED2111G6 + - type: LED2111G6 firmwares: 1.0.38 (117C-2805-01000038) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' + - type: LED1925G6 + firmwares: 1.0.021 (117C-2802-10021655) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy From 519c67683f63b9fb364bb3e7327da56c1c64de8e Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Thu, 25 Apr 2024 21:40:01 +0300 Subject: [PATCH 09/26] - Set minimum attribute reporting to 1 second - Rename `transitionTime` to `levelTransitionTime` - Added more fingerprints --- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 24 +++++++++++-------- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 16 +++++++------ ikea-zigbee-drivers/Ikea_WS-Light.groovy | 17 ++++++------- ikea-zigbee-drivers/README.md | 12 ++++++++++ .../src/capabilities/Brightness.groovy | 14 +++++------ .../src/capabilities/ColorControl.groovy | 4 ++-- .../src/capabilities/ColorTemperature.groovy | 2 +- .../src/devices/Ikea_CWS-Light/config.yaml | 12 ++++++++++ .../src/devices/Ikea_DIM-Light/config.yaml | 6 +++++ .../src/devices/Ikea_WS-Light/config.yaml | 3 +++ 10 files changed, 75 insertions(+), 35 deletions(-) diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 58689e4..c58e5ce 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -46,6 +46,10 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' // LED1925G6: 1.0.021 (117C-2802-10021655) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' // LED1923R5: 1.0.021 (117C-2802-10021655) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E27EU: 2.3.093 (117C-2801-23086631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E14EU: 2.3.093 (117C-2803-23093631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' // LED1924G9: 1.0.021 (117C-2802-10021655) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -151,7 +155,7 @@ metadata { required: true ) input( - name: 'transitionTime', type: 'enum', + name: 'levelTransitionTime', type: 'enum', title: 'Brightness transition time', description: 'Time taken to move to/from the target brightness when device is turned On/Off.', options: [ @@ -290,12 +294,12 @@ List updated(boolean auto = false) { cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) } - if (transitionTime == null) { - transitionTime = '5' - device.updateSetting 'transitionTime', [value:transitionTime, type:'enum'] + if (levelTransitionTime == null) { + levelTransitionTime = '5' + device.updateSetting 'levelTransitionTime', [value:levelTransitionTime, type:'enum'] } - log_info "🛠️ transitionTime = ${Integer.parseInt(transitionTime) / 10} second(s)" - cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(transitionTime)) + log_info "🛠️ levelTransitionTime = ${Integer.parseInt(levelTransitionTime) / 10} second(s)" + cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(levelTransitionTime)) if (prestaging == null) { prestaging = false @@ -375,19 +379,19 @@ void configure(boolean auto = false) { cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes // Configuration for capability.ColorControl - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0001 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0001 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x4000 0x21 0x0002 0xFFFE {CB0C} {}" // Report EnhancedCurrentHue (uint16) at most every 2 seconds (Δ = 5%) // Configuration for capability.ColorTemperature cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0000 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0001 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) state.minMireds = 200 // Will be updated in refresh() state.maxMireds = 600 // Will be updated in refresh() // Configuration for capability.Brightness cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0001 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) // Configuration for capability.HealthCheck sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index 9aa4595..abd3e8e 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -38,6 +38,8 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 2.3.086 (117C-4101-23086631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' // LED2103G5: 1.0.36 (117C-2100-01000036) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' // LED1842G3: 2.3.093 (117C-4103-23093631) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -110,7 +112,7 @@ metadata { required: true ) input( - name: 'transitionTime', type: 'enum', + name: 'levelTransitionTime', type: 'enum', title: 'Brightness transition time', description: 'Time taken to move to/from the target brightness when device is turned On/Off.', options: [ @@ -230,12 +232,12 @@ List updated(boolean auto = false) { cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) } - if (transitionTime == null) { - transitionTime = '5' - device.updateSetting 'transitionTime', [value:transitionTime, type:'enum'] + if (levelTransitionTime == null) { + levelTransitionTime = '5' + device.updateSetting 'levelTransitionTime', [value:levelTransitionTime, type:'enum'] } - log_info "🛠️ transitionTime = ${Integer.parseInt(transitionTime) / 10} second(s)" - cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(transitionTime)) + log_info "🛠️ levelTransitionTime = ${Integer.parseInt(levelTransitionTime) / 10} second(s)" + cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(levelTransitionTime)) if (prestaging == null) { prestaging = false @@ -316,7 +318,7 @@ void configure(boolean auto = false) { // Configuration for capability.Brightness cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0001 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) // Configuration for capability.HealthCheck sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index 4947354..a006469 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -38,6 +38,7 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // LED2101G4: 1.1.003 (117C-2204-00011003) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' // LED1949C5: 1.1.003 (117C-2204-00011003) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WS globe 1055lm', manufacturer:'IKEA of Sweden' // LED2201G8: 3.0.10 (117C-2206-03000010) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -139,7 +140,7 @@ metadata { required: true ) input( - name: 'transitionTime', type: 'enum', + name: 'levelTransitionTime', type: 'enum', title: 'Brightness transition time', description: 'Time taken to move to/from the target brightness when device is turned On/Off.', options: [ @@ -275,12 +276,12 @@ List updated(boolean auto = false) { cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) } - if (transitionTime == null) { - transitionTime = '5' - device.updateSetting 'transitionTime', [value:transitionTime, type:'enum'] + if (levelTransitionTime == null) { + levelTransitionTime = '5' + device.updateSetting 'levelTransitionTime', [value:levelTransitionTime, type:'enum'] } - log_info "🛠️ transitionTime = ${Integer.parseInt(transitionTime) / 10} second(s)" - cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(transitionTime)) + log_info "🛠️ levelTransitionTime = ${Integer.parseInt(levelTransitionTime) / 10} second(s)" + cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(levelTransitionTime)) if (prestaging == null) { prestaging = false @@ -361,13 +362,13 @@ void configure(boolean auto = false) { // Configuration for capability.ColorTemperature cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0000 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0001 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) state.minMireds = 200 // Will be updated in refresh() state.maxMireds = 600 // Will be updated in refresh() // Configuration for capability.Brightness cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster - cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0001 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) // Configuration for capability.HealthCheck sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index b43edcf..0e9c92e 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -590,6 +590,8 @@ Below you can find the details of each device, including the features and pairin * 10EU-IL-1: Tradfri LED Driver 10W * 30EU-IL-2: Tradfri LED Driver 30W * 30-IL44-1: Silverglans LED Driver 30W +* LED2103G5: Tradfri Bulb E27 WW Globe 806lm +* LED1842G3: Tradfri Bulb E27 WW Clear 250lm ### White Spectrum Light @@ -617,6 +619,7 @@ Below you can find the details of each device, including the features and pairin #### Tested devices * LED2101G4: Tradfri Bulb E14 WS Globe 470lm * LED1949C5: Tradfri Bulb E14 WS Candle Opal 470lm +* LED2201G8: Tradfri Bulb E27 WS Globe 1055lm #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. @@ -654,6 +657,15 @@ Below you can find the details of each device, including the features and pairin #### Tested devices * L2112: Ormanas LED Strip * LED2111G6: Tradfri Bulb E14 CWS Globe 806lm +* LED1925G6: Tradfri Bulb E14 CWS 470lm +* LED1923R5: Tradfri Bulb GU10 CWS 345lm +* LED1624G9E27EU: Tradfri Bulb E27 CWS Opal 600lm + - All Colour Temp changes failed. No errors generated in the test. Below errors from the log all happened during config: + - Read Attributes Reponse: 0x400B=UNSUPPORTED_ATTRIBUTE, 0x400C=UNSUPPORTED_ATTRIBUTE (ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds) + - Read Attributes Reponse: 0x0007=UNSUPPORTED_ATTRIBUTE, 0x0008=01 (ColorTemperatureMireds, ColorMode) +* LED1624G9E14EU: Tradfri Bulb E14 CWS Opal 600lm + - Same as above +* LED1924G9: Tradfri Bulb E27 CWS 806lm #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. diff --git a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy index 8bd11e0..19b738a 100644 --- a/ikea-zigbee-drivers/src/capabilities/Brightness.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Brightness.groovy @@ -31,7 +31,7 @@ input( required: true ) input( - name: 'transitionTime', type: 'enum', + name: 'levelTransitionTime', type: 'enum', title: 'Brightness transition time', description: 'Time taken to move to/from the target brightness when device is turned On/Off.', options: [ @@ -148,12 +148,12 @@ if (turnOnBehavior == 'FIXED_VALUE') { cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) } -if (transitionTime == null) { - transitionTime = '5' - device.updateSetting 'transitionTime', [value:transitionTime, type:'enum'] +if (levelTransitionTime == null) { + levelTransitionTime = '5' + device.updateSetting 'levelTransitionTime', [value:levelTransitionTime, type:'enum'] } -log_info "🛠️ transitionTime = ${Integer.parseInt(transitionTime) / 10} second(s)" -cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(transitionTime)) +log_info "🛠️ levelTransitionTime = ${Integer.parseInt(levelTransitionTime) / 10} second(s)" +cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(levelTransitionTime)) if (prestaging == null) { prestaging = false @@ -169,7 +169,7 @@ cmds += zigbee.writeAttribute(0x0008, 0x000F, 0x18, prestaging ? 0x01 : 0x00) // Configuration for capability.Brightness cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0000 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0001 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) {{/ @configure }} {{!--------------------------------------------------------------------------}} {{# @refresh }} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index 328e85e..666e8f2 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -111,8 +111,8 @@ cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) {{# @configure }} // Configuration for capability.ColorControl -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0000 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0000 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0001 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0001 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x4000 0x21 0x0002 0xFFFE {CB0C} {}" // Report EnhancedCurrentHue (uint16) at most every 2 seconds (Δ = 5%) {{/ @configure }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy index 852861d..f7100a8 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -129,7 +129,7 @@ cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) // Configuration for capability.ColorTemperature cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0300 {${device.zigbeeId}} {}" // Color Control Cluster cluster -cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0000 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) +cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0007 0x21 0x0001 0x0258 {01} {}" // Report ColorTemperatureMireds (uint16) at least every 10 minutes (Δ = 1) state.minMireds = 200 // Will be updated in refresh() state.maxMireds = 600 // Will be updated in refresh() {{/ @configure }} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index c9432e5..d7a65c8 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -18,6 +18,18 @@ device: - type: LED1925G6 firmwares: 1.0.021 (117C-2802-10021655) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' + - type: LED1923R5 + firmwares: 1.0.021 (117C-2802-10021655) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' + - type: LED1624G9E27EU + firmwares: 2.3.093 (117C-2801-23086631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' + - type: LED1624G9E14EU + firmwares: 2.3.093 (117C-2803-23093631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' + - type: LED1924G9 + firmwares: 1.0.021 (117C-2802-10021655) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml index ccd98c8..04e24f8 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml @@ -21,6 +21,12 @@ device: - type: 30-IL44-1 (Silverglans LED Driver 30W) firmwares: 1.0.021 (117C-4104-00010021) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' + - type: LED2103G5 + firmwares: 1.0.36 (117C-2100-01000036) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' + - type: LED1842G3 + firmwares: 2.3.093 (117C-4103-23093631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml index 8b80637..b19c9ec 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml @@ -15,6 +15,9 @@ device: - type: LED1949C5 firmwares: 1.1.003 (117C-2204-00011003) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' + - type: LED2201G8 + firmwares: 3.0.10 (117C-2206-03000010) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WS globe 1055lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy From 10f94c906d80024e6d8a9f38c5c1f571c4a5e22d Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Fri, 26 Apr 2024 16:19:15 +0300 Subject: [PATCH 10/26] Add new bulb fingerprints --- ikea-zigbee-drivers/CHANGELOG.md | 1 + ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 12 ++++---- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 6 ++-- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 3 ++ ikea-zigbee-drivers/README.md | 3 ++ .../src/capabilities/ColorControl.groovy | 2 +- .../src/devices/Ikea_CWS-Light/config.yaml | 28 +++++++++---------- .../src/devices/Ikea_DIM-Light/config.yaml | 18 ++++++------ .../src/devices/Ikea_WS-Light/config.yaml | 9 ++++++ 9 files changed, 49 insertions(+), 33 deletions(-) diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 79a78a3..49c382a 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - E2134, E2123: Fix bindings to use the correct endpoints - `@alexandruy2k` - Fix errors when pressing a dashboard tile assigned to "push" one of the buttons - `@OldChicagoPete` - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - `@BorrisTheCat` + ## [4.1.1] - 2024-04-25 ### Fixed diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index c58e5ce..660471b 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -12,7 +12,7 @@ import groovy.transform.Field // Fields for capability.ColorControl @Field static final Map COLOR_LOOP_SPEED = [ - 'switft':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 + 'swift':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 ] // Fields for capability.HealthCheck @@ -43,13 +43,13 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' // LED1925G6: 1.0.021 (117C-2802-10021655) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' // LED1923R5: 1.0.021 (117C-2802-10021655) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E27EU: 2.3.093 (117C-2801-23086631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E14EU: 2.3.093 (117C-2803-23093631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' // LED1924G9: 1.0.021 (117C-2802-10021655) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' // LED1923R5: 1.0.021 (117C-2802-10021655) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' // LED1925G6: 1.0.021 (117C-2802-10021655) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E14EU: 2.3.093 (117C-2803-23093631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index abd3e8e..bde897f 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -34,12 +34,12 @@ metadata { capability 'HealthCheck' capability 'PowerSource' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' // LED2103G5: 1.0.36 (117C-2100-01000036) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 1.2.245 (117C-4101-12245572) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 2.3.086 (117C-4101-23086631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' // LED2103G5: 1.0.36 (117C-2100-01000036) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' // LED1842G3: 2.3.093 (117C-4103-23093631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index a006469..18127c8 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -36,8 +36,11 @@ metadata { capability 'HealthCheck' capability 'PowerSource' + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 WS 345lm', manufacturer:'IKEA of Sweden' // LED2106R3: 3.0.10 (117C-2200-03000010) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // LED2101G4: 1.1.003 (117C-2204-00011003) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' // LED1949C5: 1.1.003 (117C-2204-00011003) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WSglobeopal470lm', manufacturer:'IKEA of Sweden' // LED2002G5: 1.0.012 (117C-2205-00010012) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbGU10WS345lm', manufacturer:'IKEA of Sweden' // LED2005R5: 1.0.012 (117C-2205-00010012) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WS globe 1055lm', manufacturer:'IKEA of Sweden' // LED2201G8: 3.0.10 (117C-2206-03000010) // Attributes for capability.HealthCheck diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 0e9c92e..c7376fd 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -619,7 +619,10 @@ Below you can find the details of each device, including the features and pairin #### Tested devices * LED2101G4: Tradfri Bulb E14 WS Globe 470lm * LED1949C5: Tradfri Bulb E14 WS Candle Opal 470lm +* LED2002G5: Tradfri Bulb E14 WS Globe Opal 470lm * LED2201G8: Tradfri Bulb E27 WS Globe 1055lm +* LED2005R5: Tradfri Bulb GU10 WS 345lm +* LED2106R3: Tradfri Bulb GU10 WS 345lm #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index 666e8f2..dfec27d 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -8,7 +8,7 @@ capability 'ColorControl' // Fields for capability.ColorControl @Field static final Map COLOR_LOOP_SPEED = [ - 'switft':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 + 'swift':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 ] {{/ @fields }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index d7a65c8..ae8d917 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -9,27 +9,27 @@ device: url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: - - type: L2112 - firmwares: 1.1.10 (117C-2804-01010010) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' - - type: LED2111G6 - firmwares: 1.0.38 (117C-2805-01000038) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' - - type: LED1925G6 + - type: LED1624G9E27EU + firmwares: 2.3.093 (117C-2801-23086631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' + - type: LED1924G9 firmwares: 1.0.021 (117C-2802-10021655) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' - type: LED1923R5 firmwares: 1.0.021 (117C-2802-10021655) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' - - type: LED1624G9E27EU - firmwares: 2.3.093 (117C-2801-23086631) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' + - type: LED1925G6 + firmwares: 1.0.021 (117C-2802-10021655) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' - type: LED1624G9E14EU firmwares: 2.3.093 (117C-2803-23093631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' - - type: LED1924G9 - firmwares: 1.0.021 (117C-2802-10021655) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' + - type: L2112 + firmwares: 1.1.10 (117C-2804-01010010) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' + - type: LED2111G6 + firmwares: 1.0.38 (117C-2805-01000038) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml index 04e24f8..2327566 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml @@ -9,24 +9,24 @@ device: url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: + - type: LED2103G5 + firmwares: 1.0.36 (117C-2100-01000036) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' - type: 10EU-IL-1 (Tradfri LED Driver 10W) firmwares: 1.2.245 (117C-4101-12245572) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - type: 10EU-IL-1 (Tradfri LED Driver 10W) firmwares: 2.3.086 (117C-4101-23086631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - - type: 30EU-IL-2 (Tradfri LED Driver 30W) - firmwares: 1.0.002 (117C-4109-00010002) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' - - type: 30-IL44-1 (Silverglans LED Driver 30W) - firmwares: 1.0.021 (117C-4104-00010021) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' - - type: LED2103G5 - firmwares: 1.0.36 (117C-2100-01000036) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' - type: LED1842G3 firmwares: 2.3.093 (117C-4103-23093631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' + - type: 30-IL44-1 (Silverglans LED Driver 30W) + firmwares: 1.0.021 (117C-4104-00010021) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' + - type: 30EU-IL-2 (Tradfri LED Driver 30W) + firmwares: 1.0.002 (117C-4109-00010002) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml index b19c9ec..665eb79 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml @@ -9,12 +9,21 @@ device: url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: + - type: LED2106R3 + firmwares: 3.0.10 (117C-2200-03000010) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 WS 345lm', manufacturer:'IKEA of Sweden' - type: LED2101G4 firmwares: 1.1.003 (117C-2204-00011003) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' - type: LED1949C5 firmwares: 1.1.003 (117C-2204-00011003) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' + - type: LED2002G5 + firmwares: 1.0.012 (117C-2205-00010012) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WSglobeopal470lm', manufacturer:'IKEA of Sweden' + - type: LED2005R5 + firmwares: 1.0.012 (117C-2205-00010012) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbGU10WS345lm', manufacturer:'IKEA of Sweden' - type: LED2201G8 firmwares: 3.0.10 (117C-2206-03000010) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WS globe 1055lm', manufacturer:'IKEA of Sweden' From 1389d4ee2b59017b23e49ebbdcef92e3facbadc3 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 28 Apr 2024 14:36:11 +0300 Subject: [PATCH 11/26] - CWS Lights: Disable color logging while the device is in color loop mode - Improve documentation --- ikea-zigbee-drivers/Aqara_DCM-K01.groovy | 3 +- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 15 +- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1603.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1743.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1745.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1766.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1810.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1812.groovy | 3 +- ikea-zigbee-drivers/Ikea_E1836.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2002.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2006.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2013.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2112.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2123.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2134.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2201.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2202.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2204.groovy | 3 +- ikea-zigbee-drivers/Ikea_E2213.groovy | 3 +- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 3 +- ikea-zigbee-drivers/Legrand_741811.groovy | 3 +- ikea-zigbee-drivers/Philips_RDM001.groovy | 3 +- ikea-zigbee-drivers/Philips_RWL022.groovy | 3 +- ikea-zigbee-drivers/README.md | 618 +++++++++--------- .../Schneider_CCTFR6600.groovy | 3 +- ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy | 3 +- ikea-zigbee-drivers/src/blueprint.groovy | 3 +- .../src/capabilities/ColorControl.groovy | 12 +- 29 files changed, 373 insertions(+), 350 deletions(-) diff --git a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy index 13f519c..513ae68 100644 --- a/ikea-zigbee-drivers/Aqara_DCM-K01.groovy +++ b/ikea-zigbee-drivers/Aqara_DCM-K01.groovy @@ -748,7 +748,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 660471b..f57f647 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -496,15 +496,17 @@ void setSaturation(BigDecimal saturation) { utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } void startColorLoop(String speed) { - Integer seconds = COLOR_LOOP_SPEED[speed] - log_debug "Starting color loop with ${seconds} seconds / loop" + Integer seconds = COLOR_LOOP_SPEED[speed] ?: 30 + log_info "Starting color loop at ${speed} speed (${seconds} sec per loop)" String payload = "0F 01 01 ${utils_payload seconds, 4} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set + state.loop = true } void stopColorLoop() { - log_debug "Stopping color loop" + log_info "Stopped color loop" String payload = "0F 00 01 0000 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set + state.remove 'loop' } private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] @@ -535,7 +537,7 @@ private void processMultipleColorAttributes(Map msg, String type) { } } - if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type, noInfo:(state.loop == true) if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type // Update colorName, if the case @@ -543,7 +545,7 @@ private void processMultipleColorAttributes(Map msg, String type) { Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) String colorName = convertHueToGenericColorName colorHue, colorSaturation - utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type, noInfo:(state.loop == true) } utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" } @@ -971,7 +973,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index bde897f..4b02851 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -684,7 +684,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1603.groovy b/ikea-zigbee-drivers/Ikea_E1603.groovy index 5295bc3..4376cd2 100644 --- a/ikea-zigbee-drivers/Ikea_E1603.groovy +++ b/ikea-zigbee-drivers/Ikea_E1603.groovy @@ -505,7 +505,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index 2d7b8ae..42492de 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -579,7 +579,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1745.groovy b/ikea-zigbee-drivers/Ikea_E1745.groovy index 49c9064..1016d08 100644 --- a/ikea-zigbee-drivers/Ikea_E1745.groovy +++ b/ikea-zigbee-drivers/Ikea_E1745.groovy @@ -629,7 +629,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1766.groovy b/ikea-zigbee-drivers/Ikea_E1766.groovy index 1a57560..b5f19ea 100644 --- a/ikea-zigbee-drivers/Ikea_E1766.groovy +++ b/ikea-zigbee-drivers/Ikea_E1766.groovy @@ -570,7 +570,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index 8f6b2f2..c1f1c96 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -609,7 +609,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1812.groovy b/ikea-zigbee-drivers/Ikea_E1812.groovy index bf3c6d2..f6cc8ef 100644 --- a/ikea-zigbee-drivers/Ikea_E1812.groovy +++ b/ikea-zigbee-drivers/Ikea_E1812.groovy @@ -594,7 +594,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E1836.groovy b/ikea-zigbee-drivers/Ikea_E1836.groovy index 18dcf99..295635c 100644 --- a/ikea-zigbee-drivers/Ikea_E1836.groovy +++ b/ikea-zigbee-drivers/Ikea_E1836.groovy @@ -504,7 +504,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index 3e8c149..537ed75 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -617,7 +617,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2006.groovy b/ikea-zigbee-drivers/Ikea_E2006.groovy index e3c98c4..3658759 100644 --- a/ikea-zigbee-drivers/Ikea_E2006.groovy +++ b/ikea-zigbee-drivers/Ikea_E2006.groovy @@ -671,7 +671,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2013.groovy b/ikea-zigbee-drivers/Ikea_E2013.groovy index ac25227..a5029f3 100644 --- a/ikea-zigbee-drivers/Ikea_E2013.groovy +++ b/ikea-zigbee-drivers/Ikea_E2013.groovy @@ -644,7 +644,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2112.groovy b/ikea-zigbee-drivers/Ikea_E2112.groovy index 5496f27..0e758f4 100644 --- a/ikea-zigbee-drivers/Ikea_E2112.groovy +++ b/ikea-zigbee-drivers/Ikea_E2112.groovy @@ -496,7 +496,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2123.groovy b/ikea-zigbee-drivers/Ikea_E2123.groovy index d78294b..51d1006 100644 --- a/ikea-zigbee-drivers/Ikea_E2123.groovy +++ b/ikea-zigbee-drivers/Ikea_E2123.groovy @@ -698,7 +698,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index ce07b15..359066f 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -604,7 +604,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2201.groovy b/ikea-zigbee-drivers/Ikea_E2201.groovy index a1d9a68..9d12827 100644 --- a/ikea-zigbee-drivers/Ikea_E2201.groovy +++ b/ikea-zigbee-drivers/Ikea_E2201.groovy @@ -620,7 +620,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index 127e70f..ee99091 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -467,7 +467,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2204.groovy b/ikea-zigbee-drivers/Ikea_E2204.groovy index 56f07f4..1641322 100644 --- a/ikea-zigbee-drivers/Ikea_E2204.groovy +++ b/ikea-zigbee-drivers/Ikea_E2204.groovy @@ -558,7 +558,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_E2213.groovy b/ikea-zigbee-drivers/Ikea_E2213.groovy index d2c1f2b..ff3c2e7 100644 --- a/ikea-zigbee-drivers/Ikea_E2213.groovy +++ b/ikea-zigbee-drivers/Ikea_E2213.groovy @@ -479,7 +479,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index 18127c8..3a6a94c 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -836,7 +836,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Legrand_741811.groovy b/ikea-zigbee-drivers/Legrand_741811.groovy index 72ce4ce..5b8e1d1 100644 --- a/ikea-zigbee-drivers/Legrand_741811.groovy +++ b/ikea-zigbee-drivers/Legrand_741811.groovy @@ -592,7 +592,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Philips_RDM001.groovy b/ikea-zigbee-drivers/Philips_RDM001.groovy index 7d8f4d2..ab79e71 100644 --- a/ikea-zigbee-drivers/Philips_RDM001.groovy +++ b/ikea-zigbee-drivers/Philips_RDM001.groovy @@ -685,7 +685,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Philips_RWL022.groovy b/ikea-zigbee-drivers/Philips_RWL022.groovy index b0a3795..c094ab9 100644 --- a/ikea-zigbee-drivers/Philips_RWL022.groovy +++ b/ikea-zigbee-drivers/Philips_RWL022.groovy @@ -627,7 +627,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index c7376fd..d00de16 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -30,7 +30,7 @@ Lights: * [White Spectrum Light](#white-spectrum-light) * [Color White Spectrum Light](#color-white-spectrum-light) -Other: +Appliances: * [Starkvind Air Purifier (E2006)](#starkvind-air-purifier-e2006) Devices from other vendors (not in HPM): @@ -59,7 +59,7 @@ Once you have HPM installed, follow these steps to install the IKEA Zigbee drive * Select the driver(s) you need from the dropdown list and follow the install instructions. ### Manual Installation -If you don’t want to use HPM, you can also install the drivers manually by importing the driver code from GitHub. Follow these steps to do so: +If you don"t want to use HPM, you can also install the drivers manually by importing the driver code from GitHub. Follow these steps to do so: * In the Hubitat interface, go to **Drivers Code**. * Click **New Driver** in the top right, then click **Import** in the top right. @@ -69,87 +69,8 @@ If you don’t want to use HPM, you can also install the drivers manually by imp More info about installing custom drivers is available in the [Official Documentation](https://docs2.hubitat.com/en/how-to/install-custom-drivers). -## Device Features and Pairing -Below you can find the details of each device, including the features and pairing instructions. - - -### Askvader On/Off Switch (E1836) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `504.638.80` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1836.groovy` | -| Tested firmwares | `1.0.002` | -| Since version | `3.1.0` | - -#### Features -* Commands: On, Off, Toggle, On with Timed Off -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Health status: online / offline -* Refresh switch state on demand -* Can be member of Zigbee groups - -#### Pairing Instructions -1. Locate the small reset hole (located between the On/Off button and the LED light) and have a pin or pencil ready to fit the hole. -1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. -1. Plug the device back into an outlet. -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Insert the pin into the reset hole and press it for at least 5 seconds; the LED light will start blinking upon release. -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. You're all set! Enjoy using your Askvader On/Off Switch. - - -### Badring Water Leakage Sensor (E2202) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `605.043.52` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2202.groovy` | -| Tested firmwares | `1.0.7` | -| Since version | `3.9.0` | - -#### Features -* Water detection: wet / dry -* Battery report: % -* Health status: online / offline - -#### Pairing Instructions -1. Open the battery compartiment and you should see the small pair button (🔗). -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. -1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. Close the device battery compartiment. -1. You're all set! Enjoy using your Badring Water Leakage Sensor. - -### Parasoll Door/Window Sensor (E2013) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `805.043.08` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2013.groovy` | -| Tested firmwares | `1.0.19` | -| Since version | `3.6.0` | - -#### Features -* Contact status: open / close -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off -* Directly control Zigbee groups: On/Off - -#### Pairing Instructions -1. Remove device from its position using a small screwdriver and, on the back, you should see the small pair button (🔗). -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. -1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. Place the device back to its position by gently pressing it until you hear it clicking in place. -1. You're all set! Enjoy using your Parasoll Door/Window Sensor. - +## Remotes +Below you can find the details of each remote device, including the features and pairing instructions. ### Rodret Dimmer (E2201) @@ -162,14 +83,11 @@ Below you can find the details of each device, including the features and pairin | Since version | `2.0.0` | #### Features -* Button Push events: both buttons -* Button Hold events: both buttons -* Button Release events: both buttons -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off and Brightness -* Directly control Zigbee groups: On/Off and Brightness - +* **Button Events**: Supports "Push", "Hold", and "Release" events for both buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Direct Zigbee Device Control**: Enables direct control of On/Off and Brightness settings for Zigbee devices. +* **Direct Zigbee Group Control**: Enables direct control of On/Off and Brightness settings for Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions 1. Open the battery compartiment and you should see the small pair button (🔗). @@ -192,12 +110,11 @@ Below you can find the details of each device, including the features and pairin | Since version | `3.6.0` | #### Features -* Button Push events: both buttons -* Button Double-Tap event: both buttons -* Button Hold events: both buttons -* Button Release events: both buttons -* Battery report: % -* Health status: online / offline +* **Button Events**: Supports "Push", "Double-Tap", "Hold", and "Release" events for both buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Direct Zigbee Device Control**: Enables direct control of On/Off and Brightness settings for Zigbee devices. +* **Direct Zigbee Group Control**: Enables direct control of On/Off and Brightness settings for Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions 1. Using a small screwdriver, open the battery compartiment and you should see the small pair button (🔗). @@ -209,38 +126,6 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your Somrig Shortcut Button. -### Starkvind Air Purifier (E2006) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `194.442.19` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2006.groovy` | -| Tested firmwares | `1.0.033`|`1.1.001` | -| Since version | `3.5.0` | - -#### Features -* Commands: On, Off, Toggle, Set/Cycle fan speed -* Particulate Matter < 2.5 microns (PM 2.5) sensor data -* Calculate US AQI score based on PM 2.5 value -* Filter usage (%) and Filter status (normal / replace) information -* Configuration: Sensor report frequency, Filter life time, LED status -* Child lock: enable / disable physical controls on the device -* Dark mode: turn off LED indicator on the device, ensuring total darkness -* Health status: online / offline -* Refresh device state on demand - -#### Pairing Instructions -1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. -1. Plug the device back into an outlet. -1. Open the round top lid and you should see the pair button (🔗) next to the filter reset button. -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Press and hold the pair button **for at least 5 seconds** until the LED starts blinking. -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. Put back the lid and fix it in place. -1. You're all set! Enjoy using your Starkvind Air Purifier. - - ### Styrbar Remote Control N2 (E2002) | Parameter | Details | @@ -252,12 +137,10 @@ Below you can find the details of each device, including the features and pairin | Since version | `2.0.0` | #### Features -* Button Push events for: all buttons -* Button Hold events for: Button 1 (🔆) and Button 2 (🔅) -* Button Release events for: Button 1 (🔆), Button 2 (🔅) -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off, Brightness and Scenes +* **Button Events**: Supports "Push" events for all buttons, "Hold" and "Release" events for Button 1 (🔆) and Button 2 (🔅). +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off, Brightness, and Scenes for Zigbee devices. #### Known Issues * The Hold / Release events don't work correctly on the Next and Prev buttons. @@ -284,14 +167,11 @@ Below you can find the details of each device, including the features and pairin | Since version | `1.0.0` | #### Features -* Button Push events: all buttons -* Button Hold events: Button 2 (Plus), Button 3 (Minus), Button 6 (•) and Button 7 (••) -* Button Release events: Button 6 (•) and Button 7 (••) -* Button Double-Tap events: Button 6 (•) and Button 7 (••) -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off -* Directly control Zigbee groups: On/Off +* **Button Events**: Supports "Push" events for all buttons, "Hold" events for Button 2 (Plus), Button 3 (Minus), Button 6 (•), and Button 7 (••), "Release" and "Double-Tap" events for Button 6 (•) and Button 7 (••). +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off settings for Zigbee devices. +* **Zigbee Group Control**: Enables direct control of On/Off settings for Zigbee groups. This allows for efficient management of multiple devices. #### Known Issues * Old firmware versions (below 1.0.35) does not send the release event for Button 6 (•) and Button 7 (••) @@ -306,67 +186,6 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your Symfonisk Sound Remote. -### Tradfri Control Outlet (E1603, E1706) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `304.883.63`|`303.561.69` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1603.groovy` | -| Tested firmwares | `2.0.0244`|`2.3.089` | -| Since version | `2.0.0` | - -#### Features -* Commands: On, Off, Toggle, On with Timed Off -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Health status: online / offline -* Refresh switch state on demand -* Can be member of Zigbee groups - -#### Pairing Instructions -1. Find the small reset hole on the side of the device and make sure you have at hand a pin that can fit the reset hole - (e.g.: a paper clip or SIM card eject pin). -1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. -1. Plug the device back into an outlet. -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Insert the pin into the reset hole and press it for at least 5 seconds; upon release, the LED light will start - blinking. -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. You're all set! Enjoy using your Tradfri Control Outlet. - - -### Tradfri Motion Sensor (E1745) - -| Parameter | Details | -|-----------|-------------| -| Product Image | | -| Product Code | `704.299.13` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1745.groovy` | -| Tested firmwares | `24.4.5` | -| Since version | `2.3.0` | - -#### Features -* Motion detection: active / inactive (50 seconds cooldown) -* Illumination detection: dim / bright -* Option to detect motion only when dark -* Battery report: % -* Health status: online / offline - -#### Known Issues -* Old firmware versions (below 24.4.5) suppport binding to groups only and this functionality is not supported by the - Hubitat hub. You can update the device to the latest version using Hubitat. -* Illumination is reported only when motion is detected. - -#### Pairing Instructions -1. Open the back compartiment and you should see the small pair button (🔗). -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. -1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. Close the device battery compartiment. -1. You're all set! Enjoy using your Tradfri Motion Sensor. - - ### Tradfri On/Off Switch (E1743) | Parameter | Details | @@ -378,13 +197,10 @@ Below you can find the details of each device, including the features and pairin | Since version | `2.0.0` | #### Features -* Button Push events: both buttons -* Button Hold events: both buttons -* Button Release events: both buttons -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off and Brightness - +* **Button Events**: Supports "Push", "Hold", and "Release" events for both buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off and Brightness settings for Zigbee devices. #### Pairing Instructions 1. Using a small screwdriver, open the battery compartiment and you should see the small pair button (🔗). @@ -407,11 +223,10 @@ Below you can find the details of each device, including the features and pairin | Since version | `3.3.0` | #### Features -* Button Push events: both buttons -* Button Release events: both buttons -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: Window Covering +* **Button Events**: Supports "Push" and "Release" events for both buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of "Window Covering" for Zigbee devices. #### Pairing Instructions 1. Using a small screwdriver, open the battery compartiment and you should see the small pair button (🔗). @@ -434,15 +249,10 @@ Below you can find the details of each device, including the features and pairin | Since version | `1.1.0` | #### Features -* Button Push events: all buttons -* Button Hold events: Button 2 (🔆), Button 3 (🔅), Button 4 (Next) and Button 5 (Prev) -* Button Release events: Button 2 (🔆), Button 3 (🔅), Button 4 (Next) and Button 5 (Prev) -* Button 1 (Play) acts as a switch (on / off) -* Button 2 (🔆) and Button 3 (🔅) act as a switch level (0 - 100%) -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off, Brightness and Scenes - +* **Button Events**: Supports "Push" events for all buttons, "Hold" and "Release" events for Button 2 (🔆), Button 3 (🔅), Button 4 (Next), and Button 5 (Prev). +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off, Brightness, and Scenes for Zigbee devices. #### Known Issues * Old firmware versions suppport binding to groups only and this functionality is not supported by the Hubitat hub. You can update the device to the latest version using Hubitat. @@ -486,33 +296,91 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your Tradfri Shortcut Button. -### Tretakt Smart Plug (E2204) +## Sensors +Below you can find the details of each sensor device, including the features and pairing instructions. + +### Badring Water Leakage Sensor (E2202) | Parameter | Details | |-----------|-------------| -| Product Image | | -| Product Code | `805.403.49` | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2204.groovy` | -| Tested firmwares | `2.4.4` | -| Since version | `4.0.0` | +| Product Image | | +| Product Code | `605.043.52` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2202.groovy` | +| Tested firmwares | `1.0.7` | +| Since version | `3.9.0` | #### Features -* Commands: On, Off, Toggle, On with Timed Off -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Child lock: enable / disable physical button on the device -* Dark mode: turn off LED indicator on the device, ensuring total darkness -* Health status: online / offline -* Refresh switch state on demand -* Can be member of Zigbee groups +* **Water Detection**: Identifies the state of water presence as either "wet" or "dry". +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Pairing Instructions -1. Find the small reset button near on/off button and make sure you have a pointy object at hand. -1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. -1. Plug the device back into an outlet. +1. Open the battery compartiment and you should see the small pair button (🔗). 1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Push the small reset button on top of the smart plug with a pointy object for at least 5 seconds until the LED starts pulsating. +1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. +1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). 1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. You're all set! Enjoy using your Tretakt Smart Plug. +1. Close the device battery compartiment. +1. You're all set! Enjoy using your Badring Water Leakage Sensor. + +### Parasoll Door/Window Sensor (E2013) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `805.043.08` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2013.groovy` | +| Tested firmwares | `1.0.19` | +| Since version | `3.6.0` | + +#### Features +* **Contact Status**: Identifies the state of contact sensor as either "open" or "close". +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Direct Zigbee Device Control**: Enables direct control of On/Off and Brightness settings for Zigbee devices. +* **Direct Zigbee Group Control**: Enables direct control of On/Off and Brightness settings for Zigbee groups. This allows for efficient management of multiple devices. + +#### Pairing Instructions +1. Remove device from its position using a small screwdriver and, on the back, you should see the small pair button (🔗). +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. +1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. Place the device back to its position by gently pressing it until you hear it clicking in place. +1. You're all set! Enjoy using your Parasoll Door/Window Sensor. + + +### Tradfri Motion Sensor (E1745) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `704.299.13` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1745.groovy` | +| Tested firmwares | `24.4.5` | +| Since version | `2.3.0` | + +#### Features +This device offers a range of features: + +* **Motion Detection**: Identifies motion as either "active" or "inactive", with a cooldown period of 50 seconds. +* **Illumination Detection**: Detects illumination levels as either "dim" or "bright". +* **Dark Motion Detection**: Provides an option to detect motion only when the illumination is dark. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". + +#### Known Issues +* **Firmware Compatibility**: Older firmware versions (below 24.4.5) support binding to groups only. This functionality is not supported by the Hubitat hub. Users can update the device to the latest version using Hubitat. +* **Illumination Reporting**: Illumination levels are reported only when motion is detected. + +#### Pairing Instructions +1. Open the back compartiment and you should see the small pair button (🔗). +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. +1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. Close the device battery compartiment. +1. You're all set! Enjoy using your Tradfri Motion Sensor. ### Vallhorn Motion Sensor (E2134) @@ -555,11 +423,12 @@ Below you can find the details of each device, including the features and pairin | Since version | `3.8.0` | #### Features -* Particulate Matter < 2.5 microns (PM 2.5) sensor data -* Calculate US AQI score based on PM 2.5 value -* [Sensirion VOC Index](https://sensirion.com/media/documents/02232963/6294E043/Info_Note_VOC_Index.pdf) (1-500) -* Temperature and Relative Humidity -* Health status: online / offline +* **Particulate Matter Sensor**: Provides data for particulate matter less than 2.5 microns (PM 2.5). +* **US AQI Score Calculation**: Calculates the US Air Quality Index (AQI) score based on the PM 2.5 value. +* **Sensirion VOC Index**: Provides a Sensirion Volatile Organic Compounds (VOC) Index that ranges from 1 to 500, offering a quantified measure of the VOCs present in the surrounding air. +* **Temperature and Humidity Measurements**: Provide standard measurements of the current temperature and the amount of moisture in the air, respectively. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. #### Pairing Instructions 1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. @@ -568,6 +437,97 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your Vindstyrka Air Quality Sensor. +## Outlets +Below you can find the details of each outlet device, including the features and pairing instructions. + +### Askvader On/Off Switch (E1836) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `504.638.80` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1836.groovy` | +| Tested firmwares | `1.0.002` | +| Since version | `3.1.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Health Status**: Indicates whether the device is online or offline. +* **Refresh Light State**: Refreshes light state on demand. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Pairing Instructions +1. Locate the small reset hole (located between the On/Off button and the LED light) and have a pin or pencil ready to fit the hole. +1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. +1. Plug the device back into an outlet. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Insert the pin into the reset hole and press it for at least 5 seconds; the LED light will start blinking upon release. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Askvader On/Off Switch. + + +### Tradfri Control Outlet (E1603, E1706) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `304.883.63`|`303.561.69` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1603.groovy` | +| Tested firmwares | `2.0.0244`|`2.3.089` | +| Since version | `2.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Health Status**: Indicates whether the device is online or offline. +* **Refresh Light State**: Refreshes light state on demand. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Pairing Instructions +1. Find the small reset hole on the side of the device and make sure you have at hand a pin that can fit the reset hole + (e.g.: a paper clip or SIM card eject pin). +1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. +1. Plug the device back into an outlet. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Insert the pin into the reset hole and press it for at least 5 seconds; upon release, the LED light will start + blinking. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Tradfri Control Outlet. + + +### Tretakt Smart Plug (E2204) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `805.403.49` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2204.groovy` | +| Tested firmwares | `2.4.4` | +| Since version | `4.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Child Lock**: Allows the enabling or disabling of the physical button on the device, providing an additional layer of safety. +* **Dark Mode**: Allows the LED indicator on the device to be turned off, ensuring total darkness when needed. +* **Health Status**: Indicates whether the device is online or offline. +* **Refresh Light State**: Refreshes light state on demand. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Pairing Instructions +1. Find the small reset button near on/off button and make sure you have a pointy object at hand. +1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. +1. Plug the device back into an outlet. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Push the small reset button on top of the smart plug with a pointy object for at least 5 seconds until the LED starts pulsating. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Tretakt Smart Plug. + + +## Lights +Below you can find the details of each lighting device, including the features and pairing instructions. + ### Dimmable Light | Parameter | Details | @@ -577,14 +537,14 @@ Below you can find the details of each device, including the features and pairin | Since version | `5.0.0` | #### Features -* Commands: On, Off, Toggle, On with Timed Off -* Brightness control: Set brightness level, Start/Stop brightness level change, Step brightness level up/down -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Configure brightness level when turned on (Always the same fixed value, Restore last level) -* Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. -* Health status: online / offline -* Refresh light state on demand -* Can be member of Zigbee groups +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. +* **Health Status**: Indicates whether the device is online or offline. +* **Refresh Light State**: Refreshes light state on demand. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Tested devices * 10EU-IL-1: Tradfri LED Driver 10W @@ -602,19 +562,18 @@ Below you can find the details of each device, including the features and pairin | Since version | `5.0.0` | #### Features -* Commands: On, Off, Toggle, On with Timed Off -* Brightness control: Set brightness level, Start/Stop brightness level change, Step brightness level up/down -* Color temperature (CT) control: Set color temperature, Start/Stop color temperature change, Step color temperature up/down -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Configure brightness level when turned on (Always the same fixed value, Restore last level) -* Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. -* Pre-staging: Set color temperature when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color temperature. -* Health status: online / offline -* Refresh light state on demand -* Can be member of Zigbee groups +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Color Temperature (CT) Control**: Enables setting of color temperature, starting/stopping color temperature change, and stepping color temperature up/down. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level and color temperature when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature. +* **Health Status**: Indicates whether the device is online or offline. +* **Refresh Light State**: Refreshes light state on demand. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Known Issues -* Color temperature and brightness level can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. +* **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. #### Tested devices * LED2101G4: Tradfri Bulb E14 WS Globe 470lm @@ -641,21 +600,20 @@ Below you can find the details of each device, including the features and pairin | Since version | `5.0.0` | #### Features -* Commands: On, Off, Toggle, On with Timed Off -* Brightness control: Set brightness level, Start/Stop brightness level change, Step brightness level up/down -* Color temperature (CT) control: Set color temperature, Start/Stop color temperature change, Step color temperature up/down -* Color (RGB) control: Set color hue and/or saturation, display color name and colo mode -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Configure brightness level when turned on (Always the same fixed value, Restore last level) -* Pre-staging: Set brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. -* Pre-staging: Set color temperature when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color temperature. -* Pre-staging: Set color when the lights are off (and they stay off). When the lights are turned on, they will start with the specified color. -* Health status: online / offline -* Refresh light state on demand -* Can be member of Zigbee groups +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Color Temperature (CT) Control**: Enables setting of color temperature, starting/stopping color temperature change, and stepping color temperature up/down. +* **Color (RGB) Control**: Provides options to set color hue and/or saturation, and display color name and color mode. +* **Color Loop**: Initiates cycling of the color hue until stopped. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level, color temperature, and color when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature/color. +* **Health Status**: Indicates whether the device is online or offline. +* **Refresh Light State**: Refreshes light state on demand. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Known Issues -* Color temperature and brightness level can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. +* **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. #### Tested devices * L2112: Ormanas LED Strip @@ -678,6 +636,44 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your White Spectrum Light. +## Appliances +Below you can find the details of each appliance device, including the features and pairing instructions. + +### Starkvind Air Purifier (E2006) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `194.442.19` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E2006.groovy` | +| Tested firmwares | `1.0.033`|`1.1.001` | +| Since version | `3.5.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "Set/Cycle fan speed" commands. +* **Particulate Matter Sensor**: Provides data for particulate matter less than 2.5 microns (PM 2.5). +* **US AQI Score Calculation**: Calculates the US Air Quality Index (AQI) score based on the PM 2.5 value. +* **Filter Information**: Reports filter usage (%) and filter status (options include "normal" and "replace"). +* **Configuration Options**: Allows configuration of sensor report frequency, filter lifetime, and LED status. +* **Child Lock**: Enables or disables physical controls on the device for safety. +* **Dark Mode**: Turns off the LED indicator on the device, ensuring total darkness for user comfort. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. + +#### Pairing Instructions +1. If the device is already plugged in, unplug it for 20 seconds (power-cycle) before each pairing attempt. +1. Plug the device back into an outlet. +1. Open the round top lid and you should see the pair button (🔗) next to the filter reset button. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Press and hold the pair button **for at least 5 seconds** until the LED starts blinking. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. Put back the lid and fix it in place. +1. You're all set! Enjoy using your Starkvind Air Purifier. + + +## Devices from other vendors +Below you can find the details of each device, including the features and pairing instructions. + ### Aqara Dual Relay Module T2 (DCM-K01) | Parameter | Details | @@ -688,17 +684,16 @@ Below you can find the details of each device, including the features and pairin | Since version | `4.0.0` | #### Features -* Standard operation mode: switch S1/S2 controls relay L1/L2 -* Decoupled operation mode: switch S1/S2 only sends "push" events -* Configure switch type: latching / momentary / disabled -* Configure relay type: wet contact / dry contact / pulse mode (with configurable pulse duration: 200ms .. 2000ms) -* Configure interlock mode: prevent both Relay L1 and Relay L2 being On at the same time -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* Report device temperature -* Report power (W): L1 + L2 -* Report energy consumption (kWh): L1 + L2 -* Health status: online / offline -* Refresh device state on demand +* **Standard Operation Mode**: In this mode, the switches S1/S2 control the relays L1/L2. +* **Decoupled Operation Mode**: In this mode, the switches S1/S2 only send “push” events. +* **Switch Type Configuration**: Allows the configuration of the switch type to be either latching, momentary, or disabled. +* **Relay Type Configuration**: Allows the configuration of the relay type to be either a wet contact, dry contact, or pulse mode. The pulse mode has a configurable pulse duration ranging from 200ms to 2000ms. +* **Interlock Mode Configuration**: Prevents both Relay L1 and Relay L2 from being On at the same time. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Device Temperature Reporting**: Reports the device's temperature. +* **Power Reporting**: Reports the power (in Watts) of L1 + L2. +* **Energy Consumption Reporting**: Reports the energy consumption (in kWh) of L1 + L2. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Pairing Instructions 1. If the device is already powered on, disconnect it for 20 seconds (power-cycle) before each pairing attempt. @@ -719,12 +714,11 @@ Below you can find the details of each device, including the features and pairin | Since version | `4.0.0` | #### Features -* Commands: On, Off, Toggle, On with Timed Off -* Configure what happens after a power outage (Power On, Power Off, Restore previous state) -* LED mode: always On, always Off, follow device power state -* Report power (W) -* Health status: online / offline -* Refresh device state on demand +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Power Reporting**: Reports the power (in Watts) +* **LED Mode**: Configures the LED to be always on, always off, or to follow the device's power state. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Pairing Instructions 1. Find the small reset hole under the device face plate and make sure you have at hand a pin that can fit the reset hole @@ -747,12 +741,12 @@ Below you can find the details of each device, including the features and pairin | Since version | `4.0.0` | #### Features -* Configure switch style: single rocker / single push button / dual rocker / dual push button -* Button Push event: both switches -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off -* Directly control Zigbee groups: On/Off +* **Switch Style Configuration**: This feature allows users to configure the switch style, with options including single rocker, single push button, dual rocker, and dual push button. +* **Button Push Event**: This feature supports events triggered by the push of either switch. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off settings for Zigbee devices. +* **Direct Zigbee Group Control**: Enables direct control of On/Off settings for Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions 1. Connect the S1 switch cable @@ -773,13 +767,11 @@ Below you can find the details of each device, including the features and pairin | Since version | `4.0.0` | #### Features -* Button Push event: all 4 buttons -* Button Hold events: all 4 buttons -* Button Release events: all 4 buttons -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off -* Directly control Zigbee groups: On/Off +* **Button Events**: Supports "Push", "Hold", and "Release" events for all 4 buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off settings for Zigbee devices. +* **Direct Zigbee Group Control**: Enables direct control of On/Off settings for Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions 1. Open the battery compartiment, find the small reset hole and make sure you have at hand a pin that can fit the reset hole @@ -802,9 +794,9 @@ Below you can find the details of each device, including the features and pairin > I don't own this device and did not test it myself. #### Features -* Button Push event: all 4 buttons -* Battery report: % -* Health status: online / offline +* **Button Events**: Supports "Push" events for all 4 buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Known Issues * Device drops off the Zigbee mesh (unknown reason) diff --git a/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy b/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy index c25ce08..ea1051d 100644 --- a/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy +++ b/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy @@ -550,7 +550,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy index a02f4dd..f6f57a6 100644 --- a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy +++ b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy @@ -491,7 +491,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/src/blueprint.groovy b/ikea-zigbee-drivers/src/blueprint.groovy index fbd197c..1bc6ff0 100644 --- a/ikea-zigbee-drivers/src/blueprint.groovy +++ b/ikea-zigbee-drivers/src/blueprint.groovy @@ -290,7 +290,8 @@ private void utils_sendZigbeeCommands(List cmds) { sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) } private void utils_sendEvent(Map event) { - if (device.currentValue(event.name, true) != event.value || event.isStateChange) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { log_info "${event.descriptionText} [${event.type}]" } else { log_debug "${event.descriptionText} [${event.type}]" diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index dfec27d..acc7869 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -49,15 +49,17 @@ void setSaturation(BigDecimal saturation) { utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } void startColorLoop(String speed) { - Integer seconds = COLOR_LOOP_SPEED[speed] - log_debug "Starting color loop with ${seconds} seconds / loop" + Integer seconds = COLOR_LOOP_SPEED[speed] ?: 30 + log_info "Starting color loop at ${speed} speed (${seconds} sec per loop)" String payload = "0F 01 01 ${utils_payload seconds, 4} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set + state.loop = true } void stopColorLoop() { - log_debug "Stopping color loop" + log_info "Stopped color loop" String payload = "0F 00 01 0000 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set + state.remove 'loop' } private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] @@ -88,7 +90,7 @@ private void processMultipleColorAttributes(Map msg, String type) { } } - if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type, noInfo:(state.loop == true) if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type // Update colorName, if the case @@ -96,7 +98,7 @@ private void processMultipleColorAttributes(Map msg, String type) { Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) String colorName = convertHueToGenericColorName colorHue, colorSaturation - utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type, noInfo:(state.loop == true) } utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" } From d0d0af4cac690237e5d5109028c0a575f8554a28 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 28 Apr 2024 14:37:55 +0300 Subject: [PATCH 12/26] Update README --- ikea-zigbee-drivers/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index d00de16..5a340b2 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -52,20 +52,20 @@ HPM is an app that allows you to easily install and update custom drivers and ap Once you have HPM installed, follow these steps to install the IKEA Zigbee drivers: -* In the Hubitat interface, go to **Apps** and select **Hubitat Package Manager**. -* Select **Install**, then **Search by Keywords**. -* Enter **IKEA Zigbee drivers** in the search box and click **Next**. -* Select **IKEA Zigbee drivers by Dan Danache** and click **Next**. -* Select the driver(s) you need from the dropdown list and follow the install instructions. +1. In the Hubitat interface, go to **Apps** and select **Hubitat Package Manager**. +1. Select **Install**, then **Search by Keywords**. +1. Enter **IKEA Zigbee drivers** in the search box and click **Next**. +1. Select **IKEA Zigbee drivers by Dan Danache** and click **Next**. +1. Select the driver(s) you need from the dropdown list and follow the install instructions. ### Manual Installation If you don"t want to use HPM, you can also install the drivers manually by importing the driver code from GitHub. Follow these steps to do so: -* In the Hubitat interface, go to **Drivers Code**. -* Click **New Driver** in the top right, then click **Import** in the top right. -* Search below for your device, look for the **Manual install file** property and enter it in the URL field. -* Click **Import**, then click **OK** and the code should load in the editor. -* Click **Save** in the top right. +1. In the Hubitat interface, go to **Drivers Code**. +1. Click **New Driver** in the top right, then click **Import** in the top right. +1. Search below for your device, look for the **Manual install file** property and enter it in the URL field. +1. Click **Import**, then click **OK** and the code should load in the editor. +1. Click **Save** in the top right. More info about installing custom drivers is available in the [Official Documentation](https://docs2.hubitat.com/en/how-to/install-custom-drivers). From efc1bc7f47b4c6421339ab3331d6ad4762a2a655 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 28 Apr 2024 14:46:29 +0300 Subject: [PATCH 13/26] Update README --- ikea-zigbee-drivers/README.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 5a340b2..5d8a40a 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -278,13 +278,10 @@ Below you can find the details of each remote device, including the features and | Since version | `2.0.0` | #### Features -* Button Push event -* Button Double-Tap event (only on firmware `24.4.6` and above) -* Button Hold event -* Button Release event -* Battery report: % -* Health status: online / offline -* Directly control Zigbee devices: On/Off +* **Button Events**: Supports "Push", "Double-Tap" (only on firmware `24.4.6` and above), "Hold", and "Release" events. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Direct Zigbee Device Control**: Enables direct control of On/Off and Brightness settings for Zigbee devices. #### Pairing Instructions 1. Using a small screwdriver, open the battery compartiment and you should see the small pair button (🔗). @@ -394,10 +391,10 @@ This device offers a range of features: | Since version | `3.6.0` | #### Features -* Motion detection: active / inactive (20 seconds cooldown) -* Illumination lux reporting (5 minutes cooldown) -* Battery report: % -* Health status: online / offline +* **Motion Detection**: Identifies motion as either "active" or "inactive", with a cooldown period of 20 seconds. +* **Illumination Lux Reporting**: Provides reports on the level of illumination, measured in lux. Reports are generated every 5 minutes. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Known Issues * Max reported illuminance is 1364 lux; more than enough for indoor usage @@ -454,7 +451,7 @@ Below you can find the details of each outlet device, including the features and * **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. * **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). * **Health Status**: Indicates whether the device is online or offline. -* **Refresh Light State**: Refreshes light state on demand. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions @@ -481,7 +478,7 @@ Below you can find the details of each outlet device, including the features and * **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. * **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). * **Health Status**: Indicates whether the device is online or offline. -* **Refresh Light State**: Refreshes light state on demand. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions @@ -512,7 +509,7 @@ Below you can find the details of each outlet device, including the features and * **Child Lock**: Allows the enabling or disabling of the physical button on the device, providing an additional layer of safety. * **Dark Mode**: Allows the LED indicator on the device to be turned off, ensuring total darkness when needed. * **Health Status**: Indicates whether the device is online or offline. -* **Refresh Light State**: Refreshes light state on demand. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Pairing Instructions @@ -543,7 +540,7 @@ Below you can find the details of each lighting device, including the features a * **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). * **Pre-staging**: Allows setting of brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. * **Health Status**: Indicates whether the device is online or offline. -* **Refresh Light State**: Refreshes light state on demand. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Tested devices @@ -569,7 +566,7 @@ Below you can find the details of each lighting device, including the features a * **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). * **Pre-staging**: Allows setting of brightness level and color temperature when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature. * **Health Status**: Indicates whether the device is online or offline. -* **Refresh Light State**: Refreshes light state on demand. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Known Issues @@ -609,7 +606,7 @@ Below you can find the details of each lighting device, including the features a * **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). * **Pre-staging**: Allows setting of brightness level, color temperature, and color when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature/color. * **Health Status**: Indicates whether the device is online or offline. -* **Refresh Light State**: Refreshes light state on demand. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Known Issues @@ -693,6 +690,7 @@ Below you can find the details of each device, including the features and pairin * **Device Temperature Reporting**: Reports the device's temperature. * **Power Reporting**: Reports the power (in Watts) of L1 + L2. * **Energy Consumption Reporting**: Reports the energy consumption (in kWh) of L1 + L2. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Pairing Instructions @@ -718,6 +716,7 @@ Below you can find the details of each device, including the features and pairin * **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). * **Power Reporting**: Reports the power (in Watts) * **LED Mode**: Configures the LED to be always on, always off, or to follow the device's power state. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". #### Pairing Instructions From e068185c9f77fa82ac1060ad4bdfe0e77614e84d Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 28 Apr 2024 14:48:22 +0300 Subject: [PATCH 14/26] Update README --- ikea-zigbee-drivers/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 5d8a40a..acba812 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -690,8 +690,8 @@ Below you can find the details of each device, including the features and pairin * **Device Temperature Reporting**: Reports the device's temperature. * **Power Reporting**: Reports the power (in Watts) of L1 + L2. * **Energy Consumption Reporting**: Reports the energy consumption (in kWh) of L1 + L2. -* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. #### Pairing Instructions 1. If the device is already powered on, disconnect it for 20 seconds (power-cycle) before each pairing attempt. @@ -716,8 +716,8 @@ Below you can find the details of each device, including the features and pairin * **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). * **Power Reporting**: Reports the power (in Watts) * **LED Mode**: Configures the LED to be always on, always off, or to follow the device's power state. -* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. * **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. #### Pairing Instructions 1. Find the small reset hole under the device face plate and make sure you have at hand a pin that can fit the reset hole From 4df50b162d759de16a732012e07b13abc0c41584 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Mon, 29 Apr 2024 21:51:31 +0300 Subject: [PATCH 15/26] E2006: Move "Dark Mode" preference to "setIndicatorStatus()" command and "indicatorStatus" attribute - `@userLP24` --- ikea-zigbee-drivers/CHANGELOG.md | 3 +- ikea-zigbee-drivers/Ikea_E2006.groovy | 33 ++++++++----------- ikea-zigbee-drivers/README.md | 3 +- ikea-zigbee-drivers/packageManifest.json | 2 +- .../src/devices/Ikea_E2006/E2006.groovy | 33 ++++++++----------- 5 files changed, 30 insertions(+), 44 deletions(-) diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 1e7a289..ab8e15b 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add driver for White Spectrum Lights devices (CT) - Add driver for Color White Spectrum Lights devices (RGB+CT) - Add Scenes support using Zigbee Bindings for E1810 and E2002 -- Put all devices in "identifying mode" while `configure()` is running - `@UncleAlias` +- Put all devices in "identifying mode" while "configure()" is running - `@UncleAlias` +- E2006: Move "Dark Mode" preference to "setIndicatorStatus()" command and "indicatorStatus" attribute - `@userLP24` ### Changed - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver diff --git a/ikea-zigbee-drivers/Ikea_E2006.groovy b/ikea-zigbee-drivers/Ikea_E2006.groovy index 3658759..774c6b8 100644 --- a/ikea-zigbee-drivers/Ikea_E2006.groovy +++ b/ikea-zigbee-drivers/Ikea_E2006.groovy @@ -43,14 +43,16 @@ metadata { attribute 'filterUsage', 'number' attribute 'pm25', 'number' attribute 'auto', 'enum', ['on', 'off'] + attribute 'indicatorStatus', 'enum', ['on', 'off'] // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } // Commands for devices.Ikea_E2006 - command 'setSpeed', [[name:'Fan speed*', type:'ENUM', description:'Fan speed to set', constraints:SUPPORTED_FAN_SPEEDS]] + command 'setSpeed', [[name:'Fan speed*', type:'ENUM', description:'Select the desired fan speed', constraints:SUPPORTED_FAN_SPEEDS]] command 'toggle' + command 'setIndicatorStatus', [[name:'Status*', type:'ENUM', description:'Select LED indicators status on the device', constraints:['on', 'off']]] // Commands for capability.FirmwareUpdate command 'updateFirmware' @@ -110,12 +112,6 @@ metadata { title: 'Child lock', description: 'Lock physical controls, safeguarding against accidental operation.', defaultValue: false - ) - input( - name: 'darkMode', type: 'bool', - title: 'Dark mode', - description: 'Turn off LED indicators on the device, ensuring total darkness.', - defaultValue: false ) } } @@ -167,13 +163,6 @@ List updated(boolean auto = false) { log_info "🛠️ childLock = ${childLock}" cmds += zigbee.writeAttribute(0xFC7D, 0x0005, 0x10, childLock ? 0x01 : 0x00, [mfgCode:'0x117C']) - if (darkMode == null) { - darkMode = false - device.updateSetting 'darkMode', [value:darkMode, type:'bool'] - } - log_info "🛠️ darkMode = ${darkMode}" - cmds += zigbee.writeAttribute(0xFC7D, 0x0003, 0x10, darkMode ? 0x01 : 0x00, [mfgCode:'0x117C']) - // Preferences for capability.HealthCheck schedule HEALTH_CHECK.schedule, 'healthCheck' @@ -231,7 +220,7 @@ void configure(boolean auto = false) { cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0000 0x23 0x0000 0x0258 {0A} {117C}" // Report FilterRunTime (uint32) at least every 10 minutes cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0001 0x20 0x0000 0x0000 {01} {117C}" // Report ReplaceFilter (uint8) cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0002 0x23 0x0000 0x0000 {01} {117C}" // Report FilterLifeTime (uint32) - //cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0003 0x10 0x0000 0x0000 {01} {117C}" // Report DarkMode (bool) + //cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0003 0x10 0x0000 0x0000 {01} {117C}" // Report IndicatorStatus (bool) //cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0004 0x21 0x0000 0x0258 {01} {117C}" // Report PM25Measurement (uint16) cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0005 0x10 0x0000 0x0000 {01} {117C}" // Report ChildLock (bool) cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0006 0x20 0x0000 0x0000 {01} {117C}" // Report FanMode (uint8) @@ -275,7 +264,7 @@ void refresh(boolean auto = false) { cmds += zigbee.readAttribute(0xFC7D, 0x0000, [mfgCode: '0x117C']) // FilterRunTime cmds += zigbee.readAttribute(0xFC7D, 0x0001, [mfgCode: '0x117C']) // ReplaceFilter cmds += zigbee.readAttribute(0xFC7D, 0x0002, [mfgCode: '0x117C']) // FilterLifeTime - cmds += zigbee.readAttribute(0xFC7D, 0x0003, [mfgCode: '0x117C']) // DarkMode + cmds += zigbee.readAttribute(0xFC7D, 0x0003, [mfgCode: '0x117C']) // IndicatorStatus cmds += zigbee.readAttribute(0xFC7D, 0x0004, [mfgCode: '0x117C']) // PM25Measurement cmds += zigbee.readAttribute(0xFC7D, 0x0005, [mfgCode: '0x117C']) // ChildLock cmds += zigbee.readAttribute(0xFC7D, 0x0006, [mfgCode: '0x117C']) // FanMode @@ -360,6 +349,10 @@ void cycleSpeed() { log_debug "Cycling speed to: ${newSpeed}" utils_sendZigbeeCommands(zigbee.writeAttribute(0xFC7D, 0x0006, 0x20, newSpeed, [mfgCode:'0x117C'])) } +void setIndicatorStatus(String status) { + utils_sendZigbeeCommands(zigbee.writeAttribute(0xFC7D, 0x0003, 0x10, status == 'off' ? 0x01 : 0x00, [mfgCode:'0x117C'])) + utils_sendEvent name:'indicatorStatus', value:status, descriptionText:"Indicator status turned ${status}", type:'digital' +} private Integer lerp(Integer ylo, Integer yhi, BigDecimal xlo, BigDecimal xhi, Integer cur) { return Math.round(((cur - xlo) / (xhi - xlo)) * (yhi - ylo) + ylo) } @@ -541,11 +534,11 @@ void parse(String description) { utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "FilterLifeTime=${msg.value} (${lifeTimeDays} days)" return - // Read Attributes: DarkMode + // Read Attributes: IndicatorStatus case { contains it, [clusterInt:0xFC7D, commandInt:0x01, attrInt:0x0003] }: - darkMode = msg.value == '01' - device.updateSetting 'darkMode', [value:darkMode, type:'bool'] - utils_processedZclMessage 'Read Attributes Response', "DarkMode=${msg.value}" + String indicatorStatus = msg.value == '01' ? 'off' : 'on' + utils_sendEvent name:'indicatorStatus', value:status, descriptionText:"Indicator status turned ${indicatorStatus}", type:'digital' + utils_processedZclMessage 'Read Attributes Response', "IndicatorStatus=${indicatorStatus}" return // Report/Read Attributes: ChildLock diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index acba812..97c719b 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -651,9 +651,8 @@ Below you can find the details of each appliance device, including the features * **Particulate Matter Sensor**: Provides data for particulate matter less than 2.5 microns (PM 2.5). * **US AQI Score Calculation**: Calculates the US Air Quality Index (AQI) score based on the PM 2.5 value. * **Filter Information**: Reports filter usage (%) and filter status (options include "normal" and "replace"). -* **Configuration Options**: Allows configuration of sensor report frequency, filter lifetime, and LED status. +* **Configuration Options**: Allows configuration of sensor report frequency, filter lifetime, and LED indicator status. * **Child Lock**: Enables or disables physical controls on the device for safety. -* **Dark Mode**: Turns off the LED indicator on the device, ensuring total darkness for user comfort. * **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". * **Device State Refresh**: Refreshes the device state on demand for real-time status updates. diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index c3061bc..ba1dc3d 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-04-??", diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy index 37188ec..063eaa5 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2006/E2006.groovy @@ -21,13 +21,15 @@ attribute 'airQuality', 'enum', ['good', 'moderate', 'unhealthy for sensitive gr attribute 'filterUsage', 'number' attribute 'pm25', 'number' attribute 'auto', 'enum', ['on', 'off'] +attribute 'indicatorStatus', 'enum', ['on', 'off'] {{/ @attributes }} {{!--------------------------------------------------------------------------}} {{# @commands }} // Commands for devices.Ikea_E2006 -command 'setSpeed', [[name:'Fan speed*', type:'ENUM', description:'Fan speed to set', constraints:SUPPORTED_FAN_SPEEDS]] +command 'setSpeed', [[name:'Fan speed*', type:'ENUM', description:'Select the desired fan speed', constraints:SUPPORTED_FAN_SPEEDS]] command 'toggle' +command 'setIndicatorStatus', [[name:'Status*', type:'ENUM', description:'Select LED indicators status on the device', constraints:['on', 'off']]] {{/ @commands }} {{!--------------------------------------------------------------------------}} {{# @inputs }} @@ -65,12 +67,6 @@ input( title: 'Child lock', description: 'Lock physical controls, safeguarding against accidental operation.', defaultValue: false -) -input( - name: 'darkMode', type: 'bool', - title: 'Dark mode', - description: 'Turn off LED indicators on the device, ensuring total darkness.', - defaultValue: false ) {{/ @inputs }} {{!--------------------------------------------------------------------------}} @@ -154,6 +150,10 @@ void cycleSpeed() { log_debug "Cycling speed to: ${newSpeed}" utils_sendZigbeeCommands(zigbee.writeAttribute(0xFC7D, 0x0006, 0x20, newSpeed, [mfgCode:'0x117C'])) } +void setIndicatorStatus(String status) { + utils_sendZigbeeCommands(zigbee.writeAttribute(0xFC7D, 0x0003, 0x10, status == 'off' ? 0x01 : 0x00, [mfgCode:'0x117C'])) + utils_sendEvent name:'indicatorStatus', value:status, descriptionText:"Indicator status turned ${status}", type:'digital' +} private Integer lerp(Integer ylo, Integer yhi, BigDecimal xlo, BigDecimal xhi, Integer cur) { return Math.round(((cur - xlo) / (xhi - xlo)) * (yhi - ylo) + ylo) } @@ -193,13 +193,6 @@ if (childLock == null) { } log_info "🛠️ childLock = ${childLock}" cmds += zigbee.writeAttribute(0xFC7D, 0x0005, 0x10, childLock ? 0x01 : 0x00, [mfgCode:'0x117C']) - -if (darkMode == null) { - darkMode = false - device.updateSetting 'darkMode', [value:darkMode, type:'bool'] -} -log_info "🛠️ darkMode = ${darkMode}" -cmds += zigbee.writeAttribute(0xFC7D, 0x0003, 0x10, darkMode ? 0x01 : 0x00, [mfgCode:'0x117C']) {{/ @updated }} {{!--------------------------------------------------------------------------}} {{# @configure }} @@ -211,7 +204,7 @@ cmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0xFC7D {${device.zigbeeI cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0000 0x23 0x0000 0x0258 {0A} {117C}" // Report FilterRunTime (uint32) at least every 10 minutes cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0001 0x20 0x0000 0x0000 {01} {117C}" // Report ReplaceFilter (uint8) cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0002 0x23 0x0000 0x0000 {01} {117C}" // Report FilterLifeTime (uint32) -//cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0003 0x10 0x0000 0x0000 {01} {117C}" // Report DarkMode (bool) +//cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0003 0x10 0x0000 0x0000 {01} {117C}" // Report IndicatorStatus (bool) //cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0004 0x21 0x0000 0x0258 {01} {117C}" // Report PM25Measurement (uint16) cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0005 0x10 0x0000 0x0000 {01} {117C}" // Report ChildLock (bool) cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0006 0x20 0x0000 0x0000 {01} {117C}" // Report FanMode (uint8) @@ -225,7 +218,7 @@ cmds += "he cr 0x${device.deviceNetworkId} 0x01 0xFC7D 0x0007 0x20 0x0000 0x0000 cmds += zigbee.readAttribute(0xFC7D, 0x0000, [mfgCode: '0x117C']) // FilterRunTime cmds += zigbee.readAttribute(0xFC7D, 0x0001, [mfgCode: '0x117C']) // ReplaceFilter cmds += zigbee.readAttribute(0xFC7D, 0x0002, [mfgCode: '0x117C']) // FilterLifeTime -cmds += zigbee.readAttribute(0xFC7D, 0x0003, [mfgCode: '0x117C']) // DarkMode +cmds += zigbee.readAttribute(0xFC7D, 0x0003, [mfgCode: '0x117C']) // IndicatorStatus cmds += zigbee.readAttribute(0xFC7D, 0x0004, [mfgCode: '0x117C']) // PM25Measurement cmds += zigbee.readAttribute(0xFC7D, 0x0005, [mfgCode: '0x117C']) // ChildLock cmds += zigbee.readAttribute(0xFC7D, 0x0006, [mfgCode: '0x117C']) // FanMode @@ -327,11 +320,11 @@ case { contains it, [clusterInt:0xFC7D, commandInt:0x01, attrInt:0x0002] }: utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "FilterLifeTime=${msg.value} (${lifeTimeDays} days)" return -// Read Attributes: DarkMode +// Read Attributes: IndicatorStatus case { contains it, [clusterInt:0xFC7D, commandInt:0x01, attrInt:0x0003] }: - darkMode = msg.value == '01' - device.updateSetting 'darkMode', [value:darkMode, type:'bool'] - utils_processedZclMessage 'Read Attributes Response', "DarkMode=${msg.value}" + String indicatorStatus = msg.value == '01' ? 'off' : 'on' + utils_sendEvent name:'indicatorStatus', value:status, descriptionText:"Indicator status turned ${indicatorStatus}", type:'digital' + utils_processedZclMessage 'Read Attributes Response', "IndicatorStatus=${indicatorStatus}" return // Report/Read Attributes: ChildLock From ed34e9f559288e4d45f119b79702d8e0f25055ac Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Tue, 30 Apr 2024 17:37:28 +0300 Subject: [PATCH 16/26] Add new fingerprints. Update README. --- ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 11 ++-- ikea-zigbee-drivers/README.md | 51 +++++++++++-------- .../src/devices/Ikea_DIM-Light/config.yaml | 17 +++++-- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index 4b02851..51ef457 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -35,11 +35,14 @@ metadata { capability 'PowerSource' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' // LED2103G5: 1.0.36 (117C-2100-01000036) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 1.2.245 (117C-4101-12245572) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1 (Tradfri LED Driver 10W): 2.3.086 (117C-4101-23086631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 WW 345lm8', manufacturer:'IKEA of Sweden' // LED2104R3: 1.0.36 (117C-2100-01000036) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 opal 1000lm', manufacturer:'IKEA of Sweden' // LED1623G12: 2.3.094 (117C-2101-23094631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1: 1.2.245 (117C-4101-12245572) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1: 2.3.086 (117C-4101-23086631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' // LED1842G3: 2.3.093 (117C-4103-23093631) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1 (Silverglans LED Driver 30W): 1.0.021 (117C-4104-00010021) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // 30EU-IL-2 (Tradfri LED Driver 30W): 1.0.002 (117C-4109-00010002) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW 806lm', manufacturer:'IKEA of Sweden' // LED1836G9: 2.3.094 (117C-4103-23093631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1: 1.0.021 (117C-4104-00010021) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' // 30EU-IL-2: 1.0.002 (117C-4109-00010002) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 97c719b..72a9a34 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -544,11 +544,16 @@ Below you can find the details of each lighting device, including the features a * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Tested devices -* 10EU-IL-1: Tradfri LED Driver 10W -* 30EU-IL-2: Tradfri LED Driver 30W -* 30-IL44-1: Silverglans LED Driver 30W -* LED2103G5: Tradfri Bulb E27 WW Globe 806lm -* LED1842G3: Tradfri Bulb E27 WW Clear 250lm +| Type | Name | +|--------------|---------------------------------| +| LED2103G5 | Tradfri Bulb E27 WW Globe 806lm | +| LED2104R3 | Tradfri Bulb GU10 WW 345lm | +| LED1623G12 | Tradfri Bulb E27 Opal 1000lm | +| 10EU-IL-1 | Tradfri LED Driver 10W | +| LED1842G3 | Tradfri Bulb E27 WW Clear 250lm | +| LED1836G9 | Tradfri Bulb E27 WW 806lm | +| 30-IL44-1 | Silverglans LED Driver 30W | +| 30EU-IL-2 | Tradfri LED Driver 30W | ### White Spectrum Light @@ -573,12 +578,15 @@ Below you can find the details of each lighting device, including the features a * **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. #### Tested devices -* LED2101G4: Tradfri Bulb E14 WS Globe 470lm -* LED1949C5: Tradfri Bulb E14 WS Candle Opal 470lm -* LED2002G5: Tradfri Bulb E14 WS Globe Opal 470lm -* LED2201G8: Tradfri Bulb E27 WS Globe 1055lm -* LED2005R5: Tradfri Bulb GU10 WS 345lm -* LED2106R3: Tradfri Bulb GU10 WS 345lm + +| Type | Name | +|--------------|---------------------------------------| +| LED2106R3 | Tradfri Bulb GU10 WS 345lm | +| LED2101G4 | Tradfri Bulb E14 WS Globe 470lm | +| LED1949C5 | Tradfri Bulb E14 WS Candle Opal 470lm | +| LED2002G5 | Tradfri Bulb E14 WS Globe Opal 470lm | +| LED2005R5 | Tradfri Bulb GU10 WS 345lm | +| LED2201G8 | Tradfri Bulb E27 WS Globe 1055lm | #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. @@ -613,17 +621,16 @@ Below you can find the details of each lighting device, including the features a * **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. #### Tested devices -* L2112: Ormanas LED Strip -* LED2111G6: Tradfri Bulb E14 CWS Globe 806lm -* LED1925G6: Tradfri Bulb E14 CWS 470lm -* LED1923R5: Tradfri Bulb GU10 CWS 345lm -* LED1624G9E27EU: Tradfri Bulb E27 CWS Opal 600lm - - All Colour Temp changes failed. No errors generated in the test. Below errors from the log all happened during config: - - Read Attributes Reponse: 0x400B=UNSUPPORTED_ATTRIBUTE, 0x400C=UNSUPPORTED_ATTRIBUTE (ColorTemperaturePhysicalMinMireds, ColorTemperaturePhysicalMaxMireds) - - Read Attributes Reponse: 0x0007=UNSUPPORTED_ATTRIBUTE, 0x0008=01 (ColorTemperatureMireds, ColorMode) -* LED1624G9E14EU: Tradfri Bulb E14 CWS Opal 600lm - - Same as above -* LED1924G9: Tradfri Bulb E27 CWS 806lm + +| Type | Name | Notes | +|----------------|---------------------------------------|--------------------------| +| LED1624G9E27EU | Tradfri Bulb E27 CWS Opal 600lm | Color Temp does not work | +| LED1924G9 | Tradfri Bulb E27 CWS 806lm | -- | +| LED1923R5 | Tradfri Bulb GU10 CWS 345lm | -- | +| LED1925G6 | Tradfri Bulb E14 CWS 470lm | -- | +| LED1624G9E14EU | Tradfri Bulb E14 CWS Opal 600lm | Color Temp does not work | +| L2112 | Ormanas LED Strip | -- | +| LED2111G6 | Tradfri Bulb E14 CWS Globe 806lm | -- | #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. diff --git a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml index 2327566..658a30a 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml @@ -12,19 +12,28 @@ device: - type: LED2103G5 firmwares: 1.0.36 (117C-2100-01000036) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' - - type: 10EU-IL-1 (Tradfri LED Driver 10W) + - type: LED2104R3 + firmwares: 1.0.36 (117C-2100-01000036) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 WW 345lm8', manufacturer:'IKEA of Sweden' + - type: LED1623G12 + firmwares: 2.3.094 (117C-2101-23094631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 opal 1000lm', manufacturer:'IKEA of Sweden' + - type: 10EU-IL-1 firmwares: 1.2.245 (117C-4101-12245572) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - - type: 10EU-IL-1 (Tradfri LED Driver 10W) + - type: 10EU-IL-1 firmwares: 2.3.086 (117C-4101-23086631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - type: LED1842G3 firmwares: 2.3.093 (117C-4103-23093631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' - - type: 30-IL44-1 (Silverglans LED Driver 30W) + - type: LED1836G9 + firmwares: 2.3.094 (117C-4103-23093631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW 806lm', manufacturer:'IKEA of Sweden' + - type: 30-IL44-1 firmwares: 1.0.021 (117C-4104-00010021) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' - - type: 30EU-IL-2 (Tradfri LED Driver 30W) + - type: 30EU-IL-2 firmwares: 1.0.002 (117C-4109-00010002) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRI Driver 30W', manufacturer:'IKEA of Sweden' From 25a956083045f1fff3ca03847a7002cc811bdd0b Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Fri, 3 May 2024 15:44:22 +0300 Subject: [PATCH 17/26] Add driver for RGB-Only Lights devices (RGB) --- ikea-zigbee-drivers/CHANGELOG.md | 3 +- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 10 +- ikea-zigbee-drivers/Ikea_RGBO-Light.groovy | 858 ++++++++++++++++++ ikea-zigbee-drivers/Ikea_WS-Light.groovy | 2 +- ikea-zigbee-drivers/README.md | 314 ++++--- ikea-zigbee-drivers/img/Ikea_RGBO-Light.webp | Bin 0 -> 8008 bytes ikea-zigbee-drivers/packageManifest.json | 28 +- .../src/capabilities/ColorControl.groovy | 6 +- .../src/capabilities/ColorTemperature.groovy | 2 +- .../src/devices/Ikea_CWS-Light/config.yaml | 6 - .../src/devices/Ikea_RGBO-Light/config.yaml | 33 + 11 files changed, 1111 insertions(+), 151 deletions(-) create mode 100644 ikea-zigbee-drivers/Ikea_RGBO-Light.groovy create mode 100644 ikea-zigbee-drivers/img/Ikea_RGBO-Light.webp create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_RGBO-Light/config.yaml diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index ab8e15b..a567c5d 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -5,12 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [5.0.0] - 2024-04-?? +## [5.0.0] - 2024-05-?? ### Added - Add driver for Dimmable Lights devices, including LED drivers - Add driver for White Spectrum Lights devices (CT) - Add driver for Color White Spectrum Lights devices (RGB+CT) +- Add driver for RGB-Only Lights devices (RGB) - Add Scenes support using Zigbee Bindings for E1810 and E2002 - Put all devices in "identifying mode" while "configure()" is running - `@UncleAlias` - E2006: Move "Dark Mode" preference to "setIndicatorStatus()" command and "indicatorStatus" attribute - `@userLP24` diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index f57f647..561447d 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -12,7 +12,7 @@ import groovy.transform.Field // Fields for capability.ColorControl @Field static final Map COLOR_LOOP_SPEED = [ - 'swift':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 + 'swift':5, 'quick':10, 'moderate':20, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 ] // Fields for capability.HealthCheck @@ -43,11 +43,9 @@ metadata { capability 'HealthCheck' capability 'PowerSource' - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E27EU: 2.3.093 (117C-2801-23086631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' // LED1924G9: 1.0.021 (117C-2802-10021655) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' // LED1923R5: 1.0.021 (117C-2802-10021655) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' // LED1925G6: 1.0.021 (117C-2802-10021655) - fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E14EU: 2.3.093 (117C-2803-23093631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) @@ -503,8 +501,8 @@ void startColorLoop(String speed) { state.loop = true } void stopColorLoop() { - log_info "Stopped color loop" - String payload = "0F 00 01 0000 0000 00 00" + log_info 'Stopped color loop' + String payload = '0F 00 01 0000 0000 00 00' utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set state.remove 'loop' } @@ -559,7 +557,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) - if (level > 0 && duration == 0) setLevel level, duration + if (level > 0 && duration == 0) setLevel level } void startColorTemperatureChange(String direction) { log_debug "Starting color temperature change ${direction}wards with a rate of ${colorTemperatureChangeRate}% / second" diff --git a/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy b/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy new file mode 100644 index 0000000..27567e8 --- /dev/null +++ b/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy @@ -0,0 +1,858 @@ +/** + * IKEA RGB-Only Light + * + * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ + */ +import groovy.transform.CompileStatic +import groovy.transform.Field + +@Field static final String DRIVER_NAME = 'IKEA RGB-Only Light' +@Field static final String DRIVER_VERSION = '5.0.0' + +// Fields for capability.ColorControl + +@Field static final Map COLOR_LOOP_SPEED = [ + 'swift':5, 'quick':10, 'moderate':20, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 +] + +// Fields for capability.HealthCheck +import groovy.time.TimeCategory + +@Field static final Map HEALTH_CHECK = [ + 'schedule': '0 0 0/1 ? * * *', // Health will be checked using this cron schedule + 'thereshold': '3600' // When checking, mark the device as offline if no Zigbee message was received in the last 3600 seconds +] + +// Fields for capability.ZigbeeGroups +@Field static final Map GROUPS = [ + '9900':'Alfa', '9901':'Bravo', '9902':'Charlie', '9903':'Delta', '9904':'Echo', '9905':'Foxtrot', '9906':'Golf', '9907':'Hotel', '9908':'India', '9909':'Juliett', '990A':'Kilo', '990B':'Lima', '990C':'Mike', '990D':'November', '990E':'Oscar', '990F':'Papa', '9910':'Quebec', '9911':'Romeo', '9912':'Sierra', '9913':'Tango', '9914':'Uniform', '9915':'Victor', '9916':'Whiskey', '9917':'Xray', '9918':'Yankee', '9919':'Zulu' +] + +metadata { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy') { + capability 'Configuration' + capability 'Refresh' + capability 'Actuator' + capability 'Switch' + capability 'ColorControl' + capability 'Light' + capability 'ChangeLevel' + capability 'SwitchLevel' + capability 'HealthCheck' + capability 'PowerSource' + + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E27EU: 2.3.093 (117C-2801-23086631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' // LED1624G9E14EU: 2.3.093 (117C-2803-23093631) + + // Attributes for capability.HealthCheck + attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] + } + + // Commands for capability.Switch + command 'toggle' + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + + // Commands for capability.ColorControl + command 'startColorLoop', [[name:'Speed*', type:'ENUM', constraints: COLOR_LOOP_SPEED.keySet()]] + command 'stopColorLoop' + + // Commands for capability.Brightness + command 'shiftLevel', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] + + // Commands for capability.FirmwareUpdate + command 'updateFirmware' + + preferences { + input( + name: 'helpInfo', type: 'hidden', + title: ''' +
+ IKEA RGB-Only Light v5.0.0
+ +
+ ''' + ) + input( + name: 'logLevel', type: 'enum', + title: 'Log verbosity', + description: 'Select what type of messages appear in the "Logs" section.', + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], + defaultValue: '1', + required: true + ) + + // Inputs for capability.Switch + input( + name: 'powerOnBehavior', + type: 'enum', + title: 'Power On behaviour', + description: 'Select what happens after a power outage.', + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], + defaultValue: 'RESTORE_PREVIOUS_STATE', + required: true + ) + + // Inputs for capability.Brightness + input( + name: 'levelStep', type: 'enum', + title: 'Brightness up/down shift', + description: 'Brightness +/- adjust for the shiftLevel() command.', + options: ['1':'1%', '2':'2%', '5':'5%', '10':'10%', '20':'20%', '25':'25%', '33':'33%', '50':'50%'], + defaultValue: '25', + required: true + ) + input( + name: 'levelChangeRate', type: 'enum', + title: 'Brightness change rate', + description: 'Brightness +/- adjust for the startLevelChange() command.', + options: [ + '10': '10% / sec - from 0% to 100% in 10 seconds', + '20': '20% / sec - from 0% to 100% in 5 seconds', + '33': '33% / sec - from 0% to 100% in 3 seconds', + '50': '50% / secs - from 0% to 100% in 2 seconds', + '100': '100% / sec - from 0% to 100% in 1 seconds', + ], + defaultValue: '20', + required: true + ) + input( + name: 'levelTransitionTime', type: 'enum', + title: 'Brightness transition time', + description: 'Time taken to move to/from the target brightness when device is turned On/Off.', + options: [ + '0': 'Instant', + '5': '0.5 seconds', + '10': '1 second', + '15': '1.5 seconds', + '20': '2 seconds', + '30': '3 seconds', + '40': '4 seconds', + '50': '5 seconds', + '100': '10 seconds' + ], + defaultValue: '5', + required: true + ) + input( + name: 'turnOnBehavior', type: 'enum', + title: 'Turn On behavior', + description: 'Select what happens when the device is turned On.', + options: [ + 'RESTORE_PREVIOUS_LEVEL': 'Restore previous brightness', + 'FIXED_VALUE': 'Always start with the same fixed brightness' + ], + defaultValue: 'RESTORE_PREVIOUS_LEVEL', + required: true + ) + if (turnOnBehavior == 'FIXED_VALUE') { + input( + name: 'onLevelValue', + type: 'number', + title: 'Fixed brightness value', + description: 'Range 1..100', + defaultValue: 50, + range: '1..100', + required: true + ) + } + input( + name: 'prestaging', type: 'bool', + title: 'Pre-staging', + description: 'Set brightness level without turning On the device (for later use).', + defaultValue: false, + required: true + ) + + // Inputs for capability.ZigbeeBindings + input( + name: 'joinGroup', type: 'enum', + title: 'Join a Zigbee group', + description: 'Select a Zigbee group you want to join.', + options: ['0000':'❌ Leave all Zigbee groups', '----':'- - - -'] + GROUPS, + defaultValue: '----', + required: false + ) + } +} + +// =================================================================================================================== +// Implement default methods +// =================================================================================================================== + +// Called when the device is first added +void installed() { + log_warn 'Installing device ...' + log_warn '[IMPORTANT] For battery-powered devices, make sure that you keep your device as close as you can (less than 2inch / 5cm) to your Hubitat hub for at least 30 seconds. Otherwise the device will successfully pair but it won\'t work properly!' +} + +// Called when the "Save Preferences" button is clicked +List updated(boolean auto = false) { + log_info "Saving preferences${auto ? ' (auto)' : ''} ..." + List cmds = [] + + unschedule() + + if (logLevel == null) { + logLevel = '1' + device.updateSetting 'logLevel', [value:logLevel, type:'enum'] + } + if (logLevel == '1') runIn 1800, 'logsOff' + log_info "🛠️ logLevel = ${['1':'Debug', '2':'Info', '3':'Warning', '4':'Error'].get(logLevel)}" + + // Preferences for capability.Switch + if (powerOnBehavior == null) { + powerOnBehavior = 'RESTORE_PREVIOUS_STATE' + device.updateSetting 'powerOnBehavior', [value:powerOnBehavior, type:'enum'] + } + log_info "🛠️ powerOnBehavior = ${powerOnBehavior}" + cmds += zigbee.writeAttribute(0x0006, 0x4003, 0x30, powerOnBehavior == 'TURN_POWER_OFF' ? 0x00 : (powerOnBehavior == 'TURN_POWER_ON' ? 0x01 : 0xFF)) + + // Preferences for capability.ColorControl + cmds += zigbee.writeAttribute(0x0300, 0x000F, 0x18, 0x01) + + // Preferences for capability.Brightness + if (levelStep == null) { + levelStep = '20' + device.updateSetting 'levelStep', [value:levelStep, type:'enum'] + } + log_info "🛠️ levelStep = ${levelStep}%" + + if (levelChangeRate == null) { + levelChangeRate = '20' + device.updateSetting 'levelChangeRate', [value:levelChangeRate, type:'enum'] + } + log_info "🛠️ levelChangeRate = ${levelChangeRate}% / second" + + if (turnOnBehavior == null) { + turnOnBehavior = 'RESTORE_PREVIOUS_LEVEL' + device.updateSetting 'turnOnBehavior', [value:turnOnBehavior, type:'enum'] + } + log_info "🛠️ turnOnBehavior = ${turnOnBehavior}" + if (turnOnBehavior == 'FIXED_VALUE') { + Integer onLevelValue = onLevelValue == null ? 50 : onLevelValue.intValue() + device.updateSetting 'onLevelValue', [value:onLevelValue, type:'number'] + log_info "🛠️ onLevelValue = ${onLevelValue}%" + Integer lvl = onLevelValue * 2.54 + utils_sendZigbeeCommands zigbee.writeAttribute(0x0008, 0x0011, 0x20, lvl) + } else { + log_debug 'Disabling OnLevel (0xFF)' + cmds += zigbee.writeAttribute(0x0008, 0x0011, 0x20, 0xFF) + } + + if (levelTransitionTime == null) { + levelTransitionTime = '5' + device.updateSetting 'levelTransitionTime', [value:levelTransitionTime, type:'enum'] + } + log_info "🛠️ levelTransitionTime = ${Integer.parseInt(levelTransitionTime) / 10} second(s)" + cmds += zigbee.writeAttribute(0x0008, 0x0010, 0x21, Integer.parseInt(levelTransitionTime)) + + if (prestaging == null) { + prestaging = false + device.updateSetting 'prestaging', [value:prestaging, type:'bool'] + } + log_info "🛠️ prestaging = ${prestaging}" + + // If prestaging is true, enable update of brightness without the need for the device to be turned On + cmds += zigbee.writeAttribute(0x0008, 0x000F, 0x18, prestaging ? 0x01 : 0x00) + + // Preferences for capability.HealthCheck + schedule HEALTH_CHECK.schedule, 'healthCheck' + + // Preferences for capability.ZigbeeGroups + if (joinGroup != null && joinGroup != '----') { + if (joinGroup == '0000') { + log_info '🛠️ Leaving all Zigbee groups' + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + } else { + String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') + log_info "🛠️ Joining group: ${joinGroup} (${groupName})" + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group + } + device.updateSetting 'joinGroup', [value:'----', type:'enum'] + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + } + + if (auto) return cmds + utils_sendZigbeeCommands cmds + return [] +} + +// =================================================================================================================== +// Capabilities helpers +// =================================================================================================================== + +// Handler method for scheduled job to disable debug logging +void logsOff() { + log_info '⏲️ Automatically reverting log level to "Info"' + device.updateSetting 'logLevel', [value:'2', type:'enum'] +} + +// Helpers for capability.HealthCheck +void healthCheck() { + log_debug '⏲️ Automatically running health check' + String healthStatus = state.lastRx == 0 || state.lastRx == null ? 'unknown' : (now() - state.lastRx < Integer.parseInt(HEALTH_CHECK.thereshold) * 1000 ? 'online' : 'offline') + utils_sendEvent name:'healthStatus', value:healthStatus, type:'physical', descriptionText:"Health status is ${healthStatus}" +} + +// =================================================================================================================== +// Implement Capabilities +// =================================================================================================================== + +// capability.Configuration +// Note: This method is also called when the device is initially installed +void configure(boolean auto = false) { + log_warn "Configuring device${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Configure" button immediately after pushing any button on the device in order to first wake it up!' + } + + // Apply preferences first + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] + cmds += updated true + + // Clear data (keep firmwareMT information though) + device.data*.key.each { if (it != 'firmwareMT') device.removeDataValue it } + + // Clear state + state.clear() + state.lastTx = 0 + state.lastRx = 0 + state.lastCx = DRIVER_VERSION + + // Configuration for capability.Switch + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes + + // Configuration for capability.ColorControl + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0000 0x20 0x0001 0x0258 {02} {}" // Report CurrentHue (uint8) at least every 10 minutes (Δ = 1%) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x0001 0x20 0x0001 0x0258 {02} {}" // Report CurrentSaturation (uint8) at least every 10 minutes (Δ = 1%) + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0x4000 0x21 0x0002 0xFFFE {CB0C} {}" // Report EnhancedCurrentHue (uint16) at most every 2 seconds (Δ = 5%) + + // Configuration for capability.Brightness + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0008 0x0000 0x20 0x0001 0x0258 {01} {}" // Report CurrentLevel (uint8) at least every 10 minutes (Δ = 1) + + // Configuration for capability.HealthCheck + sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' + sendEvent name:'checkInterval', value:3600, unit:'second', descriptionText:'Health check interval is 3600 seconds' + + // Configuration for capability.PowerSource + sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + + // Query Basic cluster attributes + cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID + cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier + cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" + utils_sendZigbeeCommands cmds + + log_info 'Configuration done; refreshing device current state in 7 seconds ...' + runIn 7, 'refresh', [data:true] +} +/* groovylint-disable-next-line UnusedPrivateMethod */ +private void autoConfigure() { + log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" + configure true +} + +// capability.Refresh +void refresh(boolean auto = false) { + log_warn "Refreshing device state${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Refresh" button immediately after pushing any button on the device in order to first wake it up!' + } + + List cmds = [] + + // Refresh for capability.Switch + cmds += zigbee.readAttribute(0x0006, 0x0000) // OnOff + cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior + + // Refresh for capability.ColorControl + cmds += zigbee.readAttribute(0x0300, [0x0000, 0x0001, 0x0008]) // CurrentHue, CurrentSaturation, ColorMode + + // Refresh for capability.Brightness + cmds += zigbee.readAttribute(0x0008, 0x0000) // CurrentLevel + + // Refresh for capability.ZigbeeGroups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + utils_sendZigbeeCommands cmds +} + +// Implementation for capability.Switch +void on() { + log_debug 'Sending On command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114301}"]) +} +void off() { + log_debug 'Sending Off command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114300}"]) +} + +void toggle() { + log_debug 'Sending Toggle command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114302}"]) +} + +void onWithTimedOff(BigDecimal onTime = 1) { + Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) + log_debug 'Sending OnWithTimedOff command' + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) +} + +// Implementation for capability.ColorControl +void setColor(Map colormap) { + Integer newHue = colormap.hue > 100 ? 100 : (colormap.hue < 0 ? 0 : colormap.hue) + Integer newSaturation = colormap.saturation > 100 ? 100 : (colormap.saturation < 0 ? 0 : colormap.saturation) + Integer newLevel = colormap.level > 100 ? 100 : (colormap.level < 0 ? 0 : colormap.level) + log_debug "Setting color to hue=${newHue}, saturation=${newSaturation}, level=${newLevel}" + newHue = Math.round(newHue * 2.54) + newSaturation = Math.round(newSaturation * 2.54) + String payload = "${utils_payload newHue, 2} ${utils_payload newSaturation, 2} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114306 ${payload}}"]) // Move to Hue and Saturation + /* groovylint-disable-next-line UnnecessarySetter */ + setLevel newLevel +} +void setHue(BigDecimal hue) { + Integer newHue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + log_debug "Setting color hue to ${newHue}%" + newHue = Math.round(newHue * 2.54) + String payload = "${utils_payload newHue, 2} 00 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114300 ${payload}}"]) // Move to Hue +} +void setSaturation(BigDecimal saturation) { + Integer newSaturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) + log_debug "Setting color saturation to ${newSaturation}%" + newSaturation = Math.round(newSaturation * 2.54) + String payload = "${utils_payload newSaturation, 2} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation +} +void startColorLoop(String speed) { + Integer seconds = COLOR_LOOP_SPEED[speed] ?: 30 + log_info "Starting color loop at ${speed} speed (${seconds} sec per loop)" + String payload = "0F 01 01 ${utils_payload seconds, 4} 0000 00 00" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set + state.loop = true +} +void stopColorLoop() { + log_info 'Stopped color loop' + String payload = '0F 00 01 0000 0000 00 00' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set + state.remove 'loop' +} +private void processMultipleColorAttributes(Map msg, String type) { + Map attributes = [:] + attributes[msg.attrInt] = msg.value + msg.additionalAttrs?.each { attributes[Integer.parseInt(it.attrId, 16)] = it.value } + + Integer hue = -1 + Integer saturation = -1 + String colorMode = null + attributes.each { + switch (it.key) { + case 0x0000: + hue = Math.round(Integer.parseInt(it.value, 16) / 2.54) + hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + break + case 0x0001: + saturation = Math.round(Integer.parseInt(it.value, 16) / 2.54) + saturation = saturation > 100 ? 100 : (saturation < 0 ? 0 : saturation) + break + case 0x0008: + case 0x4001: + colorMode = it.value == '02' ? 'CT' : 'RGB' + utils_sendEvent name:'colorMode', value:colorMode, descriptionText:"Color mode is ${colorMode}", type:type + break + case 0x4000: + hue = Math.round(Integer.parseInt(it.value, 16) / 655.34) + hue = hue > 100 ? 100 : (hue < 0 ? 0 : hue) + } + } + + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type, noInfo:(state.loop == true) + if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type + + // Update colorName, if the case + if ("${colorMode ?: device.currentValue('colorMode', true)}" == 'RGB') { + Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) + Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) + String colorName = convertHueToGenericColorName colorHue, colorSaturation + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type, noInfo:(state.loop == true) + } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" +} + +// Implementation for capability.Brightness +void setLevel(BigDecimal level, BigDecimal duration = 0) { + Integer newLevel = level > 100 ? 100 : (level < 0 ? 0 : level) + log_debug "Setting brightness level to ${newLevel}% during ${duration} seconds" + Integer lvl = newLevel * 2.54 + Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min + String command = prestaging == false ? '04' : '00' + String payload = "${utils_payload lvl, 2} ${utils_payload dur, 4}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {1143${command} ${payload}}"]) +} +void startLevelChange(String direction) { + log_debug "Starting brightness level change ${direction}wards with a rate of ${levelChangeRate}% / second" + Integer mode = direction == 'up' ? 0x00 : 0x01 + Integer rate = Integer.parseInt(levelChangeRate) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload rate, 2}" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114301 ${payload}}"]) +} +void stopLevelChange() { + log_debug 'Stopping brightness level change' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114303}"]) +} +void shiftLevel(String direction) { + log_debug "Shifting brightness level ${direction} by ${levelStep}%" + Integer mode = direction == 'up' ? 0x00 : 0x01 + Integer stepSize = Integer.parseInt(levelStep) * 2.54 + String payload = "${utils_payload mode, 2} ${utils_payload stepSize, 2} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0008 {114302 ${payload}}"]) +} + +// Implementation for capability.HealthCheck +void ping() { + log_warn 'ping ...' + utils_sendZigbeeCommands(zigbee.readAttribute(0x0000, 0x0000)) + log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' + runIn 5, 'pingExecute' +} +void pingExecute() { + if (state.lastRx == 0) { + log_info 'Did not sent any messages since it was last configured' + return + } + + Date now = new Date(Math.round(now() / 1000) * 1000) + Date lastRx = new Date(Math.round(state.lastRx / 1000) * 1000) + String lastRxAgo = TimeCategory.minus(now, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Sent last message at ${lastRx.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${lastRxAgo} ago)" + + Date thereshold = new Date(Math.round(state.lastRx / 1000 + Integer.parseInt(HEALTH_CHECK.thereshold)) * 1000) + String theresholdAgo = TimeCategory.minus(thereshold, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received for ${theresholdAgo} (hardcoded)" + + String offlineMarkAgo = TimeCategory.minus(thereshold, now).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received until ${thereshold.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${offlineMarkAgo} from now)" +} + +// Implementation for capability.FirmwareUpdate +void updateFirmware() { + log_info 'Looking for firmware updates ...' + if (device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Update Firmware" button immediately after pushing any button on the device in order to first wake it up!' + } + utils_sendZigbeeCommands zigbee.updateFirmware() +} + +// =================================================================================================================== +// Handle incoming Zigbee messages +// =================================================================================================================== + +void parse(String description) { + log_debug "description=[${description}]" + + // Auto-Configure device: configure() was not called for this driver version + if (state.lastCx != DRIVER_VERSION) { + state.lastCx = DRIVER_VERSION + runInMillis 1500, 'autoConfigure' + } + + // Extract msg + Map msg = [:] + if (description.startsWith('zone status')) msg += [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] + if (description.startsWith('enroll request')) msg += [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] + + msg += zigbee.parseDescriptionAsMap description + if (msg.containsKey('endpoint')) msg.endpointInt = Integer.parseInt(msg.endpoint, 16) + if (msg.containsKey('sourceEndpoint')) msg.endpointInt = Integer.parseInt(msg.sourceEndpoint, 16) + if (msg.containsKey('cluster')) msg.clusterInt = Integer.parseInt(msg.cluster, 16) + if (msg.containsKey('command')) msg.commandInt = Integer.parseInt(msg.command, 16) + log_debug "msg=[${msg}]" + + state.lastRx = now() + + // Parse for capability.HealthCheck + if (device.currentValue('healthStatus', true) != 'online') { + utils_sendEvent name:'healthStatus', value:'online', type:'digital', descriptionText:'Health status changed to online' + } + + // If we sent a Zigbee command in the last 3 seconds, we assume that this Zigbee event is a consequence of this driver doing something + // Therefore, we mark this event as "digital" + String type = state.containsKey('lastTx') && (now() - state.lastTx < 3000) ? 'digital' : 'physical' + + switch (msg) { + + // Events for capability.Switch + // =================================================================================================================== + + // Report/Read Attributes: OnOff + case { contains it, [clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String newState = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'switch', value:newState, descriptionText:"Was turned ${newState}", type:type + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "OnOff=${newState}" + return + + // Read Attributes Response: powerOnBehavior + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x4003] }: + String newValue = '' + switch (Integer.parseInt(msg.value, 16)) { + case 0x00: newValue = 'TURN_POWER_OFF'; break + case 0x01: newValue = 'TURN_POWER_ON'; break + case 0xFF: newValue = 'RESTORE_PREVIOUS_STATE'; break + default: log_warn "Received unexpected attribute value: PowerOnBehavior=${msg.value}"; return + } + powerOnBehavior = newValue + device.updateSetting 'powerOnBehavior', [value:newValue, type:'enum'] + utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" + return + + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" + return + case { contains it, [clusterInt:0x0006, commandInt:0x04] }: // Write Attribute Response + case { contains it, [clusterInt:0x0006, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command + return + + // Events for capability.ColorControl + // =================================================================================================================== + + // Report/Read Attributes Reponse: CurrentHue + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0000] }: + + // Report/Read Attributes Reponse: CurrentSaturation + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0001] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0001] }: + + // Report/Read Attributes Reponse: ColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0008] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x0008] }: + + // Report/Read Attributes Reponse: EnhancedColorMode + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4001] }: + case { contains it, [clusterInt:0x0300, commandInt:0x01, attrInt:0x4001] }: + + // Report Attributes Reponse: EnhancedCurrentHue + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x4000] }: + processMultipleColorAttributes msg, type + return + + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0300, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "data=${msg.data}" + return + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0003] }: // Report Attribute Current X + case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0004] }: // Report Attribute Current Y + return + + // Events for capability.Brightness + // =================================================================================================================== + + // Report/Read Attributes Reponse: CurrentLevel + case { contains it, [clusterInt:0x0008, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0008, commandInt:0x01, attrInt:0x0000] }: + Integer level = msg.value == '00' ? 0 : Math.ceil(Integer.parseInt(msg.value, 16) / 2.54) + utils_sendEvent name:'level', value:level, descriptionText:"Brightness is ${level}%", type:'digital' + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentLevel=${msg.value} (${level}%)" + return + + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0008, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=CurrentLevel, data=${msg.data}" + return + case { contains it, [clusterInt:0x0008, commandInt:0x04] }: // Write Attribute Response (0x04) + return + + // Events for capability.HealthCheck + // =================================================================================================================== + + case { contains it, [clusterInt:0x0000, attrInt:0x0000] }: + log_warn '... pong' + return + + // Configuration for capability.PowerSource + // =================================================================================================================== + + // Read Attributes Reponse: PowerSource + case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: + String powerSource = 'unknown' + + // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } + switch (msg.value) { + case ['01', '02', '05', '06']: + powerSource = 'mains'; break + case '03': + powerSource = 'battery'; break + case '04': + powerSource = 'dc' + } + utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" + utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" + return + + // Events for capability.ZigbeeGroups + // =================================================================================================================== + + // Get Group Membership Response Command + case { contains it, [clusterInt:0x0004, commandInt:0x02, direction:'01'] }: + Integer count = Integer.parseInt msg.data[1], 16 + Set groupNames = [] + for (int pos = 0; pos < count; pos++) { + String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" + log_debug "Found group membership: ${groupName}" + groupNames.add groupName + } + state.joinGrp = groupNames + if (state.joinGrp.size() == 0) state.remove 'joinGrp' + log_info "Current group membership: ${groupNames ?: 'None'}" + return + + // Add Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" + utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // Leave Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" + utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Handle common messages (e.g.: received during pairing when we query the device for information) + // --------------------------------------------------------------------------------------------------------------- + + // Device_annce: Welcome back! let's sync state. + case { contains it, [endpointInt:0x00, clusterInt:0x0013, commandInt:0x00] }: + log_warn 'Rejoined the Zigbee mesh; refreshing device state in 3 seconds ...' + runIn 3, 'refresh' + return + + // Report/Read Attributes Response (Basic cluster) + case { contains it, [clusterInt:0x0000, commandInt:0x01] }: + case { contains it, [clusterInt:0x0000, commandInt:0x0A] }: + utils_zigbeeDataValue(msg.attrInt, msg.value) + msg.additionalAttrs?.each { utils_zigbeeDataValue(it.attrInt, it.value) } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "cluster=0x${msg.cluster}, attribute=0x${msg.attrId}, value=${msg.value}" + return + + // Mgmt_Leave_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8034, commandInt:0x00] }: + log_warn 'Device is leaving the Zigbee mesh. See you later, Aligator!' + return + + // Ignore the following Zigbee messages + case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) + case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response + case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8004, commandInt:0x00] }: // ZDP: Simple_Desc_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8005, commandInt:0x00] }: // ZDP: Active_EP_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x0006, commandInt:0x00] }: // ZDP: MatchDescriptorRequest + case { contains it, [endpointInt:0x00, clusterInt:0x801F, commandInt:0x00] }: // ZDP: Parent_annce_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8021, commandInt:0x00] }: // ZDP: Mgmt_Bind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8022, commandInt:0x00] }: // ZDP: Mgmt_Unbind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Unexpected Zigbee message + // --------------------------------------------------------------------------------------------------------------- + default: + log_error "Sent unexpected Zigbee message: description=${description}, msg=${msg}" + } +} + +// =================================================================================================================== +// Logging helpers (something like this should be part of the SDK and not implemented by each driver) +// =================================================================================================================== + +private void log_debug(String message) { + if (logLevel == '1') log.debug "${device.displayName} ${message.uncapitalize()}" +} +private void log_info(String message) { + if (logLevel <= '2') log.info "${device.displayName} ${message.uncapitalize()}" +} +private void log_warn(String message) { + if (logLevel <= '3') log.warn "${device.displayName} ${message.uncapitalize()}" +} +private void log_error(String message) { + log.error "${device.displayName} ${message.uncapitalize()}" +} + +// =================================================================================================================== +// Helper methods (keep them simple, keep them dumb) +// =================================================================================================================== + +private void utils_sendZigbeeCommands(List cmds) { + if (cmds.empty) return + List send = delayBetween(cmds.findAll { !it.startsWith('delay') }, 1000) + log_debug "◀ Sending Zigbee messages: ${send}" + state.lastTx = now() + sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) +} +private void utils_sendEvent(Map event) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { + log_info "${event.descriptionText} [${event.type}]" + } else { + log_debug "${event.descriptionText} [${event.type}]" + } + sendEvent event +} +private void utils_dataValue(String key, String value) { + if (value == null || value == '') return + log_debug "Update data value: ${key}=${value}" + updateDataValue key, value +} +private void utils_zigbeeDataValue(Integer attrInt, String value) { + switch (attrInt) { + case 0x0001: utils_dataValue 'application', value; return + case 0x0003: utils_dataValue 'hwVersion', value; return + case 0x0004: utils_dataValue 'manufacturer', value; return + case 0x000A: utils_dataValue 'type', "${value ? (value.split('') as List).collate(2).collect { "${Integer.parseInt(it.join(), 16) as char}" }.join() : ''}"; return + case 0x0005: utils_dataValue 'model', value; return + case 0x4000: utils_dataValue 'softwareBuild', value; return + } +} +private void utils_processedZclMessage(String type, String details) { + log_debug "▶ Processed ZCL message: type=${type}, ${details}" +} +private void utils_processedZdpMessage(String type, String details) { + log_debug "▶ Processed ZDO message: type=${type}, ${details}" +} +private String utils_payload(String value) { + return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') +} +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} + +// switch/case syntactic sugar +@CompileStatic private boolean contains(Map msg, Map spec) { + return msg.keySet().containsAll(spec.keySet()) && spec.every { it.value == msg[it.key] } +} diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index 3a6a94c..f916a60 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -454,7 +454,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) - if (level > 0 && duration == 0) setLevel level, duration + if (level > 0 && duration == 0) setLevel level } void startColorTemperatureChange(String direction) { log_debug "Starting color temperature change ${direction}wards with a rate of ${colorTemperatureChangeRate}% / second" diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 72a9a34..273f1ca 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -3,6 +3,12 @@ This document provides instructions on how to install and use custom drivers for These drivers enable advanced features and functionalities for the following devices: +Lights: +* [Dimmable Light](#dimmable-light) +* [White Spectrum Light](#white-spectrum-light) +* [Color White Spectrum Light](#color-white-spectrum-light) +* [RGB-Only Light](#rgb-only-light) + Remotes: * [Rodret Dimmer (E2201)](#rodret-dimmer-e2201) * [Somrig Shortcut Button (E2213)](#somrig-shortcut-button-e2213) @@ -25,11 +31,6 @@ Outlets: * [Tradfri Control Outlet (E1603, E1706)](#tradfri-control-outlet-e1603-e1706) * [Tretakt Smart Plug (E2204)](#tretakt-smart-plug-e2204) -Lights: -* [Dimmable Light](#dimmable-light) -* [White Spectrum Light](#white-spectrum-light) -* [Color White Spectrum Light](#color-white-spectrum-light) - Appliances: * [Starkvind Air Purifier (E2006)](#starkvind-air-purifier-e2006) @@ -38,6 +39,7 @@ Devices from other vendors (not in HPM): * [Legrand Connected Outlet (741811)](#legrand-connected-outlet-741811) * [Philips Hue Wall Switch Module (RDM001)](#philips-hue-wall-switch-module-rdm001) * [Philips Hue Dimmer Switch (RWL022)](#philips-hue-dimmer-switch-rwl022) +* [Schneider Wiser UFH (CCTFR6600)](#schneider-wiser-ufh-cctfr6600) * [Swann One Key Fob (SWO-KEF1PA)](#swann-one-key-fob-swo-kef1pa) Advanced features: @@ -69,6 +71,167 @@ If you don"t want to use HPM, you can also install the drivers manually by impor More info about installing custom drivers is available in the [Official Documentation](https://docs2.hubitat.com/en/how-to/install-custom-drivers). +## Lights +Below you can find the details of each lighting device, including the features and pairing instructions. + +### Dimmable Light + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_DIM-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. +* **Health Status**: Indicates whether the device is online or offline. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Tested devices +| Type | Name | +|--------------|---------------------------------| +| LED2103G5 | Tradfri Bulb E27 WW Globe 806lm | +| LED2104R3 | Tradfri Bulb GU10 WW 345lm | +| LED1623G12 | Tradfri Bulb E27 Opal 1000lm | +| 10EU-IL-1 | Tradfri LED Driver 10W | +| LED1842G3 | Tradfri Bulb E27 WW Clear 250lm | +| LED1836G9 | Tradfri Bulb E27 WW 806lm | +| 30-IL44-1 | Silverglans LED Driver 30W | +| 30EU-IL-2 | Tradfri LED Driver 30W | + +#### Pairing Instructions +1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Put the light in pairing mode ([see video](https://www.youtube.com/watch?v=npxOrPxVfe0)). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Dimmable Light. + + +### White Spectrum Light + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_WS-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Color Temperature (CT) Control**: Enables setting of color temperature, starting/stopping color temperature change, and stepping color temperature up/down. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level and color temperature when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature. +* **Health Status**: Indicates whether the device is online or offline. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Known Issues +* **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. + +#### Tested devices + +| Type | Name | +|--------------|---------------------------------------| +| LED2106R3 | Tradfri Bulb GU10 WS 345lm | +| LED2101G4 | Tradfri Bulb E14 WS Globe 470lm | +| LED1949C5 | Tradfri Bulb E14 WS Candle Opal 470lm | +| LED2002G5 | Tradfri Bulb E14 WS Globe Opal 470lm | +| LED2005R5 | Tradfri Bulb GU10 WS 345lm | +| LED2201G8 | Tradfri Bulb E27 WS Globe 1055lm | + +#### Pairing Instructions +1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Put the light in pairing mode ([see video](https://www.youtube.com/watch?v=npxOrPxVfe0)). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your White Spectrum Light. + + +### Color White Spectrum Light + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_CWS-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Color Temperature (CT) Control**: Enables setting of color temperature, starting/stopping color temperature change, and stepping color temperature up/down. +* **Color (RGB) Control**: Provides options to set color hue and/or saturation, and display color name and color mode. +* **Color Loop**: Initiates cycling of the color hue until stopped. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level, color temperature, and color when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature/color. +* **Health Status**: Indicates whether the device is online or offline. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Known Issues +* **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. + +#### Tested devices + +| Type | Name | +|----------------|-----------------------------------| +| LED1924G9 | Tradfri Bulb E27 CWS 806lm | +| LED1923R5 | Tradfri Bulb GU10 CWS 345lm | +| LED1925G6 | Tradfri Bulb E14 CWS 470lm | +| L2112 | Ormanas LED Strip | +| LED2111G6 | Tradfri Bulb E14 CWS Globe 806lm | + +#### Pairing Instructions +1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Put the light in pairing mode ([see video](https://www.youtube.com/watch?v=npxOrPxVfe0)). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Color White Spectrum Light. + + +### RGB-Only Light + +Bulbs of this type lack native support for color temperature control. Their hardware is exclusively equipped with red, green, and blue LEDs, and does not include white light LEDs. As a result, they are unable to natively produce variations in light temperature. + +| Parameter | Details | +|-----------|-------------| +| Icon | | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy` | +| Since version | `5.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. +* **Color (RGB) Control**: Provides options to set color hue and/or saturation, and display color name and color mode. +* **Color Loop**: Initiates cycling of the color hue until stopped. +* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). +* **Pre-staging**: Allows setting of brightness level and color when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/color. +* **Health Status**: Indicates whether the device is online or offline. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Tested devices + +| Type | Name | +|----------------|----------------------------------| +| LED1624G9E27EU | Tradfri Bulb E27 CWS Opal 600lm | +| LED1624G9E14EU | Tradfri Bulb E14 CWS Opal 600lm | + +#### Pairing Instructions +1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Put the light in pairing mode ([see video](https://www.youtube.com/watch?v=npxOrPxVfe0)). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your RGB-Only Light. + + ## Remotes Below you can find the details of each remote device, including the features and pairing instructions. @@ -522,124 +685,6 @@ Below you can find the details of each outlet device, including the features and 1. You're all set! Enjoy using your Tretakt Smart Plug. -## Lights -Below you can find the details of each lighting device, including the features and pairing instructions. - -### Dimmable Light - -| Parameter | Details | -|-----------|-------------| -| Icon | | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_DIM-Light.groovy` | -| Since version | `5.0.0` | - -#### Features -* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. -* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). -* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. -* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). -* **Pre-staging**: Allows setting of brightness level when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level. -* **Health Status**: Indicates whether the device is online or offline. -* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. -* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. - -#### Tested devices -| Type | Name | -|--------------|---------------------------------| -| LED2103G5 | Tradfri Bulb E27 WW Globe 806lm | -| LED2104R3 | Tradfri Bulb GU10 WW 345lm | -| LED1623G12 | Tradfri Bulb E27 Opal 1000lm | -| 10EU-IL-1 | Tradfri LED Driver 10W | -| LED1842G3 | Tradfri Bulb E27 WW Clear 250lm | -| LED1836G9 | Tradfri Bulb E27 WW 806lm | -| 30-IL44-1 | Silverglans LED Driver 30W | -| 30EU-IL-2 | Tradfri LED Driver 30W | - -### White Spectrum Light - -| Parameter | Details | -|-----------|-------------| -| Icon | | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_WS-Light.groovy` | -| Since version | `5.0.0` | - -#### Features -* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. -* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). -* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. -* **Color Temperature (CT) Control**: Enables setting of color temperature, starting/stopping color temperature change, and stepping color temperature up/down. -* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). -* **Pre-staging**: Allows setting of brightness level and color temperature when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature. -* **Health Status**: Indicates whether the device is online or offline. -* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. -* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. - -#### Known Issues -* **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. - -#### Tested devices - -| Type | Name | -|--------------|---------------------------------------| -| LED2106R3 | Tradfri Bulb GU10 WS 345lm | -| LED2101G4 | Tradfri Bulb E14 WS Globe 470lm | -| LED1949C5 | Tradfri Bulb E14 WS Candle Opal 470lm | -| LED2002G5 | Tradfri Bulb E14 WS Globe Opal 470lm | -| LED2005R5 | Tradfri Bulb GU10 WS 345lm | -| LED2201G8 | Tradfri Bulb E27 WS Globe 1055lm | - -#### Pairing Instructions -1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Put the light in pairing mode. -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. You're all set! Enjoy using your White Spectrum Light. - - -### Color White Spectrum Light - -| Parameter | Details | -|-----------|-------------| -| Icon | | -| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_CWS-Light.groovy` | -| Since version | `5.0.0` | - -#### Features -* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. -* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). -* **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. -* **Color Temperature (CT) Control**: Enables setting of color temperature, starting/stopping color temperature change, and stepping color temperature up/down. -* **Color (RGB) Control**: Provides options to set color hue and/or saturation, and display color name and color mode. -* **Color Loop**: Initiates cycling of the color hue until stopped. -* **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). -* **Pre-staging**: Allows setting of brightness level, color temperature, and color when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/temperature/color. -* **Health Status**: Indicates whether the device is online or offline. -* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. -* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. - -#### Known Issues -* **Color Temperature and Brightness Level**: These can be set together if the "Transition time" is 0 (or left blank in the UI); if a transition time is specified, only the color temperature is applied. - -#### Tested devices - -| Type | Name | Notes | -|----------------|---------------------------------------|--------------------------| -| LED1624G9E27EU | Tradfri Bulb E27 CWS Opal 600lm | Color Temp does not work | -| LED1924G9 | Tradfri Bulb E27 CWS 806lm | -- | -| LED1923R5 | Tradfri Bulb GU10 CWS 345lm | -- | -| LED1925G6 | Tradfri Bulb E14 CWS 470lm | -- | -| LED1624G9E14EU | Tradfri Bulb E14 CWS Opal 600lm | Color Temp does not work | -| L2112 | Ormanas LED Strip | -- | -| LED2111G6 | Tradfri Bulb E14 CWS Globe 806lm | -- | - -#### Pairing Instructions -1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. -1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. -1. Put the light in pairing mode. -1. Return to the pairing page, provide a name for your device, and assign it to a room. -1. You're all set! Enjoy using your White Spectrum Light. - - ## Appliances Below you can find the details of each appliance device, including the features and pairing instructions. @@ -787,6 +832,29 @@ Below you can find the details of each device, including the features and pairin 1. You're all set! Enjoy using your Philips Hue Dimmer Switch. +### Schneider Wiser UFH (CCTFR6600) + +This device is extremely chatty as it polls the Hubitat hub for the status of each of the 6 zones every 1 minute. If no reponse is received from the hub for 10 minutes (10 failed polls), the device enters a "lost-connection" mode and turns all 6 zones to "On". + +**Warning**: The driver code for this device is still a work-in-progess. + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Link | [Wiser Underfloor Heating Connection Strip](https://www.productinfo.schneider-electric.com/wiser_home/viewer?docidentity=WiserUnderfloorHeatingConnectionStr-EE42C909&lang=en) | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Schneider_CCTFR6600.groovy` | +| Since version | `5.0.0` | + +#### Features +* **Command Controls**: Includes "On" and "Off" for each of the 6 zones. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". + +#### Pairing Instructions +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Press the connect button for at least 5 seconds; upon release, the LED light on the first button will start blinking green. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Schneider Wiser UFH. + ### Swann One Key Fob (SWO-KEF1PA) | Parameter | Details | diff --git a/ikea-zigbee-drivers/img/Ikea_RGBO-Light.webp b/ikea-zigbee-drivers/img/Ikea_RGBO-Light.webp new file mode 100644 index 0000000000000000000000000000000000000000..da4cef86020e4c4944cb8818f0bdadacaca16c32 GIT binary patch literal 8008 zcmZvAb95a*^Y*>5ZQHip*p1N`H?|r(X>?=Twr!_jn>23J*lBowec$(d|9x|I|C!mH zo!Nc%Ideu+K~`3R901V$B&DvSE}(}5001!l%{$NyC7wTi1{s6OTkZ>S73X;fLCN&(rfG6@C2k zlP>4g-D6FX$N zMX!)YK~%jcOCoP_SJ@o>b0w8>Nj98Sl714;6tsL%4n_QsDGD4sn9d@YfdsSIRXL19 zk7d~M0gy!xrIlaxER?cGten8AV!e>wMP+!Hon6yJSU+@>xn%jIoW;(8+ds7e;8BBb z6oDRWDn&fe=aRByL`W%#K|##f}Iv|Pq8_?Qwxo!p)~e8 z^M%ZjR5wBom+AqVW&R{P4dI^9EqGr_aH%)Jk1n7gZ^}p(-<;hdcglENBxNAhqtXTU zS^b7-y68xI<@29!w&>Yi(Sb~_1aa_l#-k!>8Aqkh_VN=*3lf1V`_k&AR-i+@JHpDP z*j7I_RuzS1HHj3RGG36j0k&l+2qc@GZjDw^mW5uxRwwFdZ18ohmFI5dfBJvsBWLZT z55xa%cnFhKSYBG$`PRx*Cj!#gNX2tLNX&9U{bMn{qrQ~PyY!C_w8rA%TnoQ;Zsicr*UcPtB0uBO?#WAr&H(tRYLa<& z23P5D;auZC^j!Zg#jh-1zAhbltc-{;rCIb8*qFYY1ee6fX-Cg2$Dm68cymr?bCMjHPb)_aPJH5Erj^wLw zDUJ-N8hdk=kN1s6vi-yB&A?Deo_6-oDy6j};!Ncdn}z`}YhdC9V;?`ohU$z*M0l}^OAgxpW_cn(9m^=wLS4Ro3_V`V%(n?CbiGNI+*mU5LT zJgE(sGaPTk7(JkPXAIn0J}!q;pBoq(2NxfZ2i2s-<#p9sT&c!$8ZI&vfJV&{4MnP< zIaSP41g5)=`v#jbsY^NuI^UGZ>84HDaATYiqeAXI*G+I08NjgBDMt6gyCN#`%> zdDp#U0A^DCd;B&THQjTx?@AEMxlE5E1{H;@rFQn>Y4J=+($~Rib{c*rPkVxwEF*i4 zQ$`iav)1;kDfjZNB0qi|>~?A_h4_xJ*svPCfupOf7k`sKL$yms)p1^qsb=9kG#k2g zQkprq#v zIG13bQoi2@`##jnxK6AqdG8SW@eiygv$JS6zaT1mr{IY)mK8d})$lwL`eFIvL0!xA z5W^hLZ~Z{oe<_`rtJ)ffRf^xW&FMN4LCY|GD&sOPlIL7|4@K&L6MEKO?$60OD!PrT;tFrFVj_|m6J zGTikdLSKrX@)n?kj4Fbn5~Qv$rpLh7OlOXsemLprJY04HulsJso^2J-P8lz8up-?U z5?(=V5*EQ4QgR=j^LG{t=iIyCgci;zQ;zU^AqEZU>hfRdGW<1EgRuE(DODsjuuU}c zRyIHJ=OcqYTQLqs-h!r2^%3wo$kwd)OD+eHkL6Acm6gnYANO(>=VZzj50edMnIxQB zZ~7nRMFS5rWswQMxQ`fw>TCHowW0>&Vr5ZFh2J{UcPZ*LhR+X?KkH5G2xCT83DiQi z=9`!75P6@o?`Ku*sNcoygwA^P~kHP0KJn+Bd=p8jM0 zFXME)UNFIoCCT0D+Vo3?*~eP^Cz$`SQBpbnVD$fwoN@txk4v(DRUH5Tr@f`s5~w~d zs`eiD{CP9Y&!~51Y_-^_&-=K*yEpopR{>`qFl=2`_mZ}l$45cT!rA$<>*Yjxr|&SX z9z?~$7fMxlYC`SiG+c&hIi*uuuC?fxR*kOrCC-n4QcTMu4k&tbK~Av^BbM1VoNc#G z(rIS*h?4m7p&c0ZmrT}npMyi>tKAVT69#@j%WRzQCbWAbbuiZP`O-Ari`=hOX|*3w z-;7KOa^A&UbX`VvFdnDbAC6WXA9~J(gmCzt1dgdU^=A%w{rxa#v3lt^y=5^weJFQ1 zpqjg-0gG-_3oWHuo5wVk)FifB=)AWZz_%kdUS=<|`IkX5n*_e&^SMh0(o!io2hU$n zt3UKQo$hikF^a#@O9wK1-d61$1c*q5`gDY^r)}L#bW`BbVQQXUTIWzIWJ5Iuv_#V0 zIX);g8HB7I246SYhrCk&?e$ZRO*;=#W~I{jC&e8DPIk3KK|H4GeHob@3{3PTl z@S-#UF7$=upThD~>*Mfi^RC(ehDjHg$q$Z(u!$4#2@?L$Uvqw(?{e?CIn0+`dVLTI zKV8{eLY#K`-i_%H#C(<&G%ud1X3n=3P%+5dH6byTB!}!xJq2M}rb{zV^X2LJp`%S4 zJN5ZpQJjPrALMn_G@(3X#IN#?AzKz(DTqP8&P4&=&get}%$Q3Q@G~%zOzoA*T*JYG}jz<3=0wS*IdiASy z&M=>+jLTA>eNDqXw9f4`Sh?|lon&QN@`Ize!N+mBS>tT*? za)MH^>H>CZc%r&IUz|J#zwM;|jU-R@la*gb9|}Y@_2%750LU zdnD*Xz}i#uLKHRVz0S;YV>fBm4w>JgoW!l9)Ac_sX;M``6Nzbd%XYTt3=ylcod`CP z5un#a279DtZ%H2dlzbJ(78cx{QGl;1It10MpACL)T@$;6i>3l0CK6v6!{H`ea1u^C z1jiv8{v|U!bk{tKu<+=e9#<)FJ;%fN^Q5H}6=zU=Z7f$Uv+a2$`w1NR_~b}d=;j;~ zo#8lvi;c7C#?!d|OG@p0?W_-+i9nW6$)(7?+k3J>TP@MWC{rYS)X!>$Hb>2w|HNnk zFSsc(e9FDO#e(l~1xGUaMWG}qcW;!LS^++g$M-nS{(gVTMVT6Yh}q>$?Dwm*F5E(z zSfKJ5Cc*qw+75=!A#UteGAFO3wABS@`7Q@%ZT za5SbqOQeGDI8F88YX@`jI+SjgF?AY||D&tS0&GG1_rL4ugOu<|2df8uGZD5w-9=<^ zMXDw3eK^?g7LaVkgF;)T`IKl^LT1$ZmJjzkfw}&o(Nx}g?}|uRNnLd#UzXH)1vS%W z;!NR4u~~G7LJ>C;v4f-U>!r5|&^KY^t@X%0wu4_u3`Y3S#i?qklP(Mt9AhN*_Fi>p z7X~S1l8wId-P9ql-TN#?p(i03dwJ;({xy)G((VuEKHZTbd~i;+Tw(s!EYl6&m>P7r z(lA-s7E)PxV7N`&Hs%57@lxHz)*?MO6?6*45WE};-;t@`=AOifJXHKnuNild(RFCU zh9X5D)>QTL0cSbw&t}1UD6Rsl3e%(~{|nP>w1O(TPQKnrT=ef`MM+eP4h4 zbVZrP!}&%b`}BlstyOiTCLyyWMqU_?%G#m$NG?%;yw~b5D~Y$^^9*q*#%*C4H^aenima`T(&*=&PT zNt~U!1xw@8VUDpjGx35W)~#}TsrYVzLc=M@CBj@W>(J{<#dG*zv%d6@moxr)^C%3b zF|BO+NG#bY-pE9igWgh}nWkbJFkvy1R9Iw|apl?ID!=;jOJ&GGbQ7KniOLOISX!|Z z3#HUh9w{%2ne=fU`WeK8t@`(N<{yj|79>!;xI^yQ`jz(Y{k66mf~W5Zur`fxe#}^w z(Y82FHSz7jpZ^RnAG5udi{z)hk~GXvO$?1M6sjR%vNz3D)Z+h?7f>*=Pwp6J71z01 zp+c1u!EUDv0psL=whmpzi%><;FwtRCX2+qQQhzOG^(0{$)-k$vxQPz5I_T*Sbz}Zw z^BVV-S9_RwWoi|Np-g^bta&%-Ws4}ag^fsz&hSjwrBOj27+)_q0Ok$4HgQL))W-3@ zM$EpJF9nv8vmk1odD|;4w8XsN=QY4Lf8D?|eZ3dv$_1dHYzcV)igowtDk+Dzo5zRK zK3)v&0)AR5jWfe%%WJ%CL09bqMKMyrxeK2@jti5&_L;HMdYrs zlpPS*iXEf(7hU`*P|IR3Q1VV@TUl%fMv&&@s?A=ZigVdmV{h2$hg$p5vQp;&lM{)P zEbRf8BGqzb6C+E^dEjyXJ+pb}X_ea3z?r<9@at6nZT1~;qw~OaUV#|Uz-oyjfQV~< z;hVVmai~Ba{d|9RZ%WiOm6YX{WTPBL2fPT8E>AaQxCRR0!qV=orSyU;g)Tr?Vi?0H zvQSagz*40IeuIfl6##czq9UANqgmq#Rn1jj&Gw=_=8V6a5NniNC3De`umT#7+9lyZgX4T6h2Ta3#S2XDVoZAj>V>7*b=0KV`gMQC zcz<+}aSA;+cL?{~i!Hr0dFkKhx(kGrm=b3bRkIj6n9KfpiF#1*d@S=>QB6C&0x1rV zORX!Zm1lmXT8h_3fhm060!c4|BiTo=@E!rLYjhTxqNrV ze^(~gsOybjOjv(`B3=z{?pU_H`^h3fM+Lzilu+6#5+=o;@X}_jRAb{!E{idyI1yf?a5sH(km4PA#rTkK&Uq2+D=KgyV8WUIMq8CYI!}1WypSv!t zD>1|h`yiL;cS#rxhy%q^?*)>tlP)5KmQ}Sf5FXj?;LDM1*=&a@SXsqS-OKdM(a_JN zqYQ>O_=$sPD78k2~R3lz(Rnz%D1T*d%@r0Pqh;q0{kJTy1d(?76&RbC|gj>Q_ zQGcvxS)3pLLLwF<+b-VJzw8UG=!&g=lIh$;y%0pod}*+F$e=1ATw>CQSUc= zqN93uLj?sp5Km|57^?gKc`N9jOgXMZN37$Lcr&PPol{C+Po8j*%R?ETYXc?RN zi51&2ul<%PGr*|}ymGo1bgHa+E|HgGa?$xFWF87 zSzRr6dR2g}xvpO6STA0Dj)qaB5vfBZjdjFWWs7>|Wg4H+HGKJIe2G&{=s#xK6=sxj z3+pDJei&`KA@+)%5cZ*<#*a1zj-p?sV!mC6h&k8lB#PxtY*z0S#kiqkLK+fk9(F>w ztr^~L^uR@4^&@*{ZvJpv2SMUE)82Na9Za1!%#*P~~B z3rUE2NcZ({lqOyCM%(A3W`+G8zag%-(FH@neS!nLU`$5&YN1yam=xn5TDHU(y{RuV zow)NN>)W26zB4+BbqfET{xv<7xa-DC(9Bh|PY7bQ=91FyshoHAfnZW zn2A#LXi{&I=#SU=KSAX*-t`JC5!qnlrezC;p|34H-~Tq2bXQlq1)AI4C@U4_!@rt>bUic9*5I(qU1Lv;}}`T;}&dPa{8J7p7^K;%Q>~fB2|t&3nr8qLKB4$Cc!_ zy#9JnU5P#mz8&YIGIvr6@iqG#L})h~5fHz2AfO|{f-w=RT5FOb(%M+AYY2Z;Ia|aW z8$}u{JxRfpFgmjl?Iu8RID7{lZB)1lqO&%ghQ2FQHqj#8B%8xx1e+k7Lw>_2t2_rN z2u8ka<&Qj~W60^DSDyj%a~Vva({UJmbKk$*3S+F2&jpZ&gc7%`qPVZTDKrP4$shSl zqVPS+o#QMei!oP`%y*}=VHIqZn*n~_20`KLB3U?w+!KpQYR5tC-_U8x<|D`>31Lrh zQg;Z_OsKt`vogrg{De@ap9Iy#-7bW9vq(?#Nt5+8@He9He%{hbafn3qsB#aclT-UM z+@>hseZ;%?8%gejZ|jm1bxr<(3V4>`pYK%{B?0r4lbGpJpGOmjvxlCcm;D!wU&?xy zcf&FT^0W%MC(jpHIItqG3#nCMa~godx zgGbY|sl{whls2q@ux%+iuV@G3EbSUyqIIM8{CAi3gY|f5{EvI>D_am`Ln8^y>DxFu ztFm^YQ7D2BwL{`HtI0CCU`#}_*bngQvvfuOIzc~J zVW_e1DP-sgQKmLgaG%1V_xCg@|GQ0JsjNUrUMS_No(&jd}fBiUVM}Kg8f5zx8ke@kDpj)pM(Mu2X z5mH$eBPR7r#2{-lpa;hYy0`g%tHZvul-`ex?4u*ii{pD(ml*7DF-Rv4GS6A#^=AY~ zKpFptd>^R`2Sa1mpOuXI)E*>!DJ|!-RAo)M2C6qE(eq+CL9Rx%Mjf)-WC`mBUiOn{ zVFad!sY0?plmkwaGBP+gffzeq@nVpUgDR-dLR>6hb!f~v%fI|CHB`~3`S$3`&6_)P ziZKY%yAdGczkrPSE<_fHXR*0=!F+n9R%Gqd?oK^N;dJo#5VokgJRDrRjw~Pyh2?zA z-CE3rZiN560mRjEP3X(*vsP#3Gi{YO&bce0n*reB%*NBt7RZR&M}RJF*<6vrOE^Wy z^OG898fGm`hh~3iwjQF6<|b!V(j7dJC!E3Le@`XY5kq9=&=di1?jq9Y%srI6Y>}LS zwc=l;Vn-8f)&r@wC~5>_4587{d8ILH=+d1}W6!1)A0X^pk3)r@^u9aqkk6IM+?NoDN+xMdGmr{wC zeQk9mO+NzSUhW^GjbUPok5R*}wsycKS6LP(fGdCCOTGB;DqR$m{g_NN+1O6CMOr@G z;{HU7w9Kcx`q1@1tOUY7Kx#b? zw*}V3&pY?|-DwAY8ydQe_&SA*;Mzi`a2K99EU@*g(8M2Gbk6XK+hXYPh{z|BdXYs< zI}sM}sDzZ2iiSV$a?l*t$s~FF@!aE%Pk9iwJS=k=vQ~+EVCp#2WcQ-p_gTHB`wf@B70lHoZip%4_9XN!b)tX+#Mv=_@&|{e*9jQZH3L36j8Wt&9(OU}i=qS4 zsl86`58%3k+IpOT{)6k`4bHcgLhrGR{#3zq()q?C90jVE)IGDUa4Dk{m09`~c0+%4 zRpy4BB&D$J3dPe|H@F)?-7$c9w0~KdiT=QDI1%0ku{frVe+36S z&gvgqt!%N$6$3-HO>F3UBHX;>H#E+P{aB9#mM)Lhg@$~R3tJO`d9SuAn(rE>Qpxv$ z=`=L~fin3dQn*=BB#N;>n6R~p>02nu BjL-l8 literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index ba1dc3d..a7af90c 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,10 +1,10 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - E2134, E2123: Fix bindings and reporting to use the correct endpoints - @alexandruy2k\n - Fix errors when pressing a dashboard tile assigned to \"push\" one of the buttons - @OldChicagoPete\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Add driver for RGB-Only Lights (RGB)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", - "dateReleased": "2024-04-??", + "dateReleased": "2024-05-??", "documentationLink": "https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/", "communityLink": "https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853", "payPalUrl": "https://www.buymeacoffee.com/dandanache", @@ -28,14 +28,6 @@ "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_E2202.groovy", "required": false }, - { - "id" : "6e9663e0-c76b-42e3-bbd4-01657e23adba", - "name": "IKEA Color White Spectrum Light", - "description": "Zigbee driver for IKEA Color White Spectrum Lights", - "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_CWS-Light.groovy", - "required": false - }, { "id" : "97ee5c2b-9c74-43f7-a149-adbfbad48da5", "name": "IKEA Dimmable Light", @@ -52,6 +44,22 @@ "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_WS-Light.groovy", "required": false }, + { + "id" : "6e9663e0-c76b-42e3-bbd4-01657e23adba", + "name": "IKEA Color White Spectrum Light", + "description": "Zigbee driver for IKEA Color White Spectrum Lights", + "namespace": "dandanache", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_CWS-Light.groovy", + "required": false + }, + { + "id" : "1374f975-41c7-4a68-a9c5-2e311d310f6b", + "name": "IKEA RGB-Only Light", + "description": "Zigbee driver for IKEA RGB-Only Lights", + "namespace": "dandanache", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy", + "required": false + }, { "id" : "d5e1cf42-b933-466b-b95b-cd0c338db077", "name": "IKEA Parasoll Door/Window Sensor (E2013)", diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index acc7869..4c49678 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -8,7 +8,7 @@ capability 'ColorControl' // Fields for capability.ColorControl @Field static final Map COLOR_LOOP_SPEED = [ - 'swift':3, 'quick':5, 'moderate':10, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 + 'swift':5, 'quick':10, 'moderate':20, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 ] {{/ @fields }} {{!--------------------------------------------------------------------------}} @@ -56,8 +56,8 @@ void startColorLoop(String speed) { state.loop = true } void stopColorLoop() { - log_info "Stopped color loop" - String payload = "0F 00 01 0000 0000 00 00" + log_info 'Stopped color loop' + String payload = '0F 00 01 0000 0000 00 00' utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set state.remove 'loop' } diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy index f7100a8..4289d7a 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -50,7 +50,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) - if (level > 0 && duration == 0) setLevel level, duration + if (level > 0 && duration == 0) setLevel level } void startColorTemperatureChange(String direction) { log_debug "Starting color temperature change ${direction}wards with a rate of ${colorTemperatureChangeRate}% / second" diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index ae8d917..eccce86 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -9,9 +9,6 @@ device: url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 fingerprints: - - type: LED1624G9E27EU - firmwares: 2.3.093 (117C-2801-23086631) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' - type: LED1924G9 firmwares: 1.0.021 (117C-2802-10021655) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 CWS 806lm', manufacturer:'IKEA of Sweden' @@ -21,9 +18,6 @@ device: - type: LED1925G6 firmwares: 1.0.021 (117C-2802-10021655) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' - - type: LED1624G9E14EU - firmwares: 2.3.093 (117C-2803-23093631) - value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' - type: L2112 firmwares: 1.1.10 (117C-2804-01010010) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' diff --git a/ikea-zigbee-drivers/src/devices/Ikea_RGBO-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_RGBO-Light/config.yaml new file mode 100644 index 0000000..6bb5482 --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_RGBO-Light/config.yaml @@ -0,0 +1,33 @@ +device: + model: IKEA RGB-Only Light + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_RGBO-Light.webp + links: + - name: device details + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#rgb-only-light + - name: community page + url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 + + fingerprints: + - type: LED1624G9E27EU + firmwares: 2.3.093 (117C-2801-23086631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E27 CWS opal 600lm', manufacturer:'IKEA of Sweden' + - type: LED1624G9E14EU + firmwares: 2.3.093 (117C-2803-23093631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019,1000', model:'TRADFRI bulb E14 CWS opal 600lm', manufacturer:'IKEA of Sweden' + + capabilities: + - file: src/capabilities/Switch.groovy + params: + powerOnBehavior: true + onWithTimedOff: true + - file: src/capabilities/ColorControl.groovy + - file: src/capabilities/Brightness.groovy + - file: src/capabilities/HealthCheck.groovy + params: + schedule: 0 0 0/1 ? * * * + checkInterval: 3600 # every hour + thereshold: 3600 # report device as offline if no message was received in the last 60 minutes (device should report On/Off status every 10 minutes) + - file: src/capabilities/PowerSource.groovy + - file: src/capabilities/ZigbeeGroups.groovy + - file: src/capabilities/FirmwareUpdate.groovy From 09afaae662db23c9f1992969ca09062ac7f77dbb Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Sun, 5 May 2024 00:39:34 +0300 Subject: [PATCH 18/26] Add fingerprint for LED2110R3 --- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 1 + ikea-zigbee-drivers/README.md | 1 + ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml | 3 +++ 3 files changed, 5 insertions(+) diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 561447d..1f6321e 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -47,6 +47,7 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' // LED1923R5: 1.0.021 (117C-2802-10021655) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,0B05,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS 470lm', manufacturer:'IKEA of Sweden' // LED1925G6: 1.0.021 (117C-2802-10021655) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' // L2112: 1.1.10 (117C-2804-01010010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' // LED2110R3: 1.0.38 (117C-2805-01000038) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' // LED2111G6: 1.0.38 (117C-2805-01000038) // Attributes for capability.HealthCheck diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index 273f1ca..a793b81 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -185,6 +185,7 @@ Below you can find the details of each lighting device, including the features a | LED1923R5 | Tradfri Bulb GU10 CWS 345lm | | LED1925G6 | Tradfri Bulb E14 CWS 470lm | | L2112 | Ormanas LED Strip | +| LED2110R3 | Tradfri Bulb GU10 CWS 345lm | | LED2111G6 | Tradfri Bulb E14 CWS Globe 806lm | #### Pairing Instructions diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index eccce86..7bc3aed 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -21,6 +21,9 @@ device: - type: L2112 firmwares: 1.1.10 (117C-2804-01010010) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'ORMANAS LED Strip', manufacturer:'IKEA of Sweden' + - type: LED2110R3 + firmwares: 1.0.38 (117C-2805-01000038) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 CWS 345lm', manufacturer:'IKEA of Sweden' - type: LED2111G6 firmwares: 1.0.38 (117C-2805-01000038) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E14 CWS globe 806lm', manufacturer:'IKEA of Sweden' From 657ab7c2960b49bcd0bd91395cdbe1b8a1f8ff8e Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Wed, 8 May 2024 21:07:01 +0300 Subject: [PATCH 19/26] - Remove color loop functionality from RGB-Only lights - Ignore Write Attribute Response message --- ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 2 +- ikea-zigbee-drivers/Ikea_RGBO-Light.groovy | 28 ++----------------- .../src/capabilities/ColorControl.groovy | 12 ++++++-- .../src/devices/Ikea_CWS-Light/config.yaml | 2 ++ 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 1f6321e..5bb0567 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -10,7 +10,6 @@ import groovy.transform.Field @Field static final String DRIVER_VERSION = '5.0.0' // Fields for capability.ColorControl - @Field static final Map COLOR_LOOP_SPEED = [ 'swift':5, 'quick':10, 'moderate':20, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 ] @@ -779,6 +778,7 @@ void parse(String description) { return case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0003] }: // Report Attribute Current X case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0004] }: // Report Attribute Current Y + case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.ColorTemperature diff --git a/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy b/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy index 27567e8..22288a6 100644 --- a/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_RGBO-Light.groovy @@ -9,12 +9,6 @@ import groovy.transform.Field @Field static final String DRIVER_NAME = 'IKEA RGB-Only Light' @Field static final String DRIVER_VERSION = '5.0.0' -// Fields for capability.ColorControl - -@Field static final Map COLOR_LOOP_SPEED = [ - 'swift':5, 'quick':10, 'moderate':20, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 -] - // Fields for capability.HealthCheck import groovy.time.TimeCategory @@ -52,10 +46,6 @@ metadata { command 'toggle' command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] - // Commands for capability.ColorControl - command 'startColorLoop', [[name:'Speed*', type:'ENUM', constraints: COLOR_LOOP_SPEED.keySet()]] - command 'stopColorLoop' - // Commands for capability.Brightness command 'shiftLevel', [[name:'Direction*', type:'ENUM', constraints: ['up', 'down']]] @@ -433,19 +423,6 @@ void setSaturation(BigDecimal saturation) { String payload = "${utils_payload newSaturation, 2} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } -void startColorLoop(String speed) { - Integer seconds = COLOR_LOOP_SPEED[speed] ?: 30 - log_info "Starting color loop at ${speed} speed (${seconds} sec per loop)" - String payload = "0F 01 01 ${utils_payload seconds, 4} 0000 00 00" - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set - state.loop = true -} -void stopColorLoop() { - log_info 'Stopped color loop' - String payload = '0F 00 01 0000 0000 00 00' - utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set - state.remove 'loop' -} private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] attributes[msg.attrInt] = msg.value @@ -475,7 +452,7 @@ private void processMultipleColorAttributes(Map msg, String type) { } } - if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type, noInfo:(state.loop == true) + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type // Update colorName, if the case @@ -483,7 +460,7 @@ private void processMultipleColorAttributes(Map msg, String type) { Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) String colorName = convertHueToGenericColorName colorHue, colorSaturation - utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type, noInfo:(state.loop == true) + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type } utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" } @@ -654,6 +631,7 @@ void parse(String description) { return case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0003] }: // Report Attribute Current X case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0004] }: // Report Attribute Current Y + case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) return // Events for capability.Brightness diff --git a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy index 4c49678..84c788e 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorControl.groovy @@ -4,19 +4,22 @@ capability 'ColorControl' {{/ @definition }} {{!--------------------------------------------------------------------------}} {{# @fields }} +{{# params.colorLoop }} // Fields for capability.ColorControl - @Field static final Map COLOR_LOOP_SPEED = [ 'swift':5, 'quick':10, 'moderate':20, 'leisurely':30, 'sluggish':60, 'snail\'s pace':180, 'glacial':300, 'stationary':600 ] +{{/ params.colorLoop }} {{/ @fields }} {{!--------------------------------------------------------------------------}} {{# @commands }} +{{# params.colorLoop }} // Commands for capability.ColorControl command 'startColorLoop', [[name:'Speed*', type:'ENUM', constraints: COLOR_LOOP_SPEED.keySet()]] command 'stopColorLoop' +{{/ params.colorLoop }} {{/ @commands }} {{!--------------------------------------------------------------------------}} {{# @implementation }} @@ -48,6 +51,7 @@ void setSaturation(BigDecimal saturation) { String payload = "${utils_payload newSaturation, 2} 0000 00 00" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114303 ${payload}}"]) // Move to Saturation } +{{# params.colorLoop }} void startColorLoop(String speed) { Integer seconds = COLOR_LOOP_SPEED[speed] ?: 30 log_info "Starting color loop at ${speed} speed (${seconds} sec per loop)" @@ -61,6 +65,7 @@ void stopColorLoop() { utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {114344 ${payload}}"]) // Color Loop Set state.remove 'loop' } +{{/ params.colorLoop }} private void processMultipleColorAttributes(Map msg, String type) { Map attributes = [:] attributes[msg.attrInt] = msg.value @@ -90,7 +95,7 @@ private void processMultipleColorAttributes(Map msg, String type) { } } - if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type, noInfo:(state.loop == true) + if (hue >= 0) utils_sendEvent name:'hue', value:hue, descriptionText:"Color hue is ${hue}%", type:type{{# params.colorLoop }}, noInfo:(state.loop == true){{/ params.colorLoop }} if (saturation >= 0) utils_sendEvent name:'saturation', value:saturation, descriptionText:"Color saturation is ${saturation}%", type:type // Update colorName, if the case @@ -98,7 +103,7 @@ private void processMultipleColorAttributes(Map msg, String type) { Integer colorHue = hue >= 0 ? hue : device.currentValue('hue', true) Integer colorSaturation = saturation >= 0 ? saturation : device.currentValue('saturation', true) String colorName = convertHueToGenericColorName colorHue, colorSaturation - utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type, noInfo:(state.loop == true) + utils_sendEvent name:'colorName', value:colorName, descriptionText:"Color name is ${colorName}", type:type{{# params.colorLoop }}, noInfo:(state.loop == true){{/ params.colorLoop }} } utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "CurrentHue=${hue}%, CurrentSaturation=${saturation}%, ColorMode=${colorMode}" } @@ -156,6 +161,7 @@ case { contains it, [clusterInt:0x0300, commandInt:0x07] }: return case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0003] }: // Report Attribute Current X case { contains it, [clusterInt:0x0300, commandInt:0x0A, attrInt:0x0004] }: // Report Attribute Current Y +case { contains it, [clusterInt:0x0300, commandInt:0x04] }: // Write Attribute Response (0x04) return {{/ @events }} {{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml index 7bc3aed..4cefed8 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_CWS-Light/config.yaml @@ -34,6 +34,8 @@ device: powerOnBehavior: true onWithTimedOff: true - file: src/capabilities/ColorControl.groovy + params: + colorLoop: true - file: src/capabilities/ColorTemperature.groovy - file: src/capabilities/Brightness.groovy - file: src/capabilities/HealthCheck.groovy From 7b87c13061102adaa9b13a546341fdf2d058bd32 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Wed, 8 May 2024 21:49:07 +0300 Subject: [PATCH 20/26] Update README --- ikea-zigbee-drivers/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index a793b81..ec74e50 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -75,6 +75,10 @@ More info about installing custom drivers is available in the [Official Document Below you can find the details of each lighting device, including the features and pairing instructions. ### Dimmable Light +These products come equipped with the following features: +* **Wireless Dimming**: Provides the flexibility to adjust the brightness level from a minimal 0% to a maximum 100%, catering to your specific lighting needs. + +Also includes the 10W and 30W LED drivers. | Parameter | Details | |-----------|-------------| @@ -113,6 +117,11 @@ Below you can find the details of each lighting device, including the features a ### White Spectrum Light +These products have the following features: +These products come equipped with the following features: +* **Wireless Dimming**: Provides the flexibility to adjust the brightness level from a minimal 0% to a maximum 100%, catering to your specific lighting needs. + +* **Adjustable White Tones**: Offers the ability to modify the color temperature within a range of 2200 Kelvin (which emits a warm glow) to 4000 Kelvin (which produces a cool white light), allowing you to create the perfect ambiance. | Parameter | Details | |-----------|-------------| @@ -154,6 +163,12 @@ Below you can find the details of each lighting device, including the features a ### Color White Spectrum Light +These products come equipped with the following features: +* **Wireless Dimming**: Provides the flexibility to adjust the brightness level from a minimal 0% to a maximum 100%, catering to your specific lighting needs. + +* **Adjustable White Tones**: Offers the ability to modify the color temperature within a range of 2200 Kelvin (which emits a warm glow) to 4000 Kelvin (which produces a cool white light), allowing you to create the perfect ambiance. + +* **Adjustable Color**: Grants comprehensive control over the light color by manipulating the hue, saturation, and level (HSL), enabling you to personalize your lighting experience. | Parameter | Details | |-----------|-------------| @@ -197,6 +212,10 @@ Below you can find the details of each lighting device, including the features a ### RGB-Only Light +These products come equipped with the following features: +* **Wireless Dimming**: Provides the flexibility to adjust the brightness level from a minimal 0% to a maximum 100%, catering to your specific lighting needs. + +* **Adjustable Color**: Grants comprehensive control over the light color by manipulating the hue, saturation, and level (HSL), enabling you to personalize your lighting experience. Bulbs of this type lack native support for color temperature control. Their hardware is exclusively equipped with red, green, and blue LEDs, and does not include white light LEDs. As a result, they are unable to natively produce variations in light temperature. From c9f089bbf9b24b288e7506d5ab4725c8fb7ec0a3 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Wed, 8 May 2024 21:50:37 +0300 Subject: [PATCH 21/26] Update README --- ikea-zigbee-drivers/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index ec74e50..de6e2d6 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -102,11 +102,11 @@ Also includes the 10W and 30W LED drivers. | LED2103G5 | Tradfri Bulb E27 WW Globe 806lm | | LED2104R3 | Tradfri Bulb GU10 WW 345lm | | LED1623G12 | Tradfri Bulb E27 Opal 1000lm | -| 10EU-IL-1 | Tradfri LED Driver 10W | | LED1842G3 | Tradfri Bulb E27 WW Clear 250lm | | LED1836G9 | Tradfri Bulb E27 WW 806lm | -| 30-IL44-1 | Silverglans LED Driver 30W | -| 30EU-IL-2 | Tradfri LED Driver 30W | +| 10EU-IL-1 | Tradfri LED Driver (10W) | +| 30-IL44-1 | Silverglans LED Driver (30W) | +| 30EU-IL-2 | Tradfri LED Driver (30W) | #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. From 84046428ba40507ec09e3405c94771015cfccb86 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Wed, 8 May 2024 21:51:50 +0300 Subject: [PATCH 22/26] Update README --- ikea-zigbee-drivers/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index de6e2d6..c06208b 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -230,7 +230,6 @@ Bulbs of this type lack native support for color temperature control. Their hard * **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). * **Brightness Control**: Allows setting of brightness level, starting/stopping brightness level change, and stepping brightness level up/down. * **Color (RGB) Control**: Provides options to set color hue and/or saturation, and display color name and color mode. -* **Color Loop**: Initiates cycling of the color hue until stopped. * **Brightness Configuration**: Configures brightness level when turned on (options include "Always the same fixed value", and "Restore last level"). * **Pre-staging**: Allows setting of brightness level and color when the lights are off (and they stay off). When the lights are turned on, they will start at the specified level/color. * **Health Status**: Indicates whether the device is online or offline. From 7c246f90e333050b10d34d271c12c157a759ef9d Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Fri, 10 May 2024 19:04:10 +0300 Subject: [PATCH 23/26] - Add driver for Knycklan Water Valve (receiver and remote) - Add more bulb fingerprints --- ikea-zigbee-drivers/CHANGELOG.md | 1 + ikea-zigbee-drivers/Ikea_CWS-Light.groovy | 1 + ikea-zigbee-drivers/Ikea_DIM-Light.groovy | 3 + ikea-zigbee-drivers/Ikea_E1743.groovy | 2 +- ikea-zigbee-drivers/Ikea_E1841.groovy | 621 +++++++++++++++++ ikea-zigbee-drivers/Ikea_E1842.groovy | 629 ++++++++++++++++++ ikea-zigbee-drivers/Ikea_E2202.groovy | 2 +- ikea-zigbee-drivers/Ikea_WS-Light.groovy | 3 + ikea-zigbee-drivers/README.md | 78 ++- ikea-zigbee-drivers/img/Ikea_E1841.webp | Bin 0 -> 7132 bytes ikea-zigbee-drivers/img/Ikea_E1842.webp | Bin 0 -> 9894 bytes ikea-zigbee-drivers/packageManifest.json | 2 +- .../src/capabilities/ColorTemperature.groovy | 1 + .../src/devices/Ikea_DIM-Light/config.yaml | 9 + .../src/devices/Ikea_E1743/E1743.groovy | 2 +- .../src/devices/Ikea_E1841/E1841.groovy | 34 + .../src/devices/Ikea_E1841/config.yaml | 36 + .../src/devices/Ikea_E1842/E1842.groovy | 20 + .../src/devices/Ikea_E1842/config.yaml | 32 + .../src/devices/Ikea_E2202/E2202.groovy | 2 +- .../src/devices/Ikea_WS-Light/config.yaml | 6 + 21 files changed, 1468 insertions(+), 16 deletions(-) create mode 100644 ikea-zigbee-drivers/Ikea_E1841.groovy create mode 100644 ikea-zigbee-drivers/Ikea_E1842.groovy create mode 100644 ikea-zigbee-drivers/img/Ikea_E1841.webp create mode 100644 ikea-zigbee-drivers/img/Ikea_E1842.webp create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_E1841/E1841.groovy create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_E1842/E1842.groovy create mode 100644 ikea-zigbee-drivers/src/devices/Ikea_E1842/config.yaml diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index a567c5d..479a848 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add driver for White Spectrum Lights devices (CT) - Add driver for Color White Spectrum Lights devices (RGB+CT) - Add driver for RGB-Only Lights devices (RGB) +- Add driver for Knycklan Water Valve (receiver and remote) - Add Scenes support using Zigbee Bindings for E1810 and E2002 - Put all devices in "identifying mode" while "configure()" is running - `@UncleAlias` - E2006: Move "Dark Mode" preference to "setIndicatorStatus()" command and "indicatorStatus" attribute - `@userLP24` diff --git a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy index 5bb0567..2a027a5 100644 --- a/ikea-zigbee-drivers/Ikea_CWS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_CWS-Light.groovy @@ -557,6 +557,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) + /* groovylint-disable-next-line UnnecessarySetter */ if (level > 0 && duration == 0) setLevel level } void startColorTemperatureChange(String direction) { diff --git a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy index 51ef457..2033fcd 100644 --- a/ikea-zigbee-drivers/Ikea_DIM-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_DIM-Light.groovy @@ -37,8 +37,11 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WW globe 806lm', manufacturer:'IKEA of Sweden' // LED2103G5: 1.0.36 (117C-2100-01000036) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb GU10 WW 345lm8', manufacturer:'IKEA of Sweden' // LED2104R3: 1.0.36 (117C-2100-01000036) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 opal 1000lm', manufacturer:'IKEA of Sweden' // LED1623G12: 2.3.094 (117C-2101-23094631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E14 W op/ch 400lm', manufacturer:'IKEA of Sweden' // LED1649C5E14EU: 2.3.094 (117C-2101-23094631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE27WWclear250lm', manufacturer:'IKEA of Sweden' // LED1934G3: 1.0.010 (117C-2102-00010010) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1: 1.2.245 (117C-4101-12245572) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' // 10EU-IL-1: 2.3.086 (117C-4101-23086631) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb GU10 WW 400lm', manufacturer:'IKEA of Sweden' // LED1837R5: 2.3.093 (117C-4103-23093631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' // LED1842G3: 2.3.093 (117C-4103-23093631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW 806lm', manufacturer:'IKEA of Sweden' // LED1836G9: 2.3.094 (117C-4103-23093631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'SILVERGLANS IP44 LED driver', manufacturer:'IKEA of Sweden' // 30-IL44-1: 1.0.021 (117C-4104-00010021) diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index 42492de..930e22d 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -166,7 +166,7 @@ void configure(boolean auto = false) { state.lastRx = 0 state.lastCx = DRIVER_VERSION - // Configuration for devices.E1743 + // Configuration for devices.Ikea_E1743 cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster diff --git a/ikea-zigbee-drivers/Ikea_E1841.groovy b/ikea-zigbee-drivers/Ikea_E1841.groovy new file mode 100644 index 0000000..ca27bb1 --- /dev/null +++ b/ikea-zigbee-drivers/Ikea_E1841.groovy @@ -0,0 +1,621 @@ +/** + * IKEA Knycklan Water Valve Remote (E1841) + * + * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ + */ +import groovy.transform.CompileStatic +import groovy.transform.Field + +@Field static final String DRIVER_NAME = 'IKEA Knycklan Water Valve Remote (E1841)' +@Field static final String DRIVER_VERSION = '5.0.0' + +// Fields for capability.HealthCheck +import groovy.time.TimeCategory + +@Field static final Map HEALTH_CHECK = [ + 'schedule': '0 0 0/1 ? * * *', // Health will be checked using this cron schedule + 'thereshold': '43200' // When checking, mark the device as offline if no Zigbee message was received in the last 43200 seconds +] + +// Fields for capability.PushableButton +@Field static final Map> BUTTONS = [ + 'ON': ['1', '💧'], + 'OFF': ['2', '🩸'], +] + +metadata { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1841.groovy') { + capability 'Configuration' + capability 'Refresh' + capability 'Battery' + capability 'HealthCheck' + capability 'HoldableButton' + capability 'PowerSource' + capability 'PushableButton' + capability 'ReleasableButton' + + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'KNYCKLAN Open/Close remote', manufacturer:'IKEA of Sweden' // Firmware: 2.3.019 (117C-11C9-23019631) + + // Attributes for capability.HealthCheck + attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] + } + + // Commands for capability.FirmwareUpdate + command 'updateFirmware' + + preferences { + input( + name: 'helpInfo', type: 'hidden', + title: ''' +
+ IKEA Knycklan Water Valve Remote (E1841) v5.0.0
+ +
+ ''' + ) + input( + name: 'logLevel', type: 'enum', + title: 'Log verbosity', + description: 'Select what type of messages appear in the "Logs" section.', + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], + defaultValue: '1', + required: true + ) + + // Inputs for capability.ZigbeeBindings + input( + name: 'controlDevice', type: 'enum', + title: 'Control Zigbee device', + description: 'Select the target Zigbee device that will be directly controlled by this device.', + options: ['0000':'❌ Stop controlling all Zigbee devices', '----':'- - - -'] + retrieveSwitchDevices(), + defaultValue: '----', + required: false + ) + } +} + +// =================================================================================================================== +// Implement default methods +// =================================================================================================================== + +// Called when the device is first added +void installed() { + log_warn 'Installing device ...' + log_warn '[IMPORTANT] For battery-powered devices, make sure that you keep your device as close as you can (less than 2inch / 5cm) to your Hubitat hub for at least 30 seconds. Otherwise the device will successfully pair but it won\'t work properly!' +} + +// Called when the "Save Preferences" button is clicked +List updated(boolean auto = false) { + log_info "Saving preferences${auto ? ' (auto)' : ''} ..." + List cmds = [] + + unschedule() + + if (logLevel == null) { + logLevel = '1' + device.updateSetting 'logLevel', [value:logLevel, type:'enum'] + } + if (logLevel == '1') runIn 1800, 'logsOff' + log_info "🛠️ logLevel = ${['1':'Debug', '2':'Info', '3':'Warning', '4':'Error'].get(logLevel)}" + + // Preferences for capability.HealthCheck + schedule HEALTH_CHECK.schedule, 'healthCheck' + + // Preferences for capability.ZigbeeBindings + if (controlDevice != null && controlDevice != '----') { + if (controlDevice == '0000') { + log_info '🛠️ Clearing all device bindings' + state.stopControlling = 'devices' + } else { + log_info "🛠️ Adding binding to device #${controlDevice} for clusters [0x0006 0x0008]" + + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0006'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0006 + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0021 {49 ${utils_payload "${device.zigbeeId}"} ${utils_payload "${device.endpointId}"} ${utils_payload '0x0008'} 03 ${utils_payload "${controlDevice}"} 01} {0x0000}" // Add device binding for cluster 0x0008 + } + device.updateSetting 'controlDevice', [value:'----', type:'enum'] + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" + } + + if (auto) return cmds + utils_sendZigbeeCommands cmds + return [] +} + +// =================================================================================================================== +// Capabilities helpers +// =================================================================================================================== + +// Handler method for scheduled job to disable debug logging +void logsOff() { + log_info '⏲️ Automatically reverting log level to "Info"' + device.updateSetting 'logLevel', [value:'2', type:'enum'] +} + +// Helpers for capability.HealthCheck +void healthCheck() { + log_debug '⏲️ Automatically running health check' + String healthStatus = state.lastRx == 0 || state.lastRx == null ? 'unknown' : (now() - state.lastRx < Integer.parseInt(HEALTH_CHECK.thereshold) * 1000 ? 'online' : 'offline') + utils_sendEvent name:'healthStatus', value:healthStatus, type:'physical', descriptionText:"Health status is ${healthStatus}" +} + +// =================================================================================================================== +// Implement Capabilities +// =================================================================================================================== + +// capability.Configuration +// Note: This method is also called when the device is initially installed +void configure(boolean auto = false) { + log_warn "Configuring device${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Configure" button immediately after pushing any button on the device in order to first wake it up!' + } + + // Apply preferences first + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] + cmds += updated true + + // Clear data (keep firmwareMT information though) + device.data*.key.each { if (it != 'firmwareMT') device.removeDataValue it } + + // Clear state + state.clear() + state.lastTx = 0 + state.lastRx = 0 + state.lastCx = DRIVER_VERSION + + // Configuration for devices.Ikea_E1841 + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster + + // Configuration for capability.Battery + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0001 {${device.zigbeeId}} {}" // Power Configuration cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0001 0x0021 0x20 0x0000 0x4650 {02} {}" // Report BatteryPercentage (uint8) at least every 5 hours (Δ = 1%) + + // Configuration for capability.HealthCheck + sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' + sendEvent name:'checkInterval', value:3600, unit:'second', descriptionText:'Health check interval is 3600 seconds' + + // Configuration for capability.PowerSource + sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + + // Configuration for capability.PushableButton + Integer numberOfButtons = BUTTONS.count { true } + sendEvent name:'numberOfButtons', value:numberOfButtons, descriptionText:"Number of buttons is ${numberOfButtons}" + + // Query Basic cluster attributes + cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID + cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier + cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" + utils_sendZigbeeCommands cmds + + log_info 'Configuration done; refreshing device current state in 7 seconds ...' + runIn 7, 'refresh', [data:true] +} +/* groovylint-disable-next-line UnusedPrivateMethod */ +private void autoConfigure() { + log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" + configure true +} + +// capability.Refresh +void refresh(boolean auto = false) { + log_warn "Refreshing device state${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Refresh" button immediately after pushing any button on the device in order to first wake it up!' + } + + List cmds = [] + + // Refresh for capability.Battery + cmds += zigbee.readAttribute(0x0001, 0x0021) // BatteryPercentage + + // Refresh for capability.ZigbeeBindings + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 00} {0x0000}" // Start querying the Bindings Table + utils_sendZigbeeCommands cmds +} + +// Implementation for capability.HealthCheck +void ping() { + log_warn 'ping ...' + utils_sendZigbeeCommands(zigbee.readAttribute(0x0000, 0x0000)) + log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' + runIn 5, 'pingExecute' +} +void pingExecute() { + if (state.lastRx == 0) { + log_info 'Did not sent any messages since it was last configured' + return + } + + Date now = new Date(Math.round(now() / 1000) * 1000) + Date lastRx = new Date(Math.round(state.lastRx / 1000) * 1000) + String lastRxAgo = TimeCategory.minus(now, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Sent last message at ${lastRx.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${lastRxAgo} ago)" + + Date thereshold = new Date(Math.round(state.lastRx / 1000 + Integer.parseInt(HEALTH_CHECK.thereshold)) * 1000) + String theresholdAgo = TimeCategory.minus(thereshold, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received for ${theresholdAgo} (hardcoded)" + + String offlineMarkAgo = TimeCategory.minus(thereshold, now).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received until ${thereshold.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${offlineMarkAgo} from now)" +} + +// Implementation for capability.HoldableButton +void hold(String buttonNumber) { hold Integer.parseInt(buttonNumber) } +void hold(BigDecimal buttonNumber) { + String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) + if (buttonName == null) { + log_warn "Cannot hold button ${buttonNumber} because it is not defined" + return + } + utils_sendEvent name:'held', value:buttonNumber, type:'digital', isStateChange:true, descriptionText:"Button ${buttonNumber} (${buttonName}) was held" +} + +// Implementation for capability.PushableButton +void push(String buttonNumber) { push Integer.parseInt(buttonNumber) } +void push(BigDecimal buttonNumber) { + String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) + if (buttonName == null) { + log_warn "Cannot push button ${buttonNumber} because it is not defined" + return + } + utils_sendEvent name:'pushed', value:buttonNumber, type:'digital', isStateChange:true, descriptionText:"Button ${buttonNumber} (${buttonName}) was pressed" +} + +// Implementation for capability.ReleasableButton +void release(String buttonNumber) { release Integer.parseInt(buttonNumber) } +void release(BigDecimal buttonNumber) { + String buttonName = BUTTONS.find { it.value[0] == "${buttonNumber}" }?.value?.getAt(1) + if (buttonName == null) { + log_warn "Cannot release button ${buttonNumber} because it is not defined" + return + } + utils_sendEvent name:'released', value:buttonNumber, type:'digital', isStateChange:true, descriptionText:"Button ${buttonNumber} (${buttonName}) was released" +} + +// Implementation for capability.ZigbeeBindings +private Map retrieveSwitchDevices() { + try { + List switchDeviceIds = httpGet([uri:'http://127.0.0.1:8080/device/listJson?capability=capability.switch']) { it.data*.id } + httpGet([uri:'http://127.0.0.1:8080/hub/zigbeeDetails/json']) { response -> + response.data.devices + .findAll { switchDeviceIds.contains(it.id) } + .sort { it.name } + .collectEntries { [(it.zigbeeId): it.name] } + } + /* groovylint-disable-next-line CatchException */ + } catch (Exception ex) { + return ['ZZZZ': "Exception: ${ex}"] + } +} + +// Implementation for capability.FirmwareUpdate +void updateFirmware() { + log_info 'Looking for firmware updates ...' + if (device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Update Firmware" button immediately after pushing any button on the device in order to first wake it up!' + } + utils_sendZigbeeCommands zigbee.updateFirmware() +} + +// =================================================================================================================== +// Handle incoming Zigbee messages +// =================================================================================================================== + +void parse(String description) { + log_debug "description=[${description}]" + + // Auto-Configure device: configure() was not called for this driver version + if (state.lastCx != DRIVER_VERSION) { + state.lastCx = DRIVER_VERSION + runInMillis 1500, 'autoConfigure' + } + + // Extract msg + Map msg = [:] + if (description.startsWith('zone status')) msg += [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] + if (description.startsWith('enroll request')) msg += [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] + + msg += zigbee.parseDescriptionAsMap description + if (msg.containsKey('endpoint')) msg.endpointInt = Integer.parseInt(msg.endpoint, 16) + if (msg.containsKey('sourceEndpoint')) msg.endpointInt = Integer.parseInt(msg.sourceEndpoint, 16) + if (msg.containsKey('cluster')) msg.clusterInt = Integer.parseInt(msg.cluster, 16) + if (msg.containsKey('command')) msg.commandInt = Integer.parseInt(msg.command, 16) + log_debug "msg=[${msg}]" + + state.lastRx = now() + + // Parse for capability.HealthCheck + if (device.currentValue('healthStatus', true) != 'online') { + utils_sendEvent name:'healthStatus', value:'online', type:'digital', descriptionText:'Health status changed to online' + } + + // If we sent a Zigbee command in the last 3 seconds, we assume that this Zigbee event is a consequence of this driver doing something + // Therefore, we mark this event as "digital" + String type = state.containsKey('lastTx') && (now() - state.lastTx < 3000) ? 'digital' : 'physical' + + switch (msg) { + + // Events for devices.Ikea_E1841 + // =================================================================================================================== + + // I/O button was pressed + case { contains it, [clusterInt:0x0006, commandInt:0x00] }: + case { contains it, [clusterInt:0x0006, commandInt:0x01] }: + List button = msg.commandInt == 0x00 ? BUTTONS.OFF : BUTTONS.ON + utils_sendEvent name:'pushed', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was pushed" + return + + // I/O button was held + case { contains it, [clusterInt:0x0008, commandInt:0x01] }: + case { contains it, [clusterInt:0x0008, commandInt:0x05] }: + List button = msg.commandInt == 0x01 ? BUTTONS.OFF : BUTTONS.ON + utils_sendEvent name:'held', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was held" + return + + // I/O button was released + case { contains it, [clusterInt:0x0008, commandInt:0x07] }: + List button = device.currentValue('held', true) == 1 ? BUTTONS.ON : BUTTONS.OFF + utils_sendEvent name:'released', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was released" + return + + // Events for capability.Battery + // =================================================================================================================== + + // Report/Read Attributes Reponse: BatteryPercentage + case { contains it, [clusterInt:0x0001, commandInt:0x0A, attrInt:0x0021] }: + case { contains it, [clusterInt:0x0001, commandInt:0x01] }: + + // Hubitat fails to parse some Read Attributes Responses + if (msg.value == null && msg.data != null && msg.data[0] == '21' && msg.data[1] == '00') { + msg.value = msg.data[2] + } + + // The value 0xff indicates an invalid or unknown reading + if (msg.value == 'FF') { + log_warn "Ignored invalid remaining battery percentage value: 0x${msg.value}" + return + } + + Integer percentage = Integer.parseInt(msg.value, 16) + percentage = percentage / 2 + utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" + return + + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0001, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=BatteryPercentage, data=${msg.data}" + return + + // Events for capability.HealthCheck + // =================================================================================================================== + + case { contains it, [clusterInt:0x0000, attrInt:0x0000] }: + log_warn '... pong' + return + + // Configuration for capability.PowerSource + // =================================================================================================================== + + // Read Attributes Reponse: PowerSource + case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: + String powerSource = 'unknown' + + // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } + switch (msg.value) { + case ['01', '02', '05', '06']: + powerSource = 'mains'; break + case '03': + powerSource = 'battery'; break + case '04': + powerSource = 'dc' + } + utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" + utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" + return + + // Events for capability.ZigbeeBindings + // =================================================================================================================== + + // Mgmt_Bind_rsp := { 08:Status, 08:BindingTableEntriesTotal, 08:StartIndex, 08:BindingTableEntriesIncluded, 112/168*n:BindingTableList } + // BindingTableList: { 64:SrcAddr, 08:SrcEndpoint, 16:ClusterId, 08:DstAddrMode, 16/64:DstAddr, 0/08:DstEndpoint } + // Example: [71, 00, 01, 00, 01, C6, 9C, FE, FE, FF, F9, E3, B4, 01, 06, 00, 03, E9, A6, C9, 17, 00, 6F, 0D, 00, 01] + case { contains it, [endpointInt:0x00, clusterInt:0x8033] }: + if (msg.data[1] != '00') { + utils_processedZdpMessage 'Mgmt_Bind_rsp', "Status=FAILED, data=${msg.data}" + return + } + Integer totalEntries = Integer.parseInt msg.data[2], 16 + Integer startIndex = Integer.parseInt msg.data[3], 16 + Integer includedEntries = Integer.parseInt msg.data[4], 16 + if (startIndex == 0) { + state.remove 'ctrlDev' + state.remove 'ctrlGrp' + } + if (includedEntries == 0) { + utils_processedZdpMessage 'Mgmt_Bind_rsp', "totalEntries=${totalEntries}, startIndex=${startIndex}, includedEntries=${includedEntries}" + return + } + + Integer pos = 5 + Integer deleted = 0 + Map allDevices = retrieveSwitchDevices() + Set devices = [] + Set groups = [] + List cmds = [] + for (int idx = 0; idx < includedEntries; idx++) { + String srcDeviceId = msg.data[(pos)..(pos + 7)].reverse().join() + String srcEndpoint = msg.data[pos + 8] + String cluster = msg.data[(pos + 9)..(pos + 10)].reverse().join() + String dstAddrMode = msg.data[pos + 11] + if (dstAddrMode != '01' && dstAddrMode != '03') continue + + // Found device binding + if (dstAddrMode == '03') { + String dstDeviceId = msg.data[(pos + 12)..(pos + 19)].reverse().join() + String dstEndpoint = msg.data[pos + 20] + String dstDeviceName = allDevices.getOrDefault(dstDeviceId, "Unknown (${dstDeviceId})") + pos += 21 + + // Remove all binds that are not targeting the hub + if (state.stopControlling == 'devices') { + if (dstDeviceId != "${location.hub.zigbeeEui}") { + log_debug "Removing binding for device ${dstDeviceName} on cluster 0x${cluster}" + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0022 {49 ${utils_payload srcDeviceId} ${srcEndpoint} ${utils_payload cluster} 03 ${utils_payload dstDeviceId} ${dstEndpoint}} {0x0000}" + deleted++ + } + continue + } + + log_debug "Found binding for device ${dstDeviceName} on cluster 0x${cluster}" + devices.add dstDeviceName + continue + } + + // Found group binding + pos += 14 + } + + Set ctrlDev = (state.ctrlDev ?: []).toSet() + ctrlDev.addAll(devices.findAll { !it.startsWith('Unknown') }) + if (ctrlDev.size() > 0) state.ctrlDev = ctrlDev.unique() + + // Get next batch + if (startIndex + includedEntries < totalEntries) { + cmds += "he raw 0x${device.deviceNetworkId} 0x00 0x00 0x0033 {57 ${Integer.toHexString(startIndex + includedEntries - deleted).padLeft(2, '0')}} {0x0000}" + } else { + log_info "Current device bindings: ${state.ctrlDev ?: 'None'}" + log_info "Current group bindings: ${state.ctrlGrp ?: 'None'}" + state.remove 'stopControlling' + } + utils_sendZigbeeCommands cmds + utils_processedZdpMessage 'Mgmt_Bind_rsp', "totalEntries=${totalEntries}, startIndex=${startIndex}, devices=${devices}, groups=${groups}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Handle common messages (e.g.: received during pairing when we query the device for information) + // --------------------------------------------------------------------------------------------------------------- + + // Device_annce: Welcome back! let's sync state. + case { contains it, [endpointInt:0x00, clusterInt:0x0013, commandInt:0x00] }: + log_warn 'Rejoined the Zigbee mesh; refreshing device state in 3 seconds ...' + runIn 3, 'refresh' + return + + // Report/Read Attributes Response (Basic cluster) + case { contains it, [clusterInt:0x0000, commandInt:0x01] }: + case { contains it, [clusterInt:0x0000, commandInt:0x0A] }: + utils_zigbeeDataValue(msg.attrInt, msg.value) + msg.additionalAttrs?.each { utils_zigbeeDataValue(it.attrInt, it.value) } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "cluster=0x${msg.cluster}, attribute=0x${msg.attrId}, value=${msg.value}" + return + + // Mgmt_Leave_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8034, commandInt:0x00] }: + log_warn 'Device is leaving the Zigbee mesh. See you later, Aligator!' + return + + // Ignore the following Zigbee messages + case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) + case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response + case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8004, commandInt:0x00] }: // ZDP: Simple_Desc_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8005, commandInt:0x00] }: // ZDP: Active_EP_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x0006, commandInt:0x00] }: // ZDP: MatchDescriptorRequest + case { contains it, [endpointInt:0x00, clusterInt:0x801F, commandInt:0x00] }: // ZDP: Parent_annce_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8021, commandInt:0x00] }: // ZDP: Mgmt_Bind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8022, commandInt:0x00] }: // ZDP: Mgmt_Unbind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Unexpected Zigbee message + // --------------------------------------------------------------------------------------------------------------- + default: + log_error "Sent unexpected Zigbee message: description=${description}, msg=${msg}" + } +} + +// =================================================================================================================== +// Logging helpers (something like this should be part of the SDK and not implemented by each driver) +// =================================================================================================================== + +private void log_debug(String message) { + if (logLevel == '1') log.debug "${device.displayName} ${message.uncapitalize()}" +} +private void log_info(String message) { + if (logLevel <= '2') log.info "${device.displayName} ${message.uncapitalize()}" +} +private void log_warn(String message) { + if (logLevel <= '3') log.warn "${device.displayName} ${message.uncapitalize()}" +} +private void log_error(String message) { + log.error "${device.displayName} ${message.uncapitalize()}" +} + +// =================================================================================================================== +// Helper methods (keep them simple, keep them dumb) +// =================================================================================================================== + +private void utils_sendZigbeeCommands(List cmds) { + if (cmds.empty) return + List send = delayBetween(cmds.findAll { !it.startsWith('delay') }, 1000) + log_debug "◀ Sending Zigbee messages: ${send}" + state.lastTx = now() + sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) +} +private void utils_sendEvent(Map event) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { + log_info "${event.descriptionText} [${event.type}]" + } else { + log_debug "${event.descriptionText} [${event.type}]" + } + sendEvent event +} +private void utils_dataValue(String key, String value) { + if (value == null || value == '') return + log_debug "Update data value: ${key}=${value}" + updateDataValue key, value +} +private void utils_zigbeeDataValue(Integer attrInt, String value) { + switch (attrInt) { + case 0x0001: utils_dataValue 'application', value; return + case 0x0003: utils_dataValue 'hwVersion', value; return + case 0x0004: utils_dataValue 'manufacturer', value; return + case 0x000A: utils_dataValue 'type', "${value ? (value.split('') as List).collate(2).collect { "${Integer.parseInt(it.join(), 16) as char}" }.join() : ''}"; return + case 0x0005: utils_dataValue 'model', value; return + case 0x4000: utils_dataValue 'softwareBuild', value; return + } +} +private void utils_processedZclMessage(String type, String details) { + log_debug "▶ Processed ZCL message: type=${type}, ${details}" +} +private void utils_processedZdpMessage(String type, String details) { + log_debug "▶ Processed ZDO message: type=${type}, ${details}" +} +private String utils_payload(String value) { + return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') +} +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} + +// switch/case syntactic sugar +@CompileStatic private boolean contains(Map msg, Map spec) { + return msg.keySet().containsAll(spec.keySet()) && spec.every { it.value == msg[it.key] } +} diff --git a/ikea-zigbee-drivers/Ikea_E1842.groovy b/ikea-zigbee-drivers/Ikea_E1842.groovy new file mode 100644 index 0000000..1d523cc --- /dev/null +++ b/ikea-zigbee-drivers/Ikea_E1842.groovy @@ -0,0 +1,629 @@ +/** + * IKEA Knycklan Water Valve Receiver (E1842) + * + * @see https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/ + */ +import groovy.transform.CompileStatic +import groovy.transform.Field + +@Field static final String DRIVER_NAME = 'IKEA Knycklan Water Valve Receiver (E1842)' +@Field static final String DRIVER_VERSION = '5.0.0' + +// Fields for capability.IAS +import hubitat.zigbee.clusters.iaszone.ZoneStatus + +// Fields for capability.HealthCheck +import groovy.time.TimeCategory + +@Field static final Map HEALTH_CHECK = [ + 'schedule': '0 0 0/1 ? * * *', // Health will be checked using this cron schedule + 'thereshold': '3600' // When checking, mark the device as offline if no Zigbee message was received in the last 3600 seconds +] + +// Fields for capability.ZigbeeGroups +@Field static final Map GROUPS = [ + '9900':'Alfa', '9901':'Bravo', '9902':'Charlie', '9903':'Delta', '9904':'Echo', '9905':'Foxtrot', '9906':'Golf', '9907':'Hotel', '9908':'India', '9909':'Juliett', '990A':'Kilo', '990B':'Lima', '990C':'Mike', '990D':'November', '990E':'Oscar', '990F':'Papa', '9910':'Quebec', '9911':'Romeo', '9912':'Sierra', '9913':'Tango', '9914':'Uniform', '9915':'Victor', '9916':'Whiskey', '9917':'Xray', '9918':'Yankee', '9919':'Zulu' +] + +metadata { + definition(name:DRIVER_NAME, namespace:'dandanache', author:'Dan Danache', importUrl:'https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1842.groovy') { + capability 'Configuration' + capability 'Refresh' + capability 'RelaySwitch' + capability 'Sensor' + capability 'WaterSensor' + capability 'Actuator' + capability 'Switch' + capability 'HealthCheck' + capability 'PowerSource' + + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,1000', outClusters:'0019,0020,1000', model:'KNYCKLAN receiver', manufacturer:'IKEA of Sweden' // Firmware: 2.3.024 (117C-1103-23024631) + + // Attributes for capability.IAS + attribute 'ias', 'enum', ['enrolled', 'not enrolled'] + + // Attributes for capability.HealthCheck + attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] + } + + // Commands for capability.Switch + command 'toggle' + command 'onWithTimedOff', [[name:'On duration*', type:'NUMBER', description:'After how many seconds power will be turned Off [1..6500]']] + + // Commands for capability.FirmwareUpdate + command 'updateFirmware' + + preferences { + input( + name: 'helpInfo', type: 'hidden', + title: ''' +
+ IKEA Knycklan Water Valve Receiver (E1842) v5.0.0
+ +
+ ''' + ) + input( + name: 'logLevel', type: 'enum', + title: 'Log verbosity', + description: 'Select what type of messages appear in the "Logs" section.', + options: ['1':'Debug - log everything', '2':'Info - log important events', '3':'Warning - log events that require attention', '4':'Error - log errors'], + defaultValue: '1', + required: true + ) + + // Inputs for capability.Switch + input( + name: 'powerOnBehavior', + type: 'enum', + title: 'Power On behaviour', + description: 'Select what happens after a power outage.', + options: ['TURN_POWER_ON':'Turn power On', 'TURN_POWER_OFF':'Turn power Off', 'RESTORE_PREVIOUS_STATE':'Restore previous state'], + defaultValue: 'RESTORE_PREVIOUS_STATE', + required: true + ) + + // Inputs for capability.ZigbeeBindings + input( + name: 'joinGroup', type: 'enum', + title: 'Join a Zigbee group', + description: 'Select a Zigbee group you want to join.', + options: ['0000':'❌ Leave all Zigbee groups', '----':'- - - -'] + GROUPS, + defaultValue: '----', + required: false + ) + } +} + +// =================================================================================================================== +// Implement default methods +// =================================================================================================================== + +// Called when the device is first added +void installed() { + log_warn 'Installing device ...' + log_warn '[IMPORTANT] For battery-powered devices, make sure that you keep your device as close as you can (less than 2inch / 5cm) to your Hubitat hub for at least 30 seconds. Otherwise the device will successfully pair but it won\'t work properly!' +} + +// Called when the "Save Preferences" button is clicked +List updated(boolean auto = false) { + log_info "Saving preferences${auto ? ' (auto)' : ''} ..." + List cmds = [] + + unschedule() + + if (logLevel == null) { + logLevel = '1' + device.updateSetting 'logLevel', [value:logLevel, type:'enum'] + } + if (logLevel == '1') runIn 1800, 'logsOff' + log_info "🛠️ logLevel = ${['1':'Debug', '2':'Info', '3':'Warning', '4':'Error'].get(logLevel)}" + + // Preferences for capability.Switch + if (powerOnBehavior == null) { + powerOnBehavior = 'RESTORE_PREVIOUS_STATE' + device.updateSetting 'powerOnBehavior', [value:powerOnBehavior, type:'enum'] + } + log_info "🛠️ powerOnBehavior = ${powerOnBehavior}" + cmds += zigbee.writeAttribute(0x0006, 0x4003, 0x30, powerOnBehavior == 'TURN_POWER_OFF' ? 0x00 : (powerOnBehavior == 'TURN_POWER_ON' ? 0x01 : 0xFF)) + + // Preferences for capability.HealthCheck + schedule HEALTH_CHECK.schedule, 'healthCheck' + + // Preferences for capability.ZigbeeGroups + if (joinGroup != null && joinGroup != '----') { + if (joinGroup == '0000') { + log_info '🛠️ Leaving all Zigbee groups' + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 04}" // Leave all groups + } else { + String groupName = GROUPS.getOrDefault(joinGroup, 'Unknown') + log_info "🛠️ Joining group: ${joinGroup} (${groupName})" + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 00 ${utils_payload joinGroup} ${Integer.toHexString(groupName.length()).padLeft(2, '0')}${groupName.bytes.encodeHex()}}" // Join group + } + device.updateSetting 'joinGroup', [value:'----', type:'enum'] + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + } + + if (auto) return cmds + utils_sendZigbeeCommands cmds + return [] +} + +// =================================================================================================================== +// Capabilities helpers +// =================================================================================================================== + +// Handler method for scheduled job to disable debug logging +void logsOff() { + log_info '⏲️ Automatically reverting log level to "Info"' + device.updateSetting 'logLevel', [value:'2', type:'enum'] +} + +// Helpers for capability.HealthCheck +void healthCheck() { + log_debug '⏲️ Automatically running health check' + String healthStatus = state.lastRx == 0 || state.lastRx == null ? 'unknown' : (now() - state.lastRx < Integer.parseInt(HEALTH_CHECK.thereshold) * 1000 ? 'online' : 'offline') + utils_sendEvent name:'healthStatus', value:healthStatus, type:'physical', descriptionText:"Health status is ${healthStatus}" +} + +// =================================================================================================================== +// Implement Capabilities +// =================================================================================================================== + +// capability.Configuration +// Note: This method is also called when the device is initially installed +void configure(boolean auto = false) { + log_warn "Configuring device${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Configure" button immediately after pushing any button on the device in order to first wake it up!' + } + + // Apply preferences first + List cmds = ["he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000213C00}"] + cmds += updated true + + // Clear data (keep firmwareMT information though) + device.data*.key.each { if (it != 'firmwareMT') device.removeDataValue it } + + // Clear state + state.clear() + state.lastTx = 0 + state.lastRx = 0 + state.lastCx = DRIVER_VERSION + + // Configuration for capability.IAS + Integer ep0500 = 0x01 + cmds += "he wattr 0x${device.deviceNetworkId} ${ep0500} 0x0500 0x0010 0xF0 {${utils_payload "${location.hub.zigbeeEui}"}}" + cmds += "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}" // Zone Enroll Response (0x00): status=Success, zoneId=0x00 + cmds += "zdo bind 0x${device.deviceNetworkId} ${ep0500} 0x01 0x0500 {${device.zigbeeId}} {}" // IAS Zone cluster + cmds += "he cr 0x${device.deviceNetworkId} ${ep0500} 0x0500 0x0002 0x19 0x0000 0x4650 {00} {}" // Report ZoneStatus (map16) at least every 5 hours (Δ = 0) + + // Configuration for capability.Switch + cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster + cmds += "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0006 0x0000 0x10 0x0000 0x0258 {01} {}" // Report OnOff (bool) at least every 10 minutes + + // Configuration for capability.HealthCheck + sendEvent name:'healthStatus', value:'online', descriptionText:'Health status initialized to online' + sendEvent name:'checkInterval', value:3600, unit:'second', descriptionText:'Health check interval is 3600 seconds' + + // Configuration for capability.PowerSource + sendEvent name:'powerSource', value:'unknown', type:'digital', descriptionText:'Power source initialized to unknown' + cmds += zigbee.readAttribute(0x0000, 0x0007) // PowerSource + + // Query Basic cluster attributes + cmds += zigbee.readAttribute(0x0000, [0x0001, 0x0003, 0x0004, 0x4000]) // ApplicationVersion, HWVersion, ManufacturerName, SWBuildID + cmds += zigbee.readAttribute(0x0000, [0x0005]) // ModelIdentifier + cmds += zigbee.readAttribute(0x0000, [0x000A]) // ProductCode + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x01 0x0003 {100002 0000210000}" + utils_sendZigbeeCommands cmds + + log_info 'Configuration done; refreshing device current state in 7 seconds ...' + runIn 7, 'refresh', [data:true] +} +/* groovylint-disable-next-line UnusedPrivateMethod */ +private void autoConfigure() { + log_warn "Detected that this device is not properly configured for this driver version (lastCx != ${DRIVER_VERSION})" + configure true +} + +// capability.Refresh +void refresh(boolean auto = false) { + log_warn "Refreshing device state${auto ? ' (auto)' : ''} ..." + if (!auto && device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Refresh" button immediately after pushing any button on the device in order to first wake it up!' + } + + List cmds = [] + + // Refresh for capability.IAS + Integer ep0500 = 0x01 + cmds += zigbee.readAttribute(0x0500, 0x0000, [destEndpoint: ep0500]) // IAS ZoneState + cmds += zigbee.readAttribute(0x0500, 0x0001, [destEndpoint: ep0500]) // IAS ZoneType + cmds += zigbee.readAttribute(0x0500, 0x0002, [destEndpoint: ep0500]) // IAS ZoneStatus + + // Refresh for capability.Switch + cmds += zigbee.readAttribute(0x0006, 0x0000) // OnOff + cmds += zigbee.readAttribute(0x0006, 0x4003) // PowerOnBehavior + + // Refresh for capability.ZigbeeGroups + cmds += "he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0004 {0143 02 00}" // Get groups membership + utils_sendZigbeeCommands cmds +} + +// Implementation for capability.Switch +void on() { + log_debug 'Sending On command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114301}"]) +} +void off() { + log_debug 'Sending Off command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114300}"]) +} + +void toggle() { + log_debug 'Sending Toggle command' + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114302}"]) +} + +void onWithTimedOff(BigDecimal onTime = 1) { + Integer delay = onTime < 1 ? 1 : (onTime > 6500 ? 6500 : onTime) + log_debug 'Sending OnWithTimedOff command' + Integer dur = delay * 10 + String payload = "00 ${utils_payload dur, 4} 0000" + utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0006 {114342 ${payload}}"]) +} + +// Implementation for capability.HealthCheck +void ping() { + log_warn 'ping ...' + utils_sendZigbeeCommands(zigbee.readAttribute(0x0000, 0x0000)) + log_debug 'Ping command sent to the device; we\'ll wait 5 seconds for a reply ...' + runIn 5, 'pingExecute' +} +void pingExecute() { + if (state.lastRx == 0) { + log_info 'Did not sent any messages since it was last configured' + return + } + + Date now = new Date(Math.round(now() / 1000) * 1000) + Date lastRx = new Date(Math.round(state.lastRx / 1000) * 1000) + String lastRxAgo = TimeCategory.minus(now, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Sent last message at ${lastRx.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${lastRxAgo} ago)" + + Date thereshold = new Date(Math.round(state.lastRx / 1000 + Integer.parseInt(HEALTH_CHECK.thereshold)) * 1000) + String theresholdAgo = TimeCategory.minus(thereshold, lastRx).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received for ${theresholdAgo} (hardcoded)" + + String offlineMarkAgo = TimeCategory.minus(thereshold, now).toString().replace('.000 seconds', ' seconds') + log_info "Will be marked as offline if no message is received until ${thereshold.format('yyyy-MM-dd HH:mm:ss', location.timeZone)} (${offlineMarkAgo} from now)" +} + +// Implementation for capability.FirmwareUpdate +void updateFirmware() { + log_info 'Looking for firmware updates ...' + if (device.currentValue('powerSource', true) == 'battery') { + log_warn '[IMPORTANT] Click the "Update Firmware" button immediately after pushing any button on the device in order to first wake it up!' + } + utils_sendZigbeeCommands zigbee.updateFirmware() +} + +// =================================================================================================================== +// Handle incoming Zigbee messages +// =================================================================================================================== + +void parse(String description) { + log_debug "description=[${description}]" + + // Auto-Configure device: configure() was not called for this driver version + if (state.lastCx != DRIVER_VERSION) { + state.lastCx = DRIVER_VERSION + runInMillis 1500, 'autoConfigure' + } + + // Extract msg + Map msg = [:] + if (description.startsWith('zone status')) msg += [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] + if (description.startsWith('enroll request')) msg += [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] + + msg += zigbee.parseDescriptionAsMap description + if (msg.containsKey('endpoint')) msg.endpointInt = Integer.parseInt(msg.endpoint, 16) + if (msg.containsKey('sourceEndpoint')) msg.endpointInt = Integer.parseInt(msg.sourceEndpoint, 16) + if (msg.containsKey('cluster')) msg.clusterInt = Integer.parseInt(msg.cluster, 16) + if (msg.containsKey('command')) msg.commandInt = Integer.parseInt(msg.command, 16) + log_debug "msg=[${msg}]" + + state.lastRx = now() + + // Parse for capability.HealthCheck + if (device.currentValue('healthStatus', true) != 'online') { + utils_sendEvent name:'healthStatus', value:'online', type:'digital', descriptionText:'Health status changed to online' + } + + // If we sent a Zigbee command in the last 3 seconds, we assume that this Zigbee event is a consequence of this driver doing something + // Therefore, we mark this event as "digital" + String type = state.containsKey('lastTx') && (now() - state.lastTx < 3000) ? 'digital' : 'physical' + + switch (msg) { + + // Events for devices.Ikea_E1842 + // =================================================================================================================== + + // Report/Read Attributes Reponse: ZoneStatus + case { contains it, [clusterInt:0x0500, commandInt:0x0A, attrInt:0x0002] }: + case { contains it, [clusterInt:0x0500, commandInt:0x01, attrInt:0x0002] }: + String water = msg.value[-1] == '1' ? 'wet' : 'dry' + utils_sendEvent name:'water', value:water, descriptionText:"Is ${water}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ZoneStatus=${msg.value}" + return + + // Ignore Configure Reporting Response for attribute ZoneStatus + case { contains it, [clusterInt:0x0500, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=ZoneStatus, data=${msg.data}" + return + + // Events for capability.IAS + // =================================================================================================================== + + // Zone Status Change Notification + case { contains it, [clusterInt:0x500, commandInt:0x00, isClusterSpecific:true] }: + ZoneStatus zs = zigbee.parseZoneStatus(description) + boolean alarm1 = zs.alarm1Set + boolean alarm2 = zs.alarm2Set + boolean tamper = zs.tamperSet + boolean lowBattery = zs.batterySet + boolean supervisionReports = zs.supervisionReportsSet + boolean restoreReports = zs.restoreReportsSet + boolean trouble = zs.troubleSet + boolean mainsFault = zs.acSet + boolean testMode = zs.testSet + boolean batteryDefect = zs.batteryDefectSet + utils_processedZclMessage 'Zone Status Change Notification', "alarm1=${alarm1} alarm2=${alarm2} tamper=${tamper} lowBattery=${lowBattery} supervisionReports=${supervisionReports} restoreReports=${restoreReports} trouble=${trouble} mainsFault=${mainsFault} testMode=${testMode} batteryDefect=${batteryDefect}" + return + + // Enroll Request + case { contains it, [clusterInt:0x500, commandInt:0x01, isClusterSpecific:true] }: + Integer ep0500 = 0x01 + utils_sendZigbeeCommands([ + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 00 00 00}", // Zone Enroll Response (0x00): status=Success, zoneId=0x00 + "he raw 0x${device.deviceNetworkId} 0x01 ${ep0500} 0x0500 {01 23 01}", // Initiate Normal Operation Mode (0x01): no_payload + ]) + utils_processedZclMessage 'Enroll Request', "description=${description}" + return + + // Read Attributes: ZoneState + case { contains it, [clusterInt:0x0500, commandInt:0x01, attrInt:0x0000] }: + String status = msg.value == '01' ? 'enrolled' : 'not enrolled' + utils_sendEvent name:'ias', value:status, descriptionText:"Device IAS status is ${status}", type:'digital' + utils_processedZclMessage 'Read Attributes Response', "ZoneState=${msg.value == '01' ? 'enrolled' : 'not_enrolled'}" + return + + // Read Attributes: ZoneType + case { contains it, [clusterInt:0x0500, commandInt:0x01, attrInt:0x0001] }: + utils_processedZclMessage 'Read Attributes Response', "ZoneType=${msg.value}" + return + + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0500, commandInt:0x04, isClusterSpecific:false] }: + utils_processedZclMessage 'Write Attribute Response', "attribute=IAS_CIE_Address, ZoneType=${msg.data}" + return + + // Events for capability.Switch + // =================================================================================================================== + + // Report/Read Attributes: OnOff + case { contains it, [clusterInt:0x0006, commandInt:0x0A, attrInt:0x0000] }: + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x0000] }: + String newState = msg.value == '00' ? 'off' : 'on' + utils_sendEvent name:'switch', value:newState, descriptionText:"Was turned ${newState}", type:type + + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "OnOff=${newState}" + return + + // Read Attributes Response: powerOnBehavior + case { contains it, [clusterInt:0x0006, commandInt:0x01, attrInt:0x4003] }: + String newValue = '' + switch (Integer.parseInt(msg.value, 16)) { + case 0x00: newValue = 'TURN_POWER_OFF'; break + case 0x01: newValue = 'TURN_POWER_ON'; break + case 0xFF: newValue = 'RESTORE_PREVIOUS_STATE'; break + default: log_warn "Received unexpected attribute value: PowerOnBehavior=${msg.value}"; return + } + powerOnBehavior = newValue + device.updateSetting 'powerOnBehavior', [value:newValue, type:'enum'] + utils_processedZclMessage 'Read Attributes Response', "PowerOnBehavior=${newValue}" + return + + // Other events that we expect but are not usefull + case { contains it, [clusterInt:0x0006, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=OnOff, data=${msg.data}" + return + case { contains it, [clusterInt:0x0006, commandInt:0x04] }: // Write Attribute Response + case { contains it, [clusterInt:0x0006, commandInt:0x06, isClusterSpecific:false, direction:'01'] }: // Configure Reporting Command + return + + // Events for capability.HealthCheck + // =================================================================================================================== + + case { contains it, [clusterInt:0x0000, attrInt:0x0000] }: + log_warn '... pong' + return + + // Configuration for capability.PowerSource + // =================================================================================================================== + + // Read Attributes Reponse: PowerSource + case { contains it, [clusterInt:0x0000, commandInt:0x01, attrInt:0x0007] }: + String powerSource = 'unknown' + + // PowerSource := { 0x00:Unknown, 0x01:MainsSinglePhase, 0x02:MainsThreePhase, 0x03:Battery, 0x04:DC, 0x05:EmergencyMainsConstantlyPowered, 0x06:EmergencyMainsAndTransferSwitch } + switch (msg.value) { + case ['01', '02', '05', '06']: + powerSource = 'mains'; break + case '03': + powerSource = 'battery'; break + case '04': + powerSource = 'dc' + } + utils_sendEvent name:'powerSource', value:powerSource, type:'digital', descriptionText:"Power source is ${powerSource}" + utils_processedZclMessage 'Read Attributes Response', "PowerSource=${msg.value}" + return + + // Events for capability.ZigbeeGroups + // =================================================================================================================== + + // Get Group Membership Response Command + case { contains it, [clusterInt:0x0004, commandInt:0x02, direction:'01'] }: + Integer count = Integer.parseInt msg.data[1], 16 + Set groupNames = [] + for (int pos = 0; pos < count; pos++) { + String groupId = "${msg.data[pos * 2 + 3]}${msg.data[pos * 2 + 2]}" + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" + log_debug "Found group membership: ${groupName}" + groupNames.add groupName + } + state.joinGrp = groupNames + if (state.joinGrp.size() == 0) state.remove 'joinGrp' + log_info "Current group membership: ${groupNames ?: 'None'}" + return + + // Add Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x00, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8A' ? 'ALREADY_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" + utils_processedZclMessage 'Add Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // Leave Group Response + case { contains it, [clusterInt:0x0004, commandInt:0x03, direction:'01'] }: + String status = msg.data[0] == '00' ? 'SUCCESS' : (msg.data[0] == '8B' ? 'NOT_A_MEMBER' : 'FAILED') + String groupId = "${msg.data[2]}${msg.data[1]}" + String groupName = GROUPS.containsKey(groupId) ? "${GROUPS.get(groupId)}" : "0x${groupId}" + utils_processedZclMessage 'Left Group Response', "Status=${status}, groupId=${groupId}, groupName=${groupName}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Handle common messages (e.g.: received during pairing when we query the device for information) + // --------------------------------------------------------------------------------------------------------------- + + // Device_annce: Welcome back! let's sync state. + case { contains it, [endpointInt:0x00, clusterInt:0x0013, commandInt:0x00] }: + log_warn 'Rejoined the Zigbee mesh; refreshing device state in 3 seconds ...' + runIn 3, 'refresh' + return + + // Report/Read Attributes Response (Basic cluster) + case { contains it, [clusterInt:0x0000, commandInt:0x01] }: + case { contains it, [clusterInt:0x0000, commandInt:0x0A] }: + utils_zigbeeDataValue(msg.attrInt, msg.value) + msg.additionalAttrs?.each { utils_zigbeeDataValue(it.attrInt, it.value) } + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "cluster=0x${msg.cluster}, attribute=0x${msg.attrId}, value=${msg.value}" + return + + // Mgmt_Leave_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8034, commandInt:0x00] }: + log_warn 'Device is leaving the Zigbee mesh. See you later, Aligator!' + return + + // Ignore the following Zigbee messages + case { contains it, [commandInt:0x0A, isClusterSpecific:false] }: // ZCL: Attribute report we don't care about (configured by other driver) + case { contains it, [commandInt:0x0B, isClusterSpecific:false] }: // ZCL: Default Response + case { contains it, [clusterInt:0x0003, commandInt:0x01] }: // ZCL: Identify Query Command + case { contains it, [clusterInt:0x0003, commandInt:0x04] }: // ZCL: Write Attribute Response (IdentifyTime) + utils_processedZclMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + case { contains it, [endpointInt:0x00, clusterInt:0x8001, commandInt:0x00] }: // ZDP: IEEE_addr_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8004, commandInt:0x00] }: // ZDP: Simple_Desc_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8005, commandInt:0x00] }: // ZDP: Active_EP_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x0006, commandInt:0x00] }: // ZDP: MatchDescriptorRequest + case { contains it, [endpointInt:0x00, clusterInt:0x801F, commandInt:0x00] }: // ZDP: Parent_annce_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8021, commandInt:0x00] }: // ZDP: Mgmt_Bind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8022, commandInt:0x00] }: // ZDP: Mgmt_Unbind_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8031, commandInt:0x00] }: // ZDP: Mgmt_LQI_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8032, commandInt:0x00] }: // ZDP: Mgmt_Rtg_rsp + case { contains it, [endpointInt:0x00, clusterInt:0x8038, commandInt:0x00] }: // ZDP: Mgmt_NWK_Update_notify + utils_processedZdpMessage 'Ignored', "endpoint=0x${msg.sourceEndpoint ?: msg.endpoint}, manufacturer=0x${msg.manufacturerId ?: '0000'}, cluster=0x${msg.clusterId ?: msg.cluster}, command=0x${msg.command}, data=${msg.data}" + return + + // --------------------------------------------------------------------------------------------------------------- + // Unexpected Zigbee message + // --------------------------------------------------------------------------------------------------------------- + default: + log_error "Sent unexpected Zigbee message: description=${description}, msg=${msg}" + } +} + +// =================================================================================================================== +// Logging helpers (something like this should be part of the SDK and not implemented by each driver) +// =================================================================================================================== + +private void log_debug(String message) { + if (logLevel == '1') log.debug "${device.displayName} ${message.uncapitalize()}" +} +private void log_info(String message) { + if (logLevel <= '2') log.info "${device.displayName} ${message.uncapitalize()}" +} +private void log_warn(String message) { + if (logLevel <= '3') log.warn "${device.displayName} ${message.uncapitalize()}" +} +private void log_error(String message) { + log.error "${device.displayName} ${message.uncapitalize()}" +} + +// =================================================================================================================== +// Helper methods (keep them simple, keep them dumb) +// =================================================================================================================== + +private void utils_sendZigbeeCommands(List cmds) { + if (cmds.empty) return + List send = delayBetween(cmds.findAll { !it.startsWith('delay') }, 1000) + log_debug "◀ Sending Zigbee messages: ${send}" + state.lastTx = now() + sendHubCommand new hubitat.device.HubMultiAction(send, hubitat.device.Protocol.ZIGBEE) +} +private void utils_sendEvent(Map event) { + boolean noInfo = event.remove('noInfo') == true + if (!noInfo && (device.currentValue(event.name, true) != event.value || event.isStateChange)) { + log_info "${event.descriptionText} [${event.type}]" + } else { + log_debug "${event.descriptionText} [${event.type}]" + } + sendEvent event +} +private void utils_dataValue(String key, String value) { + if (value == null || value == '') return + log_debug "Update data value: ${key}=${value}" + updateDataValue key, value +} +private void utils_zigbeeDataValue(Integer attrInt, String value) { + switch (attrInt) { + case 0x0001: utils_dataValue 'application', value; return + case 0x0003: utils_dataValue 'hwVersion', value; return + case 0x0004: utils_dataValue 'manufacturer', value; return + case 0x000A: utils_dataValue 'type', "${value ? (value.split('') as List).collate(2).collect { "${Integer.parseInt(it.join(), 16) as char}" }.join() : ''}"; return + case 0x0005: utils_dataValue 'model', value; return + case 0x4000: utils_dataValue 'softwareBuild', value; return + } +} +private void utils_processedZclMessage(String type, String details) { + log_debug "▶ Processed ZCL message: type=${type}, ${details}" +} +private void utils_processedZdpMessage(String type, String details) { + log_debug "▶ Processed ZDO message: type=${type}, ${details}" +} +private String utils_payload(String value) { + return value.replace('0x', '').split('(?<=\\G.{2})').reverse().join('') +} +private String utils_payload(Integer value, Integer size = 4) { + return utils_payload(Integer.toHexString(value).padLeft(size, '0')) +} + +// switch/case syntactic sugar +@CompileStatic private boolean contains(Map msg, Map spec) { + return msg.keySet().containsAll(spec.keySet()) && spec.every { it.value == msg[it.key] } +} diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index ee99091..e02dbec 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -268,7 +268,7 @@ void parse(String description) { switch (msg) { - // Events for devices.E2202 + // Events for devices.Ikea_E2202 // =================================================================================================================== // Report/Read Attributes Reponse: ZoneStatus diff --git a/ikea-zigbee-drivers/Ikea_WS-Light.groovy b/ikea-zigbee-drivers/Ikea_WS-Light.groovy index f916a60..7c208f5 100644 --- a/ikea-zigbee-drivers/Ikea_WS-Light.groovy +++ b/ikea-zigbee-drivers/Ikea_WS-Light.groovy @@ -40,8 +40,10 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRI bulb E14 WS globe 470lm', manufacturer:'IKEA of Sweden' // LED2101G4: 1.1.003 (117C-2204-00011003) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WScandleopal470lm', manufacturer:'IKEA of Sweden' // LED1949C5: 1.1.003 (117C-2204-00011003) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WSglobeopal470lm', manufacturer:'IKEA of Sweden' // LED2002G5: 1.0.012 (117C-2205-00010012) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE27WSglobeopal1055lm', manufacturer:'IKEA of Sweden' // LED2003G10: 1.0.012 (117C-2205-00010012) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbGU10WS345lm', manufacturer:'IKEA of Sweden' // LED2005R5: 1.0.012 (117C-2205-00010012) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WS globe 1055lm', manufacturer:'IKEA of Sweden' // LED2201G8: 3.0.10 (117C-2206-03000010) + fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E14 WS 470lm', manufacturer:'IKEA of Sweden' // LED1835C6: 2.3.087 (117C-4205-23087631) // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] @@ -454,6 +456,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) + /* groovylint-disable-next-line UnnecessarySetter */ if (level > 0 && duration == 0) setLevel level } void startColorTemperatureChange(String direction) { diff --git a/ikea-zigbee-drivers/README.md b/ikea-zigbee-drivers/README.md index c06208b..66c3654 100644 --- a/ikea-zigbee-drivers/README.md +++ b/ikea-zigbee-drivers/README.md @@ -10,6 +10,7 @@ Lights: * [RGB-Only Light](#rgb-only-light) Remotes: +* [Knycklan Water Valve Remote (E1841)](#knycklan-water-valve-remote-e1841) * [Rodret Dimmer (E2201)](#rodret-dimmer-e2201) * [Somrig Shortcut Button (E2213)](#somrig-shortcut-button-e2213) * [Styrbar Remote Control N2 (E2002)](#styrbar-remote-control-n2-e2002) @@ -32,6 +33,7 @@ Outlets: * [Tretakt Smart Plug (E2204)](#tretakt-smart-plug-e2204) Appliances: +* [Knycklan Water Valve Receiver (E1842)](#knycklan-water-valve-receiver-e1842) * [Starkvind Air Purifier (E2006)](#starkvind-air-purifier-e2006) Devices from other vendors (not in HPM): @@ -97,16 +99,19 @@ Also includes the 10W and 30W LED drivers. * **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. #### Tested devices -| Type | Name | -|--------------|---------------------------------| -| LED2103G5 | Tradfri Bulb E27 WW Globe 806lm | -| LED2104R3 | Tradfri Bulb GU10 WW 345lm | -| LED1623G12 | Tradfri Bulb E27 Opal 1000lm | -| LED1842G3 | Tradfri Bulb E27 WW Clear 250lm | -| LED1836G9 | Tradfri Bulb E27 WW 806lm | -| 10EU-IL-1 | Tradfri LED Driver (10W) | -| 30-IL44-1 | Silverglans LED Driver (30W) | -| 30EU-IL-2 | Tradfri LED Driver (30W) | +| Type | Name | +|----------------|---------------------------------| +| LED2103G5 | Tradfri Bulb E27 WW Globe 806lm | +| LED2104R3 | Tradfri Bulb GU10 WW 345lm | +| LED1623G12 | Tradfri Bulb E27 Opal 1000lm | +| LED1649C5E14EU | Tradfri Bulb E14 W op/ch 400lm | +| LED1934G3 | Tradfri Bulb E27 WW Clear 250lm | +| LED1837R5 | Tradfri Bulb GU10 WW 400lm | +| LED1842G3 | Tradfri Bulb E27 WW Clear 250lm | +| LED1836G9 | Tradfri Bulb E27 WW 806lm | +| 10EU-IL-1 | Tradfri LED Driver (10W) | +| 30-IL44-1 | Silverglans LED Driver (30W) | +| 30EU-IL-2 | Tradfri LED Driver (30W) | #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. @@ -151,8 +156,10 @@ These products come equipped with the following features: | LED2101G4 | Tradfri Bulb E14 WS Globe 470lm | | LED1949C5 | Tradfri Bulb E14 WS Candle Opal 470lm | | LED2002G5 | Tradfri Bulb E14 WS Globe Opal 470lm | +| LED2003G10 | Tradfri Bulb E27 WS Globe Opal 1055lm | | LED2005R5 | Tradfri Bulb GU10 WS 345lm | | LED2201G8 | Tradfri Bulb E27 WS Globe 1055lm | +| LED1835C6 | Tradfri Bulb E14 WS 470lm | #### Pairing Instructions 1. If the device is already powered on, power it off for 20 seconds (power-cycle) before each pairing attempt. @@ -254,6 +261,32 @@ Bulbs of this type lack native support for color temperature control. Their hard ## Remotes Below you can find the details of each remote device, including the features and pairing instructions. +### Knycklan Water Valve Remote (E1841) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `204.257.24` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1841.groovy` | +| Tested firmwares | `2.3.019` | +| Since version | `5.0.0` | + +#### Features +* **Button Events**: Supports "Push", "Hold", and "Release" events for both buttons. +* **Battery Report**: Provides a percentage-based report on the current battery level. +* **Health Status**: Indicates the operational status of the device, showing whether it's "online" or "offline". +* **Zigbee Device Control**: Enables direct control of On/Off and Brightness settings for Zigbee devices. + +#### Pairing Instructions +1. Using a small screwdriver, open the battery compartiment and you should see the small pair button (🔗). +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. **Important**: Move closer to your Hubitat hub and press the pair button in the battery compartment **four times within five seconds**. +1. **Important**: Immediately after the device LED starts blinking red, position the device as close as possible to your Hubitat hub for **at least 30 seconds** (wait until the LED stops blinking and turns off). +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. Close the device battery compartiment. +1. You're all set! Enjoy using your Knycklan Water Valve Remote. + + ### Rodret Dimmer (E2201) | Parameter | Details | @@ -707,6 +740,30 @@ Below you can find the details of each outlet device, including the features and ## Appliances Below you can find the details of each appliance device, including the features and pairing instructions. +### Knycklan Water Valve Receiver (E1842) + +| Parameter | Details | +|-----------|-------------| +| Product Image | | +| Product Code | `204.257.24` | +| Manual install file | `https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1842.groovy` | +| Tested firmwares | `2.3.024` | +| Since version | `5.0.0` | + +#### Features +* **Command Controls**: Includes "On", "Off", "Toggle", and "On with Timed Off" commands. +* **Power Outage Configuration**: Configures the state after a power outage (options include "Power On", "Power Off", and "Restore previous state"). +* **Health Status**: Indicates whether the device is online or offline. +* **Device State Refresh**: Refreshes the device state on demand for real-time status updates. +* **Zigbee Group Membership**: The device can be a member of Zigbee groups. This allows for efficient management of multiple devices. + +#### Pairing Instructions +1. In the Hubitat interface, navigate to **Devices**, click **Add Device** in the top right corner, select **Zigbee**, and then click **Start Zigbee Pairing**. +1. Press and hold the On and Off buttons simultaneously for 5 seconds. +1. Return to the pairing page, provide a name for your device, and assign it to a room. +1. You're all set! Enjoy using your Knycklan Water Valve Receiver. + + ### Starkvind Air Purifier (E2006) | Parameter | Details | @@ -737,7 +794,6 @@ Below you can find the details of each appliance device, including the features 1. Put back the lid and fix it in place. 1. You're all set! Enjoy using your Starkvind Air Purifier. - ## Devices from other vendors Below you can find the details of each device, including the features and pairing instructions. diff --git a/ikea-zigbee-drivers/img/Ikea_E1841.webp b/ikea-zigbee-drivers/img/Ikea_E1841.webp new file mode 100644 index 0000000000000000000000000000000000000000..66d2d823bba94c2a9b49e9980fe0baa0df468343 GIT binary patch literal 7132 zcmV<28zbaWNk&H08vp=TMM6+kP&il$0000G0002<0RZy>06|PpNSzP>009|=ZQC|* z(p~-kaCa|==>G&rLi^562!bu06+6)f<5#?bQjS8{arY9E<^V%j%sC3$wvpPb{7G;3 zB8Z3ykYuQy(YrDDFgdwMJ=f_6=Q;E_INJbbta6u>eMfC1Nm9CI5B`W3L%cR(0w%!I zw(V_O+qU(A0GLUM%8pHTZ#j&dbFPk`-*R+wkYpuF%mg?>Nl-T%zfzBg37{=Wk|Ie8 zhlr@GY8u`>X1@RbJDjC1sU8ryhlrQ}-;yL*b|l-DXatZ||Np1ecL2jp9tN_CbUd~7 zH6kWJvTfUGBdP!Yhq`CX%n)XdC0VTZo*IUhD|roTMZ^T`{Qduf|NozONRlntHskML zFyaad`9_fF=hs2}V?O!hcY~-XJ{?2`5C((;oS*oO|Ky@5+OjNV^)wF0!*1CxBZ&b2 zR{V8^FAgAr;_dnEPk%!dA-_XJR1uN+()q{Z=qTb*&wTl$;z987q$r{~;xjs;it3Yy z0;2eH`u(aQNm)IOY3|Y2r^9YHS9Morre%vjAb>z52;p}cJ!cS*KP(6!ASjNC2%<8M z2m-3WAc)W7I5Xp@bLP{>`E(xV@8{vnIM3>wab_|h5h6hT9RQJtkPvdY{2<$svz%4S zba;5UJ1(O;tJ0Pzl86g0AR;1y42q%-;xm4W&xAxoh>)WXK6)3t58g-bLQ0Twis{1A zfRHX_R#h*DtNlFBhk041sk*yLqKJ4N7NUYgM2JWT8FF+XxZQHSldX**Ts*`ubZ!`i z&UI!i%Y4{h9Z$P4cglJv;u%atgqVWU$JJnbXp9fuUnb1db@pS?Xwmo?A+p%xd3itH!^LYx*HnBU8elu|D10UW0 zsf?ldX72H`Q&+DEz4IrdTWH2D=k&*cQwPkG2hZmjH0Dz8ybzjn-8sDH z8+RM`M$ab_nssUnp9kh$X7wK*uAq50oo-v#?kscgsBb|N&&=M5#qTC!4M)BK&Ai5g zfu-L?y4m&(Xzq1)?U|3!gArN&^w<3rrPPoR=_(xPEjQ_&(6>}ZS-^kxM~OA+RnAl{SsM< ze!q1cV+RlCfURNkvh7&+0SI&v?U$M9lz#MJ1^Alf%R}l?a6gd89G!1du?OAzSAe>NO*8eZQ9r>QYTRU%a;^I_ zKprL*nQC-+8X^x+?NhFrGteaVaBUfvin-%!Kwp_&#cFB2qb>fB>SL~!)6*pWaJJYK zO0~Yb0t}YdiAtny zH6D?pfvJdRXLv+=4-G{eP2v%F_boLHy8uMC4qZip5R>$t$CWVqx0u8ORXR&dVs4u$ zVVMyofjQtxI5bBky~9+{93oMP`CJ8VNI+#C7%J#ihD*Mka|OH|;!@sRuVaHtKE3rC zoCul5-71U>mr#4HBEhBb;uRP)$ELB40-3Z1jT3=RIv?W_kl9nNLEw{lJ}bi}m2^HQ z#wNCTJ{O%-xdLy4O{~P(xE*6tWb$l`XXxZw0n72J&qpy%sI3FQUp%Yi2Re;VAU*Tw+jSzgo3bYEL^#1|j-^8vLaG$uS zUkjBht+3Ido`uTFybQ7mjP=l36$|H_BGBQuUo0TU_k3ro+saBw`M%FWsigx!A!R;JQ1&1w zv~(UtDP>Mk)aKEYC6ZEFZ;zm;oezqt^Ky!^_CZi;>r7FnH4qes+63)mN;Gw7LsGVy zwnkHwnc|vuHP$SmVSOFXooVBl$Pccg_L;^lvDdUMQQ6nP)O@7sXA{bC~cj$3I2Q# z9FOLvxRaDMiVAfIinKTrw6=z#&c~F1o;;terX5=pMba9AN+`n=b)_^(5&i`c2yU37 zO^Ko;mZm5$B_JrNGd&9_0X>1VnV|J5krdK~o`jTOf|hnun9@K{(t_z(>OfCOYLiqu zDIqm_LVMG*(4!}`1WeCDX{IQq#?X`0VS?6ln4&b48G1rn!}RQ2Ev6_e0YNb-%@ied zpeH7^30m4o77AOel%&Q=+aeofO3B6%7Fz1bLNTea(YBH-9ATlQo@~@yH8$$&ZQEZ2^VUtISb{(w-Ph^ zG{aSL#*MmH?rNR0P>(w$W;W`g>^U2Cx0sc>YGYLr_9(H$jngY&e9>VQ(5Xe%csasg|RbG>?BLEJ9L4Vsgy$B z8CV0{lrpw6;n}H3b=ZA4W^Rfs6%LV^vdC2+ZUZk>X&5Mwxv5B2Ed@X^Q@+kE6|^N@ zT2iV)D-$oRJW$ar0xPAfjuP#6!%E3AP{I#1CzWyRC=vIk%t+~$=}LgyL{3^;pk_OC zj1(_BinyuaBe|IciiEl5qingg6!E)VVxwec5Jf~Y_G}bS2cT;ERkP9dHc-Zn6c?4@ z)>203Pdp^rEOl5f>joT$!mE)&pr5!Xl+C|*3JE&)Q7F1#nnIPa>1b@P7OH-->vt{7V{QUsbmT>B#&vvr56)WJt*RUu2+y&K%Gc>U0_Di6g z5ch242`432u7rV0acBN%2kk&}rO0zzN1N>CqP-uGM{T@Wy=Qb!M3!1Q(#YBS4(w~T;1p?A9|+qUnQlxSW%9|V z9_()xY8t_q2P`15UV2lbzHIEIWLe%Z%{8oe_U zLHho?9kv9lLC-*sJbiyRVq2YYpkqgW_VtL3<^I^`xG_C^KCm@lbniQ;kvBhGS8T6+ z)@6vnez+U4#m<4n@FK5nD>j*1fkm+*Pxp;&0;;==;DmQ~ow3zaH%WyN=>4SFZbF2t z@Bz$JY&n4v2sS`dF}A%bBq6wfpcGqQQI|?opcofWGgw*SL7~6?#w7rao#4F~wsE-Adt(If3CTp|!@@hL^P%EpdioL)x zwWcasUc6imEvTJ^EHmHHb*+g?)>p5t`YzPcyhphhkBi*(d_ee8kIN4 z(R^WvH_Pe8g*LLGhKnSLZa=*L>6g!MPy5L>UqnO}-%dYXRK0*gT^Dg2&im8dci-M! zpZ2?vX&WyXdAeQSUVTmm9TV!kh{$P-dAGa$>WkasZW&!RMN%U5a!};Say9!n6WzW6 z-;Av8F?RF3+Z_+fn7XT`PG_b}^SvMlJiE#)-bBmEmxXcQ>+lWgw4JsnIWz5A=6PO5 zyQa}8S$;1`;++8m9us(Oh^&a$;c7Zvlh)9NZ(@?7#CNM&pycl9oz>|~Q}47ziKO01 z5|4uj03=9&ctYiQp2eHZd^(Bs?$9^jTOOoHkt7I^1PBr$_1MZB^O&a8yKS+%Bw3 zblMgrXQuVol0=atfCvNvz?Fo&g6gP0Q5?^VltL7Fn&qM_%c6`oaS4oDt+u5>^Nq(M zNm&#Ll7y^Oijuo3tGcJrmLyV=-{0>mR|2^b@+1nV;CN=#VRA%=(V273=jYdvl_-j| z(CGg&?)bjV0RagTAVCm>D3SmHl74@??zcNvxbOQ4fI1G6kT8x1F781H1ac+!k6(V= zlu+HU4GNfw>q-KFB;i{Tc@A8;@9T!}fB*mB|G)qLZw&xeP&gnE3jhGnjsTqjD)a&L z0X~sPpG&2rqa`HL*r2cy32AQlH^Bnxf${nl}3DprBA390|zQG?H8VC;;GYkFBpgfNnR?YoMT#n=27ft znu+Uup{}{C2SG__s?{xW*}( zGvomM2BYEW8QVW3y{zp>T2tuy#>xlV&yp~0kL7-kBP%}exc|W+h&ydZ_(J;&4u7j{ z&ycjMVaTH$N}Vgldp_{Fk!Ri)BS-F5q(((`&kK>CA!$@+$XZx5MHt}fGHdl-&;RMP z183eBBR)dp#v@=GeB9hjx6BK0&u88jAf`Obct^ju1FfCqLX%G#@TGn2LDYMZVVvNp zvM?ti)KZT@OGBMiz=B!tcFkfPPd@{enm=4irqusK3D{0+J$iiGE{6%jvqdWWs6>2) zrBR&?No?6DfZ6wj$j^|puxN@gy%h<^-WMY|(Djru183eBhKf21 zhNP63OQ=u6~X)I;*=tE{$2JXriVKtk7N7Tc}udZrs;DxvSnL@$kJP6+m_I=-! z;h=T0|45*BY=mX?s-4-Q_UC-N`(M1#qv@fW*75fsDL~dE3>K)1dDQ%;uR-ZDu(NGx%caxovPtMfyX6wWYVR+Wcwigh zas5B^g<`sgO2~_DiDJOlGtom@{zIts0_Z;g@fFQFRN2>-DpcZ*W-W5L#t&1Gt?Xae zcasKtKhz-X)nCC< z{iEuDW(B>Ny(K8+eRHWs2Ax_~kv^7dQNQa!QNZ3Z>5WKK^ag!*yzXnfMC$whFej)n zCk{pXnF9Nh6-lThpw4$98Mnf}!H=4`&J_*=T>e$Z%5?v{;@F<#!8=qCEw+9M^1`Ff z^BRyuJF^pfCX-=&L<%23jJf%-9{bk`0blF~RHmCm*ZE*qgwz^fa7hXS3RI%H0eR#$oVT?$ zk>=lutt}rgc7g@W<{j*PmYZVT@769bQQTQ0CN10DiSgqd-am0AUa-ElB(ccs2r+*z z5ULm7cf?455J~GUdzia;iQI$>ZmqiacE=Mm;POvCLqU3uBtH&lwcSgXPb1*w zKk|Z$M=DUKA;G?_K|Icj6ZEpBmpR?JDNt8w<4m zAL9O5T|~8CGZ28+#z5t@AF!4e=<1N4ASDA)H`UrfLncZp`6pgk^S zCWt@|92gUSKowXe`M@HC#8fMvEi{iukw5?j;WuC%Y5`pc%c&-YRcR-~qz3XjZIEPv zxV35KG41hJNZwhP{S%Gk*qTtg^>$CxnMuAKSA2L`nDCH`M=S!WRMe|k4O5?V=$U8` z&P6rpkD_tO_$+wRX{1{26Av&|z56|wh!A)Znw1{Q2$W;=S@a~ePn4xTJ+*Lg{DuzK z2;mH{A&8k>ok+Rc2ofP9x@PDL{Dydvl8OP@fgJ}D{vN=bBkJtTJ)odCm5aYISJgrT z^}mV2wIt2oNTOlXwY{IP|Bsi^({YWHhOgCiPr zCknpEDSpn#P;9A%boa*se~Ih~SxHbDQL(|}LqPS|+CfE~VjOCnRK;rosH|fyokGHS zb94=FkRh(a>T)}4@|2ok!L}G&#nsi{VL{OjD+cYcQWE&K18f18?Yy?_)GR)gaN%5t z=6kY(+fe)e;eN29ve!CxeD@|k4I=JJN6)G^Vw=Yfd3uUPP>QbHks5eP6xKM(iADBs zax`TEC!iFF>MXph%TMWqE(Z7;gGZWg z{f`c4z=?XpM&xo*lUN;jRCJ%|6OL%;0+p&A6M*-fePCKR5@~*c%4-Rz6OC9&iy)%E z=fAo&CJG1U&(VrRj!nnPOufpY(Dn1XuxWq$1#tpw?v6Uy+~+NACe~xbXSD>25SmLr z$sxHHYen-4B5Bs5Kl#rc1@C;P*S6R3A`n&!;!I)iI^x>35(!GB+b9%?m!v(qECK@G zcFwQ9V2>zU=FBdCJY3C5=b-e~JTL@-Y`g!?W=d7ZA!wj2RN6(K#|x7}sP5v*lbU`2 z1vg9dA6&hY0Km)?ZbU5P%A?y|{ArrE^LPA*L9%t=n7<$%g&JG{4gdW6PX+&-Dq^7k zv;q>+Y`(b%=3><|e&w6Sg{+o6CFcS4#l}W9Q&wrq9tZHhCb&~oLXR38E?!(+kb@y` zGt4`3J**B+lK)kp@f86A7v3UKJfS~7Sb`IoH|lLNqo4SP53>aKl8NOB-uEt-{g4Dk z)?8cI_WvEf^g#yOKl%@U|6dFL*O&kQU1$VX=#T`}Q7{_62?$3}_HITMpS+G~tDTjF;*6#OW2JRrpKkOr{6L%;=hJ(i2% z-KSp9sqL=74$H=-#PY7yZ+>h)zf17*UE#BIOkX2Ti@z@wxTA~&g<&0N>8FUlQ+8+` S>%$II7ybYs0KkL*0001$5Z2iM literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/img/Ikea_E1842.webp b/ikea-zigbee-drivers/img/Ikea_E1842.webp new file mode 100644 index 0000000000000000000000000000000000000000..080378daedd47418dd750a88b495fdb841fd6118 GIT binary patch literal 9894 zcmX|nWl$V#)a)(}fxzPK?(VucEbi_W+%>odTY_6~hXBFdB{&3kmyqCYH}Ch|d#h%C z%+#6FJ$<^Lr)p}{RwSpeN-aB43`(53F{(yi@ zJJak;d1d>NnbaVh&(>RzCx#Kl{^~S&LE&oNrOCGm6dgU=Vo#pFM>87emFk<}V49zW(!Z4htF*f$~MTfy_os z)CYe;yVyA6NR)iM&dCjp6@%}}(aBcFpF2z83YaDB6`v8Z6nK*XS^) z|MG0=g1J2r@fUeo;{};Vey>z*QGb^nPyY=UP3F%Bf4eTYaD5_pxii(79rg4vkLblA z_a-hNC(7^M=3OJ| zLii>0{7zo!SOHVH%3I;HE;ZML3zD+)+M|tuW$CT;CHCb&R}Zy(+-(3z`9}~tAq@u< z`9M=5=xC4I7Zhp9ztR16U~z>hY$9MaWATj*id(q^QzpWG#VM~JV9};Q*F={fuh>cw zr-?}rQQuyTAW`LA0MTB~3k4yv<&iL@G&x%GLx^_&8jDgeP4yK54O)1m0S7*gTs~G4 zX5lmeIW){bak7qYAw3ghqIq2&L-qivM%aek^cfrznVs`5U&Io>c@u2g>oY*H3wT1D zt_xJ*dw=pB(Xt#nKsSWt>%I1qN=#x^Ute;GB#+@tD#bOyj19L-szriWUHDSNtskU@ z%wT^F`5Le!<=FieMXjDfGET~@f0A9XN{EiWIv`=RQ8mA{YxX|S{mr5r17 z)3R7EA6 z5<7;zn!S~RFiiPndRjDep&g;InB#;n>7fbc-0}IX+?sK!YM=Hb?O<({=;*XbReww= zH1m2Z=Ao|8TWWqFsi~nswH&P_4>LW2bmKlBLxJMI_rse~X9N(vyzB{17tr$QL;Ho? zj*mfG^8uc@M{}n1(yE^eOKg?8w7o zrBKGuij`|b$uE?%deubRpgyTcArLA8ZR)f%b1GNd7#0|~DLUR1#*+S_NXo@%>{veR z@5yQAl9}363FO>q+Ek*I_56zAjz9U*W)%ikedlybQ=^NN?C7&BwTMtjMgHD$z@gpGzUq?J6qQ8og=5W` z5!e*$_`zgb^}AZD+Y3+4mRJ-@#IG}p@1-IrWW$G2-1U45m09=P8`$*rks_=iJ|B>E+L;tKO*}4 z(LbvnqkelWH+!|VZTfN9PwM}!@g|&{;g$~h*j`#uSsIoVhA@Rl@3+$J`KktkJGxAoZTHdaprOm=!P5#NbK_`?9tG%A;V0&GLJY${hj=5s=V~_ zx8w6vg}~?bJ+eF)6#DWkK+Kk%HR|MLq3x}&fx*zk-MDS1DJJ_zS@vx&Un5@aAOj8M z{RTrQj@AN2Le9d8z2o_$()5}2rOH+Knt%3*+goul@NO=_<-FDY?$QhQJjZch?+yZD z&-!o^-!iej#kiOK!`_El19n z49jj^CqI-7-ISCMdZ|%O&R#r-64%q86io>cSmAoadMTD7oCqL;rHUtbbPpB+VyZxm z`sd-B>QJHeUsA{u(x+z&WQ#5~5Bj7NGIkB;&#;|#9Z{;u5bD`?=)=x-G*n$H1l4)ES)uy1zTzz_~K{+!(~)@qCA6=S0oW9 zS&>hgnl%hZ`y)hcHcQ*JUnY|(l&fMpygZYtQB&O&tg3`rTlkePR;v!I3Bi;ZLXqH+ zw?(6)PaB-AHctQ)!N{mLS|_Nc)LKM1B>-xG%A8pOV72xH5VlqFf0yc}$*0)b8Oh5Q$-dh+ZoBCx{XkFW#KKoET&dEiJ;ynTpb7mmic!5+%(h_Q%^w}=1?AUf|~*xnNeOLPn9}@ z2Ja@EQi4W)OyfUDP5MuHmHc(P<*`-1mD}{x75K=i0=q@&h9BTFsQO=>D|hEKQQ=dm z`x==+RAy3rTuOs-6m|MVdoie>T*fk&iBfndT@DcUzMI4IUJ__4xT z%!9-rFaveWk9~{}l(IJLmIAx=F4SXw$r#Q2)Z zsSnLjGB$(MZ8d?YE@;M?Lnv1E;c1Cd?4(xj;?LhDReuhptVP4wk^%;Et}`(^oORnegZ##=i7ejD%YL!YzOT?6AqR;%Rf zfqwe{?_kgas~gz(=r$Bsy5yWrkIKX*T(?6Y{^D}t$+%8{BKU_=)m)>OS8N4#z&uTlUA7ZuSF2OG|yaXeC38%+!z~yguTbA zn%l*eO#}PI>L%Qf!7F!?3Gp`7%26EYG^W&8!->?`h}c5%c8 zCOyx)^z6HMENmnHZJ=gv5;WQ-POW_ozqGW}w3OyGwKNt!$F-f0?459Ti;>;G1_&l1 zhgvWC_w4!_Zwut@(Wq_nrycmr_P>6upw_Wo3N=k@S0NKUBDUBX5glH>-|PN1?$*XU z{y%$VnQdg#lV3-+(J!&D;J>!MJEMrS-=BYJDrxX&Y2iq#F{Zk4;3YQL(5)T^5gxd` zS>Bt9ow@}xu%xSh`M{Dc`TpM4q2jH@iuWaRQ2uH^14WR5{rkb~QSM$ELYaMg>F=yk zMRGu!4>?Gz22BO1fw(~+cfnQq>ZZ-SL>-ifsn+Mk&kJu3D0 zhcU}yijp!G03pVB>O|d4Fk=M|c~*TUCvHf=PS(%0 zcg01MRzi@GXv$@Pfy_r9aS$^%z(^B2_i))Svk5a1(m3Fh)6fx+F)O9w|0U{s)GwH% z#1iFiW!iSv2R@kkcNYy|tx0 zqatc%IH|34;mBg+75iTQ?>#qeCh99TNJy3nfj^_W9Lpd7v>1Q3mKT7fZcOn+La-$Q zJDPMou7}akWJY7(XH&x}jMT1POhtWPuT8??Y*@48F;@X!-1?*c)wX2>RSc5wQn?X_ z&X1^URnOp1!pJY01vAeh4W}=8Gcj@#jNyo&3g*J3m^dh6lNSxeMM)y2B<%8&>J_M4 z7JJN4=NH-H3=t6F9gEAEi;e+i)F9_f^fNJXG*&J7uqZcvMV_36Wj@&;Lh_px2Ozv9 z9B2DHAYNu(PwX){Tc#9N%bU9-8LB?jMNIh>^+;is4`|89s>zA3=mfa)v%wBhRd?Jx~Ry5I>#L8V5Y&cxR9cV-bl9B1xK=XYzR4qnKN3qpqO&|v4NvD zPluCWY>i*4Ijsf;hUq(@#qUji3MyB!#L?BZ^y>J#I#XsWd7Egt6MA~eUB3*yE?NU> zCya*-DoXXZ4##M6&bOEHQdONz`tye(801-tWRg@*sc+i$E~uQQjYa+v=RE_BT z*Zz51PsqLL7&~n;1=UFnD-+|m)bH>0pEA1Z@$t_u*}g(;qp)zoH?;y4-*VY6{(^oL zxW8Csg&+S`7UMkjWm!A552N@&Cf)Zldpl;lgi3U02%Lot?DNWJg@)$x(- zrIIW`jMUU`XOJJD+wm2{~J1{CsWpd+)~ zJ}%-9>nzJB)1@V`zd=`JS^RQ%8BftXbu}Nv9t3^!l z7naA#0UfRqE2<(BlLELL^ycBv8P7-G_24!SY%1;PSkxww5)J5-f{?d77`*MuMa!1a z@R>|K0?oKr$*L1bPXmalXaOD0q{v@iC77~290EUcL4ZGhd8cO$RrY$jTE*mxyB;kx z3a_r6Hn3~i4Fx#(dMmlWp8gqfHSwIeB@?U0#18J?nHv}B79qY^hD!q))$D(j9hog& z8wWg{)y!IV3qA+m*rXK$Y%hzBR|DI@?l6kKKb1@f24LcMULA+YdCuHi7U9H~1A>#8 zaNRQv>kDVOe_0(=lx-N3+`Qf&{VqOm<{>%deY$;bTHHRq7g0>gY&RA?Mb<)u{$|s` zne)7OZQ`vLJbq7fPM61Ho>pr5WlBQ$?3Cz%6LfVCUm4b$x$cnW()UJ3*1TF7Hue5w z1@-8A`je>t$<$Bu$t=Pn)npX5;Ix;A`(lrDCv@YSPUi8HoKs>$gu<}y;>_9~ZN}Ai zuMOOK(=p)Gj8OV~m|GI04*2(lk(?b90ASAmEz~U&@9E*A^mjvCP<3Z?!Esd{k3i1=|qS$JNoYXvzNK}jAhJjyk8|C5PBQ`E;RPO zxDNmB)0N(11rNAjWVk*muo;`)lki8-!E0{}0@o zIuy z*6^+l<+1x81!N@?&)9WUY0ZDilRv(0y|1@#2Eh9NFLC2$TG&M&yA*U#?5jW#1MP+` zdf9;7s4t;Rpo8X?g}8e{yl2pGqB$idxIMq)tNdQ9+MP^GC_jnLaU5x0Ut50X^nWk8 z{zES(+k~U->KlfS6`{eKnt8m1;0^8#ItgypmaY&}%wK~I|2m~(6!?#@7cp=YSR7}m z{|PjZd~U$Oauq9+_}R0#FEF)7q(>UrO^yl3pu;yz%N6+3{1jj5a$D16kMit_$5<%e zt<%yl2zBMa6^ksdpEUywZJgI$g}kuBzj$0NW?v_flZQE zw!^hkwPydWa@4-)manpGXixqiwVh|-lITUlj`2DC!vZO&)yBM-h}$l(Lz%YZFG6?5 zwbHPYAkV@(Qi$Ykj&Wo-{`24ti|~IVl|!t>R^Y&Ye~L5FhaDHoKBh}U@fX)@b*cJq zoERsN>xP75+(|DkIKqupIBOJk>1rm@^@(c5J2uz<%p_SN7WO6mWSvB)1TOe|WS2PR z(}nA84p|z zc0Q&|FlyPyVdOfYjsl9p@sUSU3La&5INsIeki6ypG zIIel|E4xGR|Jc^>1BDy?3mxl}yD6FnEeYJbkMdVS_0WH&We%|lX7*|dh67to)&K5E zEaX(X+$N|H(q+6@9`CYyJ9wmpm;Pto+6;+z!*!LNx4KmG=n4r;^tewz9Ktzq!j2e5 zRb^l}!l?}Yo$<@2ERJJL<*${U{|!rm83SL`x#vxdO99fn!56+8+DuZ!xzRG%aT`-M z_xX7Gm^*7>axs%8Cy>#WJ4wYG3GDxkQ-HQuLb$cIJKC?}N+Y5v{pv_xMPaU+u|S0X zq4O_G|AvN$G$jU&RACJ{yj};6c1qH6Nx?JuA^wk*Q@da(TccH7PE%v7ZaOTRD;DKT zA_*w&Yn2DbTJCG$=l=QV|K`@P0NG@^Yo`X49v?(~K_X6YSp1SY_WJ)L|1jJ*In~N( z{D%6O_%HYT_`I*_Vzf7Q=H;ibVUw?t^UjzihyMQ`xASR#R5d`MDCo~ZL8nX*%|G#W zg8^@EGeDu|3ROW-v@ps3q-W0FrSR&vB`p(dYIF`b-_FmGbnN1dwgvGoKBtbiCvW!| z=wzCI0?)7=ZEkczy9xK#E?knP#q4#05!bEpw6KT% zdQw4ctSYQuxLK2%la*ATiiPAuNLs=bFF84A!7OC<=x!LS3PS_aTef_*@hGvHP7FRS}S;Nf~ znf^|f`u<*PTyw#v#qb*196goLIrPkhcM%fs3^D!@GnlZ$d7R=~L%|6=>#+$aBuV(m zQJa-sje~RgL~FP8#LO{XfU*Cf{0|&K^>(2_%YkrAKnU}QM`q{WV;R{$nXwQvGAL(I zSw?a`YbX}dvP`<1S zv#+d1ju3b8{??`hy)(J>KolV4gj|7tR-NFjE4B24`p0<};v>^)OThzZJ zy{Amk=P$;6G~f=<|VVfN^i~&8-Oa2lr)w?|v-nGL>t+AA%t{9yyQYg)lNb zF}nnfeP0+)-_6)(SfRc-9V7E`Lii&-G@Yq+!(9>l9aC7pX&-c*5Q)9ZafS+$|ux0tL)5hz{U0YF^a#Br0Oe>7@a?1#&wi zG05-wd>kK!F7i*jPO#gxP0;Wl%MBRu1uuM3X|0G>Ms^}=tI^{YDN6c93APL0Zs-B7 zMU4~{=9pdzu0aBGF@p2a$nJp@?VsKp^OUfb8z+)V)}|U%@J#Zgm}#oAKE6Zqmw{W? z4+=KxxV1iccL>~cd~jz&?7J7d&zC+96tP{De&peayFhz0&w@vS3AUdSyPn?b)3 z#-Ae_nP8f3cu!;Iax#bO5<4w3pG7$3B70plB+I%wmWB!SjYd;7Q9mC@sd}|<&?7~h zMCNz-;q)LMG#$9yJywa5D27#pHv`ei>76RMWLsy$Qj|rGj*uPVJ4=*MO~%Tz6faE% zdtQ1jVQw9>0|7-i~HsutRyYHzjo1!*W^&evP9PkthO&)OH9Ncc2c zK+}}C0JC_l`%Dv&7s|itAExF79HeC$UGgVX^KTQRTT-&CWz<5&JN&z{FwToJP>+F| zA?;+kZXPW%Jsx;~n2jy3ZFbTUGk8OUn04@R_)K5!z7!3eZG|(Qj^2_6nFo9~HFzV5 zf@tZ5B=%JFiRR=OAsa@t;E!?;>*NEqEmYT-F~M+}wLjHoBPAdqw~^m-+Q@Pi zbmVp{b*mZ8I+==M>S(-tb86_HSEHW=ao4I#7~8_&K1CxR9=h_Z_2(bB<5x4l&&P?d znM5u5h9Tst41~#`(e{U<3|;+nBiO}X9eijLse(5+J`}wrZJGixIdNMErJEH7wxDRE zlP0;+V{!0fvKfQ5PN(NL)Yfii(rNw2S=!D#ByaZH(-uHaUyB0#RDs+^_D|a`*05Dm zSrA5bBe$f|TpXZg=OR64qBT|sgBE3zv|gXLGTALfIb+yrcKSj+gA`uRgXb>Bo1(GI zVgqk-P6|WzsNa4=`~b8Isx1~=g8H%i3n$duaG0llp?Z_^^VO@)qUxqgaCp7rk!s{8 zU#4d$)<_YX!s0pc&#=9nC`&KWbF;H}q)(zX*W(-klU8DBdZ4A$@FZUKJRF~(=arkQ zp*E~RwG`_?XLPS^?8&c^S!4$06-fz1xdd7L66GN!Ioyp9)dP?xPk1pF*y;tvQA^xN zlwXXT6;+rGUY$|m;!{pAqsr!!sHMQ6H1+2{Z#T2n@ts~e`)d!fl@=v398CV0|e6!-Sp+*rf+M^XX$;m8u;#x#BOT$MO%q zw3~{2+#Wl`kp_VS5?sYYJ|e0*VfZ_^$vs;tR6B+pzQ@EzbWf#sDJkrOPp}qRoFvg9 zi4pdt=3<>jzyx%URl1r!w_Ip~wT`1keJSAq{8h%MMU+%hR_{lLK!y0ObqUju=*2{* zj0}NvA)KKpgonp)KBo#*M30A&BVqK4K4aRd-ghk5`5n2j1GqHB`A3PR1lszTp!SuY zE}JLf(kU9-U4CQCSc*Han^6Box}qPk%fdW!WzApgPa+!n97>0gYc8l~r>97zXEPbu6ZTp*sbS zWNepd*2!wq1O8;yOjdo)X!OlN<=z7m?Zz%EcnO|YuB0D5E$fZAQAKtWDRy-t;nbrj z8$-$?njFGf)#JV>2W7!|v~q-|-bjESve@}HJe6b0u$d%_zuDcJTs~QwZ;T^)HbZn$ z{Y7fC@xqK8lMpkD-ZrP69_(gA>V($@i~t(+^Z*=~lbXXl>vfJ>!m&hAg!ic%qW8VY zn>F{QJ&k6{=Y;ik$MQr3sRm#> zg_B(@8ei@1y|6=u5@-LjSe*wp+5kwB*x$#;WPP&0WR#2$RI*co_jlDHkvG)3%~5tM z23^Z-*~mb`FD{C3+ChxVm7$aH9~X+8@KLMZ$w*=FJ!}U89Vtu7wgwQmZ253gy|Pe`q5Kv7OPKL#14D934CJOa>lOjPNzIgT#C`oklU--Z zX*Z5A%h(7)Fhc@FOt54V%|T~e(b&Y(+50JkY{gW!zPuQI(GvUq;CqpOYZIPP zuk+0d#ia!E==}b8)p#$=deasfIl8Ze^lI!fRq;?c(2f?2v6A5Ko%!(i*M~!QIH$5 zRbejS%k)NPCK1&^h$oj(82QvS6BjrJprUco-@lrplEBy-f_f-S; z#MJBn=EAOSlPI=Mf0JwE;r{8eR!uo=f!;)zGq2cgT)m_g-e;i}D~j{?Ux?IuF}YK1 zKObYK;~moa!n7q~Bq10AInN}p5vMyNzk_hjY`50{R4gXZft&kZD*a;>O?Y2VM|4Sh z&HOODA^fuI%WyFszpc@ZMM$9%t62{&!e&aey^NWyEl(o$5y6-d5?yTA>t~PuentcQ EA4_k|g#Z8m literal 0 HcmV?d00001 diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index a7af90c..965c9ed 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,7 +1,7 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Add driver for RGB-Only Lights (RGB)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Add driver for RGB-Only Lights (RGB)\n - Add driver for Knycklan Water Valve (receiver and remote)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", "dateReleased": "2024-05-??", diff --git a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy index 4289d7a..1173e95 100644 --- a/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy +++ b/ikea-zigbee-drivers/src/capabilities/ColorTemperature.groovy @@ -50,6 +50,7 @@ void setColorTemperature(BigDecimal colorTemperature, BigDecimal level = -1, Big Integer dur = (duration > 1800 ? 1800 : (duration < 0 ? 0 : duration)) * 10 // Max transition time = 30 min String payload = "${utils_payload mireds, 4} ${utils_payload dur, 4}" utils_sendZigbeeCommands(["he raw 0x${device.deviceNetworkId} 0x01 0x${device.endpointId} 0x0300 {11430A ${payload}}"]) + /* groovylint-disable-next-line UnnecessarySetter */ if (level > 0 && duration == 0) setLevel level } void startColorTemperatureChange(String direction) { diff --git a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml index 658a30a..0ef3bf5 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_DIM-Light/config.yaml @@ -18,12 +18,21 @@ device: - type: LED1623G12 firmwares: 2.3.094 (117C-2101-23094631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 opal 1000lm', manufacturer:'IKEA of Sweden' + - type: LED1649C5E14EU + firmwares: 2.3.094 (117C-2101-23094631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E14 W op/ch 400lm', manufacturer:'IKEA of Sweden' + - type: LED1934G3 + firmwares: 1.0.010 (117C-2102-00010010) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE27WWclear250lm', manufacturer:'IKEA of Sweden' - type: 10EU-IL-1 firmwares: 1.2.245 (117C-4101-12245572) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0B05,1000', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' - type: 10EU-IL-1 firmwares: 2.3.086 (117C-4101-23086631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI Driver 10W', manufacturer:'IKEA of Sweden' + - type: LED1837R5 + firmwares: 2.3.093 (117C-4103-23093631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb GU10 WW 400lm', manufacturer:'IKEA of Sweden' - type: LED1842G3 firmwares: 2.3.093 (117C-4103-23093631) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E27 WW clear 250lm', manufacturer:'IKEA of Sweden' diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1743/E1743.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E1743/E1743.groovy index 26e1879..a8a09a1 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1743/E1743.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1743/E1743.groovy @@ -1,7 +1,7 @@ {{!--------------------------------------------------------------------------}} {{# @configure }} -// Configuration for devices.E1743 +// Configuration for devices.Ikea_E1743 cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster {{/ @configure }} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1841/E1841.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E1841/E1841.groovy new file mode 100644 index 0000000..8f095f1 --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1841/E1841.groovy @@ -0,0 +1,34 @@ +{{!--------------------------------------------------------------------------}} +{{# @configure }} + +// Configuration for devices.Ikea_E1841 +cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0006 {${device.zigbeeId}} {}" // On/Off cluster +cmds += "zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0008 {${device.zigbeeId}} {}" // Level Control cluster +{{/ @configure }} +{{!--------------------------------------------------------------------------}} +{{# @events }} + +// Events for devices.Ikea_E1841 +// =================================================================================================================== + +// I/O button was pressed +case { contains it, [clusterInt:0x0006, commandInt:0x00] }: +case { contains it, [clusterInt:0x0006, commandInt:0x01] }: + List button = msg.commandInt == 0x00 ? BUTTONS.OFF : BUTTONS.ON + utils_sendEvent name:'pushed', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was pushed" + return + +// I/O button was held +case { contains it, [clusterInt:0x0008, commandInt:0x01] }: +case { contains it, [clusterInt:0x0008, commandInt:0x05] }: + List button = msg.commandInt == 0x01 ? BUTTONS.OFF : BUTTONS.ON + utils_sendEvent name:'held', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was held" + return + +// I/O button was released +case { contains it, [clusterInt:0x0008, commandInt:0x07] }: + List button = device.currentValue('held', true) == 1 ? BUTTONS.ON : BUTTONS.OFF + utils_sendEvent name:'released', value:button[0], type:'physical', isStateChange:true, descriptionText:"Button ${button[0]} (${button[1]}) was released" + return +{{/ @events }} +{{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml new file mode 100644 index 0000000..345b8c1 --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml @@ -0,0 +1,36 @@ +device: + model: IKEA Knycklan Water Valve Remote (E1841) + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1841.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_E1841.webp + links: + - name: device details + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#knycklan-water-valve-remote-e1841 + - name: community page + url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 + + fingerprints: + - firmwares: 2.3.019 (117C-11C9-23019631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'KNYCKLAN Open/Close remote', manufacturer:'IKEA of Sweden' + + capabilities: + - file: src/devices/Ikea_E1841/E1841.groovy + - file: src/capabilities/Battery.groovy + params: + half: true + - file: src/capabilities/HealthCheck.groovy + params: + schedule: 0 0 0/1 ? * * * + checkInterval: 3600 # every hour + thereshold: 43200 # report device as offline if no message was received in the last 12 hours (device should report battery percentage every 5 hours) + - file: src/capabilities/HoldableButton.groovy + - file: src/capabilities/PowerSource.groovy + - file: src/capabilities/PushableButton.groovy + params: + buttons: + - { id: 'ON', name: '💧', number: 1 } + - { id: 'OFF', name: '🩸', number: 2 } + - file: src/capabilities/ReleasableButton.groovy + - file: src/capabilities/ZigbeeBindings.groovy + params: + clusters: ['0x0006', '0x0008'] + - file: src/capabilities/FirmwareUpdate.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1842/E1842.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E1842/E1842.groovy new file mode 100644 index 0000000..c7f7f53 --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1842/E1842.groovy @@ -0,0 +1,20 @@ +{{!--------------------------------------------------------------------------}} +{{# @events }} + +// Events for devices.Ikea_E1842 +// =================================================================================================================== + +// Report/Read Attributes Reponse: ZoneStatus +case { contains it, [clusterInt:0x0500, commandInt:0x0A, attrInt:0x0002] }: +case { contains it, [clusterInt:0x0500, commandInt:0x01, attrInt:0x0002] }: + String water = msg.value[-1] == '1' ? 'wet' : 'dry' + utils_sendEvent name:'water', value:water, descriptionText:"Is ${water}", type:type + utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "ZoneStatus=${msg.value}" + return + +// Ignore Configure Reporting Response for attribute ZoneStatus +case { contains it, [clusterInt:0x0500, commandInt:0x07] }: + utils_processedZclMessage 'Configure Reporting Response', "attribute=ZoneStatus, data=${msg.data}" + return +{{/ @events }} +{{!--------------------------------------------------------------------------}} diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1842/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1842/config.yaml new file mode 100644 index 0000000..0fc91d2 --- /dev/null +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1842/config.yaml @@ -0,0 +1,32 @@ +device: + model: IKEA Knycklan Water Valve Receiver (E1842) + importUrl: https://raw.githubusercontent.com/dan-danache/hubitat/master/ikea-zigbee-drivers/Ikea_E1842.groovy + image: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/img/Ikea_E1842.webp + links: + - name: device details + url: https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/#knycklan-water-valve-receiver-e1842 + - name: community page + url: https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853 + + fingerprints: + - firmwares: 2.3.024 (117C-1103-23024631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,1000', outClusters:'0019,0020,1000', model:'KNYCKLAN receiver', manufacturer:'IKEA of Sweden' + + capabilities: + - name: RelaySwitch + - name: Sensor + - name: WaterSensor + - file: src/devices/Ikea_E1842/E1842.groovy + - file: src/capabilities/IAS.groovy + - file: src/capabilities/Switch.groovy + params: + powerOnBehavior: true + onWithTimedOff: true + - file: src/capabilities/HealthCheck.groovy + params: + schedule: 0 0 0/1 ? * * * + checkInterval: 3600 # every hour + thereshold: 3600 # report device as offline if no message was received in the last 60 minutes (device should report On/Off status every 10 minutes) + - file: src/capabilities/PowerSource.groovy + - file: src/capabilities/ZigbeeGroups.groovy + - file: src/capabilities/FirmwareUpdate.groovy diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2202/E2202.groovy b/ikea-zigbee-drivers/src/devices/Ikea_E2202/E2202.groovy index e218752..372cf40 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2202/E2202.groovy +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2202/E2202.groovy @@ -1,7 +1,7 @@ {{!--------------------------------------------------------------------------}} {{# @events }} -// Events for devices.E2202 +// Events for devices.Ikea_E2202 // =================================================================================================================== // Report/Read Attributes Reponse: ZoneStatus diff --git a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml index 665eb79..8444b0f 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_WS-Light/config.yaml @@ -21,12 +21,18 @@ device: - type: LED2002G5 firmwares: 1.0.012 (117C-2205-00010012) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE14WSglobeopal470lm', manufacturer:'IKEA of Sweden' + - type: LED2003G10 + firmwares: 1.0.012 (117C-2205-00010012) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbE27WSglobeopal1055lm', manufacturer:'IKEA of Sweden' - type: LED2005R5 firmwares: 1.0.012 (117C-2205-00010012) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC57', outClusters:'0019', model:'TRADFRIbulbGU10WS345lm', manufacturer:'IKEA of Sweden' - type: LED2201G8 firmwares: 3.0.10 (117C-2206-03000010) value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0019', model:'TRADFRI bulb E27 WS globe 1055lm', manufacturer:'IKEA of Sweden' + - type: LED1835C6 + firmwares: 2.3.087 (117C-4205-23087631) + value: fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,0008,0300,1000,FC7C', outClusters:'0005,0019,0020,1000', model:'TRADFRI bulb E14 WS 470lm', manufacturer:'IKEA of Sweden' capabilities: - file: src/capabilities/Switch.groovy From 12d35dddfc244fd3b4eb876d638a4cfdac95bf94 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Thu, 16 May 2024 21:40:51 +0300 Subject: [PATCH 24/26] Fix typo --- zigbee-map-app/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zigbee-map-app/CHANGELOG.md b/zigbee-map-app/CHANGELOG.md index e7f7062..dd21315 100644 --- a/zigbee-map-app/CHANGELOG.md +++ b/zigbee-map-app/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change "good" link quality LQI interval from [150 - 200) to [130 - 200) ### Fixed -- Scrollbars on tab contents appear only when ncesessary +- Scrollbars on tab contents appear only when necessary ## [2.0.0] - 2024-03-09 From 6cc853ab585ce349380b02159252e7ffd80c5ddc Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Tue, 21 May 2024 20:39:45 +0300 Subject: [PATCH 25/26] - Add "lastBattery" attribute representing the time the last battery report was received - Prepare release 5.0.0 --- ikea-zigbee-drivers/CHANGELOG.md | 4 ++-- ikea-zigbee-drivers/Ikea_E1743.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E1745.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E1766.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E1810.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E1812.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E1841.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2002.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2013.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2123.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2134.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2201.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2202.groovy | 8 ++++++-- ikea-zigbee-drivers/Ikea_E2213.groovy | 8 ++++++-- ikea-zigbee-drivers/Philips_RDM001.groovy | 8 ++++++-- ikea-zigbee-drivers/Philips_RWL022.groovy | 8 ++++++-- ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy | 8 ++++++-- ikea-zigbee-drivers/packageManifest.json | 4 ++-- ikea-zigbee-drivers/src/capabilities/Battery.groovy | 13 +++++++++---- .../src/devices/Ikea_E1743/config.yaml | 2 -- .../src/devices/Ikea_E1745/config.yaml | 2 -- .../src/devices/Ikea_E1766/config.yaml | 2 -- .../src/devices/Ikea_E1810/config.yaml | 2 -- .../src/devices/Ikea_E1812/config.yaml | 2 -- .../src/devices/Ikea_E1841/config.yaml | 2 -- .../src/devices/Ikea_E2002/config.yaml | 2 -- .../src/devices/Ikea_E2013/config.yaml | 2 -- .../src/devices/Ikea_E2123/config.yaml | 2 -- .../src/devices/Ikea_E2134/config.yaml | 2 -- .../src/devices/Ikea_E2201/config.yaml | 2 -- .../src/devices/Ikea_E2202/config.yaml | 2 -- .../src/devices/Ikea_E2213/config.yaml | 2 -- .../src/devices/Philips_RDM001/config.yaml | 2 -- .../src/devices/Philips_RWL022/config.yaml | 2 -- .../src/devices/Swann_SWO-KEF1PA/config.yaml | 2 -- 35 files changed, 109 insertions(+), 72 deletions(-) diff --git a/ikea-zigbee-drivers/CHANGELOG.md b/ikea-zigbee-drivers/CHANGELOG.md index 479a848..9386e95 100644 --- a/ikea-zigbee-drivers/CHANGELOG.md +++ b/ikea-zigbee-drivers/CHANGELOG.md @@ -5,14 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [5.0.0] - 2024-05-?? +## [5.0.0] - 2024-05-21 ### Added - Add driver for Dimmable Lights devices, including LED drivers - Add driver for White Spectrum Lights devices (CT) - Add driver for Color White Spectrum Lights devices (RGB+CT) - Add driver for RGB-Only Lights devices (RGB) -- Add driver for Knycklan Water Valve (receiver and remote) +- Add "lastBattery" attribute representing the time the last battery report was received - Add Scenes support using Zigbee Bindings for E1810 and E2002 - Put all devices in "identifying mode" while "configure()" is running - `@UncleAlias` - E2006: Move "Dark Mode" preference to "setIndicatorStatus()" command and "indicatorStatus" attribute - `@userLP24` diff --git a/ikea-zigbee-drivers/Ikea_E1743.groovy b/ikea-zigbee-drivers/Ikea_E1743.groovy index 930e22d..59b904d 100644 --- a/ikea-zigbee-drivers/Ikea_E1743.groovy +++ b/ikea-zigbee-drivers/Ikea_E1743.groovy @@ -36,6 +36,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI on/off switch', manufacturer:'IKEA of Sweden' // Firmware: 2.2.010, 24.4.6 (117C-11C5-24040006) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -382,9 +385,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E1745.groovy b/ikea-zigbee-drivers/Ikea_E1745.groovy index 1016d08..7da9a45 100644 --- a/ikea-zigbee-drivers/Ikea_E1745.groovy +++ b/ikea-zigbee-drivers/Ikea_E1745.groovy @@ -38,6 +38,9 @@ metadata { attribute 'requestedBrightness', 'number' // Syncs with the brightness option on device (◐/⭘) attribute 'illumination', 'enum', ['dim', 'bright'] // Works only in night mode 🌙 + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -417,9 +420,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E1766.groovy b/ikea-zigbee-drivers/Ikea_E1766.groovy index b5f19ea..060badc 100644 --- a/ikea-zigbee-drivers/Ikea_E1766.groovy +++ b/ikea-zigbee-drivers/Ikea_E1766.groovy @@ -36,6 +36,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI open/close remote', manufacturer:'IKEA of Sweden' // Firmware: 24.4.6 (117C-11C6-24040006) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -373,9 +376,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E1810.groovy b/ikea-zigbee-drivers/Ikea_E1810.groovy index c1f1c96..24f0a39 100644 --- a/ikea-zigbee-drivers/Ikea_E1810.groovy +++ b/ikea-zigbee-drivers/Ikea_E1810.groovy @@ -39,6 +39,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0004,0005,0006,0008,0019,1000', model:'TRADFRI remote control', manufacturer:'IKEA of Sweden' // Firmware: 24.4.5 (117C-11C1-24040005) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -412,9 +415,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E1812.groovy b/ikea-zigbee-drivers/Ikea_E1812.groovy index f6cc8ef..269a874 100644 --- a/ikea-zigbee-drivers/Ikea_E1812.groovy +++ b/ikea-zigbee-drivers/Ikea_E1812.groovy @@ -37,6 +37,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // Firmware: 2.3.015 (117C-11C6-23015631) fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'TRADFRI SHORTCUT Button', manufacturer:'IKEA of Sweden' // Firmware: 24.4.6 (117C-11C6-24040006) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -397,9 +400,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E1841.groovy b/ikea-zigbee-drivers/Ikea_E1841.groovy index ca27bb1..0c06d9e 100644 --- a/ikea-zigbee-drivers/Ikea_E1841.groovy +++ b/ikea-zigbee-drivers/Ikea_E1841.groovy @@ -36,6 +36,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0009,0020,1000', outClusters:'0003,0004,0006,0008,0019,0102,1000', model:'KNYCKLAN Open/Close remote', manufacturer:'IKEA of Sweden' // Firmware: 2.3.019 (117C-11C9-23019631) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -382,9 +385,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2002.groovy b/ikea-zigbee-drivers/Ikea_E2002.groovy index 537ed75..54d90b3 100644 --- a/ikea-zigbee-drivers/Ikea_E2002.groovy +++ b/ikea-zigbee-drivers/Ikea_E2002.groovy @@ -39,6 +39,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // Firmware: 1.0.024 fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57,FC7C', outClusters:'0003,0005,0006,0008,0019,1000', model:'Remote Control N2', manufacturer:'IKEA of Sweden' // Firmware: 2.4.5 (117C-11CB-02040005) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -420,9 +423,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2013.groovy b/ikea-zigbee-drivers/Ikea_E2013.groovy index a5029f3..3248e0c 100644 --- a/ikea-zigbee-drivers/Ikea_E2013.groovy +++ b/ikea-zigbee-drivers/Ikea_E2013.groovy @@ -40,6 +40,9 @@ metadata { // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -432,9 +435,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2123.groovy b/ikea-zigbee-drivers/Ikea_E2123.groovy index 51d1006..cbfb684 100644 --- a/ikea-zigbee-drivers/Ikea_E2123.groovy +++ b/ikea-zigbee-drivers/Ikea_E2123.groovy @@ -48,6 +48,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC57', outClusters:'0003,0004,0006,0008,0019,1000,FC7F', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // Firmware: 1.0.012 fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'SYMFONISK sound remote gen2', manufacturer:'IKEA of Sweden' // Firmware: 1.0.35 (117C-110E-01000035) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -486,9 +489,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2134.groovy b/ikea-zigbee-drivers/Ikea_E2134.groovy index 359066f..988202b 100644 --- a/ikea-zigbee-drivers/Ikea_E2134.groovy +++ b/ikea-zigbee-drivers/Ikea_E2134.groovy @@ -35,6 +35,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,0B05,1000,FC7C,FC81', outClusters:'0003,0004,0006,0019,1000', model:'VALLHORN Wireless Motion Sensor', manufacturer:'IKEA of Sweden' // Firmware: 1.0.57 (117C-1938-01000057) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -392,9 +395,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2201.groovy b/ikea-zigbee-drivers/Ikea_E2201.groovy index 9d12827..7980e5c 100644 --- a/ikea-zigbee-drivers/Ikea_E2201.groovy +++ b/ikea-zigbee-drivers/Ikea_E2201.groovy @@ -41,6 +41,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0020,1000,FC7C', outClusters:'0003,0004,0006,0008,0019,1000', model:'RODRET Dimmer', manufacturer:'IKEA of Sweden' // Firmware: 1.0.47 (117C-11CD-01000047) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -408,9 +411,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2202.groovy b/ikea-zigbee-drivers/Ikea_E2202.groovy index e02dbec..e249f66 100644 --- a/ikea-zigbee-drivers/Ikea_E2202.groovy +++ b/ikea-zigbee-drivers/Ikea_E2202.groovy @@ -35,6 +35,9 @@ metadata { // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -348,9 +351,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Ikea_E2213.groovy b/ikea-zigbee-drivers/Ikea_E2213.groovy index ff3c2e7..6346d9b 100644 --- a/ikea-zigbee-drivers/Ikea_E2213.groovy +++ b/ikea-zigbee-drivers/Ikea_E2213.groovy @@ -37,6 +37,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,0004,0020,1000,FC7C,FC80', outClusters:'0003,0004,0006,0008,0019,1000,FC80', model:'SOMRIG shortcut button', manufacturer:'IKEA of Sweden' // Firmware: 1.0.20 (117C-3B08-01000020) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -360,9 +363,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Philips_RDM001.groovy b/ikea-zigbee-drivers/Philips_RDM001.groovy index ab79e71..00487c5 100644 --- a/ikea-zigbee-drivers/Philips_RDM001.groovy +++ b/ikea-zigbee-drivers/Philips_RDM001.groovy @@ -49,6 +49,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00', outClusters:'0003,0004,0006,0008,0019', model:'RDM001', manufacturer:'Signify Netherlands B.V.' // Firmware: 1.0.5 (100B-011C-0000041A) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -473,9 +476,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Philips_RWL022.groovy b/ikea-zigbee-drivers/Philips_RWL022.groovy index c094ab9..1cc6af7 100644 --- a/ikea-zigbee-drivers/Philips_RWL022.groovy +++ b/ikea-zigbee-drivers/Philips_RWL022.groovy @@ -43,6 +43,9 @@ metadata { fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0001,0003,FC00,1000', outClusters:'0019,0000,0003,0004,0006,0008,0005,1000', model:'RWL022', manufacturer:'Signify Netherlands B.V.' // Firmware: 2.45.2_hF4400CA (100B-0119-02002D02) + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -415,9 +418,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy index f6f57a6..b1f94dc 100644 --- a/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy +++ b/ikea-zigbee-drivers/Swann_SWO-KEF1PA.groovy @@ -41,6 +41,9 @@ metadata { // Attributes for capability.IAS attribute 'ias', 'enum', ['enrolled', 'not enrolled'] + // Attributes for capability.Battery + attribute 'lastBattery', 'date' + // Attributes for capability.HealthCheck attribute 'healthStatus', 'enum', ['offline', 'online', 'unknown'] } @@ -392,9 +395,10 @@ void parse(String description) { return } - Integer percentage = Integer.parseInt(msg.value, 16) - percentage = percentage / 2 + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 965c9ed..829bf36 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -1,10 +1,10 @@ { "packageName": "IKEA Zigbee drivers", "version": "5.0.0", - "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Add driver for RGB-Only Lights (RGB)\n - Add driver for Knycklan Water Valve (receiver and remote)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", + "releaseNotes": "v5.0.0\n - Add driver for Dimmable Lights, including LED drivers\n - Add driver for White Spectrum Lights (CT)\n - Add driver for Color White Spectrum Lights (RGB+CT)\n - Add driver for RGB-Only Lights (RGB)\n - Remove ICPSHC24 driver; LED drivers must use the new Dimmable Lights driver\n - Add \"lastBattery\" attribute representing the time the last battery report was received\n - Add Scenes support using Zigbee Bindings for E1810 and E2002\n - Put all devices in \"identifying mode\" while configure() is running - @UncleAlias\n - E2006: Move \"Dark Mode\" preference to \"setIndicatorStatus()\" command and \"indicatorStatus\" attribute - @userLP24\n - Legrand Connected Outlet: Fix blue light is ON when the device is OFF and OFF when its ON - @BorrisTheCat", "minimumHEVersion": "2.1.9", "author": "Dan Danache (@dandanache)", - "dateReleased": "2024-05-??", + "dateReleased": "2024-05-21", "documentationLink": "https://dan-danache.github.io/hubitat/ikea-zigbee-drivers/", "communityLink": "https://community.hubitat.com/t/release-ikea-zigbee-drivers/123853", "payPalUrl": "https://www.buymeacoffee.com/dandanache", diff --git a/ikea-zigbee-drivers/src/capabilities/Battery.groovy b/ikea-zigbee-drivers/src/capabilities/Battery.groovy index 1d9ac12..3169649 100644 --- a/ikea-zigbee-drivers/src/capabilities/Battery.groovy +++ b/ikea-zigbee-drivers/src/capabilities/Battery.groovy @@ -3,6 +3,12 @@ capability 'Battery' {{/ @definition }} {{!--------------------------------------------------------------------------}} +{{# @attributes }} + +// Attributes for capability.Battery +attribute 'lastBattery', 'date' +{{/ @attributes }} +{{!--------------------------------------------------------------------------}} {{# @configure }} // Configuration for capability.Battery @@ -36,11 +42,10 @@ case { contains it, [clusterInt:0x0001, commandInt:0x01] }: return } - Integer percentage = Integer.parseInt(msg.value, 16) - {{# params.half }} - percentage = percentage / 2 - {{/ params.half }} + Integer percentage = Integer.parseInt(msg.value, 16) / 2 + Date lastBattery = new Date() utils_sendEvent name:'battery', value:percentage, unit:'%', descriptionText:"Battery is ${percentage}% full", type:type + utils_sendEvent name:'lastBattery', value:lastBattery, descriptionText:"Last battery report time is ${lastBattery}", type:type utils_processedZclMessage "${msg.commandInt == 0x0A ? 'Report' : 'Read'} Attributes Response", "BatteryPercentage=${percentage}%" return diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1743/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1743/config.yaml index a462966..c7657cb 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1743/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1743/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Ikea_E1743/E1743.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1745/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1745/config.yaml index 1689ce3..0452f09 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1745/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1745/config.yaml @@ -16,8 +16,6 @@ device: - name: Sensor - file: src/devices/Ikea_E1745/E1745.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1766/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1766/config.yaml index 56b6520..6626ddb 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1766/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1766/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Ikea_E1766/E1766.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml index 1bc952d..6c1da5a 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1810/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Ikea_E1810/E1810.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1812/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1812/config.yaml index 669d7f6..62daba5 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1812/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1812/config.yaml @@ -17,8 +17,6 @@ device: capabilities: - file: src/devices/Ikea_E1812/E1812.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/DoubleTapableButton.groovy - file: src/capabilities/HealthCheck.groovy params: diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml index 345b8c1..a152b64 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E1841/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Ikea_E1841/E1841.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml index 8e21f6e..e00a509 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2002/config.yaml @@ -17,8 +17,6 @@ device: capabilities: - file: src/devices/Ikea_E2002/E2002.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2013/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2013/config.yaml index 2277e1b..9a183d2 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2013/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2013/config.yaml @@ -20,8 +20,6 @@ device: params: endpoint: '0x02' - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2123/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2123/config.yaml index c14b27b..5f202ff 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2123/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2123/config.yaml @@ -17,8 +17,6 @@ device: capabilities: - file: src/devices/Ikea_E2123/E2123.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/DoubleTapableButton.groovy - file: src/capabilities/HealthCheck.groovy params: diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2134/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2134/config.yaml index a6c6072..e19f31a 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2134/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2134/config.yaml @@ -18,8 +18,6 @@ device: - name: IlluminanceMeasurement - file: src/devices/Ikea_E2134/E2134.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2201/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2201/config.yaml index b1c8190..4a2a498 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2201/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2201/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Ikea_E2201/E2201.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2202/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2202/config.yaml index 1fc2a06..8cb9fb2 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2202/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2202/config.yaml @@ -18,8 +18,6 @@ device: - file: src/devices/Ikea_E2202/E2202.groovy - file: src/capabilities/IAS.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Ikea_E2213/config.yaml b/ikea-zigbee-drivers/src/devices/Ikea_E2213/config.yaml index 02e3cba..26b40a6 100644 --- a/ikea-zigbee-drivers/src/devices/Ikea_E2213/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Ikea_E2213/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Ikea_E2213/E2213.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/DoubleTapableButton.groovy - file: src/capabilities/HealthCheck.groovy params: diff --git a/ikea-zigbee-drivers/src/devices/Philips_RDM001/config.yaml b/ikea-zigbee-drivers/src/devices/Philips_RDM001/config.yaml index 44296eb..8c7e240 100644 --- a/ikea-zigbee-drivers/src/devices/Philips_RDM001/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Philips_RDM001/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Philips_RDM001/RDM001.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Philips_RWL022/config.yaml b/ikea-zigbee-drivers/src/devices/Philips_RWL022/config.yaml index 8449fe5..e2409df 100644 --- a/ikea-zigbee-drivers/src/devices/Philips_RWL022/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Philips_RWL022/config.yaml @@ -15,8 +15,6 @@ device: capabilities: - file: src/devices/Philips_RWL022/RWL022.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * diff --git a/ikea-zigbee-drivers/src/devices/Swann_SWO-KEF1PA/config.yaml b/ikea-zigbee-drivers/src/devices/Swann_SWO-KEF1PA/config.yaml index 74c6317..8bebaf0 100644 --- a/ikea-zigbee-drivers/src/devices/Swann_SWO-KEF1PA/config.yaml +++ b/ikea-zigbee-drivers/src/devices/Swann_SWO-KEF1PA/config.yaml @@ -16,8 +16,6 @@ device: - file: src/devices/Swann_SWO-KEF1PA/SWO-KEF1PA.groovy - file: src/capabilities/IAS.groovy - file: src/capabilities/Battery.groovy - params: - half: true - file: src/capabilities/HealthCheck.groovy params: schedule: 0 0 0/1 ? * * * From 81beba1a827e28641873df35fbf72490104b8e31 Mon Sep 17 00:00:00 2001 From: Dan Danache Date: Tue, 21 May 2024 20:47:50 +0300 Subject: [PATCH 26/26] Fix URL --- ikea-zigbee-drivers/packageManifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ikea-zigbee-drivers/packageManifest.json b/ikea-zigbee-drivers/packageManifest.json index 829bf36..5652802 100644 --- a/ikea-zigbee-drivers/packageManifest.json +++ b/ikea-zigbee-drivers/packageManifest.json @@ -33,7 +33,7 @@ "name": "IKEA Dimmable Light", "description": "Zigbee driver for IKEA Dimmable Lights", "namespace": "dandanache", - "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_DIM-Lights.groovy", + "location": "https://raw.githubusercontent.com/dan-danache/hubitat/ikea-zigbee-drivers_5.0.0/ikea-zigbee-drivers/Ikea_DIM-Light.groovy", "required": false }, {