[Release] Tuya/Lonsonho 1-gang and 2-gang zigbee dimmer module driver

Hi all. I've been a bit busy IRL for the past few months hence I've been quiet on here.

@kkossev is correct regarding setting up the device - it needs to recognise the specific fingerprint (the entries in the driver code around line 50). If it does not then it will not create child devices. If the device is originally set up with a different driver and you switch to my one; then you should click the "configure" button to cause the child devices to be created. Deleting the child devices then clicking "configure" might also help fix things if it has gotten into a confused state and stopped working.

@Silvermane ... the instructions for my 1 and 2 channel dimmers both say that to reset "Turn off the traditional button switch (the one connected to the Zigbee dimmer module). Then press and hold for 10 seconds or more until the lamp connected to themodule flash quickly for pairing."

@Silvermane @pm1 @dean2 Sorry to hear some of you have been having problems. I've not encountered the problems some of you describe with it losing the ability to control the module after a period of time. I'm not sure what to suggest.

@dean2 @kkossev is correct regarding dimmer functions not working - some newer modules use different non-standard EF00 cluster control codes. I'm afraid I don't have one of these newer modules so am not in a position to know what the correct codes are to send and receive. If @kkossev or anyone else is able to come up with changes to the code to support them then I'll be very happy to try to merge them into the driver.

1 Like

Quick thought - I think I saw somethign like this happen to mine after a HE software update at some point during the last 6 months. The "null"s in the logging output do suggest that the driver was no longer able to correctly determine what child devices it had. Almost as if the upgrade somehow corrupted it.

The only fix was to delete and rediscover the device, or possibly just delete the child devices and "configure" again. I'm afraid I cannot remember which. But I've not had problems since.

I've finally gotten round to merging "_TZ3000_7ysdnebc" into the driver. Should be available through HPM as version 0.3.0

1 Like

Hi

I also have one of these and as you say, the driver won't work for it at all on Hubitat. I have found this one for SmartThings

When I use this in Hubitat though, it's not creating the child devices - The parent on/off works on channel one, but not on channel two.

Any chance you could help, I'm struggling to find a 2-gang dimmer module that works with Hubitat!

I've had a go at editing it and I have managed to get this, which creates the child devices, but none of the commands work on them:

/*
 *  Moes ZigBee Switch
 *  
 *  Copyright 2020 SmartThings
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 *  use this file except in compliance with the License. You may obtain a copy
 *  of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  License for the specific language governing permissions and limitations
 *  under the License.
 *
 */
public static String version() { return "v0.0.1.20210727" }


private getMODEL_MAP() { 
    [
        'TS0601' : 3
    ]
}

metadata {
    definition(name: "Moes Twin Zigbee Dimmer", namespace: "Moes", author: "Pumba", ocfDeviceType: "oic.d.light", vid: "Light") {
        capability "Actuator"
        capability "Configuration"
        capability "Refresh"
        capability "Health Check"
        capability "Switch"
        capability "Switch Level"

        command "childOn", ["string"]
        command "childOff", ["string"]
		command "childSetLevel", ["string", "string"]

        //Moeshouse Switch
        // 1 gang
		fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_amp6tsvy", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "42", deviceJoinName: "Moes Multi Switch 1"
		// 2 gang dimmer
        fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_e3oitdyu", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "44", deviceJoinName: "Moes Multi Switch 1"
        // 3 gang
        fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_tz32mtza", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "42", deviceJoinName: "Moes Multi Switch 1"
      }
 }
// Tile
    tiles(scale: 2) {
		multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
			tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
				attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
				attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
				attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
				attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
			}
			tileAttribute ("device.level", key: "SLIDER_CONTROL") {
				attributeState "level", action:"switch level.setLevel"
			}
		}
		standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		main "switch"
//		details(["switch", "refresh"])
        details(["switch", "refresh", "level"])
	}

def installed() {
	//createChildDevices()
	updateDataValue("onOff", "catchall")
	refresh()
}

// Parse incoming device messages to generate events
def parse(String description) {
    Map map = [:]
    //def event = zigbee.getEvent(description)

    if (description?.startsWith('catchall:')) {
        log.debug description
        // call parseCatchAllMessage to parse the catchall message received
        map = parseCatchAllMessage(description)
        if (map != [:]) {
            log.debug "ok send event: $map.name $map.value"
            sendEvent(name: map.name, value: map.value)
        }
    }
    else {
        log.warn "DID NOT PARSE MESSAGE for description : $description"
    }
}

private getChildEndpoint(String dni) {
	dni.split(":")[-1] as Integer
}

def off() {
    log.debug "called off"
    zigbee.command(0xEF00, 0x0, "00010101000100")  //kanaal 1
}

def on() {
    log.debug "called on" 
    zigbee.command(0xEF00, 0x0, "00010101000101")  //kanaal 1
}

def setLevel(value) {
    log.debug "called setLevel with value $value"
    if (value >= 0 && value <= 100) {
        Map commandParams = [:]
        String commandPayload = "0001020200040000" + zigbee.convertToHexString((value * 10) as Integer, 4)  //kanaal 1
        zigbee.command(0xEF00, 0x0, commandPayload)
    }
}

def refresh() {
    log.debug "called refresh"
    zigbee.command(0xEF00, 0x0, "00020100")
    //pauseExecution(1000)
    //zigbee.command(0xEF00, 0x0, "0002020200")
   
}

def childOn(String dni) {
	def childEndpoint = getChildEndpoint(dni)
    	def name = dni.split("-")[-1]
        def cmd = zigbee.smartShield(text: "${name} on").format()
        sendHubCommand(new hubitat.device.HubAction(cmd))
    log.debug(" child on ${dni} ${childEndpoint} ")
	zigbee.command(0xEF00, 0x0, "00000701000101") //kanaal 2
    }

def childOff(String dni) {
	def childEndpoint = getChildEndpoint(dni)
    	def name = dni.split("-")[-1]
        def cmd = zigbee.smartShield(text: "${name} off").format()
        sendHubCommand(new hubitat.device.HubAction(cmd))
    log.debug " child off ${dni} ${childEndpoint} "
	zigbee.command(0xEF00, 0x0, "00000701000100") //kanaal 2
    }
    

def childSetLevel (String dni, Integer value) {
	log.debug "called setLevel with value $value"
    if (value >= 0 && value <= 100) {
        Map commandParams = [:]
        String commandPayload = "0000080200040000" + zigbee.convertToHexString((value * 10) as Integer, 4)  //kanaal 2 ?
        zigbee.command(0xEF00, 0x0, commandPayload)
  }
}




def configure() {
    log.debug "Configuring Reporting and Bindings."
    zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
    setupChildDevices()
}



private Map parseCatchAllMessage(String description) {
    // Create a map from the raw zigbee message to make parsing more intuitive
    def msg = zigbee.parse(description)
    Map result = [:]
    switch(msg.clusterId) {
        case 0xEF00: 
            def attribute = getAttribute(msg.data)
            def value = getAttributeValue(msg.data)
            log.debug "173 Atribute ${attribute} AttributeValue ${value}"
                switch (attribute) {
                case "switch": 
                    switch(value) {
                        case 0:
                            result = [
                                name: 'switch',
                                value: 'off',
                                data: [buttonNumber: 1],
                                descriptionText: "${device.displayName} button was pressed",
                                isStateChange: true
                            ]
                            log.debug "185 ${device.displayName} button was pressed"
                        break;

                        case 1:
                            result = [
                                name: 'switch',
                                value: 'on',
                                data: [buttonNumber: 1],
                                descriptionText: "${device.displayName} button was pressed",
                                isStateChange: true
                            ]
                            log.debug "${device.displayName} button was pressed"
                            log.debug "297 ${result}"
                        break;
                        
                    }
                
                break;
                
                case "switch2": 
                    switch(value) {
                        case 0:
                            result = [
                                name: 'switch2',
                                value: 'off',
                                data: [buttonNumber: 7],
                                descriptionText: "${device.displayName} button was pressed",
                                isStateChange: true
                            ]
                            log.debug "214 ${device.displayName} button was pressed"
                            
                        break;

                        case 1:
                            result = [
                                name: 'switch2',
                                value: 'on',
                                data: [buttonNumber: 7],
                                descriptionText: "${device.displayName} button was pressed",
                                isStateChange: true
                            ]
                            log.debug "${device.displayName} button was pressed"
                            log.debug "227 ${result}"
                        break;
                        
                    }
                
                case "level": 
                    int levelValue = value / 10
                    result = [
                        name: 'level',
                        value: levelValue,
                        data: [buttonNumber: 1],
                        descriptionText: "${device.displayName} level was modified",
                        isStateChange: true
                    ]
                    log.debug "241 result ${result}"
               
               break;
               
               case "level2": 
                    int levelValue = value / 10
                    result = [
                        name: 'level2',
                        value: levelValue,
                        data: [buttonNumber: 7],
                        descriptionText: "${device.displayName} level was modified",
                        isStateChange: true
                    ]
                    log.debug "254 result ${result}"
               
               break;
            }
        
        break;
    }
    
    return result
}



private String getAttribute(ArrayList _data) {
    log.debug "282 data:${_data}"
    String retValue = ""
      if (_data[1] >0) {  
     	if (_data[2] == 2 && _data[3] == 2 && _data[4] == 0) {
			retValue = "level" 
        }
       
      	if (_data[2] == 1 && _data[3] == 1 && _data[4] == 0) {
            retValue = "switch"
        }
        
   		if (_data[2] == 8 && _data[3] == 2 && _data[4] == 0) {
            retValue = "level2"
        }
        
        if (_data[2] == 7 && _data[3] == 1 && _data[4] == 0) {
            retValue = "switch2"
        }  
    }
    log.debug "301 return ${retValue}"
    return retValue
	
}



private int getAttributeValue(ArrayList _data) {
    int retValue = 0
    
    if (_data.size() >= 6) {
        int dataLength = _data[5] as Integer
        int power = 1;
        for (i in dataLength..1) {
            retValue = retValue + power * _data[i+5]
            power = power * 256
        }
    }
    
    return retValue
}

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_e3oitdyu':
            buttons = 2
        break
        case '_TZE200_tz32mtza':
            buttons = 3
        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", "Generic Zigbee Dimmer", 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)
        }
    }
}

Thanks
Chris

Did you get your device working with HE?

Hi all

Thanks for the drivers. Working well on my QS-Zigbee-D02-TRIAC-2C-LN 2-gang dimmers :smiley:

Has anyone succeeded in wiring these modules 2-way? I've got 3 core available from the back box containing the module at the bottom of the stairs to another back box at the top of the stairs. Hoping to be able to wire another switch to give physical control to the landing light that the module is controlling.

The modules are working as required 1-way but if I wire the second switch the light is permanently on with no control via HE :thinking:

No, not yet!

Can you post again your device fingerprint? ( the model and the manufacturer from the device page Data section)

hey @fc352bf0a6929e9fa80f ,

Just a heads up, i purchased this dimmer module

wouldn't work out of box, so I modified the driver and added the mfg ID below in place of _TZ3210_ngqk6jia in the @Field static def modelConfigs

  • endpointId: 01
  • application: 40
  • manufacturer: _TZ3210_3mpwqzuu
  • model: TS110E

Seems to work good now.

This is the module I got. My only gripes are that it doesn't support toggle, and parameters like switch state isn't passed (likely a driver issue). Also, I'm getting this error in the logs

2023-01-18 09:08:59.677 PM[error](http://IPAddress/logs?tab=past&deviceId=443#)java.lang.NullPointerException: Cannot invoke method toInteger() on null object on line 857 (method parse)

Also, for others, in case you didn't know, you can easily convert Decora toggle switches to momentary switches. Here's a tutorial

1 Like

also, I've noticed that control of parent doesn't control channel 2. (eg: on\off\setlevel on parent controls ch1, but not ch2)

Hey, I've just got the QS-Zigbee-D02-Triac-2C-LN installed, but I think I have a problem with the device itself. The fingerprint doesn't make sense and it looks like it thinks it is a motion sensor. Can anyone advise what to do? Is there a way to flash the devise itself to teach it what it is .

Link to what I bought:
€ 20,29 34% Off | Lonsonho Tuya Smart Zigbee Dimmer Switch Module Relay With / No Neutral Smart Life Zigbee2MQTT Alexa Google Home Compatible
https://a.aliexpress.com/_m03aBh0

  • endpointId: F2
  • application: unknown
  • driver: v1.0.1.1123
  • manufacturer: unknown
  • model: unknown




Any advise would be appreciated.

This is a well-known problem of Hubitat with some new devices. Hopefully, when the new C-8 Zigbee issues are all rectified, the Hubutat team will fix this 'F2' issue as well.

Thanks for responding, so dead in the water with this device with no fix likely for the C-7 (with C-8 fixing it).

Or do you mean by the below, that the team will circle back to fix this issue for c-7 when the c-8 higher priority issues have been fixed?

Am I right in saying that I have to get a C-8? I just bought my C-7 three months ago, and would have liked to kick the buying of a new hub down the road a little. If I'm just buying new lots of devices now they may or may not have newer versions of devices am I going to keep running into this issue?

I have no idea what Hubitat plans are in regard to the old C-7 hubs Zigbee improvements, but TBH, I don't have high hopes... There are just a few devices that suffer from this problem (mostly Tuya), and Tuya support has never been a priority,

Can you delete the device ( click on REMOVE DEVICE button on the bottom right of the device page) and then pair the dimmer again as a new device? You will see a hyperlink "Device Info". Click on it (before setting the device a name) and copy/paste the detailed device information from the popup window.

Done, info below. Thanks for looking at it for me.

Device pairing info

Manufacturer: _TZ3210_pagajpog
Endpoint 01 application: 41
Endpoint 01 endpointId: 01
Endpoint 01 idAsInt: 1
Endpoint 01 inClusters: 0005,0004,0006,0008,E001,0000
Endpoint 01 initialized: true
Endpoint 01 manufacturer: _TZ3210_pagajpog
Endpoint 01 model: TS110E
Endpoint 01 outClusters: 0019,000A
Endpoint 01 profileId: 0104
Endpoint 01 stage: 4
Endpoint 02 application: unknown
Endpoint 02 endpointId: 02
Endpoint 02 idAsInt: 2
Endpoint 02 inClusters: 0005,0004,0006,0008,E001
Endpoint 02 initialized: true
Endpoint 02 manufacturer: unknown
Endpoint 02 model: unknown
Endpoint 02 profileId: 0104
Endpoint 02 stage: 4
Endpoint F2 application: unknown
Endpoint F2 endpointId: F2
Endpoint F2 idAsInt: 242
Endpoint F2 initialized: true
Endpoint F2 manufacturer: unknown
Endpoint F2 model: unknown
Endpoint F2 outClusters: 0021
Endpoint F2 profileId: A1E0
Endpoint F2 stage: 4

1 Like

Can anything be done with this device on a c7

Can you temporarily switch to this driver (it is not intended to work with these dimmers, but use it just for a test), then click on the Initialize button and then check whether a simple On/Off command works for the first switch only?

Yes it does work as a simple on / off switch with that (temporary) driver.

1 Like

That's good news, it means that the same temporary patch/workaround can be implemented in the Tuya Zigbee Dimmer driver. Hopefully, this weekend.