Преглед на файлове

Preliminary support for Anko fan (Issue #22)

Reportedly setting speed is not working. Nor is it working with tuyacli, so it seems the device needs something more than setting that single dps alone.
Jason Rumney преди 4 години
родител
ревизия
db61fc3642
променени са 2 файла, в които са добавени 210 реда и са изтрити 0 реда
  1. 36 0
      custom_components/tuya_local/devices/anko_fan.yaml
  2. 174 0
      tests/devices/test_anko_fan.py

+ 36 - 0
custom_components/tuya_local/devices/anko_fan.yaml

@@ -0,0 +1,36 @@
+name: Anko Fan
+primary_entity:
+  entity: fan
+  dps:
+    - id: 1
+      type: boolean
+      name: switch
+    - id: 2
+      type: string
+      name: preset_mode
+      mapping:
+        - dps_val: normal
+          value: normal
+        - dps_val: nature
+          value: nature
+        - dps_val: sleep
+          value: sleep
+    - id: 3
+      type: integer
+      name: speed
+      range:
+        min: 1
+        max: 8
+      mapping:
+        - scale: 0.08
+    - id: 4
+      type: string
+      name: oscillate
+      mapping:
+        - dps_val: "auto"
+          value: true
+        - dps_val: "off"
+          value: false
+    - id: 6
+      type: integer
+      name: timer

+ 174 - 0
tests/devices/test_anko_fan.py

@@ -0,0 +1,174 @@
+from unittest import IsolatedAsyncioTestCase, skip
+from unittest.mock import AsyncMock, patch
+
+from homeassistant.components.fan import (
+    SUPPORT_OSCILLATE,
+    SUPPORT_PRESET_MODE,
+    SUPPORT_SET_SPEED,
+)
+
+from homeassistant.const import STATE_UNAVAILABLE
+from custom_components.tuya_local.generic.fan import TuyaLocalFan
+from custom_components.tuya_local.helpers.device_config import TuyaDeviceConfig
+
+from ..const import ANKO_FAN_PAYLOAD
+from ..helpers import assert_device_properties_set
+
+SWITCH_DPS = "1"
+PRESET_DPS = "2"
+SPEED_DPS = "3"
+OSCILLATE_DPS = "4"
+TIMER_DPS = "6"
+
+
+class TestAnkoFan(IsolatedAsyncioTestCase):
+    def setUp(self):
+        device_patcher = patch("custom_components.tuya_local.device.TuyaLocalDevice")
+        self.addCleanup(device_patcher.stop)
+        self.mock_device = device_patcher.start()
+        cfg = TuyaDeviceConfig("anko_fan.yaml")
+        entities = {}
+        entities[cfg.primary_entity.entity] = cfg.primary_entity
+        for e in cfg.secondary_entities():
+            entities[e.entity] = e
+
+        self.fan_name = (
+            "missing" if "fan" not in entities.keys() else entities["fan"].name
+        )
+
+        self.subject = TuyaLocalFan(self.mock_device(), entities.get("fan"))
+        self.dps = ANKO_FAN_PAYLOAD.copy()
+        self.subject._device.get_property.side_effect = lambda id: self.dps[id]
+
+    def test_supported_features(self):
+        self.assertEqual(
+            self.subject.supported_features,
+            SUPPORT_OSCILLATE | SUPPORT_PRESET_MODE | SUPPORT_SET_SPEED,
+        )
+
+    def test_should_poll(self):
+        self.assertTrue(self.subject.should_poll)
+
+    def test_name_returns_device_name(self):
+        self.assertEqual(self.subject.name, self.subject._device.name)
+
+    def test_friendly_name_returns_config_name(self):
+        self.assertEqual(self.subject.friendly_name, self.fan_name)
+
+    def test_unique_id_returns_device_unique_id(self):
+        self.assertEqual(self.subject.unique_id, self.subject._device.unique_id)
+
+    def test_device_info_returns_device_info_from_device(self):
+        self.assertEqual(self.subject.device_info, self.subject._device.device_info)
+
+    def test_is_on(self):
+        self.dps[SWITCH_DPS] = True
+        self.assertTrue(self.subject.is_on)
+
+        self.dps[SWITCH_DPS] = False
+        self.assertFalse(self.subject.is_on)
+
+        self.dps[SWITCH_DPS] = None
+        self.assertEqual(self.subject.is_on, STATE_UNAVAILABLE)
+
+    async def test_turn_on(self):
+        async with assert_device_properties_set(
+            self.subject._device, {SWITCH_DPS: True}
+        ):
+            await self.subject.async_turn_on()
+
+    async def test_turn_off(self):
+        async with assert_device_properties_set(
+            self.subject._device, {SWITCH_DPS: False}
+        ):
+            await self.subject.async_turn_off()
+
+    def test_preset_mode(self):
+        self.dps[PRESET_DPS] = "normal"
+        self.assertEqual(self.subject.preset_mode, "normal")
+
+        self.dps[PRESET_DPS] = "nature"
+        self.assertEqual(self.subject.preset_mode, "nature")
+
+        self.dps[PRESET_DPS] = "sleep"
+        self.assertEqual(self.subject.preset_mode, "sleep")
+
+        self.dps[PRESET_DPS] = None
+        self.assertIs(self.subject.preset_mode, None)
+
+    def test_preset_modes(self):
+        self.assertCountEqual(self.subject.preset_modes, ["normal", "nature", "sleep"])
+
+    async def test_set_preset_mode_to_normal(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: "normal"},
+        ):
+            await self.subject.async_set_preset_mode("normal")
+
+    async def test_set_preset_mode_to_nature(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: "nature"},
+        ):
+            await self.subject.async_set_preset_mode("nature")
+
+    async def test_set_preset_mode_to_sleep(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {PRESET_DPS: "sleep"},
+        ):
+            await self.subject.async_set_preset_mode("sleep")
+
+    def test_oscillating(self):
+        self.dps[OSCILLATE_DPS] = False
+        self.assertFalse(self.subject.oscillating)
+
+        self.dps[OSCILLATE_DPS] = True
+        self.assertTrue(self.subject.oscillating)
+
+        self.dps[OSCILLATE_DPS] = None
+        self.assertFalse(self.subject.oscillating)
+
+    async def test_oscillate_off(self):
+        async with assert_device_properties_set(
+            self.subject._device, {OSCILLATE_DPS: "off"}
+        ):
+            await self.subject.async_oscillate(False)
+
+    async def test_oscillate_on(self):
+        async with assert_device_properties_set(
+            self.subject._device, {OSCILLATE_DPS: "auto"}
+        ):
+            await self.subject.async_oscillate(True)
+
+    def test_speed(self):
+        self.dps[PRESET_DPS] = "normal"
+        self.dps[SPEED_DPS] = 4
+        self.assertEqual(self.subject.percentage, 50)
+
+    def test_speed_step(self):
+        self.assertEqual(self.subject.percentage_step, 12.5)
+
+    async def test_set_speed_in_normal_mode(self):
+        self.dps[PRESET_DPS] = "normal"
+        async with assert_device_properties_set(self.subject._device, {SPEED_DPS: 2}):
+            await self.subject.async_set_percentage(25)
+
+    async def test_set_speed_in_normal_mode_snaps(self):
+        self.dps[PRESET_DPS] = "normal"
+        async with assert_device_properties_set(self.subject._device, {SPEED_DPS: 6}):
+            await self.subject.async_set_percentage(80)
+
+    def test_device_state_attributes(self):
+        self.dps[TIMER_DPS] = "5"
+        self.assertEqual(self.subject.device_state_attributes, {"timer": "5"})
+
+    async def test_update(self):
+        result = AsyncMock()
+        self.subject._device.async_refresh.return_value = result()
+
+        await self.subject.async_update()
+
+        self.subject._device.async_refresh.assert_called_once()
+        result.assert_awaited()