فهرست منبع

Add tests for madimack_elite_v3_heatpump.yaml

- Refactor target temperature tests into a mixin.
  There are a number of tests related to target temperature, and they are
  basically the same across all devices, so can be commonized.
Jason Rumney 4 سال پیش
والد
کامیت
1d9142d6b8
39فایلهای تغییر یافته به همراه873 افزوده شده و 1692 حذف شده
  1. 145 57
      custom_components/tuya_local/devices/madimack_elite_v3_heatpump.yaml
  2. 24 1
      tests/const.py
  3. 8 48
      tests/devices/test_andersson_gsh_heater.py
  4. 8 47
      tests/devices/test_awow_th213_thermostat.py
  5. 13 48
      tests/devices/test_beca_bhp6000_thermostat.py
  6. 13 44
      tests/devices/test_beca_bht002_thermostat.py
  7. 9 44
      tests/devices/test_beca_bht6000_thermostat.py
  8. 12 48
      tests/devices/test_bwt_heatpump.py
  9. 8 48
      tests/devices/test_carson_cb.py
  10. 12 66
      tests/devices/test_eberg_qubo_q40hd_heatpump.py
  11. 11 48
      tests/devices/test_electriq_12wminv_heatpump.py
  12. 12 41
      tests/devices/test_electriq_desd9lw_dehumidifier.py
  13. 10 48
      tests/devices/test_eurom_600_heater.py
  14. 10 48
      tests/devices/test_eurom_601_heater.py
  15. 11 48
      tests/devices/test_eurom_walldesignheat2000_heater.py
  16. 10 56
      tests/devices/test_fersk_vind_2_climate.py
  17. 12 48
      tests/devices/test_gardenpac_heatpump.py
  18. 12 48
      tests/devices/test_goldair_geco_heater.py
  19. 12 48
      tests/devices/test_goldair_gpcv_heater.py
  20. 8 46
      tests/devices/test_goldair_gpph_heater.py
  21. 12 42
      tests/devices/test_hellnar_heatpump.py
  22. 10 48
      tests/devices/test_kogan_kahtp_heater.py
  23. 8 48
      tests/devices/test_kogan_kashmfp20ba_heater.py
  24. 13 48
      tests/devices/test_kogan_kawfhtp_heater.py
  25. 10 48
      tests/devices/test_kogan_kawfpac09ya_airconditioner.py
  26. 250 0
      tests/devices/test_madimack_elitev3_heatpump.py
  27. 12 48
      tests/devices/test_madimack_heatpump.py
  28. 12 48
      tests/devices/test_minco_mh1823d_thermostat.py
  29. 11 44
      tests/devices/test_moes_bht002_thermostat.py
  30. 10 48
      tests/devices/test_nedis_htpl20f_heater.py
  31. 12 48
      tests/devices/test_poolex_silverline_heatpump.py
  32. 12 48
      tests/devices/test_poolex_vertigo_heatpump.py
  33. 12 49
      tests/devices/test_purline_m100_heater.py
  34. 10 48
      tests/devices/test_remora_heatpump.py
  35. 10 26
      tests/devices/test_saswell_c16_thermostat.py
  36. 15 31
      tests/devices/test_saswell_t29utk_thermostat.py
  37. 8 42
      tests/devices/test_tadiran_wind_heatpump.py
  38. 13 48
      tests/devices/test_wetair_wch750_heater.py
  39. 73 0
      tests/mixins/climate.py

+ 145 - 57
custom_components/tuya_local/devices/madimack_elite_v3_heatpump.yaml

@@ -10,7 +10,7 @@ primary_entity:
           value: "off"
           icon: "mdi:hvac-off"
           icon_priority: 1
-      hidden: false
+      hidden: true
     - id: 2
       name: hvac_mode
       type: string
@@ -23,7 +23,7 @@ primary_entity:
             - dps_val: false
               value_redirect: power
             - dps_val: true
-              value: auto
+              value: heat_cool
         - dps_val: cold
           icon: "mdi:snowflake"
           icon_priority: 2
@@ -43,22 +43,6 @@ primary_entity:
               value_redirect: power
             - dps_val: true
               value: heat
-    - id: 102
-      name: current_temperature
-      type: integer
-      readonly: true
-    - id: 6
-      name: temperature_unit
-      type: string
-      mapping:
-        - dps_val: f
-          value: F
-        - dps_val: c
-          value: C
-    - id: 20
-      name: power_level
-      type: integer
-      readonly: true
     - id: 4
       name: temperature
       type: integer
@@ -68,26 +52,11 @@ primary_entity:
             - dps_val: f
               range:
                 min: 60
-                max: 104 # is it?
+                max: 104
       range:
         min: 18
         max: 40
     # ^ in reality, the range is different for cool and auto
-    - id: 2
-      type: string
-      name: mode
-    - id: 21
-      type: integer
-      name: max_temperature
-      readonly: true
-    - id: 22
-      type: integer
-      name: min_temperature
-      readonly: true
-    - id: 23
-      type: integer
-      name: c4_evaporator_coil_pipe_temperature
-      readonly: true
     - id: 5
       name: preset_mode
       type: string
@@ -98,32 +67,24 @@ primary_entity:
           value: Perfect
         - dps_val: boost
           value: Power
-    - id: 24
-      name: c3_exhaust_gas_temperature
-      type: integer
-      readonly: true
-    - id: 25
-      name: c1_outlet_water_temperature
-      type: integer
-      readonly: true
-    - id: 26
-      name: c2_ambient_temperature
-      type: integer
-      readonly: true
-    - id: 103
-      name: c5_return_gas_temperature
-      type: integer
-      readonly: true
-    - id: 104
-      name: c6_cooling_coil_pipe_temperature
+    - id: 6
+      name: temperature_unit
+      type: string
+      mapping:
+        - dps_val: f
+          value: F
+        - dps_val: c
+          value: C
+    - id: 21
       type: integer
+      name: max_temperature
       readonly: true
-    - id: 105
-      name: c9_cooling_plate_temperature
+    - id: 22
       type: integer
+      name: min_temperature
       readonly: true
-    - id: 106
-      name: eev_opening
+    - id: 102
+      name: current_temperature
       type: integer
       readonly: true
     - id: 15
@@ -135,7 +96,7 @@ primary_entity:
       type: integer
       readonly: true
     - id: 107
-      name: unknown_130
+      name: unknown_107
       type: boolean
       readonly: true
 secondary_entities:
@@ -149,3 +110,130 @@ secondary_entities:
         name: sensor
         unit: "%"
         readonly: true
+  - entity: sensor
+    category: diagnostic
+    name: Evaporator Coil Pipe Temperature
+    class: temperature
+    dps:
+      - id: 23
+        type: integer
+        name: sensor
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: Exhaust Gas Temperature
+    class: temperature
+    dps:
+      - id: 24
+        name: sensor
+        type: integer
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: Outlet Water Temperature
+    class: temperature
+    dps:
+      - id: 25
+        name: sensor
+        type: integer
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: Ambient Temperature
+    class: temperature
+    dps:
+      - id: 26
+        name: sensor
+        type: integer
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: Return Gas Temperature
+    class: temperature
+    dps:
+      - id: 103
+        name: sensor
+        type: integer
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: Cooling Coil Pipe Temperature
+    class: temperature
+    dps:
+      - id: 104
+        name: sensor
+        type: integer
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: Cooling Plate Temperature
+    class: temperature
+    dps:
+      - id: 105
+        name: sensor
+        type: integer
+        readonly: true
+      - id: 6
+        name: unit
+        type: string
+        mapping:
+          - dps_val: f
+            value: F
+          - dps_val: c
+            value: C
+  - entity: sensor
+    category: diagnostic
+    name: EEV Opening
+    dps:
+      - id: 106
+        name: sensor
+        type: integer
+        readonly: true

+ 24 - 1
tests/const.py

@@ -171,6 +171,29 @@ MADIMACK_HEATPUMP_PAYLOAD = {
     "140": "LowSpeed",
 }
 
+MADIMACK_ELITEV3_HEATPUMP_PAYLOAD = {
+    "1": True,
+    "2": "heating",
+    "4": 28,
+    "5": "power",
+    "6": "c",
+    "15": 0,
+    "20": 50,
+    "21": 40,
+    "22": 18,
+    "23": 45,
+    "24": 40,
+    "25": 33,
+    "26": 18,
+    "101": 0,
+    "102": 21,
+    "103": 23,
+    "104": 18,
+    "105": 18,
+    "106": 480,
+    "107": False,
+}
+
 PURLINE_M100_HEATER_PAYLOAD = {
     "1": True,
     "2": 23,
@@ -320,7 +343,7 @@ HELLNAR_HEATPUMP_PAYLOAD = {
     "1": False,
     "2": 260,
     "3": 26,
-    "4": "wet",
+    "4": "cold",
     "5": "low",
     "18": 0,
     "20": 0,

+ 8 - 48
tests/devices/test_andersson_gsh_heater.py

@@ -8,6 +8,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import GSH_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -17,12 +18,18 @@ PRESET_DPS = "4"
 ERROR_DPS = "12"
 
 
-class TestAnderssonGSHHeater(TuyaDeviceTestCase):
+class TestAnderssonGSHHeater(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("andersson_gsh_heater.yaml", GSH_HEATER_PAYLOAD)
         self.subject = self.entities["climate"]
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=35,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -42,25 +49,6 @@ class TestAnderssonGSHHeater(TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "low"}
@@ -73,34 +61,6 @@ class TestAnderssonGSHHeater(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="High")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 35"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 5 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 8 - 47
tests/devices/test_awow_th213_thermostat.py

@@ -15,6 +15,7 @@ from homeassistant.const import (
 
 from ..const import TH213_THERMOSTAT_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import MultiNumberTests
 from ..mixins.select import BasicSelectTests
@@ -42,6 +43,7 @@ class TestAwowTH213Thermostat(
     BasicSelectTests,
     BasicSensorTests,
     MultiNumberTests,
+    TargetTemperatureTests,
     TuyaDeviceTestCase,
 ):
     __test__ = True
@@ -49,6 +51,12 @@ class TestAwowTH213Thermostat(
     def setUp(self):
         self.setUpForConfig("awow_th213_thermostat.yaml", TH213_THERMOSTAT_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=30,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicSensor(
             EXTERNTEMP_DPS,
@@ -107,25 +115,6 @@ class TestAwowTH213Thermostat(
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 30)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(self.subject._device, {PRESET_DPS: 2}):
             await self.subject.async_set_temperature(preset_mode="Away")
@@ -142,34 +131,6 @@ class TestAwowTH213Thermostat(
                 temperature=25, preset_mode="Smart"
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 30"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(31\\) must be between 5 and 30"
-        ):
-            await self.subject.async_set_target_temperature(31)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 13 - 48
tests/devices/test_beca_bhp6000_thermostat.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import BECA_BHP6000_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.light import BasicLightTests
 from ..mixins.lock import BasicLockTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -25,12 +26,23 @@ FAN_DPS = "6"
 LOCK_DPS = "7"
 
 
-class TestBecaBHP6000Thermostat(BasicLightTests, BasicLockTests, TuyaDeviceTestCase):
+class TestBecaBHP6000Thermostat(
+    BasicLightTests,
+    BasicLockTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("beca_bhp6000_thermostat_f.yaml", BECA_BHP6000_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=40,
+            max=95,
+        )
         self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
@@ -43,25 +55,6 @@ class TestBecaBHP6000Thermostat(BasicLightTests, BasicLockTests, TuyaDeviceTestC
     def test_temperature_unit_returns_configured_temperature_unit(self):
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 40)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 95)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 80}
-        ):
-            await self.subject.async_set_temperature(temperature=80)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(self.subject._device, {PRESET_DPS: 1}):
             await self.subject.async_set_temperature(preset_mode="Schedule")
@@ -78,34 +71,6 @@ class TestBecaBHP6000Thermostat(BasicLightTests, BasicLockTests, TuyaDeviceTestC
                 temperature=78, preset_mode="Holiday Hold"
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 75},
-        ):
-            await self.subject.async_set_target_temperature(75)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 78}
-        ):
-            await self.subject.async_set_target_temperature(77.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(39\\) must be between 40 and 95"
-        ):
-            await self.subject.async_set_target_temperature(39)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(96\\) must be between 40 and 95"
-        ):
-            await self.subject.async_set_target_temperature(96)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 70
         self.assertEqual(self.subject.current_temperature, 70)

+ 13 - 44
tests/devices/test_beca_bht002_thermostat.py

@@ -15,6 +15,7 @@ from homeassistant.const import (
 
 from ..const import BECA_BHT002_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.light import BasicLightTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.sensor import BasicSensorTests
@@ -31,7 +32,11 @@ UNKNOWN104_DPS = "104"
 
 
 class TestBecaBHT002Thermostat(
-    BasicLightTests, BasicLockTests, BasicSensorTests, TuyaDeviceTestCase
+    BasicLightTests,
+    BasicLockTests,
+    BasicSensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
 ):
     __test__ = True
 
@@ -41,6 +46,13 @@ class TestBecaBHT002Thermostat(
             BECA_BHT002_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5.0,
+            max=35.0,
+            scale=2,
+        )
         self.setUpBasicLight(POWER_DPS, self.entities.get("light_display"))
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicSensor(
@@ -64,25 +76,6 @@ class TestBecaBHT002Thermostat(
             self.subject._device.temperature_unit,
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 50
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 0.5)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 41}
-        ):
-            await self.subject.async_set_temperature(temperature=20.5)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: True}
@@ -101,30 +94,6 @@ class TestBecaBHT002Thermostat(
                 temperature=22, preset_mode=PRESET_COMFORT
             )
 
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 45},
-        ):
-            await self.subject.async_set_target_temperature(22.5)
-
-    async def test_set_target_temperature_rounds_value_to_closest_half(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 35}
-        ):
-            await self.subject.async_set_target_temperature(17.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(4.5)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(35.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(35.5)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 44
         self.assertEqual(self.subject.current_temperature, 22)

+ 9 - 44
tests/devices/test_beca_bht6000_thermostat.py

@@ -15,6 +15,7 @@ from homeassistant.const import (
 
 from ..const import BECA_BHT6000_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.light import BasicLightTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.sensor import BasicSensorTests
@@ -32,7 +33,11 @@ UNKNOWN104_DPS = "104"
 
 
 class TestBecaBHT6000Thermostat(
-    BasicLightTests, BasicLockTests, BasicSensorTests, TuyaDeviceTestCase
+    BasicLightTests,
+    BasicLockTests,
+    BasicSensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
 ):
     __test__ = True
 
@@ -42,6 +47,9 @@ class TestBecaBHT6000Thermostat(
             BECA_BHT6000_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS, self.subject, min=5.0, max=35.0, scale=2
+        )
         self.setUpBasicLight(POWER_DPS, self.entities.get("light_display"))
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicSensor(
@@ -65,25 +73,6 @@ class TestBecaBHT6000Thermostat(
             self.subject._device.temperature_unit,
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 50
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 0.5)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 41}
-        ):
-            await self.subject.async_set_temperature(temperature=20.5)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: True}
@@ -102,30 +91,6 @@ class TestBecaBHT6000Thermostat(
                 temperature=22, preset_mode=PRESET_COMFORT
             )
 
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 45},
-        ):
-            await self.subject.async_set_target_temperature(22.5)
-
-    async def test_set_target_temperature_rounds_value_to_closest_half(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 35}
-        ):
-            await self.subject.async_set_target_temperature(17.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(4.5)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(35.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(35.5)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 44
         self.assertEqual(self.subject.current_temperature, 22)

+ 12 - 48
tests/devices/test_bwt_heatpump.py

@@ -10,6 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import BWT_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -19,12 +20,22 @@ PRESET_DPS = "4"
 ERROR_DPS = "9"
 
 
-class TestBWTHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
+class TestBWTHeatpump(
+    BasicBinarySensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("bwt_heatpump.yaml", BWT_HEATPUMP_PAYLOAD)
         self.subject = self.entities["climate"]
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=40,
+        )
         self.setUpBasicBinarySensor(
             ERROR_DPS,
             self.entities.get("binary_sensor_water_flow"),
@@ -49,25 +60,6 @@ class TestBWTHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "cool"}
@@ -82,34 +74,6 @@ class TestBWTHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
                 temperature=26, preset_mode="Smart Heating"
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 40"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(41\\) must be between 5 and 40"
-        ):
-            await self.subject.async_set_target_temperature(41)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 8 - 48
tests/devices/test_carson_cb.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import CARSON_CB_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 POWER_DPS = "1"
@@ -28,12 +29,18 @@ UNKNOWN106_DPS = "106"
 UNKNOWN110_DPS = "110"
 
 
-class TestCarsonCBHeatpump(TuyaDeviceTestCase):
+class TestCarsonCBHeatpump(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("carson_cb.yaml", CARSON_CB_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=16,
+            max=30,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -60,53 +67,6 @@ class TestCarsonCBHeatpump(TuyaDeviceTestCase):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 16)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 30)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 16 and 30"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(31\\) must be between 16 and 30"
-        ):
-            await self.subject.async_set_target_temperature(31)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 66
tests/devices/test_eberg_qubo_q40hd_heatpump.py

@@ -21,6 +21,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import EBERG_QUBO_Q40HD_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.number import BasicNumberTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -36,7 +37,9 @@ SWING_DPS = "30"
 HVACACTION_DPS = "101"
 
 
-class TestEbergQuboQ40HDHeatpump(BasicNumberTests, TuyaDeviceTestCase):
+class TestEbergQuboQ40HDHeatpump(
+    BasicNumberTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -45,6 +48,12 @@ class TestEbergQuboQ40HDHeatpump(BasicNumberTests, TuyaDeviceTestCase):
             EBERG_QUBO_Q40HD_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=17,
+            max=30,
+        )
         self.setUpBasicNumber(TIMER_DPS, self.entities.get("number_timer"), max=24)
 
     def test_supported_features(self):
@@ -73,77 +82,14 @@ class TestEbergQuboQ40HDHeatpump(BasicNumberTests, TuyaDeviceTestCase):
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.dps[UNIT_DPS] = "c"
-        self.assertEqual(self.subject.min_temp, 17)
+    def test_minimum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.min_temp, 63)
 
-    def test_maximum_target_temperature(self):
-        self.dps[UNIT_DPS] = "c"
-        self.assertEqual(self.subject.max_temp, 30)
+    def test_maximum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.max_temp, 86)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-        self.dps[UNIT_DPS] = "f"
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 70},
-        ):
-            await self.subject.async_set_target_temperature(70)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(16\\) must be between 17 and 30"
-        ):
-            await self.subject.async_set_target_temperature(16)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(31\\) must be between 17 and 30"
-        ):
-            await self.subject.async_set_target_temperature(31)
-
-        self.dps[UNIT_DPS] = "f"
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(62\\) must be between 63 and 86"
-        ):
-            await self.subject.async_set_target_temperature(62)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(87\\) must be between 63 and 86"
-        ):
-            await self.subject.async_set_target_temperature(87)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 11 - 48
tests/devices/test_electriq_12wminv_heatpump.py

@@ -13,6 +13,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import ELECTRIQ_12WMINV_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.light import BasicLightTests
 from ..mixins.switch import BasicSwitchTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -36,7 +37,10 @@ UNKNOWN110_DPS = "110"
 
 
 class TestElectriq12WMINVHeatpump(
-    BasicLightTests, BasicSwitchTests, TuyaDeviceTestCase
+    BasicLightTests,
+    BasicSwitchTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
 ):
     __test__ = True
 
@@ -45,6 +49,12 @@ class TestElectriq12WMINVHeatpump(
             "electriq_12wminv_heatpump.yaml", ELECTRIQ_12WMINV_HEATPUMP_PAYLOAD
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=16,
+            max=32,
+        )
         self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
         self.setUpBasicSwitch(SWITCH_DPS, self.entities.get("switch_sleep"))
 
@@ -74,53 +84,6 @@ class TestElectriq12WMINVHeatpump(
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 16)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 32)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 16 and 32"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(33\\) must be between 16 and 32"
-        ):
-            await self.subject.async_set_target_temperature(33)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 41
tests/devices/test_electriq_desd9lw_dehumidifier.py

@@ -17,6 +17,7 @@ from homeassistant.const import (
 
 from ..const import ELECTRIQ_DESD9LW_DEHUMIDIFIER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.light import BasicLightTests
 from ..mixins.sensor import BasicSensorTests
 from ..mixins.switch import BasicSwitchTests
@@ -35,7 +36,11 @@ TEMPERATURE_DPS = "101"
 
 
 class TestElectriqDESD9LWDehumidifier(
-    BasicLightTests, BasicSensorTests, BasicSwitchTests, TuyaDeviceTestCase
+    BasicLightTests,
+    BasicSensorTests,
+    BasicSwitchTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
 ):
     __test__ = True
 
@@ -45,6 +50,12 @@ class TestElectriqDESD9LWDehumidifier(
             ELECTRIQ_DESD9LW_DEHUMIDIFIER_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=16,
+            max=30,
+        )
         self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_uv_sterilization"))
         self.setUpBasicSwitch(SWITCH_DPS, self.entities.get("switch_ionizer"))
         self.setUpBasicSensor(
@@ -82,46 +93,6 @@ class TestElectriqDESD9LWDehumidifier(
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 24
-        self.assertEqual(self.subject.target_temperature, 24)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 16)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 30)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 22}
-        ):
-            await self.subject.async_set_temperature(temperature=22)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 16 and 30"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(31\\) must be between 16 and 30"
-        ):
-            await self.subject.async_set_target_temperature(31)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 21
         self.assertEqual(self.subject.current_temperature, 21)

+ 10 - 48
tests/devices/test_eurom_600_heater.py

@@ -9,6 +9,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import EUROM_600_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -17,12 +18,20 @@ CURRENTTEMP_DPS = "5"
 ERROR_DPS = "6"
 
 
-class TestEurom600Heater(BasicBinarySensorTests, TuyaDeviceTestCase):
+class TestEurom600Heater(
+    BasicBinarySensorTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("eurom_600_heater.yaml", EUROM_600_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         self.setUpBasicBinarySensor(
             ERROR_DPS,
             self.entities.get("binary_sensor_error"),
@@ -48,53 +57,6 @@ class TestEurom600Heater(BasicBinarySensorTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 10 - 48
tests/devices/test_eurom_601_heater.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import EUROM_601_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -21,12 +22,20 @@ PRESET_DPS = "6"
 ERROR_DPS = "13"
 
 
-class TestEurom601Heater(BasicBinarySensorTests, TuyaDeviceTestCase):
+class TestEurom601Heater(
+    BasicBinarySensorTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("eurom_601_heater.yaml", EUROM_601_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         self.setUpBasicBinarySensor(
             ERROR_DPS,
             self.entities.get("binary_sensor_error"),
@@ -52,53 +61,6 @@ class TestEurom601Heater(BasicBinarySensorTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 11 - 48
tests/devices/test_eurom_walldesignheat2000_heater.py

@@ -15,6 +15,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import EUROM_WALLDESIGNHEAT2000_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -24,7 +25,10 @@ PRESET_DPS = "4"
 SWING_DPS = "7"
 
 
-class TestEuromWallDesignheat2000Heater(TuyaDeviceTestCase):
+class TestEuromWallDesignheat2000Heater(
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
@@ -33,6 +37,12 @@ class TestEuromWallDesignheat2000Heater(TuyaDeviceTestCase):
             EUROM_WALLDESIGNHEAT2000_HEATER_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=10,
+            max=35,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -54,53 +64,6 @@ class TestEuromWallDesignheat2000Heater(TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 10)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(9\\) must be between 10 and 35"
-        ):
-            await self.subject.async_set_target_temperature(9)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 10 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 10 - 56
tests/devices/test_fersk_vind_2_climate.py

@@ -14,6 +14,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import FERSK_VIND2_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 POWER_DPS = "1"
@@ -32,12 +33,18 @@ UNKNOWN109_DPS = "109"
 UNKNOWN110_DPS = "110"
 
 
-class TestFerskVind2Climate(TuyaDeviceTestCase):
+class TestFerskVind2Climate(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("fersk_vind_2_climate.yaml", FERSK_VIND2_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=16,
+            max=32,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -64,67 +71,14 @@ class TestFerskVind2Climate(TuyaDeviceTestCase):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.dps[UNIT_DPS] = "C"
-        self.assertEqual(self.subject.min_temp, 16)
+    def test_minimum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.min_temp, 60)
 
-    def test_maximum_target_temperature(self):
-        self.dps[UNIT_DPS] = "C"
-        self.assertEqual(self.subject.max_temp, 32)
+    def test_maximum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.max_temp, 90)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        self.dps[UNIT_DPS] = "C"
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        self.dps[UNIT_DPS] = "C"
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_target_temperature(24)
-        self.dps[UNIT_DPS] = "F"
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 80}
-        ):
-            await self.subject.async_set_target_temperature(80)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        self.dps[UNIT_DPS] = "C"
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 16 and 32"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(33\\) must be between 16 and 32"
-        ):
-            await self.subject.async_set_target_temperature(33)
-
-        self.dps[UNIT_DPS] = "F"
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(59\\) must be between 60 and 90"
-        ):
-            await self.subject.async_set_target_temperature(59)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(91\\) must be between 60 and 90"
-        ):
-            await self.subject.async_set_target_temperature(91)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 48
tests/devices/test_gardenpac_heatpump.py

@@ -17,6 +17,7 @@ from homeassistant.const import (
 
 from ..const import GARDENPAC_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.sensor import BasicSensorTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -33,12 +34,22 @@ UNKNOWN116_DPS = "116"
 PRESET_DPS = "117"
 
 
-class TestGardenPACPoolHeatpump(BasicSensorTests, TuyaDeviceTestCase):
+class TestGardenPACPoolHeatpump(
+    BasicSensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("gardenpac_heatpump.yaml", GARDENPAC_HEATPUMP_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=18,
+            max=45,
+        )
         self.setUpBasicSensor(
             POWERLEVEL_DPS,
             self.entities.get("sensor_power_level"),
@@ -66,19 +77,6 @@ class TestGardenPACPoolHeatpump(BasicSensorTests, TuyaDeviceTestCase):
         self.dps[UNITS_DPS] = True
         self.assertEqual(self.subject.temperature_unit, TEMP_CELSIUS)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 18)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 45)
-
     def test_minimum_fahrenheit_temperature(self):
         self.dps[UNITS_DPS] = False
         self.assertEqual(self.subject.min_temp, 60)
@@ -87,40 +85,6 @@ class TestGardenPACPoolHeatpump(BasicSensorTests, TuyaDeviceTestCase):
         self.dps[UNITS_DPS] = False
         self.assertEqual(self.subject.max_temp, 115)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_temperature(temperature=25)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 18 and 45"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(46\\) must be between 18 and 45"
-        ):
-            await self.subject.async_set_target_temperature(46)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 48
tests/devices/test_goldair_geco_heater.py

@@ -9,6 +9,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import GECO_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import BasicNumberTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -22,13 +23,23 @@ ERROR_DPS = "6"
 
 
 class TestGoldairGECOHeater(
-    BasicBinarySensorTests, BasicLockTests, BasicNumberTests, TuyaDeviceTestCase
+    BasicBinarySensorTests,
+    BasicLockTests,
+    BasicNumberTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
 ):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_geco_heater.yaml", GECO_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicNumber(TIMER_DPS, self.entities.get("number_timer"), max=24)
         self.setUpBasicBinarySensor(
@@ -56,53 +67,6 @@ class TestGoldairGECOHeater(
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 48
tests/devices/test_goldair_gpcv_heater.py

@@ -10,6 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import GPCV_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import BasicNumberTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -24,13 +25,23 @@ PRESET_DPS = "7"
 
 
 class TestGoldairGPCVHeater(
-    BasicBinarySensorTests, BasicLockTests, BasicNumberTests, TuyaDeviceTestCase
+    BasicBinarySensorTests,
+    BasicLockTests,
+    BasicNumberTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
 ):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("goldair_gpcv_heater.yaml", GPCV_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicNumber(TIMER_DPS, self.entities.get("number_timer"), max=24)
         self.setUpBasicBinarySensor(
@@ -58,25 +69,6 @@ class TestGoldairGPCVHeater(
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "Low"}
@@ -89,34 +81,6 @@ class TestGoldairGPCVHeater(
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="High")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 8 - 46
tests/devices/test_goldair_gpph_heater.py

@@ -12,6 +12,7 @@ from homeassistant.const import DEVICE_CLASS_POWER_FACTOR, PERCENTAGE, STATE_UNA
 from ..const import GPPH_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.light import BasicLightTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import BasicNumberTests
@@ -38,6 +39,7 @@ class TestGoldairHeater(
     BasicLockTests,
     BasicNumberTests,
     BasicSensorTests,
+    TargetTemperatureTests,
     TuyaDeviceTestCase,
 ):
     __test__ = True
@@ -45,6 +47,12 @@ class TestGoldairHeater(
     def setUp(self):
         self.setUpForConfig("goldair_gpph_heater.yaml", GPPH_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=35,
+        )
         self.setUpBasicLight(LIGHT_DPS, self.entities.get("light_display"))
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicNumber(
@@ -86,11 +94,6 @@ class TestGoldairHeater(
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.dps[PRESET_DPS] = "C"
-        self.assertEqual(self.subject.target_temperature, 25)
-
     def test_target_temperature_in_eco_and_af_modes(self):
         self.dps[TEMPERATURE_DPS] = 25
         self.dps[ECOTEMP_DPS] = 15
@@ -101,9 +104,6 @@ class TestGoldairHeater(
         self.dps[PRESET_DPS] = "AF"
         self.assertIs(self.subject.target_temperature, None)
 
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
     def test_minimum_temperature(self):
         self.dps[PRESET_DPS] = "C"
         self.assertEqual(self.subject.min_temp, 5)
@@ -124,12 +124,6 @@ class TestGoldairHeater(
         self.dps[PRESET_DPS] = "AF"
         self.assertIs(self.subject.max_temp, 5)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_temperature(temperature=25)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "C"}
@@ -148,18 +142,6 @@ class TestGoldairHeater(
                 temperature=25, preset_mode="comfort"
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_in_comfort_mode(self):
-        self.dps[PRESET_DPS] = "C"
-
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
     async def test_set_target_temperature_in_eco_mode(self):
         self.dps[PRESET_DPS] = "ECO"
 
@@ -168,26 +150,6 @@ class TestGoldairHeater(
         ):
             await self.subject.async_set_target_temperature(15)
 
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range_in_comfort(self):
-        self.dps[PRESET_DPS] = "C"
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 35"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 5 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     async def test_set_target_temperature_fails_outside_valid_range_in_eco(self):
         self.dps[PRESET_DPS] = "ECO"
 

+ 12 - 42
tests/devices/test_hellnar_heatpump.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import HELLNAR_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 POWER_DPS = "1"
@@ -20,12 +21,19 @@ CURRENTTEMP_DPS = "3"
 HVACMODE_DPS = "4"
 
 
-class TestHellnarHeatpump(TuyaDeviceTestCase):
+class TestHellnarHeatpump(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("hellnar_heatpump.yaml", HELLNAR_HEATPUMP_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=17.0,
+            max=30.0,
+            scale=10,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -53,53 +61,15 @@ class TestHellnarHeatpump(TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[HVACMODE_DPS] = "auto"
-        self.dps[TEMPERATURE_DPS] = 250
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 0.1)
-
-    def test_minimum_target_temperature(self):
-        self.dps[HVACMODE_DPS] = "cold"
-        self.assertEqual(self.subject.min_temp, 17.0)
+    def test_minimum_target_temperature_in_hot(self):
         self.dps[HVACMODE_DPS] = "hot"
         self.assertEqual(self.subject.min_temp, 0.0)
 
-    def test_maximum_target_temperature(self):
-        self.dps[HVACMODE_DPS] = "cold"
-        self.assertEqual(self.subject.max_temp, 30.0)
+    def test_maximum_target_temperature_in_hot(self):
         self.dps[HVACMODE_DPS] = "hot"
         self.assertEqual(self.subject.max_temp, 30.0)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        self.dps[HVACMODE_DPS] = "auto"
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 240}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        self.dps[HVACMODE_DPS] = "auto"
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        self.dps[HVACMODE_DPS] = "auto"
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 250},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        self.dps[HVACMODE_DPS] = "cold"
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 17.0 and 30.0"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
+    async def test_set_target_temperature_fails_outside_valid_range_in_hot(self):
         self.dps[HVACMODE_DPS] = "hot"
         with self.assertRaisesRegex(
             ValueError, "temperature \\(31\\) must be between 0.0 and 30.0"

+ 10 - 48
tests/devices/test_kogan_kahtp_heater.py

@@ -8,6 +8,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import KOGAN_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import BasicNumberTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -20,12 +21,20 @@ HVACMODE_DPS = "7"
 TIMER_DPS = "8"
 
 
-class TestGoldairKoganKAHTPHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTestCase):
+class TestGoldairKoganKAHTPHeater(
+    BasicLockTests, BasicNumberTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("kogan_kahtp_heater.yaml", KOGAN_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicNumber(TIMER_DPS, self.entities.get("number_timer"), max=24)
 
@@ -47,25 +56,6 @@ class TestGoldairKoganKAHTPHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTe
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "Low"}
@@ -78,34 +68,6 @@ class TestGoldairKoganKAHTPHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTe
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="High")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 8 - 48
tests/devices/test_kogan_kashmfp20ba_heater.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import KOGAN_KASHMFP20BA_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -22,7 +23,7 @@ BACKLIGHT_DPS = "5"
 FLAME_DPS = "6"
 
 
-class TestKoganKASHMF20BAHeater(TuyaDeviceTestCase):
+class TestKoganKASHMF20BAHeater(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
@@ -30,6 +31,12 @@ class TestKoganKASHMF20BAHeater(TuyaDeviceTestCase):
             "kogan_kashmfp20ba_heater.yaml", KOGAN_KASHMFP20BA_HEATER_PAYLOAD
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=10,
+            max=30,
+        )
         self.backlight = self.entities.get("light_backlight")
         self.flame = self.entities.get("light_flame")
 
@@ -51,25 +58,6 @@ class TestKoganKASHMF20BAHeater(TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 10)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 30)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "Low"}
@@ -82,34 +70,6 @@ class TestKoganKASHMF20BAHeater(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="High")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(9\\) must be between 10 and 30"
-        ):
-            await self.subject.async_set_target_temperature(9)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(31\\) must be between 10 and 30"
-        ):
-            await self.subject.async_set_target_temperature(31)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 13 - 48
tests/devices/test_kogan_kawfhtp_heater.py

@@ -8,6 +8,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import KOGAN_KAWFHTP_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import BasicNumberTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -20,12 +21,23 @@ PRESET_DPS = "7"
 LOCK_DPS = "2"
 
 
-class TestGoldairKoganKAHTPHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTestCase):
+class TestGoldairKoganKAHTPHeater(
+    BasicLockTests,
+    BasicNumberTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("kogan_kawfhtp_heater.yaml", KOGAN_KAWFHTP_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=40,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicNumber(TIMER_DPS, self.entities.get("number_timer"), max=24)
 
@@ -47,25 +59,6 @@ class TestGoldairKoganKAHTPHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTe
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "Low"}
@@ -78,34 +71,6 @@ class TestGoldairKoganKAHTPHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTe
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="High")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 40"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(41\\) must be between 5 and 40"
-        ):
-            await self.subject.async_set_target_temperature(41)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 10 - 48
tests/devices/test_kogan_kawfpac09ya_airconditioner.py

@@ -10,6 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import KOGAN_KAWFPAC09YA_AIRCON_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 POWER_DPS = "1"
@@ -23,7 +24,7 @@ UNKNOWN106_DPS = "106"
 UNKNOWN107_DPS = "107"
 
 
-class TestKoganKAWFPAC09YA(TuyaDeviceTestCase):
+class TestKoganKAWFPAC09YA(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
@@ -32,6 +33,12 @@ class TestKoganKAWFPAC09YA(TuyaDeviceTestCase):
             KOGAN_KAWFPAC09YA_AIRCON_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=16,
+            max=30,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -56,59 +63,14 @@ class TestKoganKAWFPAC09YA(TuyaDeviceTestCase):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.dps[UNIT_DPS] = "C"
-        self.assertEqual(self.subject.min_temp, 16)
+    def test_minimum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.min_temp, 60)
 
-    def test_maximum_target_temperature(self):
-        self.dps[UNIT_DPS] = "C"
-        self.assertEqual(self.subject.max_temp, 30)
+    def test_maximum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "F"
         self.assertEqual(self.subject.max_temp, 86)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 16 and 30"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(31\\) must be between 16 and 30"
-        ):
-            await self.subject.async_set_target_temperature(31)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 250 - 0
tests/devices/test_madimack_elitev3_heatpump.py

@@ -0,0 +1,250 @@
+from homeassistant.components.climate.const import (
+    HVAC_MODE_COOL,
+    HVAC_MODE_HEAT,
+    HVAC_MODE_HEAT_COOL,
+    HVAC_MODE_OFF,
+    SUPPORT_PRESET_MODE,
+    SUPPORT_TARGET_TEMPERATURE,
+)
+
+from homeassistant.const import (
+    DEVICE_CLASS_POWER_FACTOR,
+    DEVICE_CLASS_TEMPERATURE,
+    PERCENTAGE,
+    STATE_UNAVAILABLE,
+    TEMP_CELSIUS,
+    TEMP_FAHRENHEIT,
+)
+
+from ..const import MADIMACK_ELITEV3_HEATPUMP_PAYLOAD
+from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
+from ..mixins.sensor import MultiSensorTests
+from .base_device_tests import TuyaDeviceTestCase
+
+POWER_DPS = "1"
+HVACMODE_DPS = "2"
+TEMPERATURE_DPS = "4"
+PRESET_DPS = "5"
+UNIT_DPS = "6"
+UNKNOWN15_DPS = "15"
+PWRLEVEL_DPS = "20"
+TEMPMAX_DPS = "21"
+TEMPMIN_DPS = "22"
+COILTEMP_DPS = "23"
+EXHAUSTTEMP_DPS = "24"
+OUTLETTEMP_DPS = "25"
+AMBIENTTEMP_DPS = "26"
+UNKNOWN101_DPS = "101"
+CURRENTTEMP_DPS = "102"
+RETURNGASTEMP_DPS = "103"
+COOLCOILTEMP_DPS = "104"
+COOLPLATETEMP_DPS = "105"
+EEVOPENING_DPS = "106"
+UNKNOWN107_DPS = "107"
+
+
+class TestMadimackEliteV3Heatpump(
+    MultiSensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
+    __test__ = True
+
+    def setUp(self):
+        self.setUpForConfig(
+            "madimack_elite_v3_heatpump.yaml",
+            MADIMACK_ELITEV3_HEATPUMP_PAYLOAD,
+        )
+        self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=18,
+            max=40,
+        )
+        self.setUpMultiSensors(
+            [
+                {
+                    "name": "sensor_power_level",
+                    "dps": PWRLEVEL_DPS,
+                    "device_class": DEVICE_CLASS_POWER_FACTOR,
+                    "unit": PERCENTAGE,
+                },
+                {
+                    "name": "sensor_evaporator_coil_pipe_temperature",
+                    "dps": COILTEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_exhaust_gas_temperature",
+                    "dps": EXHAUSTTEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_outlet_water_temperature",
+                    "dps": OUTLETTEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_ambient_temperature",
+                    "dps": AMBIENTTEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_return_gas_temperature",
+                    "dps": RETURNGASTEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_cooling_coil_pipe_temperature",
+                    "dps": COOLCOILTEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_cooling_plate_temperature",
+                    "dps": COOLPLATETEMP_DPS,
+                    "device_class": DEVICE_CLASS_TEMPERATURE,
+                    "unit": TEMP_CELSIUS,
+                },
+                {
+                    "name": "sensor_eev_opening",
+                    "dps": EEVOPENING_DPS,
+                },
+            ]
+        )
+
+    def test_supported_features(self):
+        self.assertEqual(
+            self.subject.supported_features,
+            SUPPORT_PRESET_MODE | SUPPORT_TARGET_TEMPERATURE,
+        )
+
+    def test_icon(self):
+        self.dps[POWER_DPS] = True
+        self.dps[HVACMODE_DPS] = "auto"
+        self.assertEqual(self.subject.icon, "mdi:refresh-auto")
+        self.dps[HVACMODE_DPS] = "cold"
+        self.assertEqual(self.subject.icon, "mdi:snowflake")
+        self.dps[HVACMODE_DPS] = "heating"
+        self.assertEqual(self.subject.icon, "mdi:hot-tub")
+        self.dps[POWER_DPS] = False
+        self.assertEqual(self.subject.icon, "mdi:hvac-off")
+
+    def test_temperature_unit(self):
+        self.dps[UNIT_DPS] = "c"
+        self.assertEqual(self.subject.temperature_unit, TEMP_CELSIUS)
+        self.dps[UNIT_DPS] = "f"
+        self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
+
+    def test_minimum_target_temperature(self):
+        self.dps[TEMPMIN_DPS] = 60
+        self.assertEqual(self.subject.min_temp, 60)
+
+    def test_maximum_target_temperature(self):
+        self.dps[TEMPMAX_DPS] = 104
+        self.assertEqual(self.subject.max_temp, 104)
+
+    def test_current_temperature(self):
+        self.dps[CURRENTTEMP_DPS] = 25
+        self.assertEqual(self.subject.current_temperature, 25)
+
+    def test_hvac_mode(self):
+        self.dps[POWER_DPS] = True
+        self.dps[HVACMODE_DPS] = "auto"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_HEAT_COOL)
+        self.dps[HVACMODE_DPS] = "cold"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_COOL)
+        self.dps[HVACMODE_DPS] = "heating"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_HEAT)
+        self.dps[POWER_DPS] = False
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_OFF)
+
+    def test_hvac_modes(self):
+        self.assertCountEqual(
+            self.subject.hvac_modes,
+            [HVAC_MODE_OFF, HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_HEAT_COOL],
+        )
+
+    async def test_set_hvac_mode_to_cool(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {POWER_DPS: True, HVACMODE_DPS: "cold"},
+        ):
+            await self.subject.async_set_hvac_mode(HVAC_MODE_COOL)
+
+    async def test_set_hvac_mode_to_heat(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {POWER_DPS: True, HVACMODE_DPS: "heating"},
+        ):
+            await self.subject.async_set_hvac_mode(HVAC_MODE_HEAT)
+
+    async def test_set_hvac_mode_to_auto(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {POWER_DPS: True, HVACMODE_DPS: "auto"},
+        ):
+            await self.subject.async_set_hvac_mode(HVAC_MODE_HEAT_COOL)
+
+    async def test_set_hvac_mode_to_off(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {POWER_DPS: False},
+        ):
+            await self.subject.async_set_hvac_mode(HVAC_MODE_OFF)
+
+    def test_preset_mode(self):
+        self.dps[PRESET_DPS] = "silence"
+        self.assertEqual(self.subject.preset_mode, "Silence")
+        self.dps[PRESET_DPS] = "power"
+        self.assertEqual(self.subject.preset_mode, "Perfect")
+        self.dps[PRESET_DPS] = "boost"
+        self.assertEqual(self.subject.preset_mode, "Power")
+
+    def test_preset_modes(self):
+        self.assertCountEqual(
+            self.subject.preset_modes,
+            ["Silence", "Perfect", "Power"],
+        )
+
+    async def test_set_preset_to_silence(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: "silence"},
+        ):
+            await self.subject.async_set_preset_mode("Silence")
+
+    async def test_set_preset_to_perfect(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: "power"},
+        ):
+            await self.subject.async_set_preset_mode("Perfect")
+
+    async def test_set_preset_to_boost(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: "boost"},
+        ):
+            await self.subject.async_set_preset_mode("Power")
+
+    def test_device_state_attributes(self):
+        self.dps[UNKNOWN15_DPS] = 15
+        self.dps[UNKNOWN101_DPS] = 101
+        self.dps[UNKNOWN107_DPS] = True
+
+        self.assertDictEqual(
+            self.subject.device_state_attributes,
+            {
+                "unknown_15": 15,
+                "unknown_101": 101,
+                "unknown_107": True,
+            },
+        )

+ 12 - 48
tests/devices/test_madimack_heatpump.py

@@ -17,6 +17,7 @@ from homeassistant.const import (
 
 from ..const import MADIMACK_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.sensor import BasicSensorTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -48,12 +49,22 @@ UNKNOWN140_DPS = "140"
 PRESET_DPS = "117"
 
 
-class TestMadimackPoolHeatpump(BasicSensorTests, TuyaDeviceTestCase):
+class TestMadimackPoolHeatpump(
+    BasicSensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("madimack_heatpump.yaml", MADIMACK_HEATPUMP_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=18,
+            max=45,
+        )
         self.setUpBasicSensor(
             POWERLEVEL_DPS,
             self.entities.get("sensor_power_level"),
@@ -80,19 +91,6 @@ class TestMadimackPoolHeatpump(BasicSensorTests, TuyaDeviceTestCase):
         self.dps[UNITS_DPS] = True
         self.assertEqual(self.subject.temperature_unit, TEMP_CELSIUS)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 18)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 45)
-
     def test_minimum_fahrenheit_temperature(self):
         self.dps[UNITS_DPS] = False
         self.assertEqual(self.subject.min_temp, 60)
@@ -101,40 +99,6 @@ class TestMadimackPoolHeatpump(BasicSensorTests, TuyaDeviceTestCase):
         self.dps[UNITS_DPS] = False
         self.assertEqual(self.subject.max_temp, 115)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_temperature(temperature=25)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 18 and 45"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(46\\) must be between 18 and 45"
-        ):
-            await self.subject.async_set_target_temperature(46)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 48
tests/devices/test_minco_mh1823d_thermostat.py

@@ -17,6 +17,7 @@ from homeassistant.const import (
 
 from ..const import MINCO_MH1823D_THERMOSTAT_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import MultiNumberTests
 from ..mixins.select import MultiSelectTests
@@ -55,6 +56,7 @@ class TestMincoMH1823DThermostat(
     BasicSwitchTests,
     MultiNumberTests,
     MultiSelectTests,
+    TargetTemperatureTests,
     TuyaDeviceTestCase,
 ):
     __test__ = True
@@ -65,6 +67,12 @@ class TestMincoMH1823DThermostat(
             MINCO_MH1823D_THERMOSTAT_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=50,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicSensor(
             EXTERNTEMP_DPS,
@@ -155,36 +163,20 @@ class TestMincoMH1823DThermostat(
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
+    def test_target_temperature_f(self):
         self.dps[TEMPF_DPS] = 70
 
-        self.dps[UNIT_DPS] = "c"
-        self.assertEqual(self.subject.target_temperature, 25)
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.target_temperature, 70)
 
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.dps[UNIT_DPS] = "c"
-        self.assertEqual(self.subject.min_temp, 5)
+    def test_minimum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.min_temp, 41)
 
-    def test_maximum_target_temperature(self):
-        self.dps[UNIT_DPS] = "c"
-        self.assertEqual(self.subject.max_temp, 50)
+    def test_maximum_target_temperature_f(self):
         self.dps[UNIT_DPS] = "f"
         self.assertEqual(self.subject.max_temp, 99)
 
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "holiday"}
@@ -203,35 +195,7 @@ class TestMincoMH1823DThermostat(
                 temperature=25, preset_mode="program"
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        self.dps[UNIT_DPS] = "c"
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 50"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(51\\) must be between 5 and 50"
-        ):
-            await self.subject.async_set_target_temperature(51)
-
+    async def test_set_target_temperature_fails_outside_valid_range_f(self):
         self.dps[UNIT_DPS] = "f"
         with self.assertRaisesRegex(
             ValueError, "temp_f \\(40\\) must be between 41 and 99"

+ 11 - 44
tests/devices/test_moes_bht002_thermostat.py

@@ -11,6 +11,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import MOES_BHT002_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -23,7 +24,9 @@ LOCK_DPS = "6"
 UNKNOWN104_DPS = "104"
 
 
-class TestMoesBHT002Thermostat(BasicLockTests, TuyaDeviceTestCase):
+class TestMoesBHT002Thermostat(
+    BasicLockTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -32,6 +35,13 @@ class TestMoesBHT002Thermostat(BasicLockTests, TuyaDeviceTestCase):
             MOES_BHT002_PAYLOAD,
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5.0,
+            max=35.0,
+            scale=2,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
 
     def test_supported_features(self):
@@ -46,25 +56,6 @@ class TestMoesBHT002Thermostat(BasicLockTests, TuyaDeviceTestCase):
             self.subject._device.temperature_unit,
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 50
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 0.5)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 41}
-        ):
-            await self.subject.async_set_temperature(temperature=20.5)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: True}
@@ -83,30 +74,6 @@ class TestMoesBHT002Thermostat(BasicLockTests, TuyaDeviceTestCase):
                 temperature=22, preset_mode=PRESET_COMFORT
             )
 
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 45},
-        ):
-            await self.subject.async_set_target_temperature(22.5)
-
-    async def test_set_target_temperature_rounds_value_to_closest_half(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 35}
-        ):
-            await self.subject.async_set_target_temperature(17.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(4.5)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(35.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(35.5)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 44
         self.assertEqual(self.subject.current_temperature, 22)

+ 10 - 48
tests/devices/test_nedis_htpl20f_heater.py

@@ -11,6 +11,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import NEDIS_HTPL20F_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import BasicNumberTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -25,12 +26,20 @@ TIMER_DPS = "13"
 UNKNOWN101_DPS = "101"
 
 
-class TestNedisHtpl20fHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTestCase):
+class TestNedisHtpl20fHeater(
+    BasicLockTests, BasicNumberTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("nedis_htpl20f_heater.yaml", NEDIS_HTPL20F_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         self.setUpBasicLock(
             LOCK_DPS,
             self.entities.get("lock_child_lock"),
@@ -55,53 +64,6 @@ class TestNedisHtpl20fHeater(BasicLockTests, BasicNumberTests, TuyaDeviceTestCas
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(14\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(14)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 48
tests/devices/test_poolex_silverline_heatpump.py

@@ -10,6 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import POOLEX_SILVERLINE_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -19,7 +20,11 @@ PRESET_DPS = "4"
 ERROR_DPS = "13"
 
 
-class TestPoolexSilverlineHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
+class TestPoolexSilverlineHeatpump(
+    BasicBinarySensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
@@ -27,6 +32,12 @@ class TestPoolexSilverlineHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
             "poolex_silverline_heatpump.yaml", POOLEX_SILVERLINE_HEATPUMP_PAYLOAD
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=8,
+            max=40,
+        )
         self.setUpBasicBinarySensor(
             ERROR_DPS,
             self.entities.get("binary_sensor_water_flow"),
@@ -51,25 +62,6 @@ class TestPoolexSilverlineHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 8)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "Cool"}
@@ -82,34 +74,6 @@ class TestPoolexSilverlineHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="Heat")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(7\\) must be between 8 and 40"
-        ):
-            await self.subject.async_set_target_temperature(7)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(41\\) must be between 8 and 40"
-        ):
-            await self.subject.async_set_target_temperature(41)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 48
tests/devices/test_poolex_vertigo_heatpump.py

@@ -10,6 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import POOLEX_VERTIGO_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -19,7 +20,11 @@ PRESET_DPS = "4"
 ERROR_DPS = "9"
 
 
-class TestPoolexVertigoHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
+class TestPoolexVertigoHeatpump(
+    BasicBinarySensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
@@ -27,6 +32,12 @@ class TestPoolexVertigoHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
             "poolex_vertigo_heatpump.yaml", POOLEX_VERTIGO_HEATPUMP_PAYLOAD
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=8,
+            max=40,
+        )
         self.setUpBasicBinarySensor(
             ERROR_DPS,
             self.entities.get("binary_sensor_water_flow"),
@@ -51,25 +62,6 @@ class TestPoolexVertigoHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 8)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "cool"}
@@ -82,34 +74,6 @@ class TestPoolexVertigoHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
         ):
             await self.subject.async_set_temperature(temperature=26, preset_mode="Heat")
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(7\\) must be between 8 and 40"
-        ):
-            await self.subject.async_set_target_temperature(7)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(41\\) must be between 8 and 40"
-        ):
-            await self.subject.async_set_target_temperature(41)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 12 - 49
tests/devices/test_purline_m100_heater.py

@@ -9,7 +9,6 @@ from homeassistant.components.climate.const import (
     SWING_VERTICAL,
 )
 from homeassistant.components.light import COLOR_MODE_ONOFF
-from homeassistant.components.switch import DEVICE_CLASS_SWITCH
 from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import PURLINE_M100_HEATER_PAYLOAD
@@ -17,6 +16,7 @@ from ..helpers import (
     assert_device_properties_set,
     assert_device_properties_set_optional,
 )
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.switch import BasicSwitchTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -31,12 +31,22 @@ SWITCH_DPS = "101"
 SWING_DPS = "102"
 
 
-class TestPulineM100Heater(BasicSwitchTests, TuyaDeviceTestCase):
+class TestPulineM100Heater(
+    BasicSwitchTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("purline_m100_heater.yaml", PURLINE_M100_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=15,
+            max=35,
+        )
         # BasicLightTests mixin not used due to inverted switch
         self.light = self.entities.get("light_display")
         self.setUpBasicSwitch(
@@ -66,53 +76,6 @@ class TestPulineM100Heater(BasicSwitchTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 15)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_temperature(temperature=25)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 15 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 10 - 48
tests/devices/test_remora_heatpump.py

@@ -10,6 +10,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 from ..const import REMORA_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 HVACMODE_DPS = "1"
@@ -19,12 +20,20 @@ PRESET_DPS = "4"
 ERROR_DPS = "9"
 
 
-class TestRemoraHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
+class TestRemoraHeatpump(
+    BasicBinarySensorTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("remora_heatpump.yaml", REMORA_HEATPUMP_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5,
+            max=40,
+        )
         self.setUpBasicBinarySensor(
             ERROR_DPS,
             self.entities.get("binary_sensor_water_flow"),
@@ -49,25 +58,6 @@ class TestRemoraHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "cool"}
@@ -82,34 +72,6 @@ class TestRemoraHeatpump(BasicBinarySensorTests, TuyaDeviceTestCase):
                 temperature=26, preset_mode="Smart Heating"
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 23}
-        ):
-            await self.subject.async_set_target_temperature(22.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 40"
-        ):
-            await self.subject.async_set_target_temperature(4)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(41\\) must be between 5 and 40"
-        ):
-            await self.subject.async_set_target_temperature(41)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)

+ 10 - 26
tests/devices/test_saswell_c16_thermostat.py

@@ -13,6 +13,7 @@ from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS
 
 from ..const import SASWELL_C16_THERMOSTAT_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.lock import BasicLockTests
 from ..mixins.number import MultiNumberTests
 from ..mixins.select import MultiSelectTests
@@ -47,6 +48,7 @@ class TestSaswellC16Thermostat(
     BasicSwitchTests,
     MultiNumberTests,
     MultiSelectTests,
+    TargetTemperatureTests,
     TuyaDeviceTestCase,
 ):
     __test__ = True
@@ -56,6 +58,14 @@ class TestSaswellC16Thermostat(
             "saswell_c16_thermostat.yaml", SASWELL_C16_THERMOSTAT_PAYLOAD
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5.0,
+            max=40.0,
+            scale=10,
+            step=5,
+        )
         self.setUpBasicLock(LOCK_DPS, self.entities.get("lock_child_lock"))
         self.setUpBasicSensor(
             FLOORTEMP_DPS,
@@ -128,32 +138,6 @@ class TestSaswellC16Thermostat(
             self.subject._device.temperature_unit,
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 250
-        self.assertEqual(self.subject.target_temperature, 25.0)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 0.5)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5.0)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40.0)
-
-    async def test_set_target_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 240},
-        ):
-            await self.subject.async_set_target_temperature(24)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4.5\\) must be between 5.0 and 40.0"
-        ):
-            await self.subject.async_set_target_temperature(4.5)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 250
         self.assertEqual(self.subject.current_temperature, 25.0)

+ 15 - 31
tests/devices/test_saswell_t29utk_thermostat.py

@@ -17,6 +17,7 @@ from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
 
 from ..const import SASWELL_T29UTK_THERMOSTAT_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.select import MultiSelectTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -37,7 +38,9 @@ TEMPF_DPS = "116"
 CURTEMPF_DPS = "117"
 
 
-class TestSaswellT29UTKThermostat(MultiSelectTests, TuyaDeviceTestCase):
+class TestSaswellT29UTKThermostat(
+    MultiSelectTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
     __test__ = True
 
     def setUp(self):
@@ -45,6 +48,14 @@ class TestSaswellT29UTKThermostat(MultiSelectTests, TuyaDeviceTestCase):
             "saswell_t29utk_thermostat.yaml", SASWELL_T29UTK_THERMOSTAT_PAYLOAD
         )
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=5.0,
+            max=35.0,
+            scale=10,
+            step=5,
+        )
         self.setUpMultiSelect(
             [
                 {
@@ -91,39 +102,18 @@ class TestSaswellT29UTKThermostat(MultiSelectTests, TuyaDeviceTestCase):
         self.dps[UNITS_DPS] = "F"
         self.assertEqual(self.subject.temperature_unit, TEMP_FAHRENHEIT)
 
-    def test_target_temperature(self):
-        self.dps[UNITS_DPS] = "C"
-        self.dps[TEMPERATURE_DPS] = 250
-        self.assertEqual(self.subject.target_temperature, 25)
-        self.dps[UNITS_DPS] = "F"
-        self.dps[TEMPERATURE_DPS] = 750
-        self.assertEqual(self.subject.target_temperature, 75)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 0.5)
+    def test_target_temperature_step_f(self):
         self.dps[UNITS_DPS] = "F"
         self.assertEqual(self.subject.target_temperature_step, 1)
 
-    def test_minimum_target_temperature(self):
-        self.dps[UNITS_DPS] = "C"
-        self.assertEqual(self.subject.min_temp, 5)
+    def test_minimum_target_temperature_f(self):
         self.dps[UNITS_DPS] = "F"
         self.assertEqual(self.subject.min_temp, 41)
 
-    def test_maximum_target_temperature(self):
-        self.dps[UNITS_DPS] = "C"
-        self.assertEqual(self.subject.max_temp, 35)
+    def test_maximum_target_temperature_f(self):
         self.dps[UNITS_DPS] = "F"
         self.assertEqual(self.subject.max_temp, 95)
 
-    async def test_set_target_temperature(self):
-        self.dps[UNITS_DPS] = "C"
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 240},
-        ):
-            await self.subject.async_set_target_temperature(24)
-
     async def test_set_target_temperature_f(self):
         self.dps[UNITS_DPS] = "F"
         async with assert_device_properties_set(
@@ -132,12 +122,6 @@ class TestSaswellT29UTKThermostat(MultiSelectTests, TuyaDeviceTestCase):
         ):
             await self.subject.async_set_target_temperature(74)
 
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(4.5\\) must be between 5.0 and 35.0"
-        ):
-            await self.subject.async_set_target_temperature(4.5)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 250
         self.assertEqual(self.subject.current_temperature, 25.0)

+ 8 - 42
tests/devices/test_tadiran_wind_heatpump.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE
 
 from ..const import TADIRAN_HEATPUMP_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from .base_device_tests import TuyaDeviceTestCase
 
 POWER_DPS = "1"
@@ -21,12 +22,18 @@ HVACMODE_DPS = "4"
 FAN_DPS = "5"
 
 
-class TestTadiranWindHeatpump(TuyaDeviceTestCase):
+class TestTadiranWindHeatpump(TargetTemperatureTests, TuyaDeviceTestCase):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("tadiran_wind_heatpump.yaml", TADIRAN_HEATPUMP_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=16,
+            max=32,
+        )
 
     def test_supported_features(self):
         self.assertEqual(
@@ -54,47 +61,6 @@ class TestTadiranWindHeatpump(TuyaDeviceTestCase):
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 16)
-
-    def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 32)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 24}
-        ):
-            await self.subject.async_set_temperature(temperature=24)
-
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature_succeeds_within_valid_range(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(15\\) must be between 16 and 32"
-        ):
-            await self.subject.async_set_target_temperature(15)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(33\\) must be between 16 and 32"
-        ):
-            await self.subject.async_set_target_temperature(33)
-
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 250
         self.assertEqual(self.subject.current_temperature, 25)

+ 13 - 48
tests/devices/test_wetair_wch750_heater.py

@@ -12,6 +12,7 @@ from homeassistant.const import STATE_UNAVAILABLE, TIME_MINUTES
 
 from ..const import WETAIR_WCH750_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
 from ..mixins.select import BasicSelectTests
 from ..mixins.sensor import BasicSensorTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -26,12 +27,23 @@ UNKNOWN21_DPS = "21"
 BRIGHTNESS_DPS = "101"
 
 
-class TestWetairWCH750Heater(BasicSelectTests, BasicSensorTests, TuyaDeviceTestCase):
+class TestWetairWCH750Heater(
+    BasicSelectTests,
+    BasicSensorTests,
+    TargetTemperatureTests,
+    TuyaDeviceTestCase,
+):
     __test__ = True
 
     def setUp(self):
         self.setUpForConfig("wetair_wch750_heater.yaml", WETAIR_WCH750_HEATER_PAYLOAD)
         self.subject = self.entities.get("climate")
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.subject,
+            min=10,
+            max=35,
+        )
         self.light = self.entities.get("light_display")
         self.setUpBasicSelect(
             TIMER_DPS,
@@ -86,30 +98,11 @@ class TestWetairWCH750Heater(BasicSelectTests, BasicSensorTests, TuyaDeviceTestC
             self.subject.temperature_unit, self.subject._device.temperature_unit
         )
 
-    def test_target_temperature(self):
-        self.dps[TEMPERATURE_DPS] = 25
-        self.assertEqual(self.subject.target_temperature, 25)
-
     def test_target_temperature_in_af_mode(self):
         self.dps[TEMPERATURE_DPS] = 25
         self.dps[PRESET_DPS] = "mod_antiforst"
         self.assertEqual(self.subject.target_temperature, None)
 
-    def test_target_temperature_step(self):
-        self.assertEqual(self.subject.target_temperature_step, 1)
-
-    def test_minimum_temperature(self):
-        self.assertEqual(self.subject.min_temp, 10)
-
-    def test_maximum_temperature(self):
-        self.assertEqual(self.subject.max_temp, 35)
-
-    async def test_legacy_set_temperature_with_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_temperature(temperature=25)
-
     async def test_legacy_set_temperature_with_preset_mode(self):
         async with assert_device_properties_set(
             self.subject._device, {PRESET_DPS: "mod_antiforst"}
@@ -125,34 +118,6 @@ class TestWetairWCH750Heater(BasicSelectTests, BasicSensorTests, TuyaDeviceTestC
                 preset_mode=PRESET_BOOST, temperature=25
             )
 
-    async def test_legacy_set_temperature_with_no_valid_properties(self):
-        await self.subject.async_set_temperature(something="else")
-        self.subject._device.async_set_property.assert_not_called()
-
-    async def test_set_target_temperature(self):
-        async with assert_device_properties_set(
-            self.subject._device, {TEMPERATURE_DPS: 25}
-        ):
-            await self.subject.async_set_target_temperature(25)
-
-    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {TEMPERATURE_DPS: 25},
-        ):
-            await self.subject.async_set_target_temperature(24.6)
-
-    async def test_set_target_temperature_fails_outside_valid_range(self):
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(9\\) must be between 10 and 35"
-        ):
-            await self.subject.async_set_target_temperature(9)
-
-        with self.assertRaisesRegex(
-            ValueError, "temperature \\(36\\) must be between 10 and 35"
-        ):
-            await self.subject.async_set_target_temperature(36)
-
     async def test_set_target_temperature_fails_in_anti_frost(self):
         self.dps[PRESET_DPS] = "mod_antiforst"
 

+ 73 - 0
tests/mixins/climate.py

@@ -0,0 +1,73 @@
+# Mixins for testing climate entities
+from math import floor
+from ..helpers import assert_device_properties_set
+
+
+class TargetTemperatureTests:
+    def setUpTargetTemperature(self, dps, subject, min=15, max=35, step=1, scale=1):
+        self.targetTemp = subject
+        self.targetTempDps = dps
+        self.targetTempMin = min
+        self.targetTempMax = max
+        self.targetTempStep = step
+        self.targetTempScale = scale
+
+    def test_target_temperature(self):
+        self.dps[self.targetTempDps] = 25 * self.targetTempScale
+        self.assertEqual(self.targetTemp.target_temperature, 25)
+
+    def test_target_temperature_step(self):
+        self.assertEqual(
+            self.targetTemp.target_temperature_step,
+            self.targetTempStep / self.targetTempScale,
+        )
+
+    def test_minimum_target_temperature(self):
+        self.assertEqual(self.targetTemp.min_temp, self.targetTempMin)
+
+    def test_maximum_target_temperature(self):
+        self.assertEqual(self.targetTemp.max_temp, self.targetTempMax)
+
+    async def test_legacy_set_temperature_with_temperature(self):
+        test_val = floor((self.targetTempMin + self.targetTempMax) / 2)
+        async with assert_device_properties_set(
+            self.targetTemp._device,
+            {self.targetTempDps: test_val * self.targetTempScale},
+        ):
+            await self.targetTemp.async_set_temperature(temperature=test_val)
+
+    async def test_legacy_set_temperature_with_no_valid_properties(self):
+        await self.targetTemp.async_set_temperature(something="else")
+        self.targetTemp._device.async_set_property.assert_not_called()
+
+    async def test_set_target_temperature_succeeds_within_valid_range(self):
+        test_val = floor((self.targetTempMin + self.targetTempMax) / 2)
+        async with assert_device_properties_set(
+            self.targetTemp._device,
+            {self.targetTempDps: test_val * self.targetTempScale},
+        ):
+            await self.targetTemp.async_set_target_temperature(test_val)
+
+    async def test_set_target_temperature_fails_outside_valid_range(self):
+        test_val = self.targetTempMin - 1
+        with self.assertRaisesRegex(
+            ValueError,
+            f"temperature \\({test_val}\\) must be between {self.targetTempMin} and {self.targetTempMax}",
+        ):
+            await self.targetTemp.async_set_target_temperature(test_val)
+        test_val = self.targetTempMax + 1
+        with self.assertRaisesRegex(
+            ValueError,
+            f"temperature \\({test_val}\\) must be between {self.targetTempMin} and {self.targetTempMax}",
+        ):
+            await self.targetTemp.async_set_target_temperature(test_val)
+
+    async def test_set_target_temperature_rounds_value_to_closest_integer(self):
+        test_val = floor((self.targetTempMin + self.targetTempMax) / 2)
+        test_dpsval = test_val * self.targetTempScale
+        test_val = (test_dpsval + 0.3) / self.targetTempScale
+        async with assert_device_properties_set(
+            self.subject._device,
+            {self.targetTempDps: test_dpsval},
+        ):
+            await self.subject.async_set_target_temperature(test_val)