diff --git a/homeassistant/components/bosch_shc/sensor.py b/homeassistant/components/bosch_shc/sensor.py index 14da3a4b92b..28f23cd9765 100644 --- a/homeassistant/components/bosch_shc/sensor.py +++ b/homeassistant/components/bosch_shc/sensor.py @@ -2,12 +2,17 @@ from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + from boschshcpy import SHCSession from boschshcpy.device import SHCDevice from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, + SensorEntityDescription, SensorStateClass, ) from homeassistant.config_entries import ConfigEntry @@ -20,341 +25,207 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType from .const import DATA_SESSION, DOMAIN from .entity import SHCEntity +@dataclass(frozen=True, kw_only=True) +class SHCSensorEntityDescription(SensorEntityDescription): + """Describes a SHC sensor.""" + + value_fn: Callable[[SHCDevice], StateType] + attributes_fn: Callable[[SHCDevice], dict[str, Any]] | None = None + + +TEMPERATURE_SENSOR = "temperature" +HUMIDITY_SENSOR = "humidity" +VALVE_TAPPET_SENSOR = "valvetappet" +PURITY_SENSOR = "purity" +AIR_QUALITY_SENSOR = "airquality" +TEMPERATURE_RATING_SENSOR = "temperature_rating" +HUMIDITY_RATING_SENSOR = "humidity_rating" +PURITY_RATING_SENSOR = "purity_rating" +POWER_SENSOR = "power" +ENERGY_SENSOR = "energy" +COMMUNICATION_QUALITY_SENSOR = "communication_quality" + +SENSOR_DESCRIPTIONS: dict[str, SHCSensorEntityDescription] = { + TEMPERATURE_SENSOR: SHCSensorEntityDescription( + key=TEMPERATURE_SENSOR, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfTemperature.CELSIUS, + value_fn=lambda device: device.temperature, + ), + HUMIDITY_SENSOR: SHCSensorEntityDescription( + key=HUMIDITY_SENSOR, + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda device: device.humidity, + ), + PURITY_SENSOR: SHCSensorEntityDescription( + key=PURITY_SENSOR, + translation_key=PURITY_SENSOR, + native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION, + value_fn=lambda device: device.purity, + ), + AIR_QUALITY_SENSOR: SHCSensorEntityDescription( + key=AIR_QUALITY_SENSOR, + translation_key="air_quality", + value_fn=lambda device: device.combined_rating.name, + attributes_fn=lambda device: { + "rating_description": device.description, + }, + ), + TEMPERATURE_RATING_SENSOR: SHCSensorEntityDescription( + key=TEMPERATURE_RATING_SENSOR, + translation_key=TEMPERATURE_RATING_SENSOR, + value_fn=lambda device: device.temperature_rating.name, + ), + COMMUNICATION_QUALITY_SENSOR: SHCSensorEntityDescription( + key=COMMUNICATION_QUALITY_SENSOR, + translation_key=COMMUNICATION_QUALITY_SENSOR, + value_fn=lambda device: device.communicationquality.name, + ), + HUMIDITY_RATING_SENSOR: SHCSensorEntityDescription( + key=HUMIDITY_RATING_SENSOR, + translation_key=HUMIDITY_RATING_SENSOR, + value_fn=lambda device: device.humidity_rating.name, + ), + PURITY_RATING_SENSOR: SHCSensorEntityDescription( + key=PURITY_RATING_SENSOR, + translation_key=PURITY_RATING_SENSOR, + value_fn=lambda device: device.purity_rating.name, + ), + POWER_SENSOR: SHCSensorEntityDescription( + key=POWER_SENSOR, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.WATT, + value_fn=lambda device: device.powerconsumption, + ), + ENERGY_SENSOR: SHCSensorEntityDescription( + key=ENERGY_SENSOR, + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + value_fn=lambda device: device.energyconsumption / 1000.0, + ), + VALVE_TAPPET_SENSOR: SHCSensorEntityDescription( + key=VALVE_TAPPET_SENSOR, + translation_key=VALVE_TAPPET_SENSOR, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda device: device.position, + attributes_fn=lambda device: { + "valve_tappet_state": device.valvestate.name, + }, + ), +} + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the SHC sensor platform.""" - entities: list[SensorEntity] = [] session: SHCSession = hass.data[DOMAIN][config_entry.entry_id][DATA_SESSION] - for sensor in session.device_helper.thermostats: - entities.append( - TemperatureSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - ValveTappetSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + entities: list[SensorEntity] = [ + SHCSensor( + device, + SENSOR_DESCRIPTIONS[sensor_type], + session.information.unique_id, + config_entry.entry_id, ) + for device in session.device_helper.thermostats + for sensor_type in (TEMPERATURE_SENSOR, VALVE_TAPPET_SENSOR) + ] - for sensor in session.device_helper.wallthermostats: - entities.append( - TemperatureSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - HumiditySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + entities.extend( + SHCSensor( + device, + SENSOR_DESCRIPTIONS[sensor_type], + session.information.unique_id, + config_entry.entry_id, ) + for device in session.device_helper.wallthermostats + for sensor_type in (TEMPERATURE_SENSOR, HUMIDITY_SENSOR) + ) - for sensor in session.device_helper.twinguards: - entities.append( - TemperatureSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + entities.extend( + SHCSensor( + device, + SENSOR_DESCRIPTIONS[sensor_type], + session.information.unique_id, + config_entry.entry_id, ) - entities.append( - HumiditySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - PuritySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - AirQualitySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - TemperatureRatingSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - HumidityRatingSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - PurityRatingSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + for device in session.device_helper.twinguards + for sensor_type in ( + TEMPERATURE_SENSOR, + HUMIDITY_SENSOR, + PURITY_SENSOR, + AIR_QUALITY_SENSOR, + TEMPERATURE_RATING_SENSOR, + HUMIDITY_RATING_SENSOR, + PURITY_RATING_SENSOR, ) + ) - for sensor in ( - session.device_helper.smart_plugs + session.device_helper.light_switches_bsm - ): - entities.append( - PowerSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + entities.extend( + SHCSensor( + device, + SENSOR_DESCRIPTIONS[sensor_type], + session.information.unique_id, + config_entry.entry_id, ) - entities.append( - EnergySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + for device in ( + session.device_helper.smart_plugs + session.device_helper.light_switches_bsm ) + for sensor_type in (POWER_SENSOR, ENERGY_SENSOR) + ) - for sensor in session.device_helper.smart_plugs_compact: - entities.append( - PowerSensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - EnergySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) - ) - entities.append( - CommunicationQualitySensor( - device=sensor, - parent_id=session.information.unique_id, - entry_id=config_entry.entry_id, - ) + entities.extend( + SHCSensor( + device, + SENSOR_DESCRIPTIONS[sensor_type], + session.information.unique_id, + config_entry.entry_id, ) + for device in session.device_helper.smart_plugs_compact + for sensor_type in (POWER_SENSOR, ENERGY_SENSOR, COMMUNICATION_QUALITY_SENSOR) + ) async_add_entities(entities) -class TemperatureSensor(SHCEntity, SensorEntity): - """Representation of an SHC temperature reporting sensor.""" +class SHCSensor(SHCEntity, SensorEntity): + """Representation of a SHC sensor.""" - _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + entity_description: SHCSensorEntityDescription - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC temperature reporting sensor.""" + def __init__( + self, + device: SHCDevice, + entity_description: SHCSensorEntityDescription, + parent_id: str, + entry_id: str, + ) -> None: + """Initialize sensor.""" super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_temperature" + self.entity_description = entity_description + self._attr_unique_id = f"{device.serial}_{entity_description.key}" @property - def native_value(self): + def native_value(self) -> StateType: """Return the state of the sensor.""" - return self._device.temperature - - -class HumiditySensor(SHCEntity, SensorEntity): - """Representation of an SHC humidity reporting sensor.""" - - _attr_device_class = SensorDeviceClass.HUMIDITY - _attr_native_unit_of_measurement = PERCENTAGE - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC humidity reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_humidity" + return self.entity_description.value_fn(self._device) @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.humidity - - -class PuritySensor(SHCEntity, SensorEntity): - """Representation of an SHC purity reporting sensor.""" - - _attr_translation_key = "purity" - _attr_native_unit_of_measurement = CONCENTRATION_PARTS_PER_MILLION - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC purity reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_purity" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.purity - - -class AirQualitySensor(SHCEntity, SensorEntity): - """Representation of an SHC airquality reporting sensor.""" - - _attr_translation_key = "air_quality" - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC airquality reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_airquality" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.combined_rating.name - - @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, Any] | None: """Return the state attributes.""" - return { - "rating_description": self._device.description, - } - - -class TemperatureRatingSensor(SHCEntity, SensorEntity): - """Representation of an SHC temperature rating sensor.""" - - _attr_translation_key = "temperature_rating" - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC temperature rating sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_temperature_rating" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.temperature_rating.name - - -class CommunicationQualitySensor(SHCEntity, SensorEntity): - """Representation of an SHC communication quality reporting sensor.""" - - _attr_translation_key = "communication_quality" - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC communication quality reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_communication_quality" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.communicationquality.name - - -class HumidityRatingSensor(SHCEntity, SensorEntity): - """Representation of an SHC humidity rating sensor.""" - - _attr_translation_key = "humidity_rating" - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC humidity rating sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_humidity_rating" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.humidity_rating.name - - -class PurityRatingSensor(SHCEntity, SensorEntity): - """Representation of an SHC purity rating sensor.""" - - _attr_translation_key = "purity_rating" - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC purity rating sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_purity_rating" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.purity_rating.name - - -class PowerSensor(SHCEntity, SensorEntity): - """Representation of an SHC power reporting sensor.""" - - _attr_device_class = SensorDeviceClass.POWER - _attr_native_unit_of_measurement = UnitOfPower.WATT - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC power reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_power" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.powerconsumption - - -class EnergySensor(SHCEntity, SensorEntity): - """Representation of an SHC energy reporting sensor.""" - - _attr_device_class = SensorDeviceClass.ENERGY - _attr_state_class = SensorStateClass.TOTAL_INCREASING - _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC energy reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{self._device.serial}_energy" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.energyconsumption / 1000.0 - - -class ValveTappetSensor(SHCEntity, SensorEntity): - """Representation of an SHC valve tappet reporting sensor.""" - - _attr_translation_key = "valvetappet" - _attr_state_class = SensorStateClass.MEASUREMENT - _attr_native_unit_of_measurement = PERCENTAGE - - def __init__(self, device: SHCDevice, parent_id: str, entry_id: str) -> None: - """Initialize an SHC valve tappet reporting sensor.""" - super().__init__(device, parent_id, entry_id) - self._attr_unique_id = f"{device.serial}_valvetappet" - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._device.position - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - return { - "valve_tappet_state": self._device.valvestate.name, - } + if self.entity_description.attributes_fn is not None: + return self.entity_description.attributes_fn(self._device) + return None