core/homeassistant/components/lg_thinq/fan.py

215 lines
7.0 KiB
Python

"""Support for fan entities."""
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any
from thinqconnect import DeviceType
from thinqconnect.devices.const import Property as ThinQProperty
from thinqconnect.integration import ActiveMode
from homeassistant.components.fan import (
FanEntity,
FanEntityDescription,
FanEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.percentage import (
ordered_list_item_to_percentage,
percentage_to_ordered_list_item,
)
from . import ThinqConfigEntry
from .coordinator import DeviceDataUpdateCoordinator
from .entity import ThinQEntity
@dataclass(frozen=True, kw_only=True)
class ThinQFanEntityDescription(FanEntityDescription):
"""Describes ThinQ fan entity."""
operation_key: str
preset_modes: list[str] | None = None
DEVICE_TYPE_FAN_MAP: dict[DeviceType, tuple[ThinQFanEntityDescription, ...]] = {
DeviceType.CEILING_FAN: (
ThinQFanEntityDescription(
key=ThinQProperty.WIND_STRENGTH,
name=None,
operation_key=ThinQProperty.CEILING_FAN_OPERATION_MODE,
),
),
DeviceType.VENTILATOR: (
ThinQFanEntityDescription(
key=ThinQProperty.WIND_STRENGTH,
name=None,
translation_key=ThinQProperty.WIND_STRENGTH,
operation_key=ThinQProperty.VENTILATOR_OPERATION_MODE,
preset_modes=["auto"],
),
),
}
ORDERED_NAMED_FAN_SPEEDS = ["low", "mid", "high", "turbo", "power"]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ThinqConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up an entry for fan platform."""
entities: list[ThinQFanEntity] = []
for coordinator in entry.runtime_data.coordinators.values():
if (
descriptions := DEVICE_TYPE_FAN_MAP.get(coordinator.api.device.device_type)
) is not None:
for description in descriptions:
entities.extend(
ThinQFanEntity(coordinator, description, property_id)
for property_id in coordinator.api.get_active_idx(
description.key, ActiveMode.READ_WRITE
)
)
if entities:
async_add_entities(entities)
class ThinQFanEntity(ThinQEntity, FanEntity):
"""Represent a thinq fan platform."""
def __init__(
self,
coordinator: DeviceDataUpdateCoordinator,
entity_description: ThinQFanEntityDescription,
property_id: str,
) -> None:
"""Initialize fan platform."""
super().__init__(coordinator, entity_description, property_id)
self._ordered_named_fan_speeds = ORDERED_NAMED_FAN_SPEEDS.copy()
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.TURN_ON
| FanEntityFeature.TURN_OFF
)
self._attr_preset_modes = []
for option in self.data.options:
if (
entity_description.preset_modes is not None
and option in entity_description.preset_modes
):
self._attr_supported_features |= FanEntityFeature.PRESET_MODE
self._attr_preset_modes.append(option)
else:
for ordered_step in ORDERED_NAMED_FAN_SPEEDS:
if (
ordered_step in self._ordered_named_fan_speeds
and ordered_step not in self.data.options
):
self._ordered_named_fan_speeds.remove(ordered_step)
self._attr_speed_count = len(self._ordered_named_fan_speeds)
self._operation_id = entity_description.operation_key
def _update_status(self) -> None:
"""Update status itself."""
super()._update_status()
# Update power on state.
self._attr_is_on = _is_on = self.coordinator.data[self._operation_id].is_on
# Update fan speed.
if _is_on and (mode := self.data.value) is not None:
if self.preset_modes is not None and mode in self.preset_modes:
self._attr_preset_mode = mode
self._attr_percentage = 0
elif mode in self._ordered_named_fan_speeds:
self._attr_percentage = ordered_list_item_to_percentage(
self._ordered_named_fan_speeds, mode
)
self._attr_preset_mode = None
else:
self._attr_preset_mode = None
self._attr_percentage = 0
_LOGGER.debug(
"[%s:%s] update status: is_on=%s, percentage=%s, preset_mode=%s",
self.coordinator.device_name,
self.property_id,
_is_on,
self.percentage,
self.preset_mode,
)
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan."""
_LOGGER.debug(
"[%s:%s] async_set_preset_mode. preset_mode=%s",
self.coordinator.device_name,
self.property_id,
preset_mode,
)
await self.async_call_api(
self.coordinator.api.post(self.property_id, preset_mode)
)
async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed percentage of the fan."""
if percentage == 0:
await self.async_turn_off()
return
try:
value = percentage_to_ordered_list_item(
self._ordered_named_fan_speeds, percentage
)
except ValueError:
_LOGGER.exception("Failed to async_set_percentage")
return
_LOGGER.debug(
"[%s:%s] async_set_percentage. percentage=%s, value=%s",
self.coordinator.device_name,
self.property_id,
percentage,
value,
)
await self.async_call_api(self.coordinator.api.post(self.property_id, value))
async def async_turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any,
) -> None:
"""Turn on the fan."""
_LOGGER.debug(
"[%s:%s] async_turn_on percentage=%s, preset_mode=%s, kwargs=%s",
self.coordinator.device_name,
self._operation_id,
percentage,
preset_mode,
kwargs,
)
await self.async_call_api(
self.coordinator.api.async_turn_on(self._operation_id)
)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the fan off."""
_LOGGER.debug(
"[%s:%s] async_turn_off kwargs=%s",
self.coordinator.device_name,
self._operation_id,
kwargs,
)
await self.async_call_api(
self.coordinator.api.async_turn_off(self._operation_id)
)