diff --git a/homeassistant/components/sht31/sensor.py b/homeassistant/components/sht31/sensor.py index e5f77700409..1415c4856b6 100644 --- a/homeassistant/components/sht31/sensor.py +++ b/homeassistant/components/sht31/sensor.py @@ -1,16 +1,24 @@ """Support for Sensirion SHT31 temperature and humidity sensor.""" +from __future__ import annotations +from dataclasses import dataclass from datetime import timedelta import logging import math +from typing import Callable from Adafruit_SHT31 import SHT31 import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity +from homeassistant.components.sensor import ( + PLATFORM_SCHEMA, + SensorEntity, + SensorEntityDescription, +) from homeassistant.const import ( CONF_MONITORED_CONDITIONS, CONF_NAME, + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, PERCENTAGE, TEMP_CELSIUS, @@ -25,9 +33,40 @@ CONF_I2C_ADDRESS = "i2c_address" DEFAULT_NAME = "SHT31" DEFAULT_I2C_ADDRESS = 0x44 -SENSOR_TEMPERATURE = "temperature" -SENSOR_HUMIDITY = "humidity" -SENSOR_TYPES = (SENSOR_TEMPERATURE, SENSOR_HUMIDITY) + +@dataclass +class SHT31RequiredKeysMixin: + """Mixin for required keys.""" + + value_fn: Callable[[SHTClient], float | None] + + +@dataclass +class SHT31SensorEntityDescription(SensorEntityDescription, SHT31RequiredKeysMixin): + """Describes SHT31 sensor entity.""" + + +SENSOR_TYPES = ( + SHT31SensorEntityDescription( + key="temperature", + name="Temperature", + device_class=DEVICE_CLASS_TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + value_fn=lambda sensor: sensor.temperature, + ), + SHT31SensorEntityDescription( + key="humidity", + name="Humidity", + device_class=DEVICE_CLASS_HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + value_fn=lambda sensor: ( + round(val) # pylint: disable=undefined-variable + if (val := sensor.humidity) + else None + ), + ), +) +SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES] MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=10) @@ -36,8 +75,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_I2C_ADDRESS, default=DEFAULT_I2C_ADDRESS): vol.All( vol.Coerce(int), vol.Range(min=0x44, max=0x45) ), - vol.Optional(CONF_MONITORED_CONDITIONS, default=list(SENSOR_TYPES)): vol.All( - cv.ensure_list, [vol.In(SENSOR_TYPES)] + vol.Optional(CONF_MONITORED_CONDITIONS, default=SENSOR_KEYS): vol.All( + cv.ensure_list, [vol.In(SENSOR_KEYS)] ), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, } @@ -46,8 +85,9 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the sensor platform.""" - - i2c_address = config.get(CONF_I2C_ADDRESS) + name = config[CONF_NAME] + monitored_conditions = config[CONF_MONITORED_CONDITIONS] + i2c_address = config[CONF_I2C_ADDRESS] sensor = SHT31(address=i2c_address) try: @@ -58,17 +98,13 @@ def setup_platform(hass, config, add_entities, discovery_info=None): return sensor_client = SHTClient(sensor) - sensor_classes = { - SENSOR_TEMPERATURE: SHTSensorTemperature, - SENSOR_HUMIDITY: SHTSensorHumidity, - } + entities = [ + SHTSensor(sensor_client, name, description) + for description in SENSOR_TYPES + if description.key in monitored_conditions + ] - devs = [] - for sensor_type, sensor_class in sensor_classes.items(): - name = f"{config.get(CONF_NAME)} {sensor_type.capitalize()}" - devs.append(sensor_class(sensor_client, name)) - - add_entities(devs) + add_entities(entities) class SHTClient: @@ -77,8 +113,8 @@ class SHTClient: def __init__(self, adafruit_sht): """Initialize the sensor.""" self.adafruit_sht = adafruit_sht - self.temperature = None - self.humidity = None + self.temperature: float | None = None + self.humidity: float | None = None @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): @@ -94,50 +130,16 @@ class SHTClient: class SHTSensor(SensorEntity): """An abstract SHTSensor, can be either temperature or humidity.""" - def __init__(self, sensor, name): + entity_description: SHT31SensorEntityDescription + + def __init__(self, sensor, name, description: SHT31SensorEntityDescription): """Initialize the sensor.""" + self.entity_description = description self._sensor = sensor - self._name = name - self._state = None - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._state + self._attr_name = f"{name} {description.name}" def update(self): """Fetch temperature and humidity from the sensor.""" self._sensor.update() - - -class SHTSensorTemperature(SHTSensor): - """Representation of a temperature sensor.""" - - _attr_device_class = DEVICE_CLASS_TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS - - def update(self): - """Fetch temperature from the sensor.""" - super().update() - self._state = self._sensor.temperature - - -class SHTSensorHumidity(SHTSensor): - """Representation of a humidity sensor.""" - - @property - def native_unit_of_measurement(self): - """Return the unit of measurement.""" - return PERCENTAGE - - def update(self): - """Fetch humidity from the sensor.""" - super().update() - humidity = self._sensor.humidity - if humidity is not None: - self._state = round(humidity) + self._attr_native_value = self.entity_description.value_fn(self._sensor)