Преглед изворни кода

Add support for AlecoAir D14 dehumidifier.

Issue #189
Jason Rumney пре 3 година
родитељ
комит
1e2f3b17d7

+ 1 - 0
ACKNOWLEDGEMENTS.md

@@ -99,3 +99,4 @@ Further device support has been made with the assistance of users.  Please consi
 - [yurgh](https://github.com/yurgh) for assistant supporting Eesee Adam dehumidifier
 - [KaportsevIA](https://github.com/KaportsevIA) for assistance supporting Hyundai Sahara dehumidifier and Yandax color bulb.
 - [poolMiniDomo](https://github.com/poolMiniDomo) for assistance supporting Moes Temperature and Humidity switches.
+- [pretoriano80](https://github.com/pretoriano80) for assistance supporting AlecoAir dehumidifiers.

+ 1 - 0
README.md

@@ -124,6 +124,7 @@ the device will not work despite being listed below.
 - JJPro JPD02 dehumidifier
 - Eesee Adam dehumidifier
 - Hyundai Sahara dehumidifier
+- AlecoAir D14 Purifying Dehumidifier
 
 ### Humidifiers
 - Eanons QT-JS2014 Purifying humidifier

+ 174 - 0
custom_components/tuya_local/devices/alecoair_d14_dehumidifier.yaml

@@ -0,0 +1,174 @@
+name: AlecoAir D14 dehumidifier
+primary_entity:
+  entity: humidifier
+  class: dehumidifier
+  dps:
+    - id: 1
+      name: switch
+      type: boolean
+      mapping:
+      - dps_val: false
+        icon: "mdi:air-humidifier-off"
+        icon_priority: 1
+      - dps_val: true
+        icon: "mdi:air-humidifier"
+        icon_priority: 4
+    - id: 2
+      name: humidity
+      type: integer
+      range:
+        min: 25
+        max: 80
+      mapping:
+        - step: 5
+    - id: 4
+      name: mode
+      type: string
+      mapping:
+        - dps_val: manual
+          value: Manual
+        - dps_val: laundry
+          value: Dry clothes
+          icon: "mdi:tshirt-crew-outline"
+          icon_priority: 3
+        - dps_val: purify
+          value: Purify
+          icon: "mdi:air-filter"
+          icon_priority: 3
+        - dps_val: sleep
+          value: Sleep
+          icon: "mdi:power-sleep"
+          icon_priority: 3
+    - id: 19
+      type: bitfield
+      name: error
+      mapping:
+        - dps_val: 1
+          icon: "mdi:cup-water"
+          icon_priority: 2
+      hidden: true
+secondary_entities:
+  - entity: fan
+    dps:
+      - id: 1
+        type: boolean
+        name: switch
+      - id: 4
+        type: string
+        name: dehumidifier_mode
+        hidden: true
+      - id: 5
+        type: string
+        name: speed
+        mapping:
+          - dps_val: low
+            value: 50
+            constraint: dehumidifier_mode
+            conditions:
+              - dps_val: laundry
+                invalid: true
+          - dps_val: high
+            value: 100
+            constraint: dehumidifier_mode
+            conditions:
+              - dps_val: sleep
+                invalid: true
+  - entity: switch
+    name: Ionizer
+    icon: "mdi:creation"
+    dps:
+      - id: 10
+        type: boolean
+        name: switch
+  - entity: sensor
+    name: Current Humidity
+    class: humidity
+    dps:
+      - id: 16
+        type: integer
+        name: sensor
+        unit: "%"
+        class: measurement
+  - entity: binary_sensor
+    name: Tank
+    class: problem
+    category: diagnostic
+    dps:
+      - id: 19
+        type: bitfield
+        name: sensor
+        mapping:
+          - dps_val: 0
+            value: false
+          - value: true
+  - entity: lock
+    name: Child Lock
+    category: config
+    dps:
+      - id: 14
+        type: boolean
+        name: lock
+        mapping:
+          - dps_val: true
+            icon: "mdi:hand-back-right-off"
+          - dps_val: false
+            icon: "mdi:hand-back-right"
+  - entity: select
+    name: Timer
+    icon: "mdi:timer"
+    category: config
+    dps:
+      - id: 17
+        type: string
+        name: option
+        mapping:
+          - dps_val: cancel
+            value: "Off"
+          - dps_val: 1h
+            value: 1 hour
+          - dps_val: 2h
+            value: 2 hours
+          - dps_val: 3h
+            value: 3 hours
+          - dps_val: 4h
+            value: 4 hours
+          - dps_val: 5h
+            value: 5 hours
+          - dps_val: 6h
+            value: 6 hours
+          - dps_val: 7h
+            value: 7 hours
+          - dps_val: 8h
+            value: 8 hours
+          - dps_val: 9h
+            value: 9 hours
+          - dps_val: 10h
+            value: 10 hours
+          - dps_val: 11h
+            value: 11 hours
+          - dps_val: 12h
+            value: 12 hours
+          - dps_val: 13h
+            value: 13 hours
+          - dps_val: 14h
+            value: 14 hours
+          - dps_val: 15h
+            value: 15 hours
+          - dps_val: 16h
+            value: 16 hours
+          - dps_val: 17h
+            value: 17 hours
+          - dps_val: 18h
+            value: 18 hours
+          - dps_val: 19h
+            value: 19 hours
+          - dps_val: 20h
+            value: 20 hours
+          - dps_val: 21h
+            value: 21 hours
+          - dps_val: 22h
+            value: 22 hours
+          - dps_val: 23h
+            value: 23 hours
+          - dps_val: 24h
+            value: 24 hours

+ 12 - 0
tests/const.py

@@ -1271,6 +1271,18 @@ EESEE_ADAM_PAYLOAD = {
     "19": 0,
 }
 
+ALECOAIR_D14_PAYLOAD = {
+    "1": True,
+    "2": 50,
+    "4": "manual",
+    "5": "low",
+    "10": False,
+    "14": False,
+    "16": 72,
+    "17": "cancel",
+    "19": 0,
+}
+
 HYUNDAI_SAHARA_PAYLOAD = {
     "1": True,
     "2": 50,

+ 240 - 0
tests/devices/test_alecoair_d14_dehumidifier.py

@@ -0,0 +1,240 @@
+from homeassistant.components.binary_sensor import BinarySensorDeviceClass
+from homeassistant.components.fan import FanEntityFeature
+from homeassistant.components.humidifier import HumidifierEntityFeature
+from homeassistant.components.sensor import SensorDeviceClass
+from homeassistant.const import PERCENTAGE
+
+from ..const import ALECOAIR_D14_PAYLOAD
+from ..helpers import assert_device_properties_set
+from ..mixins.binary_sensor import BasicBinarySensorTests
+from ..mixins.lock import BasicLockTests
+from ..mixins.select import BasicSelectTests
+from ..mixins.sensor import BasicSensorTests
+from ..mixins.switch import BasicSwitchTests, SwitchableTests
+from .base_device_tests import TuyaDeviceTestCase
+
+SWITCH_DPS = "1"
+HUMIDITY_DPS = "2"
+MODE_DPS = "4"
+FAN_DPS = "5"
+IONIZER_DPS = "10"
+LOCK_DPS = "14"
+CURRENTHUMID_DPS = "16"
+TIMER_DPS = "17"
+ERROR_DPS = "19"
+
+
+class TestAlecoAirDehumidifier(
+    BasicBinarySensorTests,
+    BasicLockTests,
+    BasicSelectTests,
+    BasicSensorTests,
+    BasicSwitchTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+):
+    __test__ = True
+
+    def setUp(self):
+        self.setUpForConfig("alecoair_d14_dehumidifier.yaml", ALECOAIR_D14_PAYLOAD)
+        self.subject = self.entities.get("humidifier")
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        self.fan = self.entities.get("fan")
+        self.setUpBasicLock(
+            LOCK_DPS,
+            self.entities.get("lock_child_lock"),
+        )
+        self.setUpBasicSelect(
+            TIMER_DPS,
+            self.entities.get("select_timer"),
+            {
+                "cancel": "Off",
+                "1h": "1 hour",
+                "2h": "2 hours",
+                "3h": "3 hours",
+                "4h": "4 hours",
+                "5h": "5 hours",
+                "6h": "6 hours",
+                "7h": "7 hours",
+                "8h": "8 hours",
+                "9h": "9 hours",
+                "10h": "10 hours",
+                "11h": "11 hours",
+                "12h": "12 hours",
+                "13h": "13 hours",
+                "14h": "14 hours",
+                "15h": "15 hours",
+                "16h": "16 hours",
+                "17h": "17 hours",
+                "18h": "18 hours",
+                "19h": "19 hours",
+                "20h": "20 hours",
+                "21h": "21 hours",
+                "22h": "22 hours",
+                "23h": "23 hours",
+                "24h": "24 hours",
+            },
+        )
+        self.setUpBasicBinarySensor(
+            ERROR_DPS,
+            self.entities.get("binary_sensor_tank"),
+            device_class=BinarySensorDeviceClass.PROBLEM,
+            testdata=(1, 0),
+        )
+        self.setUpBasicSensor(
+            CURRENTHUMID_DPS,
+            self.entities.get("sensor_current_humidity"),
+            device_class=SensorDeviceClass.HUMIDITY,
+            state_class="measurement",
+            unit=PERCENTAGE,
+        )
+        self.setUpBasicSwitch(
+            IONIZER_DPS,
+            self.entities.get("switch_ionizer"),
+        )
+        self.mark_secondary(["binary_sensor_tank", "lock_child_lock", "select_timer"])
+
+    def test_supported_features(self):
+        self.assertEqual(
+            self.subject.supported_features,
+            HumidifierEntityFeature.MODES,
+        )
+        self.assertEqual(
+            self.fan.supported_features,
+            FanEntityFeature.SET_SPEED,
+        )
+
+    def test_icon(self):
+        """Test that the icon is as expected."""
+        self.dps[SWITCH_DPS] = True
+        self.dps[MODE_DPS] = "manual"
+        self.dps[ERROR_DPS] = 0
+        self.assertEqual(self.subject.icon, "mdi:air-humidifier")
+
+        self.dps[SWITCH_DPS] = False
+        self.assertEqual(self.subject.icon, "mdi:air-humidifier-off")
+
+        self.dps[MODE_DPS] = "laundry"
+        self.assertEqual(self.subject.icon, "mdi:air-humidifier-off")
+
+        self.dps[SWITCH_DPS] = True
+        self.assertEqual(self.subject.icon, "mdi:tshirt-crew-outline")
+
+        self.dps[MODE_DPS] = "sleep"
+        self.assertEqual(self.subject.icon, "mdi:power-sleep")
+
+        self.dps[MODE_DPS] = "purify"
+        self.assertEqual(self.subject.icon, "mdi:air-filter")
+
+        self.dps[ERROR_DPS] = 1
+        self.assertEqual(self.subject.icon, "mdi:cup-water")
+
+    def test_min_target_humidity(self):
+        self.assertEqual(self.subject.min_humidity, 25)
+
+    def test_max_target_humidity(self):
+        self.assertEqual(self.subject.max_humidity, 80)
+
+    def test_target_humidity(self):
+        self.dps[HUMIDITY_DPS] = 55
+        self.assertEqual(self.subject.target_humidity, 55)
+
+    async def test_fan_turn_on(self):
+        async with assert_device_properties_set(
+            self.subject._device, {SWITCH_DPS: True}
+        ):
+            await self.fan.async_turn_on()
+
+    async def test_fan_turn_off(self):
+        async with assert_device_properties_set(
+            self.subject._device, {SWITCH_DPS: False}
+        ):
+            await self.fan.async_turn_off()
+
+    def test_modes(self):
+        self.assertCountEqual(
+            self.subject.available_modes,
+            [
+                "Manual",
+                "Dry clothes",
+                "Purify",
+                "Sleep",
+            ],
+        )
+
+    def test_mode(self):
+        self.dps[MODE_DPS] = "manual"
+        self.assertEqual(self.subject.mode, "Manual")
+        self.dps[MODE_DPS] = "laundry"
+        self.assertEqual(self.subject.mode, "Dry clothes")
+        self.dps[MODE_DPS] = "purify"
+        self.assertEqual(self.subject.mode, "Purify")
+        self.dps[MODE_DPS] = "sleep"
+        self.assertEqual(self.subject.mode, "Sleep")
+
+    async def test_set_mode_to_manual(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {
+                MODE_DPS: "manual",
+            },
+        ):
+            await self.subject.async_set_mode("Manual")
+            self.subject._device.anticipate_property_value.assert_not_called()
+
+    async def test_set_mode_to_clothes(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {
+                MODE_DPS: "laundry",
+            },
+        ):
+            await self.subject.async_set_mode("Dry clothes")
+            self.subject._device.anticipate_property_value.assert_not_called()
+
+    async def test_set_mode_to_purify(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {
+                MODE_DPS: "purify",
+            },
+        ):
+            await self.subject.async_set_mode("Purify")
+            self.subject._device.anticipate_property_value.assert_not_called()
+
+    async def test_set_mode_to_sleep(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {
+                MODE_DPS: "sleep",
+            },
+        ):
+            await self.subject.async_set_mode("Sleep")
+            self.subject._device.anticipate_property_value.assert_not_called()
+
+    def test_fan_speed_steps(self):
+        self.assertEqual(self.fan.speed_count, 2)
+
+    def test_fan_speed(self):
+        self.dps[FAN_DPS] = "low"
+        self.assertEqual(self.fan.percentage, 50)
+        self.dps[FAN_DPS] = "high"
+        self.assertEqual(self.fan.percentage, 100)
+
+    async def test_fan_set_speed_to_low(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {
+                FAN_DPS: "low",
+            },
+        ):
+            await self.fan.async_set_percentage(50)
+
+    async def test_fan_set_speed_to_high(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {
+                FAN_DPS: "high",
+            },
+        ):
+            await self.fan.async_set_percentage(100)