Przeglądaj źródła

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

Jason Rumney 4 lat temu
rodzic
commit
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
 so if any overlap exists in the rules, it won't always be predictable which
 icon will be displayed.
 icon will be displayed.
 
 
-### `value-redirect`
+### `value_redirect`
 
 
 //Optional.//
 //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.
 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
 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
         - constraint: preset_mode
           conditions:
           conditions:
             - dps_val: "ECO"
             - dps_val: "ECO"
-              value-redirect: eco_temperature
+              value_redirect: eco_temperature
               range:
               range:
                 min: 5
                 min: 5
                 max: 21
                 max: 21
@@ -89,7 +89,20 @@ primary_entity:
         - dps_val: "auto"
         - dps_val: "auto"
           value: "Auto"
           value: "Auto"
         - dps_val: "user"
         - 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
       name: swing_mode
     - id: 106
     - id: 106
       type: integer
       type: integer

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

@@ -351,7 +351,7 @@ class TuyaDpsConfig:
             scale = mapping.get("scale", 1)
             scale = mapping.get("scale", 1)
             if not isinstance(scale, (int, float)):
             if not isinstance(scale, (int, float)):
                 scale = 1
                 scale = 1
-            redirect = mapping.get("value-redirect")
+            redirect = mapping.get("value_redirect")
             replaced = "value" in mapping
             replaced = "value" in mapping
             result = mapping.get("value", result)
             result = mapping.get("value", result)
             cond = self._active_condition(mapping, device)
             cond = self._active_condition(mapping, device)
@@ -361,7 +361,7 @@ class TuyaDpsConfig:
                 replaced = replaced or "value" in cond
                 replaced = replaced or "value" in cond
                 result = cond.get("value", result)
                 result = cond.get("value", result)
                 scale = cond.get("scale", scale)
                 scale = cond.get("scale", scale)
-                redirect = cond.get("value-redirect", redirect)
+                redirect = cond.get("value_redirect", redirect)
 
 
                 for m in cond.get("mapping", {}):
                 for m in cond.get("mapping", {}):
                     if str(m.get("dps_val")) == str(result):
                     if str(m.get("dps_val")) == str(result):
@@ -400,16 +400,17 @@ class TuyaDpsConfig:
                     return m
                     return m
         return default
         return default
 
 
-    def _active_condition(self, mapping, device):
+    def _active_condition(self, mapping, device, value=None):
         constraint = mapping.get("constraint")
         constraint = mapping.get("constraint")
         conditions = mapping.get("conditions")
         conditions = mapping.get("conditions")
         if constraint and conditions:
         if constraint and conditions:
             c_dps = self._entity.find_dps(constraint)
             c_dps = self._entity.find_dps(constraint)
             c_val = None if c_dps is None else device.get_property(c_dps.id)
             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
         return None
 
 
     def get_values_to_set(self, device, value):
     def get_values_to_set(self, device, value):
@@ -420,7 +421,7 @@ class TuyaDpsConfig:
         if mapping:
         if mapping:
             replaced = False
             replaced = False
             scale = mapping.get("scale", 1)
             scale = mapping.get("scale", 1)
-            redirect = mapping.get("value-redirect")
+            redirect = mapping.get("value_redirect")
             if not isinstance(scale, (int, float)):
             if not isinstance(scale, (int, float)):
                 scale = 1
                 scale = 1
             step = mapping.get("step")
             step = mapping.get("step")
@@ -430,11 +431,16 @@ class TuyaDpsConfig:
                 result = mapping["dps_val"]
                 result = mapping["dps_val"]
                 replaced = True
                 replaced = True
             # Conditions may have side effect of setting another value.
             # 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:
                 if cond.get("value") == value:
                 if cond.get("value") == value:
                     c_dps = self._entity.find_dps(mapping["constraint"])
                     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
                 # Allow simple conditional mapping overrides
                 for m in cond.get("mapping", {}):
                 for m in cond.get("mapping", {}):
                     if m.get("value") == value:
                     if m.get("value") == value:
@@ -442,7 +448,7 @@ class TuyaDpsConfig:
 
 
                 scale = cond.get("scale", scale)
                 scale = cond.get("scale", scale)
                 step = cond.get("step", step)
                 step = cond.get("step", step)
-                redirect = cond.get("value-redirect", redirect)
+                redirect = cond.get("value_redirect", redirect)
 
 
             if redirect:
             if redirect:
                 _LOGGER.debug(f"Redirecting {self.name} to {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.dps[POWERLEVEL_DPS] = "3"
         self.assertEqual(self.subject.swing_mode, "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):
     def test_non_user_swing_mode(self):
         self.dps[SWING_DPS] = "stop"
         self.dps[SWING_DPS] = "stop"
         self.assertEqual(self.subject.swing_mode, "Stop")
         self.assertEqual(self.subject.swing_mode, "Stop")
@@ -271,14 +268,13 @@ class TestGoldairHeater(TuyaDeviceTestCase):
         self.dps[SWING_DPS] = None
         self.dps[SWING_DPS] = None
         self.assertIs(self.subject.swing_mode, None)
         self.assertIs(self.subject.swing_mode, None)
 
 
-    @skip("Conditional redirection not supported yet")
     def test_swing_modes(self):
     def test_swing_modes(self):
         self.assertCountEqual(
         self.assertCountEqual(
             self.subject.swing_modes,
             self.subject.swing_modes,
             ["Stop", "1", "2", "3", "4", "5", "Auto"],
             ["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 def test_set_power_level_to_stop(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
             self.subject._device,
             self.subject._device,
@@ -293,15 +289,14 @@ class TestGoldairHeater(TuyaDeviceTestCase):
         ):
         ):
             await self.subject.async_set_swing_mode("Auto")
             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 def test_set_power_level_to_numeric_value(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
             self.subject._device,
             self.subject._device,
-            {POWERLEVEL_DPS: "3"},
+            {SWING_DPS: "user", POWERLEVEL_DPS: "3"},
         ):
         ):
             await self.subject.async_set_swing_mode("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):
     async def test_set_power_level_to_invalid_value_raises_error(self):
         with self.assertRaisesRegex(ValueError, "Invalid power level: unknown"):
         with self.assertRaisesRegex(ValueError, "Invalid power level: unknown"):
             await self.subject.async_set_swing_mode("unknown")
             await self.subject.async_set_swing_mode("unknown")