1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00
ha-core/homeassistant/components/fjaraskupan/fan.py

208 lines
6.6 KiB
Python

"""Support for Fjäråskupan fans."""
from __future__ import annotations
from fjaraskupan import (
COMMAND_AFTERCOOKINGTIMERAUTO,
COMMAND_AFTERCOOKINGTIMERMANUAL,
COMMAND_AFTERCOOKINGTIMEROFF,
COMMAND_STOP_FAN,
Device,
State,
)
from homeassistant.components.fan import (
SUPPORT_PRESET_MODE,
SUPPORT_SET_SPEED,
FanEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.util.percentage import (
ordered_list_item_to_percentage,
percentage_to_ordered_list_item,
)
from . import DeviceState, async_setup_entry_platform
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 = {
PRESET_MODE_AFTER_COOKING_MANUAL: COMMAND_AFTERCOOKINGTIMERMANUAL,
PRESET_MODE_AFTER_COOKING_AUTO: COMMAND_AFTERCOOKINGTIMERAUTO,
PRESET_MODE_NORMAL: COMMAND_AFTERCOOKINGTIMEROFF,
}
class UnsupportedPreset(HomeAssistantError):
"""The preset is unsupported."""
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):
return [
Fan(device_state.coordinator, device_state.device, device_state.device_info)
]
async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor)
class Fan(CoordinatorEntity[State], FanEntity):
"""Fan entity."""
def __init__(
self,
coordinator: DataUpdateCoordinator[State],
device: Device,
device_info: DeviceInfo,
) -> None:
"""Init fan entity."""
super().__init__(coordinator)
self._device = device
self._default_on_speed = 25
self._attr_name = device_info["name"]
self._attr_unique_id = device.address
self._attr_device_info = device_info
self._percentage = 0
self._preset_mode = PRESET_MODE_NORMAL
self._update_from_device_data(coordinator.data)
async def async_set_percentage(self, percentage: int) -> None:
"""Set speed."""
new_speed = percentage_to_ordered_list_item(
ORDERED_NAMED_FAN_SPEEDS, percentage
)
await self._device.send_fan_speed(int(new_speed))
self.coordinator.async_set_updated_data(self._device.state)
async def async_turn_on(
self,
speed: str = None,
percentage: int = None,
preset_mode: str = None,
**kwargs,
) -> None:
"""Turn on the fan."""
if preset_mode is None:
preset_mode = self._preset_mode
if percentage is None:
percentage = self._default_on_speed
new_speed = percentage_to_ordered_list_item(
ORDERED_NAMED_FAN_SPEEDS, percentage
)
async with self._device:
if preset_mode != self._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))
elif preset_mode == PRESET_MODE_AFTER_COOKING_MANUAL:
await self._device.send_after_cooking(int(new_speed))
elif preset_mode == PRESET_MODE_AFTER_COOKING_AUTO:
await self._device.send_after_cooking(0)
self.coordinator.async_set_updated_data(self._device.state)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
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."""
await self._device.send_command(COMMAND_STOP_FAN)
self.coordinator.async_set_updated_data(self._device.state)
@property
def speed_count(self) -> int:
"""Return the number of speeds the fan supports."""
return len(ORDERED_NAMED_FAN_SPEEDS)
@property
def percentage(self) -> int | None:
"""Return the current speed."""
return self._percentage
@property
def supported_features(self) -> int:
"""Flag supported features."""
return SUPPORT_SET_SPEED | SUPPORT_PRESET_MODE
@property
def is_on(self) -> bool:
"""Return true if fan is on."""
return self._percentage != 0
@property
def preset_mode(self) -> str | None:
"""Return the current preset mode."""
return self._preset_mode
@property
def preset_modes(self) -> list[str] | None:
"""Return a list of available preset modes."""
return PRESET_MODES
def _update_from_device_data(self, data: State | None) -> None:
"""Handle data update."""
if not data:
self._percentage = 0
return
if data.fan_speed:
self._percentage = ordered_list_item_to_percentage(
ORDERED_NAMED_FAN_SPEEDS, str(data.fan_speed)
)
else:
self._percentage = 0
if data.after_cooking_on:
if data.after_cooking_fan_speed:
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
@callback
def _handle_coordinator_update(self) -> None:
"""Handle data update."""
self._update_from_device_data(self.coordinator.data)
self.async_write_ha_state()