From 2efc75fdf5ba33a93dd00809c00f99d5b3e530fb Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 2 Jan 2025 07:31:54 +0100 Subject: [PATCH] Add base entity to Mill (#134415) --- homeassistant/components/mill/climate.py | 56 +++++++----------------- homeassistant/components/mill/entity.py | 54 +++++++++++++++++++++++ homeassistant/components/mill/number.py | 30 ++----------- homeassistant/components/mill/sensor.py | 47 ++++++++------------ 4 files changed, 93 insertions(+), 94 deletions(-) create mode 100644 homeassistant/components/mill/entity.py diff --git a/homeassistant/components/mill/climate.py b/homeassistant/components/mill/climate.py index 4f700d24e1b..0df2fe9335e 100644 --- a/homeassistant/components/mill/climate.py +++ b/homeassistant/components/mill/climate.py @@ -41,6 +41,7 @@ from .const import ( SERVICE_SET_ROOM_TEMP, ) from .coordinator import MillDataUpdateCoordinator +from .entity import MillBaseEntity SET_ROOM_TEMP_SCHEMA = vol.Schema( { @@ -85,10 +86,9 @@ async def async_setup_entry( ) -class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity): +class MillHeater(MillBaseEntity, ClimateEntity): """Representation of a Mill Thermostat device.""" - _attr_has_entity_name = True _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] _attr_max_temp = MAX_TEMP _attr_min_temp = MIN_TEMP @@ -102,24 +102,13 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity): _attr_temperature_unit = UnitOfTemperature.CELSIUS def __init__( - self, coordinator: MillDataUpdateCoordinator, heater: mill.Heater + self, coordinator: MillDataUpdateCoordinator, device: mill.Heater ) -> None: """Initialize the thermostat.""" - super().__init__(coordinator) - - self._available = False - - self._id = heater.device_id - self._attr_unique_id = heater.device_id - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, heater.device_id)}, - manufacturer=MANUFACTURER, - model=heater.model, - name=heater.name, - ) - - self._update_attr(heater) + super().__init__(coordinator, device) + self._attr_unique_id = device.device_id + self._update_attr(device) async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" @@ -143,36 +132,25 @@ class MillHeater(CoordinatorEntity[MillDataUpdateCoordinator], ClimateEntity): ) await self.coordinator.async_request_refresh() - @property - def available(self) -> bool: - """Return True if entity is available.""" - return super().available and self._available - @callback - def _handle_coordinator_update(self) -> None: - """Handle updated data from the coordinator.""" - self._update_attr(self.coordinator.data[self._id]) - self.async_write_ha_state() - - @callback - def _update_attr(self, heater): - self._available = heater.available + def _update_attr(self, device: mill.Heater) -> None: + self._available = device.available self._attr_extra_state_attributes = { - "open_window": heater.open_window, - "controlled_by_tibber": heater.tibber_control, + "open_window": device.open_window, + "controlled_by_tibber": device.tibber_control, } - if heater.room_name: - self._attr_extra_state_attributes["room"] = heater.room_name - self._attr_extra_state_attributes["avg_room_temp"] = heater.room_avg_temp + if device.room_name: + self._attr_extra_state_attributes["room"] = device.room_name + self._attr_extra_state_attributes["avg_room_temp"] = device.room_avg_temp else: self._attr_extra_state_attributes["room"] = "Independent device" - self._attr_target_temperature = heater.set_temp - self._attr_current_temperature = heater.current_temp - if heater.is_heating: + self._attr_target_temperature = device.set_temp + self._attr_current_temperature = device.current_temp + if device.is_heating: self._attr_hvac_action = HVACAction.HEATING else: self._attr_hvac_action = HVACAction.IDLE - if heater.power_status: + if device.power_status: self._attr_hvac_mode = HVACMode.HEAT else: self._attr_hvac_mode = HVACMode.OFF diff --git a/homeassistant/components/mill/entity.py b/homeassistant/components/mill/entity.py new file mode 100644 index 00000000000..f24dbeb2c26 --- /dev/null +++ b/homeassistant/components/mill/entity.py @@ -0,0 +1,54 @@ +"""Base entity for Mill devices.""" + +from __future__ import annotations + +from abc import abstractmethod + +from mill import Heater, MillDevice + +from homeassistant.core import callback +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, MANUFACTURER +from .coordinator import MillDataUpdateCoordinator + + +class MillBaseEntity(CoordinatorEntity[MillDataUpdateCoordinator]): + """Representation of a Mill number device.""" + + _attr_has_entity_name = True + + def __init__( + self, + coordinator: MillDataUpdateCoordinator, + mill_device: MillDevice, + ) -> None: + """Initialize the number.""" + super().__init__(coordinator) + + self._id = mill_device.device_id + self._available = False + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, mill_device.device_id)}, + name=mill_device.name, + manufacturer=MANUFACTURER, + model=mill_device.model, + ) + self._update_attr(mill_device) + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._update_attr(self.coordinator.data[self._id]) + self.async_write_ha_state() + + @abstractmethod + @callback + def _update_attr(self, device: MillDevice | Heater) -> None: + """Update the attribute of the entity.""" + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return super().available and self._available diff --git a/homeassistant/components/mill/number.py b/homeassistant/components/mill/number.py index e2425cfba5b..af27159caf0 100644 --- a/homeassistant/components/mill/number.py +++ b/homeassistant/components/mill/number.py @@ -8,12 +8,11 @@ from homeassistant.components.number import NumberDeviceClass, NumberEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_USERNAME, UnitOfPower from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import CLOUD, CONNECTION_TYPE, DOMAIN, MANUFACTURER +from .const import CLOUD, CONNECTION_TYPE, DOMAIN from .coordinator import MillDataUpdateCoordinator +from .entity import MillBaseEntity async def async_setup_entry( @@ -31,11 +30,10 @@ async def async_setup_entry( ) -class MillNumber(CoordinatorEntity[MillDataUpdateCoordinator], NumberEntity): +class MillNumber(MillBaseEntity, NumberEntity): """Representation of a Mill number device.""" _attr_device_class = NumberDeviceClass.POWER - _attr_has_entity_name = True _attr_native_max_value = 2000 _attr_native_min_value = 0 _attr_native_step = 1 @@ -47,25 +45,10 @@ class MillNumber(CoordinatorEntity[MillDataUpdateCoordinator], NumberEntity): mill_device: MillDevice, ) -> None: """Initialize the number.""" - super().__init__(coordinator) - - self._id = mill_device.device_id - self._available = False + super().__init__(coordinator, mill_device) self._attr_unique_id = f"{mill_device.device_id}_max_heating_power" - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, mill_device.device_id)}, - name=mill_device.name, - manufacturer=MANUFACTURER, - model=mill_device.model, - ) self._update_attr(mill_device) - @callback - def _handle_coordinator_update(self) -> None: - """Handle updated data from the coordinator.""" - self._update_attr(self.coordinator.data[self._id]) - self.async_write_ha_state() - @callback def _update_attr(self, device: MillDevice) -> None: self._attr_native_value = device.data["deviceSettings"]["reported"].get( @@ -73,11 +56,6 @@ class MillNumber(CoordinatorEntity[MillDataUpdateCoordinator], NumberEntity): ) self._available = device.available and self._attr_native_value is not None - @property - def available(self) -> bool: - """Return True if entity is available.""" - return super().available and self._available - async def async_set_native_value(self, value: float) -> None: """Set new value.""" await self.coordinator.mill_data_connection.max_heating_power(self._id, value) diff --git a/homeassistant/components/mill/sensor.py b/homeassistant/components/mill/sensor.py index c4b975ab039..018b9466deb 100644 --- a/homeassistant/components/mill/sensor.py +++ b/homeassistant/components/mill/sensor.py @@ -25,6 +25,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ( @@ -41,6 +42,8 @@ from .const import ( TEMPERATURE, TVOC, ) +from .coordinator import MillDataUpdateCoordinator +from .entity import MillBaseEntity HEATER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( @@ -179,37 +182,19 @@ async def async_setup_entry( async_add_entities(entities) -class MillSensor(CoordinatorEntity, SensorEntity): +class MillSensor(MillBaseEntity, SensorEntity): """Representation of a Mill Sensor device.""" - _attr_has_entity_name = True - - def __init__(self, coordinator, entity_description, mill_device): + def __init__( + self, + coordinator: MillDataUpdateCoordinator, + entity_description: SensorEntityDescription, + mill_device: mill.Socket | mill.Heater, + ) -> None: """Initialize the sensor.""" - super().__init__(coordinator) - - self._id = mill_device.device_id + super().__init__(coordinator, mill_device) self.entity_description = entity_description - self._available = False self._attr_unique_id = f"{mill_device.device_id}_{entity_description.key}" - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, mill_device.device_id)}, - name=mill_device.name, - manufacturer=MANUFACTURER, - model=mill_device.model, - ) - self._update_attr(mill_device) - - @callback - def _handle_coordinator_update(self) -> None: - """Handle updated data from the coordinator.""" - self._update_attr(self.coordinator.data[self._id]) - self.async_write_ha_state() - - @property - def available(self) -> bool: - """Return True if entity is available.""" - return super().available and self._available @callback def _update_attr(self, device): @@ -217,12 +202,16 @@ class MillSensor(CoordinatorEntity, SensorEntity): self._attr_native_value = getattr(device, self.entity_description.key) -class LocalMillSensor(CoordinatorEntity, SensorEntity): +class LocalMillSensor(CoordinatorEntity[MillDataUpdateCoordinator], SensorEntity): """Representation of a Mill Sensor device.""" _attr_has_entity_name = True - def __init__(self, coordinator, entity_description): + def __init__( + self, + coordinator: MillDataUpdateCoordinator, + entity_description: SensorEntityDescription, + ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -239,6 +228,6 @@ class LocalMillSensor(CoordinatorEntity, SensorEntity): ) @property - def native_value(self): + def native_value(self) -> StateType: """Return the native value of the sensor.""" return self.coordinator.data[self.entity_description.key]