climate.py 8.1 KB

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