test_beok_tr9b_thermostat.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. from homeassistant.components.binary_sensor import BinarySensorDeviceClass
  2. from homeassistant.components.climate.const import (
  3. ClimateEntityFeature,
  4. HVACMode,
  5. )
  6. from homeassistant.components.number.const import NumberDeviceClass
  7. from homeassistant.const import PRECISION_TENTHS, UnitOfTemperature
  8. from ..const import BEOK_TR9B_PAYLOAD
  9. from ..mixins.climate import TargetTemperatureTests
  10. from ..mixins.binary_sensor import MultiBinarySensorTests
  11. from ..mixins.lock import BasicLockTests
  12. from ..mixins.number import MultiNumberTests
  13. from ..mixins.select import MultiSelectTests
  14. from ..mixins.switch import BasicSwitchTests
  15. from .base_device_tests import TuyaDeviceTestCase
  16. POWER_DPS = "1"
  17. HVACMODE_DPS = "2"
  18. ANTIFROST_DPS = "10"
  19. TEMPERATURE_DPS = "16"
  20. MAXTEMP_DPS = "19"
  21. UNIT_DPS = "23"
  22. CURRENTTEMP_DPS = "24"
  23. MINTEMP_DPS = "26"
  24. SCHED_DPS = "31"
  25. VALVE_DPS = "36"
  26. LOCK_DPS = "40"
  27. ERROR_DPS = "45"
  28. UNKNOWN101_DPS = "101"
  29. UNKNOWN102_DPS = "102"
  30. class TestBeokTR9BThermostat(
  31. MultiBinarySensorTests,
  32. BasicLockTests,
  33. MultiNumberTests,
  34. MultiSelectTests,
  35. BasicSwitchTests,
  36. TargetTemperatureTests,
  37. TuyaDeviceTestCase,
  38. ):
  39. __test__ = True
  40. def setUp(self):
  41. self.setUpForConfig(
  42. "beok_tr9b_thermostat.yaml",
  43. BEOK_TR9B_PAYLOAD,
  44. )
  45. self.subject = self.entities.get("climate")
  46. self.setUpTargetTemperature(
  47. TEMPERATURE_DPS,
  48. self.subject,
  49. min=41.0,
  50. max=99.0,
  51. scale=10,
  52. step=10,
  53. )
  54. self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
  55. self.setUpMultiSelect(
  56. [
  57. {
  58. "dps": SCHED_DPS,
  59. "name": "select_schedule",
  60. "options": {
  61. "5_2": "Weekday+Weekend",
  62. "6_1": "Mon-Sat+Sun",
  63. "7": "Daily",
  64. },
  65. },
  66. {
  67. "dps": UNIT_DPS,
  68. "name": "select_temperature_unit",
  69. "options": {
  70. "c": "Celsius",
  71. "f": "Fahrenheit",
  72. },
  73. },
  74. ],
  75. )
  76. self.setUpBasicSwitch(
  77. ANTIFROST_DPS,
  78. self.entities.get("switch_anti_frost"),
  79. )
  80. self.setUpMultiBinarySensors(
  81. [
  82. {
  83. "dps": ERROR_DPS,
  84. "name": "binary_sensor_error",
  85. "device_class": BinarySensorDeviceClass.PROBLEM,
  86. "testdata": (1, 0),
  87. },
  88. {
  89. "dps": VALVE_DPS,
  90. "name": "binary_sensor_valve",
  91. "device_class": BinarySensorDeviceClass.OPENING,
  92. "testdata": ("open", "close"),
  93. },
  94. ],
  95. )
  96. self.setUpMultiNumber(
  97. [
  98. {
  99. "dps": MINTEMP_DPS,
  100. "name": "number_low_temperature_limit",
  101. "device_class": NumberDeviceClass.TEMPERATURE,
  102. "min": 5.0,
  103. "max": 1000.0,
  104. "step": 1.0,
  105. "scale": 10,
  106. "unit": UnitOfTemperature.CELSIUS,
  107. },
  108. {
  109. "dps": MAXTEMP_DPS,
  110. "name": "number_high_temperature_limit",
  111. "device_class": NumberDeviceClass.TEMPERATURE,
  112. "min": 5.0,
  113. "max": 1000.0,
  114. "step": 1.0,
  115. "scale": 10,
  116. "unit": UnitOfTemperature.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. ClimateEntityFeature.TARGET_TEMPERATURE,
  136. )
  137. def test_temperature_unit(self):
  138. self.dps[UNIT_DPS] = "c"
  139. self.assertEqual(
  140. self.subject.temperature_unit,
  141. UnitOfTemperature.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. UnitOfTemperature.FAHRENHEIT,
  148. )
  149. self.assertEqual(self.subject.target_temperature_step, 1.0)
  150. def test_precision(self):
  151. self.assertEqual(self.subject.precision, PRECISION_TENTHS)
  152. def test_current_temperature(self):
  153. self.dps[CURRENTTEMP_DPS] = 685
  154. self.assertEqual(self.subject.current_temperature, 68.5)
  155. def test_hvac_mode(self):
  156. self.dps[POWER_DPS] = False
  157. self.dps[HVACMODE_DPS] = "auto"
  158. self.assertEqual(self.subject.hvac_mode, HVACMode.OFF)
  159. self.dps[POWER_DPS] = True
  160. self.assertEqual(self.subject.hvac_mode, HVACMode.AUTO)
  161. self.dps[HVACMODE_DPS] = "manual"
  162. self.assertEqual(self.subject.hvac_mode, HVACMode.HEAT)
  163. def test_hvac_modes(self):
  164. self.assertCountEqual(
  165. self.subject.hvac_modes,
  166. [
  167. HVACMode.HEAT,
  168. HVACMode.AUTO,
  169. HVACMode.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")