/* groovylint-disable CompileStatic, DuplicateNumberLiteral, DuplicateStringLiteral, ImplicitClosureParameter, ImplicitReturnStatement, LineLength, MethodCount, MethodReturnTypeRequired, PublicMethodsBeforeNonPublicMethods, ReturnNullFromCatchBlock, StaticMethodsBeforeInstanceMethods, UnnecessaryGetter */ /** * Zemismart ZigBee Wall Switch Multi-Gang - Device Driver for Hubitat Elevation hub * * https://community.hubitat.com/t/zemismart-zigbee-1-2-3-4-gang-light-switches/21124/36?u=kkossev * * Based on Muxa's driver Version 0.2.0 (last updated Feb 5, 2020) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Ver. 0.0.1 2019-08-21 Muxa - first version * Ver. 0.1.0 2020-02-05 Muxa - Driver name "Zemismart ZigBee Wall Switch Multi-Gang" * Ver. 0.2.1 2022-02-26 kkossev - TuyaBlackMagic for TS0003 _TZ3000_vjhcenzo * Ver. 0.2.2 2022-02-27 kkossev - TS0004 4-button, logEnable, txtEnable, ping(), intercept cluster: E000 attrId: D001 and D002 exceptions; * Ver. 0.2.3 2022-03-04 kkossev - powerOnState options * Ver. 0.2.4 2022-04-16 kkossev - _TZ3000_w58g68s3 Yagusmart 3 gang zigbee switch fingerprint * Ver. 0.2.5 2022-05-28 kkossev - _TYZB01_Lrjzz1UV Zemismart 3 gang zigbee switch fingerprint; added TS0011 TS0012 TS0013 models and fingerprints; more TS002, TS003, TS004 manufacturers * Ver. 0.2.6 2022-06-03 kkossev - powerOnState and Debug logs improvements; importUrl; singleThreaded * Ver. 0.2.7 2022-06-06 kkossev - command '0B' (command response) bug fix; added Tuya Zugbee mini switch TMZ02L (_TZ3000_txpirhfq); bug fix for TS0011 single-gang switches. * Ver. 0.2.8 2022-07-13 kkossev - added _TZ3000_18ejxno0 and _TZ3000_qewo8dlz fingerprints; added TS0001 wall switches fingerprints; added TS011F 2-gang wall outlets; added switchType configuration * Ver. 0.2.9 2022-09-29 kkossev - added _TZ3000_hhiodade (ZTS-EU_1gang); added TS0001 _TZ3000_oex7egmt; _TZ3000_b9vanmes; _TZ3000_zmy4lslw * Ver. 0.2.10 2022-10-15 kkossev - _TZ3000_hhiodade fingerprint correction; added _TZ3000_ji4araar * Ver. 0.2.11 2022-11-07 kkossev - added _TZ3000_tqlv4ug4 * Ver. 0.2.12 2022-11-11 kkossev - added _TZ3000_cfnprab5 (TS011F) Xenon 4-gang + 2 USB extension; _TYZB01_vkwryfdr (TS0115) UseeLink; _TZ3000_udtmrasg (TS0003) * Ver. 0.2.13 2022-11-12 kkossev - tuyaBlackMagic() for Xenon similar to Tuya Metering Plug; _TZ3000_cfnprab5 fingerprint correction; added SiHAS and NodOn switches * Ver. 0.2.14 2022-11-23 kkossev - added 'ledMOode' command; fingerprints critical bug fix. * Ver. 0.2.15 2022-11-23 kkossev - added added _TZ3000_zmy1waw6 * Ver. 0.3.0 2023-01-07 kkossev - noBindingButPolling() for _TZ3000_fvh3pjaz _TZ3000_9hpxg80k _TZ3000_wyhuocal * Ver. 0.3.1 2023-01-22 kkossev - restored TS0003 _TZ3000_vjhcenzo fingerprint; added _TZ3000_iwhuhzdo * Ver. 0.4.0 2023-01-22 kkossev - parsing multiple attributes; * Ver. 0.4.1 2023-02-10 kkossev - IntelliJ lint; added _TZ3000_18ejxno0 third fingerprint; * Ver. 0.5.0 2023-03-13 kkossev - removed the Initialize capability and replaced it with a custom command * Ver. 0.5.1 2023-04-15 kkossev - bugfix: initialize() was not called when a new device is paired; added _TZ3000_pfc7i3kt; added TS011F _TZ3000_18ejxno0 (2 gangs); _TZ3000_zmy1waw6 bug fix; added TS011F _TZ3000_yf8iuzil (2 gangs) * Ver. 0.5.2 2023-06-10 kkossev - added TS0002 _TZ3000_5gey1ohx; unschedule all remaining jobs from previous drivers on initialize(); added _TZ3000_zigisuyh * Ver. 0.5.3 2023-10-19 kkossev - added TS0001 _TZ3000_agpdnnyd @Sekenenz; added TS011F _TZ3000_iy2c3n6p @tom.guelker * Ver. 0.5.4 2023-10-29 kkossev - added TS0002 _TZ3000_qcgw8qfa * Ver. 0.6.0 2024-01-14 kkossev - Groovy lint; TS0004 _TZ3000_a37eix1s fingerprint correction @Rafael; * Ver. 0.6.1 2024-01-29 kkossev - added TS011F _TZ3000_pmz6mjyu @g.machado * Ver. 0.7.0 2024-02-29 kkossev - (dev. branch) more Groovy lint; E000_D003 exception processing; ignored duplicated on/off events for the parent device; added ping() and rtt measurement; * * TODO: add LIDL // https://github.com/Koenkk/zigbee-herdsman-converters/blob/38bf79304292c380dc8366966aaefb71ca0b03da/src/devices/lidl.ts#L342 // https://community.hubitat.com/t/release-lidl-smart-home-drivers-with-device-health-status/86444/15?u=kkossev * TODO: check a possible problem w/ initialize() : https://community.hubitat.com/t/driver-needed-for-moes-3-gang-smart-switch-module-ms-104cz/116449/15?u=kkossev * TODO: automatic logsOff() * TODO: add healthCheck * TODO: add numberOfGangs setting * TODO: deviceProfiles */ import hubitat.device.HubAction import hubitat.device.Protocol import groovy.transform.Field import com.hubitat.app.DeviceWrapper import com.hubitat.app.ChildDeviceWrapper static String version() { '0.7.0' } static String timeStamp() { '2024/02/29 11:56 PM' } @Field static final Boolean debug = false @Field static final Integer MAX_PING_MILISECONDS = 10000 // rtt more than 10 seconds will be ignored @Field static final Integer COMMAND_TIMEOUT = 10 // timeout time in seconds metadata { definition(name: 'Zemismart ZigBee Wall Switch Multi-Gang', namespace: 'muxa', author: 'Muxa', importUrl: 'https://raw.githubusercontent.com/kkossev/hubitat-muxa-fork/development/drivers/zemismart-zigbee-multigang-switch.groovy', singleThreaded: true) { //capability "Initialize" removed 2023-03-14 capability 'Actuator' capability 'Configuration' capability 'Refresh' capability 'Switch' capability 'Health Check' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_npzfdcof', deviceJoinName: 'Tuya Zigbee Switch' // https://www.aliexpress.com/item/1005002852788275.html fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_hktqahrq', deviceJoinName: 'Tuya Zigbee Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_mx3vgyea', deviceJoinName: 'Tuya Zigbee Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_5ng23zjs', deviceJoinName: 'Tuya Zigbee Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_rmjr4ufz', deviceJoinName: 'Tuya Zigbee Switch IHSW02' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_v7gnj3ad', deviceJoinName: 'Tuya Zigbee Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_qsp2pwtf', deviceJoinName: 'Tuya Zigbee Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS000F', manufacturer: '_TZ3000_m9af2l6g', deviceJoinName: 'Tuya Zigbee Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_oex7egmt', deviceJoinName: 'Tuya 1 gang Zigbee switch MYQ-KLS01L' //https://expo.tuya.com/product/601097 fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0001', manufacturer: '_TZ3000_tqlv4ug4', deviceJoinName: 'GIRIER Tuya ZigBee 3.0 Light Switch Module' //https://community.hubitat.com/t/girier-tuya-zigbee-3-0-light-switch-module-smart-diy-breaker-1-2-3-4-gang-supports-2-way-control/104546 fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0001', manufacturer: '_TZ3000_agpdnnyd', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: 'Zemismart', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,000A,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_tas0zemd', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0003,0006,0019', model: 'TS0002', manufacturer: '_TZ3000_qcgw8qfa', deviceJoinName: 'Zemismart 2 gang smart switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,000A,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_7hp93xpr', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_7hp93xpr', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006', outClusters: '0019,000A', model: 'TS0002', manufacturer: '_TZ3000_vjhyd6ar', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_tonrapsk', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_bvrlqyj7', deviceJoinName: 'Avatto Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_atp7xmd9', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TZ3000_h34ihclt', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0002', manufacturer: '_TYZB01_wmak4qjy', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006,E000,E001', outClusters: '0019,000A', model: 'TS0002', manufacturer: '_TZ3000_qn8qvk9y', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0002', manufacturer: '_TZ3000_b9vanmes', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0002', manufacturer: '_TZ3000_tqlv4ug4', deviceJoinName: 'GIRIER Tuya ZigBee 3.0 Light Switch Module' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0702,0B04,E000,E001,0000', outClusters: '0019,000A', model: 'TS0002', manufacturer: '_TZ3000_zmy4lslw', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0006,0003,0004,0005,E001', outClusters: '0019,000A', model: 'TS0002', manufacturer: '_TZ3000_5gey1ohx', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' //https://community.hubitat.com/t/mbg-line-tuya-2ch-ln/115309?u=kkossev fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,000A,0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TYZB01_pdevogdj', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,000A,0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_pdevogdj', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_odzoiovu', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_vsasbzkf', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_34zbimxh', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_wqfdvxen', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_c0wbnbbf', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006', outClusters: '0019', model: 'TS0003', manufacturer: '_TZ3000_c0wbnbbf', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001', outClusters: '0019,000A', model: 'TS0003', manufacturer: '_TZ3000_tbfw3xj0', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0003', manufacturer: '_TZ3000_tqlv4ug4', deviceJoinName: 'GIRIER Tuya ZigBee 3.0 Light Switch Module' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0003', manufacturer: '_TZ3000_vjhcenzo', deviceJoinName: 'Tuya 3-gang Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '2101,0000', outClusters: '0021', model: 'TS0003', manufacturer: '_TZ3000_udtmrasg', deviceJoinName: 'Tuya 3-gang Switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0003', manufacturer: '_TZ3000_iwhuhzdo', deviceJoinName: 'Zemismart ZL-LU03' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0702,0B04,E000,E001,0000', outClusters: '0019,000A', model: 'TS0003', manufacturer: '_TZ3000_pfc7i3kt', deviceJoinName: 'MOES Tuya Zigebee Module' // https://community.hubitat.com/t/driver-needed-for-moes-3-gang-smart-switch-module-ms-104cz/116449?u=kkossev fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0004', manufacturer: '_TZ3000_ltt60asa', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0004', manufacturer: '_TZ3000_excgg5kb', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006,E000,E001', outClusters: '0019,000A', model: 'TS0004', manufacturer: '_TZ3000_a37eix1s', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // @Rafael fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,000A,0004,0005,0006', outClusters: '0019', model: 'TS0004', manufacturer: '_TZ3000_go9rahj5', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001', outClusters: '0019', model: 'TS0004', manufacturer: '_TZ3000_aqgofyol', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0004', manufacturer: '_TZ3000_excgg5kb' // 4-relays module fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0004', manufacturer: '_TZ3000_w58g68s3' // Yagusmart 3 gang zigbee switch fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0004', manufacturer: '_TZ3000_tqlv4ug4', deviceJoinName: 'GIRIER Tuya ZigBee 3.0 Light Switch Module' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0011', manufacturer: '_TZ3000_ybaprszv', deviceJoinName: 'Zemismart Zigbee Switch No Neutral' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0011', manufacturer: '_TZ3000_txpirhfq', deviceJoinName: 'Tuya Zigbee Mini Switch TMZ02L' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0011', manufacturer: '_TZ3000_hhiodade', deviceJoinName: 'Moes ZTS-EU_1gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0000', outClusters: '0019,000A', model: 'TS0011', manufacturer: '_TZ3000_hhiodade', deviceJoinName: 'Moes ZTS-EU_1gang' // https://community.hubitat.com/t/uk-moes-zigbee-1-2-3-or-4-gang-light-switch/89747/5?u=kkossev fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0011', manufacturer: '_TZ3000_ji4araar', deviceJoinName: 'Tuya 1 gang switch' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0000', outClusters: '0019,000A', model: 'TS0011', manufacturer: '_TZ3000_9hpxg80k', deviceJoinName: 'Tuya 1 gang' // https://github.com/zigpy/zha-device-handlers/issues/535 fingerprint profileId: '0104', endpointId: '01', inClusters: '0001,0007,0000,0003,0004,0005,0006,E000,E001,0002', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_k008kbls', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0012', manufacturer: '_TZ3000_uz5xzdgy', deviceJoinName: 'Zemismart Zigbee Switch No Neutral' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_fvh3pjaz', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006,EF00', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_lupfd8zu', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_jl7qyupf', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0000', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_18ejxno0', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006,E000,E001', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_18ejxno0', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,E000,E001,0000', outClusters: '0019,000A', model: 'TS0012', manufacturer: '_TZ3000_18ejxno0', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TYZB01_Lrjzz1UV', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TZ3000_bvrlqyj7', deviceJoinName: 'Avatto Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TZ3000_wu0shw0i', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TYZB01_stv9a4gy', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006', outClusters: '0019,000A', model: 'TS0013', manufacturer: '_TZ3000_wyhuocal', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TYZB01_mqel1whf', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TZ3000_fvh3pjaz', deviceJoinName: 'Zemismart Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TYZB01_mtlhqn48', deviceJoinName: 'Lonsonho Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: 'TUYATEC-O6SNCwd6', deviceJoinName: 'TUYATEC Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TZ3000_h34ihclt', deviceJoinName: 'Tuya Zigbee Switch Multi-Gang' // check! fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006', outClusters: '0019', model: 'TS0013', manufacturer: '_TZ3000_k44bsygw', deviceJoinName: 'Zemismart Zigbee Switch No Neutral' fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0000', outClusters: '0019,000A', model: 'TS0013', manufacturer: '_TZ3000_qewo8dlz', deviceJoinName: 'Tuya Zigbee Switch 3 Gang No Neutral' // @dingyang.yee https://www.aliexpress.com/item/4000298926256.html https://github.com/Koenkk/zigbee2mqtt/issues/6138#issuecomment-774720939 /* these do NOT work with Hubitat ! fingerprint profileId:"0104", endpointId:"01", inClusters:"0003,0004,0005,0006,E000,E001,0000", outClusters:"0019,000A", model:"TS011F", manufacturer:"_TZ3000_cfnprab5", deviceJoinName: "Xenon 4-gang + 2 USB extension" //https://community.hubitat.com/t/xenon-4-gang-2-usb-extension-unable-to-switch-off-individual-sockets/101384/14?u=kkossev fingerprint profileId:"0104", endpointId:"01", inClusters:"0000,0006,0003,0004,0005,E001", outClusters:"0019,000A", model:"TS011F", manufacturer:"_TZ3000_cfnprab5", deviceJoinName: "Xenon 4-gang + 2 USB extension" //https://community.hubitat.com/t/xenon-4-gang-2-usb-extension-unable-to-switch-off-individual-sockets/101384/14?u=kkossev */ fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0004,0005,0006', outClusters: '0019,000A', model: 'TS011F', manufacturer: '_TZ3000_zmy1waw6', deviceJoinName: 'Moes 1 gang' // https://github.com/zigpy/zha-device-handlers/issues/1262 fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0702,0B04,E000,E001,0000', outClusters: '0019,000A', model: 'TS011F', manufacturer: '_TZ3000_18ejxno0', deviceJoinName: 'Moes 2 gang' // https://pl.aliexpress.com/item/1005002061628356.html fingerprint profileId: '0104', endpointId: '01', inClusters: '0003,0004,0005,0006,0702,0B04,E000,E001,0000', outClusters: '0019,000A', model: 'TS011F', manufacturer: '_TZ3000_yf8iuzil', deviceJoinName: 'Moes 2 gang' // https://community.hubitat.com/t/moes-zigbee-wall-touch-smart-light-switch/97870/36?u=kkossev fingerprint profileId:'0104', endpointId:'01', inClusters:'0003,0004,0005,0006,E000,E001,0000', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_v1pdxuqq', deviceJoinName: 'XH-002P Outlet TS011F No Power Monitoring' // - no power monitoring ! fingerprint profileId:'0104', endpointId:'01', inClusters:'0003,0004,0005,0006,E000,E001,0000', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_hyfvrar3', deviceJoinName: 'TS011F No Power Monitoring' // - no power monitoring ! fingerprint profileId:'0104', endpointId:'01', inClusters:'0003,0004,0005,0006,E000,E001,0000', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_cymsnfvf', deviceJoinName: 'TS011F No Power Monitoring' // - no power monitoring ! fingerprint profileId:'0104', endpointId:'01', inClusters:'0003,0004,0005,0006,E000,E001,0000', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_bfn1w0mm', deviceJoinName: 'TS011F No Power Monitoring' // - no power monitoring ! fingerprint profileId:'0104', endpointId:'01', inClusters:'0003,0004,0005,0006,E000,E001,0000', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_zigisuyh', deviceJoinName: 'Wall Outlet with USB Universal' // https://zigbee.blakadder.com/Zemismart_B90.html fingerprint profileId:'0104', endpointId:'01', inClusters:'0003,0004,0005,0006,E000,E001,0000', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_iy2c3n6p', deviceJoinName: 'Tuya Zigbee dual outlet wall socket' // https://community.hubitat.com/t/dual-zigbee-outlet/126216?u=kkossev fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006,E000,E001', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_pmz6mjyu', deviceJoinName: 'Tuya Zigbee dual outlet wall socket' // @g.machado fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_vzopcetz', deviceJoinName: 'Silvercrest 3 gang switch, with 4 USB (CZ)' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_vmpbygs5', deviceJoinName: 'Silvercrest 3 gang switch, with 4 USB (BS)' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_4uf3d0ax', deviceJoinName: 'Silvercrest 3 gang switch, with 4 USB (FR)' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_1obwwnmq', deviceJoinName: 'Silvercrest 3 gang switch, with 4 USB' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_oznonj5q', deviceJoinName: 'Silvercrest 3 gang switch, with 4 USB' fingerprint profileId:'0104', endpointId:'01', inClusters:'0000,0003,0004,0005,0006', outClusters:'0019,000A', model:'TS011F', manufacturer:'_TZ3000_wzauvbcs', deviceJoinName: 'Silvercrest 3 gang switch, with 4 USB (EU)' // fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,000A,0004,0005,0006', outClusters: '0019', model: 'TS0115', manufacturer: '_TYZB01_vkwryfdr', deviceJoinName: 'UseeLink Power Strip' //https://community.hubitat.com/t/another-brick-in-the-wall-tuya-joins-the-zigbee-alliance/44152/28?u=kkossev // SiHAS Switch (2~6 Gang) fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006,0019', outClusters: '0003,0004,0019', manufacturer: 'ShinaSystem', model: 'SBM300Z2', deviceJoinName: 'SiHAS Switch 2-gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006,0019', outClusters: '0003,0004,0019', manufacturer: 'ShinaSystem', model: 'SBM300Z3', deviceJoinName: 'SiHAS Switch 3-gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006,0019', outClusters: '0003,0004,0019', manufacturer: 'ShinaSystem', model: 'SBM300Z4', deviceJoinName: 'SiHAS Switch 4-gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006,0019', outClusters: '0003,0004,0019', manufacturer: 'ShinaSystem', model: 'SBM300Z5', deviceJoinName: 'SiHAS Switch 5-gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006,0019', outClusters: '0003,0004,0019', manufacturer: 'ShinaSystem', model: 'SBM300Z6', deviceJoinName: 'SiHAS Switch 6-gang' fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0006,0019', outClusters: '0003,0004,0019', manufacturer: 'ShinaSystem', model: 'ISM300Z3', deviceJoinName: 'SiHAS Switch 3-gang' // NodOn fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006,0007,0008,1000,FC57', outClusters: '0003,0006,0019', manufacturer: 'NodOn', model: 'SIN-4-2-20', deviceJoinName: 'NodOn Light 2 channels' // https://nodon.pro/en/produits/zigbee-pro-on-off-lighting-relay-switch/ fingerprint profileId: '0104', endpointId: '01', inClusters: '0000,0003,0004,0005,0006,0007,0008,1000,FC57', outClusters: '0003,0006,0019', manufacturer: 'NodOn', model: 'SIN-4-2-20_PRO', deviceJoinName: 'NodOn Light 2 channels' command 'powerOnState', [ [name: 'powerOnState', type: 'ENUM', constraints: ['--- Select ---', 'OFF', 'ON', 'Last state'], description: 'Select Power On State'] ] command 'switchType', [ [name: 'switchType', type: 'ENUM', constraints: ['--- Select ---', 'toggle', 'state', 'momentary'], description: 'Select Switch Type'] // 0: 'toggle', 1: 'state', 2: 'momentary' ] command 'ledMode', [ [name: 'ledMode', type: 'ENUM', constraints: ['--- Select ---', 'Disabled', 'Lit when On', 'Lit when Off'], description: 'Select LED Mode'] ] command 'initialize', [[name: "Select 'Yes' to re-initialize the device", type: 'ENUM', description: 're-creates the child devices!', constraints: ['--- Select ---', 'Yes', 'No']]] if (debug == true) { command 'test', ['string'] } //attribute 'lastCheckin', 'string' attribute 'rtt', 'number' } preferences { input(name: 'txtEnable', type: 'bool', title: 'Enable description text logging', defaultValue: true) input(name: 'logEnable', type: 'bool', title: 'Enable debug logging', defaultValue: true) input(title: 'IMPORTANT', description: 'In order to operate normally, please pair the device to HE after changing to this driver!', type: 'paragraph', element: 'paragraph') } } /* groovylint-disable-next-line UnusedPrivateMethod */ private boolean isHEProblematic() { device.getDataValue('manufacturer') in ['_TZ3000_okaz9tjs', '_TZ3000_r6buo8ba', '_TZ3000_cfnprab5', 'SONOFF', 'Woolley', 'unknown'] } private boolean noBindingButPolling() { device.getDataValue('manufacturer') in ['_TZ3000_fvh3pjaz', '_TZ3000_9hpxg80k', '_TZ3000_wyhuocal'] } //0x4001 OnTime: value 0 //0x4002 OffWaitTime: value 0 // Parse incoming device messages to generate events void parse(String description) { checkDriverVersion() unschedule('deviceCommandTimeout') Map descMap = [:] try { descMap = zigbee.parseDescriptionAsMap(description) } catch (e) { if (settings?.logEnable) { log.warn "${device.displayName} exception caught while parsing description ${description} \r descMap: ${descMap}" } return } logDebug "Parsed descMap: ${descMap} (description:${description})" if (descMap.attrId != null) { //log.trace "parsing descMap.attrId ${descMap.attrId}" parseAttributes(descMap) return } else if (descMap?.clusterId == '0013' && descMap?.profileId != null && descMap?.profileId == '0000') { logInfo "device model ${device.data.model}, manufacturer ${device.data.manufacturer} re-joined the network (deviceNetworkId ${device.properties.deviceNetworkId}, zigbeeId ${device.properties.zigbeeId})" } else if (descMap?.clusterId == '0006' && descMap?.profileId != null && descMap?.profileId == '0000') { logInfo "Match Descriptor Request (deviceNetworkId ${device.properties.deviceNetworkId}, zigbeeId ${device.properties.zigbeeId})" } else if (descMap?.clusterId == '8021' && descMap?.profileId != null && descMap?.profileId == '0000') { logInfo "Bind Response (deviceNetworkId ${device.properties.deviceNetworkId}, zigbeeId ${device.properties.zigbeeId})" } else if (descMap?.profileId != null && descMap?.command == '07') { parseConfigureResponse(descMap) } else { logDebug "${device.displayName} unprocessed EP: ${descMap.sourceEndpoint} cluster: ${descMap.clusterId} command: ${descMap?.command} attrId: ${descMap.attrId}" } } void parseAttributes(final Map descMap) { // attribute report received List attrData = [[cluster: descMap.cluster, attrId: descMap.attrId, value: descMap.value, status: descMap.status]] descMap.additionalAttrs.each { attrData << [cluster: descMap.cluster, attrId: it.attrId, value: it.value, status: it.status] } attrData.each { parseSingleAttribute(it, descMap) } } private void parseSingleAttribute(final Map it, final Map descMap) { //log.trace "parseSingleAttribute :${it}" if (it.status == '86') { log.warn "${device.displayName} Read attribute response: unsupported Attributte ${it.attrId} cluster ${descMap.cluster}" return } switch (it.cluster) { case '0000': parseBasicClusterAttribute(it) break case '0006': //log.warn "case cluster 0006" switch (it.attrId) { case '0000': //log.warn "case cluster 0006 attrId 0000" processOnOff(it, descMap) break default: //log.warn "case cluster 0006 attrId ${it.attrId}" processOnOfClusterOtherAttr(it) break } break case '0008': if (logEnable) { log.warn "${device.displayName} may be a dimmer? This is not the right driver...cluster:${cluster} attrId ${it.attrId} value:${it.value}" } break case '0300': if (logEnable) { log.warn "${device.displayName} may be a bulb? This is not the right driver...cluster:${cluster} attrId ${it.attrId} value:${it.value}" } break case '0702': case '0B04': if (logEnable) { log.warn "${device.displayName} may be a power monitoring socket? This is not the right driver...cluster:${cluster} attrId ${it.attrId} value:${it.value}" } break case 'E000': case 'E001': processOnOfClusterOtherAttr(it) break case 'EF00': // Tuya cluster log.warn "${device.displayName} NOT PROCESSED Tuya Cluster EF00 attribute ${it.attrId}\n descMap = ${descMap}" break case 'FFFD': // TuyaClusterRevision if (logEnable) { log.warn "${device.displayName} Tuya Cluster Revision cluster:${cluster} attrId ${it.attrId} value:${it.value}" } break case 'FFFE': // AttributeReportingStatus if (logEnable) { log.warn "${device.displayName} Tuya Attribute Reporting Status cluster:${cluster} attrId ${it.attrId} value:${it.value}" } break default: if (logEnable) { String respType = (command == '0A') ? 'reportResponse' : 'readAttributeResponse' log.warn "${device.displayName} parseAttributes: NOT PROCESSED: cluster ${descMap.cluster} attribite:${it.attrId}, value:${it.value}, encoding:${it.encoding}, respType:${respType}" } break } // it.cluster } void parseBasicClusterAttribute(final Map it) { // https://github.com/zigbeefordomoticz/Domoticz-Zigbee/blob/6df64ab4656b65ec1a450bd063f71a350c18c92e/Modules/readClusters.py switch (it.attrId) { case '0000': logDebug "ZLC version: ${it.value}" // default 0x03 break case '0001': if (state.states == null) { state.states = [:] } if (state.stats == null) { state.stats = [:] } if (state.lastTx == null) { state.lastTx = [:] } Long now = new Date().getTime() boolean isPing = state.states['isPing'] ?: false if (isPing) { def timeRunning = now.toInteger() - (state.lastTx['pingTime'] ?: '0').toInteger() if (timeRunning > 0 && timeRunning < MAX_PING_MILISECONDS) { state.stats['pingsOK'] = (state.stats['pingsOK'] ?: 0) + 1 if (timeRunning < safeToInt((state.stats['pingsMin'] ?: '999'))) { state.stats['pingsMin'] = timeRunning } if (timeRunning > safeToInt((state.stats['pingsMax'] ?: '0'))) { state.stats['pingsMax'] = timeRunning } state.stats['pingsAvg'] = approxRollingAverage(safeToDouble(state.stats['pingsAvg']), safeToDouble(timeRunning)) as int sendRttEvent() } else { logWarn "unexpected ping timeRunning=${timeRunning} " } state.states['isPing'] = false } logDebug "Applicaiton version: ${it.value}" // For example, 0b 01 00 0001 = 1.0.1, where 0x41 is 1.0.1 break // https://developer.tuya.com/en/docs/iot-device-dev/tuya-zigbee-lighting-dimmer-swith-access-standard?id=K9ik6zvlvbqyw case '0002': logDebug "Stack version: ${it.value}" // default 0x02 break case '0003': logDebug "HW version: ${it.value}" // default 0x01 break case '0004': logDebug "Manufacturer name: ${it.value}" break case '0005': logDebug "Model Identifier: ${it.value}" break case '0006': logDebug "DateCode: ${it.value}" break case '0007': logDebug "Power Source: ${it.value}" // enum8-0x30 default 0x03 break case '4000': //software build logDebug "softwareBuild: ${it.value}" //updateDataValue("$LAB softwareBuild",it.value ?: "unknown") break case 'FFE2': case 'FFE4': logDebug "Attribite ${it.attrId} : ${it.value}" break case 'FFFD': // Cluster Revision (Tuya specific) logDebug "Cluster Revision 0xFFFD: ${it.value}" //uint16 -0x21 default 0x0001 break case 'FFFE': // Tuya specific logDebug "Tuya specific 0xFFFE: ${it.value}" break default: if (logEnable) { log.warn "${device.displayName} parseBasicClusterAttribute cluster:${cluster} UNKNOWN attrId ${it.attrId} value:${it.value}" } } } @Field static final int ROLLING_AVERAGE_N = 10 BigDecimal approxRollingAverage(BigDecimal avg, BigDecimal newSample) { if (avg == null || avg == 0) { avg = newSample } avg -= avg / ROLLING_AVERAGE_N avg += newSample / ROLLING_AVERAGE_N return avg } /** * sends 'rtt'event (after a ping() command) * @param null: calculate the RTT in ms * value: send the text instead ('timeout', 'n/a', etc..) * @return none */ void sendRttEvent( String value=null) { def now = new Date().getTime() if (state.stats == null ) { state.stats = [:] } if (state.lastTx == null ) { state.lastTx = [:] } def timeRunning = now.toInteger() - (state.lastTx['pingTime'] ?: now).toInteger() def descriptionText = "Round-trip time is ${timeRunning} ms (min=${state.stats['pingsMin']} max=${state.stats['pingsMax']} average=${state.stats['pingsAvg']})" if (value == null) { logInfo "${descriptionText}" sendEvent(name: 'rtt', value: timeRunning, descriptionText: descriptionText, unit: 'ms', isDigital: true) } else { descriptionText = "Round-trip time : ${value}" logInfo "${descriptionText}" sendEvent(name: 'rtt', value: value, descriptionText: descriptionText, isDigital: true) } } void ping() { if (state.lastTx == null ) { state.lastTx = [:] } state.lastTx['pingTime'] = new Date().getTime() if (state.states == null ) { state.states = [:] } state.states['isPing'] = true scheduleCommandTimeoutCheck() sendZigbeeCommands(zigbee.readAttribute(zigbee.BASIC_CLUSTER, 0x01, [:], 0)) logDebug 'ping...' } private void scheduleCommandTimeoutCheck(int delay = COMMAND_TIMEOUT) { runIn(delay, 'deviceCommandTimeout') } void deviceCommandTimeout() { logWarn 'no response received (sleepy device or offline?)' sendRttEvent('timeout') state.stats['pingsFail'] = (state.stats['pingsFail'] ?: 0) + 1 } /** * Zigbee Configure Reporting Response Parsing - command 0x07 */ void parseConfigureResponse(final Map descMap) { // TODO - parse the details of the configuration respose - cluster, min, max, delta ... final String status = ((List)descMap.data).first() final int statusCode = hexStrToUnsignedInt(status) final String statusName = statusCode == 0 ? "Success" : "0x${status}" if (statusCode > 0x00) { log.warn "zigbee configure reporting error: ${statusName} ${descMap.data}" } else { if (logEnable) { logInfo "zigbee configure reporting response: ${statusName} ${descMap.data}" } } } /* groovylint-disable-next-line UnusedMethodParameter */ void processOnOff(final Map it, final Map descMap) { // descMap.command =="0A" - switch toggled physically // descMap.command =="01" - get switch status // descMap.command =="0B" - command response ChildDeviceWrapper cd = getChildDevice("${device.id}-${descMap.endpoint}") if (cd == null) { if (!(device.data.model in ['TS0011', 'TS0001'])) { log.warn "${device.displayName} Child device ${device.id}-${descMap.endpoint} not found. Initialise parent device first" return } } String switchAttribute = descMap.value == '01' ? 'on' : 'off' if (cd != null) { if (descMap.command in ['0A', '0B']) { // switch toggled cd.parse([[name: 'switch', value: switchAttribute, descriptionText: "Child switch ${descMap.endpoint} turned $switchAttribute"]]) } else if (descMap.command == '01') { // report switch status cd.parse([[name: 'switch', value: switchAttribute, descriptionText: "Child switch ${descMap.endpoint} is $switchAttribute"]]) } } if (switchAttribute == 'on') { if (device.currentValue('switch') == 'on') { logDebug 'switch is already on' return } sendEvent(name: 'switch', value: 'on') logInfo 'switch is on' return } else if (switchAttribute == 'off') { int cdsOn = 0 // cound number of switches on getChildDevices().each { child -> if (getChildId(child) != descMap.endpoint && child.currentValue('switch') == 'on') { cdsOn++ } } if (cdsOn == 0) { if (device.currentValue('switch') == 'off') { logDebug 'switch is already off' return } sendEvent(name: 'switch', value: 'off') logInfo 'switch is off' return } } } void off() { if (settings?.txtEnable) { log.info "${device.displayName} Turning all child switches off" } sendZigbeeCommands(["he cmd 0x${device.deviceNetworkId} 0xFF 0x0006 0x0 {}"]) } void on() { if (settings?.txtEnable) { log.info "${device.displayName} Turning all child switches on" } sendZigbeeCommands(["he cmd 0x${device.deviceNetworkId} 0xFF 0x0006 0x1 {}"]) } void refresh() { logDebug 'refreshing' sendZigbeeCommands(["he rattr 0x${device.deviceNetworkId} 0xFF 0x0006 0x0"]) } /* groovylint-disable-next-line UnusedPrivateMethod */ private Integer convertHexToInt(String hex) { Integer.parseInt(hex, 16) } private String getChildId(DeviceWrapper childDevice) { return childDevice.deviceNetworkId.substring(childDevice.deviceNetworkId.length() - 2) } void componentOn(DeviceWrapper childDevice) { logDebug "sending componentOn ${childDevice.deviceNetworkId}" sendHubCommand(new HubAction("he cmd 0x${device.deviceNetworkId} 0x${getChildId(childDevice)} 0x0006 0x1 {}", Protocol.ZIGBEE)) } void componentOff(DeviceWrapper childDevice) { logDebug "sending componentOff ${childDevice.deviceNetworkId}" sendHubCommand(new HubAction("he cmd 0x${device.deviceNetworkId} 0x${getChildId(childDevice)} 0x0006 0x0 {}", Protocol.ZIGBEE)) } void componentRefresh(DeviceWrapper childDevice) { logDebug "sending componentRefresh ${childDevice.deviceNetworkId} ${childDevice}" sendHubCommand(new HubAction("he rattr 0x${device.deviceNetworkId} 0x${getChildId(childDevice)} 0x0006 0x0", Protocol.ZIGBEE)) } void setupChildDevices() { logDebug 'Parent setupChildDevices' deleteObsoleteChildren() Integer buttons = 0 switch (device.data.model) { case 'SBM300Z6': buttons = 6 break case 'TS011F': if (device.data.manufacturer == '_TZ3000_zmy1waw6') { buttons = 0 } else if (device.data.manufacturer in ['_TZ3000_18ejxno0', '_TZ3000_yf8iuzil', '_TZ3000_iy2c3n6p', '_TZ3000_pmz6mjyu']) { buttons = 2 } else if (device.data.manufacturer in ['_TZ3000_wzauvbcs', '_TZ3000_oznonj5q', '_TZ3000_1obwwnmq', '_TZ3000_4uf3d0ax', '_TZ3000_vzopcetz', '_TZ3000_vmpbygs5']) { buttons = 3 // LIDL Silvercrest 3 gang switch, with 4 USB (EU, FR, CZ, BS) // https://github.com/Koenkk/zigbee-herdsman-converters/blob/38bf79304292c380dc8366966aaefb71ca0b03da/src/devices/lidl.ts#L342 } else { buttons = 5 } break case 'TS0115': case 'SBM300Z5': buttons = 5 break case 'TS0004': case 'TS0014': case 'SBM300Z4': buttons = 4 break case 'TS0003': case 'TS0013': case 'SBM300Z3': case 'ISM300Z3': buttons = 3 break case 'TS0002': case 'TS0012': case 'SBM300Z2': case 'SIN-4-2-20': case 'SIN-4-2-20_PRO': buttons = 2 break case 'TS0011': case 'TS0001': buttons = 0 break default: break } logInfo "model: ${device.data.model} gangs:${buttons == 0 ? 1 : buttons} child devices: ${buttons}" createChildDevices((int) buttons) } /* groovylint-disable-next-line BuilderMethodWithSideEffects, FactoryMethodName */ void createChildDevices(int buttons) { logDebug 'Parent createChildDevices' if (buttons == 0) { return } for (i in 1..buttons) { String childId = "${device.id}-0${i}" DeviceWrapper existingChild = getChildDevices()?.find { it.deviceNetworkId == childId } if (existingChild) { log.info "${device.displayName} Child device ${childId} already exists (${existingChild})" } else { log.info "${device.displayName} Creatung device ${childId}" addChildDevice('hubitat', 'Generic Component Switch', childId, [isComponent: true, name: "Switch EP0${i}", label: "${device.displayName} EP0${i}"]) } } } void deleteObsoleteChildren() { logDebug 'Parent deleteChildren' getChildDevices().each { child -> if (!child.deviceNetworkId.startsWith(device.id) || child.deviceNetworkId == "${device.id}-00") { log.info "${device.displayName} Deleting ${child.deviceNetworkId}" deleteChildDevice(child.deviceNetworkId) } } } static String driverVersionAndTimeStamp() { version() + ' ' + timeStamp() } void checkDriverVersion() { if (state.driverVersion == null || (driverVersionAndTimeStamp() != state.driverVersion)) { if (txtEnable == true) { log.debug "${device.displayName} updating the settings from the current driver ${device.properties.typeName} version ${state.driverVersion} to the new version ${driverVersionAndTimeStamp()} [model ${device.data.model}, manufacturer ${device.data.manufacturer}, application ${device.data.application}, endpointId ${device.endpointId}]" } initializeVars(fullInit = false) state.driverVersion = driverVersionAndTimeStamp() } } void initializeVars(boolean fullInit = true) { if (settings?.txtEnable) { log.info "${device.displayName} InitializeVars()... fullInit = ${fullInit}" } if (fullInit == true) { unschedule() state.clear() state.driverVersion = driverVersionAndTimeStamp() } if (settings?.logEnable == null) { device.updateSetting('logEnable', true) } if (settings?.txtEnable == null) { device.updateSetting('txtEnable', true) } } void initialize(final String str) { if (str == 'Yes') { initialize() } else { logInfo "initialize aborted! (str=${str})" } } void initialize() { logDebug 'Initializing...' initializeVars(fullInit = true) configure() // added 11/12/2022 setupChildDevices() } void installed() { logInfo "Parent installed, typeName ${device.properties.typeName}, version ${driverVersionAndTimeStamp()}, deviceNetworkId ${device.properties.deviceNetworkId}, zigbeeId ${device.properties.zigbeeId}" logInfo "model ${device.data.model}, manufacturer ${device.data.manufacturer}, application ${device.data.application}, endpointId ${device.endpointId}" initialize() } void updated() { logDebug 'Parent updated' } List tuyaBlackMagic() { List cmds = [] cmds += zigbee.readAttribute(0x0000, [0x0004, 0x0000, 0x0001, 0x0005, 0x0007, 0xfffe], [:], delay = 200) cmds += zigbee.writeAttribute(0x0000, 0xffde, 0x20, 0x0d, [destEndpoint: 0x01], delay = 50) return cmds } void configure() { logDebug ' configure()..' List cmds = [] if (device.data.manufacturer in ['_TZ3000_cfnprab5', '_TZ3000_okaz9tjs']) { log.warn "this device ${device.data.manufacturer} is known to NOT work with HE!" } cmds += tuyaBlackMagic() if (noBindingButPolling()) { //these will send out device anounce message at ervery 2 mins as heart beat, setting 0x0099 to 1 will disable it. cmds += zigbee.writeAttribute(zigbee.BASIC_CLUSTER, 0x0099, 0x20, 0x01, [mfgCode: 0x0000]) // Hack : Need to disable reporting for thoses devices, else It will enable a auto power off after 2mn. // see https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3693 // https://github.com/Mariano-Github/Edge-Drivers-Beta/blob/652bcfbcf7b8ab8a14557e097b740216760f2b02/zigbee-multi-switch-v4-childs/src/init.lua log.warn "disabling ${device.data.manufacturer} device announce message every 2 mins and skipping reporting configuiration!" cmds += zigbee.onOffRefresh() } else { //cmds += refresh() cmds += zigbee.onOffConfig() cmds += zigbee.onOffRefresh() } sendZigbeeCommands(cmds) } void sendZigbeeCommands(List cmds) { logDebug "sendZigbeeCommands : ${cmds}" sendHubCommand(new hubitat.device.HubMultiAction(cmds, hubitat.device.Protocol.ZIGBEE)) } static Integer safeToInt(val, Integer defaultVal=0) { return "${val}"?.isInteger() ? "${val}".toInteger() : defaultVal } static Double safeToDouble(val, Double defaultVal=0.0) { return "${val}"?.isDouble() ? "${val}".toDouble() : defaultVal } static BigDecimal safeToBigDecimal(val, BigDecimal defaultVal=0.0) { return "${val}"?.isBigDecimal() ? "${val}".toBigDecimal() : defaultVal } void logDebug(final String msg) { String sDnMsg = device?.displayName + ' ' + msg if (settings?.logEnable) { log.debug sDnMsg } } void logWarn(final String msg) { String sDnMsg = device?.displayName + ' ' + msg if (settings?.logEnable) { log.warn sDnMsg } } void logInfo(final String msg) { String sDnMsg = device?.displayName + ' ' + msg if (settings?.txtEnable) { log.info sDnMsg } } void powerOnState(final String relayMode) { List cmds = [] int modeEnum = 99 switch (relayMode) { case 'OFF': modeEnum = 0 break case 'ON': modeEnum = 1 break case 'Last state': modeEnum = 2 break default: log.error "${device.displayName} please select a Power On State option" return } logDebug("setting Power On State option to: ${relayMode} (${modeEnum}") cmds += zigbee.writeAttribute(0x0006, 0x8002, DataType.ENUM8, modeEnum) sendZigbeeCommands(cmds) } void switchType(final String type) { List cmds = [] int modeEnum = 99 switch (type) { case 'toggle': modeEnum = 0 break case 'state': modeEnum = 1 break case 'momentary': modeEnum = 2 break default: log.error "${device.displayName} please select a Switch Type" return } logDebug("setting Switch Type to: ${type} (${modeEnum})") cmds += zigbee.writeAttribute(0xE001, 0xD030, DataType.ENUM8, modeEnum) sendZigbeeCommands(cmds) } // mode = value == 0 ? "Disabled" : value == 1 ? "Lit when On" : value == 2 ? "Lit when Off" : null // [name:"ledMode", type: "ENUM", constraints: ["--- Select ---", "Disabled", "Lit when On", "Lit when Off], description: "Select LED Mode"] void ledMode(final String mode) { List cmds = [] int modeEnum = 99 switch (mode) { case 'Disabled': modeEnum = 0 break case 'Lit when On': modeEnum = 1 break case 'Lit when Off': modeEnum = 2 break default: log.error "${device.displayName} please select a LED mode option" return } logDebug("setting LED mode option to: ${mode} (${modeEnum})") cmds += zigbee.writeAttribute(0x0006, 0x8001, DataType.ENUM8, modeEnum) sendZigbeeCommands(cmds) } void processOnOfClusterOtherAttr(final Map it) { logDebug "processOnOfClusterOtherAttr attribute ${it}" String mode = null String attrName = null Integer value = null String valueStr = it.value try { value = Integer.parseInt(it.value) } catch (e) { logDebug "processOnOfClusterOtherAttr: EXCEPTION ${e} while processing attrId: ${it.attrId} value: ${it.value} as Integer. Using valueStr instead." } String clusterPlusAttr = it.cluster + '_' + it.attrId //log.trace "clusterPlusAttr = ${clusterPlusAttr}" switch (clusterPlusAttr) { case '0006_4001': case '0006_4002': attrName = 'attribute ' + clusterPlusAttr mode = value.toString() break case '0006_8000': attrName = 'Child Lock' mode = value == 0 ? 'off' : 'on' break case '0006_8001': attrName = 'LED mode' mode = value == 0 ? 'Disabled' : value == 1 ? 'Lit when On' : value == 2 ? 'Lit when Off' : null break case '0006_8002': attrName = 'Power On State' mode = value == 0 ? 'off' : value == 1 ? 'on' : value == 2 ? 'Last state' : null break case 'E000_D001': case 'E000_D002': case 'E000_D003': // encoding:42, command:0A, value:AAAA, attrName = 'attribute ' + clusterPlusAttr mode = value == null ? valueStr : value.toString() break case 'E001_D030': attrName = 'Switch Type' mode = value == 0 ? 'toggle' : value == 1 ? 'state' : value == 2 ? 'momentary state' : null break default: logDebug "processOnOfClusterOtherAttr: UNPROCESSED On/Off Cluster attrId: ${it.attrId} value: ${it.value}" return } if (logEnable) { log.info "${device.displayName} ${attrName} is: ${mode} (${value != null ? value : '?'})" } } void test(final String description) { log.warn "testing ${description}" parse(description) }