From 6b1b8c9880172a440430eab3446118066c6f5013 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 26 Oct 2021 17:53:13 +0200 Subject: [PATCH] Fjaraskupan number entity for periodic venting (#58179) --- .coveragerc | 1 + .../components/fjaraskupan/__init__.py | 2 +- homeassistant/components/fjaraskupan/fan.py | 20 +++++- .../components/fjaraskupan/number.py | 70 +++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/fjaraskupan/number.py diff --git a/.coveragerc b/.coveragerc index 74da8acf8b8..fe268171b33 100644 --- a/.coveragerc +++ b/.coveragerc @@ -331,6 +331,7 @@ omit = homeassistant/components/fjaraskupan/const.py homeassistant/components/fjaraskupan/fan.py homeassistant/components/fjaraskupan/light.py + homeassistant/components/fjaraskupan/number.py homeassistant/components/fjaraskupan/sensor.py homeassistant/components/fleetgo/device_tracker.py homeassistant/components/flexit/climate.py diff --git a/homeassistant/components/fjaraskupan/__init__.py b/homeassistant/components/fjaraskupan/__init__.py index 56a67a14a02..f5cedad243d 100644 --- a/homeassistant/components/fjaraskupan/__init__.py +++ b/homeassistant/components/fjaraskupan/__init__.py @@ -23,7 +23,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import DISPATCH_DETECTION, DOMAIN -PLATFORMS = ["binary_sensor", "fan", "light", "sensor"] +PLATFORMS = ["binary_sensor", "fan", "light", "sensor", "number"] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/fjaraskupan/fan.py b/homeassistant/components/fjaraskupan/fan.py index 4a81e70b848..7cb7c7cd18e 100644 --- a/homeassistant/components/fjaraskupan/fan.py +++ b/homeassistant/components/fjaraskupan/fan.py @@ -35,10 +35,12 @@ ORDERED_NAMED_FAN_SPEEDS = ["1", "2", "3", "4", "5", "6", "7", "8"] PRESET_MODE_NORMAL = "normal" PRESET_MODE_AFTER_COOKING_MANUAL = "after_cooking_manual" PRESET_MODE_AFTER_COOKING_AUTO = "after_cooking_auto" +PRESET_MODE_PERIODIC_VENTILATION = "periodic_ventilation" PRESET_MODES = [ PRESET_MODE_NORMAL, PRESET_MODE_AFTER_COOKING_AUTO, PRESET_MODE_AFTER_COOKING_MANUAL, + PRESET_MODE_PERIODIC_VENTILATION, ] PRESET_TO_COMMAND = { @@ -48,6 +50,10 @@ PRESET_TO_COMMAND = { } +class UnsupportedPreset(Exception): + """The preset is unsupported.""" + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -112,7 +118,10 @@ class Fan(CoordinatorEntity[State], FanEntity): async with self._device: if preset_mode != self._preset_mode: - await self._device.send_command(PRESET_TO_COMMAND[preset_mode]) + if command := PRESET_TO_COMMAND.get(preset_mode): + await self._device.send_command(command) + else: + raise UnsupportedPreset(f"The preset {preset_mode} is unsupported") if preset_mode == PRESET_MODE_NORMAL: await self._device.send_fan_speed(int(new_speed)) @@ -125,8 +134,11 @@ class Fan(CoordinatorEntity[State], FanEntity): async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" - await self._device.send_command(PRESET_TO_COMMAND[preset_mode]) - self.coordinator.async_set_updated_data(self._device.state) + if command := PRESET_TO_COMMAND.get(preset_mode): + await self._device.send_command(command) + self.coordinator.async_set_updated_data(self._device.state) + else: + raise UnsupportedPreset(f"The preset {preset_mode} is unsupported") async def async_turn_off(self, **kwargs) -> None: """Turn the entity off.""" @@ -181,6 +193,8 @@ class Fan(CoordinatorEntity[State], FanEntity): self._preset_mode = PRESET_MODE_AFTER_COOKING_MANUAL else: self._preset_mode = PRESET_MODE_AFTER_COOKING_AUTO + elif data.periodic_venting_on: + self._preset_mode = PRESET_MODE_PERIODIC_VENTILATION else: self._preset_mode = PRESET_MODE_NORMAL diff --git a/homeassistant/components/fjaraskupan/number.py b/homeassistant/components/fjaraskupan/number.py new file mode 100644 index 00000000000..d5862bf2e7f --- /dev/null +++ b/homeassistant/components/fjaraskupan/number.py @@ -0,0 +1,70 @@ +"""Support for sensors.""" +from __future__ import annotations + +from fjaraskupan import Device, State + +from homeassistant.components.number import NumberEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ENTITY_CATEGORY_CONFIG, TIME_MINUTES +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import DeviceInfo, Entity +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) + +from . import DeviceState, async_setup_entry_platform + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up sensors dynamically through discovery.""" + + def _constructor(device_state: DeviceState) -> list[Entity]: + return [ + PeriodicVentingTime( + device_state.coordinator, device_state.device, device_state.device_info + ), + ] + + async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) + + +class PeriodicVentingTime(CoordinatorEntity[State], NumberEntity): + """Periodic Venting.""" + + _attr_max_value: float = 59 + _attr_min_value: float = 0 + _attr_step: float = 1 + _attr_entity_registry_enabled_default = True + _attr_entity_category = ENTITY_CATEGORY_CONFIG + _attr_unit_of_measurement = TIME_MINUTES + + def __init__( + self, + coordinator: DataUpdateCoordinator[State], + device: Device, + device_info: DeviceInfo, + ) -> None: + """Init sensor.""" + super().__init__(coordinator) + self._device = device + self._attr_unique_id = f"{device.address}-periodic-venting" + self._attr_device_info = device_info + self._attr_name = f"{device_info['name']} Periodic Venting" + + @property + def value(self) -> float | None: + """Return the entity value to represent the entity state.""" + if data := self.coordinator.data: + return data.periodic_venting + return None + + async def async_set_value(self, value: float) -> None: + """Set new value.""" + await self._device.send_periodic_venting(int(value)) + self.coordinator.async_set_updated_data(self._device.state)