Help adding a variation of Tuya TS0044, TS004F to ZHA

Hello everyone, first post for the community !
I’ve been setting up my HA for 2 months now and had no problem thanks to this forum.

I’ve ordered a Tuya 4 gang switch that is apparently the new model of TS0044 : TS004F
Some people had issues with this on this forum but I could not find any solution. The gang only appears with one switch inside HA using ZHA integration and cannot be used propoerly.
So, I’ve decided to add this new device to zigpy following the protocol.
The problem is that my quirk is recognized but not taken into account :

2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZ3000_xabckq1v TS004F (60:a4:23:ff:fe:db:30:83)
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'zhaquirks.ts004f.ZemiSmartRemote004F'>
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Fail because device_type mismatch on at least one endpoint
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'zhaquirks.ts004f.TuyaSmartRemote004F'>
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Fail because device_type mismatch on at least one endpoint

here is the identifier :

{
  "node_descriptor": "NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=0, *allocate_address=True, *complex_descriptor_available=False, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False, *is_valid=True, *logical_type=<LogicalType.EndDevice: 2>, *user_descriptor_available=False)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0104",
      "in_clusters": [
        "0x0000",
        "0x0001",
        "0x0003",
        "0x0004",
        "0x0006",
        "0x1000"
      ],
      "out_clusters": [
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0008",
        "0x000a",
        "0x0019",
        "0x1000"
      ]
    }
  },
  "manufacturer": "_TZ3000_xabckq1v",
  "model": "TS004F",
  "class": "zigpy.device.Device"
}

And here is my quirk :

"""Tuya 4 Button Remote TS004F"""

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import Basic, OnOff, Identify, Ota, LevelControl, PowerConfiguration, Time, Groups, Scenes
from zigpy.zcl.clusters.lightlink import LightLink

from zhaquirks.const import (
    BUTTON_1,
    BUTTON_2,
    BUTTON_3,
    BUTTON_4,
    COMMAND,
    DEVICE_TYPE,
    DOUBLE_PRESS,
    ENDPOINT_ID,
    ENDPOINTS,
    INPUT_CLUSTERS,
    LONG_PRESS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    SHORT_PRESS,
)
from zhaquirks.tuya import TuyaSmartRemoteOnOffCluster


class Tuya4NewButtonTriggers:
    """Tuya 4-button New version remote device triggers."""

    device_automation_triggers = {
        (SHORT_PRESS, BUTTON_1): {ENDPOINT_ID: 1, COMMAND: SHORT_PRESS},
        (LONG_PRESS, BUTTON_1): {ENDPOINT_ID: 1, COMMAND: LONG_PRESS},
        (DOUBLE_PRESS, BUTTON_1): {ENDPOINT_ID: 1, COMMAND: DOUBLE_PRESS},
        (SHORT_PRESS, BUTTON_2): {ENDPOINT_ID: 1, COMMAND: SHORT_PRESS},
        (LONG_PRESS, BUTTON_2): {ENDPOINT_ID: 1, COMMAND: LONG_PRESS},
        (DOUBLE_PRESS, BUTTON_2): {ENDPOINT_ID: 1, COMMAND: DOUBLE_PRESS},
        (SHORT_PRESS, BUTTON_3): {ENDPOINT_ID: 1, COMMAND: SHORT_PRESS},
        (LONG_PRESS, BUTTON_3): {ENDPOINT_ID: 1, COMMAND: LONG_PRESS},
        (DOUBLE_PRESS, BUTTON_3): {ENDPOINT_ID: 1, COMMAND: DOUBLE_PRESS},
        (SHORT_PRESS, BUTTON_4): {ENDPOINT_ID: 1, COMMAND: SHORT_PRESS},
        (LONG_PRESS, BUTTON_4): {ENDPOINT_ID: 1, COMMAND: LONG_PRESS},
        (DOUBLE_PRESS, BUTTON_4): {ENDPOINT_ID: 1, COMMAND: DOUBLE_PRESS},
    }


class TuyaSmartRemote004F(CustomDevice, Tuya4NewButtonTriggers):
    """Tuya 4-button New version remote device."""

    signature = {
        # "node_descriptor": "NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=0, *allocate_address=True, *complex_descriptor_available=False, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False, *is_valid=True, *logical_type=<LogicalType.EndDevice: 2>, *user_descriptor_available=False)",
        # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=260, device_version=1, input_clusters=[0, 1, 3, 4, 6, 4096], output_clusters=[25, 10, 3, 4, 5, 6, 8, 4096])
        MODELS_INFO: [("_TZ3000_xabckq1v", "TS004F")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    OnOff.cluster_id,
                    LightLink.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    TuyaSmartRemoteOnOffCluster,
                    LightLink.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaSmartRemoteOnOffCluster,
                    LevelControl.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        },
    }


class ZemiSmartRemote004F(CustomDevice, Tuya4NewButtonTriggers):
    """Tuya 4-button New version remote device."""

    signature = {
        # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=260, device_version=1, input_clusters=[0, 1, 3, 4, 6, 4096], output_clusters=[25, 10, 3, 4, 5, 6, 8, 4096])
        MODELS_INFO: [("_TZ3000_xabckq1v", "TS004F")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    OnOff.cluster_id,
                    LightLink.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    TuyaSmartRemoteOnOffCluster,
                    LightLink.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaSmartRemoteOnOffCluster,
                    LevelControl.cluster_id,
                    LightLink.cluster_id,
                ],
            }
        },
    }

I used the original file of the ts0044 in order to set this up and adapt it. I’ve tried using different signatures including and not including

 # "node_descriptor": "NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=0, *allocate_address=True, *complex_descriptor_available=False, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False, *is_valid=True, *logical_type=<LogicalType.EndDevice: 2>, *user_descriptor_available=False)",

but it does not matter apparently.

Here is the full log for the joining of the device :

2021-05-23 20:57:20 INFO (MainThread) [zigpy.application] Device 0x66f7 (60:a4:23:ff:fe:db:30:83) joined the network
2021-05-23 20:57:20 DEBUG (MainThread) [zigpy.zdo] [0x66f7:zdo] ZDO request ZDOCmd.Device_annce: [0x66F7, 60:a4:23:ff:fe:db:30:83, 128]
2021-05-23 20:57:20 INFO (MainThread) [zigpy.device] [0x66f7] Requesting 'Node Descriptor'
2021-05-23 20:57:20 DEBUG (MainThread) [zigpy.util] Tries remaining: 2
2021-05-23 20:57:20 DEBUG (MainThread) [zigpy.device] [0x66f7] Extending timeout for 0x25 request
2021-05-23 20:57:21 INFO (MainThread) [zigpy.device] [0x66f7] Node Descriptor: NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=0, *allocate_address=True, *complex_descriptor_available=False, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False, *is_valid=True, *logical_type=<LogicalType.EndDevice: 2>, *user_descriptor_available=False)
2021-05-23 20:57:21 INFO (MainThread) [zigpy.device] [0x66f7] Discovering endpoints
2021-05-23 20:57:21 DEBUG (MainThread) [zigpy.util] Tries remaining: 3
2021-05-23 20:57:21 DEBUG (MainThread) [zigpy.device] [0x66f7] Extending timeout for 0x26 request
2021-05-23 20:57:21 INFO (MainThread) [zigpy.device] [0x66f7] Discovered endpoints: [1]
2021-05-23 20:57:21 INFO (MainThread) [zigpy.endpoint] [0x66f7:1] Discovering endpoint information
2021-05-23 20:57:21 DEBUG (MainThread) [zigpy.util] Tries remaining: 3
2021-05-23 20:57:21 DEBUG (MainThread) [zigpy.device] [0x66f7] Extending timeout for 0x27 request
2021-05-23 20:57:21 INFO (MainThread) [zigpy.endpoint] [0x66f7:1] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=260, device_version=1, input_clusters=[0, 1, 3, 4, 6, 4096], output_clusters=[25, 10, 3, 4, 5, 6, 8, 4096])
2021-05-23 20:57:21 DEBUG (MainThread) [zigpy.device] [0x66f7] Extending timeout for 0x28 request
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.zcl] [0xdbbe:1:0x0006] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=58 command_id=Command.Report_Attributes>
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.zcl] [0xdbbe:1:0x0006] ZCL request 0x000a: [[Attribute(attrid=0, value=<TypeValue type=int8s, value=0>)]]
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.zcl] [0xdbbe:1:0x0006] Attribute report received: on_off=0
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.zcl] [0x66f7:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=True> manufacturer=None tsn=40 command_id=Command.Read_Attributes_rsp>
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.endpoint] [0x66f7:1] Manufacturer: _TZ3000_xabckq1v
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.endpoint] [0x66f7:1] Model: TS004F
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZ3000_xabckq1v TS004F (60:a4:23:ff:fe:db:30:83)
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'zhaquirks.ts004f.ZemiSmartRemote004F'>
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Fail because device_type mismatch on at least one endpoint
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'zhaquirks.ts004f.TuyaSmartRemote004F'>
2021-05-23 20:57:22 DEBUG (MainThread) [zigpy.quirks.registry] Fail because device_type mismatch on at least one endpoint

If someone can tell me what I am missing ?

Thanks

Unless mistaken, this is a DIMMER_SWITCH id (zigpy/zigpy/profiles/zha.py at fc8e923e2f2482810b9643c0b2bde149a56a1930 · zigpy/zigpy · GitHub)

so thats what you have to put in your quirk signature

1 Like

Thanks, with this it is recognized.
But the device does not fully sync, I’ll have to dig deeper in order to make it work

Works now, there was a glitch on adding the device.
Now figuring out the events…

I have a support request open at [Device Support Request] Tuya zigbee remote TS004F · Issue #897 · zigpy/zha-device-handlers · GitHub

I tried doing something similar but failed miserably. I followed the instructions at https://github.com/zigpy/zha-device-handlers#testing-new-releases. Copied the TS0044.py into the /deps/ folder where it is supposed to go. I hoped it would be a simple matter of changing the MODELS_INFO to correspond but nope. I broke HA. I believe I corrupted the zigbee.db. I had to delete it to get things kinda working and then restore from a snapshot. I may give it another shot with the info from the link but I’m going to make a complete backup before trying.

Oh
my quirk works with no trouble but it cant be mapped to be used as a 4 scene switch.
I had one trouble when adding the switch at first but I just reseted it and synced it again and it was working fine
I added the quirk based on the guide here : GitHub - zigpy/zha-device-handlers: ZHA device handlers bridge the functionality gap created when manufacturers deviate from the ZCL specification, handling deviations and exceptions by parsing custom messages to and from Zigbee devices. (so not in the deps directory)

I am currently trying to analyse events in order to map them in a blueprint relying on this one :

If get it working please also submit a pull request to upstream → GitHub - zigpy/zha-device-handlers

I was having some success with the button maps using Node-RED and watching zha_events. I’ll give the docker install a try and see what I can come up with.

that is planned :wink:

Still trying to figure out how to bind the new scheme since there is only one endpoint for all buttons…

On this thread they manage to bind one click for all and long press for 3/4 for Zigbee2MQTT

Since this is the first time I deal with this kind of things on HA (I am a software developer), I still have to fully understand how to create these custom handlers and get all the debug info I need.

FYI 3/4 trigger these events

2021-05-25 20:45:03 DEBUG (MainThread) [zigpy.zcl] [0x943a:1:0x0008] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=False disable_default_response=False> manufacturer=None tsn=61 command_id=2>
2021-05-25 20:45:03 DEBUG (MainThread) [zigpy.zcl] [0x943a:1:0x0008] ZCL request 0x0002: [1, 51, 10]
2021-05-25 20:45:03 DEBUG (MainThread) [zigpy.zcl] [0x943a:1:0x0008] No handler for cluster command 2
2021-05-25 20:45:03 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event zha_event[L]: device_ieee=60:a4:23:ff:fe:db:30:83, unique_id=60:a4:23:ff:fe:db:30:83:1:0x0008, device_id=87b8bc27dfe9d12510741cebfb3dae1f, endpoint_id=1, cluster_id=8, command=step, args=[1, 51, 10]>

2021-05-25 20:45:01 DEBUG (MainThread) [zigpy.zcl] [0x943a:1:0x0008] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=False disable_default_response=False> manufacturer=None tsn=60 command_id=2>
2021-05-25 20:45:01 DEBUG (MainThread) [zigpy.zcl] [0x943a:1:0x0008] ZCL request 0x0002: [0, 51, 10]
2021-05-25 20:45:01 DEBUG (MainThread) [zigpy.zcl] [0x943a:1:0x0008] No handler for cluster command 2
2021-05-25 20:45:01 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event zha_event[L]: device_ieee=60:a4:23:ff:fe:db:30:83, unique_id=60:a4:23:ff:fe:db:30:83:1:0x0008, device_id=87b8bc27dfe9d12510741cebfb3dae1f, endpoint_id=1, cluster_id=8, command=step, args=[0, 51, 10]>


FYI request on the repo:

1 Like

I think the mapping goes somewhere like this:

        (SHORT_PRESS, BUTTON_3): {
            COMMAND: COMMAND_STEP,
            CLUSTER_ID: 8,
            ENDPOINT_ID: 1,
            ARGS: [0, 51, 10],
        },
        (SHORT_PRESS, BUTTON_4): {
            COMMAND: COMMAND_STEP,
            CLUSTER_ID: 8,
            ENDPOINT_ID: 1,
            ARGS: [1, 51, 10],
        },		
[...]

According to the zigbee2mqtt code, “Hold” would be COMMAND_MOVE

I too have this switch and don’t even know where to start. Did anyone figure this out?

Got my switch in the mail today, realized it’s not working as it should, and thus I ended up here. Anything I can do to help? Just let me know, I know myself around python quite well!
I ripped out the previous switch so I’m very eager to do whatever it takes to get this 4-way switch to work!

5 Likes

Did anyone have any luck with this?

I have tried the quirk above and button 3 & 4 send a zha_event for long and short press. But button 1 & 2 just turn off and on the device’s entity

From HA? It cannot be automagical :wink:

Sorry I am quite new to HA, I didn’t completely understand what you meant.

With the comment above I meant that even without the quirk the default action of button 1 was turning on Test on_off and button 2 would turn the entity off. With the quirk the default action is unchanged.

image

I just keep checking back hoping. This sort of thing is above my current knowledge. Maybe I can look into quirks.

1 Like

Anyone had luck with this?
I’m still struggling to my it works. The only way I managed to used it is with NodeRed.

3 Likes

Same here, only the top & bottom right buttons respond in NodeRED through zha_events.