Home Assistant Unofficial Reference 2024.12.1
fan.py
Go to the documentation of this file.
1 """Support for TPLink Fan devices."""
2 
3 import logging
4 import math
5 from typing import Any
6 
7 from kasa import Device, Module
8 from kasa.interfaces import Fan as FanInterface
9 
10 from homeassistant.components.fan import FanEntity, FanEntityFeature
11 from homeassistant.core import HomeAssistant, callback
12 from homeassistant.helpers.entity_platform import AddEntitiesCallback
14  percentage_to_ranged_value,
15  ranged_value_to_percentage,
16 )
17 from homeassistant.util.scaling import int_states_in_range
18 
19 from . import TPLinkConfigEntry
20 from .coordinator import TPLinkDataUpdateCoordinator
21 from .entity import CoordinatedTPLinkEntity, async_refresh_after
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 
27  hass: HomeAssistant,
28  config_entry: TPLinkConfigEntry,
29  async_add_entities: AddEntitiesCallback,
30 ) -> None:
31  """Set up fans."""
32  data = config_entry.runtime_data
33  parent_coordinator = data.parent_coordinator
34  device = parent_coordinator.device
35  entities: list[CoordinatedTPLinkEntity] = []
36  if Module.Fan in device.modules:
37  entities.append(
39  device, parent_coordinator, fan_module=device.modules[Module.Fan]
40  )
41  )
42  entities.extend(
44  child,
45  parent_coordinator,
46  fan_module=child.modules[Module.Fan],
47  parent=device,
48  )
49  for child in device.children
50  if Module.Fan in child.modules
51  )
52  async_add_entities(entities)
53 
54 
55 SPEED_RANGE = (1, 4) # off is not included
56 
57 
59  """Representation of a fan for a TPLink Fan device."""
60 
61  _attr_speed_count = int_states_in_range(SPEED_RANGE)
62  _attr_supported_features = (
63  FanEntityFeature.SET_SPEED
64  | FanEntityFeature.TURN_OFF
65  | FanEntityFeature.TURN_ON
66  )
67  _enable_turn_on_off_backwards_compatibility = False
68 
69  def __init__(
70  self,
71  device: Device,
72  coordinator: TPLinkDataUpdateCoordinator,
73  fan_module: FanInterface,
74  parent: Device | None = None,
75  ) -> None:
76  """Initialize the fan."""
77  self.fan_modulefan_module = fan_module
78  # If _attr_name is None the entity name will be the device name
79  self._attr_name_attr_name = None if parent is None else device.alias
80 
81  super().__init__(device, coordinator, parent=parent)
82 
83  @async_refresh_after
84  async def async_turn_on(
85  self,
86  percentage: int | None = None,
87  preset_mode: str | None = None,
88  **kwargs: Any,
89  ) -> None:
90  """Turn on the fan."""
91  if percentage is not None:
92  value_in_range = math.ceil(
93  percentage_to_ranged_value(SPEED_RANGE, percentage)
94  )
95  else:
96  value_in_range = SPEED_RANGE[1]
97  await self.fan_modulefan_module.set_fan_speed_level(value_in_range)
98 
99  @async_refresh_after
100  async def async_turn_off(self, **kwargs: Any) -> None:
101  """Turn the fan off."""
102  await self.fan_modulefan_module.set_fan_speed_level(0)
103 
104  async def async_set_percentage(self, percentage: int) -> None:
105  """Set the speed percentage of the fan."""
106  value_in_range = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
107  await self.fan_modulefan_module.set_fan_speed_level(value_in_range)
108 
109  @callback
110  def _async_update_attrs(self) -> None:
111  """Update the entity's attributes."""
112  fan_speed = self.fan_modulefan_module.fan_speed_level
113  self._attr_is_on_attr_is_on = fan_speed != 0
114  if self._attr_is_on_attr_is_on:
115  self._attr_percentage_attr_percentage = ranged_value_to_percentage(SPEED_RANGE, fan_speed)
116  else:
117  self._attr_percentage_attr_percentage = None
float percentage_to_ranged_value(tuple[float, float] low_high_range, float percentage)
Definition: percentage.py:81
int ranged_value_to_percentage(tuple[float, float] low_high_range, float value)
Definition: percentage.py:64
int int_states_in_range(tuple[float, float] low_high_range)
Definition: scaling.py:61