Add fan for ventilator (#142444)

Add ventilator device

Co-authored-by: yunseon.park <yunseon.park@lge.com>
This commit is contained in:
LG-ThinQ-Integration 2025-05-15 00:58:25 +09:00 committed by GitHub
parent d44a34ce1e
commit 7963665c40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,11 +2,13 @@
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any
from thinqconnect import DeviceType
from thinqconnect.integration import ExtendedProperty
from thinqconnect.devices.const import Property as ThinQProperty
from thinqconnect.integration import ActiveMode
from homeassistant.components.fan import (
FanEntity,
@ -24,16 +26,35 @@ from . import ThinqConfigEntry
from .coordinator import DeviceDataUpdateCoordinator
from .entity import ThinQEntity
DEVICE_TYPE_FAN_MAP: dict[DeviceType, tuple[FanEntityDescription, ...]] = {
@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: (
FanEntityDescription(
key=ExtendedProperty.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"],
),
),
}
FOUR_STEP_SPEEDS = ["low", "mid", "high", "turbo"]
ORDERED_NAMED_FAN_SPEEDS = ["low", "mid", "high", "turbo", "power"]
_LOGGER = logging.getLogger(__name__)
@ -52,7 +73,9 @@ async def async_setup_entry(
for description in descriptions:
entities.extend(
ThinQFanEntity(coordinator, description, property_id)
for property_id in coordinator.api.get_active_idx(description.key)
for property_id in coordinator.api.get_active_idx(
description.key, ActiveMode.READ_WRITE
)
)
if entities:
@ -65,48 +88,76 @@ class ThinQFanEntity(ThinQEntity, FanEntity):
def __init__(
self,
coordinator: DeviceDataUpdateCoordinator,
entity_description: FanEntityDescription,
entity_description: ThinQFanEntityDescription,
property_id: str,
) -> None:
"""Initialize fan platform."""
super().__init__(coordinator, entity_description, property_id)
self._ordered_named_fan_speeds = []
self._ordered_named_fan_speeds = ORDERED_NAMED_FAN_SPEEDS.copy()
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.TURN_ON
| FanEntityFeature.TURN_OFF
)
if (fan_modes := self.data.fan_modes) is not None:
self._attr_speed_count = len(fan_modes)
if self.speed_count == 4:
self._ordered_named_fan_speeds = FOUR_STEP_SPEEDS
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 = self.data.is_on
self._attr_is_on = _is_on = self.coordinator.data[self._operation_id].is_on
# Update fan speed.
if (
self.data.is_on
and (mode := self.data.fan_mode) in self._ordered_named_fan_speeds
):
self._attr_percentage = ordered_list_item_to_percentage(
self._ordered_named_fan_speeds, mode
)
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: %s -> %s (percentage=%s)",
"[%s:%s] update status: is_on=%s, percentage=%s, preset_mode=%s",
self.coordinator.device_name,
self.property_id,
self.data.is_on,
self.is_on,
_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:
@ -129,9 +180,7 @@ class ThinQFanEntity(ThinQEntity, FanEntity):
percentage,
value,
)
await self.async_call_api(
self.coordinator.api.async_set_fan_mode(self.property_id, value)
)
await self.async_call_api(self.coordinator.api.post(self.property_id, value))
async def async_turn_on(
self,
@ -141,13 +190,25 @@ class ThinQFanEntity(ThinQEntity, FanEntity):
) -> None:
"""Turn on the fan."""
_LOGGER.debug(
"[%s:%s] async_turn_on", self.coordinator.device_name, self.property_id
"[%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)
)
await self.async_call_api(self.coordinator.api.async_turn_on(self.property_id))
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the fan off."""
_LOGGER.debug(
"[%s:%s] async_turn_off", self.coordinator.device_name, self.property_id
"[%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)
)
await self.async_call_api(self.coordinator.api.async_turn_off(self.property_id))