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.
Apache License 2.0
743 stars 679 forks source link

ts0012 - new firmware #1744

Closed kryanth closed 2 years ago

kryanth commented 2 years ago

I have a couple of newer ts0012 "moes" switches which appear to have a newer firmware, which is fixed in koenkk's z2m

https://pullanswer.com/questions/tuya-ts0012-turns-on-both-loads-when-only-one-switch-is-pressed

The model signature is a bit different. I have some older TS0012 / _TZ3000_18ejxno0 that work without quirks, but this one appears to need some assistance as per the above URL.

{ "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, allocate_address=True, 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)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0100", "in_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006" ], "out_clusters": [ "0x0019" ] }, "2": { "profile_id": 260, "device_type": "0x0100", "in_clusters": [ "0x0004", "0x0005", "0x0006" ], "out_clusters": [] } }, "manufacturer": "_TZ3000_18ejxno0", "model": "TS0012", }

kryanth commented 2 years ago

Actually, it looks different to that as well, might just be some TS0012 devices shipping with "enchanted device".

kryanth commented 2 years ago

This is yet another enchanted device. These were newer TS0012 _TZ3000_18ejxno0 - I had to add a new device signature specifically for these, and extend the EnchantedDevice class, then remove and re-add, they are working as expected now.

Do I need to keep that signature and class, or is the enchanted device a once-off call?

eugeene commented 2 years ago

Hello,

I too am having this problem with the '_TZ3000_18ejxno0' device. HASS turns on both parts when any of the software button is pressed. I have the most recent version of HA (and thus zigpy, I guess)

I'd like to know if there is something I can do on my server to make the switches work separately or if it is something I just have to wait for - zigpy update?

Thank you all for an answer

EDIT: can anyone tell me what an 'enchanted' device means?

kryanth commented 2 years ago

I believe its just a wierd firmware quirk, and the enchanted class (found in that same zhaquirk library) is just a super class that initialises the device with some commands on HA startup. It does have a different signature to my other devices.

in your HA directory, find the python library for zhaquirks

I just added the following snippet to the ts001x.py, you can also just add whatever. You may need to verify the device signature, but this just worked for the couple of these "_TZ3000_18ejxno0" devices. (edited here because the formatting is whack)

class TuyaDoubleEnchantedSwitch(EnchantedDevice, TuyaSwitch):
    signature = {
        MODEL: "TS0012",
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                ],
                OUTPUT_CLUSTERS: [],
            },
        },
    }
    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaZBOnOffAttributeCluster,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaZBOnOffAttributeCluster,
                ],
                OUTPUT_CLUSTERS: [],
            },
        },
    }
eugeene commented 2 years ago

Hello and thank you for explaining. I am running HA in a docker image and will try something tomorrow. Thank you again and have a nice day!

eugeene commented 2 years ago

Hello again, I'd like to ask for a help.

I downloaded the quirk ts001x.py file for the '_TZ3000_18ejxno0' double switch, enabled ZHA quirks in HA config and restarted Home Assistant. I guess I am supposed to see something like 'Quirk: TS0012' in the 'Device info' in ZHA pane. But there is no quirk info on that device and the switch still turns on/off both parts when switched in Home Assisstant. Is there anythin else I have to do?

I guess the .py file is loaded, because the __pycache__ folder was created beside the .py file.

Thank you very much

kryanth commented 2 years ago

Compare the signature for your device , and make sure it matches the signature in that snippet.

If the ts001x.pyc file in the python .cache directory exists, it will need to be removed on every restart of HA, so it rebuilds the cache file. Odds are good if you launched the docker image, it built the python cache then so updating the content has no effect until after the cache file is created.

I should be less lazy and throw a branch in to make it easier, which if it works as is for you might encourage me to do that. Not sure how many of these are accepted.

eugeene commented 2 years ago

Yes, I use docker image for HA.

The signature shows:


{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *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)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0100",
      "in_clusters": [
        "0x0000",
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0006"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    },
    "2": {
      "profile_id": 260,
      "device_type": "0x0100",
      "in_clusters": [
        "0x0004",
        "0x0005",
        "0x0006"
      ],
      "out_clusters": []
    }
  },
  "manufacturer": "_TZ3000_18ejxno0",
  "model": "TS0012",
  "class": "zigpy.device.Device"
}
kryanth commented 2 years ago

Pretty sure that should match the signature from my sample, so it's not using that instance of ts001x.py , or the cache isn't being swept (possibly not even that instance of zhaquirks) so you may need to play with that.

eugeene commented 2 years ago

Thank you. I'll try something (clearing cache, repairing - I guess this is not needed?)

Anyways thank you for your time! I'll get back soon.

kryanth commented 2 years ago

I don't think repairing will do much in this context. You may need to look at the constant definitions for cluster_id (in zhaquirks) to check if the signature matches, it's possible they have several slightly similiar firmwares with slightly different signatures that all require the "enchanted class" to unlock.

eugeene commented 2 years ago

I have just one last question - is the ts001x.py enough to copy to the HA quirks folder or do I have to put the whole tuya folder (or full zha-device-handlers folder) there (this fails on zigbee initialization - there is some python import error) When I use the ts001x.py file alone, no errors are produced.

I am so sorry, I have never used these quirks and I am pretty new to ZigBee. So I hope I do the things the right way in the first place.

Thank you :X

MattWestb commented 2 years ago

@eugeene Your device is 95% identical with the current "normal" TS0012 only one cluster is being added and need being added in the quirk. Editing this device class: https://github.com/zigpy/zha-device-handlers/blob/b8d8480c72c871e2408637231ac1a2a485e33b03/zhaquirks/tuya/ts001x.py#L68 and adding Identify.cluster_id, on one new line in the signature and replacement (with the same indent as the other like and no extra spaces) best after the Basic.cluster_id, so its looks nice and saving it and restarting HA and look if the system is loading the updated quirk for your device.

eugeene commented 2 years ago

Yes, that worked! Now it says:

Quirk: ts001x.TuyaDoubleNoNeutralSwitch

But unfortunately, both parts are still being switched on/off :(

MattWestb commented 2 years ago

Great then the quirk is being loaded OK.

And the device is needing some "tuya magic spell" being casted for getting the both endpoints working OK (some other device is doing the same like LIDL power strip with new firmware).

I calling for more help !!!

Hola @javicalle can you taking one look if its one easy way getting the tuya magic being implanted on this new device ??

I dont knowing if its enough casting it then its already being pared or must being done then its paring in the init stage.

Thanks in advance !!

eugeene commented 2 years ago

Thank you for your time and patience, guys. If you need any data from my device, I am ready to help. ;)

kryanth commented 2 years ago

My comment has the full code snippet required to "fix" the device worked for me and I just added it to the ts001x.py file.

MattWestb commented 2 years ago

Need more testing then have implanting the "tuya magic" but im no code worrier so i cant doing the coding and need help from our devs for implanting the function in the quirk.

If you is changing the state of the device on the device is it reporting it OK in ZHA (manual on and off on the device) ?

@kryanth Your device have the same ID _TZ3000_18ejxno0 but is having the same firmware version (reading the sw_build_id (id: 0x4000) on the basic cluster and you is getting it) or have your device being coonnected to one tuya ZBGW ?

eugeene commented 2 years ago

My comment has the full code snippet required to "fix" the device worked for me and I just added it to the ts001x.py file.

The code from your comment extends from EnchantedDevice as well. I am missing that class in my installation. Can you tell me where do I get the class definition and where to put it? (beside the ts001x.py?)

edit: I just found the import statement for that, but it stil won't work (both sides being switched at the same time) it now shows: Quirk: ts001x.TuyaDoubleEnchantedSwitch, so that's correct. But no worky :(

eugeene commented 2 years ago

If you is changing the state of the device on the device is it reporting it OK in ZHA (manual on and off on the device) ?

when I switch any part of the switch manually, HA reports correct state for both parts. Jus the 'software' swiching is wrong (always both parts)

MattWestb commented 2 years ago

I think the way @kryanth have doing (using the EnchantedDevice class) shall working but its need deleting the device and resetting it and waiting one minute and then paring it agen so the "magic" is being sent to the device then its bring paired.

Try doing that @eugeene and is its not working the first time deleting the device in ZHA and resetting it and then restarting HA and try paring it then ZHA is stable after the restart.

eugeene commented 2 years ago

@MattWestb I'll try that when I get home. But thank you for your patience and kindness. I'll get back to you.

eugeene commented 2 years ago

Hello. Indeed, the re-pairing was needed. I removed the switches, paired again and... It seems to work! Thank you very much again for your time!

MattWestb commented 2 years ago

Then its verified working well :-))

Is some interesting making one PR for adding one new device lass like @kryanth was doing so its working without local quirk for all user in the future ?

eugeene commented 2 years ago

Ill try that again from scratch. Currently, I added the code from the comment above (the code that inherits from EnchantedDevice, the file in this repo does not, it inherits from TuyaSwitch only) Ill try the file from this repo again tomorrow, because I am currently not at home again.

eugeene commented 2 years ago

Ok, so i removed the old file, removed the pycache, installed the file from this repo, restarted HA and it seems to be working. But the file is not 'loaded' in 'quirk' part of the device info. So the switches are probably set correctly from somethig from the code above from earlier. The question is - will it work after another re-pair? Should i try that?

MattWestb commented 2 years ago

If the system is not loading the quirk its some problem with the signature for your device (very likely missing the cluster 0x0003).

The "tuya magic" is saved in the device so if it being casted on the device its keeping it until the device is being retested and re-powered.

Pleas making one test by deleting the device in ZHA and then resetting it on the device (pressing the reset switch or other way by the device manual) and in the end taking the power away for one minute and then trying paring it with ZHA.

If all is going as expected your device is being as it was from beginning (both switches is switching from ZHA as one).

I have only experience from the problematic TS004F that is doing very mush strange things but we have getting it working OK now.

eugeene commented 2 years ago

Hello, sorry I was gone - I was Ill

  1. I unpaired the switch from HA, turned the power down for a couple of minutes, put back original ts001x.py, deleted pycache restarted HA and the device is, again working wrong - both sides switched at the same time
  2. Unpaired the device again, turned the power down, put back ENCHANTED script from above, deleted pycache, restarted HA and it is working how it supposed to.

So I had to edit the ts001x.py from this repo - replaced the enchanted code from above, and added these lines to the top of the file to properly make it working:

from zigpy.quirks import CustomDevice
import asyncio

class EnchantedDevice(CustomDevice):
    """Class for enchanted Tuya devices which needs to be unlocked by casting a 'spell'."""

    def __init__(self, *args, **kwargs):
        """Initialize with task."""
        super().__init__(*args, **kwargs)
        self._init_device_task = asyncio.create_task(self.spell())

    async def spell(self) -> None:
        """Initialize device so that all endpoints become available."""
        attr_to_read = [4, 0, 1, 5, 7, 0xFFFE]
        basic_cluster = self.endpoints[1].in_clusters[0]
        await basic_cluster.read_attributes(attr_to_read)

Now everything seems to be working.

javicalle commented 2 years ago

Not sure what is working and not but a few comments from my side.

The signature in the first post has diferent OUTPUT_CLUSTERS that the quirk from https://github.com/zigpy/zha-device-handlers/issues/1744#issuecomment-1259551095. Not sure which one is the good one.

@eugeene you would not need to define the EnchantedDevice class in the file. Just adding the import must work:

from zhaquirks.tuya.mcu import EnchantedDevice

If I have read all correctly, this quirk must work for you (or both if the signature is the same):

class TuyaDoubleNoNeutralSwitch_2(EnchantedDevice, TuyaSwitch):
    """Tuya 2 gang no neutral light switch (v2)."""

    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)
        # "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)"
        MODEL: "TS0012",
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=100
            # device_version=1
            # input_clusters=["0x0000", "0x0003", "0x0004", "0x0005", "0x0006"]
            # output_clusters=["0x000a", "0x0019"]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            },
            # <SimpleDescriptor endpoint=2 profile=260 device_type=100
            # device_version=1
            # input_clusters=[4, 5, 6]
            # output_clusters=[]>
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                ],
                OUTPUT_CLUSTERS: [],
            },
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaZBOnOffAttributeCluster,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaZBOnOffAttributeCluster,
                ],
                OUTPUT_CLUSTERS: [],
            },
        },
    }

The full file would be (not checked, not tested):

ts001x.py ```python """Tuya 1,2,3 gang no neutral light switch.""" from zigpy.profiles import zha from zigpy.zcl.clusters.general import Basic, Groups, Identify, OnOff, Ota, Scenes, Time from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODEL, OUTPUT_CLUSTERS, PROFILE_ID, SKIP_CONFIGURATION, ) from zhaquirks.tuya import ( TuyaSwitch, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, TuyaZBOnOffAttributeCluster, ) from zhaquirks.tuya.mcu import EnchantedDevice class TuyaSingleNoNeutralSwitch(TuyaSwitch): """Tuya 1 gang no neutral light switch.""" 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) MODEL: "TS0011", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, }, } replacement = { SKIP_CONFIGURATION: True, ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } class TuyaDoubleNoNeutralSwitch(TuyaSwitch): """Tuya 2 gang no neutral light switch.""" 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) # "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)" MODEL: "TS0012", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, # 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [], }, }, } replacement = { SKIP_CONFIGURATION: True, ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [], }, }, } class TuyaDoubleNoNeutralSwitch_2(EnchantedDevice, TuyaSwitch): """Tuya 2 gang no neutral light switch (v2).""" 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) # "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)" MODEL: "TS0012", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, # 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [], }, }, } replacement = { SKIP_CONFIGURATION: True, ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [], }, }, } class TuyaTripleNoNeutralSwitch(TuyaSwitch): """Tuya 3 gang no neutral light switch.""" 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) # "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)" MODEL: "TS0013", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, # 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [], }, # 3: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [], }, }, } replacement = { SKIP_CONFIGURATION: True, ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [], }, 3: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, ], OUTPUT_CLUSTERS: [], }, }, } class Tuya_Single_No_N(TuyaSwitch): """Tuya 1 gang no neutral light switch.""" 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) MODEL: "TS0011", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, }, } class Tuya_Double_No_N(TuyaSwitch): """Tuya 2 gang no neutral light switch.""" 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) # "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)" MODEL: "TS0012", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, # 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, }, } class Tuya_Double_No_N_Plus(TuyaSwitch): """Tuya 2 gang no neutral light switch.""" signature = { # "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, # user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, # mac_capability_flags=, manufacturer_code=4417, maximum_buffer_size=66, # maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, # descriptor_capability_field=, *allocate_address=True, *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)", MODEL: "TS0012", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, # 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, }, } class Tuya_Triple_No_N(TuyaSwitch): """Tuya 3 gang no neutral light switch.""" 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) # "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)" MODEL: "TS0013", ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id], }, # 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, # 3: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, 3: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, }, } ```

Regards.

edit: update code

Quelbazar commented 2 years ago

Hi, @javicalle 's ts001x.py fixed the problem for me. I had to unpair / pair my switches.

JIBYC commented 1 year ago

Hi,

I'm on HA 2023.3 and still getting this behavior. Anything sent from HA causes both switches to be toggled.

What do I do?

TheJulianJES commented 1 year ago

@JIBYC Create a new issue on this repo (and make sure to fill out the issue template).