"""Quirk for lumi.airmonitor.acn01 tvoc air monitor.""" from typing import Final from zigpy.profiles import zha from zigpy.quirks import CustomCluster import zigpy.types as t from zigpy.zcl.clusters.general import ( AnalogInput, Basic, Identify, Ota, PowerConfiguration, ) from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement from zigpy.zcl.clusters.security import IasZone from zigpy.zcl.foundation import BaseAttributeDefs, DataTypeId, ZCLAttributeDef from zigpy.zdo.types import NodeDescriptor from zhaquirks import LocalDataCluster from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, NODE_DESCRIPTOR, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.xiaomi import ( LUMI, BasicCluster, RelativeHumidityCluster, TemperatureMeasurementCluster, XiaomiAqaraE1Cluster, XiaomiCustomDevice, XiaomiPowerConfiguration, ) MEASURED_VALUE = 0x0000 DISPLAY_UNIT = 0x0114 class AnalogInputCluster(CustomCluster, AnalogInput): """Analog input cluster, relay tvoc to the correct cluster.""" def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) self.endpoint.voc_level.update_attribute(MEASURED_VALUE, value) class EmulatedTVOCMeasurement(LocalDataCluster): """TVOC measurement cluster to receive reports from the AnalogInput cluster.""" PRESENT_VALUE = 0x0055 ONE_HOUR = 3600 MIN_CHANGE = 5 TEN_SECONDS = 10 cluster_id = 0x042E name = "VOC Level" ep_attribute = "voc_level" class AttributeDefs(BaseAttributeDefs): """Attribute definitions.""" measured_value: Final = ZCLAttributeDef(id=MEASURED_VALUE, type=t.Single) async def bind(self): """Bind cluster.""" result = await self.endpoint.analog_input.bind() await self.endpoint.analog_input.configure_reporting( self.PRESENT_VALUE, self.TEN_SECONDS, self.ONE_HOUR, self.MIN_CHANGE, ) return result class TVOCDisplayUnit(t.enum8): """Display values.""" mgm3_celsius = 0x00 ppb_celsius = 0x01 mgm3_fahrenheit = 0x10 ppb_fahrenheit = 0x11 class TVOCCluster(XiaomiAqaraE1Cluster): """Aqara LUMI Config cluster.""" class AttributeDefs(XiaomiAqaraE1Cluster.AttributeDefs): """Attribute definitions.""" display_unit: Final = ZCLAttributeDef( id=DISPLAY_UNIT, type=TVOCDisplayUnit, zcl_type=DataTypeId.uint8, is_manufacturer_specific=True, ) class TVOCMonitor(XiaomiCustomDevice): """Aqara LUMI lumi.airmonitor.acn01.""" signature = { # MODELS_INFO: [(LUMI, "lumi.airmonitor.acn01")], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, TemperatureMeasurement.cluster_id, RelativeHumidity.cluster_id, PowerConfiguration.cluster_id, AnalogInput.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id], } }, } replacement = { NODE_DESCRIPTOR: NodeDescriptor( 0x02, 0x40, 0x80, 0x115F, 0x7F, 0x0064, 0x2C00, 0x0064, 0x00 ), ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR, INPUT_CLUSTERS: [ BasicCluster, Identify.cluster_id, TemperatureMeasurementCluster, XiaomiPowerConfiguration, RelativeHumidityCluster, AnalogInputCluster, EmulatedTVOCMeasurement, TVOCCluster, ], OUTPUT_CLUSTERS: [Ota.cluster_id], } }, } class TVOCMonitor2(XiaomiCustomDevice): """Aqara LUMI lumi.airmonitor.acn01.""" signature = { # MODELS_INFO: [(LUMI, "lumi.airmonitor.acn01")], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.IAS_ZONE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, IasZone.cluster_id, PowerConfiguration.cluster_id, ], OUTPUT_CLUSTERS: [Ota.cluster_id], } }, } replacement = TVOCMonitor.replacement