Home Assistant Unofficial Reference 2024.12.1
fan.py
Go to the documentation of this file.
1 """Support for VeSync fans."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import math
7 from typing import Any
8 
9 from homeassistant.components.fan import FanEntity, FanEntityFeature
10 from homeassistant.config_entries import ConfigEntry
11 from homeassistant.core import HomeAssistant, callback
12 from homeassistant.helpers.dispatcher import async_dispatcher_connect
13 from homeassistant.helpers.entity_platform import AddEntitiesCallback
15  percentage_to_ranged_value,
16  ranged_value_to_percentage,
17 )
18 from homeassistant.util.scaling import int_states_in_range
19 
20 from .const import DEV_TYPE_TO_HA, DOMAIN, SKU_TO_BASE_DEVICE, VS_DISCOVERY, VS_FANS
21 from .entity import VeSyncDevice
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 FAN_MODE_AUTO = "auto"
26 FAN_MODE_SLEEP = "sleep"
27 FAN_MODE_PET = "pet"
28 FAN_MODE_TURBO = "turbo"
29 
30 PRESET_MODES = {
31  "LV-PUR131S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
32  "Core200S": [FAN_MODE_SLEEP],
33  "Core300S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
34  "Core400S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
35  "Core600S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
36  "EverestAir": [FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_TURBO],
37  "Vital200S": [FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_PET],
38  "Vital100S": [FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_PET],
39 }
40 SPEED_RANGE = { # off is not included
41  "LV-PUR131S": (1, 3),
42  "Core200S": (1, 3),
43  "Core300S": (1, 3),
44  "Core400S": (1, 4),
45  "Core600S": (1, 4),
46  "EverestAir": (1, 3),
47  "Vital200S": (1, 4),
48  "Vital100S": (1, 4),
49 }
50 
51 
53  hass: HomeAssistant,
54  config_entry: ConfigEntry,
55  async_add_entities: AddEntitiesCallback,
56 ) -> None:
57  """Set up the VeSync fan platform."""
58 
59  @callback
60  def discover(devices):
61  """Add new devices to platform."""
62  _setup_entities(devices, async_add_entities)
63 
64  config_entry.async_on_unload(
65  async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_FANS), discover)
66  )
67 
68  _setup_entities(hass.data[DOMAIN][VS_FANS], async_add_entities)
69 
70 
71 @callback
72 def _setup_entities(devices, async_add_entities):
73  """Check if device is online and add entity."""
74  entities = []
75  for dev in devices:
76  if DEV_TYPE_TO_HA.get(SKU_TO_BASE_DEVICE.get(dev.device_type)) == "fan":
77  entities.append(VeSyncFanHA(dev))
78  else:
79  _LOGGER.warning(
80  "%s - Unknown device type - %s", dev.device_name, dev.device_type
81  )
82  continue
83 
84  async_add_entities(entities, update_before_add=True)
85 
86 
88  """Representation of a VeSync fan."""
89 
90  _attr_supported_features = (
91  FanEntityFeature.SET_SPEED
92  | FanEntityFeature.PRESET_MODE
93  | FanEntityFeature.TURN_OFF
94  | FanEntityFeature.TURN_ON
95  )
96  _attr_name = None
97  _attr_translation_key = "vesync"
98  _enable_turn_on_off_backwards_compatibility = False
99 
100  def __init__(self, fan) -> None:
101  """Initialize the VeSync fan device."""
102  super().__init__(fan)
103  self.smartfansmartfan = fan
104 
105  @property
106  def percentage(self) -> int | None:
107  """Return the current speed."""
108  if (
109  self.smartfansmartfan.mode == "manual"
110  and (current_level := self.smartfansmartfan.fan_level) is not None
111  ):
113  SPEED_RANGE[SKU_TO_BASE_DEVICE[self.devicedevice.device_type]], current_level
114  )
115  return None
116 
117  @property
118  def speed_count(self) -> int:
119  """Return the number of speeds the fan supports."""
120  return int_states_in_range(
121  SPEED_RANGE[SKU_TO_BASE_DEVICE[self.devicedevice.device_type]]
122  )
123 
124  @property
125  def preset_modes(self) -> list[str]:
126  """Get the list of available preset modes."""
127  return PRESET_MODES[SKU_TO_BASE_DEVICE[self.devicedevice.device_type]]
128 
129  @property
130  def preset_mode(self) -> str | None:
131  """Get the current preset mode."""
132  if self.smartfansmartfan.mode in (FAN_MODE_AUTO, FAN_MODE_SLEEP, FAN_MODE_TURBO):
133  return self.smartfansmartfan.mode
134  return None
135 
136  @property
137  def unique_info(self):
138  """Return the ID of this fan."""
139  return self.smartfansmartfan.uuid
140 
141  @property
142  def extra_state_attributes(self) -> dict[str, Any]:
143  """Return the state attributes of the fan."""
144  attr = {}
145 
146  if hasattr(self.smartfansmartfan, "active_time"):
147  attr["active_time"] = self.smartfansmartfan.active_time
148 
149  if hasattr(self.smartfansmartfan, "screen_status"):
150  attr["screen_status"] = self.smartfansmartfan.screen_status
151 
152  if hasattr(self.smartfansmartfan, "child_lock"):
153  attr["child_lock"] = self.smartfansmartfan.child_lock
154 
155  if hasattr(self.smartfansmartfan, "night_light"):
156  attr["night_light"] = self.smartfansmartfan.night_light
157 
158  if hasattr(self.smartfansmartfan, "mode"):
159  attr["mode"] = self.smartfansmartfan.mode
160 
161  return attr
162 
163  def set_percentage(self, percentage: int) -> None:
164  """Set the speed of the device."""
165  if percentage == 0:
166  self.smartfansmartfan.turn_off()
167  return
168 
169  if not self.smartfansmartfan.is_on:
170  self.smartfansmartfan.turn_on()
171 
172  self.smartfansmartfan.manual_mode()
173  self.smartfansmartfan.change_fan_speed(
174  math.ceil(
176  SPEED_RANGE[SKU_TO_BASE_DEVICE[self.devicedevice.device_type]], percentage
177  )
178  )
179  )
180  self.schedule_update_ha_stateschedule_update_ha_state()
181 
182  def set_preset_mode(self, preset_mode: str) -> None:
183  """Set the preset mode of device."""
184  if preset_mode not in self.preset_modespreset_modespreset_modes:
185  raise ValueError(
186  f"{preset_mode} is not one of the valid preset modes: "
187  f"{self.preset_modes}"
188  )
189 
190  if not self.smartfansmartfan.is_on:
191  self.smartfansmartfan.turn_on()
192 
193  if preset_mode == FAN_MODE_AUTO:
194  self.smartfansmartfan.auto_mode()
195  elif preset_mode == FAN_MODE_SLEEP:
196  self.smartfansmartfan.sleep_mode()
197  elif preset_mode == FAN_MODE_PET:
198  self.smartfansmartfan.pet_mode()
199  elif preset_mode == FAN_MODE_TURBO:
200  self.smartfansmartfan.turbo_mode()
201 
202  self.schedule_update_ha_stateschedule_update_ha_state()
203 
204  def turn_on(
205  self,
206  percentage: int | None = None,
207  preset_mode: str | None = None,
208  **kwargs: Any,
209  ) -> None:
210  """Turn the device on."""
211  if preset_mode:
212  self.set_preset_modeset_preset_modeset_preset_mode(preset_mode)
213  return
214  if percentage is None:
215  percentage = 50
216  self.set_percentageset_percentageset_percentage(percentage)
None set_preset_mode(self, str preset_mode)
Definition: __init__.py:375
None set_percentage(self, int percentage)
Definition: __init__.py:336
list[str]|None preset_modes(self)
Definition: __init__.py:540
dict[str, Any] extra_state_attributes(self)
Definition: fan.py:142
None set_percentage(self, int percentage)
Definition: fan.py:163
None set_preset_mode(self, str preset_mode)
Definition: fan.py:182
None turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
Definition: fan.py:209
None schedule_update_ha_state(self, bool force_refresh=False)
Definition: entity.py:1244
list[tuple[str, int]] discover(HomeAssistant hass)
Definition: config_flow.py:97
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: fan.py:56
def _setup_entities(devices, async_add_entities)
Definition: fan.py:72
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103
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