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
751 stars 688 forks source link

[Device Support Request] Motion Sensor with Light Sensor and Scene Switch #2163

Closed B1199 closed 9 months ago

B1199 commented 1 year ago

Is your feature request related to a problem? Please describe. I paired the motion sensor. IAS_ZONE works but i have no entities for light sensor and scene switch. I need help for create a quirks for this device. https://www.aliexpress.com/item/1005004767798199.html?spm=a2g0o.order_list.order_list_main.16.3c7d5c5fNzEbT2&gatewayAdapt=glo2deu

TS0202 _TZ3210_cwamkvua

Sorry for formating the request, it is my first Github request.

Device signature ```yaml { "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=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": 260, "device_type": "0x0402", "in_clusters": [ "0x0000", "0x0001", "0x0500", "0xef00" ], "out_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZ3210_cwamkvua", "model": "TS0202", "class": "zigpy.device.Device" } ```
Diagnostic information ```yaml { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.2.0b4", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.10.7", "docker": true, "arch": "x86_64", "timezone": "Europe/Berlin", "os_name": "Linux", "os_version": "5.15.80", "supervisor": "2023.01.1", "host_os": "Home Assistant OS 9.4", "docker_version": "20.10.19", "chassis": "vm", "run_as_root": true }, "custom_components": { "browser_mod": { "version": "2.2.0", "requirements": [] }, "localtuya": { "version": "5.0.0", "requirements": [] }, "dewpoint": { "version": "0.0.1", "requirements": [ "psychrolib==2.1.0" ] }, "tuya": { "version": "2.0.0", "requirements": [ "tuya-iot-py-sdk==0.6.6" ] }, "hacs": { "version": "1.30.0", "requirements": [ "aiogithubapi>=22.10.1" ] }, "zha_toolkit": { "version": "v0.8.29", "requirements": [ "packaging>=20.8", "pytz" ] }, "powercalc": { "version": "v1.3.2", "requirements": [ "numpy>=1.21.1" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ "bellows==0.34.6", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.92", "zigpy-deconz==0.19.2", "zigpy==0.53.0", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", "zigpy-znp==0.9.2" ], "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" ] } ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly" ], "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*" } ], "dependencies": [ "file_upload" ], "after_dependencies": [ "onboarding", "usb", "zeroconf" ], "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp" ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 59881, "manufacturer": "_TZ3210_cwamkvua", "model": "TS0202", "name": "_TZ3210_cwamkvua TS0202", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4417, "power_source": "Battery or Unknown", "lqi": 255, "rssi": -61, "last_seen": "2023-02-02T07:03:38", "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=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=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": 260, "device_type": "0x0402", "in_clusters": [ "0x0000", "0x0001", "0x0500", "0xef00" ], "out_clusters": [ "0x000a", "0x0019" ] } } }, "active_coordinator": false, "entities": [ { "entity_id": "sensor.ak55_battery", "name": "_TZ3210_cwamkvua TS0202" }, { "entity_id": "binary_sensor.ak55_iaszone", "name": "_TZ3210_cwamkvua TS0202" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "IAS_ZONE" } ], "user_given_name": "ak55", "device_reg_id": "19d99f41b10fc4029fe0199cebd28014", "area_id": null, "cluster_details": { "1": { "device_type": { "name": "IAS_ZONE", "id": 1026 }, "profile_id": 260, "in_clusters": { "0x0001": { "endpoint_attribute": "power", "attributes": { "0x0020": { "attribute_name": "battery_voltage", "value": 30 }, "0x0021": { "attribute_name": "battery_percentage_remaining", "value": 200 } }, "unsupported_attributes": { "0x0033": { "attribute_name": "battery_quantity" }, "0x0031": { "attribute_name": "battery_size" } } }, "0x0500": { "endpoint_attribute": "ias_zone", "attributes": { "0x0000": { "attribute_name": "zone_state", "value": 1 }, "0x0001": { "attribute_name": "zone_type", "value": 21 }, "0x0002": { "attribute_name": "zone_status", "value": 5 }, "0x0010": { "attribute_name": "cie_addr", "value": [ 116, 204, 6, 255, 255, 46, 33, 0 ] } }, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} }, "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0004": { "attribute_name": "manufacturer", "value": "_TZ3210_cwamkvua" }, "0x0005": { "attribute_name": "model", "value": "TS0202" } }, "unsupported_attributes": {} } }, "out_clusters": { "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} }, "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} } } } } } } ```
Additional logs ``` ![image](https://user-images.githubusercontent.com/58260827/216245005-0f3f6373-d613-4847-a15a-4d931e36b5f7.png) ```

Additional context zigpy.application LOG: iaszone closed 2023-02-02 12:49:51.399 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=1280, data=Serialized[b'\t\x8d\x00\x04\x00\x00\x00\x00\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=239, rssi=-64)

iaszone open 2023-02-02 12:50:22.193 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=1280, data=Serialized[b'\t\x8e\x00\x05\x00\x00\x00\x00\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=239, rssi=-64)

light sensor dark 2023-02-02 12:54:09.352 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\x99\x06\x01Mf\x01\x00\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=223, rssi=-66) 2023-02-02 12:54:10.548 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\x99\x06\x01Mf\x01\x00\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=223, rssi=-66) 2023-02-02 12:54:12.942 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=1280, data=Serialized[b'\t\x9a\x00\x05\x00\x00\x00\x00\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=223, rssi=-66)

light sensor bright 2023-02-02 12:54:17.383 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\x9b\x06\x01Nf\x01\x00\x01\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=215, rssi=-67) 2023-02-02 12:54:18.599 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\x9b\x06\x01Nf\x01\x00\x01\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=223, rssi=-66) 2023-02-02 12:54:20.993 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=1280, data=Serialized[b'\t\x9c\x00\x05\x00\x00\x00\x00\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=215, rssi=-67)

scene button pressed 2023-02-02 12:56:06.209 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\xa1\x06\x01Qe\x04\x00\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=215, rssi=-67) 2023-02-02 12:56:07.402 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\xa1\x06\x01Qe\x04\x00\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=215, rssi=-67)

scene button double_pressed 2023-02-02 12:57:06.507 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\xa7\x06\x01We\x04\x00\x01\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=215, rssi=-67) 2023-02-02 12:57:07.704 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xE9E9), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=61184, data=Serialized[b'\t\xa7\x06\x01We\x04\x00\x01\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=215, rssi=-67)

javicalle commented 1 year ago

I have that quirk in my local environment:

```python """BlitzWolf IS-3/Tuya motion rechargeable occupancy sensor.""" import math 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, Ota, Time from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing 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, TuyaPowerConfigurationCluster, ) class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): """Tuya local OccupancySensing cluster.""" class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster): """Tuya local IlluminanceMeasurement cluster.""" class SensitivityLevel(t.enum8): """Sensitivity level enum.""" LOW = 0x00 MEDIUM = 0x01 HIGH = 0x02 class OnTimeValues(t.enum8): """Sensitivity level enum.""" _10_SEC = 0x00 _30_SEC = 0x01 _60_SEC = 0x02 _120_SEC = 0x03 class PirMotionManufCluster(TuyaMCUCluster): """Tuya manufacturer cluster.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update({0xEF09: ("sensitivity_level", SensitivityLevel)}) attributes.update({0xEF0A: ("keep_time", OnTimeValues)}) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaOccupancySensing.ep_attribute, "occupancy", dp_type=TuyaDPType.BOOL, ), 4: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", dp_type=TuyaDPType.VALUE, ), 9: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "sensitivity_level", dp_type=TuyaDPType.ENUM, converter=lambda x: SensitivityLevel(x), ), 10: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "keep_time", dp_type=TuyaDPType.ENUM, converter=lambda x: OnTimeValues(x), ), 12: DPToAttributeMapping( TuyaIlluminanceMeasurement.ep_attribute, "measured_value", dp_type=TuyaDPType.VALUE, converter=lambda x: (10000 * math.log10(x)) + 1, ), } data_point_handlers = { 1: "_dp_2_attr_update", 4: "_dp_2_attr_update", 9: "_dp_2_attr_update", 10: "_dp_2_attr_update", 12: "_dp_2_attr_update", } class PirMotion(CustomDevice): """Tuya PIR motion sensor.""" signature = { MODELS_INFO: [ ("_TZE200_3towulqd", "TS0601"), ("_TZ3210_cwamkvua", "TS0601"), ], ENDPOINTS: { # endpoints=1 profile=260 device_type=0x0402 # in_clusters=[0x0000, 0x0001, 0x0500], # out_clusters=[0x000a, 0x0019] 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, TuyaPowerConfigurationCluster.cluster_id, IasZone.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], } }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, TuyaPowerConfigurationCluster, PirMotionManufCluster, TuyaOccupancySensing, TuyaIlluminanceMeasurement, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], } } } ```

Here you have a guide to enable the local quirk:

B1199 commented 1 year ago

home-assistant_TS0202.log Thanks for your response. I added your suggested custom quirks but load fails with "input cluster mismatch". Read model 'TS0202' and manufacturer '_TZ3210_cwamkvua' from <Endpoint id=1 in=[power:0x0001, ias_zone:0x0500, None:0xEF00, basic:0x0000] out=[ota:0x0019, time:0x000A]

2023-02-03 09:34:33.221 INFO (MainThread) [zigpy.device] [0x0755] Read model 'TS0202' and manufacturer '_TZ3210_cwamkvua' from <Endpoint id=1 in=[power:0x0001, ias_zone:0x0500, None:0xEF00, basic:0x0000] out=[ota:0x0019, time:0x000A] status=<Status.ZDO_INIT: 1>> 2023-02-03 09:34:33.222 INFO (MainThread) [zigpy.device] [0x0755] Discovered basic device information for 2023-02-03 09:34:33.222 DEBUG (MainThread) [zigpy.application] Device is initialized 2023-02-03 09:34:33.223 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZ3210_cwamkvua TS0202 (a4:c1:38:10:a9:14:28:d7) 2023-02-03 09:34:33.223 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'ts0601_pirmotion.PirMotion'> 2023-02-03 09:34:33.223 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint

javicalle commented 1 year ago

Probably not working version but can you update the signature part like this:


    signature = {
        MODELS_INFO: [
            ("_TZE200_3towulqd", "TS0601"),
            ("_TZ3210_cwamkvua", "TS0601"),
        ],
        ENDPOINTS: {
            # endpoints=1 profile=260 device_type=0x0402
            # in_clusters=[0x0000, 0x0001, 0x0500],
            # out_clusters=[0x000a, 0x0019]
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    TuyaPowerConfigurationCluster.cluster_id,
                    IasZone.cluster_id,
                    PirMotionManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Time.cluster_id,
                    Ota.cluster_id,
                ],
            }
        },
    }

(the new PirMotionManufCluster part)

B1199 commented 1 year ago

I changed the signature code in ts0601_pirmotion.py and rebind the device. Device signature in HA remains at "class": "zigpy.device.Device"

[Uploading home-assistant1233.log…]()

javicalle commented 1 year ago

Ouuuch Replace the MODELS_INFO part:

        MODELS_INFO: [
            ("_TZE200_3towulqd", "TS0601"),
            ("_TZ3210_cwamkvua", "TS0202"),
        ],
B1199 commented 1 year ago

considering works now: Considering <class 'ts0601_pirmotion.PirMotion'>

Now we have to configure the right cluster bindings to get the entities in HA? When i connected the motion sensor to Tuya i found the following features of the sensor:

Motion: detected / clear Scene Switch: single click / double click / long click Environmental light: Bright/Dark Battery: %

What we have to do for customs quirks now to have this properties as HA entities? Thanks.

findings: #https://github.com/Koenkk/zigbee2mqtt/issues/14190

home-assistant1500.log

` """BlitzWolf IS-3/Tuya motion rechargeable occupancy sensor.""" import math 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, Ota, Time from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing 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, TuyaPowerConfigurationCluster, ) class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): """Tuya local OccupancySensing cluster.""" class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster): """Tuya local IlluminanceMeasurement cluster.""" class SensitivityLevel(t.enum8): """Sensitivity level enum.""" LOW = 0x00 MEDIUM = 0x01 HIGH = 0x02 class OnTimeValues(t.enum8): """Sensitivity level enum.""" _10_SEC = 0x00 _30_SEC = 0x01 _60_SEC = 0x02 _120_SEC = 0x03 class PirMotionManufCluster(TuyaMCUCluster): """Tuya manufacturer cluster.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update({0xEF09: ("sensitivity_level", SensitivityLevel)}) attributes.update({0xEF0A: ("keep_time", OnTimeValues)}) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaOccupancySensing.ep_attribute, "occupancy", dp_type=TuyaDPType.BOOL, ), 4: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", dp_type=TuyaDPType.VALUE, ), 9: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "sensitivity_level", dp_type=TuyaDPType.ENUM, converter=lambda x: SensitivityLevel(x), ), 10: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "keep_time", dp_type=TuyaDPType.ENUM, converter=lambda x: OnTimeValues(x), ), 12: DPToAttributeMapping( TuyaIlluminanceMeasurement.ep_attribute, "measured_value", dp_type=TuyaDPType.VALUE, converter=lambda x: (10000 * math.log10(x)) + 1, ), } data_point_handlers = { 1: "_dp_2_attr_update", 4: "_dp_2_attr_update", 9: "_dp_2_attr_update", 10: "_dp_2_attr_update", 12: "_dp_2_attr_update", } class PirMotion(CustomDevice): """Tuya PIR motion sensor.""" signature = { MODELS_INFO: [ ("_TZE200_3towulqd", "TS0601"), ("_TZ3210_cwamkvua", "TS0202"), ], ENDPOINTS: { # Endpoint id=1 in=[power:0x0001, ias_zone:0x0500, None:0xEF00, basic:0x0000] out=[ota:0x0019, time:0x000A] # endpoint=1, profile=260, device_type=1026, device_version=1, input_clusters=[1, 1280, 61184, 0], output_clusters=[25, 10]) 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, TuyaPowerConfigurationCluster.cluster_id, IasZone.cluster_id, PirMotionManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], } }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, TuyaPowerConfigurationCluster, PirMotionManufCluster, TuyaOccupancySensing, TuyaIlluminanceMeasurement, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], } } } `
javicalle commented 1 year ago

We need to map the Tuya DPs to clusters. Decons is not too friendly to get the required info, but (as always) Z2M had done a good job. DPs mappings are:

My quick and dirty proposal would be:

ts0601_pirmotion.py ```python """BlitzWolf IS-3/Tuya motion rechargeable occupancy sensor.""" import math 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, Ota, Time from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing 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, TuyaPowerConfigurationCluster, ) class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): """Tuya local OccupancySensing cluster.""" class TapAction(t.enum8): """Sensitivity level enum.""" SINGLE = 0x00 HOLD = 0x01 DOUBLE = 0x02 class EnvironmentalLight(t.enum8): """Sensitivity level enum.""" BRIGHT = 0x00 DARK = 0x01 class PirMotionManufCluster(TuyaMCUCluster): """Tuya manufacturer cluster.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update({0xEF65: ("scene_switch", TapAction)}) attributes.update({0xEF66: ("environmental_light", EnvironmentalLight)}) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 3: DPToAttributeMapping( TuyaOccupancySensing.ep_attribute, "occupancy", dp_type=TuyaDPType.BOOL, ), 25: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", dp_type=TuyaDPType.VALUE, ), 101: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "scene_switch", dp_type=TuyaDPType.ENUM, converter=lambda x: TapAction(x), ), 102: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "environmental_light", dp_type=TuyaDPType.ENUM, converter=lambda x: EnvironmentalLight(x), ), } data_point_handlers = { 3: "_dp_2_attr_update", 25: "_dp_2_attr_update", 101: "_dp_2_attr_update", 102: "_dp_2_attr_update", } class PirMotion(CustomDevice): """Tuya PIR motion sensor.""" signature = { MODELS_INFO: [("_TZ3210_cwamkvua", "TS0202")], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, TuyaPowerConfigurationCluster.cluster_id, IasZone.cluster_id, PirMotionManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], } }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, TuyaPowerConfigurationCluster, PirMotionManufCluster, TuyaOccupancySensing, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], } } } ```
B1199 commented 1 year ago

home-assistant1756.log image

I updated ts0601_pirmotion.py, reboot, and repaired the device. Only "occupancy" available.

jposouza commented 1 year ago

Hello. Thanks for the post.

Any solution for this?

B1199 commented 1 year ago

Songs still no progress. I can't write python code myself. I still need help for the quirks file. I can only test ready made code in my environment with TS0202 _TZ3210_cwamkvua device. Who can help to write a correct quirks code?

GeneralLouis commented 1 year ago

@javicalle you still around and willing to help?

I have been following this thread with the same device trying to get it (and the rest of the devices that came with my Fingerbot Sense Kickstarter from Adaprox) and I think I have made progress.

Originally I would just get the sensor for PIR but it would just always be "open", now that I have enabled quirks and dropped your file in it now reacts to motion but that is it. Also, what is weird, is the ZigBee info tab doesn't show a quirk active like it is on the 2-button TuYa switch I got working.

Current PIR and scene button (TS0202) image

Adaprox (TuYa guts) TS0042 with Quirk loading image

github-actions[bot] commented 9 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.