/* groovylint-disable BuilderMethodWithSideEffects, CompileStatic, DuplicateListLiteral, DuplicateMapLiteral, DuplicateNumberLiteral, DuplicateStringLiteral, FactoryMethodName, ImplicitClosureParameter, ImplicitReturnStatement, InsecureRandom, LineLength, MethodCount, MethodParameterTypeRequired, MethodReturnTypeRequired, NestedBlockDepth, NoDef, NoJavaUtilDate, ParameterName, PublicMethodsBeforeNonPublicMethods, UnnecessaryGetter, UnnecessaryPackageReference, VariableTypeRequired */ /* * revision 1.0.0 - 2021-05-25 - martinkura - latest original driver version update * revision 1.0.1 - 2022-02-22 - kkossev - added Moes 4-Gang Switch / ZTS-EU4 * revision 1.0.2 - 2022-02-27 - kkossev - added more Tuya fingerprints for 1,2,3 and 4 gangs TS0601 wall switches * revision 1.0.3 - 2022-09-26 - kkossev - added Zemismart 6 Gangs Wall Light Switch * revision 1.0.4 - 2022-10-12 - kkossev - _TZE200_tz32mtza bug fix; code cleanup * revision 1.0.5 - 2023-03-16 - kkossev - added OZ Smart 1-2-3-4 gang switches _TZE200_gbagoilo _TZE200_nh9m9emk _TZE200_go3tvswy _TZE200_mexisfik * revision 1.0.6 - 2023-04-24 - kkossev - added importUrl; _TZE200_aqnazj70 _TZE200_wunufsil _TZE200_oisqyl4o _TZE200_atpwqgml * revision 1.0.7 - 2023-07-18 - kkossev - added _TZE200_7deq70b8 (@pabutterworth) * revision 1.0.8 - 2023-11-20 - kkossev - added TS0601 _TZE204_dqolcpcp (@alex1) (only the first 6 relays should be working) * revision 1.0.9 - 2024-01-02 - kkossev - added TS0601 _TZE200_r731zlxk * revision 1.1.0 - 2024-03-08 - kkossev - Groovy linting; addeed Zemismart 4-gang switch _TZE200_1n2kyphz; _TZE200_shkxsgis; _TZE204_shkxsgis; added testParse; Tuya cluster 0xEF00 data size check; * revision 1.1.1 - 2024-04-03 - kkossev - added TS0601 _TZE200_m*******j @Joao; * revision 1.1.2 - 2024-04-28 - kkossev - removed the T3E fingerprint; added _TZE204_aagrxlbd; * revision 1.1.3 - 2024-05-06 - kkossev - added _TZE204_xjknlqz8 * revision 1.1.4 - 2024-05-17 - hhorigian - added _TZE204_4cl0dzt4 * revision 1.1.5 - 2024-06-28 - kkossev - added TS0006 _TYZB01_ltundz9m _TZ3000_jyupj3fw * revision 1.1.6 - 2024-07-22 - hhorigian - added _TZE204_rkbxtclc, _TZE204_zpvusbtv, _TZE204_rzdkn5rxc * revision 1.1.7 - 2024-08-20 - kkossev - added _TZE204_dvosyycn (limited to 6 relays only); * revision 1.2.0 - 2024-08-20 - kkossev - added syncTuyaDateTime() * revision 1.3.0 - 2024-08-30 - kkossev/hhorigian - TratoZemismartKeypad configuration options * revision 1.3.1 - 2024-09-07 - kkossev - added Mercator Ikuü six switch _TZE200_wnp4d4va @hpgurgel * revision 1.3.2 - 2024-09-29 - kkossev/hhorigian - testing _TZE204_hwyydvqm (device is now handled in the 'Tuya Zigbee Dimmer' driver!) * revision 1.3.3 - 2024-10-23 - kkossev - added TS0601 _TZE204_lmgrbuwf (NOVADIGITAL) * revision 1.3.4 - 2025-04-07 - kkossev - added TS0601 _TZE204_g4au0afs (NOVADIGITAL); added TS0601 _TZE200_mwvfvw8g _TZE200_cduqh1l0 _TZE200_emxxanvi ; added TS0601 _TZE200_kyfqmmyl _TZE200_2hf7x9n3 _TZE204_atpwqgml _TZE200_bynnczcb * revision 1.3.5 - 2025-06-07 - kkossev - added TS0601 _TZE204_cduqh1l0 * revision 1.4.0 - 2025-08-30 - kkossev - (dev.) added Moes Star Feather Smart Switches SFL02-Z (1,2,3,4 gangs); added Nova Digital Topazio Smart Switches (without the 'scene mode') */ static String version() { '1.4.0' } static String timeStamp() { '2025/08/30 6:37 PM' } import hubitat.device.HubAction import hubitat.device.Protocol import groovy.transform.Field @Field static final Boolean _DEBUG = false metadata { definition(name: 'Moes ZigBee Wall Switch 1/2/3-Gang', namespace: 'Moes 1.31', author: 'Martin Kura', importUrl: 'https://raw.githubusercontent.com/martinkura-svk/Hubitat/main/Moes%20ZigBee%20Wall%20Switch') { capability 'Initialize' capability 'Actuator' capability 'Refresh' capability 'Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_amp6tsvy', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes 1-Gang Switch / ZTS-EU1' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_oisqyl4o', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'No Neutral Push Button Light Switch 1 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_g1ib5ldv', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes 2-Gang Switch / ZTS-EU2' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_wunufsil', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'No Neutral Push Button Light Switch 2 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_tz32mtza', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes 3-Gang Switch / ZTS-EU3' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_atpwqgml', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'No Neutral Push Button Light Switch 3 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_k6jhsr0q', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes 4-Gang Switch / ZTS-EU4' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_aqnazj70', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Touch Switch 4 Gang No Neutral' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_9mahtqtg', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 6 Gangs Wall Light Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_wnp4d4va', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Mercator Ikuü Six Switch' // https://community.hubitat.com/t/driver-needed-for-a-6-gang-mercator-ikuu-zigbee-switch/142341/3?u=kkossev fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_r731zlxk', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 6 Gangs Wall Light Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_gbagoilo', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'OZ Smart Single Light Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_nh9m9emk', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'OZ Smart Double Light Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_go3tvswy', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'OZ Smart Triple Light Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_mexisfik', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'OZ Smart Quad Light Switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_7deq70b8', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes 2-gang switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_dqolcpcp', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 12-way Relay Moduleh' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_1n2kyphz', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 4-gang switch' // https://www.aliexpress.com/item/1005003972289459.html fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_shkxsgis', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 4-gang switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_shkxsgis', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 4-gang switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_aagrxlbd', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 4-gang switch' // @Gabriel fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_xjknlqz8', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 6-gang switch' // @Vartan BR - TrTRON fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_4cl0dzt4', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 6 Gangs Wall Light Switch' // @Vartan BR - QA fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_rkbxtclc', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Keypad 3-gang switch' // @Vartan BR - TR fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_zpvusbtv', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 2-gang switch' // @Vartan BR - QA fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_rzdkn5rx', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Zemismart 1-gang switch' // @Vartan BR - QA fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_dvosyycn', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya ZXYH 8IN/8OUT board' // https://community.hubitat.com/t/device-labels-changing-on-reboot/141754/5?u=kkossev fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_lmgrbuwf', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'NOVADIGITAL 1 socket 2 switches' // https://www.novadigitalsmart.com.br/produtos/interruptor-tecla-fisica-tomada-4x2-ntzb-01-02-w-b fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_g4au0afs', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'NOVADIGITAL 6-gang switch' // https://community.hubitat.com/t/novadigital-tuya-6-gang-wall-switch-not-recognized-by-hubitat/152353?u=kkossev fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_mwvfvw8g', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 6-gang switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_cduqh1l0', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 6-gang switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_cduqh1l0', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 6-gang switch' // https://community.hubitat.com/t/driver-zigbee-smart-switch-6-gang-tuya/154041?u=kkossev fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_emxxanvi', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Tuya 6-gang switch' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_kyfqmmyl', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'Interruptor touch Zigbee 3 Teclas' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_2hf7x9n3', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'Interruptor touch Zigbee 3 Teclas' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE204_atpwqgml', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'Interruptor touch Zigbee 3 Teclas' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_bynnczcb', endpointId:'01', inClusters:'0000,0004,0005,EF00', outClusters:'0019,000A', application:'46', deviceJoinName: 'Interruptor touch Zigbee 3 Teclas' // Moes Star Feather Smart Switches fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_ydkqbmpt', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 1 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_z3u99qxt', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 1 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_uenof8jd', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 2 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_tzyy0rtq', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 2 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_hktk6hze', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 2 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_wv9ukqca', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 3 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_zo0cfekv', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 3 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_rd8cdssd', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 3 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_dq8bu0pt', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 4 Gang' fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_9dhenr94', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Moes Star Feather Smart Switch 4 Gang' // Nova Digital Topazio Smart Switches fingerprint profileId:'0104', model:'TS0601', manufacturer:'_TZE200_hmabvy81', endpointId:'01', inClusters:'0004,0005,EF00,0000', outClusters:'0019,000A', application:'42', deviceJoinName: 'Nova Digital Topazio Smart Switch 4 Gang' } attribute 'switchLightMode', 'enum', ['off', 'on', 'position'] attribute 'powerOnBehavior', 'enum', ['off', 'on', 'last state'] attribute 'lastCheckin', 'string' attribute 'backlightLevel', 'number' attribute 'childLock', 'enum', ['off', 'on'] attribute 'backlightMode', 'enum', ['off', 'on'] attribute 'lightIndicatorMode', 'enum', ['none', 'lit when on', 'lit when off'] attribute 'vibrationMode', 'enum', ['Gear 0', 'Gear 1', 'Gear 2', 'Gear 3', 'Gear 4', 'Gear 5'] attribute 'inductionMode', 'enum', ['off', 'on'] attribute 'onColor', 'enum', ['Red', 'Blue', 'Green', 'White', 'Yellow', 'Magenta', 'Cyan', 'WarmWhite', 'WarmYellow'] attribute 'offColor', 'enum', ['Red', 'Blue', 'Green', 'White', 'Yellow', 'Magenta', 'Cyan', 'WarmWhite', 'WarmYellow'] if (_DEBUG == true) { command 'testParse', [[name:'val', type: 'STRING', description: 'description', constraints: ['STRING']]] command 'queryAllTuyaDP' command 'test' command 'tuyaTest', [[name:'dpCommand', type: 'STRING', description: 'Tuya DP Command', constraints: ['STRING']], [name:'dpValue', type: 'STRING', description: 'Tuya DP value', constraints: ['STRING']], [name:'dpType', type: 'ENUM', constraints: ['DP_TYPE_VALUE', 'DP_TYPE_BOOL', 'DP_TYPE_ENUM'], description: 'DP data type']] } preferences { input(name: 'infoLogging', type: 'bool', title: ("Enable info logging"), description: '', defaultValue: true) input(name: 'debugLogging', type: 'bool', title: ("Enable debug logging"), description: '', defaultValue: true) if (device) { if (isTratoZemismartKeypad()) { input(name: 'backlightLevel', type: 'number', title: ("Led: Backlight Level"), description: ("Select led backlight level 0-100 (default: 50)"), defaultValue: 50, range: [0..100]) input(name: 'backlightMode', type: 'enum', title: ("Led: Backlight Mode"), description: ("Select backlight mode (default: on)"), options: ['off', 'on'], defaultValue: 'on') input(name: 'lightIndicatorMode', type: 'enum', title: ("Led: Indicator Mode"), description: ("Select led indicator mode (default: lit when on)"), options: ['none', 'lit when on', 'lit when off'], defaultValue: 'lit when on') input(name: 'onColor', type: 'enum', title: ("Led: On Color"), description: ("Select led color when switch is ON(default: Blue)"), options: ['Red', 'Blue', 'Green', 'White', 'Yellow', 'Magenta', 'Cyan', 'WarmWhite', 'WarmYellow'], defaultValue: 'Blue') input(name: 'offColor', type: 'enum', title: ("Led: Off Color"), description: ("Select led color when switch is OFF(default: White)"), options: ['Red', 'Blue', 'Green', 'White', 'Yellow', 'Magenta', 'Cyan', 'WarmWhite', 'WarmYellow'], defaultValue: 'White') input(name: 'childLlock', type: 'enum', title: ("Child Lock"), description: ("Select child lock mode (default: off)"), options: ['off', 'on'], defaultValue: 'off') } else if (isMoesStarFeatherSwitch()) { input(name: 'backlightMode', type: 'enum', title: ("Led: Backlight Mode"), description: ("Select backlight mode (default: on)"), options: ['off', 'on'], defaultValue: 'on') input(name: 'lightIndicatorMode', type: 'enum', title: ("Led: Indicator Mode"), description: ("Select led indicator mode (default: lit when on)"), options: ['none', 'lit when on', 'lit when off'], defaultValue: 'lit when on') input(name: 'vibrationMode', type: 'enum', title: ("Vibration Mode"), description: ("Select vibration intensity for tactile feedback (default: Gear 1)"), options: ['Gear 0', 'Gear 1', 'Gear 2', 'Gear 3', 'Gear 4', 'Gear 5'], defaultValue: 'Gear 1') input(name: 'inductionMode', type: 'enum', title: ("Induction Mode"), description: ("Enable/disable induction sensing (default: off)"), options: ['off', 'on'], defaultValue: 'off') } else { input(name: 'switchLightMode', type: 'enum', title: ("Switch Backlight Mode"), description: ("Select type of backlight indicator (default: Position)"), options: ['off', 'on', 'position'], defaultValue: 'position') } input(name: 'powerOnBehavior', type: 'enum', title: ("Switch Power On Behavior"), description: ("Select relay renew state after AC failed (default: OFF)"), options: ['off', 'on', 'last state'], defaultValue: 'off') } } } boolean isTratoZemismartKeypad() { return device.getDataValue('manufacturer') in ['_TZE204_rzdkn5rx', '_TZE204_zpvusbtv', '_TZE204_rkbxtclc'/*, '_TZ3000_excgg5kb'*/] // _TZ3000_excgg5kb for tests only - TOBEDEL! } boolean isMoesStarFeatherSwitch() { return device.getDataValue('manufacturer') in ['_TZE200_ydkqbmpt', '_TZE200_z3u99qxt', '_TZE200_uenof8jd', '_TZE200_tzyy0rtq', '_TZE200_hktk6hze', '_TZE200_wv9ukqca', '_TZE200_zo0cfekv', '_TZE200_rd8cdssd', '_TZE200_dq8bu0pt', '_TZE200_9dhenr94', '_TZE200_hmabvy81'] } void initializeVars() { if (isTratoZemismartKeypad()) { device.updateSetting('backlightMode', [type:'enum', value:'on']) device.updateSetting('lightIndicatorMode', [type:'enum', value:'lit when on']) } else if (isMoesStarFeatherSwitch()) { device.updateSetting('backlightMode', [type:'enum', value:'on']) device.updateSetting('lightIndicatorMode', [type:'enum', value:'lit when on']) device.updateSetting('vibrationMode', [type:'enum', value:'Gear 1']) device.updateSetting('inductionMode', [type:'enum', value:'off']) } else { device.updateSetting('switchLightMode', [type:'enum', value:'position']) } device.updateSetting('powerOnBehavior', [type:'enum', value:'off']) device.updateSetting('debugLogging', [type:'bool', value:'true']) device.updateSetting('infoLogging', [type:'bool', value:'true']) } def initialize() { if (infoLogging) { log.info 'Initializing...' } log.warn 'Debug logging will be automatically disabled after 24 hours!' initializeVars() setupChildDevices() if (debugLogging) { runIn(86400, logsOff) } refresh() } void logsOff() { log.warn 'Debug logging disabled...' device.updateSetting('debugLogging', [value:'false' ,type:'bool']) } def installed() { log.info 'Installing...' log.warn 'Debug logging will be automatically disabled after 24 hours!' setupChildDevices() initializeVars() if (debugLogging) { runIn(86400, logsOff) } refresh() } def updated() { List cmds = [] String dpValHex = '' int key = 0 log.warn "debug logging is: ${debugLogging == true}" log.warn "description logging is: ${infoLogging == true}" if (debugLogging) { runIn(86400, logsOff) } if (debugLogging) { log.debug 'Parent updated' } cmds = switchLightModeConfig() + powerOnBehaviorConfig() /*+ refresh()*/ if (isMoesStarFeatherSwitch()) { cmds += vibrationModeConfig() + inductionModeConfig() } if (isTratoZemismartKeypad()) { cmds += getTuyaCommand('66', DP_TYPE_VALUE, zigbee.convertToHexString(settings?.backlightLevel as int, 8)) key = BacklightSwitchOptions.find { it.value == settings?.backlightMode }?.key cmds += getTuyaCommand('10', DP_TYPE_BOOL, zigbee.convertToHexString(key as int, 2)) key = ChildLockOptions.find { it.value == settings?.childLlock }?.key cmds += getTuyaCommand('65', DP_TYPE_BOOL, zigbee.convertToHexString(key as int, 2)) key = LightIndicatorOptions.find { it.value == settings?.lightIndicatorMode }?.key cmds += getTuyaCommand('0F', DP_TYPE_ENUM, zigbee.convertToHexString(key as int, 2)) key = ColorOptions.find { it.value == settings?.onColor }?.key cmds += getTuyaCommand('67', DP_TYPE_ENUM, zigbee.convertToHexString(key as int, 2)) key = ColorOptions.find { it.value == settings?.offColor }?.key cmds += getTuyaCommand('68', DP_TYPE_ENUM, zigbee.convertToHexString(key as int, 2)) key = PowerOnBehaviorOptions.find { it.value == settings?.powerOnBehavior }?.key cmds += getTuyaCommand('0E', DP_TYPE_ENUM, zigbee.convertToHexString(key as int, 2)) } if (infoLogging) { log.info 'Updated...' } sendZigbeeCommands(cmds) } private static int getCLUSTER_TUYA() { 0xEF00 } private static int getSETDATA() { 0x00 } private static int getSETTIME() { 0x24 } private static String getDP_TYPE_RAW() { '01' } // [ bytes ] private static String getDP_TYPE_BOOL() { '01' } // [ 0/1 ] private static String getDP_TYPE_VALUE() { '02' } // [ 4 byte value ] private static String getDP_TYPE_STRING() { '03' } // [ N byte string ] private static String getDP_TYPE_ENUM() { '04' } // [ 0-255 ] private static String getDP_TYPE_BITMAP() { '05' } // [ 1,2,4 bytes ] as bits // Parse incoming device messages to generate events def parse(String description) { if (debugLogging) { log.debug "description: ${description}" } if (description?.startsWith('catchall:') || description?.startsWith('read attr -')) { Map descMap = zigbee.parseDescriptionAsMap(description) if (descMap?.clusterInt == CLUSTER_TUYA) { if (debugLogging) { log.debug "descMap: ${descMap}" } if ((descMap?.command in ['00', '01', '02']) && descMap?.data?.size() >= 7) { def switchFunc = (descMap?.data[2]) def switchAttr = (descMap?.data[3]) def switchState = (descMap?.data[6]) == '01' ? 'on' : 'off' if (switchFunc in ['01', '02', '03', '04', '05', '06'] && switchAttr == '01') { // Check if this is a Star Feather device - if so, skip traditional switch logic if (isMoesStarFeatherSwitch()) { parseMoesStarFeatherSwitch(descMap) // handle Star Feather specific parsing return [:] } // Traditional switch logic for Moes and Trato devices def cd = getChildDevice("${device.id}-${switchFunc}") if (cd == null) { return createEvent(name: 'switch', value: switchState) } if (descMap?.command == '00') { // switch toggled cd.parse([[name: 'switch', value:switchState, descriptionText: "Child switch ${switchFunc} turned $switchState"]]) } else if (descMap?.command in ['01', '02']) { // report switch status cd.parse([[name: 'switch', value:switchState, descriptionText: "Child switch ${switchFunc} is $switchState"]]) } if (switchState == 'on') { if (debugLogging) { log.debug 'Parent Switch ON' } return createEvent(name: 'switch', value: 'on') } else if (switchState == 'off') { def cdsOn = 0 // cound number of switches on getChildDevices().each { child -> if (getChildId(child) != switchFunc && child.currentValue('switch') == 'on') { cdsOn++ } } if (cdsOn == 0) { if (debugLogging) { log.debug 'Parent Switch OFF' } return createEvent(name: 'switch', value: 'off') } } } else if (isTratoZemismartKeypad()) { parseTratoZemismartKeypad(descMap) // andy event will be generated there return [:] } else { if (debugLogging) { log.debug "unprocessed Tuya command switchFunc: 0x${switchFunc} " } } } else if (descMap?.command == '24') { syncTuyaDateTime() } } } } void parseTratoZemismartKeypad(Map descMap) { if (descMap?.data?.size() < 7) { if (debugLogging) { log.warn "parseTratoZemismartKeypad: unprocessed descMap = ${descMap}" } return } int dataLen = descMap?.data.size() //log.warn "dataLen=${dataLen}" //def transid = zigbee.convertHexToInt(descMap?.data[1]) // "transid" is just a "counter", a response will have the same transid as the command if (dataLen <= 5) { if (debugLogging) { log.warn "unprocessed short Tuya command response: dp_id=${descMap?.data[3]} dp=${descMap?.data[2]} fncmd_len=${fncmd_len} data=${descMap?.data})" } return } for (int i = 0; i < (dataLen - 4); ) { int dp = zigbee.convertHexToInt(descMap?.data[2 + i]) // "dp" field describes the action/message of a command frame int dp_id = zigbee.convertHexToInt(descMap?.data[3 + i]) // "dp_identifier" is device dependant int fncmd_len = zigbee.convertHexToInt(descMap?.data[5 + i]) int fncmd = getTuyaAttributeValue(descMap?.data, i) // processTuyaDP(descMap, dp, dp_id, fncmd) i = i + fncmd_len + 4 } } void parseMoesStarFeatherSwitch(Map descMap) { if (descMap?.data?.size() < 7) { if (debugLogging) { log.warn "parseMoesStarFeatherSwitch: unprocessed descMap = ${descMap}" } return } int dataLen = descMap?.data.size() if (dataLen <= 5) { if (debugLogging) { log.warn "unprocessed short Tuya command response: dp_id=${descMap?.data[3]} dp=${descMap?.data[2]} data=${descMap?.data})" } return } for (int i = 0; i < (dataLen - 4); ) { int dp = zigbee.convertHexToInt(descMap?.data[2 + i]) // "dp" field describes the action/message of a command frame int dp_id = zigbee.convertHexToInt(descMap?.data[3 + i]) // "dp_identifier" is device dependant int fncmd_len = zigbee.convertHexToInt(descMap?.data[5 + i]) int fncmd = getTuyaAttributeValue(descMap?.data, i) // processStarFeatherDP(descMap, dp, dp_id, fncmd) i = i + fncmd_len + 4 } } @Field static final Map SwitchStateOptions = [0: 'off', 1: 'on'] @Field static final Map ChildLockOptions = [0: 'off', 1: 'on'] // 0x65 @Field static final Map PowerOnBehaviorOptions = [0: 'off', 1: 'on', 2: 'last state'] // 0x0E 'Turn power Off', 'Turn power On', 'Restore previous state'], @Field static final Map BacklightSwitchOptions = [0: 'off', 1: 'on'] // 0x10 @Field static final Map LightIndicatorOptions = [0: 'none', 1: 'lit when on', 2: 'lit when off'] // 0x0F @Field static final Map ColorOptions = [0: 'Red', 1: 'Blue', 2: 'Green', 3: 'White', 4: 'Yellow', 5: 'Magenta', 6: 'Cyan', 7: 'WarmWhite', 8: 'WarmYellow'] void processTuyaDP(final Map descMap, final int dp, final int dp_id, final int fncmd, final int dp_len=0) { if (debugLogging) { log.trace "processTuyaDP: dp=${dp} dp_id=${dp_id} fncmd=${fncmd} dp_len=${dp_len}" } Map event = [:] String description = '' String strValue = '' switch (dp) { case 0x01 : // (1) switch // handled in the main parse method case 0x02 : // (2) switch Boolean "{true,false}" case 0x03 : // (3) switch case 0x04 : // (4) switch case 0x05 : // (5) switch case 0x06 : // (6) switch if (debugLogging) { log.debug "Switch ${dp} is ${fncmd}" } break case 0x07 : // timer 1 countdown_1 Integer { "unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1 } case 0x08 : // timer 2 case 0x09 : // timer 3 int gang = dp - 0x07 + 1 description = "Gang #${gang} auto timer is ${fncmd}" break case 0x0D : // (13) - Master Switch switch_all Boolean "{true,false}" strValue = SwitchStateOptions[fncmd] description = "Master switch is ${strValue}" // event = createEvent(name: 'switch', value: strValue, descriptionText: description) // already handled ? break case 0x0E : // (14) Power ON Behavior (Restart Status) relay_status Enum { "range": ["off", "on", "memory" ]} [0: 'off', 1: 'on', 2: 'last state'] strValue = PowerOnBehaviorOptions[fncmd] description = "Power ON Behavior is ${strValue}" event = createEvent(name: 'powerOnBehavior', value: strValue, descriptionText: description) break case 0x0F : // (15) indicator status light_mode Enum { "range": ["none", "relay", "pos"] } strValue = LightIndicatorOptions[fncmd] description = "Light indicator status is ${strValue}" event = createEvent(name: 'lightIndicatorMode', value: strValue, descriptionText: description) break case 0x10 : // (16) "Backlight Switch", 0x10 bool backlight_switch Boolean "{true,false}" strValue = BacklightSwitchOptions[fncmd] description = "Backlight mode is ${strValue}" event = createEvent(name: 'backlightMode', value: strValue, descriptionText: description) break case 0x13 : // (19) delay-off schedule strValue = fncmd description = "delay-off schedule is ${strValue}" break case 0x1F : // (31) restart status 3 case 0x1E : // (30) restart status 2 case 0x1D : // (29) restart status 1 int gang = dp - 0x1D + 1 description = "Restart status for gang #${gang} is ${fncmd}" break case 0x20 : // (32) unknown0x20 strValue = fncmd description = "unknown0x20 is ${strValue}" event = createEvent(name: 'unknown0x20', value: strValue, descriptionText: description) break case 0x65 : // (101) child lock strValue = ChildLockOptions[fncmd] description = "Child lock is ${strValue}" event = createEvent(name: 'childLock', value: strValue, descriptionText: description) break case 0x66 : // (102) backlight "Backlight", 0x66, value, OK! backlight Integer { "unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1 } strValue = fncmd description = "Backlight level is ${strValue}" event = createEvent(name: 'backlightLevel', value: strValue, descriptionText: description) break case 0x67 : // (103) onColor night_light_color Enum strValue = ColorOptions[fncmd] description = "onColor is ${strValue}" event = createEvent(name: 'onColor', value: strValue, descriptionText: description) break case 0x68 : // (104) offColor night_light_mode ? Enum strValue = ColorOptions[fncmd] description = "offColor is ${strValue}" event = createEvent(name: 'offColor', value: strValue, descriptionText: description) break case 0xD1 : // (209) cycle schedule strValue = fncmd description = "cycle schedule is ${strValue}" break case 0xD2 : // (210) random schedule strValue = fncmd description = "random schedule is ${strValue}" break default: if (debugLogging) { log.warn "NOT PROCESSED Tuya cmd: dp=${dp} value=${fncmd} descMap.data = ${descMap?.data}" } break } if (event != [:]) { sendEvent(event) if (infoLogging) { log.info description } } else if (description != '') { if (infoLogging) { log.info description } } } void processStarFeatherDP(final Map descMap, final int dp, final int dp_id, final int fncmd, final int dp_len=0) { if (debugLogging) { log.trace "processStarFeatherDP: dp=${dp} dp_id=${dp_id} fncmd=${fncmd} dp_len=${dp_len}" } Map event = [:] String description = '' String strValue = '' switch (dp) { case 0x01 : // (1) scene_1 action if (debugLogging) { log.debug "Scene 1 action triggered" } sendEvent(name: 'pushed', value: 1, descriptionText: 'Scene 1 button pushed', isStateChange: true) break case 0x02 : // (2) scene_2 action if (debugLogging) { log.debug "Scene 2 action triggered" } sendEvent(name: 'pushed', value: 2, descriptionText: 'Scene 2 button pushed', isStateChange: true) break case 0x03 : // (3) scene_3 action if (debugLogging) { log.debug "Scene 3 action triggered" } sendEvent(name: 'pushed', value: 3, descriptionText: 'Scene 3 button pushed', isStateChange: true) break case 0x04 : // (4) scene_4 action if (debugLogging) { log.debug "Scene 4 action triggered" } sendEvent(name: 'pushed', value: 4, descriptionText: 'Scene 4 button pushed', isStateChange: true) break case 0x12 : // (18) mode_l1 - Switch1 mode strValue = fncmd == 0 ? 'switch' : 'scene' description = "Switch 1 mode is ${strValue}" if (debugLogging) { log.debug description } break case 0x13 : // (19) mode_l2 - Switch2 mode strValue = fncmd == 0 ? 'switch' : 'scene' description = "Switch 2 mode is ${strValue}" if (debugLogging) { log.debug description } break case 0x14 : // (20) mode_l3 - Switch3 mode strValue = fncmd == 0 ? 'switch' : 'scene' description = "Switch 3 mode is ${strValue}" if (debugLogging) { log.debug description } break case 0x15 : // (21) mode_l4 - Switch4 mode strValue = fncmd == 0 ? 'switch' : 'scene' description = "Switch 4 mode is ${strValue}" if (debugLogging) { log.debug description } break case 0x18 : // (24) state_l1 - Switch 1 state strValue = fncmd == 1 ? 'on' : 'off' def cd1 = getChildDevice("${device.id}-01") if (cd1 != null) { cd1.parse([[name: 'switch', value: strValue, descriptionText: "Child switch 1 is $strValue"]]) } else { sendEvent(name: 'switch', value: strValue, descriptionText: "Switch 1 is $strValue") } updateParentSwitchState() break case 0x19 : // (25) state_l2 - Switch 2 state strValue = fncmd == 1 ? 'on' : 'off' def cd2 = getChildDevice("${device.id}-02") if (cd2 != null) { cd2.parse([[name: 'switch', value: strValue, descriptionText: "Child switch 2 is $strValue"]]) } updateParentSwitchState() break case 0x1A : // (26) state_l3 - Switch 3 state strValue = fncmd == 1 ? 'on' : 'off' def cd3 = getChildDevice("${device.id}-03") if (cd3 != null) { cd3.parse([[name: 'switch', value: strValue, descriptionText: "Child switch 3 is $strValue"]]) } updateParentSwitchState() break case 0x1B : // (27) state_l4 - Switch 4 state strValue = fncmd == 1 ? 'on' : 'off' def cd4 = getChildDevice("${device.id}-04") if (cd4 != null) { cd4.parse([[name: 'switch', value: strValue, descriptionText: "Child switch 4 is $strValue"]]) } updateParentSwitchState() break case 0x1E : // (30) countdown_l1 - Timer 1 case 0x1F : // (31) countdown_l2 - Timer 2 case 0x20 : // (32) countdown_l3 - Timer 3 case 0x21 : // (33) countdown_l4 - Timer 4 int gang = dp - 0x1E + 1 description = "Gang #${gang} countdown timer is ${fncmd} seconds" if (debugLogging) { log.debug description } break case 0x24 : // (36) backlight_mode strValue = BacklightSwitchOptions[fncmd] description = "Backlight mode is ${strValue}" event = createEvent(name: 'backlightMode', value: strValue, descriptionText: description) break case 0x25 : // (37) indicator_status strValue = fncmd == 0 ? 'none' : fncmd == 1 ? 'lit when on' : 'lit when off' description = "Light indicator status is ${strValue}" event = createEvent(name: 'lightIndicatorMode', value: strValue, descriptionText: description) break case 0x26 : // (38) power_on_behavior strValue = PowerOnBehaviorOptions[fncmd] description = "Power ON Behavior is ${strValue}" event = createEvent(name: 'powerOnBehavior', value: strValue, descriptionText: description) break case 0x67 : // (103) induction_mode strValue = fncmd == 1 ? 'on' : 'off' description = "Induction mode is ${strValue}" event = createEvent(name: 'inductionMode', value: strValue, descriptionText: description) break case 0x68 : // (104) vibration_mode strValue = "Gear ${fncmd}" description = "Vibration mode is ${strValue}" event = createEvent(name: 'vibrationMode', value: strValue, descriptionText: description) break case 0x69 : // (105) momentary_1 case 0x6A : // (106) momentary_2 case 0x6B : // (107) momentary_3 case 0x6C : // (108) momentary_4 int gang = dp - 0x69 + 1 description = "Gang #${gang} momentary timer is ${fncmd} seconds" if (debugLogging) { log.debug description } break default: if (debugLogging) { log.warn "NOT PROCESSED Star Feather Tuya cmd: dp=${dp} value=${fncmd} descMap.data = ${descMap?.data}" } break } if (event != [:]) { sendEvent(event) if (infoLogging) { log.info description } } else if (description != '') { if (infoLogging) { log.info description } } } void updateParentSwitchState() { def cdsOn = 0 // count number of switches on getChildDevices().each { child -> if (child.currentValue('switch') == 'on') { cdsOn++ } } String parentState = cdsOn > 0 ? 'on' : 'off' if (device.currentValue('switch') != parentState) { if (debugLogging) { log.debug "Parent Switch ${parentState.toUpperCase()}" } sendEvent(name: 'switch', value: parentState) } } private int getTuyaAttributeValue(final List _data, final int index) { int retValue = 0 if (_data.size() >= 6) { int dataLength = zigbee.convertHexToInt(_data[5 + index]) if (dataLength == 0) { return 0 } int power = 1 for (i in dataLength..1) { retValue = retValue + power * zigbee.convertHexToInt(_data[index + i + 5]) power = power * 256 } } return retValue } String getPACKET_ID() { return zigbee.convertToHexString(new Random().nextInt(65536), 4) } List getTuyaCommand(String dp, String dp_type, String fncmd, int tuyaCmdDefault = SETDATA) { return sendTuyaCommand(dp, dp_type, fncmd, tuyaCmdDefault) } List sendTuyaCommand(String dp, String dp_type, String fncmd, int tuyaCmdDefault = SETDATA) { List cmds = [] int tuyaCmd = tuyaCmdDefault cmds = zigbee.command(CLUSTER_TUYA, tuyaCmd, [:], delay = 201, PACKET_ID + dp + dp_type + zigbee.convertToHexString((int)(fncmd.length() / 2), 4) + fncmd ) if (debugLogging) { "${device.displayName} getTuyaCommand (dp=$dp fncmd=$fncmd dp_type=$dp_type) = ${cmds}" } return cmds } public void tuyaTest(String dpCommand, String dpValue, String dpTypeString ) { String dpType = dpTypeString == 'DP_TYPE_VALUE' ? DP_TYPE_VALUE : dpTypeString == 'DP_TYPE_BOOL' ? DP_TYPE_BOOL : dpTypeString == 'DP_TYPE_ENUM' ? DP_TYPE_ENUM : null String dpValHex = dpTypeString == 'DP_TYPE_VALUE' ? zigbee.convertToHexString(dpValue as int, 8) : dpValue if (settings?.logEnable) { log.warn "${device.displayName} sending TEST command=${dpCommand} value=${dpValue} ($dpValHex) type=${dpType}" } sendZigbeeCommands( sendTuyaCommand(dpCommand, dpType, dpValHex) ) } void syncTuyaDateTime() { long offset = 0 int offsetHours = 0 Calendar cal = Calendar.getInstance() //it return same time as new Date() int hour = cal.get(Calendar.HOUR_OF_DAY) try { offset = location.getTimeZone().getOffset(new Date().getTime()) offsetHours = (offset / 3600000) as int if (debugLogging) { log.debug "${device.displayName} timezone offset of current location is ${offset} (${offsetHours} hours), current hour is ${hour} h" } } catch (e) { log.error "${device.displayName} cannot resolve current location. please set location in Hubitat location setting. Setting timezone offset to zero" } // List cmds = zigbee.command(0xEF00, 0x24, '0008' + zigbee.convertToHexString((int)(now() / 1000), 8) + zigbee.convertToHexString((int)((now() + offset) / 1000), 8)) sendZigbeeCommands(cmds) if (debugLogging) { log.debug "${device.displayName} Tuya device time synchronized" } } void sendZigbeeCommands(List cmd) { if (debugLogging) { log.debug "${device.displayName} sendZigbeeCommands: ${cmd}" } hubitat.device.HubMultiAction allActions = new hubitat.device.HubMultiAction() cmd.each { allActions.add(new hubitat.device.HubAction(it, hubitat.device.Protocol.ZIGBEE)) } sendHubCommand(allActions) } def lastCheckin() { // send event for heartbeat def now = new Date() sendEvent(name: 'lastCheckin', value: now) } def off() { if (infoLogging) { log.info 'Turn all switches OFF' } return [ "he cmd 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00 {0001010100010002010001000301000100}", 'delay 200', ] } def on() { if (infoLogging) { log.info 'Turn all switches ON' } return [ "he cmd 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00 {0001010100010102010001010301000101}", 'delay 200', ] } def refresh() { if (infoLogging) { log.info 'Refreshing...' } if (isTratoZemismartKeypad()) { return [queryAllTuyaDP()] } return [ lastCheckin() ] } private String getChildId(childDevice) { return childDevice.deviceNetworkId.substring(childDevice.deviceNetworkId.length() - 2) } void componentOn(childDevice) { if (debugLogging) { log.debug "component state is ON - ${childDevice} {${childDevice.deviceNetworkId}}" } if (infoLogging) { log.info "${childDevice} is ON" } if (isMoesStarFeatherSwitch()) { // Star Feather switches use different data points: 0x18(24), 0x19(25), 0x1A(26), 0x1B(27) String childId = getChildId(childDevice) int dpValue = 0x18 + (childId as int) - 1 // Convert to hex: 01->0x18, 02->0x19, etc. String dpHex = zigbee.convertToHexString(dpValue, 2) List cmds = getTuyaCommand(dpHex, DP_TYPE_BOOL, '01') sendZigbeeCommands(cmds) } else { // Original method for other switches String fullDataOn = '0001' + getChildId(childDevice) + '01000101' sendHubCommand(new HubAction("he cmd 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00 {${fullDataOn}}", Protocol.ZIGBEE)) if (debugLogging) { log.debug { "{executed} 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00 {${fullDataOn}}" } } } } void componentOff(childDevice) { if (debugLogging) { log.debug "component state is OFF - ${childDevice} {${childDevice.deviceNetworkId}}" } if (infoLogging) { log.info "${childDevice} is OFF" } if (isMoesStarFeatherSwitch()) { // Star Feather switches use different data points: 0x18(24), 0x19(25), 0x1A(26), 0x1B(27) String childId = getChildId(childDevice) int dpValue = 0x18 + (childId as int) - 1 // Convert to hex: 01->0x18, 02->0x19, etc. String dpHex = zigbee.convertToHexString(dpValue, 2) List cmds = getTuyaCommand(dpHex, DP_TYPE_BOOL, '00') sendZigbeeCommands(cmds) } else { // Original method for other switches String fullDataOff = '0001' + getChildId(childDevice) + '01000100' sendHubCommand(new HubAction("he cmd 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00 {${fullDataOff}}", Protocol.ZIGBEE)) if (debugLogging) { log.debug "{executed} 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00 {${fullDataOff}}" } } } void componentRefresh(childDevice) { if (debugLogging) { log.debug "component refresh ${childDevice.deviceNetworkId} ${childDevice}" } sendHubCommand(new HubAction("he rattr 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00", Protocol.ZIGBEE)) if (debugLogging) { log.debug "{executed} 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x00" } } void setupChildDevices() { if (debugLogging) { log.debug 'Parent setupChildDevices' } deleteObsoleteChildren() int buttons = 0 switch (device.data.manufacturer) { case '_TZE200_amp6tsvy' : case '_TZE200_oisqyl4o' : case '_TZE200_wfxuhoea' : case '_TZE200_gbagoilo' : case '_TZE204_rzdkn5rx' : case '_TZE200_ydkqbmpt' : // Moes Star Feather 1 Gang case '_TZE200_z3u99qxt' : // Moes Star Feather 1 Gang buttons = 1 break case '_TZE200_g1ib5ldv' : case '_TZE200_wunufsil' : case '_TZE200_nh9m9emk' : case '_TZE200_7deq70b8' : case '_TZE204_zpvusbtv' : case '_TZE200_uenof8jd' : // Moes Star Feather 2 Gang case '_TZE200_tzyy0rtq' : // Moes Star Feather 2 Gang case '_TZE200_hktk6hze' : // Moes Star Feather 2 Gang / Nova Digital TPZ-2 buttons = 2 break case '_TZE200_tz32mtza' : case '_TZE200_kyfqmmyl' : case '_TZE200_go3tvswy' : case '_TZE200_atpwqgml' : case '_TZE204_rkbxtclc' : case '_TZE200_2hf7x9n3' : case '_TZE204_atpwqgml' : case '_TZE200_bynnczcb' : case '_TZE200_wv9ukqca' : // Moes Star Feather 3 Gang case '_TZE200_zo0cfekv' : // Moes Star Feather 3 Gang case '_TZE200_rd8cdssd' : // Moes Star Feather 3 Gang / Nova Digital TPZ-3 buttons = 3 break case '_TZE200_k6jhsr0q' : case '_TZE200_aqnazj70' : case '_TZE200_1ozguk6x' : case '_TZE200_mexisfik' : case '_TZE200_1n2kyphz' : case '_TZE200_shkxsgis' : case '_TZE204_shkxsgis' : case '_TZE200_mua6ucdj' : case '_TZE204_aagrxlbd' : case '_TZE200_dq8bu0pt' : // Moes Star Feather 4 Gang case '_TZE200_9dhenr94' : // Moes Star Feather 4 Gang case '_TZE200_hmabvy81' : // Moes Star Feather 4 Gang / Nova Digital TPZ-4 buttons = 4 break case '_TZE200_9mahtqtg' : case '_TZE200_r731zlxk' : case '_TZE204_xjknlqz8' : case '_TZE204_4cl0dzt4' : case '_TYZB01_ltundz9m' : case '_TZ3000_jyupj3fw' : case '_TZE200_wnp4d4va' : case '_TZE204_lmgrbuwf' : // NOVADIGITAL case '_TZE204_g4au0afs' : // NOVADIGITAL case '_TZE200_mwvfvw8g' : case '_TZE200_cduqh1l0' : case '_TZE204_cduqh1l0' : case '_TZE200_emxxanvi' : buttons = 6 break case '_TZE204_dvosyycn' : buttons = 6 // limited to 6 relays only, actually has 8 relays and 8 inputs break case '_TZE204_dqolcpcp' : buttons = 12 break case '_TZE200_vhy3iakz' : case '_TZ3000_uim07oem' : default : // assume 4 buttons also for any unknown manufacturers codes! buttons = 4 break } if (infoLogging) { log.info "model: ${device.data.manufacturer} buttons: $buttons" } createChildDevices((int)buttons) } void createChildDevices(int buttons) { if (debugLogging) { log.debug 'Parent createChildDevices' } if (buttons <= 1) { if (debugLogging) { log.debug "This device have only: $buttons button, Child devices not needed." } return } /* groovylint-disable-next-line UnnecessaryElseStatement */ else { for (i in 1..buttons) { def childId = "${device.id}-0${i}" def existingChild = getChildDevices()?.find { it.deviceNetworkId == childId } if (existingChild) { if (infoLogging) { log.info "Child device ${childId} already exists (${existingChild})" } } else { if (infoLogging) { log.info "Creating device ${childId}" } addChildDevice('hubitat', 'Generic Component Switch', childId, [isComponent: true, name: "Switch EP0${i}", label: "${device.displayName} EP0${i}"]) } } } } void deleteObsoleteChildren() { if (debugLogging) { log.debug 'Parent deleteChildren' } getChildDevices().each { child -> if (!child.deviceNetworkId.startsWith(device.id) || child.deviceNetworkId == "${device.id}-00") { if (infoLogging) { log.info "Deleting ${child.deviceNetworkId}" } deleteChildDevice(child.deviceNetworkId) } } } List switchLightModeConfig() { List cmds = [] if (isTratoZemismartKeypad()) { int bl = settings?.switchLightMode == 'on' ? 1 : 0 String dpValHex = zigbee.convertToHexString(bl as int, 2) cmds = getTuyaCommand('10', DP_TYPE_BOOL, dpValHex) } else if (isMoesStarFeatherSwitch()) { // Star Feather uses separate DPs for backlight and indicator - treat independently // Handle backlight mode (DP 0x24) int backlightValue = settings?.backlightMode == 'on' ? 1 : 0 if (infoLogging) { log.info "Star Feather - Setting backlight mode: ${settings?.backlightMode}" } cmds += getTuyaCommand('24', DP_TYPE_BOOL, zigbee.convertToHexString(backlightValue, 2)) // Handle indicator mode (DP 0x25) int indicatorValue = settings?.lightIndicatorMode == 'none' ? 0 : settings?.lightIndicatorMode == 'lit when on' ? 1 : 2 if (infoLogging) { log.info "Star Feather - Setting indicator mode: ${settings?.lightIndicatorMode}" } cmds += getTuyaCommand('25', DP_TYPE_ENUM, zigbee.convertToHexString(indicatorValue, 2)) } else { switch (settings?.switchLightMode) { case 'off': if (infoLogging) { log.info 'Backlight - OFF' } cmds = zigbee.command(0xEF00, 0x0, '00010f04000100') break case 'on': if (infoLogging) { log.info 'Backlight - ON' } cmds = zigbee.command(0xEF00, 0x0, '00010f04000101') break case 'position': if (infoLogging) { log.info 'Backlight - position' } cmds = zigbee.command(0xEF00, 0x0, '00010f04000102') break } } return cmds } List powerOnBehaviorConfig() { List cmds = [] if (isTratoZemismartKeypad()) { int rl = settings?.powerOnBehavior == 'off' ? 0 : settings?.powerOnBehavior == 'on' ? 1 : 2 if (debugLogging) { log.debug "Relay mode: ${rl} (${settings?.powerOnBehavior})" } String dpValHex = zigbee.convertToHexString(rl, 2) cmds = getTuyaCommand('0E', DP_TYPE_ENUM, dpValHex) } else if (isMoesStarFeatherSwitch()) { int rl = settings?.powerOnBehavior == 'off' ? 0 : settings?.powerOnBehavior == 'on' ? 1 : 2 if (debugLogging) { log.debug "Star Feather Power-on behavior: ${rl} (${settings?.powerOnBehavior})" } String dpValHex = zigbee.convertToHexString(rl, 2) cmds = getTuyaCommand('26', DP_TYPE_ENUM, dpValHex) // DP 0x26 for Star Feather switches } else { switch (powerOnBehavior) { case 'off': if (infoLogging) { log.info 'Relay state - OFF' } cmds = zigbee.command(0xEF00, 0x0, '00010e04000100') break case 'on': if (infoLogging) { log.info 'Relay state - ON' } cmds = zigbee.command(0xEF00, 0x0, '00010e04000101') break case 'last state': if (infoLogging) { log.info 'Relay state - last state' } cmds = zigbee.command(0xEF00, 0x0, '00010e04000102') break } } return cmds } List vibrationModeConfig() { List cmds = [] if (isMoesStarFeatherSwitch()) { // Extract gear number from "Gear X" format int gearNumber = 1 // default if (settings?.vibrationMode) { String gearStr = settings?.vibrationMode?.replaceAll(/[^0-9]/, '') gearNumber = gearStr ? (gearStr as int) : 1 } if (infoLogging) { log.info "Star Feather - Setting vibration mode: ${settings?.vibrationMode}" } String dpValHex = zigbee.convertToHexString(gearNumber, 2) cmds = getTuyaCommand('68', DP_TYPE_ENUM, dpValHex) } return cmds } List inductionModeConfig() { List cmds = [] if (isMoesStarFeatherSwitch()) { int inductionValue = settings?.inductionMode == 'on' ? 1 : 0 if (infoLogging) { log.info "Star Feather - Setting induction mode: ${settings?.inductionMode}" } String dpValHex = zigbee.convertToHexString(inductionValue, 2) cmds = getTuyaCommand('67', DP_TYPE_BOOL, dpValHex) } return cmds } void queryAllTuyaDP() { log.trace 'queryAllTuyaDP()' List cmds = zigbee.command(0xEF00, 0x03) sendZigbeeCommands(cmds) } void testParse(final String description) { log.warn "testParse: ${description}" parse(description) log.trace '---end of testParse---' } void test() { log.trace 'test()' List cmds = childModeConfig() sendZigbeeCommands(cmds) }