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
682 stars 631 forks source link

[Device Support Request] Tuya Zigbee Smart Curtain Robot _TZE200_cpbo62rn #2933

Open david-romero opened 5 months ago

david-romero commented 5 months ago

Problem description

the device is integrated without any sensor or control

Solution description

Screenshots/Video

Screenshots/Video ![Captura de pantalla 2024-01-23 a las 15 28 56](https://github.com/zigpy/zha-device-handlers/assets/8822102/7126f374-f8bf-4711-bd6d-e1ca15c1b9aa) ![Captura de pantalla 2024-01-23 a las 15 29 51](https://github.com/zigpy/zha-device-handlers/assets/8822102/58d791a3-4900-4e56-b5cf-2b3b1ffbb9fc)

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=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0202", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0x0102", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_cpbo62rn", "model": "TS0601", "class": "ts0601_cover.TuyaMoesCover0601" } ```

Diagnostic information

Diagnostic information ```json [Paste the diagnostic information here] ```

Logs

Logs ```python { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2024.1.5", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.11.6", "docker": true, "arch": "aarch64", "timezone": "Europe/Madrid", "os_name": "Linux", "os_version": "6.1.63-haos-raspi", "supervisor": "2023.12.1", "host_os": "Home Assistant OS 11.4", "docker_version": "24.0.7", "chassis": "embedded", "run_as_root": true }, "custom_components": { "teamtracker": { "version": "0.1", "requirements": [ "arrow", "aiofiles" ] }, "alexa_media": { "version": "4.9.0", "requirements": [ "alexapy==1.27.10", "packaging>=20.3", "wrapt>=1.14.0" ] }, "average": { "version": "2.3.1", "requirements": [ "colorlog==6.7.0" ] }, "tarifa_20td": { "version": "1.1.0", "requirements": [ "holidays>=0.18.0" ] }, "spotcast": { "version": "v3.6.30", "requirements": [] }, "zoom": { "version": "v0.8.5", "requirements": [] }, "xiaomi_gateway3": { "version": "3.3.5", "requirements": [ "zigpy>=0.44.1" ] }, "sonoff": { "version": "3.5.4", "requirements": [ "pycryptodome>=3.6.6" ] }, "spook": { "version": "2.1.1", "requirements": [] }, "localtuya": { "version": "5.2.1", "requirements": [] }, "govee": { "version": "2023.11.1", "requirements": [ "govee-api-laggat==0.2.2", "dacite==1.8.0" ] }, "pyscript": { "version": "1.5.0", "requirements": [ "croniter==1.3.8", "watchdog==2.3.1" ] }, "tuya_ble": { "version": "0.1.8", "requirements": [ "tuya-iot-py-sdk==0.6.6", "pycountry==22.3.5" ] }, "tapo": { "version": "2.13.0", "requirements": [ "plugp100==4.0.3" ] }, "next_holiday": { "version": "0.3.2", "requirements": [ "holidays" ] }, "battery_notes": { "version": "1.4.1", "requirements": [] }, "meross_cloud": { "version": "1.2.10", "requirements": [ "meross_iot==0.4.6.0rc2" ] }, "browser_mod": { "version": "2.3.0", "requirements": [] }, "google_fit": { "version": "v3.1.0", "requirements": [ "oauth2client==4.1.3", "google-api-python-client==2.82.0" ] }, "anniversaries": { "version": "5.2.0", "requirements": [ "python-dateutil>=2.8.1", "integrationhelper>=0.2.2", "voluptuous>=0.12.1" ] }, "ui_lovelace_minimalist": { "version": "v1.3.9", "requirements": [ "aiofiles==0.8.0", "aiogithubapi>=22.2.4" ] }, "xiaomi_airfryer": { "version": "0.0.1", "requirements": [ "construct==2.10.56", "micloud==0.4", "python-miio>=0.5.7" ] }, "spook_inverse": { "version": "2.1.1", "requirements": [] }, "tplink_deco": { "version": "3.6.0", "requirements": [ "pycryptodome>=3.12.0" ] }, "ble_monitor": { "version": "12.9.0", "requirements": [ "pycryptodomex>=3.18.0", "janus>=1.0.0", "aioblescan>=0.2.14", "btsocket>=0.2.0", "pyric>=0.1.6.3" ] }, "openmindsai": { "version": "0.1.0", "requirements": [] }, "hacs": { "version": "1.34.0", "requirements": [ "aiogithubapi>=22.10.1" ] }, "iaquk": { "version": "1.6.4", "requirements": [ "colorlog==6.7.0", "ruff==0.0.291" ] }, "xiaomi_cloud_map_extractor": { "version": "v2.2.0", "requirements": [ "pillow", "pybase64", "python-miio", "requests", "pycryptodome" ] }, "octopus_spain": { "version": "0.1.0", "requirements": [ "python-graphql-client==0.4.3" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "after_dependencies": [ "onboarding", "usb" ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly", "@TheJulianJES" ], "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", "universal_silabs_flasher" ], "requirements": [ "bellows==0.37.6", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.109", "zigpy-deconz==0.22.4", "zigpy==0.60.6", "zigpy-xbee==0.20.1", "zigpy-zigate==0.12.0", "zigpy-znp==0.12.1", "universal-silabs-flasher==0.0.15", "pyserial-asyncio-fast==0.11" ], "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": "0403", "pid": "6015", "description": "*conbee*", "known_devices": [ "Conbee III" ] }, { "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": "_uzg-01._tcp.local.", "name": "uzg-01*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 47524, "manufacturer": "_TZE200_cpbo62rn", "model": "TS0601", "name": "_TZE200_cpbo62rn TS0601", "quirk_applied": true, "quirk_class": "ts0601_cover.TuyaMoesCover0601", "quirk_id": null, "manufacturer_code": 4098, "power_source": "Battery or Unknown", "lqi": 104, "rssi": -74, "last_seen": "2024-01-23T15:30:46", "available": true, "device_type": "EndDevice", "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=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0202", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0x0102", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_cpbo62rn", "model": "TS0601" }, "active_coordinator": false, "entities": [ { "entity_id": "cover.right_curtain_motor_cubierta", "name": "_TZE200_cpbo62rn TS0601" }, { "entity_id": "sensor.right_curtain_motor_lqi", "name": "_TZE200_cpbo62rn TS0601" }, { "entity_id": "sensor.right_curtain_motor_rssi", "name": "_TZE200_cpbo62rn TS0601" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "WINDOW_COVERING_DEVICE" } ], "user_given_name": "Right curtain motor", "device_reg_id": "0cfcc86070c8dc70d3cdfa798d82f298", "area_id": "salon", "cluster_details": { "1": { "device_type": { "name": "WINDOW_COVERING_DEVICE", "id": 514 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 65 }, "0x0004": { "attribute_name": "manufacturer", "value": "_TZE200_cpbo62rn" }, "0x0005": { "attribute_name": "model", "value": "TS0601" } }, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": "tuya_manufacturer", "attributes": {}, "unsupported_attributes": {} }, "0x0102": { "endpoint_attribute": "window_covering", "attributes": { "0x0008": { "attribute_name": "current_position_lift_percentage", "value": 44 } }, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Custom quirk

Custom quirk ```python """Tuya based cover and blinds.""" from zigpy.profiles import zha from zigpy.zcl.clusters.general import Basic, Groups, Identify, OnOff, Ota, Scenes, Time from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( TuyaManufacturerWindowCover, TuyaManufCluster, TuyaWindowCover, TuyaWindowCoverControl, ) class TuyaZemismartSmartCover0601(TuyaWindowCover): """Tuya Zemismart blind cover motor.""" signature = { # "node_descriptor": "", # input_clusters=[0x0000, 0x0004, 0x0005, 0x000a, 0xef00] # output_clusters=[0x0019] # MODELS_INFO: [ ("_TZE200_fzo2pocs", "TS0601"), ("_TZE200_zpzndjez", "TS0601"), ("_TZE200_cowvfni3", "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, Time.cluster_id, TuyaManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, Time.cluster_id, TuyaManufacturerWindowCover, TuyaWindowCoverControl, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } class TuyaZemismartSmartCover0601_3(TuyaWindowCover): """Tuya Zemismart blind cover motor.""" signature = { # "node_descriptor": "", # input_clusters=[0x0000, 0x0004, 0x0005, 0x000a, 0xef00] # output_clusters=[0x0019] # MODELS_INFO: [ ("_TZE200_fzo2pocs", "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], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, 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], }, }, } class TuyaZemismartSmartCover0601_2(TuyaWindowCover): """Tuya Zemismart curtain cover motor.""" signature = { # "node_descriptor": "", # input_clusters=[0x0000, 0x000a, 0x0004, 0x0005, 0xef00] # output_clusters=[0x0019] # MODELS_INFO: [ ("_TZE200_3i3exuay", "TS0601"), ], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Time.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, Time.cluster_id, TuyaManufacturerWindowCover, TuyaWindowCoverControl, ], OUTPUT_CLUSTERS: [Ota.cluster_id], }, }, } class TuyaMoesCover0601(TuyaWindowCover): """Tuya blind controller device.""" signature = { # "node_descriptor": "NodeDescriptor(byte1=2, byte2=64, mac_capability_flags=128, manufacturer_code=4098, # maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, # maximum_outgoing_transfer_size=82, descriptor_capability_field=0)", # "endpoints": { # "1": { "profile_id": 260, "device_type": "0x0051", "in_clusters": [ "0x0000", "0x0004","0x0005","0xef00"], "out_clusters": ["0x000a","0x0019"] } # }, # "manufacturer": "_TZE200_zah67ekd", # "model": "TS0601", # "class": "zigpy.device.Device" # } MODELS_INFO: [ ("_TZE200_zah67ekd", "TS0601"), ("_TZE200_xuzcvlku", "TS0601"), ("_TZE200_rddyvrci", "TS0601"), ("_TZE200_nueqqe6k", "TS0601"), ("_TZE200_gubdgai2", "TS0601"), ("_TZE200_yenbr4om", "TS0601"), ("_TZE200_5sbebbzs", "TS0601"), ("_TZE200_xaabybja", "TS0601"), ("_TZE200_hsgrhjpf", "TS0601"), ("_TZE200_cpbo62rn", "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], } }, } 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: [Time.cluster_id, Ota.cluster_id], } } } class TuyaCloneCover0601(TuyaWindowCover): """Tuya blind controller device.""" signature = { # # }, # "manufacturer": "_TYST11_wmcdj3aq", # "model": "mcdj3aq", # "class": "zigpy.device.Device" # } MODELS_INFO: [("_TYST11_wmcdj3aq", "mcdj3aq")], # Not tested ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id], } }, } 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], } } } ```

Additional information

No response

i8nemo commented 4 months ago

i also have these units

gabstrusi commented 3 months ago

I’ve also got this unit.

BlikeBread commented 3 months ago

Thanks for sharing it. I added the quirk and I can now control the open/close/stop function. however, i do not see any sensors like battery and luminosity. Any suggestions?

GuidoC1 commented 3 months ago

Thanks for sharing it. I added the quirk and I can now control the open/close/stop function. however, i do not see any sensors like battery and luminosity. Any suggestions?

@BlikeBread Nope, I have not tried to dive into it myself yet. I also have no experience yet, as I have never tried to debug zigbee devices in order to figure out what to add to the custom quick.

P.S. The missing sensors (battery, luminosity, calibration settings), that is exactly what this device support request is for. Hence it is still open :)

SebastianBrase85 commented 2 weeks ago

I'd also love to have this