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
685 stars 638 forks source link

[Device Support Request] Tuya Curtain Motor #2483

Closed AlterSu24 closed 5 months ago

AlterSu24 commented 11 months ago

Problem description

Can be paired successfully but showed nothing to control with. No entities or sensors were created.

Solution description

Full support of the device. Open/Close/Stop and controlling of open percentage.

Screenshots/Video

Screenshots/Video ![擷取](https://github.com/zigpy/zha-device-handlers/assets/128820170/ce4084fe-3bc7-46b6-a9a1-cdd1759e0c65) ### Device signature
Device signature ```json { "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=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": "0x0104", "device_type": "0x0051", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "_TZE200_xu4a5rhj", "model": "TS0601", "class": "zigpy.device.Device" } ```
### Diagnostic information
Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.6.3", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.11.4", "docker": true, "arch": "x86_64", "timezone": "Asia/Taipei", "os_name": "Linux", "os_version": "6.1.25", "supervisor": "2023.07.1", "host_os": "Home Assistant OS 10.1", "docker_version": "23.0.3", "chassis": "vm", "run_as_root": true }, "custom_components": { "watchman": { "version": "0.5.1", "requirements": [ "prettytable==3.0.0" ] }, "ui_lovelace_minimalist": { "version": "v1.3.6", "requirements": [ "aiofiles==0.8.0", "aiogithubapi>=22.2.4" ] }, "blueair": { "version": "1.0.0", "requirements": [] }, "localtuya": { "version": "5.2.1", "requirements": [] }, "opencwb": { "version": "1.0.1", "requirements": [ "geojson" ] }, "browser_mod": { "version": "2.2.2", "requirements": [] }, "hacs": { "version": "1.32.1", "requirements": [ "aiogithubapi>=22.10.1" ] }, "smartthinq_sensors": { "version": "0.31.8", "requirements": [ "pycountry>=20.7.3", "xmltodict>=0.12.0", "charset_normalizer>=2.0.0" ] }, "smartir": { "version": "1.17.7", "requirements": [ "aiofiles>=0.6.0" ] }, "dyson_local": { "version": "0.16.4-4", "requirements": [ "logan_libdyson==0.8.13" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "after_dependencies": [ "onboarding", "usb" ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly" ], "config_flow": true, "dependencies": [ "file_upload" ], "documentation": "https://www.home-assistant.io/integrations/zha", "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp" ], "requirements": [ "bellows==0.35.5", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.100", "zigpy-deconz==0.21.0", "zigpy==0.55.0", "zigpy-xbee==0.18.0", "zigpy-zigate==0.11.0", "zigpy-znp==0.11.1" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "1A86", "pid": "55D4", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "vid": "10C4", "pid": "EA60", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus" ] }, { "vid": "10C4", "pid": "EA60", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*zigstar*", "known_devices": [ "ZigStar Coordinators" ] }, { "vid": "1CF1", "pid": "0030", "description": "*conbee*", "known_devices": [ "Conbee II" ] }, { "vid": "10C4", "pid": "8A2A", "description": "*zigbee*", "known_devices": [ "Nortek HUSBZB-1" ] }, { "vid": "0403", "pid": "6015", "description": "*zigate*", "known_devices": [ "ZiGate+" ] }, { "vid": "10C4", "pid": "EA60", "description": "*zigate*", "known_devices": [ "ZiGate" ] }, { "vid": "10C4", "pid": "8B34", "description": "*bv 2010/10*", "known_devices": [ "Bitron Video AV2010/10" ] } ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 33571, "manufacturer": "_TZE200_xu4a5rhj", "model": "TS0601", "name": "_TZE200_xu4a5rhj TS0601", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4417, "power_source": "Mains", "lqi": 176, "rssi": -56, "last_seen": "2023-07-20T13:49:18", "available": true, "device_type": "Router", "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=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": "0x0104", "device_type": "0x0051", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "_TZE200_xu4a5rhj", "model": "TS0601" }, "active_coordinator": false, "entities": [], "neighbors": [ { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Parent", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xD455", "permit_joining": "Unknown", "depth": "1", "lqi": "69" }, { "device_type": "Coordinator", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x0000", "permit_joining": "Unknown", "depth": "0", "lqi": "115" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x120A", "permit_joining": "Unknown", "depth": "1", "lqi": "63" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xCA6E", "permit_joining": "Unknown", "depth": "1", "lqi": "12" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xCEAB", "permit_joining": "Unknown", "depth": "1", "lqi": "54" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xD8A8", "permit_joining": "Unknown", "depth": "1", "lqi": "60" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x4F95", "permit_joining": "Unknown", "depth": "1", "lqi": "21" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xFC9E", "permit_joining": "Unknown", "depth": "0", "lqi": "136" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0xB3E5", "permit_joining": "Unknown", "depth": "1", "lqi": "30" }, { "device_type": "Router", "rx_on_when_idle": "On", "relationship": "Sibling", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x6AE9", "permit_joining": "Unknown", "depth": "0", "lqi": "15" } ], "routes": [], "endpoint_names": [ { "name": "SMART_PLUG" }, { "name": "unknown 97 device_type of 0xa1e0 profile id" } ], "user_given_name": null, "device_reg_id": "e0fcc37e9eb73962db9ffa5b8d6a9d52", "area_id": null, "cluster_details": { "1": { "device_type": { "name": "SMART_PLUG", "id": 81 }, "profile_id": 260, "in_clusters": { "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} }, "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 70 }, "0x0004": { "attribute_name": "manufacturer", "value": "_TZE200_xu4a5rhj" }, "0x0005": { "attribute_name": "model", "value": "TS0601" } }, "unsupported_attributes": {} } }, "out_clusters": { "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} }, "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} } } }, "242": { "device_type": { "name": "unknown", "id": 97 }, "profile_id": 41440, "in_clusters": {}, "out_clusters": { "0x0021": { "endpoint_attribute": "green_power", "attributes": {}, "unsupported_attributes": {} } } } } } } ```
### Logs
Logs ```python [Paste the logs here] ```
### Custom quirk
Custom quirk ```python [Paste your custom quirk here] ```
### Additional information I bought this LS82 motors from taobao. And this motor seems to be the same or similar with DS82 motor which should be supported by ZHA. (https://zigbee.blakadder.com/Tuya_DS82.html) So maybe this can also be supported by ZHA? Any tips would be helpful, and I am willing to offer more detail informations. Please help, thanks.
besiktas97 commented 11 months ago

I have a zigbee blind motor _TS0601_TZE200_cpbo62rn, device can be added without issues, but cant see entities or controlling it. As signature from ZHA, I can see the following:

{ "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=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, 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": "0x0104", "device_type": "0x0051", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_cpbo62rn", "model": "TS0601", "class": "zigpy.device.Device"

javicalle commented 11 months ago

I have a zigbee blind motor _TS0601_TZE200_cpbo62rn, device can be added without issues, but cant see entities or controlling it.

I would suggest to add your device to the TuyaZemismartSmartCover0601_3 quirk:

Or maybe the inverted one:

AlterSu24 commented 11 months ago

@javicalle My zigbee cover motor is _TZE200_xu4a5rhj where should I add my device?

AlterSu24 commented 11 months ago

class TuyaZemismartSmartCover0601_2(TuyaWindowCover): """Tuya Zemismart curtain cover motor."""

signature = {
    # "node_descriptor": "<NodeDescriptor byte1=1 byte2=64 mac_capability_flags=142 manufacturer_code=4098
    #                       maximum_buffer_size=82 maximum_incoming_transfer_size=82 server_mask=11264
    #                       maximum_outgoing_transfer_size=82 descriptor_capability_field=0>",
    # input_clusters=[0x0000, 0x000a, 0x0004, 0x0005, 0xef00]
    # output_clusters=[0x0019]
    # <SimpleDescriptor endpoint=1 profile=260 device_type=81 input_clusters=[0, 10, 4, 5, 61184] output_clusters=[25]>
    MODELS_INFO: [
        ("_TZE200_3i3exuay", "TS0601"),

Just and my device info here? Or should I do anything else?

I tried to add ("_TZE200_xu4a5rhj", "TS0601"), and deleted my device then paired it again, but nothing changed. Still can't see any entities.

AlterSu24 commented 11 months ago

It didn't load with any quirks. Zigbbe Info: IEEE: a4:c1:38:ea:63:85:c9:2c Nwk: 0xfc9e Device Type: Router LQI: 56 RSSI: -86 上次出現: 2023-07-28T13:25:44 電力來源: Mains

javicalle commented 11 months ago

@AlterSu24 I couldn't see your device signature in the first post because there are some format error.

In your case you need a new quirk for your device (a GPP one). Can you try with this one?:

TuyaCoverTS0601_var04 ```python class TuyaCover0601_var04(TuyaWindowCover): """Tuya blind cover motor.""" signature = { MODELS_INFO: [ ("_TZE200_xu4a5rhj", "TS0601"), ], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaManufCluster.cluster_id, ], 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], }, }, } replacement = { ENDPOINTS: { 1: { DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaManufacturerWindowCover, TuyaWindowCoverControl, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, 242: { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } ```

You will need to add new imports in the ts0601_cover.py file:

"""Tuya based cover and blinds."""
from zigpy.profiles import zgp, zha
from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Identify, OnOff, Ota, Scenes, Time
besiktas97 commented 11 months ago

_TZE200_cpbo62rn

Thanks this worked for me. Maybe you also know this one. Is it possible to do something with the position of the cover(s)? When I am testing it, the cover doesn't open fully or close fully and cant set it to position with 50% for example?

AlterSu24 commented 11 months ago

Thanks for your reply, it can be identified as a cover now!

But when I try to control it, it has no response at all.

The log showed the cover was opened and closed, but the cover didn't move.

When I control it with the remoter, it also shows the state in the log.

Here is my 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.FullFunctionDevice|MainsPowered|RxOnWhenIdle|AllocateAddress: 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": "0x0104", "device_type": "0x0202", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0x0102", "0xef00" ], "output_clusters": [ "0x0019" ] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "_TZE200_xu4a5rhj", "model": "TS0601", "class": "ts0601_cover.TuyaCover0601_var04" }

And here is my quirk:

"""Tuya based cover and blinds.""" from zigpy.profiles import zgp, zha from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Identify, OnOff, Ota, Scenes, Time

from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( TuyaManufacturerWindowCover, TuyaManufCluster, TuyaWindowCover, TuyaWindowCoverControl, )

class TuyaCover0601_var04(TuyaWindowCover): """Tuya blind cover motor."""

signature = {
    MODELS_INFO: [
        ("_TZE200_xu4a5rhj", "TS0601"),
    ],
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                TuyaManufCluster.cluster_id,
            ],
            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],
        },
    },
}
replacement = {
    ENDPOINTS: {
        1: {
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                TuyaManufacturerWindowCover,
                TuyaWindowCoverControl,
            ],
            OUTPUT_CLUSTERS: [Ota.cluster_id],
        },
        242: {
            PROFILE_ID: zgp.PROFILE_ID,
            DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
            INPUT_CLUSTERS: [],
            OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
        },
    },
}

Thanks again.

javicalle commented 11 months ago

Thanks this worked for me. Maybe you also know this one. Is it possible to do something with the position of the cover(s)? When I am testing it, the cover doesn't open fully or close fully and cant set it to position with 50% for example?

Some Tuya cover can configure the 'travel time' (calibration_time) but not your model. Any error in your logs? Can enable the debug logs and attach the device operation from HA and from the physical switch?

But when I try to control it, it has no response at all.

The log showed the cover was opened and closed, but the cover didn't move.

When I control it with the remoter, it also shows the state in the log.

Does your device respond to the phisycal switch? If not, the problem would be in the device connections. If yes, then enable the debug logs and attach the device operation from HA and from the physical switch.

AlterSu24 commented 11 months ago

My device has no physical switch. You can control it only by pulling the cover by hand or using the remote controller. IMG_9070

So here is the code and what I did is:

  1. Open the cover by remote controller
  2. Close the cover by remote controller
  3. Press open and close in HA
  4. Open and close the cover by hand

home-assistant.log

@javicalle Thanks for your reply. If you need more information, please let me know.

besiktas97 commented 11 months ago

Thanks this worked for me. Maybe you also know this one. Is it possible to do something with the position of the cover(s)? When I am testing it, the cover doesn't open fully or close fully and cant set it to position with 50% for example?

Some Tuya cover can configure the 'travel time' (calibration_time) but not your model. Any error in your logs? Can enable the debug logs and attach the device operation from HA and from the physical switch?

But when I try to control it, it has no response at all. The log showed the cover was opened and closed, but the cover didn't move. When I control it with the remoter, it also shows the state in the log.

Does your device respond to the phisycal switch? If not, the problem would be in the device connections. If yes, then enable the debug logs and attach the device operation from HA and from the physical switch.

Hey Javicalle,

I can see from the developer tools that it must have 15 supported features, see below screenshots:

tuya_zigbee_cover

What do you mean by device operation from HA and from the physical switch ? With the physical switch its doing the same, like in HA pressing the open or close button.

The debug log files:

home-assistant_zha_2023-08-01T13-31-13.897Z.log

Thanks again Javicalle, appreciate your help!

github-actions[bot] commented 5 months ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.