Explorar o código

Improvements to conditions to cover some more of the GPPH climate functionality.

Jason Rumney %!s(int64=4) %!d(string=hai) anos
pai
achega
3cb6d52b34

+ 2 - 2
custom_components/tuya_local/devices/README.md

@@ -213,10 +213,10 @@ If you don't specify any priorities, the icons will all get the same priority,
 so if any overlap exists in the rules, it won't always be predictable which
 icon will be displayed.
 
-### `value-redirect`
+### `value_redirect`
 
 //Optional.//
-When `value-redirect` is set, the value of the attribute and any attempt to
+When `value_redirect` is set, the value of the attribute and any attempt to
 set it will be redirected to the named attribute instead of the current one.
 
 An example of how this can be useful is where a Tuya heater has a dps for the

+ 15 - 2
custom_components/tuya_local/devices/goldair_gpph_heater.yaml

@@ -25,7 +25,7 @@ primary_entity:
         - constraint: preset_mode
           conditions:
             - dps_val: "ECO"
-              value-redirect: eco_temperature
+              value_redirect: eco_temperature
               range:
                 min: 5
                 max: 21
@@ -89,7 +89,20 @@ primary_entity:
         - dps_val: "auto"
           value: "Auto"
         - dps_val: "user"
-          value-redirect: power_level
+          constraint: power_level
+          conditions:
+            - dps_val: "1"
+              value: "1"
+            - dps_val: "2"
+              value: "2"
+            - dps_val: "3"
+              value: "3"
+            - dps_val: "4"
+              value: "4"
+            - dps_val: "5"
+              value: "5"
+            - dps_val: "stop"
+              value: "Stop"
       name: swing_mode
     - id: 106
       type: integer

+ 17 - 11
custom_components/tuya_local/helpers/device_config.py

@@ -351,7 +351,7 @@ class TuyaDpsConfig:
             scale = mapping.get("scale", 1)
             if not isinstance(scale, (int, float)):
                 scale = 1
-            redirect = mapping.get("value-redirect")
+            redirect = mapping.get("value_redirect")
             replaced = "value" in mapping
             result = mapping.get("value", result)
             cond = self._active_condition(mapping, device)
@@ -361,7 +361,7 @@ class TuyaDpsConfig:
                 replaced = replaced or "value" in cond
                 result = cond.get("value", result)
                 scale = cond.get("scale", scale)
-                redirect = cond.get("value-redirect", redirect)
+                redirect = cond.get("value_redirect", redirect)
 
                 for m in cond.get("mapping", {}):
                     if str(m.get("dps_val")) == str(result):
@@ -400,16 +400,17 @@ class TuyaDpsConfig:
                     return m
         return default
 
-    def _active_condition(self, mapping, device):
+    def _active_condition(self, mapping, device, value=None):
         constraint = mapping.get("constraint")
         conditions = mapping.get("conditions")
         if constraint and conditions:
             c_dps = self._entity.find_dps(constraint)
             c_val = None if c_dps is None else device.get_property(c_dps.id)
-            if c_val is not None:
-                for cond in conditions:
-                    if c_val == cond.get("dps_val"):
-                        return cond
+            for cond in conditions:
+                if c_val is not None and c_val == cond.get("dps_val"):
+                    return cond
+                if value is not None and value == cond.get("value"):
+                    return cond
         return None
 
     def get_values_to_set(self, device, value):
@@ -420,7 +421,7 @@ class TuyaDpsConfig:
         if mapping:
             replaced = False
             scale = mapping.get("scale", 1)
-            redirect = mapping.get("value-redirect")
+            redirect = mapping.get("value_redirect")
             if not isinstance(scale, (int, float)):
                 scale = 1
             step = mapping.get("step")
@@ -430,11 +431,16 @@ class TuyaDpsConfig:
                 result = mapping["dps_val"]
                 replaced = True
             # Conditions may have side effect of setting another value.
-            cond = self._active_condition(mapping, device)
+            cond = self._active_condition(mapping, device, value)
             if cond:
                 if cond.get("value") == value:
                     c_dps = self._entity.find_dps(mapping["constraint"])
-                    dps_map.update(c_dps.get_values_to_set(device, cond["dps_val"]))
+                    c_val = c_dps._map_from_dps(
+                        cond.get("dps_val", device.get_property(c_dps.id)),
+                        device,
+                    )
+                    dps_map.update(c_dps.get_values_to_set(device, c_val))
+
                 # Allow simple conditional mapping overrides
                 for m in cond.get("mapping", {}):
                     if m.get("value") == value:
@@ -442,7 +448,7 @@ class TuyaDpsConfig:
 
                 scale = cond.get("scale", scale)
                 step = cond.get("step", step)
-                redirect = cond.get("value-redirect", redirect)
+                redirect = cond.get("value_redirect", redirect)
 
             if redirect:
                 _LOGGER.debug(f"Redirecting {self.name} to {redirect}")

+ 3 - 8
tests/devices/test_goldair_gpph_heater.py

@@ -258,9 +258,6 @@ class TestGoldairHeater(TuyaDeviceTestCase):
         self.dps[POWERLEVEL_DPS] = "3"
         self.assertEqual(self.subject.swing_mode, "3")
 
-        self.dps[POWERLEVEL_DPS] = None
-        self.assertIs(self.subject.swing_mode, None)
-
     def test_non_user_swing_mode(self):
         self.dps[SWING_DPS] = "stop"
         self.assertEqual(self.subject.swing_mode, "Stop")
@@ -271,14 +268,13 @@ class TestGoldairHeater(TuyaDeviceTestCase):
         self.dps[SWING_DPS] = None
         self.assertIs(self.subject.swing_mode, None)
 
-    @skip("Conditional redirection not supported yet")
     def test_swing_modes(self):
         self.assertCountEqual(
             self.subject.swing_modes,
             ["Stop", "1", "2", "3", "4", "5", "Auto"],
         )
 
-    @skip("Conditional redirection not supported yet")
+    @skip("Paired settings not supported yet")
     async def test_set_power_level_to_stop(self):
         async with assert_device_properties_set(
             self.subject._device,
@@ -293,15 +289,14 @@ class TestGoldairHeater(TuyaDeviceTestCase):
         ):
             await self.subject.async_set_swing_mode("Auto")
 
-    @skip("Conditional redirection not supported yet")
     async def test_set_power_level_to_numeric_value(self):
         async with assert_device_properties_set(
             self.subject._device,
-            {POWERLEVEL_DPS: "3"},
+            {SWING_DPS: "user", POWERLEVEL_DPS: "3"},
         ):
             await self.subject.async_set_swing_mode("3")
 
-    @skip("Conditional redirection not supported yet")
+    @skip("Restriction to mapped values not supported yet")
     async def test_set_power_level_to_invalid_value_raises_error(self):
         with self.assertRaisesRegex(ValueError, "Invalid power level: unknown"):
             await self.subject.async_set_swing_mode("unknown")