climate.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. """
  2. Platform to control tuya climate devices.
  3. """
  4. import logging
  5. from homeassistant.components.climate import ClimateEntity
  6. from homeassistant.components.climate.const import (
  7. ATTR_PRESET_MODE,
  8. DEFAULT_MIN_TEMP,
  9. DEFAULT_MAX_TEMP,
  10. HVAC_MODE_HEAT,
  11. SUPPORT_PRESET_MODE,
  12. SUPPORT_TARGET_TEMPERATURE,
  13. )
  14. from homeassistant.const import ATTR_TEMPERATURE, STATE_UNAVAILABLE
  15. from ..device import TuyaLocalDevice
  16. from ..helpers.device_config import TuyaEntityConfig
  17. _LOGGER = logging.getLogger(__name__)
  18. class TuyaLocalClimate(ClimateEntity):
  19. """Representation of a Tuya Climate entity."""
  20. def __init__(self, device: TuyaLocalDevice, config: TuyaEntityConfig):
  21. """
  22. Initialise the climate device.
  23. Args:
  24. device (TuyaLocalDevice): The device API instance.
  25. config (TuyaEntityConfig): The entity config.
  26. """
  27. self._device = device
  28. self._config = config
  29. self._support_flags = 0
  30. self._current_temperature_dps = None
  31. self._temperature_dps = None
  32. self._preset_mode_dps = None
  33. self._hvac_mode_dps = None
  34. self._attr_dps = []
  35. self._temperature_step = 1
  36. for d in config.dps():
  37. if d.name == "hvac_mode":
  38. self._hvac_mode_dps = d
  39. elif d.name == "temperature":
  40. self._temperature_dps = d
  41. self._support_flags |= SUPPORT_TARGET_TEMPERATURE
  42. elif d.name == "current_temperature":
  43. self._current_temperature_dps = d
  44. elif d.name == "preset_mode":
  45. self._preset_mode_dps = d
  46. self._support_flags |= SUPPORT_PRESET_MODE
  47. else:
  48. self._attr_dps.append(d)
  49. @property
  50. def supported_features(self):
  51. """Return the features supported by this climate device."""
  52. return self._support_flags
  53. @property
  54. def should_poll(self):
  55. """Return the polling state."""
  56. return True
  57. @property
  58. def name(self):
  59. """Return the name of the climate device."""
  60. return self._device.name
  61. @property
  62. def friendly_name(self):
  63. """Return the friendly name of the climate entity for the UI."""
  64. return self._config.name
  65. @property
  66. def unique_id(self):
  67. """Return the unique id for this climate device."""
  68. return self._device.unique_id
  69. @property
  70. def device_info(self):
  71. """Return device information about this heater."""
  72. return self._device.device_info
  73. @property
  74. def icon(self):
  75. """Return the icon to use in the frontend for this device."""
  76. if self.hvac_mode == HVAC_MODE_HEAT:
  77. return "mdi:radiator"
  78. else:
  79. return "mdi:radiator-disabled"
  80. @property
  81. def temperature_unit(self):
  82. """Return the unit of measurement."""
  83. return self._device.temperature_unit
  84. @property
  85. def target_temperature(self):
  86. """Return the currently set target temperature."""
  87. if self._temperature_dps is None:
  88. raise NotImplementedError()
  89. return self._temperature_dps.get_value(self._device)
  90. @property
  91. def target_temperature_step(self):
  92. """Return the supported step of target temperature."""
  93. return self._temperature_step
  94. @property
  95. def min_temp(self):
  96. """Return the minimum supported target temperature."""
  97. if self._temperature_dps is None or self._temperature_dps.range is None:
  98. return DEFAULT_MIN_TEMP
  99. return self._temperature_dps.range["min"]
  100. @property
  101. def max_temp(self):
  102. """Return the maximum supported target temperature."""
  103. if self._temperature_dps is None or self._temperature_dps.range is None:
  104. return DEFAULT_MIN_TEMP
  105. return self._temperature_dps.range["max"]
  106. async def async_set_temperature(self, **kwargs):
  107. """Set new target temperature."""
  108. if kwargs.get(ATTR_PRESET_MODE) is not None:
  109. await self.async_set_preset_mode(kwargs.get(ATTR_PRESET_MODE))
  110. if kwargs.get(ATTR_TEMPERATURE) is not None:
  111. await self.async_set_target_temperature(kwargs.get(ATTR_TEMPERATURE))
  112. async def async_set_target_temperature(self, target_temperature):
  113. if self._temperature_dps is None:
  114. raise NotImplementedError()
  115. target_temperature = int(round(target_temperature))
  116. if not self.min_temp <= target_temperature <= self.max_temp:
  117. raise ValueError(
  118. f"Target temperature ({target_temperature}) must be between "
  119. f"{self.min_temp} and {self.max_temp}."
  120. )
  121. await self._temperature_dps.async_set_value(self._device, target_temperature)
  122. @property
  123. def current_temperature(self):
  124. """Return this current temperature."""
  125. if self._current_temperature_dps is None:
  126. return None
  127. return self._current_temperature_dps.get_value(self._device)
  128. @property
  129. def hvac_mode(self):
  130. """Return current HVAC mode."""
  131. if self._hvac_mode_dps is None:
  132. raise NotImplementedError()
  133. hvac_mode = self._hvac_mode_dps.get_value(self._device)
  134. return STATE_UNAVAILABLE if hvac_mode is None else hvac_mode
  135. @property
  136. def hvac_modes(self):
  137. """Return available HVAC modes."""
  138. if self._hvac_mode_dps is None:
  139. return None
  140. else:
  141. return self._hvac_mode_dps.values
  142. async def async_set_hvac_mode(self, hvac_mode):
  143. """Set new HVAC mode."""
  144. if self._hvac_mode_dps is None:
  145. raise NotImplementedError()
  146. await self._hvac_mode_dps.async_set_value(self._device, hvac_mode)
  147. @property
  148. def preset_mode(self):
  149. """Return the current preset mode."""
  150. if self._preset_mode_dps is None:
  151. raise NotImplementedError()
  152. return self._preset_mode_dps.get_value(self._device)
  153. @property
  154. def preset_modes(self):
  155. """Return the list of presets that this device supports."""
  156. if self._preset_mode_dps is None:
  157. return None
  158. return self._preset_mode_dps.values
  159. async def async_set_preset_mode(self, preset_mode):
  160. """Set the preset mode."""
  161. if self._preset_mode_dps is None:
  162. raise NotImplementedError()
  163. await self._preset_mode_dps.async_set_value(self._device, preset_mode)
  164. @property
  165. def device_state_attributes(self):
  166. """Get additional attributes that the integration itself does not support."""
  167. attr = {}
  168. for a in self._attr_dps:
  169. attr[a.name] = a.get_value(self._device)
  170. return attr
  171. async def async_update(self):
  172. await self._device.async_refresh()