""" DY-YG400A Smoke Sensor, TS0601 from _TZE204_ntcy3xu1 https://www.aliexpress.com/item/1005005863519099.html https://www.aliexpress.com/item/1005005854203557.html """ import logging import zigpy.profiles.zha from zigpy.quirks import CustomDevice import zigpy.types as t from zigpy.zcl.clusters.general import ( Basic, Groups, Ota, Scenes, Time, BinaryInput, BatterySize, ) from zigpy.zcl.clusters.security import IasZone from zhaquirks import Bus from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ZONE_TYPE, ) from zhaquirks.tuya import ( TuyaLocalCluster, TuyaManufCluster, TuyaManufClusterAttributes, TuyaPowerConfigurationCluster, ) _LOGGER = logging.getLogger(__name__) TUYA_SMOKE_DETECTED_ATTR = 0x0401 # [0]/[1] [Detected]/[Clear]! TUYA_SMOKE_TAMPERED_ATTR = 0x0104 # [0]/[1] [Clear]/[Tampered]! TUYA_SMOKE_BATTERY_ATTR = 0x040e # [0]/[1]/[2] [Low]/[Med]]/[Full]! class TuyaSmokeDetectorCluster(TuyaManufClusterAttributes): """Manufacturer Specific Cluster of the TS0601 smoke detector.""" attributes = { TUYA_SMOKE_DETECTED_ATTR: ("smoke_detected", t.uint8_t, True), TUYA_SMOKE_TAMPERED_ATTR: ("tampered_device", t.uint8_t, True), TUYA_SMOKE_BATTERY_ATTR: ("battery_status", t.uint8_t, True), } def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == TUYA_SMOKE_DETECTED_ATTR: status = IasZone.ZoneStatus.Alarm_1 if value == 0 else 0 self.endpoint.device.ias_bus.listener_event("update_attribute", "zone_status", status) elif attrid == TUYA_SMOKE_TAMPERED_ATTR: self.endpoint.device.tamper_detection_bus.listener_event( "update_attribute", "present_value", bool(value) ) elif attrid == TUYA_SMOKE_BATTERY_ATTR: batt = 5 if value == 0 else 40 if value == 1 else 100 self.endpoint.device.battery_bus.listener_event("battery_change", batt) else: _LOGGER.warning( "[0x%04x:%s:0x%04x] unhandled attribute: 0x%04x", self.endpoint.device.nwk, self.endpoint.endpoint_id, self.cluster_id, attrid, ) class TuyaIasZone(TuyaLocalCluster, IasZone): """ IAS Zone: this generates the "Smoke" entity for HA. Receives updates from TuyaSmokeDetectorCluster. """ _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Fire_Sensor} def __init__(self, *args, **kwargs): """Init.""" super().__init__(*args, **kwargs) self.endpoint.device.ias_bus.add_listener(self) class TuyaPowerConfigCluster(TuyaPowerConfigurationCluster): """ Power Config Cluster: this generates the "Battery" entity for HA. Receives updates from TuyaSmokeDetectorCluster. """ _CONSTANT_ATTRIBUTES = { TuyaPowerConfigurationCluster.attributes_by_name["battery_size"].id: BatterySize.AAA, TuyaPowerConfigurationCluster.attributes_by_name["battery_quantity"].id: 2, TuyaPowerConfigurationCluster.attributes_by_name["battery_rated_voltage"].id: 15, } class TuyaTamperDetection(TuyaLocalCluster, BinaryInput): """ Tamper Detection Cluster: this generates the "Binary input" entity for HA, which is updated with the Tampered state. Receives updates from TuyaSmokeDetectorCluster. """ _CONSTANT_ATTRIBUTES = { BinaryInput.attributes_by_name["description"].id: "Tamper Detected", BinaryInput.attributes_by_name["active_text"].id: "Tampered", BinaryInput.attributes_by_name["inactive_text"].id: "Clear", } def __init__(self, *args, **kwargs): """Init.""" super().__init__(*args, **kwargs) self.endpoint.device.tamper_detection_bus.add_listener(self) class TuyaSmokeDetector0601(CustomDevice): """TS0601 Smoke detector quirk.""" def __init__(self, *args, **kwargs): """Init.""" self.ias_bus = Bus() self.battery_bus = Bus() self.tamper_detection_bus = Bus() super().__init__(*args, **kwargs) signature = { MODELS_INFO: [ ("_TZE200_uebojraa", "TS0601"), ], ENDPOINTS: { 1: { PROFILE_ID: zigpy.profiles.zha.PROFILE_ID, DEVICE_TYPE: zigpy.profiles.zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zigpy.profiles.zha.PROFILE_ID, DEVICE_TYPE: zigpy.profiles.zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaIasZone, TuyaSmokeDetectorCluster, TuyaPowerConfigCluster, TuyaTamperDetection, ], OUTPUT_CLUSTERS: [ Time.cluster_id, Ota.cluster_id, ], }, }, }