-
-
Save vinzent/2cd645b848fd3b6a0c3e5762956ec89f to your computer and use it in GitHub Desktop.
""" | |
* TS0601 ZG-204ZM | |
* _TZE200_kb5noeto | |
* https://de.aliexpress.com/item/1005006174074799.html ("Color": Mmwave PIR) | |
* https://github.com/13717033460/zigbee-herdsman-converters/blob/6c9cf1b0de836ec2172d569568d3c7fe75268958/src/devices/tuya.ts#L5730-L5762 | |
* https://www.zigbee2mqtt.io/devices/ZG-204ZM.html | |
* https://smarthomescene.com/reviews/zigbee-battery-powered-presence-sensor-zg-204zm-review/ | |
* https://doc.szalarm.com/zg-205ZL/cntop_zigbee_sensor.js | |
* https://github.com/Koenkk/zigbee2mqtt/issues/21919 | |
""" | |
import logging | |
from typing import Final | |
from zigpy.quirks.v2 import add_to_registry_v2 | |
import zigpy.types as t | |
from zigpy.zcl.foundation import ZCLAttributeDef | |
from zigpy.zcl.clusters.measurement import ( | |
IlluminanceMeasurement, | |
OccupancySensing, | |
) | |
from zigpy.zcl.clusters.security import IasZone | |
from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType | |
from zhaquirks.tuya import ( | |
TuyaLocalCluster, | |
TuyaPowerConfigurationCluster2AAA, | |
) | |
from zhaquirks.tuya.mcu import TuyaMCUCluster, DPToAttributeMapping | |
class HumanMotionState(t.enum8): | |
"""Human Motion State values""" | |
none = 0x00 | |
large_move = 0x01 | |
small_move = 0x02 | |
breathe = 0x03 | |
class MotionDetectionMode(t.enum8): | |
"""Motion detection mode values""" | |
Only_PIR: 0x00 | |
PIR_radar: 0x01 | |
Only_radar: 0x02 | |
@staticmethod | |
def converter(value): | |
"""" If value is None, Only_PIR should be returned """ | |
if value is None: | |
return MotionDetectionMode.Only_PIR | |
return value | |
class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): | |
"""Tuya local OccupancySensing cluster.""" | |
class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster): | |
"""Tuya local IlluminanceMeasurement cluster.""" | |
class HumanPresenceSensorManufCluster(TuyaMCUCluster): | |
"""Human Presence Sensor ZG-204ZM (PIR+mmWave, battery)""" | |
# Tuya Data points | |
# "1":"Human Presence State", (presence_state, Enum, none|presence) | |
# "2":"Stationary detection sensitivity", (sensitivity, Integer, 0-10, unit=x, step=1) | |
# "3":"Minimum detection distance", (near_detection, Integer, 0-1000, unit=cm, step=1) (NOT AVAILABLE IN TUYA SMART LIFE APP) | |
# "4":"Stationary detection distance", (far_detection, Integer, 0-1000, unit=cm, step=1) | |
# "101":"Human Motion State", (human_motion_state, Enum, none|large_move|small_move|breathe) | |
# "102":"Presence Keep Time", (presence_time, 10-28800, unit=s, step=1) | |
# "106":"Illuminance Value", (illuminance_value, Integer, 0-6000, unit=lux ) | |
# "107":"Indicator", (indicator, Boolean) | |
# "112":"Reset setting", (reset_setting, Boolean) | |
# "121":"Battery", (battery, Integer, -1-100, step=1, unit=%) | |
# "122":"Motion detection ", (motion_detection_mode, Enum, Only_PIR|PIR_radar|Only_radar) (NOT AVAILABLE IN TUYA SMART LIFE APP) | |
# "123":"Motion detection sensitivity", (motion_detection_sen, Integer, 0-10, step=1, unit=x) (NOT AVAILABLE IN TUYA SMART LIFE APP) | |
# "124":"ver" (ver, Integer, 0-100, step=1) (NOT AVAILABLE IN TUYA SMART LIFE APP) | |
class AttributeDefs(TuyaMCUCluster.AttributeDefs): | |
"""Tuya DataPoints attributes""" | |
# Human presence state (mapped to the OccupancySensing cluster) | |
#presence_state: Final = ZCLAttributeDef( | |
# id=0xEF01, # DP 1 | |
# type=Occupancy, | |
# access="rp", | |
# is_manufacturer_specific=True, | |
#) | |
# Stationary detection sensitivity | |
sensitivity: Final = ZCLAttributeDef( | |
id=0x0002, # DP 2 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
# Minimum detection distance | |
near_detection: Final = ZCLAttributeDef( | |
id=0x0003, # DP 3 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
# Stationary detection distance | |
far_detection: Final = ZCLAttributeDef( | |
id=0x0004, # DP 4 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
# Human motion state | |
human_motion_state: Final = ZCLAttributeDef( | |
id=0x0101, # DP 101 | |
type=HumanMotionState, | |
access="rp", | |
is_manufacturer_specific=True, | |
) | |
# Presence keep time | |
presence_time: Final = ZCLAttributeDef( | |
id=0x0102, # DP 102 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
# Illuminance value | |
illuminance_value: Final = ZCLAttributeDef( | |
id=0x0106, # DP 106 | |
type=t.uint16_t, | |
access="rp", | |
is_manufacturer_specific=True, | |
) | |
# Indicator | |
indicator: Final = ZCLAttributeDef( | |
id=0x0107, # DP 107 | |
type=t.Bool, | |
is_manufacturer_specific=True, | |
) | |
# Reset setting | |
reset_setting: Final = ZCLAttributeDef( | |
id=0x0112, # DP 112 | |
type=t.Bool, | |
is_manufacturer_specific=True, | |
) | |
# Battery (also provided by the TuyaPowerConfigurationCluster2AAA) | |
battery: Final = ZCLAttributeDef( | |
id=0x0121, # DP 121 | |
type=t.int16s, | |
is_manufacturer_specific=True, | |
) | |
# Motion detection | |
motion_detection_mode: Final = ZCLAttributeDef( | |
id=0x0122, # DP 122 | |
type=MotionDetectionMode, | |
is_manufacturer_specific=True, | |
) | |
# Motion detection sensitivity | |
motion_detection_sen: Final = ZCLAttributeDef( | |
id=0x0123, # DP 123 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
# ver | |
ver: Final = ZCLAttributeDef( | |
id=0x0124, # DP 124 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
dp_to_attribute: dict[int, DPToAttributeMapping] = { | |
1: DPToAttributeMapping( | |
TuyaOccupancySensing.ep_attribute, | |
"occupancy", | |
), | |
2: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"sensitivity", | |
# Value in Tuya App after Factory reset is 6 | |
converter=lambda x: x if x is not None else 6 | |
), | |
3: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"near_detection", | |
# Guessing a default of 0 | |
converter=lambda x: x if x is not None else 0 | |
), | |
4: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"far_detection", | |
# Value in Tuya App after Factory reset is 600cm | |
converter=lambda x: x if x is not None else 600 | |
), | |
101: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"human_motion_state", | |
converter=HumanMotionState | |
), | |
102: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"presence_time", | |
# Value in Tuya App is 30 after Factory reset | |
converter=lambda x: x if x is not None else 30 | |
), | |
106: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"illuminance_value", | |
), | |
107: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"indicator", | |
), | |
112: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"reset_setting", | |
), | |
121: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"battery", | |
), | |
122: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"motion_detection_mode", | |
converter=MotionDetectionMode, | |
), | |
123: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"motion_detection_sen", | |
# Guessing a default of 10 | |
converter=lambda x: x if x is not None else 10 | |
), | |
124: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"ver", | |
), | |
} | |
data_point_handlers = { | |
1: "_dp_2_attr_update", | |
2: "_dp_2_attr_update", | |
3: "_dp_2_attr_update", | |
4: "_dp_2_attr_update", | |
101: "_dp_2_attr_update", | |
102: "_dp_2_attr_update", | |
106: "_dp_2_attr_update", | |
107: "_dp_2_attr_update", | |
112: "_dp_2_attr_update", | |
121: "_dp_2_attr_update", | |
122: "_dp_2_attr_update", | |
123: "_dp_2_attr_update", | |
124: "_dp_2_attr_update", | |
} | |
( | |
add_to_registry_v2("_TZE200_kb5noeto", "TS0601") | |
.skip_configuration() | |
.removes(IasZone.cluster_id) | |
.adds(HumanPresenceSensorManufCluster) | |
.adds(TuyaOccupancySensing) | |
.replaces(TuyaPowerConfigurationCluster2AAA) | |
.replaces(TuyaIlluminanceMeasurement) | |
.number(HumanPresenceSensorManufCluster.AttributeDefs.sensitivity.name, HumanPresenceSensorManufCluster.cluster_id,step=1,min_value=1,max_value=10) | |
#.number(HumanPresenceSensorManufCluster.AttributeDefs.near_detection.name, HumanPresenceSensorManufCluster.cluster_id,step=1,min_value=0,max_value=1000) | |
.number(HumanPresenceSensorManufCluster.AttributeDefs.far_detection.name, HumanPresenceSensorManufCluster.cluster_id,step=1,min_value=0,max_value=1000) | |
.enum(HumanPresenceSensorManufCluster.AttributeDefs.human_motion_state.name,HumanMotionState,HumanPresenceSensorManufCluster.cluster_id,entity_platform=EntityPlatform.SENSOR, entity_type=EntityType.STANDARD) | |
.number(HumanPresenceSensorManufCluster.AttributeDefs.presence_time.name, HumanPresenceSensorManufCluster.cluster_id,step=1,min_value=10,max_value=28800) | |
#.binary_sensor(HumanPresenceSensorManufCluster.AttributeDefs.indicator.name,HumanPresenceSensorManufCluster.cluster_id) | |
#.binary_sensor(HumanPresenceSensorManufCluster.AttributeDefs.reset_setting.name,HumanPresenceSensorManufCluster.cluster_id) | |
#.enum(HumanPresenceSensorManufCluster.AttributeDefs.motion_detection_mode.name,MotionDetectionMode,HumanPresenceSensorManufCluster.cluster_id) | |
#.number(HumanPresenceSensorManufCluster.AttributeDefs.motion_detection_sen.name, HumanPresenceSensorManufCluster.cluster_id,step=1,min_value=0,max_value=10) | |
#.number(HumanPresenceSensorManufCluster.AttributeDefs.ver.name, HumanPresenceSensorManufCluster.cluster_id,step=1,min_value=0,max_value=10) | |
) |
@greenamit the first one is the human_motion_state tuya datapoint . so it probably should be called like this? the attribute name is passed by HumanPresenceSensorManufCluster.AttributeDefs.human_motion_state.name
.
also for the other enum/numbers the name passed should probably be used.
but I don't have much insight in how these things are handled in HA Core. still reading dev docs on developers.home-assistant.io for a better understanding.
@vinzent I never needed to repair the device when changing quirks. But it's true it takes a while for all the sensors to populate, but I waited several minutes and nothing happened. With my old style quirk it takes just a couple of seconds.
But the device itself is quite unreliable unfortunately (last night it stayed on presence the whole night) so it may have something to do with the unreliable cluster operation.
I have another such sensor, I set it up and will be monitoring it (with my old quirk for now).
Still working on this. learning. :) there are multiple DPToAttributeMapping classes. one in tuya, one in tuya/mcu. the imported DPToAttributeMapping in rev 6 is probably wrong (why are there 2 of them?)
Rev 7:
- removed the datapoint entities that did not work out of the box
- import DPToAttributeMapping from zhaquirks.tuya.mcu instead of the probably wrong zhaquirks.tuya.
- Sensor None is the
human_motion_state
datapoint - Config first is
sensitivity
, 2ndfar_detection
, 3rdpresence_time
Filed and issue about the ZHA integration reload issue: zigpy/zigpy#1410
But the device itself is quite unreliable unfortunately (last night it stayed on presence the whole night) so it may have something to do with the unreliable cluster operation. I have another such sensor, I set it up and will be monitoring it (with my old quirk for now).
I have the same behaviour it would work "fine" changing state between "Large_move" , "breathe" and "none" couple of time that then just gets stuck on "breathe" until I remove the batteries. Not sure if it is a hardware problem, read on other posts, resoldering pins on the pcb improved reliability
See here -> Koenkk/zigbee2mqtt#21919 (comment)
But the device itself is quite unreliable unfortunately (last night it stayed on presence the whole night) so it may have something to do with the unreliable cluster operation. I have another such sensor, I set it up and will be monitoring it (with my old quirk for now).
I have the same behaviour it would work "fine" changing state between "Large_move" , "breathe" and "none" couple of time that then just gets stuck on "breathe" until I remove the batteries. Not sure if it is a hardware problem, read on other posts, resoldering pins on the pcb improved reliability
See here -> Koenkk/zigbee2mqtt#21919 (comment)
I actually only cleaned those. It was difficult to desolder from the battery terminals so I didn't bother with my second unit.
Also for mine I only need to press the pairing button to reset the device. Last time it lasted more than a week. If it becomes too bothersome I will probably give it some more soldering time.
@mikosoft83 did you delete and re-add the device? I think some things only get initialized properly with resetting the device. also some entities were disabled at the beginning and worked after few minutes. (probably until the first valid value was reported?)