-
-
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 QuirkBuilder | |
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", | |
} | |
( | |
QuirkBuilder("_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) | |
.add_to_registry() | |
) |
@Coysh what doesn't work? did you set values with the "Manage zigbee device" functionality (the 3 dots right of "Reconfigure")? See:
zigpy/zha-device-handlers#3125 (comment)
zigpy/zha-device-handlers#3125 (comment)
That was helpful thanks. Is there any future plans to add it into HA directly?
@Coysh i want to implement it as quirks v2. this will allow to have the config attributes to be exposed in a user friendly way.
@vinzent, thanks for the quirk! Can't wait for v2! ;)
large rewrite. i've got the tuya zigbee gatewaay and i was able to access the IOT Dev thing (iot.tuya.com) to disvover the datapoints:
https://gist.github.com/vinzent/2cd645b848fd3b6a0c3e5762956ec89f#file-zg-204zm-py-L67-L80
still need to figure out how to implement it as a quirk v2 with exposed config entries.
Does this sample will help you to figure out, how to implement quirk v2?
This is another quirk, i use with an usb powered mmWave 24GHz Sensor, where all configuration numbers appear in zha:
usb powered mmWave 24GHz Sensor quirk
"""Device handler for Tuya ZG-205Z-A Mini 24Ghz human presence sensor."""
import logging
from typing import Dict, Optional, Tuple, Union
from zigpy.profiles import zgp, zha
from zigpy.profiles.zha import DeviceType
from zigpy.quirks import CustomCluster, CustomDevice
import zigpy.types as t
from zigpy.zcl import foundation
from zigpy.zcl.clusters.general import Basic, Identify, GreenPowerProxy, AnalogOutput, MultistateInput
from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.clusters.measurement import (
IlluminanceMeasurement,
OccupancySensing,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import TuyaLocalCluster, TuyaNewManufCluster, TuyaZBE000Cluster, NoManufacturerCluster
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaAttributesCluster,
TuyaMCUCluster,
TuyaOnOff,
)
_LOGGER = logging.getLogger(__name__)
class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster):
"""Tuya local OccupancySensing cluster."""
class TuyaMmwMotionState (TuyaAttributesCluster, MultistateInput):
"""Tuya local AnalogInput cluster."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "motion_state")
self._update_attribute(self.attributes_by_name["number_of_states"].id, 4)
self._update_attribute(self.attributes_by_name["state_text"].id, ["None","Large","Small","Static" ])
class TuyaMmwRadarFadingTime(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for fading time."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "fading_time")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 600)
self._update_attribute(self.attributes_by_name["resolution"].id, 1)
self._update_attribute(
self.attributes_by_name["engineering_units"].id, 73
)
class TuyaMmwRadarLargeMotionDetectionSensitivity(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for Large motion detection sensitivity."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "large_sensitivity")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 10)
self._update_attribute(self.attributes_by_name["resolution"].id, 1)
class TuyaMmwRadarLargeMotionDetectionDistance(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for Large motion detection distance."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "large_distance")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 1000)
self._update_attribute(self.attributes_by_name["resolution"].id, 10)
self._update_attribute(
self.attributes_by_name["engineering_units"].id, 118
)
class TuyaMmwRadarSmallMotionDetectionSensitivity(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for Small motion detection sensitivity."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "small_sensitivity")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 10)
self._update_attribute(self.attributes_by_name["resolution"].id, 1)
class TuyaMmwRadarSmallMotionDetectionDistance(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for Small motion detection distance."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "small_distance")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 600)
self._update_attribute(self.attributes_by_name["resolution"].id, 10)
self._update_attribute(
self.attributes_by_name["engineering_units"].id, 118
)
class TuyaMmwRadarStaticMotionDetectionSensitivity(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for Static motion detection sensitivity."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "static_sensitivity")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 10)
self._update_attribute(self.attributes_by_name["resolution"].id, 1)
class TuyaMmwRadarStaticMotionDetectionDistance(TuyaAttributesCluster, AnalogOutput):
"""AnalogOutput cluster for Static motion detection distance."""
def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
self._update_attribute(self.attributes_by_name["description"].id, "static_distance")
self._update_attribute(self.attributes_by_name["min_present_value"].id, 0)
self._update_attribute(self.attributes_by_name["max_present_value"].id, 600)
self._update_attribute(self.attributes_by_name["resolution"].id, 10)
self._update_attribute(
self.attributes_by_name["engineering_units"].id, 118
)
class TuyaMotionState(t.enum8):
NONE = 0
LARGE = 1
SMALL = 2
STATIC = 3
class MmwRadarManufCluster (NoManufacturerCluster,TuyaMCUCluster):
"""Tuya ZG-205Z-A Mini 24Ghz human presence sensor cluster."""
attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
0xEF65: ("motion_state", TuyaMotionState, True),
0xEF01: ("presence", t.uint32_t, True),
0xEF6A: ("illuminance_lux", t.uint32_t, True),
0xEF66: ("fading_time", t.uint32_t, True),
0xEF04: ("large_distance", t.uint32_t, True),
0xEF02: ("large_sensitivity", t.uint32_t, True),
0xEF68: ("small_distance", t.uint32_t, True),
0xEF69: ("small_sensitivity", t.uint32_t, True),
0xEF6C: ("static_distance", t.uint32_t, True),
0xEF6D: ("static_sensitivity", t.uint32_t, True),
0xEF6B: ("indicator", t.enum8, True),
}
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
1: DPToAttributeMapping(
TuyaOccupancySensing.ep_attribute,
"occupancy",
),
107: DPToAttributeMapping(
TuyaOnOff.ep_attribute,
"on_off",
),
101: DPToAttributeMapping(
TuyaMmwMotionState.ep_attribute,
"present_value",
converter=lambda x: TuyaMotionState(x),
endpoint_id=9,
),
102: DPToAttributeMapping(
TuyaMmwRadarFadingTime.ep_attribute,
"present_value",
endpoint_id=2,
),
4: DPToAttributeMapping(
TuyaMmwRadarLargeMotionDetectionDistance.ep_attribute,
"present_value",
endpoint_id=4,
),
2: DPToAttributeMapping(
TuyaMmwRadarLargeMotionDetectionSensitivity.ep_attribute,
"present_value",
endpoint_id=3,
),
104: DPToAttributeMapping(
TuyaMmwRadarSmallMotionDetectionDistance.ep_attribute,
"present_value",
endpoint_id=6,
),
105: DPToAttributeMapping(
TuyaMmwRadarSmallMotionDetectionSensitivity.ep_attribute,
"present_value",
endpoint_id=5,
),
108: DPToAttributeMapping(
TuyaMmwRadarStaticMotionDetectionDistance.ep_attribute,
"present_value",
endpoint_id=8,
),
109: DPToAttributeMapping(
TuyaMmwRadarStaticMotionDetectionSensitivity.ep_attribute,
"present_value",
endpoint_id=7,
),
}
data_point_handlers = {
1: "_dp_2_attr_update",
2: "_dp_2_attr_update",
4: "_dp_2_attr_update",
101: "_dp_2_attr_update",
102: "_dp_2_attr_update",
104: "_dp_2_attr_update",
105: "_dp_2_attr_update",
107: "_dp_2_attr_update",
108: "_dp_2_attr_update",
109: "_dp_2_attr_update",
}
class TS0225Radar(CustomDevice):
"""Quirk for Tuya ZG-205Z-A Mini 24Ghz human presence sensor."""
signature = {
# endpoint=1, profile=260, device_type=1026, device_version=1,
# input_clusters=["0x0000", "0x0003", "0x0400", "0x0500","0xe000","0xe002", "0xee00", "0xef00"], output_clusters=[])
MODELS_INFO: [("_TZE200_2aaelwxk", "TS0225")],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Identify.cluster_id,
IlluminanceMeasurement.cluster_id,
IasZone.cluster_id,
TuyaZBE000Cluster.cluster_id,
0xE002, # Unknown
0xEE00, # Unknown
TuyaNewManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [0x0003,0xe000,0xe002,0xee00,0xef00],
},
242: {
# "profile_id": "0xA1E0", "device_type": "0x0061",
# "in_clusters": [], "out_clusters": ["0x0021"]
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.OCCUPANCY_SENSOR,
INPUT_CLUSTERS: [
Basic.cluster_id,
Identify.cluster_id,
IlluminanceMeasurement.cluster_id,
TuyaZBE000Cluster,
MmwRadarManufCluster,
TuyaOccupancySensing,
TuyaOnOff,
],
OUTPUT_CLUSTERS: [],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarFadingTime,
],
OUTPUT_CLUSTERS: [],
},
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarLargeMotionDetectionSensitivity,
],
OUTPUT_CLUSTERS: [],
},
4: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarLargeMotionDetectionDistance,
],
OUTPUT_CLUSTERS: [],
},
5: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarSmallMotionDetectionSensitivity,
],
OUTPUT_CLUSTERS: [],
},
6: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarSmallMotionDetectionDistance,
],
OUTPUT_CLUSTERS: [],
},
7: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarStaticMotionDetectionSensitivity,
],
OUTPUT_CLUSTERS: [],
},
8: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwRadarStaticMotionDetectionDistance,
],
OUTPUT_CLUSTERS: [],
},
9: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.COMBINED_INTERFACE,
INPUT_CLUSTERS: [
TuyaMmwMotionState,
],
OUTPUT_CLUSTERS: [],
},
10: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
Its from here: Device Support Request: Tuya _TZE200_2aaelwxk presence sensor #2551
@jdm09 quirks v2 is this: zigpy/zha-device-handlers#3019 (rendered markdown: https://github.com/zigpy/zha-device-handlers/blob/a97ee3baa790b13b7e468f5489233d6b1ed558c1/quirks_v2.md)
Still trying to learn this Zigbee / ZHA / Tuya / HA stuff. I'm no expert either. :)
the _TZE200_2aaelwxk looks somehow similiar but not battery powered. The _TZE200_kb5noeto is battery powred (2x AAA / HR03) and uses PIR to detect motion and only uses mmwave after PIr doesn't detect motion anymore. at least that is how I understand it.
Interesting (ab)use(?) of endpoints/clusters to get the controls into HA. shouldn't be required anymore with the quirks v2 possibilities.
What i've learned so far is that Tuya devices are cheap and don't implement the ZHA cluster specs nicely. Also these tuya datapoints are vendor and device specific and might change from device to device even from the same vendor.
@vinzent Ok. Now i got the different between what i am using as custom quirk and your linked description :) Thanks for pointing out. I will also try to understand that v2 documentation, but i think i am much more far away from an expert than you :)
I tried adding this to your quirk:
( add_to_registry_v2("_TZE200_kb5noeto", "TS0601-block") .skip_configuration() .removes(IasZone.cluster_id) .adds(HumanPresenceSensorCluster) .replaces(TuyaPowerConfigurationCluster2AAA) .replaces(TuyaOccupancySensing) .replaces(TuyaIlluminanceMeasurement) .sensor(HumanPresenceSensorCluster.AttributeDefs.human_motion_state.name, HumanPresenceSensorCluster.cluster_id) .number(HumanPresenceSensorCluster.AttributeDefs.sensitivity.name,HumanPresenceSensorCluster.cluster_id,step=1,min_value=1,max_value=10) .number(HumanPresenceSensorCluster.AttributeDefs.far_detection.name,HumanPresenceSensorCluster.cluster_id,unit="cm",step=1,min_value=0,max_value=1000,mode="slider") .number(HumanPresenceSensorCluster.AttributeDefs.presence_time.name,HumanPresenceSensorCluster.cluster_id,unit="s",step=1,min_value=0,max_value=28800,mode="slider") )
In addition to occupancy and illuminance I got one sensor that displayed the motion state (as a number but that could be fixed to show the enum names instead) but the sensor was unnamed (it was shown as "_TZE200_kb5noeto TS0601 None") and the number sliders didn't show at all.
So I guess the quirk v2 support isn't really there yet.
On another note, I made a quirk, or rather I hacked together a quirk based on other Tuya radar sensors and I was at least able to expose the parameter sliders. I made it part of the ts0601_motion.py so if you want to try that out, here it is:
ts0601_motion.py
I'm not sure if I should submit it to the quirks repo as it's not really cleaned up and I don't know python at all to properly clean it up. Also it doesn't expose motion state because no matter what cluster types I tried it would not expose that cluster. I don't have enough knowledge about Zigbee, ZCL, Tuya MCU and Python to further work on this and it's enough for my use as it is now, so if anyone is willing to pick this up, be my guest.
Rev. 6 looks like this:
Issues:
- ZHA integration fails to reload when using a custom quirk v2. Full core restart required.(log:
... Multiple matches found for device <Device model='TS0601' manuf='....
) - some attributes report None instead of (probably) 0 for default (example: near_detection), this leads to disabled config entities
- custom sensor/config entities have no good names. They all use "None". Don't know how to set the name.
Tuya iot Dev-Platform Screenshot: https://photos.app.goo.gl/Lc5TMy9qFoG7hML39
I tested rev 6 but none of the controls worked for me. The were all empty and disabled.
But at least they did show up in the device which is a step up from what I was attempting (I wish I understood why I didn't see anything with my config)
@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?)
@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.
After updating to Homeassistant 2024.08.0 they quirk stopped working for me and I got this error in my logs
add_to_registry_v2 is deprecated and will be removed in a future release. Please QuirkBuilder() instead and ensure you call add_to_registry().
I could fix it by replacing that add_to_registry_v2
call to the QuirkBulder
constructor and appending .add_to_registry()
as the last function called on the bulder.
Or as a patch:
diff --git a/zg-204zm.py b/zg-204zm.py
index db78705..dc21156 100644
--- a/zg-204zm.py
+++ b/zg-204zm.py
@@ -12,7 +12,7 @@
import logging
from typing import Final
-from zigpy.quirks.v2 import add_to_registry_v2
+from zigpy.quirks.v2 import QuirkBuilder
import zigpy.types as t
from zigpy.zcl.foundation import ZCLAttributeDef
@@ -247,7 +247,7 @@ class HumanPresenceSensorManufCluster(TuyaMCUCluster):
}
(
- add_to_registry_v2("_TZE200_kb5noeto", "TS0601")
+ QuirkBuilder("_TZE200_kb5noeto", "TS0601")
.skip_configuration()
.removes(IasZone.cluster_id)
.adds(HumanPresenceSensorManufCluster)
@@ -264,4 +264,5 @@ class HumanPresenceSensorManufCluster(TuyaMCUCluster):
#.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)
-)
\ No newline at end of file
+ .add_to_registry()
+)
Also sorry for the double notifications, accidentally left this comment on the wrong gist first
After updating to Homeassistant 2024.08.0 they quirk stopped working for me and I got this error in my logs
add_to_registry_v2 is deprecated and will be removed in a future release. Please QuirkBuilder() instead and ensure you call add_to_registry().
Thanks, @frod0r. This fixed it for me too!
Filed and issue about the ZHA integration reload issue: zigpy/zigpy#1410
Hi, just would like to double check: Is this issue still open?
zigpy/zigpy#1410 (comment)
I haven't found the merge into main branch yet. I'm running on HA 2024.8.3 and if i use this wonderful custom quirk v2 for this mmWave+PiR Sensor, which is absolutely working well (thanks a lot to @vinzent and @frod0r for the 2024.8.x fix), i get still that ZHA Failures on ZHA reloads or ZG restarts.
So I added the quirk, can add the device and also seeing the different options and sensors in the preview. But directly after that all switching to grey/ not available.
What could be the problem here and how to solve it? It can detect the device, add it and then after adding directly loosing it? The device led stays on all time or does never stop blinking until I remove the batteries and start again.
Using Home Assistant 2024.9.0
I've installed quirk v7 (latest) and HA 2024.9.1. I've restarted HA and the sensor a few times, but I never can get it to show anything else on Occupancy: Detected. And the 'none' sensor is either small_move or large_move. Can never get anything else. Anyone else having this issue?
I know this is not very helpful, but I had the same problem, it would get stuck on "Occupancy: Detected" for hours / days until I removed the batteries, would work again maybe for a day and then the same would happen. I gave and moved to a different Zigbee PIR sensor without occupancy and is work well enough for me.
After like 5 times removing the batteries, it now seems to work (without getting stuck). For 12 hours so far or so at least - will report back.
However, it looks like the Occupancy is not working really? It seems to be triggered purely by the motion sensor (aka 'None' in screenshot). So if the PIR doesn't detect anything for a while, it just goes to 'clear' again.
To give a proper example, I have the sensor 3 meters away from me in the living room. I enter the room, it detects it as large move. I sit down, it moves to small_move. And after about 15 seconds, while I'm sitting down in front of it, it goes to 'clear'. Its as if the mmWave sensor never actually works or gets used by the quirk?
This doesn't seem to be working for me: