/* 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)
}