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
734 stars 673 forks source link

[Device Support Request] Haozee 24G Presence Sensor (Tuya TS0601 _TZE204_kyhbrfyl) #2827

Open anthony-spruyt opened 9 months ago

anthony-spruyt commented 9 months ago

Problem description

I've added a new presence sensor (TS0601 by _TZE204_kyhbrfyl). Pairing was successful, but the device doesn't show any entities.

Solution description

Please add support for this 24G mmWave presence sensor from Haozee.

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

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": "_TZE204_kyhbrfyl", "model": "TS0601", "class": "zigpy.device.Device" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.12.1", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.11.6", "docker": true, "arch": "aarch64", "timezone": "Australia/Melbourne", "os_name": "Linux", "os_version": "6.1.58-haos-raspi", "supervisor": "2023.11.6", "host_os": "Home Assistant OS 11.2", "docker_version": "24.0.7", "chassis": "embedded", "run_as_root": true }, "custom_components": { "moonraker": { "version": "1.0.0", "requirements": [ "moonraker-api==2.0.5" ] }, "bureau_of_meteorology": { "version": "1.1.21", "requirements": [ "iso8601" ] }, "adaptive_lighting": { "version": "1.20.0", "requirements": [ "ulid-transform" ] }, "smartlife": { "version": "0.1.0", "requirements": [ "tuya-device-sharing-sdk==0.1.9" ] }, "asusrouter": { "version": "0.24.2", "requirements": [ "asusrouter==1.1.2" ] }, "hacs": { "version": "1.33.0", "requirements": [ "aiogithubapi>=22.10.1" ] }, "alexa_media": { "version": "4.8.0", "requirements": [ "alexapy==1.27.10", "packaging>=20.3", "wrapt>=1.14.0" ] }, "smartir": { "version": "1.17.8", "requirements": [ "aiofiles>=0.6.0" ] }, "rpi_gpio": { "version": "2022.7.0", "requirements": [ "RPi.GPIO==0.7.1" ] } }, "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.1", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.107", "zigpy-deconz==0.22.0", "zigpy==0.60.0", "zigpy-xbee==0.20.0", "zigpy-zigate==0.12.0", "zigpy-znp==0.12.0", "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": 29758, "manufacturer": "_TZE204_kyhbrfyl", "model": "TS0601", "name": "_TZE204_kyhbrfyl TS0601", "quirk_applied": true, "quirk_class": "ts0601_motion.HaozeeMmwRadarMotion", "quirk_id": null, "manufacturer_code": 4417, "power_source": "Mains", "lqi": 156, "rssi": -61, "last_seen": "2023-12-10T20:50:25", "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": "0x0107", "input_clusters": [ "0x0000", "0x0004", "0x0005", "0x000c", "0x0406", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE204_kyhbrfyl", "model": "TS0601" }, "active_coordinator": false, "entities": [ { "entity_id": "binary_sensor.study_mmwave_occupancy", "name": "_TZE204_kyhbrfyl TS0601" } ], "neighbors": [ { "device_type": "Coordinator", "rx_on_when_idle": "On", "relationship": "Parent", "extended_pan_id": "**REDACTED**", "ieee": "**REDACTED**", "nwk": "0x0000", "permit_joining": "Unknown", "depth": "0", "lqi": "69" } ], "routes": [], "endpoint_names": [ { "name": "OCCUPANCY_SENSOR" } ], "user_given_name": "Study mmWave", "device_reg_id": "45935b1c4236e11704e2b248b94417b9", "area_id": "study", "cluster_details": { "1": { "device_type": { "name": "OCCUPANCY_SENSOR", "id": 263 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 74 }, "0x0004": { "attribute_name": "manufacturer", "value": "_TZE204_kyhbrfyl" }, "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": { "0xef00": { "attribute_name": "mcu_version", "value": "1.0.10" } }, "unsupported_attributes": {} }, "0x0406": { "endpoint_attribute": "occupancy", "attributes": { "0x0000": { "attribute_name": "occupancy", "value": 1 } }, "unsupported_attributes": {} }, "0x000c": { "endpoint_attribute": "analog_input", "attributes": { "0x0055": { "attribute_name": "present_value", "value": 1.06 } }, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Logs

Logs ```python [Paste the logs here] ```

Custom quirk

Custom quirk ```python import math 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 ( AnalogInput, Basic, GreenPowerProxy, Groups, Ota, Scenes, Time, ) from zigpy.zcl.clusters.measurement import ( IlluminanceMeasurement, OccupancySensing, RelativeHumidity, TemperatureMeasurement, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( DPToAttributeMapping, TuyaLocalCluster, TuyaNewManufCluster, ) from zhaquirks.tuya.mcu import TuyaMCUCluster class HaozeeTuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): """Tuya local OccupancySensing cluster.""" class HaozeeTuyaAnalogInput(AnalogInput, TuyaLocalCluster): """Tuya local AnalogInput cluster.""" class HaozeeTuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster): """Tuya local IlluminanceMeasurement cluster.""" class HaozeeMmwRadarManufCluster(TuyaMCUCluster): '''Millimeter wave occupancy sensor.''' attributes = TuyaMCUCluster.attributes.copy() attributes.update( { 0xEF02: ("dp_2", t.uint32_t, True), 0xEF03: ("dp_3", t.uint32_t, True), 0xEF04: ("dp_4", t.uint32_t, True), 0xEF06: ("dp_6", t.enum8, True), 0xEF65: ("dp_101", t.uint32_t, True), 0xEF66: ("dp_102", t.uint32_t, True), 0xEF67: ("dp_103", t.CharacterString, True), 0xEF69: ("dp_105", t.enum8, True), 0xEF6A: ("dp_106", t.enum8, True), 0xEF6B: ("dp_107", t.enum8, True), 0xEF6C: ("dp_108", t.uint32_t, True), } ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( HaozeeTuyaOccupancySensing.ep_attribute, "occupancy", ), 2: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_2", ), 3: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_3", ), 4: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_4", ), 6: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_6", ), 9: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, #HaozeeTuyaAnalogInput.ep_attribute, "dp_9", #"present_value", #lambda x: x / 100, ), 11: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "movement_type", ), 12: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "detection_delay", ), 13: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "detection_range", ), 15: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "radar_sensitivity", ), 16: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "motion_sensitivity", ), 19: DPToAttributeMapping( HaozeeTuyaAnalogInput.ep_attribute, #TuyaMCUCluster.ep_attribute, "present_value", #"current_distance", lambda x: x / 100, ), 101: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "battery_level", ), 102: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_102", ), 103: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_103", ), 104: DPToAttributeMapping( HaozeeTuyaIlluminanceMeasurement.ep_attribute, "measured_value", lambda x: 10000 * math.log10(x) + 1 if x != 0 else 0, ), 105: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_105", ), 106: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_106", ), 107: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_107", ), 108: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_108", ), } data_point_handlers = { 1: "_dp_2_attr_update", 2: "_dp_2_attr_update", 3: "_dp_2_attr_update", 4: "_dp_2_attr_update", 6: "_dp_2_attr_update", 9: "_dp_2_attr_update", 11: "_dp_2_attr_update", 12: "_dp_2_attr_update", 13: "_dp_2_attr_update", 15: "_dp_2_attr_update", 16: "_dp_2_attr_update", 19: "_dp_2_attr_update", 101: "_dp_2_attr_update", 102: "_dp_2_attr_update", 103: "_dp_2_attr_update", 104: "_dp_2_attr_update", 105: "_dp_2_attr_update", 106: "_dp_2_attr_update", 107: "_dp_2_attr_update", 108: "_dp_2_attr_update", } class HaozeeMmwRadarMotion(CustomDevice): signature = { MODELS_INFO: [ ("_TZE204_kyhbrfyl", "TS0601"), ], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, #0x0000 Groups.cluster_id, #0x0004 Scenes.cluster_id, #0x0005 TuyaNewManufCluster.cluster_id, #0xef00 ], OUTPUT_CLUSTERS: [ Time.cluster_id, #0x000a Ota.cluster_id, #0x0019 ], }, 242: { PROFILE_ID: zgp.PROFILE_ID, #0xa1e0 DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, INPUT_CLUSTERS: [ ], OUTPUT_CLUSTERS: [ GreenPowerProxy.cluster_id, #0x0021 ], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.OCCUPANCY_SENSOR, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, HaozeeMmwRadarManufCluster, HaozeeTuyaOccupancySensing, HaozeeTuyaAnalogInput, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id ], } } } ```

Additional information

No response

anoril1979 commented 9 months ago

Interrested too! :)

VerodinDev commented 9 months ago

Received this device today, only to discover it's not supported. So yes, add me to the list of people who are interested in support for this one.

anthony-spruyt commented 9 months ago

My custom quirk had been working well in the interim, just haven't been able to change any config such as sensitivity etc.

anoril1979 commented 8 months ago

Thanks Anthony for the quirk. I installed your custom and device is well detected as Occupancy detector. But for now, it detected nothing. :/

anthony-spruyt commented 8 months ago

Thanks Anthony for the quirk. I installed your custom and device is well detected as Occupancy detector. But for now, it detected nothing. :/

I remember initially it also didn't detect anything for me. I restarted it and HA a few times and then suddenly it was good. The default config range is also pretty short from my experience. Overall it has not given me any problems since I got it working, it does sometimes not pick me up if I sit very still, but as soon as I twitch even the slightest it detects presence again.

anoril1979 commented 7 months ago

Hey! I updated and now it works! Thanks a lot. (It's a pity we can't yet configure the settings, but it basically works for now).

nublemeduble commented 7 months ago

I have the same sensor So yes, add me to the list of people who are interested. It works great with your custom quirk. It would be great if you can add the settings for the sensor

nublemeduble commented 7 months ago

Hello everyone, I've now managed to get the controls to be displayed, but I don't think the sliders have any effect. I use this quirk. I use variant 4

class TuyaMmwRadarOccupancyVariant4(CustomDevice): TS0601_radar.txt

The detection itself works

human

anoril1979 commented 7 months ago

Hello! Just to say that after a HA/Pi reboot, I had a new Integration discovered, Hi-Link LD2410 BLE, with 1 device (HLK-LD2410_BC51) and 27 entities. This one is actually the Radar Presence sensor detrected as Bluetooth device! (and for some reason, the ZigBee is no more active...)

nublemeduble commented 7 months ago

@anoril1979 Hm, that's strange because the sensor that is shown to you is not a Zigbee sensor but a Bluetooth sensor. https://de.aliexpress.com/item/1005004775187154.html Have you already found the solution?

anoril1979 commented 7 months ago

Yes, it is actually.

I have no other sensor arround me (to my knowledge) and the only one is the zigbee radio sensor. I suppose it embeds a ble one too. I managed to get both working in my HA and they behave very similarily. (Presence and Movement, BLE looks faster BTW) image (I use the move sensor icon in place of occupancy default for more readability on my side, card custom...)

nublemeduble commented 7 months ago

@anoril1979 You're right, I turned off bluetooth because I didn't need it. After I turned it on, it worked for me too. Unbenannt

There is even a Bluetooth app in the Google Play Store for settings. https://play.google.com/store/apps/details?id=com.hlk.hlkradartool

anoril1979 commented 7 months ago

That's weird, isn't it?! I disabled my Bluetooth integration for the same reasons, and I'm surprised the device use both Zigbee and BLE as it is not written in the device specs! (I bought it on Aliexpress: https://fr.aliexpress.com/item/1005005965164376.html is it explicitly sold as a Zigbee device, no mention of BLE).

Anyway, I suppose I will use it that way (as it has better performances).

It is almost the same as the one you shown earlier, but with no little window (PIR) on it. Anyway, Using the dedicated App is a great alternative until a working quirk is available! <3 (Note that I installed the app but it did not detected the device... I will try by resetting it)

I think we are anyway going further the initial topic of this thread :') Sorry readers for the divergence.

steflrx commented 3 months ago

Hello, many thanks for sharing the quirk, it save my lfe !

g1r1 commented 2 months ago

This quirk worked and the device is quite reliable.

Is there a way to expose other values which are seen in the Bluetooth app like Static Target / Moving Target or distance from target? Also is there a way of switching off bluetooth radio on this device?