diff --git a/.coveragerc b/.coveragerc index c72400392b7..d9cb511e86e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1009,6 +1009,7 @@ omit = homeassistant/components/renson/const.py homeassistant/components/renson/entity.py homeassistant/components/renson/sensor.py + homeassistant/components/renson/fan.py homeassistant/components/renson/binary_sensor.py homeassistant/components/raspyrfm/* homeassistant/components/recollect_waste/sensor.py diff --git a/homeassistant/components/renson/__init__.py b/homeassistant/components/renson/__init__.py index 86dfdc1f18b..dbc0468a11a 100644 --- a/homeassistant/components/renson/__init__.py +++ b/homeassistant/components/renson/__init__.py @@ -21,6 +21,7 @@ _LOGGER = logging.getLogger(__name__) PLATFORMS = [ Platform.BINARY_SENSOR, + Platform.FAN, Platform.SENSOR, ] diff --git a/homeassistant/components/renson/fan.py b/homeassistant/components/renson/fan.py new file mode 100644 index 00000000000..0fe639d40ec --- /dev/null +++ b/homeassistant/components/renson/fan.py @@ -0,0 +1,118 @@ +"""Platform to control a Renson ventilation unit.""" +from __future__ import annotations + +import logging +import math +from typing import Any + +from renson_endura_delta.field_enum import CURRENT_LEVEL_FIELD, DataType +from renson_endura_delta.renson import Level, RensonVentilation + +from homeassistant.components.fan import FanEntity, FanEntityFeature +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util.percentage import ( + int_states_in_range, + percentage_to_ranged_value, + ranged_value_to_percentage, +) + +from . import RensonCoordinator +from .const import DOMAIN +from .entity import RensonEntity + +_LOGGER = logging.getLogger(__name__) + +CMD_MAPPING = { + 0: Level.HOLIDAY, + 1: Level.LEVEL1, + 2: Level.LEVEL2, + 3: Level.LEVEL3, + 4: Level.LEVEL4, +} + +SPEED_MAPPING = { + Level.OFF.value: 0, + Level.HOLIDAY.value: 0, + Level.LEVEL1.value: 1, + Level.LEVEL2.value: 2, + Level.LEVEL3.value: 3, + Level.LEVEL4.value: 4, +} + + +SPEED_RANGE: tuple[float, float] = (1, 4) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Renson fan platform.""" + + api: RensonVentilation = hass.data[DOMAIN][config_entry.entry_id].api + coordinator: RensonCoordinator = hass.data[DOMAIN][ + config_entry.entry_id + ].coordinator + + async_add_entities([RensonFan(api, coordinator)]) + + +class RensonFan(RensonEntity, FanEntity): + """Representation of the Renson fan platform.""" + + _attr_icon = "mdi:air-conditioner" + _attr_has_entity_name = True + _attr_name = None + _attr_supported_features = FanEntityFeature.SET_SPEED + + def __init__(self, api: RensonVentilation, coordinator: RensonCoordinator) -> None: + """Initialize the Renson fan.""" + super().__init__("fan", api, coordinator) + self._attr_speed_count = int_states_in_range(SPEED_RANGE) + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + level = self.api.parse_value( + self.api.get_field_value(self.coordinator.data, CURRENT_LEVEL_FIELD.name), + DataType.LEVEL, + ) + + self._attr_percentage = ranged_value_to_percentage( + SPEED_RANGE, SPEED_MAPPING[level] + ) + + super()._handle_coordinator_update() + + async def async_turn_on( + self, + percentage: int | None = None, + preset_mode: str | None = None, + **kwargs: Any, + ) -> None: + """Turn on the fan.""" + if percentage is None: + percentage = 1 + + await self.async_set_percentage(percentage) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off the fan (to away).""" + await self.async_set_percentage(0) + + async def async_set_percentage(self, percentage: int) -> None: + """Set fan speed percentage.""" + _LOGGER.debug("Changing fan speed percentage to %s", percentage) + + if percentage == 0: + cmd = Level.HOLIDAY + else: + speed = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage)) + cmd = CMD_MAPPING[speed] + + await self.hass.async_add_executor_job(self.api.set_manual_level, cmd) + + await self.coordinator.async_request_refresh()