Home Assistant Unofficial Reference 2024.12.1
fan.py
Go to the documentation of this file.
1 """Platform to control a Renson ventilation unit."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import math
7 from typing import Any
8 
9 from renson_endura_delta.field_enum import (
10  BREEZE_LEVEL_FIELD,
11  BREEZE_TEMPERATURE_FIELD,
12  CURRENT_LEVEL_FIELD,
13  DataType,
14 )
15 from renson_endura_delta.renson import Level, RensonVentilation
16 import voluptuous as vol
17 
18 from homeassistant.components.fan import FanEntity, FanEntityFeature
19 from homeassistant.config_entries import ConfigEntry
20 from homeassistant.core import HomeAssistant, callback
21 from homeassistant.helpers import entity_platform
23 from homeassistant.helpers.entity_platform import AddEntitiesCallback
24 from homeassistant.helpers.typing import VolDictType
26  percentage_to_ranged_value,
27  ranged_value_to_percentage,
28 )
29 from homeassistant.util.scaling import int_states_in_range
30 
31 from .const import DOMAIN
32 from .coordinator import RensonCoordinator
33 from .entity import RensonEntity
34 
35 _LOGGER = logging.getLogger(__name__)
36 
37 CMD_MAPPING = {
38  0: Level.HOLIDAY,
39  1: Level.LEVEL1,
40  2: Level.LEVEL2,
41  3: Level.LEVEL3,
42  4: Level.LEVEL4,
43 }
44 
45 SPEED_MAPPING = {
46  Level.OFF.value: 0,
47  Level.HOLIDAY.value: 0,
48  Level.BREEZE.value: 0,
49  Level.LEVEL1.value: 1,
50  Level.LEVEL2.value: 2,
51  Level.LEVEL3.value: 3,
52  Level.LEVEL4.value: 4,
53 }
54 
55 SET_TIMER_LEVEL_SCHEMA: VolDictType = {
56  vol.Required("timer_level"): vol.In(
57  ["level1", "level2", "level3", "level4", "holiday", "breeze"]
58  ),
59  vol.Required("minutes"): cv.positive_int,
60 }
61 
62 SET_BREEZE_SCHEMA: VolDictType = {
63  vol.Required("breeze_level"): vol.In(["level1", "level2", "level3", "level4"]),
64  vol.Required("temperature"): cv.positive_int,
65  vol.Required("activate"): bool,
66 }
67 
68 SET_POLLUTION_SETTINGS_SCHEMA: VolDictType = {
69  vol.Required("day_pollution_level"): vol.In(
70  ["level1", "level2", "level3", "level4"]
71  ),
72  vol.Required("night_pollution_level"): vol.In(
73  ["level1", "level2", "level3", "level4"]
74  ),
75  vol.Optional("humidity_control", default=True): bool,
76  vol.Optional("airquality_control", default=True): bool,
77  vol.Optional("co2_control", default=True): bool,
78  vol.Optional("co2_threshold", default=600): cv.positive_int,
79  vol.Optional("co2_hysteresis", default=100): cv.positive_int,
80 }
81 
82 
83 SPEED_RANGE: tuple[float, float] = (1, 4)
84 
85 
87  hass: HomeAssistant,
88  config_entry: ConfigEntry,
89  async_add_entities: AddEntitiesCallback,
90 ) -> None:
91  """Set up the Renson fan platform."""
92 
93  api: RensonVentilation = hass.data[DOMAIN][config_entry.entry_id].api
94  coordinator: RensonCoordinator = hass.data[DOMAIN][
95  config_entry.entry_id
96  ].coordinator
97 
98  async_add_entities([RensonFan(api, coordinator)])
99 
100  platform = entity_platform.async_get_current_platform()
101 
102  platform.async_register_entity_service(
103  "set_timer_level",
104  SET_TIMER_LEVEL_SCHEMA,
105  "set_timer_level",
106  )
107 
108  platform.async_register_entity_service(
109  "set_breeze", SET_BREEZE_SCHEMA, "set_breeze"
110  )
111 
112  platform.async_register_entity_service(
113  "set_pollution_settings",
114  SET_POLLUTION_SETTINGS_SCHEMA,
115  "set_pollution_settings",
116  )
117 
118 
120  """Representation of the Renson fan platform."""
121 
122  _attr_has_entity_name = True
123  _attr_name = None
124  _attr_translation_key = "fan"
125  _attr_supported_features = (
126  FanEntityFeature.SET_SPEED
127  | FanEntityFeature.TURN_OFF
128  | FanEntityFeature.TURN_ON
129  )
130  _enable_turn_on_off_backwards_compatibility = False
131 
132  def __init__(self, api: RensonVentilation, coordinator: RensonCoordinator) -> None:
133  """Initialize the Renson fan."""
134  super().__init__("fan", api, coordinator)
135  self._attr_speed_count_attr_speed_count = int_states_in_range(SPEED_RANGE)
136 
137  @callback
138  def _handle_coordinator_update(self) -> None:
139  """Handle updated data from the coordinator."""
140  level = self.apiapiapi.parse_value(
141  self.apiapiapi.get_field_value(self.coordinator.data, CURRENT_LEVEL_FIELD.name),
142  DataType.LEVEL,
143  )
144 
145  if level == Level.BREEZE.value:
146  level = self.apiapiapi.parse_value(
147  self.apiapiapi.get_field_value(
148  self.coordinator.data, BREEZE_LEVEL_FIELD.name
149  ),
150  DataType.LEVEL,
151  )
152  else:
153  level = self.apiapiapi.parse_value(
154  self.apiapiapi.get_field_value(
155  self.coordinator.data, CURRENT_LEVEL_FIELD.name
156  ),
157  DataType.LEVEL,
158  )
159 
161  SPEED_RANGE, SPEED_MAPPING[level]
162  )
163 
165 
166  async def async_turn_on(
167  self,
168  percentage: int | None = None,
169  preset_mode: str | None = None,
170  **kwargs: Any,
171  ) -> None:
172  """Turn on the fan."""
173  if percentage is None:
174  percentage = 1
175 
176  await self.async_set_percentageasync_set_percentageasync_set_percentage(percentage)
177 
178  async def async_turn_off(self, **kwargs: Any) -> None:
179  """Turn off the fan (to away)."""
180  await self.async_set_percentageasync_set_percentageasync_set_percentage(0)
181 
182  async def async_set_percentage(self, percentage: int) -> None:
183  """Set fan speed percentage."""
184  _LOGGER.debug("Changing fan speed percentage to %s", percentage)
185 
186  level = self.apiapiapi.parse_value(
187  self.apiapiapi.get_field_value(self.coordinator.data, CURRENT_LEVEL_FIELD.name),
188  DataType.LEVEL,
189  )
190 
191  if percentage == 0:
192  cmd = Level.HOLIDAY
193  else:
194  speed = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
195  cmd = CMD_MAPPING[speed]
196 
197  if level == Level.BREEZE.value:
198  all_data = self.coordinator.data
199  breeze_temp = self.apiapiapi.get_field_value(all_data, BREEZE_TEMPERATURE_FIELD)
200  await self.hasshasshass.async_add_executor_job(
201  self.apiapiapi.set_breeze, cmd.name, breeze_temp, True
202  )
203  else:
204  await self.hasshasshass.async_add_executor_job(self.apiapiapi.set_manual_level, cmd)
205 
206  await self.coordinator.async_request_refresh()
207 
208  async def set_timer_level(self, timer_level: str, minutes: int) -> None:
209  """Set timer level."""
210  level = Level[str(timer_level).upper()]
211 
212  await self.hasshasshass.async_add_executor_job(self.apiapiapi.set_timer_level, level, minutes)
213 
214  async def set_breeze(
215  self, breeze_level: str, temperature: int, activate: bool
216  ) -> None:
217  """Configure breeze feature."""
218  level = Level[str(breeze_level).upper()]
219 
220  await self.hasshasshass.async_add_executor_job(
221  self.apiapiapi.set_breeze, level, temperature, activate
222  )
223 
225  self,
226  day_pollution_level: str,
227  night_pollution_level: str,
228  humidity_control: bool,
229  airquality_control: bool,
230  co2_control: str,
231  co2_threshold: int,
232  co2_hysteresis: int,
233  ) -> None:
234  """Configure pollutions settings."""
235  day = Level[str(day_pollution_level).upper()]
236  night = Level[str(night_pollution_level).upper()]
237 
238  await self.apiapiapi.set_pollution(
239  day,
240  night,
241  humidity_control,
242  airquality_control,
243  co2_control,
244  co2_threshold,
245  co2_hysteresis,
246  )
None async_set_percentage(self, int percentage)
Definition: __init__.py:340
None set_breeze(self, str breeze_level, int temperature, bool activate)
Definition: fan.py:216
None async_turn_off(self, **Any kwargs)
Definition: fan.py:178
None set_pollution_settings(self, str day_pollution_level, str night_pollution_level, bool humidity_control, bool airquality_control, str co2_control, int co2_threshold, int co2_hysteresis)
Definition: fan.py:233
None __init__(self, RensonVentilation api, RensonCoordinator coordinator)
Definition: fan.py:132
None async_set_percentage(self, int percentage)
Definition: fan.py:182
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
Definition: fan.py:171
None set_timer_level(self, str timer_level, int minutes)
Definition: fan.py:208
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: fan.py:90
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