test_eanons_humidifier.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. from unittest import IsolatedAsyncioTestCase, skip
  2. from unittest.mock import AsyncMock, patch
  3. from homeassistant.components.climate.const import (
  4. FAN_HIGH,
  5. FAN_MEDIUM,
  6. FAN_LOW,
  7. HVAC_MODE_DRY,
  8. HVAC_MODE_OFF,
  9. SUPPORT_FAN_MODE,
  10. SUPPORT_PRESET_MODE,
  11. SUPPORT_TARGET_HUMIDITY,
  12. )
  13. from homeassistant.components.humidifier.const import (
  14. MODE_NORMAL,
  15. MODE_AUTO,
  16. MODE_SLEEP,
  17. SUPPORT_MODES,
  18. )
  19. from homeassistant.components.switch import DEVICE_CLASS_SWITCH
  20. from homeassistant.const import STATE_UNAVAILABLE
  21. from custom_components.tuya_local.generic.climate import TuyaLocalClimate
  22. from custom_components.tuya_local.generic.humidifier import TuyaLocalHumidifier
  23. from custom_components.tuya_local.generic.switch import TuyaLocalSwitch
  24. from custom_components.tuya_local.helpers.device_config import TuyaDeviceConfig
  25. from ..const import EANONS_HUMIDIFIER_PAYLOAD
  26. from ..helpers import assert_device_properties_set
  27. FANMODE_DPS = "2"
  28. TIMERHR_DPS = "3"
  29. TIMER_DPS = "4"
  30. ERROR_DPS = "9"
  31. HVACMODE_DPS = "10"
  32. PRESET_DPS = "12"
  33. HUMIDITY_DPS = "15"
  34. CURRENTHUMID_DPS = "16"
  35. SWITCH_DPS = "22"
  36. class TestEanonsHumidifier(IsolatedAsyncioTestCase):
  37. def setUp(self):
  38. device_patcher = patch("custom_components.tuya_local.device.TuyaLocalDevice")
  39. self.addCleanup(device_patcher.stop)
  40. self.mock_device = device_patcher.start()
  41. cfg = TuyaDeviceConfig("eanons_humidifier.yaml")
  42. entities = {}
  43. entities[cfg.primary_entity.entity] = cfg.primary_entity
  44. for e in cfg.secondary_entities():
  45. entities[e.entity] = e
  46. self.climate_name = (
  47. "missing" if "climate" not in entities else entities["climate"].name
  48. )
  49. self.switch_name = (
  50. "missing" if "switch" not in entities else entities["switch"].name
  51. )
  52. self.humidifier_name = (
  53. "missing" if "humidifier" not in entities else entities["humidifier"].name
  54. )
  55. self.subject = TuyaLocalHumidifier(
  56. self.mock_device(), entities.get("humidifier")
  57. )
  58. self.climate = TuyaLocalClimate(self.mock_device(), entities.get("climate"))
  59. self.switch = TuyaLocalSwitch(self.mock_device(), entities.get("switch"))
  60. self.dps = EANONS_HUMIDIFIER_PAYLOAD.copy()
  61. self.subject._device.get_property.side_effect = lambda id: self.dps[id]
  62. def test_supported_features(self):
  63. self.assertEqual(
  64. self.climate.supported_features,
  65. SUPPORT_TARGET_HUMIDITY | SUPPORT_PRESET_MODE | SUPPORT_FAN_MODE,
  66. )
  67. self.assertEqual(self.subject.supported_features, SUPPORT_MODES)
  68. def test_shouldPoll(self):
  69. self.assertTrue(self.subject.should_poll)
  70. self.assertTrue(self.climate.should_poll)
  71. self.assertTrue(self.switch.should_poll)
  72. def test_name_returns_device_name(self):
  73. self.assertEqual(self.subject.name, self.subject._device.name)
  74. self.assertEqual(self.climate.name, self.subject._device.name)
  75. self.assertEqual(self.switch.name, self.subject._device.name)
  76. def test_friendly_name_returns_config_name(self):
  77. self.assertEqual(self.subject.friendly_name, self.humidifier_name)
  78. self.assertEqual(self.climate.friendly_name, self.climate_name)
  79. self.assertEqual(self.switch.friendly_name, self.switch_name)
  80. def test_unique_id_returns_device_unique_id(self):
  81. self.assertEqual(self.subject.unique_id, self.subject._device.unique_id)
  82. self.assertEqual(self.climate.unique_id, self.subject._device.unique_id)
  83. self.assertEqual(self.switch.unique_id, self.subject._device.unique_id)
  84. def test_device_info_returns_device_info_from_device(self):
  85. self.assertEqual(self.subject.device_info, self.subject._device.device_info)
  86. self.assertEqual(self.climate.device_info, self.subject._device.device_info)
  87. self.assertEqual(self.switch.device_info, self.subject._device.device_info)
  88. @skip("Icon customisation not supported yet")
  89. def test_climate_icon_is_humidifier(self):
  90. """Test that the icon is as expected."""
  91. self.dps[HVACMODE_DPS] = True
  92. self.assertEqual(self.climate.icon, "mdi:air-humidifier")
  93. self.dps[HVACMODE_DPS] = False
  94. self.assertEqual(self.climate.icon, "mdi:air-humidifier-off")
  95. @skip("Icon customisation not supported yet")
  96. def test_icon_is_humidifier(self):
  97. """Test that the icon is as expected."""
  98. self.dps[HVACMODE_DPS] = True
  99. self.assertEqual(self.subject.icon, "mdi:air-humidifier")
  100. self.dps[HVACMODE_DPS] = False
  101. self.assertEqual(self.subject.icon, "mdi:air-humidifier-off")
  102. def test_current_humidity(self):
  103. self.dps[CURRENTHUMID_DPS] = 47
  104. self.assertEqual(self.climate.current_humidity, 47)
  105. def test_min_target_humidity(self):
  106. self.assertEqual(self.climate.min_humidity, 40)
  107. self.assertEqual(self.subject.min_humidity, 40)
  108. def test_max_target_humidity(self):
  109. self.assertEqual(self.climate.max_humidity, 90)
  110. self.assertEqual(self.subject.max_humidity, 90)
  111. def test_target_humidity(self):
  112. self.dps[HUMIDITY_DPS] = 55
  113. self.assertEqual(self.climate.target_humidity, 55)
  114. self.assertEqual(self.subject.target_humidity, 55)
  115. def test_climate_hvac_mode(self):
  116. self.dps[HVACMODE_DPS] = True
  117. self.assertEqual(self.climate.hvac_mode, HVAC_MODE_DRY)
  118. self.dps[HVACMODE_DPS] = False
  119. self.assertEqual(self.climate.hvac_mode, HVAC_MODE_OFF)
  120. self.dps[HVACMODE_DPS] = None
  121. self.assertEqual(self.climate.hvac_mode, STATE_UNAVAILABLE)
  122. def test_climate_hvac_modes(self):
  123. self.assertCountEqual(self.climate.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_DRY])
  124. def test_is_on(self):
  125. self.dps[HVACMODE_DPS] = True
  126. self.assertEqual(self.subject.is_on, True)
  127. self.dps[HVACMODE_DPS] = False
  128. self.assertEqual(self.subject.is_on, False)
  129. self.dps[HVACMODE_DPS] = None
  130. self.assertEqual(self.subject.is_on, STATE_UNAVAILABLE)
  131. async def test_climate_turn_on(self):
  132. async with assert_device_properties_set(
  133. self.climate._device, {HVACMODE_DPS: True}
  134. ):
  135. await self.climate.async_set_hvac_mode(HVAC_MODE_DRY)
  136. async def test_climate_turn_off(self):
  137. async with assert_device_properties_set(
  138. self.climate._device, {HVACMODE_DPS: False}
  139. ):
  140. await self.climate.async_set_hvac_mode(HVAC_MODE_OFF)
  141. async def test_turn_on(self):
  142. async with assert_device_properties_set(
  143. self.subject._device, {HVACMODE_DPS: True}
  144. ):
  145. await self.subject.async_turn_on()
  146. async def test_turn_off(self):
  147. async with assert_device_properties_set(
  148. self.subject._device, {HVACMODE_DPS: False}
  149. ):
  150. await self.subject.async_turn_off()
  151. def test_preset_mode(self):
  152. self.dps[PRESET_DPS] = "sleep"
  153. self.assertEqual(self.climate.preset_mode, MODE_SLEEP)
  154. self.assertEqual(self.subject.mode, MODE_SLEEP)
  155. self.dps[PRESET_DPS] = "humidity"
  156. self.assertEqual(self.climate.preset_mode, MODE_AUTO)
  157. self.assertEqual(self.subject.mode, MODE_AUTO)
  158. self.dps[PRESET_DPS] = "work"
  159. self.assertEqual(self.climate.preset_mode, MODE_NORMAL)
  160. self.assertEqual(self.subject.mode, MODE_NORMAL)
  161. self.dps[PRESET_DPS] = None
  162. self.assertEqual(self.climate.preset_mode, None)
  163. self.assertEqual(self.subject.mode, None)
  164. def test_preset_modes(self):
  165. self.assertCountEqual(
  166. self.climate.preset_modes,
  167. {MODE_NORMAL, MODE_SLEEP, MODE_AUTO},
  168. )
  169. self.assertCountEqual(
  170. self.subject.available_modes,
  171. {MODE_NORMAL, MODE_SLEEP, MODE_AUTO},
  172. )
  173. async def test_set_climate_preset_to_auto(self):
  174. async with assert_device_properties_set(
  175. self.climate._device,
  176. {
  177. PRESET_DPS: "humidity",
  178. },
  179. ):
  180. await self.climate.async_set_preset_mode(MODE_AUTO)
  181. self.climate._device.anticipate_property_value.assert_not_called()
  182. async def test_set_climate_preset_to_sleep(self):
  183. async with assert_device_properties_set(
  184. self.climate._device,
  185. {
  186. PRESET_DPS: "sleep",
  187. },
  188. ):
  189. await self.climate.async_set_preset_mode(MODE_SLEEP)
  190. self.climate._device.anticipate_property_value.assert_not_called()
  191. async def test_set_climate_preset_to_normal(self):
  192. async with assert_device_properties_set(
  193. self.climate._device,
  194. {
  195. PRESET_DPS: "work",
  196. },
  197. ):
  198. await self.climate.async_set_preset_mode(MODE_NORMAL)
  199. self.climate._device.anticipate_property_value.assert_not_called()
  200. async def test_set_mode_to_auto(self):
  201. async with assert_device_properties_set(
  202. self.subject._device,
  203. {
  204. PRESET_DPS: "humidity",
  205. },
  206. ):
  207. await self.subject.async_set_mode(MODE_AUTO)
  208. self.subject._device.anticipate_property_value.assert_not_called()
  209. async def test_set_mode_to_sleep(self):
  210. async with assert_device_properties_set(
  211. self.subject._device,
  212. {
  213. PRESET_DPS: "sleep",
  214. },
  215. ):
  216. await self.subject.async_set_mode(MODE_SLEEP)
  217. self.subject._device.anticipate_property_value.assert_not_called()
  218. async def test_set_mode_to_normal(self):
  219. async with assert_device_properties_set(
  220. self.subject._device,
  221. {
  222. PRESET_DPS: "work",
  223. },
  224. ):
  225. await self.subject.async_set_mode(MODE_NORMAL)
  226. self.subject._device.anticipate_property_value.assert_not_called()
  227. def test_climate_device_state_attributes(self):
  228. self.dps[ERROR_DPS] = 0
  229. self.dps[TIMERHR_DPS] = "cancel"
  230. self.dps[TIMER_DPS] = 0
  231. self.assertCountEqual(
  232. self.climate.device_state_attributes,
  233. {
  234. "error": "OK",
  235. "timer_hr": "cancel",
  236. "timer_min": 0,
  237. },
  238. )
  239. self.dps[ERROR_DPS] = 1
  240. self.dps[TIMERHR_DPS] = "1"
  241. self.dps[TIMER_DPS] = 60
  242. self.assertCountEqual(
  243. self.climate.device_state_attributes,
  244. {
  245. "error": 1,
  246. "timer_hr": "1",
  247. "timer_min": 60,
  248. },
  249. )
  250. def test_device_state_attributes(self):
  251. self.dps[ERROR_DPS] = 0
  252. self.dps[TIMERHR_DPS] = "cancel"
  253. self.dps[TIMER_DPS] = 0
  254. self.dps[CURRENTHUMID_DPS] = 50
  255. self.dps[FANMODE_DPS] = "middle"
  256. self.assertCountEqual(
  257. self.subject.device_state_attributes,
  258. {
  259. "error": "OK",
  260. "timer_hr": "cancel",
  261. "timer_min": 0,
  262. "current_humidity": 50,
  263. },
  264. )
  265. def test_climate_fan_mode(self):
  266. self.dps[FANMODE_DPS] = "small"
  267. self.assertEqual(self.climate.fan_mode, FAN_LOW)
  268. self.dps[FANMODE_DPS] = "middle"
  269. self.assertEqual(self.climate.fan_mode, FAN_MEDIUM)
  270. self.dps[FANMODE_DPS] = "large"
  271. self.assertEqual(self.climate.fan_mode, FAN_HIGH)
  272. self.dps[FANMODE_DPS] = None
  273. self.assertEqual(self.climate.fan_mode, None)
  274. def test_climate_fan_modes(self):
  275. self.assertCountEqual(
  276. self.climate.fan_modes,
  277. {FAN_LOW, FAN_MEDIUM, FAN_HIGH},
  278. )
  279. async def test_climate_set_fan_mode(self):
  280. async with assert_device_properties_set(
  281. self.climate._device,
  282. {FANMODE_DPS: "small"},
  283. ):
  284. await self.climate.async_set_fan_mode(FAN_LOW)
  285. def test_switch_was_created(self):
  286. self.assertIsInstance(self.switch, TuyaLocalSwitch)
  287. def test_switch_is_same_device(self):
  288. self.assertEqual(self.switch._device, self.climate._device)
  289. def test_switch_class_is_switch(self):
  290. self.assertEqual(self.switch.device_class, DEVICE_CLASS_SWITCH)
  291. def test_switch_is_on(self):
  292. self.dps[SWITCH_DPS] = True
  293. self.assertTrue(self.switch.is_on)
  294. self.dps[SWITCH_DPS] = False
  295. self.assertFalse(self.switch.is_on)
  296. def test_switch_is_on_when_unavailable(self):
  297. self.dps[SWITCH_DPS] = None
  298. self.assertEqual(self.switch.is_on, STATE_UNAVAILABLE)
  299. async def test_switch_turn_on(self):
  300. async with assert_device_properties_set(
  301. self.switch._device, {SWITCH_DPS: True}
  302. ):
  303. await self.switch.async_turn_on()
  304. async def test_switch_turn_off(self):
  305. async with assert_device_properties_set(
  306. self.switch._device, {SWITCH_DPS: False}
  307. ):
  308. await self.switch.async_turn_off()
  309. async def test_toggle_turns_the_switch_on_when_it_was_off(self):
  310. self.dps[SWITCH_DPS] = False
  311. async with assert_device_properties_set(
  312. self.switch._device, {SWITCH_DPS: True}
  313. ):
  314. await self.switch.async_toggle()
  315. async def test_toggle_turns_the_switch_off_when_it_was_on(self):
  316. self.dps[SWITCH_DPS] = True
  317. async with assert_device_properties_set(
  318. self.switch._device, {SWITCH_DPS: False}
  319. ):
  320. await self.switch.async_toggle()
  321. def test_switch_returns_none_for_power(self):
  322. self.assertIsNone(self.switch.current_power_w)
  323. def test_switch_state_attributes_set(self):
  324. self.assertEqual(self.switch.device_state_attributes, {})