climate.py 8.1 KB

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