"""Matter sensors.""" from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from functools import partial from typing import Any from chip.clusters import Objects as clusters from chip.clusters.Types import Nullable, NullValue from matter_server.common.models import device_types from matter_server.common.models.device_type_instance import MatterDeviceTypeInstance from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( LIGHT_LUX, PERCENTAGE, Platform, UnitOfPressure, UnitOfTemperature, UnitOfVolumeFlowRate, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from .entity import MatterEntity, MatterEntityDescriptionBaseClass from .helpers import get_matter async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up Matter sensors from Config Entry.""" matter = get_matter(hass) matter.register_platform_handler(Platform.SENSOR, async_add_entities) class MatterSensor(MatterEntity, SensorEntity): """Representation of a Matter sensor.""" _attr_state_class = SensorStateClass.MEASUREMENT entity_description: MatterSensorEntityDescription @callback def _update_from_device(self) -> None: """Update from device.""" measurement: Nullable | float | None measurement = _get_attribute_value( self._device_type_instance, # We always subscribe to a single value self.entity_description.subscribe_attributes[0], ) if measurement is NullValue or measurement is None: measurement = None else: measurement = self.entity_description.measurement_to_ha(measurement) self._attr_native_value = measurement def _get_attribute_value( device_type_instance: MatterDeviceTypeInstance, attribute: clusters.ClusterAttributeDescriptor, ) -> Any: """Return the value of an attribute.""" # Find the cluster for this attribute. We don't have a lookup table yet. cluster_cls: clusters.Cluster = next( cluster for cluster in device_type_instance.device_type.clusters if cluster.id == attribute.cluster_id ) # Find the attribute descriptor so we know the instance variable to fetch attribute_descriptor: clusters.ClusterObjectFieldDescriptor = next( descriptor for descriptor in cluster_cls.descriptor.Fields if descriptor.Tag == attribute.attribute_id ) cluster_data = device_type_instance.get_cluster(cluster_cls) return getattr(cluster_data, attribute_descriptor.Label) @dataclass class MatterSensorEntityDescriptionMixin: """Required fields for sensor device mapping.""" measurement_to_ha: Callable[[float], float] @dataclass class MatterSensorEntityDescription( SensorEntityDescription, MatterEntityDescriptionBaseClass, MatterSensorEntityDescriptionMixin, ): """Matter Sensor entity description.""" # You can't set default values on inherited data classes MatterSensorEntityDescriptionFactory = partial( MatterSensorEntityDescription, entity_cls=MatterSensor ) DEVICE_ENTITY: dict[ type[device_types.DeviceType], MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass], ] = { device_types.TemperatureSensor: MatterSensorEntityDescriptionFactory( key=device_types.TemperatureSensor, name="Temperature", measurement_to_ha=lambda x: x / 100, subscribe_attributes=( clusters.TemperatureMeasurement.Attributes.MeasuredValue, ), native_unit_of_measurement=UnitOfTemperature.CELSIUS, device_class=SensorDeviceClass.TEMPERATURE, ), device_types.PressureSensor: MatterSensorEntityDescriptionFactory( key=device_types.PressureSensor, name="Pressure", measurement_to_ha=lambda x: x / 10, subscribe_attributes=(clusters.PressureMeasurement.Attributes.MeasuredValue,), native_unit_of_measurement=UnitOfPressure.KPA, device_class=SensorDeviceClass.PRESSURE, ), device_types.FlowSensor: MatterSensorEntityDescriptionFactory( key=device_types.FlowSensor, name="Flow", measurement_to_ha=lambda x: x / 10, subscribe_attributes=(clusters.FlowMeasurement.Attributes.MeasuredValue,), native_unit_of_measurement=UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR, ), device_types.HumiditySensor: MatterSensorEntityDescriptionFactory( key=device_types.HumiditySensor, name="Humidity", measurement_to_ha=lambda x: x / 100, subscribe_attributes=( clusters.RelativeHumidityMeasurement.Attributes.MeasuredValue, ), native_unit_of_measurement=PERCENTAGE, device_class=SensorDeviceClass.HUMIDITY, ), device_types.LightSensor: MatterSensorEntityDescriptionFactory( key=device_types.LightSensor, name="Light", measurement_to_ha=lambda x: round(pow(10, ((x - 1) / 10000)), 1), subscribe_attributes=( clusters.IlluminanceMeasurement.Attributes.MeasuredValue, ), native_unit_of_measurement=LIGHT_LUX, device_class=SensorDeviceClass.ILLUMINANCE, ), }