Home Assistant Unofficial Reference 2024.12.1
fan.py
Go to the documentation of this file.
1 """Support for KNX/IP fans."""
2 
3 from __future__ import annotations
4 
5 import math
6 from typing import Any, Final
7 
8 from xknx.devices import Fan as XknxFan
9 
10 from homeassistant import config_entries
11 from homeassistant.components.fan import FanEntity, FanEntityFeature
12 from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, Platform
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.entity_platform import AddEntitiesCallback
15 from homeassistant.helpers.typing import ConfigType
17  percentage_to_ranged_value,
18  ranged_value_to_percentage,
19 )
20 from homeassistant.util.scaling import int_states_in_range
21 
22 from . import KNXModule
23 from .const import KNX_ADDRESS, KNX_MODULE_KEY
24 from .entity import KnxYamlEntity
25 from .schema import FanSchema
26 
27 DEFAULT_PERCENTAGE: Final = 50
28 
29 
31  hass: HomeAssistant,
32  config_entry: config_entries.ConfigEntry,
33  async_add_entities: AddEntitiesCallback,
34 ) -> None:
35  """Set up fan(s) for KNX platform."""
36  knx_module = hass.data[KNX_MODULE_KEY]
37  config: list[ConfigType] = knx_module.config_yaml[Platform.FAN]
38 
39  async_add_entities(KNXFan(knx_module, entity_config) for entity_config in config)
40 
41 
43  """Representation of a KNX fan."""
44 
45  _device: XknxFan
46  _enable_turn_on_off_backwards_compatibility = False
47 
48  def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
49  """Initialize of KNX fan."""
50  max_step = config.get(FanSchema.CONF_MAX_STEP)
51  super().__init__(
52  knx_module=knx_module,
53  device=XknxFan(
54  xknx=knx_module.xknx,
55  name=config[CONF_NAME],
56  group_address_speed=config.get(KNX_ADDRESS),
57  group_address_speed_state=config.get(FanSchema.CONF_STATE_ADDRESS),
58  group_address_oscillation=config.get(
59  FanSchema.CONF_OSCILLATION_ADDRESS
60  ),
61  group_address_oscillation_state=config.get(
62  FanSchema.CONF_OSCILLATION_STATE_ADDRESS
63  ),
64  max_step=max_step,
65  ),
66  )
67  # FanSpeedMode.STEP if max_step is set
68  self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None
69  self._attr_entity_category_attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
70 
71  self._attr_unique_id_attr_unique_id = str(self._device_device.speed.group_address)
72 
73  async def async_set_percentage(self, percentage: int) -> None:
74  """Set the speed of the fan, as a percentage."""
75  if self._step_range:
76  step = math.ceil(percentage_to_ranged_value(self._step_range, percentage))
77  await self._device_device.set_speed(step)
78  else:
79  await self._device_device.set_speed(percentage)
80 
81  @property
82  def supported_features(self) -> FanEntityFeature:
83  """Flag supported features."""
84  flags = (
85  FanEntityFeature.SET_SPEED
86  | FanEntityFeature.TURN_ON
87  | FanEntityFeature.TURN_OFF
88  )
89 
90  if self._device_device.supports_oscillation:
91  flags |= FanEntityFeature.OSCILLATE
92 
93  return flags
94 
95  @property
96  def percentage(self) -> int | None:
97  """Return the current speed as a percentage."""
98  if self._device_device.current_speed is None:
99  return None
100 
101  if self._step_range:
103  self._step_range, self._device_device.current_speed
104  )
105  return self._device_device.current_speed
106 
107  @property
108  def speed_count(self) -> int:
109  """Return the number of speeds the fan supports."""
110  if self._step_range is None:
111  return super().speed_count
112  return int_states_in_range(self._step_range)
113 
114  async def async_turn_on(
115  self,
116  percentage: int | None = None,
117  preset_mode: str | None = None,
118  **kwargs: Any,
119  ) -> None:
120  """Turn on the fan."""
121  if percentage is None:
122  await self.async_set_percentageasync_set_percentageasync_set_percentage(DEFAULT_PERCENTAGE)
123  else:
124  await self.async_set_percentageasync_set_percentageasync_set_percentage(percentage)
125 
126  async def async_turn_off(self, **kwargs: Any) -> None:
127  """Turn the fan off."""
128  await self.async_set_percentageasync_set_percentageasync_set_percentage(0)
129 
130  async def async_oscillate(self, oscillating: bool) -> None:
131  """Oscillate the fan."""
132  await self._device_device.set_oscillation(oscillating)
133 
134  @property
135  def oscillating(self) -> bool | None:
136  """Return whether or not the fan is currently oscillating."""
137  return self._device_device.current_oscillation
None async_set_percentage(self, int percentage)
Definition: __init__.py:340
None async_turn_off(self, **Any kwargs)
Definition: fan.py:126
FanEntityFeature supported_features(self)
Definition: fan.py:82
None async_set_percentage(self, int percentage)
Definition: fan.py:73
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
Definition: fan.py:119
None __init__(self, KNXModule knx_module, ConfigType config)
Definition: fan.py:48
None async_oscillate(self, bool oscillating)
Definition: fan.py:130
None async_setup_entry(HomeAssistant hass, config_entries.ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: fan.py:34
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