fan.py 5.5 KB

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