fan.py 5.4 KB

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