Hubitat Tuya Switch 4channels

Hello :slight_smile:
I recently bought a Zigbee switch with 4 channels. Unfortunately, I'm not able to correctly configure it on Hubitat.
Here is a link to this device : Module de commutateur d'éclairage intelligent Zigbee 4CH, DC 5/12/32V, RF433, relais de réception 10A, fonctionne avec Alexa Google Assistant,Tuya Smart Life | AliExpress

I already read this post Tuya Zigbee Wall Switch (aka. Lonsonho or Bandi) - #29 by jacubmikita and try the @martinkura solution : Tuya Zigbee Wall Switch (aka. Lonsonho or Bandi) - #50 by martinkura adapting his code to my case...but after some attempts and debug, i'm not able to do it with success... :confused:

When I set the parent device to "GE Zigbee Swith" type i'm able to torn on and off the first channel of the device...However, if I set the Child Device with same type, it doesn't work :frowning:

Do you have some suggestions ?

Here is the signature of my device :

  • endpointId: 01
  • application: 43
  • softwareBuild:
  • inClusters: 0003,0004,0005,0006,E000,E001,0000
  • outClusters: 0019,000A
  • model: TS0004
  • isMultiEP: true
  • manufacturer: _TZ3000_excgg5kb

And the code that I use on my driver :

import hubitat.device.HubAction
import hubitat.device.Protocol

metadata {
    definition (name: "Tuya ZigBee Switch module 1/2/3/4-Gang", namespace: "Moes 1.31", author: "Martin Kura") {
        capability "Initialize"
        capability "Actuator"
//       capability "Configuration"
        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_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_tz32mtza", endpointId:"01", inClusters:"0000,0004,0005,EF00", outClusters:"0019,000A", application:"42", deviceJoinName: "Moes 3-Gang Switch / ZTS-EU3"
        fingerprint profileId:"0104", model:"TS0004", manufacturer:"_TZ3000_excgg5kb", endpointId:"01", inClusters:"0003,0004,0005,0006,E000,E001,0000", outClusters:"0019,000A", application:"43", deviceJoinName: "Tuya 4CH Zigbee"
    }
    attribute "switchLightMode","enum",["OFF", "ON", "Position"]
    attribute "relayMode","enum",["OFF", "ON", "Last state"]
    attribute "lastCheckin", "string"

    preferences {
        input(name: "switchLightMode", type: "enum", title: ("Switch Backlight Mode"), description: ("- select type of backlight indicator (default: Position)"), options: ["OFF", "ON", "Position"], defaultValue: "Position", submitOnChange: true)
        input(name: "relayMode", type: "enum", title: ("Switch Relay Mode"), description: ("- select relay renew state after AC failed (default: OFF)"), options: ["OFF", "ON", "Last state"], defaultValue: "OFF", submitOnChange: true)
        input(name: "debugLogging", type: "bool", title: ("Enable debug logging"), description: "", defaultValue: true, submitOnChange: true, displayDuringSetup: true)
        input(name: "infoLogging", type: "bool", title: ("Enable info logging"), description: "", defaultValue: true, submitOnChange: true, displayDuringSetup: true)
    }
}

def initialize() {
    if (infoLogging) log.info "Initializing..."
    log.warn "Debug logging will be automatically disabled after 30 minutes!"
    setupChildDevices()
    device.updateSetting("switchLightMode",[type:"enum",value:"Position"])
    device.updateSetting("relayMode",[type:"enum",value:"OFF"])
    device.updateSetting("debugLogging",[type:"bool",value:"true"])
    device.updateSetting("infoLogging",[type:"bool",value:"true"])
    if (debugLogging) runIn(1800,logsOff)
    refresh()
}

void logsOff(){
    log.warn "Debug logging disabled..."
    device.updateSetting("debugLogging",[value:"false",type:"bool"])
}

/*def configure(){
     if (debugLogging) log.debug "Configuring Reporting and Bindings..."

    def cmds = [
            //bindings
            "zdo bind 0x${device.deviceNetworkId} 0x01 0x00 0xEF00 {${device.zigbeeId}} {}", "delay 200",
            "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0xEF00 {${device.zigbeeId}} {}", "delay 200",
            //"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0004 {${device.zigbeeId}} {}", "delay 200",
            //"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 0x01 0x0005 {${device.zigbeeId}} {}", "delay 200",
            //reporting
            "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0 0x00 0 0xFFFF {}","delay 200",
            "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0 0x01 0 0xFFFF {}", "delay 200",
            //"he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0 0x0000 0 0xFFFF {}", "delay 200",
           //"he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0 0x0001 0 0xFFFF {}", "delay 200",
           // "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0 0x0007 0 0xFFFF {}", "delay 200",
           // "he cr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0300 0 0x0008 1 0xFFFE {}", "delay 200",
    ] + refresh()
    return cmds

}     */

def installed() {
    log.info "Installing..."
    log.warn "Debug logging will be automatically disabled after 30 minutes!"
    setupChildDevices()
    device.updateSetting("switchLightMode",[type:"enum",value:"Position"])
    device.updateSetting("relayMode",[type:"enum",value:"OFF"])
    device.updateSetting("debugLogging",[type:"bool",value:"true"])
    device.updateSetting("infoLogging",[type:"bool",value:"true"])
    if (debugLogging) runIn(1800,logsOff)
    refresh()
}

def updated() {
    log.warn "debug logging is: ${debugLogging == true}"
    log.warn "description logging is: ${infoLogging == true}"
    if (infoLogging) log.info "Updated..."
    if (debugLogging) log.debug "Parent updated"
    switchLightModeConfig() + relayModeConfig() + refresh()
}

private getCLUSTER_TUYA() { 0xEF00 }
// private getSETDATA() { 0x00 }

// Parse incoming device messages to generate events
def parse(String description) {
    if (description?.startsWith('catchall:') || description?.startsWith('read attr -')) {
        Map descMap = zigbee.parseDescriptionAsMap(description)
        if (descMap?.clusterInt==CLUSTER_TUYA) {
            if (debugLogging) log.debug descMap
            if ( descMap?.command == "01" || descMap?.command == "02" ) {
                def switchFunc = (descMap?.data[2])
                def switchAttr = (descMap?.data[3])
                def switchState = (descMap?.data[6]) == "01" ? "on" : "off"
                if (switchFunc <= "03" && switchAttr =="01") {
                    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 == "01") {
                        // 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")
                        }
                    }
                }
            }
        }
    }
}

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",
            //"he rattr 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x01 {}","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",
            // "he rattr 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x01 {}","delay 200"
    ]
}

def refresh() {
    if (infoLogging) log.info "Refreshing..."
    return  [
            //"he rattr  0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x04 {}","delay 200",  //light state
            //"he rattr 0x${device.deviceNetworkId} 0x${device.endpointId} 0xEF00 0x01 {}","delay 200",  //light state
            //"he rattr 0x${device.deviceNetworkId} 0x${device.endpointId} 0x0005 0x00 {}","delay 200",  //
            lastCheckin()
    ]
}

private String getChildId(childDevice) {
    return childDevice.deviceNetworkId.substring(childDevice.deviceNetworkId.length() - 2)
}

def componentOn(childDevice) {
    if (debugLogging) log.debug "component state is ON - ${childDevice} {${childDevice.deviceNetworkId}}"
    if (infoLogging) log.info "${childDevice} is ON"
    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}}"
}

def componentOff(childDevice) {
    if (debugLogging) log.debug "component state is OFF - ${childDevice} {${childDevice.deviceNetworkId}}"
    if (infoLogging) log.info "${childDevice} is OFF"
    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}}"
}

def 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"
}

def setupChildDevices() {
    if (debugLogging) log.debug "Parent setupChildDevices"
    deleteObsoleteChildren()
    def buttons = 0
    switch (device.data.manufacturer) {
        case '_TZE200_amp6tsvy':
            buttons = 1
            break
        case '_TZE200_g1ib5ldv':
            buttons = 2
            break
        case '_TZE200_tz32mtza':
            buttons = 3
            break
        case '_TZ3000_excgg5kb' :
            buttons = 4
            break
    }
    if (infoLogging) log.info  "model: ${device.data.manufacturer}   buttons: $buttons"
    createChildDevices((int)buttons)
}

def 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         }

    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", "GE Zigbee Switch", childId, [isComponent: true, name: "Switch EP0${i}", label: "${device.displayName} EP0${i}"])
            }
        }
}

def 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)
        }
    }
}

def switchLightModeConfig(){
    def cmds = []
    switch(switchLightMode) {
        case "OFF":
            if (infoLogging) log.info "Backlight - OFF"
            zigbee.command(0xEF00, 0x0, "00010f04000100")
            break
        case "ON":
            if (infoLogging) log.info "Backlight - ON"
            zigbee.command(0xEF00, 0x0, "00010f04000101")
            break
        case "Position":
            if (infoLogging) log.info "Backlight - position"
            zigbee.command(0xEF00, 0x0, "00010f04000102")
            break
    }
}

def relayModeConfig(){
    def cmds = []
    switch(relayMode) {
        case "OFF":
            if (infoLogging) log.info "Relay state - OFF"
            zigbee.command(0xEF00, 0x0, "00010e04000100")
            break
        case "ON":
            if (infoLogging) log.info "Relay state - ON"
            zigbee.command(0xEF00, 0x0, "00010e04000101")
            break
        case "Last state":
            if (infoLogging) log.info "Relay state - last state"
            zigbee.command(0xEF00, 0x0, "00010e04000102")
            break
    }
}

Thanks for your help :slight_smile:

My driver works with cluster 0xEF00, your device use standard cluster 0x0006. Try some default HE drivers.

Hello @martinkura, thanks for your answer :slight_smile:

I try a lot of HE drivers but the only one that change the first switch of my module is the driver "GE Zigbee Switch"
It is possible to look to code of this driver to do something similar but only changing the target switch id?

For those that are looking for an easy answer for this post, there is the solution =>

It worked for me without coding anything :slight_smile:

Thank you @mike.maxwell :+1:

2 Likes