Tuya/moes 2 gang zigbee light switch

HI there

New here and just bought my first Hubitat. I was able to pair my light switch and used the following code:

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

I problem is that it turns both gang on and off together. Can I control them individually? is their a code for it

Hi,

Please use Martin's driver latest version - download it from here :

https://raw.githubusercontent.com/martinkura-svk/Hubitat/main/Moes%20ZigBee%20Wall%20Switch

Note, that the driver name differs a bit - it is "Moes ZigBee Wall Switch 1/2/3-Gang"
It may be a good idea to delete the version that you are using at the moment to avoid further confusion.

After installing the driver from the link above, pair the switch once again to your Hubitat hub.
The new Zigbee pairing is obligatory, otherwise the individual gangs control will not work.

Good luck!


Update: the driver is now available for installation via HPM :

thank you for your reply. I'll try it tonight and get back to you

thank you so much for that, it worked like a charm

1 Like