Home Assistant Unofficial Reference 2024.12.1
fan.py
Go to the documentation of this file.
1 """Support for fans through the SmartThings cloud API."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Sequence
6 import math
7 from typing import Any
8 
9 from pysmartthings import Capability
10 
11 from homeassistant.components.fan import FanEntity, FanEntityFeature
12 from homeassistant.config_entries import ConfigEntry
13 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.entity_platform import AddEntitiesCallback
16  percentage_to_ranged_value,
17  ranged_value_to_percentage,
18 )
19 from homeassistant.util.scaling import int_states_in_range
20 
21 from .const import DATA_BROKERS, DOMAIN
22 from .entity import SmartThingsEntity
23 
24 SPEED_RANGE = (1, 3) # off is not included
25 
26 
28  hass: HomeAssistant,
29  config_entry: ConfigEntry,
30  async_add_entities: AddEntitiesCallback,
31 ) -> None:
32  """Add fans for a config entry."""
33  broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
35  SmartThingsFan(device)
36  for device in broker.devices.values()
37  if broker.any_assigned(device.device_id, "fan")
38  )
39 
40 
41 def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
42  """Return all capabilities supported if minimum required are present."""
43 
44  # MUST support switch as we need a way to turn it on and off
45  if Capability.switch not in capabilities:
46  return None
47 
48  # These are all optional but at least one must be supported
49  optional = [
50  Capability.air_conditioner_fan_mode,
51  Capability.fan_speed,
52  ]
53 
54  # At least one of the optional capabilities must be supported
55  # to classify this entity as a fan.
56  # If they are not then return None and don't setup the platform.
57  if not any(capability in capabilities for capability in optional):
58  return None
59 
60  supported = [Capability.switch]
61 
62  supported.extend(
63  capability for capability in optional if capability in capabilities
64  )
65 
66  return supported
67 
68 
70  """Define a SmartThings Fan."""
71 
72  _attr_speed_count = int_states_in_range(SPEED_RANGE)
73  _enable_turn_on_off_backwards_compatibility = False
74 
75  def __init__(self, device):
76  """Init the class."""
77  super().__init__(device)
78  self._attr_supported_features_attr_supported_features = self._determine_features_determine_features()
79 
81  flags = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
82 
83  if self._device_device.get_capability(Capability.fan_speed):
84  flags |= FanEntityFeature.SET_SPEED
85  if self._device_device.get_capability(Capability.air_conditioner_fan_mode):
86  flags |= FanEntityFeature.PRESET_MODE
87 
88  return flags
89 
90  async def async_set_percentage(self, percentage: int) -> None:
91  """Set the speed percentage of the fan."""
92  await self._async_set_percentage_async_set_percentage(percentage)
93 
94  async def _async_set_percentage(self, percentage: int | None) -> None:
95  if percentage is None:
96  await self._device_device.switch_on(set_status=True)
97  elif percentage == 0:
98  await self._device_device.switch_off(set_status=True)
99  else:
100  value = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage))
101  await self._device_device.set_fan_speed(value, set_status=True)
102  # State is set optimistically in the command above, therefore update
103  # the entity state ahead of receiving the confirming push updates
104  self.async_write_ha_stateasync_write_ha_state()
105 
106  async def async_set_preset_mode(self, preset_mode: str) -> None:
107  """Set the preset_mode of the fan."""
108  await self._device_device.set_fan_mode(preset_mode, set_status=True)
109  self.async_write_ha_stateasync_write_ha_state()
110 
111  async def async_turn_on(
112  self,
113  percentage: int | None = None,
114  preset_mode: str | None = None,
115  **kwargs: Any,
116  ) -> None:
117  """Turn the fan on."""
118  if FanEntityFeature.SET_SPEED in self._attr_supported_features_attr_supported_features:
119  # If speed is set in features then turn the fan on with the speed.
120  await self._async_set_percentage_async_set_percentage(percentage)
121  else:
122  # If speed is not valid then turn on the fan with the
123  await self._device_device.switch_on(set_status=True)
124  # State is set optimistically in the command above, therefore update
125  # the entity state ahead of receiving the confirming push updates
126  self.async_write_ha_stateasync_write_ha_state()
127 
128  async def async_turn_off(self, **kwargs: Any) -> None:
129  """Turn the fan off."""
130  await self._device_device.switch_off(set_status=True)
131  # State is set optimistically in the command above, therefore update
132  # the entity state ahead of receiving the confirming push updates
133  self.async_write_ha_stateasync_write_ha_state()
134 
135  @property
136  def is_on(self) -> bool:
137  """Return true if fan is on."""
138  return self._device_device.status.switch
139 
140  @property
141  def percentage(self) -> int | None:
142  """Return the current speed percentage."""
143  return ranged_value_to_percentage(SPEED_RANGE, self._device_device.status.fan_speed)
144 
145  @property
146  def preset_mode(self) -> str | None:
147  """Return the current preset mode, e.g., auto, smart, interval, favorite.
148 
149  Requires FanEntityFeature.PRESET_MODE.
150  """
151  return self._device_device.status.fan_mode
152 
153  @property
154  def preset_modes(self) -> list[str] | None:
155  """Return a list of available preset modes.
156 
157  Requires FanEntityFeature.PRESET_MODE.
158  """
159  return self._device_device.status.supported_ac_fan_modes
None _async_set_percentage(self, int|None percentage)
Definition: fan.py:94
None async_set_preset_mode(self, str preset_mode)
Definition: fan.py:106
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
Definition: fan.py:116
None async_set_percentage(self, int percentage)
Definition: fan.py:90
Sequence[str]|None get_capabilities(Sequence[str] capabilities)
Definition: fan.py:41
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: fan.py:31
Any|None get_capability(HomeAssistant hass, str entity_id, str capability)
Definition: entity.py:139
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