Skip to content

Instantly share code, notes, and snippets.

@vinzent
Last active August 15, 2024 15:18
Show Gist options
  • Save vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9 to your computer and use it in GitHub Desktop.
Save vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9 to your computer and use it in GitHub Desktop.
zg-204zs.py
"""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)
.enum(LuminanceMotionManufCluster.AttributeDefs.pir_sensitivity.name,PirSensitivity,LuminanceMotionManufCluster.cluster_id)
# 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)
.add_to_registry()
)
@vinzent
Copy link
Author

vinzent commented Jun 7, 2024

With HA 2024.6.0:

grafik

@b3nni93
Copy link

b3nni93 commented Aug 13, 2024

This quirk was the first solution making the PIR working correctly at HA for me. Thank you so much. But it seems to stop working in 2024.8. I fixed it like described by frod0r at the ZG-204zm topic. Maybe you could update this file like that too, to replace the add_to_registry_v2() stuff?

@vinzent
Copy link
Author

vinzent commented Aug 13, 2024

@b3nni93 will update it by wednesday.

@b3nni93
Copy link

b3nni93 commented Aug 13, 2024

Very nice.. thanks :)
Maybe it‘s an idea to commit it to the official branch, so they can include it natively into ZHA without the need of a quirk?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment