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
737 stars 675 forks source link

[Device Support Request] _TZ3218_ya5d6wth switch with 4 channels and temperature sensor #3338

Open kskalski opened 1 month ago

kskalski commented 1 month ago

Problem description

I have a device (model the same as https://www.zigbee2mqtt.io/devices/TYZGTH4CH-D1RF.html) that connects over Zigbee and adds 4 control switches, but the temperature sensor attached to the device is missing from ZHA.

Solution description

Temperature sensor entity should be included.

Screenshots/Video

Screenshots/Video ![IMG20240831172649](https://github.com/user-attachments/assets/dd6994fe-cd1d-4db9-8b88-607beddebb51)

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": "0x0100", "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0xe000", "0xe001", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] }, "2": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [] }, "3": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [] }, "4": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "_TZ3218_ya5d6wth", "model": "TS000F", "class": "zigpy.device.Device" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant Core", "version": "2024.7.2", "dev": false, "hassio": false, "virtualenv": true, "python_version": "3.12.3", "docker": false, "arch": "aarch64", "timezone": "Europe/Warsaw", "os_name": "Linux", "os_version": "6.8.0-1010-raspi", "run_as_root": false }, "custom_components": { }, "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.39.1", "pyserial==3.5", "zha-quirks==0.0.117", "zigpy-deconz==0.23.2", "zigpy==0.64.1", "zigpy-xbee==0.20.1", "zigpy-zigate==0.12.1", "zigpy-znp==0.12.2", "universal-silabs-flasher==0.0.20", "pyserial-asyncio-fast==0.11" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "10C4", "pid": "EA60", "description": "*slzb-07*", "known_devices": [ "smlight slzb-07" ] }, { "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*" }, { "type": "_xzg._tcp.local.", "name": "xzg*" }, { "type": "_czc._tcp.local.", "name": "czc*" } ], "is_built_in": true }, "setup_times": { "null": { "setup": 0.00027031199999782984 }, "01J30QNW024QD4XFVS0Q9G76FD": { "wait_import_platforms": -0.0010390540000031478, "wait_base_component": -0.003204721000003019, "config_entry_setup": 18.935454093000004 } }, "data": { "ieee": "**REDACTED**", "nwk": 11187, "manufacturer": "_TZ3218_ya5d6wth", "model": "TS000F", "name": "_TZ3218_ya5d6wth TS000F", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "quirk_id": null, "manufacturer_code": 4417, "power_source": "Mains", "lqi": 144, "rssi": -64, "last_seen": "2024-08-31T15:19:39", "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": "0x0100", "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0xe000", "0xe001", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] }, "2": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [] }, "3": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [] }, "4": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0003", "0x0004", "0x0005", "0x0006", "0xe001" ], "output_clusters": [] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "_TZ3218_ya5d6wth", "model": "TS000F" }, "active_coordinator": false, "entities": [ { "entity_id": "button.tz3218_ya5d6wth_ts000f_zidentyfikuj", "name": "_TZ3218_ya5d6wth TS000F" }, { "entity_id": "light.tz3218_ya5d6wth_ts000f_swiatlo", "name": "_TZ3218_ya5d6wth TS000F" }, { "entity_id": "light.tz3218_ya5d6wth_ts000f_swiatlo_2", "name": "_TZ3218_ya5d6wth TS000F" }, { "entity_id": "light.tz3218_ya5d6wth_ts000f_swiatlo_3", "name": "_TZ3218_ya5d6wth TS000F" }, { "entity_id": "light.tz3218_ya5d6wth_ts000f_swiatlo_4", "name": "_TZ3218_ya5d6wth TS000F" }, { "entity_id": "update.tz3218_ya5d6wth_ts000f_firmware", "name": "_TZ3218_ya5d6wth TS000F" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "ON_OFF_LIGHT" }, { "name": "ON_OFF_LIGHT" }, { "name": "ON_OFF_LIGHT" }, { "name": "ON_OFF_LIGHT" }, { "name": "PROXY_BASIC" } ], "user_given_name": null, "device_reg_id": "04199cbfa2b74805eda422d871e171fc", "area_id": "garaz", "cluster_details": { "1": { "device_type": { "name": "ON_OFF_LIGHT", "id": 256 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0004": { "attribute_name": "manufacturer", "value": "_TZ3218_ya5d6wth" }, "0x0005": { "attribute_name": "model", "value": "TS000F" } }, "unsupported_attributes": {} }, "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": 0 } }, "unsupported_attributes": { "0x4003": { "attribute_name": "start_up_on_off" } } }, "0xe001": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} }, "0xe000": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": { "0x0002": { "attribute_name": "current_file_version", "value": 102 } }, "unsupported_attributes": {} } } }, "2": { "device_type": { "name": "ON_OFF_LIGHT", "id": 256 }, "profile_id": 260, "in_clusters": { "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": 0 } }, "unsupported_attributes": { "0x4003": { "attribute_name": "start_up_on_off" } } }, "0xe001": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": {} }, "3": { "device_type": { "name": "ON_OFF_LIGHT", "id": 256 }, "profile_id": 260, "in_clusters": { "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": 0 } }, "unsupported_attributes": { "0x4003": { "attribute_name": "start_up_on_off" } } }, "0xe001": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": {} }, "4": { "device_type": { "name": "ON_OFF_LIGHT", "id": 256 }, "profile_id": 260, "in_clusters": { "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": 0 } }, "unsupported_attributes": { "0x4003": { "attribute_name": "start_up_on_off" } } }, "0xe001": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": {} }, "242": { "device_type": { "name": "PROXY_BASIC", "id": 97 }, "profile_id": 41440, "in_clusters": {}, "out_clusters": { "0x0021": { "endpoint_attribute": "green_power", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Logs

No response

Custom quirk

Custom quirk Based on discussion from https://community.home-assistant.io/t/zigbee-temperature-sensor-switch-only-detected-as-light/748950/9 and code in https://github.com/klibro/zha-quirks/blob/e53ec83f044628f2f1eed239a0c0f7c4d7743056/TS000F_TZ3218_7fiyo3kv.py I managed to get it to work with: ```python """tuya TS000F _TZ3218_ya5d6wth On/Off In-line Switches with thermometer support.""" from zigpy.profiles import zgp, zha from zigpy.zcl.clusters.general import ( Basic, GreenPowerProxy, Groups, Identify, OnOff, Ota, Scenes, Time, ) from zigpy.zcl.clusters.lightlink import LightLink from zigpy.zcl.clusters.measurement import ( TemperatureMeasurement, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODEL, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( EnchantedDevice, TuyaZBE000Cluster, TuyaZBOnOffAttributeCluster, TuyaLocalCluster, TuyaZBExternalSwitchTypeCluster, ) from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaMCUCluster class TuyaTemperatureMeasurement(TemperatureMeasurement, TuyaLocalCluster): """Tuya local TemperatureMeasurement cluster.""" class TemperatureHumidityManufCluster(TuyaMCUCluster): """Tuya Manufacturer Cluster with Temperature and Humidity data points.""" dp_to_attribute: dict[int, DPToAttributeMapping] = { 102: DPToAttributeMapping( TuyaTemperatureMeasurement.ep_attribute, "measured_value", converter=lambda x: x * 10, # decidegree to centidegree ), } data_point_handlers = { 102: "_dp_2_attr_update", } class Tuya_4G_Switch_Temperature(EnchantedDevice): """Tuya 4 gang switch with temperature measurement.""" signature = { MODEL: "TS000F", ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBE000Cluster.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, TemperatureHumidityManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, 3: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, 4: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, TuyaZBExternalSwitchTypeCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, # 242: { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBE000Cluster, TuyaZBExternalSwitchTypeCluster, TemperatureHumidityManufCluster, TuyaTemperatureMeasurement, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, 2: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, 3: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, 4: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaZBOnOffAttributeCluster, TuyaZBExternalSwitchTypeCluster, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: zgp.PROFILE_ID, DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } ```

Additional information

No response

Ulukai0 commented 3 days ago

Hi, I have the same problem, and it's my first try with custom quirks, so I don't know if it's a config problem or the quirk file problem... I copied & pasted your code, deleted, reboot HA and re-add the device but doesn't work, do I have to modify a part of this code?

kskalski commented 2 days ago

I use the exact same code, placed into /home/homeassistant/.homeassistant/custom_zha_quirks/TZ3218_ya5d6wth.py

You can check that home assistant log has this line when loading: WARNING (ImportExecutor_0) [zhaquirks] Loaded custom quirks. Please contribute them to https://github.com/zigpy/zha-device-handlers

and on Zigbee Home Automation integration you should be able to pair it as a new device

Ulukai0 commented 2 days ago

Thanks for the answer, I forgot the ".py" in file name..... Now the Quirk is working !!! I changed the device type to DeviceType.ON_OFF_SWITCH to control my floor heating valves. Winter is coming !!! and I'm ready thanks to you !