Last active
January 29, 2025 13:08
-
-
Save vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9 to your computer and use it in GitHub Desktop.
zg-204zs.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Luminance motion sensor ZG-204ZS""" | |
from typing import Dict, Final | |
from zigpy.quirks.v2 import QuirkBuilder | |
import zigpy.types as t | |
from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing | |
from zigpy.zcl.clusters.security import IasZone | |
from zigpy.zcl.clusters.general import Identify | |
from zigpy.zcl.foundation import ZCLAttributeDef | |
from zhaquirks.tuya import NoManufacturerCluster, TuyaLocalCluster, TuyaPowerConfigurationCluster2AAA | |
from zhaquirks.tuya.mcu import ( | |
DPToAttributeMapping, | |
TuyaMCUCluster, | |
) | |
class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): | |
"""Tuya local OccupancySensing cluster.""" | |
class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster): | |
"""Tuya local IlluminanceMeasurement cluster.""" | |
class PirState(t.enum8): | |
"""Pir state""" | |
pir = 0x00 | |
none = 0x01 | |
@staticmethod | |
def to_occupancy(x): | |
""" Map PIR state to occupancy enum. Actually the PirState is Occupancy inverted.""" | |
if x == PirState.pir: | |
return OccupancySensing.Occupancy.Occupied | |
return OccupancySensing.Occupancy.Unoccupied | |
class PirSensitivity(t.enum8): | |
"""Sensitivity level enum.""" | |
LOW = 0x00 | |
MEDIUM = 0x01 | |
HIGH = 0x02 | |
class PirTime(t.enum8): | |
"""Keep time""" | |
_10_SEC = 0x00 | |
_30_SEC = 0x01 | |
_60_SEC = 0x02 | |
_120_SEC = 0x03 | |
class LuminanceMotionManufCluster(NoManufacturerCluster, TuyaMCUCluster): | |
"""Tuya Manufacturer cluster""" | |
# 1 "PIR state" pir_state Enum pir|none | |
# 4 "Battery level" battery_percentage Integer unit=%,min=0,max=100,scale=0,step=1 | |
# 9 "PIR Sensitivity" pir_sensitivity Enum low|middle|high | |
# 10 "Keep time" pir_time Enum 10s|30s|60s|120s | |
# 12 "Illuminance value" illuminance_value Integer unit=lux,min=0,max=10000,scale=0,step=1 | |
# 102 "Light Sense Interval Time(Version 2.0.1 and above)" interval_time Integer unit="Min",min=1,max=720,scale=0,step=1 | |
class AttributeDefs(TuyaMCUCluster.AttributeDefs): | |
"""Manufacturer specific attribute definitions""" | |
# Mapped to OccupancySensing cluster | |
# pir_state: Final = ZCLAttributeDef( | |
# id=0x0001, # DP 9 | |
# type=PirState, | |
# is_manufacturer_specific=True, | |
# ) | |
battery_percentage: Final = ZCLAttributeDef( | |
id=0x0004, # DP 4 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
pir_sensitivity: Final = ZCLAttributeDef( | |
id=0x0009, # DP 9 | |
type=PirSensitivity, | |
is_manufacturer_specific=True, | |
) | |
pir_time: Final = ZCLAttributeDef( | |
id=0x0010, # DP 10 | |
type=PirTime, | |
is_manufacturer_specific=True, | |
) | |
illuminance_value: Final = ZCLAttributeDef( | |
id=0x0012, # DP 12 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
interval_time: Final = ZCLAttributeDef( | |
id=0x0102, # DP 102 | |
type=t.uint16_t, | |
is_manufacturer_specific=True, | |
) | |
dp_to_attribute: Dict[int, DPToAttributeMapping] = { | |
1: DPToAttributeMapping( | |
TuyaOccupancySensing.ep_attribute, | |
"occupancy", | |
converter=PirState.to_occupancy | |
), | |
4: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"battery_percentage", | |
), | |
9: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"pir_sensitivity", | |
converter=PirSensitivity | |
), | |
10: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"pir_time", | |
converter=PirTime | |
), | |
12: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"illuminance_value", | |
), | |
102: DPToAttributeMapping( | |
TuyaMCUCluster.ep_attribute, | |
"interval_time", | |
), | |
} | |
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", | |
102: "_dp_2_attr_update", | |
} | |
( | |
QuirkBuilder("_TZE200_3towulqd", "TS0601") | |
.skip_configuration() | |
.removes(IasZone.cluster_id) | |
.removes(Identify.cluster_id) | |
.adds(LuminanceMotionManufCluster) | |
.adds(TuyaOccupancySensing) | |
.replaces(TuyaPowerConfigurationCluster2AAA) | |
.replaces(TuyaIlluminanceMeasurement) | |
.enum( | |
LuminanceMotionManufCluster.AttributeDefs.pir_time.name, | |
PirTime, | |
LuminanceMotionManufCluster.cluster_id, | |
fallback_name="PIR time", | |
translation_key="pir_time" | |
) | |
.enum( | |
LuminanceMotionManufCluster.AttributeDefs.pir_sensitivity.name, | |
PirSensitivity, | |
LuminanceMotionManufCluster.cluster_id, | |
fallback_name="PIR sensitivity", | |
translation_key="pir_sensitivity" | |
) | |
# Exception with unit="m" seen with HA 2024.6.0 + 2024.5.x | |
.number( | |
LuminanceMotionManufCluster.AttributeDefs.interval_time.name, | |
LuminanceMotionManufCluster.cluster_id, | |
step=1, | |
min_value=1, | |
max_value=720, | |
fallback_name="Interval time", | |
translation_key="interval_time" | |
) | |
.add_to_registry() | |
) |
Thank you! Will take a look 👌
I’m trying to make this to work:
Tuya Zigbee Human Motion Sensor
https://a.aliexpress.com/_EGQq8gk
This is motion + illuminace sensor and I need to make configurable detection intervals.
@wkrzyzanowski
What i recently noticed is a small difference between two sorts of this ZB PIR you mentioned above:
1. this Signature seems to work with this quirk
{
"node_descriptor": {
"logical_type": 2,
"complex_descriptor_available": 0,
"user_descriptor_available": 0,
"reserved": 0,
"aps_flags": 0,
"frequency_band": 8,
"mac_capability_flags": 128,
"manufacturer_code": 4417,
"maximum_buffer_size": 66,
"maximum_incoming_transfer_size": 66,
"server_mask": 10752,
"maximum_outgoing_transfer_size": 66,
"descriptor_capability_field": 0
},
"endpoints": {
"1": {
"profile_id": "0x0104",
"device_type": "0x0402",
"input_clusters": [
"0x0000",
"0x0001",
"0x0400",
"0x0406",
"0xe000",
"0xe002",
"0xee00",
"0xef00"
],
"output_clusters": []
}
},
"manufacturer": "_TZE200_3towulqd",
"model": "TS0601",
"class": "zigpy.quirks.v2.CustomDeviceV2"
}
2. this slightly different Signature is also from the same Model but is not working:
{
"node_descriptor": {
"logical_type": 2,
"complex_descriptor_available": 0,
"user_descriptor_available": 0,
"reserved": 0,
"aps_flags": 0,
"frequency_band": 8,
"mac_capability_flags": 128,
"manufacturer_code": 4417,
"maximum_buffer_size": 66,
"maximum_incoming_transfer_size": 66,
"server_mask": 10752,
"maximum_outgoing_transfer_size": 66,
"descriptor_capability_field": 0
},
"endpoints": {
"1": {
"profile_id": "0x0104",
"device_type": "0x0402",
"input_clusters": [
"0x0000",
"0x0001",
"0x0400",
"0x0406",
"0xef00"
],
"output_clusters": [
"0x000a",
"0x0019"
]
}
},
"manufacturer": "_TZE200_3towulqd",
"model": "TS0601",
"class": "zigpy.quirks.v2.CustomDeviceV2"
}
Actually i have both of these PIRs in my ZHA Network.
Seems that there are slightly different Models with the same Manufacturer and Model Signature.
But sorry, i have no idea on how to identify and treat these differences with the quirk.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@wkrzyzanowski Sure, would really appreciate this!
here is the Version, which is working actually, but this one does not update the illuminance and battery percentage.
I can not find the wrong part.
Thanks for your support!
Modified Quirk for round PIR