import logging from typing import Final from zigpy.quirks.v2 import QuirkBuilder, BinarySensorDeviceClass 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 PresenceState(t.enum8): """Presence State values""" none = 0x00 presence = 0x01 peaceful = 0x02 small_movement = 0x03 large_movement = 0x04 class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster): """Tuya local OccupancySensing cluster.""" class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster): """Tuya local IlluminanceMeasurement cluster.""" class HumanPresenceSensorManufCluster(TuyaMCUCluster): """Human Presence Sensor ZG-205Z (5.8GHz)""" class AttributeDefs(TuyaMCUCluster.AttributeDefs): """Tuya DataPoints attributes""" # Presence state presence_state: Final = ZCLAttributeDef( id=0x0001, # DP 1 type=t.uint16_t, access="rp", is_manufacturer_specific=True, ) # Target distance target_distance: Final = ZCLAttributeDef( id=0x0101, # DP 101 type=t.uint16_t, access="rp", is_manufacturer_specific=True, ) # Illuminance value illuminance_lux: Final = ZCLAttributeDef( id=0x0102, # DP 102 type=t.uint16_t, access="rp", is_manufacturer_specific=True, ) # None delay time (presence keep time) none_delay_time: Final = ZCLAttributeDef( id=0x0103, # DP 103 type=t.uint16_t, is_manufacturer_specific=True, ) # Indicator indicator: Final = ZCLAttributeDef( id=0x0104, # DP 104 type=t.Bool, is_manufacturer_specific=True, ) # Move detection max distance move_detection_max: Final = ZCLAttributeDef( id=0x0107, # DP 107 type=t.uint16_t, is_manufacturer_specific=True, ) # Move detection min distance move_detection_min: Final = ZCLAttributeDef( id=0x0108, # DP 108 type=t.uint16_t, is_manufacturer_specific=True, ) # Breath detection max distance breath_detection_max: Final = ZCLAttributeDef( id=0x0109, # DP 109 type=t.uint16_t, is_manufacturer_specific=True, ) # Breath detection min distance breath_detection_min: Final = ZCLAttributeDef( id=0x0110, # DP 110 type=t.uint16_t, is_manufacturer_specific=True, ) # Small move detection max distance small_move_detection_max: Final = ZCLAttributeDef( id=0x0114, # DP 114 type=t.uint16_t, is_manufacturer_specific=True, ) # Small move detection min distance small_move_detection_min: Final = ZCLAttributeDef( id=0x0115, # DP 115 type=t.uint16_t, is_manufacturer_specific=True, ) # Move sensitivity (1 is higher, 9 is lower) move_sensitivity: Final = ZCLAttributeDef( id=0x0116, # DP 116 type=t.uint8_t, is_manufacturer_specific=True, ) # Small move sensitivity small_move_sensitivity: Final = ZCLAttributeDef( id=0x0117, # DP 117 type=t.uint8_t, is_manufacturer_specific=True, ) # Breath sensitivity breath_sensitivity: Final = ZCLAttributeDef( id=0x0118, # DP 118 type=t.uint8_t, is_manufacturer_specific=True, ) dp_to_attribute: dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "presence_state", converter=PresenceState ), 101: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "target_distance", converter=lambda x: x / 100 if x is not None else 0 ), 102: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "illuminance_lux", ), 103: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "none_delay_time", # Value in Tuya App is 30 after Factory reset #converter=lambda x: x if x is not None else 30 ), 104: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "indicator", ), 107: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "move_detection_max", converter=lambda x: x / 100 if x is not None else 10 ), 108: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "move_detection_min", converter=lambda x: x / 100 if x is not None else 0 ), 109: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "breath_detection_max", converter=lambda x: x / 100 if x is not None else 6 ), 110: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "breath_detection_min", converter=lambda x: x / 100 if x is not None else 0 ), 114: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "small_move_detection_max", converter=lambda x: x / 100 if x is not None else 6 ), 115: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "small_move_detection_min", converter=lambda x: x / 100 if x is not None else 0 ), 116: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "move_sensitivity", converter=lambda x: x if x is not None else 5 ), 117: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "small_move_sensitivity", converter=lambda x: x if x is not None else 5 ), 118: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "breath_sensitivity", converter=lambda x: x if x is not None else 5 ), } data_point_handlers = { 1: "_dp_2_attr_update", 101: "_dp_2_attr_update", 102: "_dp_2_attr_update", 103: "_dp_2_attr_update", 104: "_dp_2_attr_update", 107: "_dp_2_attr_update", 108: "_dp_2_attr_update", 109: "_dp_2_attr_update", 110: "_dp_2_attr_update", 114: "_dp_2_attr_update", 115: "_dp_2_attr_update", 116: "_dp_2_attr_update", 117: "_dp_2_attr_update", 118: "_dp_2_attr_update", } ( QuirkBuilder("_TZE204_dapwryy7", "TS0601") .skip_configuration() .removes(IasZone.cluster_id) .adds(HumanPresenceSensorManufCluster) #.adds(TuyaOccupancySensing) .replaces(TuyaPowerConfigurationCluster2AAA) .replaces(TuyaIlluminanceMeasurement) .binary_sensor( "presence_state", HumanPresenceSensorManufCluster.cluster_id, endpoint_id=1, #entity_type=EntityType.STANDARD, # very soon (zigpy channel #dev on github device_class=BinarySensorDeviceClass.OCCUPANCY, fallback_name="Presence" ) .enum( HumanPresenceSensorManufCluster.AttributeDefs.presence_state.name, PresenceState, HumanPresenceSensorManufCluster.cluster_id, entity_platform=EntityPlatform.SENSOR, entity_type=EntityType.STANDARD, fallback_name="Presence State", translation_key="presence_state" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.illuminance_lux.name, HumanPresenceSensorManufCluster.cluster_id, step=1, min_value=0, max_value=2500, fallback_name="Illuminance (lx)", translation_key="illuminance_lux", #entity_type=EntityType.STANDARD # not yet :/ ) .number( HumanPresenceSensorManufCluster.AttributeDefs.target_distance.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=10, fallback_name="Target Distance (m)", translation_key="target_distance", #entity_type=EntityType.STANDARD # not yet :/ ) .number( HumanPresenceSensorManufCluster.AttributeDefs.none_delay_time.name, HumanPresenceSensorManufCluster.cluster_id, step=1, min_value=0, max_value=28800, #unit="s", fallback_name="Hold Delay Time", translation_key="none_delay_time" ) .switch( HumanPresenceSensorManufCluster.AttributeDefs.indicator.name, HumanPresenceSensorManufCluster.cluster_id, fallback_name="LED Indicator", translation_key="indicator" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.move_detection_max.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=10, fallback_name="Move Detection Max Distance (m)", translation_key="move_detection_max" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.move_detection_min.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=10, fallback_name="Move Detection Min Distance (m)", translation_key="move_detection_min" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_max.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=6, fallback_name="Small Move Detection Max Distance (m)", translation_key="small_move_detection_max" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_min.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=6, fallback_name="Small Move Detection Min Distance (m)", translation_key="small_move_detection_min" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_max.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=6, fallback_name="Breath Detection Max Distance (m)", translation_key="breath_detection_max" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_min.name, HumanPresenceSensorManufCluster.cluster_id, step=0.01, min_value=0, max_value=6, fallback_name="Breath Detection Min Distance (m)", translation_key="breath_detection_min" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.move_sensitivity.name, HumanPresenceSensorManufCluster.cluster_id, step=1, min_value=0, max_value=10, fallback_name="Move Sensitivity", translation_key="move_sensitivity" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.small_move_sensitivity.name, HumanPresenceSensorManufCluster.cluster_id, step=1, min_value=0, max_value=10, fallback_name="Small Move Sensitivity", translation_key="small_move_sensitivity" ) .number( HumanPresenceSensorManufCluster.AttributeDefs.breath_sensitivity.name, HumanPresenceSensorManufCluster.cluster_id, step=1, min_value=0, max_value=10, fallback_name="Breath Sensitivity", translation_key="breath_sensitivity" ) .add_to_registry() )