Home Assistant Unofficial Reference 2024.12.1
fan.py
Go to the documentation of this file.
1 """Support for Tasmota fans."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from hatasmota import const as tasmota_const, fan as tasmota_fan
8 from hatasmota.entity import TasmotaEntity as HATasmotaEntity
9 from hatasmota.models import DiscoveryHashType
10 
11 from homeassistant.components.fan import (
12  DOMAIN as FAN_DOMAIN,
13  FanEntity,
14  FanEntityFeature,
15 )
16 from homeassistant.config_entries import ConfigEntry
17 from homeassistant.core import HomeAssistant, callback
18 from homeassistant.helpers.dispatcher import async_dispatcher_connect
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21  ordered_list_item_to_percentage,
22  percentage_to_ordered_list_item,
23 )
24 
25 from .const import DATA_REMOVE_DISCOVER_COMPONENT
26 from .discovery import TASMOTA_DISCOVERY_ENTITY_NEW
27 from .entity import TasmotaAvailability, TasmotaDiscoveryUpdate
28 
29 ORDERED_NAMED_FAN_SPEEDS = [
30  tasmota_const.FAN_SPEED_LOW,
31  tasmota_const.FAN_SPEED_MEDIUM,
32  tasmota_const.FAN_SPEED_HIGH,
33 ] # off is not included
34 
35 
37  hass: HomeAssistant,
38  config_entry: ConfigEntry,
39  async_add_entities: AddEntitiesCallback,
40 ) -> None:
41  """Set up Tasmota fan dynamically through discovery."""
42 
43  @callback
44  def async_discover(
45  tasmota_entity: HATasmotaEntity, discovery_hash: DiscoveryHashType
46  ) -> None:
47  """Discover and add a Tasmota fan."""
49  [TasmotaFan(tasmota_entity=tasmota_entity, discovery_hash=discovery_hash)]
50  )
51 
52  hass.data[DATA_REMOVE_DISCOVER_COMPONENT.format(FAN_DOMAIN)] = (
54  hass,
55  TASMOTA_DISCOVERY_ENTITY_NEW.format(FAN_DOMAIN),
56  async_discover,
57  )
58  )
59 
60 
61 class TasmotaFan(
62  TasmotaAvailability,
63  TasmotaDiscoveryUpdate,
64  FanEntity,
65 ):
66  """Representation of a Tasmota fan."""
67 
68  _attr_supported_features = (
69  FanEntityFeature.SET_SPEED
70  | FanEntityFeature.TURN_OFF
71  | FanEntityFeature.TURN_ON
72  )
73  _fan_speed = tasmota_const.FAN_SPEED_MEDIUM
74  _tasmota_entity: tasmota_fan.TasmotaFan
75  _enable_turn_on_off_backwards_compatibility = False
76 
77  def __init__(self, **kwds: Any) -> None:
78  """Initialize the Tasmota fan."""
79  self._state_state: int | None = None
80 
81  super().__init__(
82  **kwds,
83  )
84 
85  async def async_added_to_hass(self) -> None:
86  """Subscribe to MQTT events."""
87  self._tasmota_entity_tasmota_entity.set_on_state_callback(self.fan_state_updatedfan_state_updated)
88  await super().async_added_to_hass()
89 
90  @callback
91  def fan_state_updated(self, state: int, **kwargs: Any) -> None:
92  """Handle state updates."""
93  self._state_state = state
94  if self._state_state is not None and self._state_state != 0:
95  # Store the last known fan speed
96  self._fan_speed_fan_speed = state
97  self.async_write_ha_stateasync_write_ha_state()
98 
99  @property
100  def speed_count(self) -> int:
101  """Return the number of speeds the fan supports."""
102  return len(ORDERED_NAMED_FAN_SPEEDS)
103 
104  @property
105  def percentage(self) -> int | None:
106  """Return the current speed percentage."""
107  if self._state_state is None:
108  return None
109  if self._state_state == 0:
110  return 0
111  return ordered_list_item_to_percentage(ORDERED_NAMED_FAN_SPEEDS, self._state_state)
112 
113  async def async_set_percentage(self, percentage: int) -> None:
114  """Set the speed of the fan."""
115  if percentage == 0:
116  await self.async_turn_offasync_turn_offasync_turn_off()
117  else:
118  tasmota_speed = percentage_to_ordered_list_item(
119  ORDERED_NAMED_FAN_SPEEDS, percentage
120  )
121  await self._tasmota_entity_tasmota_entity.set_speed(tasmota_speed)
122 
123  async def async_turn_on(
124  self,
125  percentage: int | None = None,
126  preset_mode: str | None = None,
127  **kwargs: Any,
128  ) -> None:
129  """Turn the fan on."""
130  # Tasmota does not support turning a fan on with implicit speed
131  await self.async_set_percentageasync_set_percentageasync_set_percentage(
132  percentage
133  or ordered_list_item_to_percentage(
134  ORDERED_NAMED_FAN_SPEEDS, self._fan_speed_fan_speed
135  )
136  )
137 
138  async def async_turn_off(self, **kwargs: Any) -> None:
139  """Turn the fan off."""
140  await self._tasmota_entity_tasmota_entity.set_speed(tasmota_const.FAN_SPEED_OFF)
None async_set_percentage(self, int percentage)
Definition: __init__.py:340
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
Definition: fan.py:128
None fan_state_updated(self, int state, **Any kwargs)
Definition: fan.py:91
None async_set_percentage(self, int percentage)
Definition: fan.py:113
None async_turn_off(self, **Any kwargs)
Definition: fan.py:138
None async_turn_off(self, **Any kwargs)
Definition: entity.py:1709
None async_discover(DiscoveryInfo discovery_info)
Definition: sensor.py:217
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: fan.py:40
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103