Browse Source

Implement tests for Carson CB air conditioner.

- Modify temperature unit handling to default to C as a fallback.
Other devices have had issues with this, even though unit tests show the selection is working.

- Document the new support
Jason Rumney 4 years ago
parent
commit
0b4f9790f0

+ 1 - 0
ACKNOWLEDGEMENTS.md

@@ -39,3 +39,4 @@ Further device support has been made with the assistance of users.  Please consi
  - [wwalczyszyn](https://github.com/wwalczyszyn) for contributing support for Fersk Vind 2 heatpumps.
  - [xbmcnut](https://github.com/xbmcnut) for assistance in supporting Kogan Smart Kettles and the new type of Kogan heater.
  - [ThomasADavis](https://github.com/ThomasADavis) for contributing support for Renpho RP-AP001S air purifiers.
+ - [darek-margas](https://github.com/darek-margas) for contributing support for Arlec fans and Carson portable air conditioners.

+ 2 - 0
README.md

@@ -30,6 +30,7 @@ Note that devices sometimes get firmware upgrades, or incompatible versions are
 - ElectriQ 12WMINV
 - Tadiran Wind 65/3P
 - Fersk Vind 2
+- Carson CB PA280
 
 ### Pool heaters / heatpumps
 
@@ -58,6 +59,7 @@ Note that devices sometimes get firmware upgrades, or incompatible versions are
 - Anko HEGSM40 fan
 - Lexy F501 fan
 - Deta fan controller
+- Arlec Grid Connect Smart Ceiling Fan (without light)
 
 ### Air Purifiers
 - Renpho RP-AP001S air purifier

+ 3 - 4
custom_components/tuya_local/devices/carson_cb.yaml

@@ -14,13 +14,12 @@ primary_entity:
     - id: 2
       name: temperature
       type: integer
+      range:
+        min: 16
+        max: 30
       mapping:
         - constraint: temperature_unit
           conditions:
-            - dps_val: "C"
-              range:
-                min: 16
-                max: 30
             - dps_val: "F"
               range:
                 min: 60

+ 1 - 1
tests/const.py

@@ -423,7 +423,7 @@ ARLEC_FAN_PAYLOAD = {
     "103": "off",
 }
 
-CARSON_CB_PAYOAD = {
+CARSON_CB_PAYLOAD = {
     "1": True,
     "2": 20,
     "3": 23,

+ 243 - 0
tests/devices/test_carson_cb.py

@@ -0,0 +1,243 @@
+from homeassistant.components.climate.const import (
+    HVAC_MODE_COOL,
+    HVAC_MODE_DRY,
+    HVAC_MODE_FAN_ONLY,
+    HVAC_MODE_HEAT,
+    HVAC_MODE_OFF,
+    SUPPORT_FAN_MODE,
+    SUPPORT_SWING_MODE,
+    SUPPORT_TARGET_TEMPERATURE,
+)
+from homeassistant.components.light import COLOR_MODE_ONOFF
+from homeassistant.const import STATE_UNAVAILABLE, TEMP_CELSIUS, TEMP_FAHRENHEIT
+
+from ..const import CARSON_CB_PAYLOAD
+from ..helpers import assert_device_properties_set
+from .base_device_tests import TuyaDeviceTestCase
+
+POWER_DPS = "1"
+TEMPERATURE_DPS = "2"
+CURRENTTEMP_DPS = "3"
+HVACMODE_DPS = "4"
+FAN_DPS = "5"
+UNIT_DPS = "19"
+UNKNOWN102_DPS = "102"
+TIMER_DPS = "103"
+SWING_DPS = "104"
+COUNTDOWN_DPS = "105"
+UNKNOWN106_DPS = "106"
+UNKNOWN110_DPS = "110"
+
+
+class TestCarsonCBHeatpump(TuyaDeviceTestCase):
+    __test__ = True
+
+    def setUp(self):
+        self.setUpForConfig("carson_cb.yaml", CARSON_CB_PAYLOAD)
+        self.subject = self.entities.get("climate")
+        self.light = self.entities.get("light")
+        self.switch = self.entities.get("switch")
+
+    def test_supported_features(self):
+        self.assertEqual(
+            self.subject.supported_features,
+            SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE | SUPPORT_SWING_MODE,
+        )
+
+    def test_icon(self):
+        self.dps[POWER_DPS] = True
+        self.dps[HVACMODE_DPS] = "COOL"
+        self.assertEqual(self.subject.icon, "mdi:snowflake")
+        self.dps[HVACMODE_DPS] = "HEAT"
+        self.assertEqual(self.subject.icon, "mdi:fire")
+        self.dps[HVACMODE_DPS] = "DRY"
+        self.assertEqual(self.subject.icon, "mdi:water")
+        self.dps[HVACMODE_DPS] = "FAN"
+        self.assertEqual(self.subject.icon, "mdi:fan")
+        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_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)
+
+    def test_hvac_mode(self):
+        self.dps[POWER_DPS] = True
+        self.dps[HVACMODE_DPS] = "HEAT"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_HEAT)
+
+        self.dps[HVACMODE_DPS] = "COOL"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_COOL)
+
+        self.dps[HVACMODE_DPS] = "DRY"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_DRY)
+
+        self.dps[HVACMODE_DPS] = "FAN"
+        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_FAN_ONLY)
+
+        self.dps[HVACMODE_DPS] = None
+        self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
+
+        self.dps[HVACMODE_DPS] = "DRY"
+        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_HEAT,
+                HVAC_MODE_COOL,
+                HVAC_MODE_DRY,
+                HVAC_MODE_FAN_ONLY,
+            ],
+        )
+
+    async def test_turn_on(self):
+        async with assert_device_properties_set(
+            self.subject._device, {POWER_DPS: True, HVACMODE_DPS: "HEAT"}
+        ):
+            await self.subject.async_set_hvac_mode(HVAC_MODE_HEAT)
+
+    async def test_turn_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_fan_mode(self):
+        self.dps[FAN_DPS] = 1
+        self.assertEqual(self.subject.fan_mode, "low")
+        self.dps[FAN_DPS] = 2
+        self.assertEqual(self.subject.fan_mode, "medium")
+        self.dps[FAN_DPS] = 3
+        self.assertEqual(self.subject.fan_mode, "high")
+
+    def test_fan_modes(self):
+        self.assertCountEqual(
+            self.subject.fan_modes,
+            [
+                "low",
+                "medium",
+                "high",
+            ],
+        )
+
+    async def test_set_fan_mode_to_low(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {FAN_DPS: 1},
+        ):
+            await self.subject.async_set_fan_mode("low")
+
+    async def test_set_fan_mode_to_medium(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {FAN_DPS: 2},
+        ):
+            await self.subject.async_set_fan_mode("medium")
+
+    async def test_set_fan_mode_to_high(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {FAN_DPS: 3},
+        ):
+            await self.subject.async_set_fan_mode("high")
+
+    def test_swing_modes(self):
+        self.assertCountEqual(
+            self.subject.swing_modes,
+            ["off", "vertical"],
+        )
+
+    def test_swing_mode(self):
+        self.dps[SWING_DPS] = False
+        self.assertEqual(self.subject.swing_mode, "off")
+
+        self.dps[SWING_DPS] = True
+        self.assertEqual(self.subject.swing_mode, "vertical")
+
+    async def test_set_swing_mode_to_off(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {SWING_DPS: False},
+        ):
+            await self.subject.async_set_swing_mode("off")
+
+    async def test_set_swing_mode_to_vertical(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {SWING_DPS: True},
+        ):
+            await self.subject.async_set_swing_mode("vertical")
+
+    def test_device_state_attribures(self):
+        self.dps[UNKNOWN102_DPS] = True
+        self.dps[TIMER_DPS] = 103
+        self.dps[COUNTDOWN_DPS] = 105
+        self.dps[UNKNOWN106_DPS] = 106
+        self.dps[UNKNOWN110_DPS] = 110
+        self.assertDictEqual(
+            self.subject.device_state_attributes,
+            {
+                "unknown_102": True,
+                "timer": 103,
+                "countdown": 105,
+                "unknown_106": 106,
+                "unknown_110": 110,
+            },
+        )