climate.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. """
  2. Goldair WiFi Dehumidifier device.
  3. """
  4. from homeassistant.components.climate import ClimateEntity
  5. from homeassistant.components.climate.const import (
  6. ATTR_FAN_MODE,
  7. ATTR_HUMIDITY,
  8. ATTR_HVAC_MODE,
  9. ATTR_PRESET_MODE,
  10. FAN_HIGH,
  11. FAN_LOW,
  12. HVAC_MODE_OFF,
  13. SUPPORT_FAN_MODE,
  14. SUPPORT_PRESET_MODE,
  15. SUPPORT_TARGET_HUMIDITY,
  16. )
  17. from homeassistant.const import ATTR_TEMPERATURE, STATE_UNAVAILABLE
  18. from ..device import TuyaLocalDevice
  19. from .const import (
  20. ATTR_AIR_CLEAN_ON,
  21. ATTR_DEFROSTING,
  22. ATTR_ERROR,
  23. ATTR_ERROR_CODE,
  24. ATTR_TARGET_HUMIDITY,
  25. ERROR_CODE_TO_DPS_CODE,
  26. ERROR_TANK,
  27. FAN_MODE_TO_DPS_MODE,
  28. HVAC_MODE_TO_DPS_MODE,
  29. PRESET_AIR_CLEAN,
  30. PRESET_DRY_CLOTHES,
  31. PRESET_HIGH,
  32. PRESET_LOW,
  33. PRESET_MODE_TO_DPS_MODE,
  34. PRESET_NORMAL,
  35. PROPERTY_TO_DPS_ID,
  36. )
  37. SUPPORT_FLAGS = SUPPORT_TARGET_HUMIDITY | SUPPORT_PRESET_MODE | SUPPORT_FAN_MODE
  38. class GoldairDehumidifier(ClimateEntity):
  39. """Representation of a Goldair WiFi dehumidifier."""
  40. def __init__(self, device):
  41. """Initialize the dehumidifier.
  42. Args:
  43. name (str): The device's name.
  44. device (TuyaLocalDevice): The device API instance."""
  45. self._device = device
  46. self._support_flags = SUPPORT_FLAGS
  47. self._HUMIDITY_STEP = 5
  48. self._HUMIDITY_LIMITS = {"min": 30, "max": 80}
  49. @property
  50. def supported_features(self):
  51. """Return the list of supported features."""
  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 unique_id(self):
  63. """Return the unique id for this dehumidifier."""
  64. return self._device.unique_id
  65. @property
  66. def device_info(self):
  67. """Return device information about this dehumidifier."""
  68. return self._device.device_info
  69. @property
  70. def icon(self):
  71. """Return the icon to use in the frontend based on the device state."""
  72. if self.tank_full_or_missing:
  73. return "mdi:cup-water"
  74. elif self.defrosting:
  75. return "mdi:snowflake-melt"
  76. elif (
  77. self.hvac_mode is not HVAC_MODE_OFF
  78. and self.preset_mode is PRESET_DRY_CLOTHES
  79. ):
  80. return "mdi:tshirt-crew-outline"
  81. elif (
  82. self.hvac_mode is not HVAC_MODE_OFF and self.preset_mode is PRESET_AIR_CLEAN
  83. ):
  84. return "mdi:air-purifier"
  85. else:
  86. return "mdi:air-humidifier"
  87. @property
  88. def current_humidity(self):
  89. """Return the current reading of the humidity sensor."""
  90. return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_HUMIDITY])
  91. @property
  92. def min_humidity(self):
  93. """Return the minimum humidity setting."""
  94. return self._HUMIDITY_LIMITS["min"]
  95. @property
  96. def max_humidity(self):
  97. """Return the maximum humidity setting."""
  98. return self._HUMIDITY_LIMITS["max"]
  99. @property
  100. def target_humidity(self):
  101. """Return the current target humidity."""
  102. if self.preset_mode is PRESET_NORMAL:
  103. return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY])
  104. else:
  105. return None
  106. async def async_set_humidity(self, humidity):
  107. """Set the device's target humidity."""
  108. if self.preset_mode is not PRESET_NORMAL:
  109. raise ValueError(
  110. "Target humidity can only be changed while in Normal mode."
  111. )
  112. humidity = int(
  113. self._HUMIDITY_STEP * round(float(humidity) / self._HUMIDITY_STEP)
  114. )
  115. await self._device.async_set_property(
  116. PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY], humidity
  117. )
  118. @property
  119. def temperature_unit(self):
  120. """Return the unit of measurement."""
  121. return self._device.temperature_unit
  122. @property
  123. def min_temp(self):
  124. """Return the minimum temperature setting."""
  125. return None
  126. @property
  127. def max_temp(self):
  128. """Return the maximum temperature setting."""
  129. return None
  130. @property
  131. def current_temperature(self):
  132. """Return the current temperature."""
  133. return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TEMPERATURE])
  134. @property
  135. def hvac_mode(self):
  136. """Return current HVAC mode, ie Dry or Off."""
  137. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE])
  138. if dps_mode is not None:
  139. return TuyaLocalDevice.get_key_for_value(HVAC_MODE_TO_DPS_MODE, dps_mode)
  140. else:
  141. return STATE_UNAVAILABLE
  142. @property
  143. def hvac_modes(self):
  144. """Return the list of available HVAC modes."""
  145. return list(HVAC_MODE_TO_DPS_MODE.keys())
  146. async def async_set_hvac_mode(self, hvac_mode):
  147. """Set new HVAC mode."""
  148. dps_mode = HVAC_MODE_TO_DPS_MODE[hvac_mode]
  149. await self._device.async_set_property(
  150. PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE], dps_mode
  151. )
  152. @property
  153. def preset_mode(self):
  154. """Return current preset mode, ie Normal, Low, High, Dry Clothes, or Air Clean."""
  155. air_clean_on = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON])
  156. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE])
  157. if air_clean_on:
  158. return PRESET_AIR_CLEAN
  159. elif dps_mode is not None:
  160. return TuyaLocalDevice.get_key_for_value(PRESET_MODE_TO_DPS_MODE, dps_mode)
  161. else:
  162. return None
  163. @property
  164. def preset_modes(self):
  165. """Return the list of available preset modes."""
  166. return list(PRESET_MODE_TO_DPS_MODE.keys()) + [PRESET_AIR_CLEAN]
  167. async def async_set_preset_mode(self, preset_mode):
  168. """Set new preset mode."""
  169. if preset_mode == PRESET_AIR_CLEAN:
  170. await self._device.async_set_property(
  171. PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON], True
  172. )
  173. self._device.anticipate_property_value(
  174. PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
  175. )
  176. else:
  177. dps_mode = PRESET_MODE_TO_DPS_MODE[preset_mode]
  178. await self._device.async_set_property(
  179. PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON], False
  180. )
  181. await self._device.async_set_property(
  182. PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE], dps_mode
  183. )
  184. if preset_mode == PRESET_LOW:
  185. self._device.anticipate_property_value(
  186. PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_LOW
  187. )
  188. elif preset_mode in [PRESET_HIGH, PRESET_DRY_CLOTHES]:
  189. self._device.anticipate_property_value(
  190. PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
  191. )
  192. @property
  193. def fan_mode(self):
  194. """Return the fan mode."""
  195. preset = self.preset_mode
  196. if preset in [PRESET_HIGH, PRESET_DRY_CLOTHES, PRESET_AIR_CLEAN]:
  197. return FAN_HIGH
  198. elif preset == PRESET_LOW:
  199. return FAN_LOW
  200. else:
  201. dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_FAN_MODE])
  202. if dps_mode is not None:
  203. return TuyaLocalDevice.get_key_for_value(FAN_MODE_TO_DPS_MODE, dps_mode)
  204. else:
  205. return None
  206. @property
  207. def fan_modes(self):
  208. """List of fan modes."""
  209. preset = self.preset_mode
  210. if preset in [PRESET_HIGH, PRESET_DRY_CLOTHES, PRESET_AIR_CLEAN]:
  211. return [FAN_HIGH]
  212. elif preset == PRESET_LOW:
  213. return [FAN_LOW]
  214. elif preset == PRESET_NORMAL:
  215. return list(FAN_MODE_TO_DPS_MODE.keys())
  216. else:
  217. return []
  218. async def async_set_fan_mode(self, fan_mode):
  219. """Set new fan mode."""
  220. if self.preset_mode != PRESET_NORMAL:
  221. raise ValueError(
  222. "Fan mode can only be changed while in Normal preset mode."
  223. )
  224. if fan_mode not in FAN_MODE_TO_DPS_MODE.keys():
  225. raise ValueError(f"Invalid fan mode: {fan_mode}")
  226. dps_mode = FAN_MODE_TO_DPS_MODE[fan_mode]
  227. await self._device.async_set_property(
  228. PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], dps_mode
  229. )
  230. @property
  231. def tank_full_or_missing(self):
  232. error = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
  233. return (
  234. TuyaLocalDevice.get_key_for_value(ERROR_CODE_TO_DPS_CODE, error)
  235. == ERROR_TANK
  236. )
  237. @property
  238. def defrosting(self):
  239. return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_DEFROSTING])
  240. @property
  241. def device_state_attributes(self):
  242. """Get additional attributes that HA doesn't naturally support."""
  243. error_code = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
  244. if isinstance(error_code, int):
  245. error = TuyaLocalDevice.get_key_for_value(
  246. ERROR_CODE_TO_DPS_CODE, error_code, f"Error {error_code}"
  247. )
  248. else:
  249. error = STATE_UNAVAILABLE
  250. return {
  251. ATTR_ERROR: error,
  252. ATTR_ERROR_CODE: error_code,
  253. ATTR_DEFROSTING: self.defrosting,
  254. }
  255. async def async_update(self):
  256. await self._device.async_refresh()