Просмотр исходного кода

Add tests for aspen_asp200_fan.yaml

- update docs for previous, this and next PR.
Jason Rumney 4 лет назад
Родитель
Сommit
d2e4ace8ce

+ 4 - 0
ACKNOWLEDGEMENTS.md

@@ -57,3 +57,7 @@ Further device support has been made with the assistance of users.  Please consi
  - [maartendamen](https://github.com/maartendamen) for assistance in supporting Eurom Mon Soleil 601 heaters.
  - [TeddyLafrite](https://github.com/TeddyLafrite) for assistance in supporting Nedis HTPL20F heaters.
  - [mvroosmalen1970](https://github.com/mvroosmalen1970) for assistance in supporting Eurom SaniWall 2000 heaters.
+ - [petrkotek](https://github.com/petrkotek) for contributing support for Madimack Elite V3 pool heatpumps.
+ - [irakhlin](https://github.com/irakhlin) for contributing support for Aspen ASP200 fans.
+ - [vampywiz17](https://github.com/vampywiz17) for contributing support for TMWF02 fan controllers.
+

+ 4 - 1
README.md

@@ -55,7 +55,8 @@ the device will not work despite being listed below.
 ### Pool heaters / heatpumps
 
 - Garden PAC pool heatpump
-- Madimack pool heatpump
+- Madimack Elite V3 pool heatpump
+- Madimack(model unknown) pool heatpump
 - Remora pool heatpump
 - BWT FI 45 heatpump
 - Poolex Silverline and Vertigo heatpump
@@ -86,6 +87,8 @@ the device will not work despite being listed below.
 - Deta fan controller
 - Arlec Grid Connect Smart Ceiling Fan (without light)
 - Stirling FS1-40DC Pedestal fan
+- Aspen ASP 200 fan
+- TMWF02 fan controller
 
 ### Air Purifiers
 - Renpho RP-AP001S air purifier

+ 25 - 45
custom_components/tuya_local/devices/aspen_fan.yaml → custom_components/tuya_local/devices/aspen_asp200_fan.yaml

@@ -1,4 +1,5 @@
 name: Aspen Fan
+legacy_type: aspen_fan
 primary_entity:
   entity: fan
   dps:
@@ -7,67 +8,44 @@ primary_entity:
       name: switch
     - id: 2
       type: string
-      name: preset_mode
+      name: direction
       mapping:
         - dps_val: in
-          value: cool
+          value: forward
         - dps_val: out
-          value: exhaust
+          value: reverse
         - dps_val: exch
           value: exchange
     - id: 3
       type: integer
       name: speed
+      range:
+        min: 0
+        max: 3
       mapping:
-        - dps_val: 1
-          value: low
-        - dps_val: 2
-          value: medium
-        - dps_val: 3
-          value: high
+        - scale: 0.03
+    - id: 101
+      type: boolean
+      name: preset_mode
+      mapping:
+        - dps_val: false
+          value: "constant"
+        - dps_val: true
+          value: "auto"
+    - id: 8
+      type: integer
+      name: unknown_8
 secondary_entities:
   - entity: climate
-    name: Fan
     dps:
       - id: 1
         type: boolean
-        name: switch
-      - id: 2
-        type: string
-        name: preset_mode
-        mapping:
-          - dps_val: in
-            value: intake
-          - dps_val: out
-            value: exhaust
-          - dps_val: exch
-            value: exchange
-      - id: 101
-        type: boolean
-        mapping:
-          - dps_val: false
-            value: "fan_only"
-            icon: "mdi:fan-off"
-          - dps_val: true
-            value: "cool"
-            icon: "mdi:fan"
         name: hvac_mode
-      - id: 3
-        type: integer
-        name: fan_mode
         mapping:
-          - dps_val: 1
-            value: low
-            icon: "mdi:fan-speed-1"
-          - dps_val: 2
-            value: medium
-            icon: "mdi:fan-speed-2"
-          - dps_val: 3
-            value: high
-            icon: "mdi:fan-speed-3"
-      - id: 8
-        type: integer
-        name: unknown_008
+          - dps_val: true
+            value: fan_only
+          - dps_val: false
+            value: "off"
       - id: 18
         name: temperature
         type: integer
@@ -78,10 +56,12 @@ secondary_entities:
       - id: 19
         name: current_temperature
         type: integer
+        unit: F
         icon: "mdi:thermometer"
         readonly: true
   - entity: light
     name: Display
+    category: config
     dps:
       - id: 102
         type: integer

+ 1 - 1
custom_components/tuya_local/manifest.json

@@ -2,7 +2,7 @@
     "domain": "tuya_local",
     "iot_class": "local_polling",
     "name": "Tuya Local",
-    "version": "0.13.3",
+    "version": "0.13.4",
     "documentation": "https://github.com/make-all/tuya-local",
     "issue_tracker": "https://github.com/make-all/tuya-local/issues",
     "dependencies": [],

+ 11 - 0
tests/const.py

@@ -664,3 +664,14 @@ NEDIS_HTPL20F_PAYLOAD = {
     "13": 0,
     "101": False,
 }
+
+ASPEN_ASP200_FAN_PAYLOAD = {
+    "1": True,
+    "2": "in",
+    "3": 1,
+    "8": 0,
+    "18": 20,
+    "19": 25,
+    "101": True,
+    "102": 3,
+}

+ 0 - 2
tests/devices/test_arlec_fan.py

@@ -6,8 +6,6 @@ from homeassistant.components.fan import (
     SUPPORT_SET_SPEED,
 )
 
-from homeassistant.const import STATE_UNAVAILABLE
-
 from ..const import ARLEC_FAN_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..mixins.select import BasicSelectTests

+ 180 - 0
tests/devices/test_aspen_adv200_fan.py

@@ -0,0 +1,180 @@
+from homeassistant.components.climate.const import (
+    HVAC_MODE_FAN_ONLY,
+    HVAC_MODE_OFF,
+    SUPPORT_TARGET_TEMPERATURE,
+)
+from homeassistant.components.fan import (
+    DIRECTION_FORWARD,
+    DIRECTION_REVERSE,
+    SUPPORT_DIRECTION,
+    SUPPORT_PRESET_MODE,
+    SUPPORT_SET_SPEED,
+)
+from homeassistant.const import TEMP_FAHRENHEIT
+
+from ..const import ASPEN_ASP200_FAN_PAYLOAD
+from ..helpers import assert_device_properties_set
+from ..mixins.climate import TargetTemperatureTests
+from ..mixins.light import DimmableLightTests
+from ..mixins.switch import SwitchableTests
+from .base_device_tests import TuyaDeviceTestCase
+
+SWITCH_DPS = "1"
+DIRECTION_DPS = "2"
+SPEED_DPS = "3"
+UNKNOWN8_DPS = "8"
+TEMPERATURE_DPS = "18"
+CURTEMP_DPS = "19"
+PRESET_DPS = "101"
+LIGHT_DPS = "102"
+
+
+class TestAspenASP200Fan(
+    DimmableLightTests, SwitchableTests, TargetTemperatureTests, TuyaDeviceTestCase
+):
+    __test__ = True
+
+    def setUp(self):
+        self.setUpForConfig("aspen_asp200_fan.yaml", ASPEN_ASP200_FAN_PAYLOAD)
+        self.subject = self.entities.get("fan")
+        self.climate = self.entities.get("climate")
+        self.setUpDimmableLight(
+            LIGHT_DPS,
+            self.entities.get("light_display"),
+            offval=0,
+            tests=[
+                (1, 85),
+                (2, 170),
+                (3, 255),
+            ],
+        )
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.setUpTargetTemperature(
+            TEMPERATURE_DPS,
+            self.climate,
+            min=40,
+            max=95,
+        )
+
+    def test_supported_features(self):
+        self.assertEqual(
+            self.subject.supported_features,
+            SUPPORT_DIRECTION | SUPPORT_PRESET_MODE | SUPPORT_SET_SPEED,
+        )
+        self.assertEqual(
+            self.climate.supported_features,
+            SUPPORT_TARGET_TEMPERATURE,
+        )
+
+    def test_fan_direction(self):
+        self.dps[DIRECTION_DPS] = "in"
+        self.assertEqual(self.subject.current_direction, DIRECTION_FORWARD)
+        self.dps[DIRECTION_DPS] = "out"
+        self.assertEqual(self.subject.current_direction, DIRECTION_REVERSE)
+        self.dps[DIRECTION_DPS] = "exch"
+        self.assertEqual(self.subject.current_direction, "exchange")
+
+    async def test_fan_set_direction_forward(self):
+        async with assert_device_properties_set(
+            self.subject._device, {DIRECTION_DPS: "in"}
+        ):
+            await self.subject.async_set_direction(DIRECTION_FORWARD)
+
+    async def test_fan_set_direction_reverse(self):
+        async with assert_device_properties_set(
+            self.subject._device, {DIRECTION_DPS: "out"}
+        ):
+            await self.subject.async_set_direction(DIRECTION_REVERSE)
+
+    async def test_fan_set_direction_exchange(self):
+        async with assert_device_properties_set(
+            self.subject._device, {DIRECTION_DPS: "exch"}
+        ):
+            await self.subject.async_set_direction("exchange")
+
+    def test_fan_speed(self):
+        self.dps[SPEED_DPS] = "1"
+        self.assertAlmostEqual(self.subject.percentage, 33.3, 1)
+        self.dps[SPEED_DPS] = "2"
+        self.assertAlmostEqual(self.subject.percentage, 66.7, 1)
+        self.dps[SPEED_DPS] = "3"
+        self.assertEqual(self.subject.percentage, 100)
+
+    def test_fan_speed_step(self):
+        self.assertAlmostEqual(self.subject.percentage_step, 33.33, 2)
+        self.assertEqual(self.subject.speed_count, 3)
+
+    async def test_fan_set_speed(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {SPEED_DPS: 1},
+        ):
+            await self.subject.async_set_percentage(33)
+
+    async def test_fan_set_speed_snaps(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {SPEED_DPS: 2},
+        ):
+            await self.subject.async_set_percentage(80)
+
+    def test_fan_preset_modes(self):
+        self.assertCountEqual(self.subject.preset_modes, ["constant", "auto"])
+
+    def test_fan_preset_mode(self):
+        self.dps[PRESET_DPS] = False
+        self.assertEqual(self.subject.preset_mode, "constant")
+        self.dps[PRESET_DPS] = True
+        self.assertEqual(self.subject.preset_mode, "auto")
+
+    async def test_fan_set_preset_to_constant(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: False},
+        ):
+            await self.subject.async_set_preset_mode("constant")
+
+    async def test_fan_set_preset_to_auto(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: True},
+        ):
+            await self.subject.async_set_preset_mode("auto")
+
+    def test_climate_current_temperature(self):
+        self.dps[CURTEMP_DPS] = 24
+        self.assertEqual(self.climate.current_temperature, 24)
+
+    def test_climate_temperature_unit(self):
+        self.assertEqual(self.climate.temperature_unit, TEMP_FAHRENHEIT)
+
+    def test_climate_hvac_mode(self):
+        self.dps[SWITCH_DPS] = False
+        self.assertEqual(self.climate.hvac_mode, HVAC_MODE_OFF)
+        self.dps[SWITCH_DPS] = True
+        self.assertEqual(self.climate.hvac_mode, HVAC_MODE_FAN_ONLY)
+
+    def test_climate_hvac_modes(self):
+        self.assertCountEqual(
+            self.climate.hvac_modes,
+            [HVAC_MODE_FAN_ONLY, HVAC_MODE_OFF],
+        )
+
+    async def test_climate_turn_on(self):
+        async with assert_device_properties_set(
+            self.climate._device,
+            {SWITCH_DPS: True},
+        ):
+            await self.climate.async_set_hvac_mode(HVAC_MODE_FAN_ONLY)
+
+    async def test_climate_turn_off(self):
+        async with assert_device_properties_set(
+            self.climate._device,
+            {SWITCH_DPS: False},
+        ):
+            await self.climate.async_set_hvac_mode(HVAC_MODE_OFF)
+
+    def test_device_state_attributes(self):
+        self.dps[UNKNOWN8_DPS] = 8
+        self.assertDictEqual(self.subject.device_state_attributes, {"unknown_8": 8})
+        self.assertEqual(self.climate.device_state_attributes, {})

+ 16 - 103
tests/devices/test_wetair_wch750_heater.py

@@ -13,6 +13,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.light import DimmableLightTests
 from ..mixins.select import BasicSelectTests
 from ..mixins.sensor import BasicSensorTests
 from .base_device_tests import TuyaDeviceTestCase
@@ -30,6 +31,7 @@ BRIGHTNESS_DPS = "101"
 class TestWetairWCH750Heater(
     BasicSelectTests,
     BasicSensorTests,
+    DimmableLightTests,
     TargetTemperatureTests,
     TuyaDeviceTestCase,
 ):
@@ -44,6 +46,16 @@ class TestWetairWCH750Heater(
             min=10,
             max=35,
         )
+        self.setUpDimmableLight(
+            BRIGHTNESS_DPS,
+            self.entities.get("light_display"),
+            offval="level0",
+            tests=[
+                ("level1", 85),
+                ("level2", 170),
+                ("level3", 255),
+            ],
+        )
         self.light = self.entities.get("light_display")
         self.setUpBasicSelect(
             TIMER_DPS,
@@ -149,7 +161,7 @@ class TestWetairWCH750Heater(
         ):
             await self.subject.async_set_hvac_mode(HVAC_MODE_HEAT)
 
-    async def test_trn_off(self):
+    async def test_turn_off(self):
         async with assert_device_properties_set(
             self.subject._device,
             {HVACMODE_DPS: False},
@@ -204,111 +216,12 @@ class TestWetairWCH750Heater(
             },
         )
 
-    def test_light_supported_color_modes(self):
-        self.assertCountEqual(
-            self.light.supported_color_modes,
-            [COLOR_MODE_BRIGHTNESS],
-        )
-
-    def test_light_color_mode(self):
-        self.assertEqual(self.light.color_mode, COLOR_MODE_BRIGHTNESS)
-
     def test_light_icon(self):
-        self.assertEqual(self.light.icon, None)
-
-    def test_light_is_on(self):
-        self.dps[BRIGHTNESS_DPS] = "level0"
-        self.assertEqual(self.light.is_on, False)
-
-        self.dps[BRIGHTNESS_DPS] = "level1"
-        self.assertEqual(self.light.is_on, True)
-        self.dps[BRIGHTNESS_DPS] = "level2"
-        self.assertEqual(self.light.is_on, True)
-        self.dps[BRIGHTNESS_DPS] = "level3"
-        self.assertEqual(self.light.is_on, True)
-        # Test the case where device is not ready does not cause errors that
-        # would prevent initialization.
-        self.dps[BRIGHTNESS_DPS] = None
-        self.assertEqual(self.light.is_on, False)
-
-    def test_light_brightness(self):
-        self.dps[BRIGHTNESS_DPS] = "level0"
-        self.assertEqual(self.light.brightness, 0)
-
-        self.dps[BRIGHTNESS_DPS] = "level1"
-        self.assertEqual(self.light.brightness, 85)
-
-        self.dps[BRIGHTNESS_DPS] = "level2"
-        self.assertEqual(self.light.brightness, 170)
-
-        self.dps[BRIGHTNESS_DPS] = "level3"
-        self.assertEqual(self.light.brightness, 255)
-
-    def test_light_state_attributes(self):
-        self.assertEqual(self.light.device_state_attributes, {})
-
-    async def test_light_turn_on(self):
-        async with assert_device_properties_set(
-            self.light._device, {BRIGHTNESS_DPS: "level3"}
-        ):
-            await self.light.async_turn_on()
-
-    async def test_light_turn_off(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level0"},
-        ):
-            await self.light.async_turn_off()
-
-    async def test_light_brightness_to_low(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level1"},
-        ):
-            await self.light.async_turn_on(brightness=85)
-
-    async def test_light_brightness_to_mid(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level2"},
-        ):
-            await self.light.async_turn_on(brightness=170)
-
-    async def test_light_brightness_to_high(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level3"},
-        ):
-            await self.light.async_turn_on(brightness=255)
-
-    async def test_light_brightness_to_off(self):
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level0"},
-        ):
-            await self.light.async_turn_on(brightness=0)
-
-    async def test_toggle_turns_the_light_on_when_it_was_off(self):
-        self.dps[BRIGHTNESS_DPS] = "level0"
-
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level3"},
-        ):
-            await self.light.async_toggle()
-
-    async def test_toggle_turns_the_light_off_when_it_was_on(self):
-        self.dps[BRIGHTNESS_DPS] = "level2"
-
-        async with assert_device_properties_set(
-            self.light._device,
-            {BRIGHTNESS_DPS: "level0"},
-        ):
-            await self.light.async_toggle()
+        self.assertEqual(self.dimmableLight.icon, None)
 
     async def test_light_brightness_snaps(self):
         async with assert_device_properties_set(
-            self.light._device,
+            self.dimmableLight._device,
             {BRIGHTNESS_DPS: "level1"},
         ):
-            await self.light.async_turn_on(brightness=100)
+            await self.dimmableLight.async_turn_on(brightness=100)

+ 2 - 2
tests/mixins/climate.py

@@ -67,7 +67,7 @@ class TargetTemperatureTests:
         test_dpsval = test_val * self.targetTempScale
         test_val = (test_dpsval + 0.3) / self.targetTempScale
         async with assert_device_properties_set(
-            self.subject._device,
+            self.targetTemp._device,
             {self.targetTempDps: test_dpsval},
         ):
-            await self.subject.async_set_target_temperature(test_val)
+            await self.targetTemp.async_set_target_temperature(test_val)

+ 60 - 1
tests/mixins/light.py

@@ -1,5 +1,8 @@
 # Mixins for testing lights
-from homeassistant.components.light import COLOR_MODE_ONOFF
+from homeassistant.components.light import (
+    COLOR_MODE_BRIGHTNESS,
+    COLOR_MODE_ONOFF,
+)
 
 from ..helpers import assert_device_properties_set
 
@@ -64,3 +67,59 @@ class BasicLightTests:
 
     def test_basic_light_state_attributes(self):
         self.assertEqual(self.basicLight.device_state_attributes, {})
+
+
+class DimmableLightTests:
+    def setUpDimmableLight(self, dps, subject, offval=0, tests=[(100, 100)]):
+        self.dimmableLight = subject
+        self.dimmableLightDps = dps
+        self.dimmableLightOff = offval
+        self.dimmableLightTest = tests
+
+    def test_dimmable_light_supported_features(self):
+        self.dps[self.dimmableLightDps] = self.dimmableLightOff
+        self.assertFalse(self.dimmableLight.is_on)
+        self.dps[self.dimmableLightDps] = self.dimmableLightTest[0][0]
+        self.assertTrue(self.dimmableLight.is_on)
+        self.dps[self.dimmableLightDps] = None
+        self.assertFalse(self.dimmableLight.is_on)
+
+    def test_dimmable_light_brightness(self):
+        self.dps[self.dimmableLightDps] = self.dimmableLightOff
+        self.assertEqual(self.dimmableLight.brightness, 0)
+        for dps, val in self.dimmableLightTest:
+            self.dps[self.dimmableLightDps] = dps
+            self.assertEqual(self.dimmableLight.brightness, val)
+
+    def test_dimmable_light_state_attributes(self):
+        self.assertEqual(self.dimmableLight.device_state_attributes, {})
+
+    async def test_dimmable_light_turn_off(self):
+        async with assert_device_properties_set(
+            self.dimmableLight._device,
+            {self.dimmableLightDps: self.dimmableLightOff},
+        ):
+            await self.dimmableLight.async_turn_off()
+
+    async def test_dimmable_light_set_brightness(self):
+        for dps, val in self.dimmableLightTest:
+            async with assert_device_properties_set(
+                self.dimmableLight._device,
+                {self.dimmableLightDps: dps},
+            ):
+                await self.dimmableLight.async_turn_on(brightness=val)
+
+    async def test_dimmable_light_set_brightness_to_off(self):
+        async with assert_device_properties_set(
+            self.dimmableLight._device,
+            {self.dimmableLightDps: self.dimmableLightOff},
+        ):
+            await self.dimmableLight.async_turn_on(brightness=0)
+
+    async def test_dimmable_light_toggle_turns_off_when_it_was_on(self):
+        self.dps[self.dimmableLightDps] = self.dimmableLightTest[0][0]
+        async with assert_device_properties_set(
+            self.dimmableLight._device,
+            {self.dimmableLightDps: self.dimmableLightOff},
+        ):
+            await self.dimmableLight.async_toggle()