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
695 stars 639 forks source link

_TZE200_3towulqd TS0601 Tuya Motion Sensor with illuminance sensor #1599

Open itsolon opened 2 years ago

itsolon commented 2 years ago

Hello, the device is possible to connect to ZHA integration but then it is not really recognized

the device is bought from here: https://de.aliexpress.com/item/1005004294995498.html?spm=a2g0o.productlist.0.0.7cff2ed9XwpAXp&algo_pvid=c25e2c8c-63d9-4cea-bb0c-5f190d010c75&aem_p4p_detail=202206032153431297942660195650067651185&algo_exp_id=c25e2c8c-63d9-4cea-bb0c-5f190d010c75-55&pdp_ext_f=%7B%22sku_id%22%3A%2212000028662981700%22%7D&pdp_npi=2%40dis%21EUR%21%2111.38%21%21%21%21%21%400b0a01f816543184230937794e1339%2112000028662981700%21sea

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" ], "out_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_3towulqd", "model": "TS0601", "class": "zigpy.device.Device" } Don't remove the extra line breaks outside the ``` marks. ```
Diagnostic information ```yaml { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2022.6.1", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.9.12", "docker": true, "arch": "x86_64", "timezone": "Europe/Berlin", "os_name": "Linux", "os_version": "5.15.41", "supervisor": "2022.05.3", "host_os": "Home Assistant OS 8.1", "docker_version": "20.10.14", "chassis": "vm", "run_as_root": true }, "custom_components": { "ecowitt": { "version": "0.7", "requirements": [ "pyecowitt==0.14" ] }, "frigate": { "version": "2.3", "requirements": [] }, "hacs": { "version": "1.25.5", "requirements": [ "aiogithubapi>=22.2.4" ] }, "sonoff": { "version": "3.0.5", "requirements": [ "pycryptodome>=3.6.6" ] }, "alarmo": { "version": "v1.9.3", "requirements": [] }, "remote_homeassistant": { "version": "3.6", "requirements": [] }, "ble_monitor": { "version": "8.8.0", "requirements": [ "pycryptodomex>=3.14.1", "janus>=1.0.0", "aioblescan>=0.2.12", "btsocket>=0.2.0", "pyric>=0.1.6.3" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ "bellows==0.30.0", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.75", "zigpy-deconz==0.16.0", "zigpy==0.45.1", "zigpy-xbee==0.14.0", "zigpy-zigate==0.7.4", "zigpy-znp==0.7.0" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "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" ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" } ], "after_dependencies": [ "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": 46235, "manufacturer": "_TZE200_3towulqd", "model": "TS0601", "name": "_TZE200_3towulqd TS0601", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4417, "power_source": "Battery or Unknown", "lqi": 18, "rssi": null, "last_seen": "2022-06-03T19:40:04", "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" ], "out_clusters": [ "0x000a", "0x0019" ] } } }, "entities": [ { "entity_id": "binary_sensor.tze200_3towulqd_ts0601_5bc87feb_ias_zone", "name": "_TZE200_3towulqd TS0601" }, { "entity_id": "sensor.tze200_3towulqd_ts0601_5bc87feb_power", "name": "_TZE200_3towulqd TS0601" } ], "neighbors": [], "endpoint_names": [ { "name": "IAS_ZONE" } ], "user_given_name": null, "device_reg_id": "c2fdfe7937f3ebd26c55aef21bd6cda7", "area_id": "diele" } } ``
Additional logs ``` ```

Tuya-2-in-1-Zigbee-Mini-PIR-Motion-Bewegung-Menschlichen-K-rper-Detektor-Helligkeit-Leuchtdichte-Sensor jpg_Q90 jpg_

guidocioni commented 1 year ago

Ah ok that's good. I'll try to configure it again from scratch to see if it works. Just a clarification: you're using ZHA and not zigbee2mqtt right? And if i may ask on which hardware?

Here it is.... didn't do a very good job filming because I had to dim the screen down to the point where I could hardly see what I was filming... However, you can clearly see the sensor triggering... https://youtu.be/5ldyrA5Ct-I

Thank you so much for the video and yep, it seems that your setup (sensor, installation) is exactly the same as mine (I don't know what you use as coodinator, I have a Conbee II stick). I tried to delete the sensor and re-install it. For now it is working as expected but I'll let you know tonight with no lights and hope that it works.

P.S. Just to be clear until now the motion was also detected by the sensor in dark environments (the red light was flashing) but it wasn't picked up by ZHA.

cordvision commented 1 year ago

I got a Conbee II as well. Just to make sure the motion didn't get picked up after turning on the light, I just did another test. I put the sensor under a thick blanked in a dark room and had my hand under the blanket as well. This allowed me to move the hand under the blanket (in complete darkness) and watch home assistant at the same time. Works fine every-time...

guidocioni commented 1 year ago

You're right, it works now :D Super happy and thanks for giving me hope and the reason to test it again

guidocioni commented 1 year ago

I got a Conbee II as well. Just to make sure the motion didn't get picked up after turning on the light, I just did another test. I put the sensor under a thick blanked in a dark room and had my hand under the blanket as well. This allowed me to move the hand under the blanket (in complete darkness) and watch home assistant at the same time. Works fine every-time...

do you also get all these false positives? the sensor seems to work really well when I test it but during the night I get all these detected motion events and there is definitely no motion where the sensor is placed. Screenshot_20230106-083515

cordvision commented 1 year ago

I don't, see attached picture. Could you possibly have a faulty sensor? afab615f-d541-4175-bfeb-b043a0507660

guidocioni commented 1 year ago

I don't, see attached picture. Could you possibly have a faulty sensor? afab615f-d541-4175-bfeb-b043a0507660

I mean, due to all the problems that I had until now with the quirk I tend to believe the problem would be in the integration, and not the sensor, but honestly I have no other clue. I've done some test with the sensor and it seems to work really well: it is not triggered by sudden light changes or far away movements. But leaving it on I can see all these triggers in the logbook. I'm not sure if the sensor did really pick up any movement, as I wasn't there to see if the red light flashes, so I don't really know how to debug..I also tried to change the sensitivity to low as.zigbeee attribute but that didn't seem to change anything. The only thing left to do is to leave the sensor in a closed space and see if it detects any movement there, otherwise I don't have any other idea.

vincenttor commented 1 year ago

I don't, see attached picture. Could you possibly have a faulty sensor? afab615f-d541-4175-bfeb-b043a0507660

I mean, due to all the problems that I had until now with the quirk I tend to believe the problem would be in the integration, and not the sensor, but honestly I have no other clue. I've done some test with the sensor and it seems to work really well: it is not triggered by sudden light changes or far away movements. But leaving it on I can see all these triggers in the logbook. I'm not sure if the sensor did really pick up any movement, as I wasn't there to see if the red light flashes, so I don't really know how to debug..I also tried to change the sensitivity to low as.zigbeee attribute but that didn't seem to change anything. The only thing left to do is to leave the sensor in a closed space and see if it detects any movement there, otherwise I don't have any other idea.

This is the same in my situation there is no movement and it does detect it, i thought maybe its a bad reception so i moved another antenna and power plug in the area acting as a repeater but also this didn't do much. I am going to remove them for now since i can't turn on or off my lights in the workshop.

MattWestb commented 1 year ago

If getting false detection then not have restarting HA and the log looks OK and no errors or warning its very likely sent from the device = bad device.

vincenttor commented 1 year ago

will check the logs, but otherwise i'd have a bad batch , hope not since i have 5 in use...

guidocioni commented 1 year ago

honestly i think these devices are just hit or miss. I ended up returning my tuya and buying an Ikea motion sensor. Even though the latter is way less configurable, has more latency and goes offline after a while, it still does not detect motion when there is none (what a sensor like this should do 😄) and just works

MattWestb commented 1 year ago

@guidocioni Try reconfiguration from the device card and waking the device then sending to it (its not easy then its sleeping very log if its the new model) then all IKEA controllers is missing some config after being pared. If its OK its making one check in every 55 minutes (not the old one its still ZLL) = OK configured. IKEA motion sensor is not one real motion sensor is one OnOff controller (sending commands to light groups) but its working OK. Only if battery is being old / week they can start sending false detection.

Bafnek commented 1 year ago

Thanks for all this, I got it to work with this quirk.

I probably made a mistake by buying this for illuminance reporting, but here goed nothing…

is it possible to get the sensor to report based on a determined change in Lux? Or at least in certain intervals?

qtwre commented 1 year ago

I'd be happy for some sort of times lux update. I found it only really reports lux along with a motion event. I've been trying to work around it by basing automations on lux OR a certain time of day as a backup.

On Fri, Feb 3, 2023, 11:06 a.m. Bafnek @.***> wrote:

Thanks for all this, I got it to work with this quirk.

I probably made a mistake by buying this for illuminance reporting, but here goed nothing…

is it possible to get the sensor to report based on a determined change in Lux? Or at least in certain intervals?

— Reply to this email directly, view it on GitHub https://github.com/zigpy/zha-device-handlers/issues/1599#issuecomment-1416077530, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF36VXSVBFWDFFFL2XG46PLWVUUHBANCNFSM5XZ35VCQ . You are receiving this because you commented.Message ID: @.***>

PeterKawa commented 1 year ago

Most implementation extracted from the info in:

* [[New device support]: ZG-204ZL / TS0601 _TZE200_3towulqd / PIR sensor with illuminance Koenkk/zigbee2mqtt#12364](https://github.com/Koenkk/zigbee2mqtt/issues/12364)

* https://github.com/Koenkk/zigbee-herdsman-converters/pull/4219/files

Proposed quirk: ts0601_pirmotion.py

The device is a low power device and reading/setting values can be difficult:

* https://github.com/jaspernlp/zigbee2mqtt.io/blob/master/docs/devices/ZG-204ZL.md#reading-and-setting-values

Thanks a mil!! Finally my PIR with luminance meter works!

Kaot93 commented 1 year ago

Hey, I'm having the (seemingly) same pir sensor and have the problem, that my illuminance sensor doesn't show anything useful. It only shows 1 or 2.

If I read the cluster measured value I get different values that also update from differing brightness.

Is there something wrong in the conversion of my sensor values?

adi90x commented 1 year ago

Hello,

Why is this quirk not pushed to official release ?

Regards,

cordvision commented 1 year ago

I updated to the latest Home Assistant version today (2023.3.0). Since then, the custom quirk doesn't work for me anymore and I get the following error:

Logger: homeassistant.config_entries Source: zha_quirks/ts0601_pirmotion.py:22 First occurred: 00:15:37 (1 occurrences) Last logged: 00:15:37

Error setting up entry ConBee II - /dev/serial/by-id/usb-dresden_elektronik_ingenieurtechnik_GmbH_ConBee_II_DE2492391-if00, s/n: DE2492391 - dresden elektronik ingenieurtechnik GmbH - 1CF1:0030 for zha Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 383, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/zha/init.py", line 100, in async_setup_entry setup_quirks(config) File "/usr/local/lib/python3.10/site-packages/zhaquirks/init.py", line 409, in setup importer.find_module(modname).load_module(modname) File "", line 548, in _check_name_wrapper File "", line 1063, in load_module File "", line 888, in load_module File "", line 290, in _load_module_shim File "", line 719, in _load File "", line 688, in _load_unlocked File "", line 883, in exec_module File "", line 241, in _call_with_frames_removed File "/config/zha_quirks/ts0601_pirmotion.py", line 22, in from zhaquirks.tuya.mcu import ( ImportError: cannot import name 'TuyaDPType' from 'zhaquirks.tuya.mcu' (/usr/local/lib/python3.10/site-packages/zhaquirks/tuya/mcu/init.py)

The custom quirk causes most zigbee devices to stop working. If I delete it... all devices work fine again except for the motion sensor. Any chance anybody knows what the issue is?

javicalle commented 1 year ago

That wouldn't happen if a PR is created for that quirk...

Just remove all the dp_type=TuyaDPType.XXXXXX from the dp_to_attribute part:

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            MotionSensorCluster.ep_attribute,
            "zone_status",
            dp_type=TuyaDPType.BOOL,   # <-- REMOVE FROM ALL DPs
            converter=lambda x: IasZone.ZoneStatus.Alarm_1 if not x else 0,
        ),
        .../...
cordvision commented 1 year ago

@javicalle I tried but I'm still getting an error.

Here is what I did (just in case I'm miss-understanding what I'm supposed to do):

Original quirk:

"""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):
    """Neo 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,
            converter=lambda x: IasZone.ZoneStatus.Alarm_1 if not x else 0,
        ),
        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.0 * math.log10(x + 1.0)
        ),
    }

    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")],
        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,
                ],
            }
        }
    }

Modified Quirk:

"""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):
    """Neo 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",
            converter=lambda x: IasZone.ZoneStatus.Alarm_1 if not x else 0,
        ),
        4: DPToAttributeMapping(
            TuyaPowerConfigurationCluster.ep_attribute,
            "battery_percentage_remaining",
        ),
        9: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "sensitivity_level",
            converter=lambda x: SensitivityLevel(x),
        ),
        10: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "keep_time",
            converter=lambda x: OnTimeValues(x),
        ),
        12: DPToAttributeMapping(
            TuyaIlluminanceMeasurement.ep_attribute,
            "measured_value",
            converter=lambda x: 10000.0 * math.log10(x + 1.0)
        ),
    }

I'm still getting the following Error after re-starting Home Assistant:

Home Assistant Core 2023-03-02 09:28:29.240 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry ConBee II - /dev/serial/by-id/usb-dresden_elektronik_ingenieurtechnik_GmbH_ConBee_II_DE2492391-if00, s/n: DE2492391 - dresden elektronik ingenieurtechnik GmbH - 1CF1:0030 for zha File "/usr/src/homeassistant/homeassistant/components/zha/init.py", line 100, in async_setup_entry File "/usr/local/lib/python3.10/site-packages/zhaquirks/init.py", line 409, in setup File "/config/zha_quirks/ts0601_pirmotion.py", line 22, in from zhaquirks.tuya.mcu import ( ImportError: cannot import name 'TuyaDPType' from 'zhaquirks.tuya.mcu' (/usr/local/lib/python3.10/site-packages/zhaquirks/tuya/mcu/init.py)

javicalle commented 1 year ago

Remove also the TuyaDPType from the import part:

from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    TuyaDPType,  # <-- REMOVE
    TuyaMCUCluster,
    TuyaPowerConfigurationCluster,
)

PS: to format code put it inside ``` marks.

cordvision commented 1 year ago

Thank you very much! It is working now (obviously haven't had a chance to properly test it yet, but so far everything looks good). In case anybody else is looking for the "fixed" quirk, here is the one that is currently working for me (Home Assistant 2023.3.0):

https://hochseesegelteam.ch/privatemedia/homeassistantfiles/ts0601_pirmotion.py.txt (remove ".txt" file ending before using)

TheJulianJES commented 1 year ago

Reopening until whatever custom quirk is available here is merged into zha-quirks.

James-Lee-007 commented 1 year ago

Most implementation extracted from the info in:

Proposed quirk:

ts0601_pirmotion.py

"""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):
    """Neo 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")],
        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,
                ],
            }
        }
    }

The device is a low power device and reading/setting values can be difficult:

Excuse me, maybe someone could help me see what I'm doing wrong...

I have the same small motion sensors from Aliexpress:

image

I connected it with the zha integration, but it appeared as a smoke detector. So I searched and found this quirk. I copied the code into a python file and named it "motion _sensor.py" and put it into a custom quirks folder in my home assistant server, and in the configuration.yaml file I wrote this:

zha: custom_quirks_path: /config/custom_zha_quirks/

But when I restart HA it completely disables my zha, and gives this error:

`Logger: homeassistant.config_entries Source: custom_zha_quirks/motion_sensor.py:22 First occurred: 13:28:52 (1 occurrences) Last logged: 13:28:52

Error setting up entry Sonoff Zigbee 3.0 USB Dongle Plus - Sonoff Zigbee 3.0 USB Dongle Plus for zha Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 383, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/zha/init.py", line 110, in async_setup_entry setup_quirks(config) File "/usr/local/lib/python3.10/site-packages/zhaquirks/init.py", line 409, in setup importer.find_module(modname).load_module(modname) File "", line 548, in _check_name_wrapper File "", line 1063, in load_module File "", line 888, in load_module File "", line 290, in _load_module_shim File "", line 719, in _load File "", line 688, in _load_unlocked File "", line 883, in exec_module File "", line 241, in _call_with_frames_removed File "/config/custom_zha_quirks/motion_sensor.py", line 22, in from zhaquirks.tuya.mcu import ( ImportError: cannot import name 'TuyaDPType' from 'zhaquirks.tuya.mcu' (/usr/local/lib/python3.10/site-packages/zhaquirks/tuya/mcu/init.py) `

What am I doing wrong? I would appreciate any help, thanks in advance!

javicalle commented 1 year ago

Check here: https://github.com/zigpy/zha-device-handlers/issues/2250#issuecomment-1453978417

Naheem commented 1 year ago

Can some of the people updating this please submit it as a pull request?

I have used the modified quirk from https://github.com/zigpy/zha-device-handlers/issues/1599#issuecomment-1451517386, thank you very much.

I did have to update my scripts though as the IASzone that I was using previously was not unavailable but occupacy was available (I cant remember if that existed before or not).

Also, I have no idea how often it checks for luminance. is it whenever the occupancy state changes or more often?

GerardUmbert commented 1 year ago

You should be able to set the sensitivity_level and keep_time attributes from the PirMotionManufCluster cluster.

Accepted values would be:

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

For setting the value, just write the numeric value (0, 1, 2, ...) and push the "SET ZIGBEE ATTRIBUTE". To check if value is setted, just push the "GET ZIGBEE ATTRIBUTE".

Can you explain further about the setting the timing attribute, right now IASzone triggers every 10 seconds, i think for my usecase 60secs would be better, but i don't understand the "SET ZIGBEE ATTRIBUTE" you mention.

EDIT: I've managet to set the attribute, but it does most certainly not do anything with the 120secs I have setup, it reacts in like 8-10 seconds

image

mwkldeveloper commented 1 year ago

Most implementation extracted from the info in:

Proposed quirk:

ts0601_pirmotion.py The device is a low power device and reading/setting values can be difficult:

TuyaDPType seems not in mcu anymore --> https://github.com/zigpy/zha-device-handlers/issues/2190#issuecomment-1451475355 Update ts0601_pirmotion.py by removing all TuyaDPType related code and restart HA twice times <--I don't know why need restart twice times to get all entity work. but it works.

updated 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 zigpy.zcl import foundation 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, 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): """Neo manufacturer cluster.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update({0xEF09: ("sensitivity_level", SensitivityLevel)}) attributes.update({0xEF0A: ("keep_time", OnTimeValues)}) async def write_attributes(self, attributes, manufacturer=None): """Overwrite to force manufacturer code.""" return await super().write_attributes( attributes, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaOccupancySensing.ep_attribute, "occupancy", converter=lambda x: IasZone.ZoneStatus.Alarm_1 if not x else 0, ), 4: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", ), 9: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "sensitivity_level", converter=lambda x: SensitivityLevel(x), ), 10: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "keep_time", converter=lambda x: OnTimeValues(x), ), 12: DPToAttributeMapping( TuyaIlluminanceMeasurement.ep_attribute, "measured_value", converter=lambda x: 10000 * math.log10(x) + 1 if x != 0 else 0, ), } 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")], 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, ], } } } ```
antoweb commented 1 year ago

Most implementation extracted from the info in:

Proposed quirk: ts0601_pirmotion.py The device is a low power device and reading/setting values can be difficult:

TuyaDPType seems not in mcu anymore --> #2190 (comment) Update ts0601_pirmotion.py by removing all TuyaDPType related code and restart HA twice times <--I don't know why need restart twice times to get all entity work. but it works.

updated ts0601_pirmotion.py

Hello with this quirks my sensor goes on and off the presence detection every minute itself. Any help?

polter05 commented 1 year ago

Hello,

Same problem. After deletion, reset then reinstallation, triggering every 30 minutes. Any idea knowing that it worked perfectly before last weekend? I always do day-to-day updates.

polter05 commented 1 year ago

Hello,

Same problem. After deletion, reset then reinstallation, triggering every 30 minutes. Any idea knowing that it worked perfectly before last weekend? I always do day-to-day updates.

After a few time, every minutes yet. nothing to do. no body have the same issue ? with zha or with z2b, it's the same problem.

jmanis2 commented 1 year ago

The updated quirk in https://github.com/zigpy/zha-device-handlers/issues/1599#issuecomment-1482281897 has gotten this to work and resolved the issue with the original quirk and mappings for mcu. Can we get this added to the repository as I missed this burried in the comments.

The only issue that I'm having is that the occupancy sensor appears to be inverted, meaning that when motion is detected it's clearing the occupancy and when there's no motion it sets occupancy. Additionally the device is updating the status every 10 seconds so if I'm near it, it's continually flipping status from occupied to unoccupied. I've attempted to change the PirMotionManfuCluster : keeptime attribute as well as the TuyaOccupancySensing : pir_o_to_u_delay attributes but none appear to have any affect on the delay between occupied to unoccupied. I may just try to integrate this in to the automation that if the status hasn't changed in 5 mins then trigger an unoccupied action but I'd rather see this get improved in the integration.

Naheem commented 1 year ago

I have created a pull request with the quirk in this issue, to try and get it merged upstream instead of being left in an issue.

However it may need input from people more knowledgeable than me, so any assistance in getting this committed will be helpful.

ivan-gj commented 1 year ago

Hello, @Naheem ! I just tried the quirk, and though HA now perfectly shows the "Occupancy" sensor, the Illuminance sensor is not being shown, but I can perfectly get its reading in the device manager as shown in this screen shot (sorry for the Spanish): Screenshot_20230720_174841 The question would clearly be... how can I turn that into a Sensor? Or is this working differently for you?


Solved!

Seems like a full restart of the HA machine suddenly did the thing.

fr4ngus commented 10 months ago

Hi, I'm going to ZHA zigbee manage sensor section but I don't find how to change the value of the sensitivity of the motion sensor. I use the updated script of the sensor but it doesn't work. Thank you for your help.

AdaaamB commented 9 months ago

https://github.com/zigpy/zha-device-handlers/issues/1599#issuecomment-1482281897 working perfectly for me, thank you and thanks to thatguy-za for the steps to get it working!

vascofazza commented 7 months ago

I installed the quirk and according to the logs it's getting loaded. Anyway, I cannot see any configuration about keep_time. I restarted the HA instance and I didn't have any quirk before, so no cache was there already. Am I doing something wrong?

image
antoweb commented 6 months ago

Quirks as follow works good for presence and illuminance but no battery. Any news?

`"""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 zigpy.zcl import foundation

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, 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): """Neo manufacturer cluster."""

attributes = TuyaMCUCluster.attributes.copy()
attributes.update({0xEF09: ("sensitivity_level", SensitivityLevel)})
attributes.update({0xEF0A: ("keep_time", OnTimeValues)})

async def write_attributes(self, attributes, manufacturer=None):
    """Overwrite to force manufacturer code."""

    return await super().write_attributes(
        attributes, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID
    )

dp_to_attribute: Dict[int, DPToAttributeMapping] = {
    1: DPToAttributeMapping(
        TuyaOccupancySensing.ep_attribute,
        "occupancy",
        converter=lambda x: IasZone.ZoneStatus.Alarm_1 if not x else 0,
    ),
    4: DPToAttributeMapping(
        TuyaPowerConfigurationCluster.ep_attribute,
        "battery_percentage_remaining",
    ),
    9: DPToAttributeMapping(
        TuyaMCUCluster.ep_attribute,
        "sensitivity_level",
        converter=lambda x: SensitivityLevel(x),
    ),
    10: DPToAttributeMapping(
        TuyaMCUCluster.ep_attribute,
        "keep_time",
        converter=lambda x: OnTimeValues(x),
    ),
    12: DPToAttributeMapping(
        TuyaIlluminanceMeasurement.ep_attribute,
        "measured_value",
        converter=lambda x: 10000 * math.log10(x) + 1 if x != 0 else 0,
    ),
}

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")],
    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,
            ],
        }
    }
}`
antoweb commented 6 months ago

Sorry battery sensor works good but illuminance value is updated only after presence detection

Naheem commented 6 months ago

I dont think the battery sensor actually works - I had a device battery die recently and until the last day it had battery status as 100%

vinzent commented 1 month ago

For the records.

Bildschirmfoto vom 2024-06-03 10-06-44 Bildschirmfoto vom 2024-06-03 10-06-23 Bildschirmfoto vom 2024-06-03 09-52-50 Screenshot_20240603-093536 Screenshot_20240603-093527 Screenshot_20240603-093507 Screenshot_20240603-093456 Screenshot_20240603-093448 Screenshot_20240603-093428

Extracted DP Id's:

{
"1":"PIR state",
"4":"Battery level",
"9":"PIR Sensitivity",
"10":"Keep time",
"12":"Illuminance value",
"102":"Light Sense Interval Time(Version 2.0.1 and above)"
}
vinzent commented 1 month ago

quirk v2 variant: https://gist.github.com/vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9

Looks like in HA 2024.06.0:

grafik

cemizm commented 1 month ago

I have the same sensor and without any custom quirks (2024.6.2)

image

The illuminance stays at exactly zero with some short spikes around 2000 (blue line). As comparision i included the illuminance of motion sensor from another vendor.

image

It is also strange that the illuminance is more like a discrete then continuous values. i dont have a tuya bride to test it with the tuya app, but would be interesting to correlate the value of the app with the raw value we receive before converting with log10. or maybe it is just my sensor that has some defect?

vinzent commented 1 month ago

@cemizm At least for me the Illumimance measurement seems to be good (with quirk). Developping my version of the quirk, i've discovered that when mapping the tuya datapoint to the Illuminance cluster, there were 2 values sent every time. The device sends illuminance values even when zha doesnt bind to the illuminance cluster measure_value attribute. with the double reporting the value switched between around 0 and the real lux value.

cemizm commented 1 month ago

@vinzent thank you for your great work! With your quirk the illuminance seems to be working now at a first glance, but I will let it run for while to plot the results for a whole day. :)

vinzent commented 1 month ago

@cemizm beaware of the quirks v2 issue which prevents reloading the ZHA integration: https://github.com/zigpy/zigpy/issues/1410

cemizm commented 1 month ago

@cemizm beaware of the quirks v2 issue which prevents reloading the ZHA integration: zigpy/zigpy#1410

good to know, will save me a lot of time when i run into that issue

cemizm commented 1 month ago

@vinzent again, thank you for the custom_quirk. the illuminance value look much better now:

image

how can i help to get this quirk integrated as default device handler?