fan.py 5.4 KB

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