climate.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """
  2. Goldair WiFi Heater device.
  3. """
  4. from homeassistant.components.climate import ClimateEntity
  5. from homeassistant.components.climate.const import (
  6. ATTR_HVAC_MODE,
  7. ATTR_PRESET_MODE,
  8. HVAC_MODE_HEAT,
  9. SUPPORT_PRESET_MODE,
  10. SUPPORT_SWING_MODE,
  11. SUPPORT_TARGET_TEMPERATURE,
  12. )
  13. from homeassistant.const import ATTR_TEMPERATURE, STATE_UNAVAILABLE
  14. from ..device import TuyaLocalDevice
  15. from .const import (
  16. ATTR_ECO_TARGET_TEMPERATURE,
  17. ATTR_ERROR,
  18. ATTR_ERROR_CODE,
  19. ATTR_POWER_LEVEL,
  20. ATTR_POWER_MODE,
  21. ATTR_POWER_MODE_AUTO,
  22. ATTR_POWER_MODE_USER,
  23. ATTR_TARGET_TEMPERATURE,
  24. HVAC_MODE_TO_DPS_MODE,
  25. POWER_LEVEL_STOP,
  26. POWER_LEVEL_TO_DPS_LEVEL,
  27. PRESET_MODE_TO_DPS_MODE,
  28. PROPERTY_TO_DPS_ID,
  29. STATE_ANTI_FREEZE,
  30. STATE_COMFORT,
  31. STATE_ECO,
  32. )
  33. SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE | SUPPORT_SWING_MODE
  34. class GoldairHeater(ClimateEntity):
  35. """Representation of a Goldair WiFi heater."""
  36. def __init__(self, device):
  37. """Initialize the heater.
  38. Args:
  39. name (str): The device's name.
  40. device (TuyaLocalDevice): The device API instance."""
  41. self._device = device
  42. self._support_flags = SUPPORT_FLAGS
  43. self._TEMPERATURE_STEP = 1
  44. self._TEMPERATURE_LIMITS = {
  45. STATE_COMFORT: {"min": 5, "max": 35},
  46. STATE_ECO: {"min": 5, "max": 21},
  47. }
  48. @property
  49. def supported_features(self):
  50. """Return the list of supported features."""
  51. return self._support_flags
  52. @property
  53. def should_poll(self):
  54. """Return the polling state."""
  55. return True
  56. @property
  57. def name(self):
  58. """Return the name of the climate device."""
  59. return self._device.name
  60. @property
  61. def unique_id(self):
  62. """Return the unique id for this heater."""
  63. return self._device.unique_id
  64. @property
  65. def device_info(self):
  66. """Return device information about this heater."""
  67. return self._device.device_info
  68. @property
  69. def icon(self):
  70. """Return the icon to use in the frontend for this device."""
  71. hvac_mode = self.hvac_mode
  72. power_level = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_POWER_LEVEL])
  73. if hvac_mode == HVAC_MODE_HEAT and power_level != POWER_LEVEL_STOP:
  74. return "mdi:radiator"
  75. else:
  76. return "mdi:radiator-disabled"
  77. @property
  78. def temperature_unit(self):
  79. """Return the unit of measurement."""
  80. return self._device.temperature_unit
  81. @property
  82. def target_temperature(self):
  83. """Return the temperature we try to reach."""
  84. if self.preset_mode == STATE_COMFORT:
  85. return self._device.get_property(
  86. PROPERTY_TO_DPS_ID[ATTR_TARGET_TEMPERATURE]
  87. )
  88. elif self.preset_mode == STATE_ECO:
  89. return self._device.get_property(
  90. PROPERTY_TO_DPS_ID[ATTR_ECO_TARGET_TEMPERATURE]
  91. )
  92. else:
  93. return None
  94. @property
  95. def target_temperature_step(self):
  96. """Return the supported step of target temperature."""
  97. return self._TEMPERATURE_STEP
  98. @property
  99. def min_temp(self):
  100. """Return the minimum temperature."""
  101. if self.preset_mode and self.preset_mode != STATE_ANTI_FREEZE:
  102. return self._TEMPERATURE_LIMITS[self.preset_mode]["min"]
  103. else:
  104. return None
  105. @property
  106. def max_temp(self):
  107. """Return the maximum temperature."""
  108. if self.preset_mode and self.preset_mode != STATE_ANTI_FREEZE:
  109. return self._TEMPERATURE_LIMITS[self.preset_mode]["max"]
  110. else:
  111. return None
  112. async def async_set_temperature(self, **kwargs):
  113. """Set new target temperatures."""
  114. if kwargs.get(ATTR_PRESET_MODE) is not None:
  115. await self.async_set_preset_mode(kwargs.get(ATTR_PRESET_MODE))
  116. if kwargs.get(ATTR_TEMPERATURE) is not None:
  117. await self.async_set_target_temperature(kwargs.get(ATTR_TEMPERATURE))
  118. async def async_set_target_temperature(self, target_temperature):
  119. target_temperature = int(round(target_temperature))
  120. preset_mode = self.preset_mode
  121. if preset_mode == STATE_ANTI_FREEZE:
  122. raise ValueError("You cannot set the temperature in Anti-freeze mode.")
  123. limits = self._TEMPERATURE_LIMITS[preset_mode]
  124. if not limits["min"] <= target_temperature <= limits["max"]:
  125. raise ValueError(
  126. f"Target temperature ({target_temperature}) must be between "
  127. f'{limits["min"]} and {limits["max"]}.'
  128. )
  129. if preset_mode == STATE_ECO:
  130. await self._device.async_set_property(
  131. PROPERTY_TO_DPS_ID[ATTR_ECO_TARGET_TEMPERATURE], target_temperature
  132. )
  133. else:
  134. await self._device.async_set_property(
  135. PROPERTY_TO_DPS_ID[ATTR_TARGET_TEMPERATURE], target_temperature
  136. )
  137. @property
  138. def current_temperature(self):
  139. """Return the current temperature."""
  140. return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TEMPERATURE])
  141. @property
  142. def hvac_mode(self):
  143. """Return current HVAC mode, ie Heat or Off."""
  144. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE])
  145. if dps_mode is not None:
  146. return TuyaLocalDevice.get_key_for_value(HVAC_MODE_TO_DPS_MODE, dps_mode)
  147. else:
  148. return STATE_UNAVAILABLE
  149. @property
  150. def hvac_modes(self):
  151. """Return the list of available HVAC modes."""
  152. return list(HVAC_MODE_TO_DPS_MODE.keys())
  153. async def async_set_hvac_mode(self, hvac_mode):
  154. """Set new HVAC mode."""
  155. dps_mode = HVAC_MODE_TO_DPS_MODE[hvac_mode]
  156. await self._device.async_set_property(
  157. PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE], dps_mode
  158. )
  159. @property
  160. def preset_mode(self):
  161. """Return current preset mode, ie Comfort, Eco, Anti-freeze."""
  162. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE])
  163. if dps_mode is not None:
  164. return TuyaLocalDevice.get_key_for_value(PRESET_MODE_TO_DPS_MODE, dps_mode)
  165. else:
  166. return None
  167. @property
  168. def preset_modes(self):
  169. """Return the list of available preset modes."""
  170. return list(PRESET_MODE_TO_DPS_MODE.keys())
  171. async def async_set_preset_mode(self, preset_mode):
  172. """Set new preset mode."""
  173. dps_mode = PRESET_MODE_TO_DPS_MODE[preset_mode]
  174. await self._device.async_set_property(
  175. PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE], dps_mode
  176. )
  177. @property
  178. def swing_mode(self):
  179. """Return the power level."""
  180. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_POWER_MODE])
  181. if dps_mode == None:
  182. return None
  183. if dps_mode == ATTR_POWER_MODE_USER:
  184. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_POWER_LEVEL])
  185. return TuyaLocalDevice.get_key_for_value(POWER_LEVEL_TO_DPS_LEVEL, dps_mode)
  186. @property
  187. def swing_modes(self):
  188. """List of power levels."""
  189. return list(POWER_LEVEL_TO_DPS_LEVEL.keys())
  190. async def async_set_swing_mode(self, swing_mode):
  191. """Set new power level."""
  192. new_level = swing_mode
  193. if new_level not in POWER_LEVEL_TO_DPS_LEVEL.keys():
  194. raise ValueError(f"Invalid power level: {new_level}")
  195. dps_level = POWER_LEVEL_TO_DPS_LEVEL[new_level]
  196. await self._device.async_set_property(
  197. PROPERTY_TO_DPS_ID[ATTR_POWER_LEVEL], dps_level
  198. )
  199. @property
  200. def device_state_attributes(self):
  201. """Get additional attributes that HA doesn't naturally support."""
  202. error_code = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
  203. if error_code:
  204. error = f"Error {error_code}"
  205. else:
  206. error = "OK"
  207. return {ATTR_ERROR: error, ATTR_ERROR_CODE: error_code}
  208. async def async_update(self):
  209. await self._device.async_refresh()