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

Implement basic conditions.

Implement enough of conditional dps behaviour that purline heaters should now be able to operate with the generic class.
Jason Rumney 4 лет назад
Родитель
Сommit
3010ceb8ef

+ 6 - 6
custom_components/tuya_local/devices/gardenpac_heatpump.yaml

@@ -39,12 +39,12 @@ primary_entity:
       name: temperature
       name: temperature
       type: integer
       type: integer
       mapping:
       mapping:
-        constraint: temperature_unit
-        conditions:
-          - dps_val: false
-            range:
-              min: 60
-              max: 115
+        - constraint: temperature_unit
+          conditions:
+            - dps_val: false
+              range:
+                min: 60
+                max: 115
       range:
       range:
         min: 18
         min: 18
         max: 45
         max: 45

+ 13 - 13
custom_components/tuya_local/devices/goldair_gpph_heater.yaml

@@ -22,13 +22,13 @@ primary_entity:
         min: 5
         min: 5
         max: 35
         max: 35
       mapping:
       mapping:
-        constraint: preset_mode
-        conditions:
-          - dps_val: "ECO"
-            value-redirect: eco_temperature
-          - dps_val: "AF"
-            invalid: true
-            value: 5
+        - constraint: preset_mode
+          conditions:
+            - dps_val: "ECO"
+              value-redirect: eco_temperature
+            - dps_val: "AF"
+              invalid: true
+              value: 5
       name: temperature
       name: temperature
     - id: 3
     - id: 3
       type: integer
       type: integer
@@ -91,12 +91,12 @@ primary_entity:
         min: 5
         min: 5
         max: 21
         max: 21
       mapping:
       mapping:
-        constraint: preset_mode
-        conditions:
-          - dps_val: "C"
-            invalid: true
-          - dps_val: "AF"
-            invalid: true
+        - constraint: preset_mode
+          conditions:
+            - dps_val: "C"
+              invalid: true
+            - dps_val: "AF"
+              invalid: true
       hidden: true
       hidden: true
       name: eco_temperature
       name: eco_temperature
 secondary_entities:
 secondary_entities:

+ 35 - 6
custom_components/tuya_local/helpers/device_config.py

@@ -138,6 +138,13 @@ class TuyaEntityConfig:
         for d in self._config["dps"]:
         for d in self._config["dps"]:
             yield TuyaDpsConfig(self, d)
             yield TuyaDpsConfig(self, d)
 
 
+    def find_dps(self, name):
+        """Find a dps with the specified name."""
+        for d in self.dps():
+            if d.name == name:
+                return d
+        return None
+
 
 
 class TuyaDpsConfig:
 class TuyaDpsConfig:
     """Representation of a dps config."""
     """Representation of a dps config."""
@@ -168,11 +175,11 @@ class TuyaDpsConfig:
 
 
     def get_value(self, device):
     def get_value(self, device):
         """Return the value of the dps from the given device."""
         """Return the value of the dps from the given device."""
-        return self.map_from_dps(device.get_property(self.id))
+        return self.map_from_dps(device.get_property(self.id), device)
 
 
     async def async_set_value(self, device, value):
     async def async_set_value(self, device, value):
         """Set the value of the dps in the given device to given value."""
         """Set the value of the dps in the given device to given value."""
-        await device.async_set_property(self.id, self.map_to_dps(value))
+        await device.async_set_property(self.id, self.map_to_dps(value, device))
 
 
     @property
     @property
     def values(self):
     def values(self):
@@ -183,7 +190,12 @@ class TuyaDpsConfig:
         for map in self._config["mapping"]:
         for map in self._config["mapping"]:
             if "value" in map:
             if "value" in map:
                 v.append(map["value"])
                 v.append(map["value"])
-        return v if len(v) > 0 else None
+            if "conditions" in map:
+                for c in map["conditions"]:
+                    if "value" in c:
+                        v.append(c["value"])
+
+        return list(set(v)) if len(v) > 0 else None
 
 
     @property
     @property
     def range(self):
     def range(self):
@@ -201,7 +213,7 @@ class TuyaDpsConfig:
     def isreadonly(self):
     def isreadonly(self):
         return "readonly" in self._config.keys() and self._config["readonly"] is True
         return "readonly" in self._config.keys() and self._config["readonly"] is True
 
 
-    def map_from_dps(self, value):
+    def map_from_dps(self, value, device):
         result = value
         result = value
         replaced = False
         replaced = False
         default_value = None
         default_value = None
@@ -218,6 +230,18 @@ class TuyaDpsConfig:
                     if "value" in map:
                     if "value" in map:
                         result = map["value"]
                         result = map["value"]
                         replaced = True
                         replaced = True
+                    if "conditions" in map:
+                        cond_dps = self
+                        if "constraint" in map:
+                            cond_dps = self._entity.find_dps(map["constraint"])
+                        for c in map["conditions"]:
+                            if (
+                                "dps_val" in c
+                                and c["dps_val"] == device.get_property(cond_dps.id)
+                                and "value" in c
+                            ):
+                                result = c["value"]
+                                replaced = True
 
 
         if not replaced and default_value is not None:
         if not replaced and default_value is not None:
             result = default_value
             result = default_value
@@ -238,7 +262,7 @@ class TuyaDpsConfig:
 
 
         return result
         return result
 
 
-    def map_to_dps(self, value):
+    def map_to_dps(self, value, device):
         result = value
         result = value
         replaced = False
         replaced = False
         scale = 1
         scale = 1
@@ -253,7 +277,12 @@ class TuyaDpsConfig:
                 ):
                 ):
                     result = map["dps_val"]
                     result = map["dps_val"]
                     replaced = True
                     replaced = True
-
+                elif "conditions" in map:
+                    for c in map["conditions"]:
+                        if "value" in c and c["value"] == value:
+                            result = map["dps_val"]
+                            c_dps = self._entity.find_dps(map["constraint"])
+                            device.set_property(c_dps.id, c["dps_val"])
                 if (
                 if (
                     "scale" in map
                     "scale" in map
                     and "value" not in map
                     and "value" not in map

+ 1 - 1
tests/devices/test_gardenpac_heatpump.py

@@ -150,7 +150,7 @@ class TestGardenPACPoolHeatpump(IsolatedAsyncioTestCase):
         self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
         self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
 
 
     def test_hvac_modes(self):
     def test_hvac_modes(self):
-        self.assertEqual(self.subject.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_HEAT])
+        self.assertCountEqual(self.subject.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_HEAT])
 
 
     async def test_turn_on(self):
     async def test_turn_on(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(

+ 11 - 9
tests/devices/test_goldair_fan.py

@@ -95,7 +95,9 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
         self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
 
 
     def test_hvac_modes(self):
     def test_hvac_modes(self):
-        self.assertEqual(self.subject.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY])
+        self.assertCountEqual(
+            self.subject.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
+        )
 
 
     async def test_turn_on(self):
     async def test_turn_on(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
@@ -175,7 +177,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         ):
         ):
             await self.subject.async_set_swing_mode(SWING_HORIZONTAL)
             await self.subject.async_set_swing_mode(SWING_HORIZONTAL)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     def test_fan_modes(self):
     def test_fan_modes(self):
         self.dps[PRESET_DPS] = "normal"
         self.dps[PRESET_DPS] = "normal"
         self.assertCountEqual(self.subject.fan_modes, list(range(1, 13)))
         self.assertCountEqual(self.subject.fan_modes, list(range(1, 13)))
@@ -189,7 +191,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         self.dps[PRESET_DPS] = None
         self.dps[PRESET_DPS] = None
         self.assertEqual(self.subject.fan_modes, [])
         self.assertEqual(self.subject.fan_modes, [])
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     def test_fan_mode_for_normal_preset(self):
     def test_fan_mode_for_normal_preset(self):
         self.dps[PRESET_DPS] = "normal"
         self.dps[PRESET_DPS] = "normal"
 
 
@@ -205,7 +207,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         self.dps[FANMODE_DPS] = None
         self.dps[FANMODE_DPS] = None
         self.assertEqual(self.subject.fan_mode, None)
         self.assertEqual(self.subject.fan_mode, None)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     async def test_set_fan_mode_for_normal_preset(self):
     async def test_set_fan_mode_for_normal_preset(self):
         self.dps[PRESET_DPS] = "normal"
         self.dps[PRESET_DPS] = "normal"
 
 
@@ -215,7 +217,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         ):
         ):
             await self.subject.async_set_fan_mode(6)
             await self.subject.async_set_fan_mode(6)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     def test_fan_mode_for_eco_preset(self):
     def test_fan_mode_for_eco_preset(self):
         self.dps[PRESET_DPS] = "nature"
         self.dps[PRESET_DPS] = "nature"
 
 
@@ -231,7 +233,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         self.dps[FANMODE_DPS] = None
         self.dps[FANMODE_DPS] = None
         self.assertEqual(self.subject.fan_mode, None)
         self.assertEqual(self.subject.fan_mode, None)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     async def test_set_fan_mode_for_eco_preset(self):
     async def test_set_fan_mode_for_eco_preset(self):
         self.dps[PRESET_DPS] = "nature"
         self.dps[PRESET_DPS] = "nature"
 
 
@@ -241,7 +243,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         ):
         ):
             await self.subject.async_set_fan_mode(1)
             await self.subject.async_set_fan_mode(1)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     def test_fan_mode_for_sleep_preset(self):
     def test_fan_mode_for_sleep_preset(self):
         self.dps[PRESET_DPS] = PRESET_SLEEP
         self.dps[PRESET_DPS] = PRESET_SLEEP
 
 
@@ -257,7 +259,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         self.dps[FANMODE_DPS] = None
         self.dps[FANMODE_DPS] = None
         self.assertEqual(self.subject.fan_mode, None)
         self.assertEqual(self.subject.fan_mode, None)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     async def test_set_fan_mode_for_sleep_preset(self):
     async def test_set_fan_mode_for_sleep_preset(self):
         self.dps[PRESET_DPS] = PRESET_SLEEP
         self.dps[PRESET_DPS] = PRESET_SLEEP
 
 
@@ -267,7 +269,7 @@ class TestGoldairFan(IsolatedAsyncioTestCase):
         ):
         ):
             await self.subject.async_set_fan_mode(2)
             await self.subject.async_set_fan_mode(2)
 
 
-    @skip("Conditions not supported yet")
+    @skip("Complex conditions not supported yet")
     async def test_set_fan_mode_does_nothing_when_preset_mode_is_not_set(self):
     async def test_set_fan_mode_does_nothing_when_preset_mode_is_not_set(self):
         self.dps[PRESET_DPS] = None
         self.dps[PRESET_DPS] = None
 
 

+ 1 - 4
tests/devices/test_purline_m100_heater.py

@@ -160,7 +160,6 @@ class TestPulineM100Heater(IsolatedAsyncioTestCase):
         self.dps[CURRENTTEMP_DPS] = 25
         self.dps[CURRENTTEMP_DPS] = 25
         self.assertEqual(self.subject.current_temperature, 25)
         self.assertEqual(self.subject.current_temperature, 25)
 
 
-    @skip("Conditions not supported yet")
     def test_hvac_mode(self):
     def test_hvac_mode(self):
         self.dps[HVACMODE_DPS] = True
         self.dps[HVACMODE_DPS] = True
         self.dps[PRESET_DPS] = "auto"
         self.dps[PRESET_DPS] = "auto"
@@ -175,7 +174,6 @@ class TestPulineM100Heater(IsolatedAsyncioTestCase):
         self.dps[HVACMODE_DPS] = None
         self.dps[HVACMODE_DPS] = None
         self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
         self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
 
 
-    @skip("Conditions not supported yet")
     def test_hvac_modes(self):
     def test_hvac_modes(self):
         self.assertCountEqual(
         self.assertCountEqual(
             self.subject.hvac_modes,
             self.subject.hvac_modes,
@@ -197,7 +195,6 @@ class TestPulineM100Heater(IsolatedAsyncioTestCase):
         ):
         ):
             await self.subject.async_set_hvac_mode(HVAC_MODE_OFF)
             await self.subject.async_set_hvac_mode(HVAC_MODE_OFF)
 
 
-    @skip("Conditions not supported yet")
     async def test_turn_on_fan(self):
     async def test_turn_on_fan(self):
         async with assert_device_properties_set_optional(
         async with assert_device_properties_set_optional(
             self.subject._device,
             self.subject._device,
@@ -332,7 +329,7 @@ class TestPulineM100Heater(IsolatedAsyncioTestCase):
     def test_switch_is_same_device(self):
     def test_switch_is_same_device(self):
         self.assertEqual(self.switch._device, self.subject._device)
         self.assertEqual(self.switch._device, self.subject._device)
 
 
-    def test_switch_class_is_outlet(self):
+    def test_switch_class_is_switch(self):
         self.assertEqual(self.switch.device_class, DEVICE_CLASS_SWITCH)
         self.assertEqual(self.switch.device_class, DEVICE_CLASS_SWITCH)
 
 
     def test_switch_is_on(self):
     def test_switch_is_on(self):