Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Device Support Request] TS0601 GDC311ZBQ1 Tuya Loratap Garage Switcher #1260

Open
tiagoqpinto opened this issue Jan 6, 2022 · 200 comments
Open
Labels
custom quirk available A custom quirk is available to solve the issue, but it's not merged in the repo yet Tuya Request/PR regarding a Tuya device

Comments

@tiagoqpinto
Copy link

tiagoqpinto commented Jan 6, 2022

Device signature:

{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, 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=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0051",
      "in_clusters": [
        "0x0000",
        "0x0004",
        "0x0005",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    },
    "242": {
      "profile_id": 41440,
      "device_type": "0x0061",
      "in_clusters": [],
      "out_clusters": [
        "0x0021"
      ]
    }
  },
  "manufacturer": "_TZE200_wfxuhoea",
  "model": "TS0601",
  "class": "zigpy.device.Device"
}

Tuya device that has no entities/controls in home assistant and its a simple Zigbee Tuya garage switcher. It has a sensor if the garage is opened or not, but it should be very similar to many other devices like this.

Thanks in advance for the support.

@EagleAdam
Copy link

I have one of these as well, happy to help anyone who is able to help integrate it in to zha, I’m not a developer but happy to help in any way I can!

@cfnascimento
Copy link

Hi! We are together in pain...
Someone who have one of these working fine could help to integrate it in to ZHA?

@javicalle
Copy link
Collaborator

Device operation traces are needed to see if it is possible to implement a quirk.
Each trace must be able to be identified with the action, eg: open, close, stop, etc.

If any of you can include the information, I'll see what can be done.

Regards.

@EagleAdam
Copy link

EagleAdam commented Jan 22, 2022 via email

@MattWestb
Copy link
Contributor

Its have 3 function and i think its sending all of them the repower the device.
https://github.com/Koenkk/zigbee-herdsman-converters/pull/3714/files
You can trying "signal cable" and see if its sending that the door closed then closing the contact.

@javicalle
Copy link
Collaborator

In the herdsman-converter seems that the door status is not reported.
The most similar device doc that I have found it's the 'curtain switch'. Maybe the status report will be the same:

@MattWestb
Copy link
Contributor

Its not so easy saying than the device is working like one switch (its parallel with the normal installed) and can only detecting the door is closed or not closed.
Bets is getting all DPs its using by having it pared in ZHA and taking the power away and reconnecting it and it shall sending al DPs is using (its still tuya).

@javicalle
Copy link
Collaborator

In case someone wants to try it, it seems that it has already been included as an on/off switch:

@juanjoSanz
Copy link

juanjoSanz commented Feb 13, 2022

There is a similar device: _TZE200_nklqjk62 "MOES Garage opener" ( supported by zigbee2mqtt)
difference is that door sensor is wired instead of using RF.

Have anyone tried the changed pointed by @javicalle ?

Update: I tried changing that file locally adding my device id '_TZE200_nklqjk62' but it does not work.

@javicalle
Copy link
Collaborator

Without logs from device operation, nothing can be done.

Enable logs and attach the relevant information:

  zigpy: debug
  zigpy.zcl: debug
  custom_zha_quirks: debug
  zhaquirks.tuya: debug
  homeassistant.components.zha: debug

@juanjoSanz
Copy link

juanjoSanz commented Feb 14, 2022

Zigbee device signature might be useful:

{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, 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=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0051",
      "in_clusters": [
        "0x0000",
        "0x0004",
        "0x0005",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    },
    "242": {
      "profile_id": 41440,
      "device_type": "0x0061",
      "in_clusters": [],
      "out_clusters": [
        "0x0021"
      ]
    }
  },
  "manufacturer": "_TZE200_nklqjk62",
  "model": "TS0601",
  "class": "zigpy.device.Device"
}

@MattWestb
Copy link
Contributor

@juanjoSanz Your device is having one GPP end point so you need making one new device class and adding the GPP endpoint and your device IDs.

@juanjoSanz
Copy link

@juanjoSanz Your device is having one GPP end point so you need making one new device class and adding the GPP endpoint and your device IDs.

Thanks for the advice, I have been trying to define my own class:
https://raw.githubusercontent.com/juanjoSanz/zha-device-handlers/dev/zhaquirks/tuya/ts0601_switch_door.py

With my class the switch control is exposed, I have to figure out how to get the door sensor too.

@cfnascimento
Copy link

Hi!
What should I do to make it work in Zigbee2MQTT?

@cfnascimento
Copy link

It was interviewed and recognized (unsupported), but doesn't exposes none of it's entities...

Smart Home ZigBee Garage Door Opener (GDC311ZBQ1)
por Loratap

Zigbee2MQTT:debug 2022-02-14 23:43:07: Received Zigbee message from 'Garage door', type 'attributeReport', cluster 'genBasic', data '{"65506":48,"65508":0,"appVersion":68}' from endpoint 1 with groupID 0
Zigbee2MQTT:warn 2022-02-14 23:43:07: Received message from unsupported device with Zigbee model 'TS0601' and manufacturer name '_TZE200_wfxuhoea'
Zigbee2MQTT:warn 2022-02-14 23:43:07: Please see: https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html

Tks...

@MattWestb
Copy link
Contributor

@juanjoSanz can you taking the power off and putting it back so the device is restarting ?
If its being normal its sending the DP that is using and wee needing for fixing the missing parts.
Have some extra logging for ZHA is needed as @javicalle was writing.

@javicalle
Copy link
Collaborator

I think that the appropriate thing would be to create a new file for cover type entities (maybe ts0601_cover.py) and implement this class inside (better than in a switch class).
Currently there are already some implementations of Tuya covers, but I can try to create an implementation with the new approach and create the attributes that can be extracted from the logs.

This device is supposed to have 3 DP (garage trigger, door contact and door status):
https://github.com/Koenkk/zigbee-herdsman-converters/blob/c9e6a47f0a7331a5e0548e9df0f83ccd1d7ce130/converters/fromZigbee.js#L7773-L7781

'Door status' seems to be not reliable, but maybe can get some information from logs.

@juanjoSanz
Copy link

If that helps, here are additional logs.

[0x0000:zdo] ZDO request ZDOCmd.Mgmt_Permit_Joining_rsp: [<Status.SUCCESS: 0>]
[0x0000:zdo] No handler for ZDO request:ZDOCmd.Mgmt_Permit_Joining_rsp([<Status.SUCCESS: 0>])
[0x5a61:zdo] ZDO request ZDOCmd.Mgmt_Permit_Joining_rsp: [<Status.SUCCESS: 0>]
[0x5a61:zdo] No handler for ZDO request:ZDOCmd.Mgmt_Permit_Joining_rsp([<Status.SUCCESS: 0>])
New device 0xff96 (a4:c1:38:3b:09:ba:bb:13) joined the network
[0xff96] Scheduling initialization
Received frame on uninitialized device <Device model=None manuf=None nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=False> from ep 0 to ep 0, cluster 19: b'\x00\x96\xff\x13\xbb\xba\t;8\xc1\xa4\x8e'
[0xff96:zdo] ZDO request ZDOCmd.Device_annce: [0xFF96, a4:c1:38:3b:09:ba:bb:13, 142]
Tries remaining: 3
[0xff96] Requesting 'Node Descriptor'
Tries remaining: 2
[0xff96] Extending timeout for 0x1b request
Received frame on uninitialized device <Device model=None manuf=None nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=False> from ep 0 to ep 0, cluster 32770: b'\x1b\x00\x96\xff\x01@\x8eA\x11BB\x00\x00*B\x00\x00'
[0xff96] Got Node Descriptor: NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 142>, 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=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)
[0xff96] Discovering endpoints
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=False> from ep 0 to ep 0, cluster 32773: b'\x1c\x00\x96\xff\x02\x01\xf2'
[0xff96] Discovered endpoints: [1, 242]
[0xff96] Initializing endpoints [<Endpoint id=1 in=[] out=[] status=<Status.NEW: 0>>, <Endpoint id=242 in=[] out=[] status=<Status.NEW: 0>>]
[0xff96:1] Discovering endpoint information
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=False> from ep 0 to ep 0, cluster 32772: b'\x1d\x00\x96\xff\x14\x01\x04\x01Q\x00\x01\x04\x04\x00\x05\x00\x00\xef\x00\x00\x02\x19\x00\n\x00'
[0xff96:1] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=81, device_version=1, input_clusters=[4, 5, 61184, 0], output_clusters=[25, 10])
Unknown cluster 61184
[0xff96:242] Discovering endpoint information
Tries remaining: 3
Received frame on uninitialized device <Device model=None manuf=None nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=False> from ep 0 to ep 0, cluster 32772: b'\x1e\x00\x96\xff\n\xf2\xe0\xa1a\x00\x00\x00\x01!\x00'
[0xff96:242] Discovered endpoint information: SizePrefixedSimpleDescriptor(endpoint=242, profile=41440, device_type=97, device_version=0, input_clusters=[], output_clusters=[33])
[0xff96: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=31 command_id=Command.Read_Attributes_rsp>
[0xff96] Read model 'TS0601' and manufacturer '_TZE200_nklqjk62' from <Endpoint id=1 in=[groups:0x0004, scenes:0x0005, None:0xEF00, basic:0x0000] out=[ota:0x0019, time:0x000A] status=<Status.ZDO_INIT: 1>>
[0xff96] Discovered basic device information for <Device model='TS0601' manuf='_TZE200_nklqjk62' nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=True>
Device is initialized <Device model='TS0601' manuf='_TZE200_nklqjk62' nwk=0xFF96 ieee=a4:c1:38:3b:09:ba:bb:13 is_initialized=True>
Checking quirks for _TZE200_nklqjk62 TS0601 (a4:c1:38:3b:09:ba:bb:13)
Considering <class 'zhaquirks.tuya.ts0601_switch_door.TuyaGarageSwitchTO'>
Found custom device replacement for a4:c1:38:3b:09:ba:bb:13: <class 'zhaquirks.tuya.ts0601_switch_door.TuyaGarageSwitchTO'>
'sensor' component -> 'RSSISensor' using ['basic']
'sensor' component -> 'LQISensor' using ['basic']
device - 0xFF96:a4:c1:38:3b:09:ba:bb:13 entering async_device_initialized - is_new_join: True
device - 0xFF96:a4:c1:38:3b:09:ba:bb:13 has joined the ZHA zigbee network
[0xFF96](TS0601): started configuration
[0xFF96:ZDO](TS0601): 'async_configure' stage succeeded
[0xFF96:1:0x0000]: finished channel configuration
[0xFF96:1:0x0019]: finished channel configuration
Error handling '_save_attribute' event with (a4:c1:38:3b:09:ba:bb:13, 1, 0, 4, '_TZE200_nklqjk62') params: FOREIGN KEY constraint failed
Error handling '_save_attribute' event with (a4:c1:38:3b:09:ba:bb:13, 1, 0, 5, 'TS0601') params: FOREIGN KEY constraint failed
[0xFF96:1:0x0006]: bound 'on_off' cluster: Status.INVALID_EP
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=63 command_id=17>
[0xff96:1:0xef00] ZCL request 0x0011: [MCUVersionRsp(tsn=1280, version=64)]
[0xFF96:1:0x0006]: failed to set reporting on 'on_off' cluster for: 
[0xFF96:1:0x0006]: finished channel configuration
[0xFF96:1:0x0006]: 'async_configure' stage succeeded
[0xFF96:1:0x0000]: 'async_configure' stage succeeded
[0xFF96:1:0x0019]: 'async_configure' stage succeeded
[0xFF96](TS0601): completed configuration
[0xFF96](TS0601): stored in registry: ZhaDeviceEntry(name='_TZE200_nklqjk62 TS0601', ieee='a4:c1:38:3b:09:ba:bb:13', last_seen=1645027548.3382998)
[0xFF96](TS0601): started initialization
[0xFF96:ZDO](TS0601): 'async_initialize' stage succeeded
[0xFF96:1:0x0006]: initializing channel: from_cache: False
[0xFF96:1:0x0000]: initializing channel: from_cache: False
[0xFF96:1:0x0000]: finished channel initialization
[0xFF96:1:0x0019]: initializing channel: from_cache: False
[0xFF96:1:0x0019]: finished channel initialization
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=63 command_id=17>
[0xff96:1:0xef00] ZCL request 0x0011: [MCUVersionRsp(tsn=1280, version=64)]
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=64 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=90, command_id=1036, function=0, data=[1, 2])]
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=65 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=91, command_id=516, function=0, data=[4, 0, 0, 0, 10])]
[0xF34C](TRADFRI bulb E27 W opal 1000lm): Attempting to checkin with device - missed checkins: 1
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=66 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=92, command_id=514, function=0, data=[4, 0, 0, 0, 0])]
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=67 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=93, command_id=517, function=0, data=[4, 0, 0, 14, 16])]
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=68 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=94, command_id=267, function=0, data=[1, 0])]
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=69 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=95, command_id=259, function=0, data=[1, 1])]
[0xff96:1:0x000a] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=False disable_default_response=False> manufacturer=None tsn=70 command_id=Command.Read_Attributes>
[0xff96:1:0x000a] ZCL request 0x0000: [[7]]
[0xE6B9](TS0601): Attempting to checkin with device - missed checkins: 1
[0xFF96:1:0x0006]: failed to get attributes '['on_off']' on 'on_off' cluster: 
[0xFF96:1:0x0006]: async_initialize: retryable request #1 failed: . Retrying in 1.1s
[0xFF96:1:0x0006]: initializing channel: from_cache: False
[0x5A61:1:0x0b04]: async_update
[0x3904:1:0x0b04]: async_update
[0xff96:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=71 command_id=Command.Report_Attributes>
[0xff96:1:0x0000] ZCL request 0x000a: [[Attribute(attrid=1, value=<TypeValue type=uint8_t, value=70>), Attribute(attrid=65506, value=<TypeValue type=uint8_t, value=54>), Attribute(attrid=65508, value=<TypeValue type=uint8_t, value=1>)]]
[0xff96:1:0x0000] Attribute report received: app_version=70, 65506=54, 65508=1
[0xff96:1:0x0000] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=GLOBAL_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=71 command_id=Command.Report_Attributes>
[0xff96:1:0x0000] ZCL request 0x000a: [[Attribute(attrid=1, value=<TypeValue type=uint8_t, value=70>), Attribute(attrid=65506, value=<TypeValue type=uint8_t, value=54>), Attribute(attrid=65508, value=<TypeValue type=uint8_t, value=1>)]]
[0xff96:1:0x0000] Attribute report received: app_version=70, 65506=54, 65508=1
Duplicate 71 TSN
[0xFF96:1:0x0006]: failed to get attributes '['on_off']' on 'on_off' cluster: 
[0xFF96:1:0x0006]: async_initialize: retryable request #2 failed: . Retrying in 0.8s
[0xFF96:1:0x0006]: initializing channel: from_cache: False
[0xff96:1:0xef00] ZCL deserialize: <ZCLHeader frame_control=<FrameControl frame_type=CLUSTER_COMMAND manufacturer_specific=False is_reply=True disable_default_response=False> manufacturer=None tsn=72 command_id=2>
[0xff96:1:0xef00] ZCL request 0x0002: [Command(status=0, tsn=97, command_id=259, function=0, data=[1, 1])]

@sigalou
Copy link

sigalou commented Mar 26, 2022

Good morning all,
I also have this GDC311ZBQ1 module? Were you finally able to integrate it? Do we have any leads or is it dead?
Thank you

@EagleAdam
Copy link

So I saw that Zigbee2MQTT now seems to support this device. What's needed to get ZHA to support it?

Koenkk/zigbee2mqtt#11570

@BenjaminOe
Copy link

Same problem here. The device is recognized with ZHA but no entities are available. Would be very happy if there is any solution for it. 😄

@jeison-souza
Copy link

jeison-souza commented May 27, 2022

So I saw that Zigbee2MQTT now seems to support this device. What's needed to get ZHA to support it?

Koenkk/zigbee2mqtt#11570

Yeap, I saw this too, and works great, I tested it in a dev network with zigbee2mqtt and HA via mqtt. But, my real network is under ZHA integration and I'm not want this much to recreate the entire network with zb2mqtt. I tried create a qirk, but it didn't work at all.

For example, in debug of ZHA I found that the door sensor sends some commands, some bytes changes and I presume that is because it means the timestamp of the command or something like that. For the closing action the last 4 bytes are always the same, for the opening action the last 5 are the same. And always the secondish byte changes.
For the closing action i can see 15 times one command then a single time other command. For the opening, it's just a 15 times the same command. Very weird indeed.

I don't know how to help, I tried to modify the @juanjoSanz file. But didn't worked very well, I had a on_off button, but it only toggles when I press the device button and when I try to switch the button on HA, I get an error saying something command error/unknown

`Closing

Test #1
15x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00\x95\x03\x01\x00\x01\x00'
1x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00\x96\x0c\x04\x00\x01\x02'

Test #2
15x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00\x99\x03\x01\x00\x01\x00'
1x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00\x9a\x0c\x04\x00\x01\x02'

Test #3
15x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x002\x03\x01\x00\x01\x00'
1x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x003\x0c\x04\x00\x01\x02'

Opening

Test #1
15x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00(\x03\x01\x00\x01\x01'

Test #2
15x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00\xda\x03\x01\x00\x01\x01'

Test #3
15x WARNING (MainThread) [zigpy.zcl] [0x9075:1:0xef00] Unknown cluster command 2 b'\x00\x1d\x03\x01\x00\x01\x01'

`

@javicalle
Copy link
Collaborator

javicalle commented May 28, 2022

Hi, there is my proposal for _TZE200_nklqjk62 device and maybe others:

ts0601_garage.py
"""Tuya based cover and blinds."""
from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasZone

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import TuyaLocalCluster
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    TuyaDPType,
    TuyaMCUCluster,
    TuyaOnOff,
    TuyaOnOffNM,
)

ZONE_TYPE = 0x0001


class ContactSwitchCluster(TuyaLocalCluster, IasZone):
    """Tuya ContactSwitch Sensor."""

    _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}

    def _update_attribute(self, attrid, value):
        self.debug("_update_attribute '%s': %s", attrid, value)
        super()._update_attribute(attrid, value)


class TuyaGarageManufCluster(TuyaMCUCluster):
    """Tuya garage door opener."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # ramdom attribute IDs
            0xEF02: ("dp_2", t.uint32_t, True),
            0xEF04: ("dp_4", t.uint32_t, True),
            0xEF05: ("dp_5", t.uint32_t, True),
            0xEF0B: ("dp_11", t.Bool, True),
            0xEF0C: ("dp_12", t.enum8, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        # garage door trigger ¿on movement, on open, on closed?
        1: DPToAttributeMapping(
            TuyaOnOffNM.ep_attribute,
            "on_off",
            dp_type=TuyaDPType.BOOL,
        ),
        2: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_2",
            dp_type=TuyaDPType.VALUE,
        ),
        3: DPToAttributeMapping(
            ContactSwitchCluster.ep_attribute,
            "zone_status",
            dp_type=TuyaDPType.BOOL,
            converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
        ),
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_4",
            dp_type=TuyaDPType.VALUE,
        ),
        5: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_5",
            dp_type=TuyaDPType.VALUE,
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_11",
            dp_type=TuyaDPType.BOOL,
        ),
        # garage door status (open, closed, ...)
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_12",
            dp_type=TuyaDPType.ENUM,
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        3: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }


class TuyaGarageSwitchTO(CustomDevice):
    """Tuya Garage switch."""

    signature = {
        MODELS_INFO: [
            ("_TZE200_nklqjk62", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # input_clusters=[]
            # output_clusters=[33]
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                    TuyaOnOffNM,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                INPUT_CLUSTERS: [
                    ContactSwitchCluster
                ],
                OUTPUT_CLUSTERS: [],
            },
            242: {
                PROFILE_ID: 0xA1E0,
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [0x0021],
            },
        },
    }

Implementation must give a switch for open/close (already implemented and working)
Added the contact sensor (DP3). It may not work, but the traces can provide information on its use.
And all the other reported DPs as TuyaGarageManufCluster attributes:

  • dp_2 (numeric value: 0) unknown function
  • dp_4 (numeric value: 10) unknown function
  • dp_5 (numeric value: ¿4110?) unknown function
  • dp_11 (boolean: 0) unknown function
  • dp_12 (enum: 2) garage door status (open, closed, ...)

For anyone who wants to test on another device, add your signature to the MODELS_INFO:

        MODELS_INFO: [
            ("_TZE200_nklqjk62", "TS0601"),
        ],

@javicalle
Copy link
Collaborator

javicalle commented May 28, 2022

Probably a WindowCovering device would be more appropriate, but not sure how cluster status is updated. I suppose that device doesn't have any 'door position', and WindowCovering seems that only have current_position but not a 'window status'.

I have a question? This door can be stopped alfway? If yes, how is it? It seems that only accepts open and close buttons (no stop button).

@jeison-souza
Copy link

jeison-souza commented May 28, 2022

Probably a WindowCovering device would be more appropriate, but not sure how cluster status is updated. I suppose that device doesn't have any 'door position', and WindowCovering seems that only have current_position but not a 'window status'.

I have a question? This door can be stopped halfway? If yes, how is it? It seems that only accepts open and close buttons (no stop button).

In reality, physically the device it only has a "Action button" momentary switch, that activate a relay for 2 seconds then turn it off. And also a wireless contact switch for door status detection. (Closed or not closed, but with no way to determine the position)

The garage door itself, start a movement if it isn't moving ou stops if it's moving. If you press it again, it inverts the direction, but I guess it will change from depending on the garage motor vendor.

Looking at the zb2mqtt converter code, we can see this:

           switch (dpValue.dp) {
           case tuya.dataPoints.garageDoorTrigger: // dp 1
               result.action = 'trigger';
               break;
           case tuya.dataPoints.garageDoorContact: // dp 3
               result.garage_door_contact = Boolean(!value);
               break;
           case tuya.dataPoints.garageDoorStatus: // dp 12
               // This reports a garage door status (open, closed), but it is very naive and misleading
               break;
           default:
               meta.logger.debug("zigbee-herdsman-converters:matsee_garage_door_opener: NOT RECOGNIZED " +
                   "DP #${dpValue.dp} with data ${JSON.stringify(dpValue)}");
           }

They don't use the dp 12 for statut as you can see above, they use the 3 (wireless contact switch).

My device model is a little bit different than yours, but I'll give your code a shoot then I'll let you know the results.
"_TZE200_wfxuhoea", "TS0601"

Thanks a lot. !

@jeison-souza
Copy link

Hi @javicalle , unfortunately it didn't work, on the on_off dp_1, I have a "toggle" when I press the button, but when I toggle in HA, nothing is sent to the device, no relay click sound.
The sensor doesn't work as a IAS_Zone, always shows off. But if I put it as TuyaOnOff cluster, it reports the state of the sensor, but in form of a switch. Not ideal, but it is something already.

I'll try to change some bits in your code to see if I can have the command sent to the device at least.

@javicalle
Copy link
Collaborator

javicalle commented May 29, 2022

So, from your comments (thanks for that) it would be more appropiate represent in HA as a button, isn't it? I am going to see how a button can be defined in Zigpy.
The dp_1 was not already working? or am I confused? The logs from the HA interaction could give to us some clues about whats happening here.

Can you try with another iteration for the sensor? Just add the converter as:

        3: DPToAttributeMapping(
            ContactSwitchCluster.ep_attribute,
            "zone_status",
            dp_type=TuyaDPType.BOOL,
            endpoint_id=2,
            converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
        ),

Can you also check if (with the IasZone cluster) the zone_status is updating its value?

EDIT: the DP converter is needed: #1502 (comment)

@javicalle
Copy link
Collaborator

Another random test that can be done is to replace the TuyaOnOff cluster:

  1. add the TuyaOnOffNM import:
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    TuyaDPType,
    TuyaMCUCluster,
    TuyaOnOff,
    TuyaOnOffNM,
)
  1. replace the class (in the replacement part):
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                    TuyaOnOffNM,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },

@jeison-souza
Copy link

I'll try that @javicalle , I'll keep you informed. Thanks

@DonMaverick
Copy link

Found it :-) ... YES... Party on....
My Mistake was the missing link from the "configuration.yaml " to the py.
i now add:
default_config:
zha:
custom_quirks_path: /config/zhaquirks

Many many Thanks :-)

@DonMaverick
Copy link

The switch i working....
But i dont receive an signal of the "Sensor" Open/close.
Should i press some buttons on the device to "link" them?

@philo686
Copy link

Hi,
@hallenmaia , thank you for the quirk. Since month I´m trying different quirks and non of them worked without any issues. But now it does!
@DonMaverick As far as I remamber, the open/close sensor doesn´t work. If you can make it work, please let me know.

Thank you!

@hallenmaia
Copy link

@philo686 😁

With the quirk above, the open/closed sensor works on the "_TZE200_nklqjk62" model. I have no way to confirm this in the "_TZE200_wfxuhoea" model. In some comment above it was said that maybe this model is a little different 😔.

image

@bsfaxi
Copy link

bsfaxi commented Sep 15, 2023

It works for me:

image

@philo686
Copy link

philo686 commented Sep 15, 2023

@hallenmaia Indeed I have the model "_TZE200_wfxuhoea" and couldn´t make the open/close sensor work :(
@bsfaxi that´s interesting. How could you manage that it works? Could you share you quirk?

I opened the door several times, but won´t be recognized by the sensor.
image
image

@bsfaxi
Copy link

bsfaxi commented Sep 15, 2023

"""Tuya based cover and blinds."""
from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasZone

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

from zhaquirks.tuya import NoManufacturerCluster, TuyaLocalCluster
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    TuyaMCUCluster,
    TuyaOnOff,
)
from zhaquirks.tuya.ts0601_dimmer import TuyaOnOffNM

ZONE_TYPE = 0x0001

class ContactSwitchCluster(TuyaLocalCluster, IasZone):
    """Tuya ContactSwitch Sensor."""

    _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}

    def _update_attribute(self, attrid, value):
        self.debug("_update_attribute '%s': %s", attrid, value)
        super()._update_attribute(attrid, value)


class TuyaGarageManufCluster(NoManufacturerCluster, TuyaMCUCluster):
    """Tuya garage door opener."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # ramdom attribute IDs
            0xEF02: ("dp_2", t.uint32_t, True),
            0xEF04: ("dp_4", t.uint32_t, True),
            0xEF05: ("dp_5", t.uint32_t, True),
            0xEF0B: ("dp_11", t.Bool, True),
            0xEF0C: ("dp_12", t.enum8, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        # garage door trigger ¿on movement, on open, on closed?
        1: DPToAttributeMapping(
            TuyaOnOffNM.ep_attribute,
            "on_off",
        ),
        2: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_2",
        ),
        3: DPToAttributeMapping(
            ContactSwitchCluster.ep_attribute,
            "zone_status",
            lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
            endpoint_id=2,
        ),
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_4",
        ),
        5: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_5",
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_11",
        ),
        # garage door status (open, closed, ...)
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_12",
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        3: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }


class TuyaGarageSwitchTO(CustomDevice):
    """Tuya Garage switch."""

    signature = {
        MODELS_INFO: [
            ("_TZE200_nklqjk62", "TS0601"),
            ("_TZE200_wfxuhoea", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # input_clusters=[]
            # output_clusters=[33]
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                    TuyaOnOffNM,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                INPUT_CLUSTERS: [
                    ContactSwitchCluster
                ],
                OUTPUT_CLUSTERS: [],
            },
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

@bsfaxi
Copy link

bsfaxi commented Sep 15, 2023

Directly from the vendor. ;)

TS0601.GDC311ZBQ1.Tuya.Loratap.Garage.Switcher.mp4

@philo686
Copy link

@bsfaxi Thank you for the video, tried to pair the open/close sensor to the main unit according to the video, but unfortunately it doesn´t work. I just recognized that there is also a dip-switch in the sensor. It is set to pos. 2.

However, I´ve already installed another zigbee open/close sensor month ago and since today I can open and close the garage, this made already my day. ;)

Thank you for your effort!

@DonMaverick
Copy link

Many thanks to all .... :-)

@balu79
Copy link

balu79 commented Sep 15, 2023

Directly from the vendor. ;)

TS0601.GDC311ZBQ1.Tuya.Loratap.Garage.Switcher.mp4

But is that really to pair the sensor to the relay? I thought that the sensor and the relay / main unit are pre-paired from the vendor and that this button is just to pair the relay / main unit to the bridge (or Zigbee USB dongle). Fyi, I can also confirm that the sensor works for my "_TZE200_wfxuhoea" model.

@DonMaverick
Copy link

DonMaverick commented Sep 15, 2023

Bildschirmfoto vom 2023-09-15 17-56-30
Bildschirmfoto vom 2023-09-15 17-56-41

Hi, @hallenmaia , thank you for the quirk. Since month I´m trying different quirks and non of them worked without any issues. But now it does! @DonMaverick As far as I remamber, the open/close sensor doesn´t work. If you can make it work, please let me know.

Thank you!

I have only followed the instructions in the video.
Hold for several seconds until the blue light glances (hard to see)
Then open the contact once and close it again... ready

@DonMaverick
Copy link

But I also have another question.
Has anyone of you built a template to use it also like a garage opener? should then also be transferred to Homekit.

@bsfaxi
Copy link

bsfaxi commented Sep 15, 2023

I have the following cover template:
The "stop cover" depends on your garage engine. Mine stops only when I send a switch command in opening mode. In closing mode, the switch command will open the garage...

# Cover template for Garage
cover:
  - platform: template
    covers:
      garage_door:
        device_class: garage
        friendly_name: "Porte du garage"
        value_template: >-
          {% if is_state('binary_sensor.porte_du_garage_opening','off') %}closed
          {% else %}open
          {% endif %}
        open_cover:
          - condition: state
            entity_id: binary_sensor.porte_du_garage_opening
            state: "off"
          - service: light.turn_on
            target:
              entity_id: light.porte_du_garage_light
        close_cover:
          - condition: state
            entity_id: binary_sensor.porte_du_garage_opening
            state: "on"
          - service: light.turn_off
            target:
              entity_id: light.porte_du_garage_light
#        stop_cover:
#          service: light.turn_on
#          target:
#            entity_id: light.porte_du_garage_light
        icon_template: >-
          {% if states('binary_sensor.porte_du_garage_opening') == "on" %}
            mdi:garage-open-variant
          {% else %}
            mdi:garage-variant
          {% endif %}

And the following automation to reset the switch after each execution:

- id: 'xxxxxxxxxx'
  alias: Covers - Porte du garage switch reset
  description: Remise à inactif du bouton
  trigger:
  - platform: state
    entity_id:
    - light.porte_du_garage_light
    to: 'on'
    for:
      hours: 0
      minutes: 0
      seconds: 2
  condition: []
  action:
  - service: light.turn_off
    data: {}
    target:
      entity_id: light.porte_du_garage_light

@DonMaverick
Copy link

Thanks a lot... but why it looks like a cover, and not like a garage opener?
Bildschirmfoto vom 2023-09-15 19-02-08
Bildschirmfoto vom 2023-09-15 19-02-33

@ji76
Copy link

ji76 commented Sep 19, 2023

Je l'ai trouvé :-) ... OUI... Faites la fête... Mon erreur était le chaînon manquant de la "configuration.yaml" au py.J'ajoute maintenant :default_config :zha :custom_quirks_path : /config/zhaquirks------------- > > Merci beaucoup :-) —

Bonjour je n'arrive pas : j'ai cette erreur =
Erreur d'intégration : custom_quirks_path - Intégration 'custom_quirks_path' introuvable.

qui peut m'aider ?

Merci

@balu79
Copy link

balu79 commented Sep 21, 2023

Somehow I'm observing strange behaviors for my _TZE200_wfxuhoea" model. I guess I need to look into the logs, increase the log level and try to trouble shoot this from my end once I find some time, but wanted to know who else observed these and under which conditions.

First, I now also see status problems with the sensor. When I open my garage with the standard garage switch, the status from the sensor seems to work fine. But when I trigger the Loratap relay from Home Assistant that status sensor often seems to be getting out of sync. It's not a big problem, in worst case I could fix this by installing another zigbee door sensor from another vendor like @philo686 did.

Secondly, I observed a really worrying behavior. Something seems to trigger the opening (maybe also closing, not sure yet) of the garage from time to time, leaving me with an open garage when I didn't trigger it through home assistant (at least not knowingly). I thought I had seen at least one user reporting such a behavior either in this or another forum, unfortunately I couldn't find that anymore when searching. So if anyone ever observed this, please let me know. Otherwise I'll probably start looking into logs in more detail. In the logbook I could only see "Relay Garage Light turned on" so far.

@balu79
Copy link

balu79 commented Oct 6, 2023

I somehow had a problem with the quirks which expose the device as a light, either since of some light set-up / configuration at my end or since of my automation to reset the status of the light on/off control or some other reason. Whenever I triggered the relay from within home assistant, somehow it got triggered once more at a later point (like 30 to 120 minutes later) leaving me with an open garage even though I had closed it earlier. After changing to the quirk from @hallenmaia which exposes the relay as a plug / switch (and disabling my switch reset automation) I don't have this problem anymore. Am wondering whether anybody could see from the following logs what could have triggered that unintentional triggering of the relay. I couldn't figure it out unfortunately.

2023-10-04 20:49:47.061 DEBUG (MainThread) [homeassistant.components.zha.entity] light.relay_garage: polling current state
2023-10-04 20:49:47.068 DEBUG (MainThread) [homeassistant.components.zha.entity] light.relay_garage: setting transitioning flag to True
...
2023-10-04 20:49:47.074 DEBUG (MainThread) [homeassistant.components.zha.entity] light.relay_garage: received onoff False while transitioning - skipping update
2023-10-04 20:49:47.074 DEBUG (MainThread) [homeassistant.components.zha.entity] light.relay_garage: starting transitioning timer for 1.25
2023-10-04 20:49:47.074 DEBUG (MainThread) [homeassistant.components.zha.entity] light.relay_garage: turned off: Default_Response(command_id=0, status=<Status.SUCCESS: 0>)

@hallenmaia
Copy link

@balu79 Do you have any integration that controls the brightness or colors of the lamps? (Adaptive Lighting, Circadian Lighting, ...).

Maybe they might be interfering.

@balu79
Copy link

balu79 commented Oct 6, 2023

Yeah, I was wondering the exact same. I don't have Adaptive Lighting, Circadian Lighting, I though have a light_profiles.csv configuration for setting a default level but in that config I have only defined other lights. Other than that I have the following light group defined in my configuration.yaml file:

light:
  - platform: group
    name: "Lights Sleep"
    entities:
      - light.sleep_u
      - light.sleep_d

@xxrb86
Copy link

xxrb86 commented Nov 18, 2023

Figured it out for anyone playing along at home :)

If you use HassIO the folder goes in the 'config' folder which is of course the 'root' when you use the 'File Editor' inside the instance

i.e. create the 'zhaquirks' folder in the same folder you find your configuration YAML....


hi everyone,

I'm losing the plot trying to get this to work :(

I cant think of what else to try?

I can attach the device, but couldn't see the button to control the door opener - so I found this thread, added the config comment, then added the folder and inside created a new file using the inbuilt file editor, and pasted in the code

then i edited the section towards the end with the TZE204 but still no joy :(

but despite deleting it and re-adding, and rebooting the HA instance multiple times - it never presents the button

what am I missing?! :)

device is listed as

TS0601
by _TZE204_nklqjk62

"""Tuya based cover and blinds."""
from typing import Dict

from zigpy.profiles import zgp, zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import NoManufacturerCluster
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaMCUCluster

TUYA_MANUFACTURER_GARAGE = "tuya_manufacturer_garage"


class TuyaGarageManufCluster(NoManufacturerCluster, TuyaMCUCluster):
    """Tuya garage door opener."""

    ep_attribute = TUYA_MANUFACTURER_GARAGE

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # ramdom attribute IDs
            0xEF01: ("button", t.Bool, True),
            0xEF02: ("dp_2", t.uint32_t, True),
            0xEF03: ("contact_sensor", t.Bool, True),
            0xEF04: ("dp_4", t.uint32_t, True),
            0xEF05: ("dp_5", t.uint32_t, True),
            0xEF0B: ("dp_11", t.Bool, True),
            0xEF0C: ("dp_12", t.enum8, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        # garage door trigger ¿on movement, on open, on closed?
        1: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "button",
        ),
        2: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "dp_2",
        ),
        3: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "contact_sensor",
        ),
        4: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "dp_4",
        ),
        5: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "dp_5",
        ),
        11: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "dp_11",
        ),
        # garage door status (open, closed, ...)
        12: DPToAttributeMapping(
            TUYA_MANUFACTURER_GARAGE,
            "dp_12",
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        3: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }


class TuyaGarageSwitchTO(CustomDevice):
    """Tuya Garage switch."""

    signature = {
        MODELS_INFO: [
            ("_TZE204_nklqjk62", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # input_clusters=[]
            # output_clusters=[33]
            242: {
                PROFILE_ID: zgp.PROFILE_ID,
                DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            242: {
                PROFILE_ID: zgp.PROFILE_ID,
                DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

@jairbj
Copy link

jairbj commented Jan 30, 2024

This quirk is working with my device:

TS0601
by _TZE200_nklqjk62

image

I have the model "TZE200_nklqjk62" running for more than 1 year with this quirk.

I don't know if it works with "wfxuhoea".

The original quirk treated the device as "light" and this caused the activation key to appear as "light" in the HA. I changed it to "plug" to appear as a switch.

As I said earlier, the button depends on a PR that has been stopped for a long time.

"""Tuya based cover and blinds."""
from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasZone

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

from zhaquirks.tuya import NoManufacturerCluster, TuyaLocalCluster
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    TuyaMCUCluster,
    TuyaOnOff,
    TuyaOnOffNM,
)

ZONE_TYPE = 0x0001


class ContactSwitchCluster(TuyaLocalCluster, IasZone):
    """Tuya ContactSwitch Sensor."""
    _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}


class TuyaGarageManufCluster(NoManufacturerCluster, TuyaMCUCluster):
    """Tuya garage door opener."""
    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # ramdom attribute IDs
            0xEF02: ("dp_2", t.uint32_t, True),
            0xEF04: ("dp_4", t.uint32_t, True),
            0xEF05: ("dp_5", t.uint32_t, True),
            0xEF0B: ("dp_11", t.Bool, True),
            0xEF0C: ("dp_12", t.enum8, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        # garage door trigger ¿on movement, on open, on closed?
        1: DPToAttributeMapping(
            TuyaOnOffNM.ep_attribute,
            "on_off"
        ),
        2: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_2"
        ),
        3: DPToAttributeMapping(
            ContactSwitchCluster.ep_attribute,
            "zone_status",
            #TuyaDPType.BOOL,
            lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
            endpoint_id=2,
        ),
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_4"
        ),
        5: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_5"
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_11"
        ),
        # garage door status (open, closed, ...)
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_12"
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        3: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }


class TuyaGarageSwitchTO(CustomDevice):
    """Tuya Garage switch."""

    signature = {
        MODELS_INFO: [
            ("_TZE200_nklqjk62", "TS0601"),
            ("_TZE200_wfxuhoea", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # input_clusters=[]
            # output_clusters=[33]
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                    TuyaOnOffNM,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                INPUT_CLUSTERS: [
                    ContactSwitchCluster
                ],
                OUTPUT_CLUSTERS: [],
            },
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

@chriexpe
Copy link

chriexpe commented Mar 20, 2024

Update: I deleted my zigbee dongle from ZHA and after adding it back it worked.

Did someone broke again this quirk? I did everything right, yet HA will always get one generic quirk that doesn't work instead of using one from local... And the worst part is: I had it working before reinstalling my Home Assistant (OS), tried that same quirk but had zero success.
image

And here is my garage door info
image

@lpaseen
Copy link

lpaseen commented Apr 18, 2024

Sorry, just a "me2" here. I looked at what seems to be needed but for a start - while my garage door device looks the same as TZE200_nklqjk62 above (white block) it seems to be a slightly different model - TZE204_nklqjk62, that's why I adding this comment in case some GA version of the code is released.
I'm a newbie and I'm running HAOS (in a vm) so I need to figure out how to add the quirks before I can do any testing.
image

@PsychedelicHell
Copy link

Hello friends,

If you're using the _TZE204_nklqjk62 model then use the following for your 'ts0601_garage.py' file:

"""Tuya based cover and blinds."""
from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasZone

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

from zhaquirks.tuya import TuyaLocalCluster, TuyaDPType
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaMCUCluster, TuyaOnOff
from zhaquirks.tuya.ts0601_dimmer import TuyaOnOffNM

ZONE_TYPE = 0x0001

class ContactSwitchCluster(TuyaLocalCluster, IasZone):
    """Tuya ContactSwitch Sensor."""

    _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}

    def _update_attribute(self, attrid, value):
        self.debug("_update_attribute '%s': %s", attrid, value)
        super()._update_attribute(attrid, value)


class TuyaGarageManufCluster(TuyaMCUCluster):
    """Tuya garage door opener."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # ramdom attribute IDs
            0xEF02: ("dp_2", t.uint32_t, True),
            0xEF04: ("dp_4", t.uint32_t, True),
            0xEF05: ("dp_5", t.uint32_t, True),
            0xEF0B: ("dp_11", t.Bool, True),
            0xEF0C: ("dp_12", t.enum8, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        # garage door trigger ¿on movement, on open, on closed?
        1: DPToAttributeMapping(
            TuyaOnOffNM.ep_attribute,
            "on_off",
            TuyaDPType.BOOL,
        ),
        2: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_2",
            TuyaDPType.VALUE,
        ),
        3: DPToAttributeMapping(
            ContactSwitchCluster.ep_attribute,
            "zone_status",
            #TuyaDPType.BOOL,
            lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
            endpoint_id=2,
        ),
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_4",
            TuyaDPType.VALUE,
        ),
        5: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_5",
            TuyaDPType.VALUE,
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_11",
            TuyaDPType.BOOL,
        ),
        # garage door status (open, closed, ...)
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_12",
            TuyaDPType.ENUM,
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        3: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }


class TuyaGarageSwitchTO(CustomDevice):
    """Tuya Garage switch."""

    signature = {
        MODELS_INFO: [
            ("_TZE204_nklqjk62", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # input_clusters=[]
            # output_clusters=[33]
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                    TuyaOnOffNM,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                INPUT_CLUSTERS: [
                    ContactSwitchCluster
                ],
                OUTPUT_CLUSTERS: [],
            },
            242: {
                PROFILE_ID: 0xA1E0,
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [0x0021],
            },
        },
    }

@noahcub
Copy link

noahcub commented Jul 14, 2024

Hello friends,

If you're using the _TZE204_nklqjk62 model then use the following for your 'ts0601_garage.py' file:

"""Tuya based cover and blinds."""
from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasZone

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

from zhaquirks.tuya import TuyaLocalCluster, TuyaDPType
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaMCUCluster, TuyaOnOff
from zhaquirks.tuya.ts0601_dimmer import TuyaOnOffNM

ZONE_TYPE = 0x0001

class ContactSwitchCluster(TuyaLocalCluster, IasZone):
    """Tuya ContactSwitch Sensor."""

    _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}

    def _update_attribute(self, attrid, value):
        self.debug("_update_attribute '%s': %s", attrid, value)
        super()._update_attribute(attrid, value)


class TuyaGarageManufCluster(TuyaMCUCluster):
    """Tuya garage door opener."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            # ramdom attribute IDs
            0xEF02: ("dp_2", t.uint32_t, True),
            0xEF04: ("dp_4", t.uint32_t, True),
            0xEF05: ("dp_5", t.uint32_t, True),
            0xEF0B: ("dp_11", t.Bool, True),
            0xEF0C: ("dp_12", t.enum8, True),
        }
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        # garage door trigger ¿on movement, on open, on closed?
        1: DPToAttributeMapping(
            TuyaOnOffNM.ep_attribute,
            "on_off",
            TuyaDPType.BOOL,
        ),
        2: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_2",
            TuyaDPType.VALUE,
        ),
        3: DPToAttributeMapping(
            ContactSwitchCluster.ep_attribute,
            "zone_status",
            #TuyaDPType.BOOL,
            lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
            endpoint_id=2,
        ),
        4: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_4",
            TuyaDPType.VALUE,
        ),
        5: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_5",
            TuyaDPType.VALUE,
        ),
        11: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_11",
            TuyaDPType.BOOL,
        ),
        # garage door status (open, closed, ...)
        12: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "dp_12",
            TuyaDPType.ENUM,
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        3: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        5: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        12: "_dp_2_attr_update",
    }


class TuyaGarageSwitchTO(CustomDevice):
    """Tuya Garage switch."""

    signature = {
        MODELS_INFO: [
            ("_TZE204_nklqjk62", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
            # input_clusters=[]
            # output_clusters=[33]
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaGarageManufCluster,
                    TuyaOnOffNM,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            2: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                INPUT_CLUSTERS: [
                    ContactSwitchCluster
                ],
                OUTPUT_CLUSTERS: [],
            },
            242: {
                PROFILE_ID: 0xA1E0,
                DEVICE_TYPE: 0x0061,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [0x0021],
            },
        },
    }

Works for me.
Thanks

@TiagoFerreiratf
Copy link

I tried everything I saw on the forums, but none of it worked. I can get the Switch control and the Sensor, but they don't work. The logs say that the garage door opens and closes, but nothing happens. My device is _TZE200_wfxuhoea TS0601. After all this time, there should already have been a direct integration in ZHA.

@duloup
Copy link

duloup commented Sep 25, 2024

I own one of this device to control the opening of my front gate. It is reported as _TZE204_nklqjk62 in HA. After the first connection, the device had no entities/controls so I installed the quirk provided in this comment (#1260 (comment)). After enabling the quirk, I was able to open and close the gate without problem for weeks until it failed.

After checking the log file, I saw a lot of _ Exception running handler_ errors in bellows/ezsp/init.py:

Exception running handler

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/bellows/ezsp/__init__.py", line 484, in handle_callback
    handler(*args)
  File "/usr/local/lib/python3.12/site-packages/bellows/zigbee/application.py", line 475, in ezsp_callback_handler
    self._handle_frame(
  File "/usr/local/lib/python3.12/site-packages/bellows/zigbee/application.py", line 558, in _handle_frame
    self.packet_received(
  File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 1025, in packet_received
    return device.packet_received(packet)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zigpy/device.py", line 497, in packet_received
    endpoint.handle_message(
  File "/usr/local/lib/python3.12/site-packages/zigpy/endpoint.py", line 247, in handle_message
    handler(hdr, args, dst_addressing=dst_addressing)
  File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 431, in handle_message
    self.handle_cluster_request(hdr, args, dst_addressing=dst_addressing)
  File "/usr/local/lib/python3.12/site-packages/zhaquirks/tuya/__init__.py", line 1576, in handle_cluster_request
    status = getattr(self, handler_name)(*args)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/zhaquirks/tuya/__init__.py", line 1594, in handle_get_data
    getattr(self, dp_handler)(record)
  File "/usr/local/lib/python3.12/site-packages/zhaquirks/tuya/__init__.py", line 1627, in _dp_2_attr_update
    value = dp_map.converter(value)
            ^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'TuyaDPType' object is not callable 

The error seems to occur at a scheduled frequency, each hour:

2024-09-22 01:00:00.323 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-22 02:00:00.552 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-22 03:00:00.327 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-22 04:00:00.412 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-22 05:00:00.416 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-22 05:59:59.530 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-22 06:59:59.173 ERROR (MainThread) [bellows.ezsp] Exception running handler

and sometimes even every 10 seconds:

2024-09-18 06:58:10.325 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-18 06:58:20.355 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-18 06:58:30.386 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-18 06:58:40.407 ERROR (MainThread) [bellows.ezsp] Exception running handler
2024-09-18 06:58:50.430 ERROR (MainThread) [bellows.ezsp] Exception running handler

If I shutdown the device, the error flow stops.

Does anyone with this quirk saw the same behaviour and has an idea to fix this?

FYI, I'm on the 2024.9.2 version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
custom quirk available A custom quirk is available to solve the issue, but it's not merged in the repo yet Tuya Request/PR regarding a Tuya device
Projects
None yet
Development

No branches or pull requests