fan.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. """
  2. Setup for different kinds of Tuya fan devices
  3. """
  4. import logging
  5. from homeassistant.components.fan import FanEntity, FanEntityFeature
  6. from .device import TuyaLocalDevice
  7. from .helpers.config import async_tuya_setup_platform
  8. from .helpers.device_config import TuyaEntityConfig
  9. from .helpers.mixin import TuyaLocalEntity
  10. _LOGGER = logging.getLogger(__name__)
  11. async def async_setup_entry(hass, config_entry, async_add_entities):
  12. config = {**config_entry.data, **config_entry.options}
  13. await async_tuya_setup_platform(
  14. hass,
  15. async_add_entities,
  16. config,
  17. "fan",
  18. TuyaLocalFan,
  19. )
  20. class TuyaLocalFan(TuyaLocalEntity, FanEntity):
  21. """Representation of a Tuya Fan entity."""
  22. def __init__(self, device: TuyaLocalDevice, config: TuyaEntityConfig):
  23. """
  24. Initialise the fan device.
  25. Args:
  26. device (TuyaLocalDevice): The device API instance.
  27. config (TuyaEntityConfig): The entity config.
  28. """
  29. super().__init__()
  30. dps_map = self._init_begin(device, config)
  31. self._switch_dps = dps_map.pop("switch", None)
  32. self._preset_dps = dps_map.pop("preset_mode", None)
  33. self._speed_dps = dps_map.pop("speed", None)
  34. self._oscillate_dps = dps_map.pop("oscillate", None)
  35. self._direction_dps = dps_map.pop("direction", None)
  36. self._init_end(dps_map)
  37. self._support_flags = 0
  38. if self._preset_dps:
  39. self._support_flags |= FanEntityFeature.PRESET_MODE
  40. if self._speed_dps:
  41. self._support_flags |= FanEntityFeature.SET_SPEED
  42. if self._oscillate_dps:
  43. self._support_flags |= FanEntityFeature.OSCILLATE
  44. if self._direction_dps:
  45. self._support_flags |= FanEntityFeature.DIRECTION
  46. @property
  47. def supported_features(self):
  48. """Return the features supported by this climate device."""
  49. return self._support_flags
  50. @property
  51. def is_on(self):
  52. """Return whether the switch is on or not."""
  53. # If there is no switch, it is always on
  54. if self._switch_dps is None:
  55. return self.available
  56. return self._switch_dps.get_value(self._device)
  57. async def async_turn_on(self, **kwargs):
  58. """Turn the switch on"""
  59. if self._switch_dps is None:
  60. raise NotImplementedError()
  61. await self._switch_dps.async_set_value(self._device, True)
  62. async def async_turn_off(self, **kwargs):
  63. """Turn the switch off"""
  64. if self._switch_dps is None:
  65. raise NotImplementedError
  66. await self._switch_dps.async_set_value(self._device, False)
  67. @property
  68. def percentage(self):
  69. """Return the currently set percentage."""
  70. if self._speed_dps is None:
  71. return None
  72. return self._speed_dps.get_value(self._device)
  73. @property
  74. def percentage_step(self):
  75. """Return the step for percentage."""
  76. if self._speed_dps is None:
  77. return None
  78. if self._speed_dps.values(self._device):
  79. return 100 / len(self._speed_dps.values(self._device))
  80. return self._speed_dps.step(self._device)
  81. @property
  82. def speed_count(self):
  83. """Return the number of speeds supported by the fan."""
  84. if self._speed_dps is None:
  85. return 0
  86. if self._speed_dps.values(self._device):
  87. return len(self._speed_dps.values(self._device))
  88. return int(round(100 / self.percentage_step))
  89. async def async_set_percentage(self, percentage):
  90. """Set the fan speed as a percentage."""
  91. # If speed is 0, turn the fan off
  92. if percentage == 0 and self._switch_dps:
  93. return await self.async_turn_off()
  94. if self._speed_dps is None:
  95. return None
  96. # If there is a fixed list of values, snap to the closest one
  97. if self._speed_dps.values(self._device):
  98. percentage = min(
  99. self._speed_dps.values(self._device),
  100. key=lambda x: abs(x - percentage),
  101. )
  102. values_to_set = self._speed_dps.get_values_to_set(self._device, percentage)
  103. if not self.is_on and self._switch_dps:
  104. values_to_set.update(self._switch_dps.get_values_to_set(self._device, True))
  105. await self._device.async_set_properties(values_to_set)
  106. @property
  107. def preset_mode(self):
  108. """Return the current preset mode."""
  109. if self._preset_dps is None:
  110. return None
  111. return self._preset_dps.get_value(self._device)
  112. @property
  113. def preset_modes(self):
  114. """Return the list of presets that this device supports."""
  115. if self._preset_dps is None:
  116. return []
  117. return self._preset_dps.values(self._device)
  118. async def async_set_preset_mode(self, preset_mode):
  119. """Set the preset mode."""
  120. if self._preset_dps is None:
  121. raise NotImplementedError()
  122. await self._preset_dps.async_set_value(self._device, preset_mode)
  123. @property
  124. def current_direction(self):
  125. """Return the current direction [forward or reverse]."""
  126. if self._direction_dps is None:
  127. return None
  128. return self._direction_dps.get_value(self._device)
  129. async def async_set_direction(self, direction):
  130. """Set the direction of the fan."""
  131. if self._direction_dps is None:
  132. raise NotImplementedError()
  133. await self._direction_dps.async_set_value(self._device, direction)
  134. @property
  135. def oscillating(self):
  136. """Return whether or not the fan is oscillating."""
  137. if self._oscillate_dps is None:
  138. return None
  139. return self._oscillate_dps.get_value(self._device)
  140. async def async_oscillate(self, oscillating):
  141. """Oscillate the fan."""
  142. if self._oscillate_dps is None:
  143. raise NotImplementedError()
  144. await self._oscillate_dps.async_set_value(self._device, oscillating)