test_beok_tr9b_thermostat.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. from homeassistant.components.binary_sensor import BinarySensorDeviceClass
  2. from homeassistant.components.climate.const import (
  3. HVAC_MODE_AUTO,
  4. HVAC_MODE_HEAT,
  5. HVAC_MODE_OFF,
  6. SUPPORT_TARGET_TEMPERATURE,
  7. )
  8. from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
  9. from ..const import BEOK_TR9B_PAYLOAD
  10. from ..helpers import assert_device_properties_set
  11. from ..mixins.climate import TargetTemperatureTests
  12. from ..mixins.binary_sensor import MultiBinarySensorTests
  13. from ..mixins.lock import BasicLockTests
  14. from ..mixins.number import MultiNumberTests
  15. from ..mixins.select import MultiSelectTests
  16. from ..mixins.switch import BasicSwitchTests
  17. from .base_device_tests import TuyaDeviceTestCase
  18. POWER_DPS = "1"
  19. HVACMODE_DPS = "2"
  20. ANTIFROST_DPS = "10"
  21. TEMPERATURE_DPS = "16"
  22. MAXTEMP_DPS = "19"
  23. UNIT_DPS = "23"
  24. CURRENTTEMP_DPS = "24"
  25. MINTEMP_DPS = "26"
  26. SCHED_DPS = "31"
  27. VALVE_DPS = "36"
  28. LOCK_DPS = "40"
  29. ERROR_DPS = "45"
  30. UNKNOWN101_DPS = "101"
  31. UNKNOWN102_DPS = "102"
  32. class TestBeokTR9BThermostat(
  33. MultiBinarySensorTests,
  34. BasicLockTests,
  35. MultiNumberTests,
  36. MultiSelectTests,
  37. BasicSwitchTests,
  38. TargetTemperatureTests,
  39. TuyaDeviceTestCase,
  40. ):
  41. __test__ = True
  42. def setUp(self):
  43. self.setUpForConfig(
  44. "beok_tr9b_thermostat.yaml",
  45. BEOK_TR9B_PAYLOAD,
  46. )
  47. self.subject = self.entities.get("climate")
  48. self.setUpTargetTemperature(
  49. TEMPERATURE_DPS,
  50. self.subject,
  51. min=41.0,
  52. max=99.0,
  53. scale=10,
  54. step=10,
  55. )
  56. self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
  57. self.setUpMultiSelect(
  58. [
  59. {
  60. "dps": SCHED_DPS,
  61. "name": "select_schedule",
  62. "options": {
  63. "5_2": "Weekday+Weekend",
  64. "6_1": "Mon-Sat+Sun",
  65. "7": "Daily",
  66. },
  67. },
  68. {
  69. "dps": UNIT_DPS,
  70. "name": "select_temperature_unit",
  71. "options": {
  72. "c": "Celsius",
  73. "f": "Fahrenheit",
  74. },
  75. },
  76. ],
  77. )
  78. self.setUpBasicSwitch(
  79. ANTIFROST_DPS,
  80. self.entities.get("switch_anti_frost"),
  81. )
  82. self.setUpMultiBinarySensors(
  83. [
  84. {
  85. "dps": ERROR_DPS,
  86. "name": "binary_sensor_error",
  87. "device_class": BinarySensorDeviceClass.PROBLEM,
  88. "testdata": (1, 0),
  89. },
  90. {
  91. "dps": VALVE_DPS,
  92. "name": "binary_sensor_valve",
  93. "device_class": BinarySensorDeviceClass.OPENING,
  94. "testdata": ("open", "close"),
  95. },
  96. ],
  97. )
  98. self.setUpMultiNumber(
  99. [
  100. {
  101. "dps": MINTEMP_DPS,
  102. "name": "number_low_temperature_limit",
  103. "min": 5.0,
  104. "max": 1000.0,
  105. "step": 1.0,
  106. "scale": 10,
  107. "unit": TEMP_CELSIUS,
  108. },
  109. {
  110. "dps": MAXTEMP_DPS,
  111. "name": "number_high_temperature_limit",
  112. "min": 5.0,
  113. "max": 1000.0,
  114. "step": 1.0,
  115. "scale": 10,
  116. "unit": TEMP_CELSIUS,
  117. },
  118. ],
  119. )
  120. self.mark_secondary(
  121. [
  122. "binary_sensor_error",
  123. "binary_sensor_valve",
  124. "lock_child_lock",
  125. "number_low_temperature_limit",
  126. "number_high_temperature_limit",
  127. "select_schedule",
  128. "select_temperature_unit",
  129. "switch_anti_frost",
  130. ],
  131. )
  132. def test_supported_features(self):
  133. self.assertEqual(
  134. self.subject.supported_features,
  135. SUPPORT_TARGET_TEMPERATURE,
  136. )
  137. def test_temperature_unit(self):
  138. self.dps[UNIT_DPS] = "c"
  139. self.assertEqual(
  140. self.subject.temperature_unit,
  141. TEMP_CELSIUS,
  142. )
  143. self.assertEqual(self.subject.target_temperature_step, 0.5)
  144. self.dps[UNIT_DPS] = "f"
  145. self.assertEqual(
  146. self.subject.temperature_unit,
  147. TEMP_FAHRENHEIT,
  148. )
  149. self.assertEqual(self.subject.target_temperature_step, 1.0)
  150. def test_current_temperature(self):
  151. self.dps[CURRENTTEMP_DPS] = 685
  152. self.assertEqual(self.subject.current_temperature, 68.5)
  153. def test_hvac_mode(self):
  154. self.dps[POWER_DPS] = False
  155. self.dps[HVACMODE_DPS] = "auto"
  156. self.assertEqual(self.subject.hvac_mode, HVAC_MODE_OFF)
  157. self.dps[POWER_DPS] = True
  158. self.assertEqual(self.subject.hvac_mode, HVAC_MODE_AUTO)
  159. self.dps[HVACMODE_DPS] = "manual"
  160. self.assertEqual(self.subject.hvac_mode, HVAC_MODE_HEAT)
  161. self.dps[HVACMODE_DPS] = None
  162. self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
  163. def test_hvac_modes(self):
  164. self.assertCountEqual(
  165. self.subject.hvac_modes,
  166. [
  167. HVAC_MODE_HEAT,
  168. HVAC_MODE_AUTO,
  169. HVAC_MODE_OFF,
  170. ],
  171. )
  172. # Override - since min and max are set by attributes, the range
  173. # allowed when setting is wider than normal. The thermostat seems
  174. # to be configurable as at least a water heater (to 212F), as tuya
  175. # doc says max 1000.0 (after scaling)
  176. async def test_set_target_temperature_fails_outside_valid_range(self):
  177. with self.assertRaisesRegex(
  178. ValueError,
  179. f"temperature \\(4.5\\) must be between 5.0 and 1000.0",
  180. ):
  181. await self.subject.async_set_target_temperature(4.5)
  182. with self.assertRaisesRegex(
  183. ValueError,
  184. f"temperature \\(1001\\) must be between 5.0 and 1000.0",
  185. ):
  186. await self.subject.async_set_target_temperature(1001)
  187. def test_extra_state_attributes(self):
  188. self.dps[ERROR_DPS] = 8
  189. self.dps[UNKNOWN101_DPS] = 101
  190. self.dps[UNKNOWN102_DPS] = 102
  191. self.assertDictEqual(
  192. self.subject.extra_state_attributes,
  193. {"Error Code": 8, "unknown_101": 101, "unknown_102": 102},
  194. )
  195. def test_icons(self):
  196. self.dps[LOCK_DPS] = True
  197. self.assertEqual(self.basicLock.icon, "mdi:hand-back-right-off")
  198. self.dps[LOCK_DPS] = False
  199. self.assertEqual(self.basicLock.icon, "mdi:hand-back-right")