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

Add support for Poiema One air purifier.

Issue #107
Jason Rumney 4 лет назад
Родитель
Сommit
7cb3984956

+ 1 - 2
ACKNOWLEDGEMENTS.md

@@ -66,6 +66,5 @@ Further device support has been made with the assistance of users.  Please consi
  - [PaulJoosten](https://github.com/PaulJoosten) for assistance in figuring out the similarities and capabilities of different Eurom heaters.
  - [jdavidr17](https://github.com/jdavidr17) for assistance with discovering timer parameters for switches.
  - [miannelli516](https://github.com/miannelli516) for assistance with TR9B thermostats.
- - [edwinyoo44](https://github.com/edwinyoo44) for contributing support for JJPro JPD01 dehumidifiers.
+ - [edwinyoo44](https://github.com/edwinyoo44) for contributing support for JJPro JPD01 dehumidifiers and assistance with Poiema One purifiers.
  - [mpetcuRO](https://github.com/mpetcuRO) for assistance with Hysen HT08WE-2 thermometers.
- 

+ 1 - 0
README.md

@@ -97,6 +97,7 @@ the device will not work despite being listed below.
 
 ### Air Purifiers
 - Renpho RP-AP001S air purifier
+- Poiema One air purifier
 
 ### Dehumidifiers
 - Goldair GPDH420 dehumidifier

+ 92 - 0
custom_components/tuya_local/devices/poiema_one_purifier.yaml

@@ -0,0 +1,92 @@
+name: Poiema One Air Purifier
+products:
+  - id: 2q2jgeegaziyugdg
+primary_entity:
+  entity: fan
+  icon: "mdi:air-purifier"
+  dps:
+    - id: 1
+      type: boolean
+      name: switch
+    - id: 3
+      type: string
+      name: preset_mode
+      mapping:
+        - dps_val: manual
+          value: Manual
+        - dps_val: auto
+          value: Auto
+        - dps_val: sleep
+          value: Sleep
+    - id: 4
+      type: string
+      name: speed
+      mapping:
+        - dps_val: "low"
+          value: 25
+        - dps_val: "mid"
+          value: 50
+        - dps_val: "high"
+          value: 75
+        - dps_val: "strong"
+          value: 100
+secondary_entities:
+  - entity: sensor
+    name: PM2.5
+    class: pm25
+    dps:
+      - id: 2
+        type: integer
+        name: sensor
+        class: measurement
+        unit: ugm3
+  - entity: lock
+    name: Child Lock
+    category: config
+    dps:
+      - id: 7
+        type: boolean
+        name: lock
+        mapping:
+          - dps_val: true
+            icon: "mdi:hand-back-right-off"
+          - dps_val: false
+            icon: "mdi-hand-back-right"
+  - entity: switch
+    name: Filter Reset
+    category: config
+    icon: "mdi:refresh"
+    dps:
+      - id: 11
+        type: boolean
+        name: switch
+  - entity: select
+    name: Timer
+    category: config
+    icon: "mdi:timer"
+    dps:
+      - id: 18
+        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"
+  - entity: sensor
+    name: Timer
+    category: diagnostic
+    icon: "mdi:timer"
+    dps:
+      - id: 19
+        type: integer
+        unit: min
+        name: sensor

+ 1 - 1
custom_components/tuya_local/helpers/device_config.py

@@ -187,7 +187,7 @@ class TuyaEntityConfig:
 
     @property
     def config_id(self):
-        """The identifier for this entitiy in the config."""
+        """The identifier for this entity in the config."""
         own_name = self._config.get("name")
         if own_name:
             return f"{self.entity}_{slugify(own_name)}"

+ 6 - 1
custom_components/tuya_local/helpers/mixin.py

@@ -2,7 +2,11 @@
 Mixins to make writing new platforms easier
 """
 import logging
-from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
+from homeassistant.const import (
+    CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
+    TEMP_CELSIUS,
+    TEMP_FAHRENHEIT,
+)
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -73,6 +77,7 @@ class TuyaLocalEntity:
 UNIT_ASCII_MAP = {
     "C": TEMP_CELSIUS,
     "F": TEMP_FAHRENHEIT,
+    "ugm3": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
 }
 
 

+ 11 - 0
tests/const.py

@@ -815,3 +815,14 @@ HYSEN_HY08WE2_THERMOSTAT_PAYLOAD = {
     "117": "keep",
     "118": "2days",
 }
+
+POIEMA_ONE_PURIFIER_PAYLOAD = {
+    "1": True,
+    "2": 12,
+    "3": "manual",
+    "4": "mid",
+    "7": False,
+    "11": False,
+    "18": "cancel",
+    "19": 0,
+}

+ 137 - 0
tests/devices/test_poiema_one_purifier.py

@@ -0,0 +1,137 @@
+from homeassistant.components.fan import SUPPORT_PRESET_MODE, SUPPORT_SET_SPEED
+from homeassistant.const import (
+    CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
+    DEVICE_CLASS_PM25,
+    TIME_MINUTES,
+)
+
+from ..const import POIEMA_ONE_PURIFIER_PAYLOAD
+from ..helpers import assert_device_properties_set
+from ..mixins.lock import BasicLockTests
+from ..mixins.select import BasicSelectTests
+from ..mixins.sensor import MultiSensorTests
+from ..mixins.switch import BasicSwitchTests, SwitchableTests
+from .base_device_tests import TuyaDeviceTestCase
+
+SWITCH_DPS = "1"
+PM25_DPS = "2"
+MODE_DPS = "3"
+SPEED_DPS = "4"
+LOCK_DPS = "7"
+RESET_DPS = "11"
+TIMER_DPS = "18"
+COUNTDOWN_DPS = "19"
+
+
+class TestPoeimaOnePurifier(
+    BasicLockTests,
+    BasicSelectTests,
+    BasicSwitchTests,
+    MultiSensorTests,
+    SwitchableTests,
+    TuyaDeviceTestCase,
+):
+    __test__ = True
+
+    def setUp(self):
+        self.setUpForConfig("poiema_one_purifier.yaml", POIEMA_ONE_PURIFIER_PAYLOAD)
+        self.subject = self.entities["fan"]
+        self.setUpSwitchable(SWITCH_DPS, self.subject)
+        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",
+            },
+        )
+        self.setUpBasicSwitch(RESET_DPS, self.entities.get("switch_filter_reset"))
+        self.setUpMultiSensors(
+            [
+                {
+                    "dps": PM25_DPS,
+                    "name": "sensor_pm2_5",
+                    "device_class": DEVICE_CLASS_PM25,
+                    "state_class": "measurement",
+                    "unit": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
+                },
+                {
+                    "dps": COUNTDOWN_DPS,
+                    "name": "sensor_timer",
+                    "unit": TIME_MINUTES,
+                },
+            ]
+        )
+        self.mark_secondary(
+            [
+                "lock_child_lock",
+                "switch_filter_reset",
+                "select_timer",
+                "sensor_timer",
+            ]
+        )
+
+    def test_supported_features(self):
+        self.assertEqual(
+            self.subject.supported_features,
+            SUPPORT_PRESET_MODE | SUPPORT_SET_SPEED,
+        )
+
+    def test_speed(self):
+        self.dps[SPEED_DPS] = "low"
+        self.assertEqual(self.subject.percentage, 25)
+
+    def test_speed_step(self):
+        self.assertEqual(self.subject.percentage_step, 25)
+
+    async def test_set_speed(self):
+        async with assert_device_properties_set(
+            self.subject._device, {SPEED_DPS: "mid"}
+        ):
+            await self.subject.async_set_percentage(50)
+
+    async def test_set_speed_snaps(self):
+        async with assert_device_properties_set(
+            self.subject._device, {SPEED_DPS: "high"}
+        ):
+            await self.subject.async_set_percentage(70)
+
+    def test_preset_modes(self):
+        self.assertCountEqual(
+            self.subject.preset_modes,
+            ["Manual", "Auto", "Sleep"],
+        )
+
+    def test_preset_mode(self):
+        self.dps[MODE_DPS] = "manual"
+        self.assertEqual(self.subject.preset_mode, "Manual")
+        self.dps[MODE_DPS] = "auto"
+        self.assertEqual(self.subject.preset_mode, "Auto")
+        self.dps[MODE_DPS] = "sleep"
+        self.assertEqual(self.subject.preset_mode, "Sleep")
+
+    async def test_set_preset_to_manual(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {MODE_DPS: "manual"},
+        ):
+            await self.subject.async_set_preset_mode("Manual")
+
+    async def test_set_preset_to_auto(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {MODE_DPS: "auto"},
+        ):
+            await self.subject.async_set_preset_mode("Auto")
+
+    async def test_set_preset_to_sleep(self):
+        async with assert_device_properties_set(
+            self.subject._device,
+            {MODE_DPS: "sleep"},
+        ):
+            await self.subject.async_set_preset_mode("Sleep")