Przeglądaj źródła

M027 curtain: make some attributes optional

Some attributes that are defined in the iot.tuya.com docs do not seem to be returned (maybe depending on state).  Also there are a couple more unknown attributes that are present.
- mark the missing attributes as optional
- add the extra attributes so they can be monitored to figure out what they mean
- modify the cover component to handle missing current_position attributes when they are defined as optional in the config.

Issue #69
Jason Rumney 3 lat temu
rodzic
commit
dba3c519c2

+ 12 - 0
custom_components/tuya_local/devices/m027_curtain.yaml

@@ -24,6 +24,7 @@ primary_entity:
       name: current_position
       type: integer
       unit: "%"
+      optional: true
     - id: 7
       name: action
       type: string
@@ -33,13 +34,23 @@ primary_entity:
           conditions:
             - dps_val: 100
               value: opened
+            - dps_val: null
+              value: opened
             - value: opening
         - dps_val: closing
           constraint: current_position
           conditions:
             - dps_val: 0
               value: closed
+            - dps_val: null
+              value: closed
             - value: closing
+    - id: 12
+      name: unknown_12
+      type: integer
+    - id: 101
+      name: unknown_101
+      type: boolean
 secondary_entities:
   - entity: select
     name: Mode
@@ -62,6 +73,7 @@ secondary_entities:
       - id: 9
         name: sensor
         type: integer
+        optional: true
         unit: s
   - entity: sensor
     name: Travel Time

+ 3 - 1
custom_components/tuya_local/generic/cover.py

@@ -67,7 +67,9 @@ class TuyaLocalCover(TuyaLocalEntity, CoverEntity):
     def current_cover_position(self):
         """Return current position of cover."""
         if self._currentpos_dps:
-            return self._currentpos_dps.get_value(self._device)
+            pos = self._currentpos_dps.get_value(self._device)
+            if pos is not None:
+                return pos
         if self._position_dps:
             return self._position_dps.get_value(self._device)
 

+ 9 - 0
custom_components/tuya_local/helpers/device_config.py

@@ -583,6 +583,15 @@ class TuyaDpsConfig:
             for cond in conditions:
                 if c_val is not None and c_val == cond.get("dps_val"):
                     c_match = cond
+                # Case where matching None, need extra checks to ensure we
+                # are not just defaulting and it is really a match
+                elif (
+                    c_val is None
+                    and c_dps is not None
+                    and "dps_val" in cond
+                    and cond.get("dps_val") is None
+                ):
+                    c_match = cond
                 # when changing, another condition may become active
                 # return that if it exists over a current condition
                 if value is not None and value == cond.get("value"):

+ 1 - 1
custom_components/tuya_local/manifest.json

@@ -2,7 +2,7 @@
     "domain": "tuya_local",
     "iot_class": "local_polling",
     "name": "Tuya Local",
-    "version": "0.18.0",
+    "version": "0.18.1",
     "documentation": "https://github.com/make-all/tuya-local",
     "issue_tracker": "https://github.com/make-all/tuya-local/issues",
     "dependencies": [],

+ 5 - 5
tests/const.py

@@ -1233,13 +1233,13 @@ QS_C01_CURTAIN_PAYLOAD = {
 }
 
 M027_CURTAIN_PAYLOAD = {
-    "1": "stop",
+    "1": "close",
     "2": 0,
-    "3": 0,
     "4": "morning",
-    "7": "opening",
-    "9": 0,
-    "10": 20000,
+    "7": "closing",
+    "10": 0,
+    "12": 0,
+    "101": False,
 }
 
 JIAHONG_ET72W_PAYLOAD = {

+ 11 - 0
tests/devices/test_m027_curtain.py

@@ -18,6 +18,8 @@ MODE_DPS = "4"
 ACTION_DPS = "7"
 TIMER_DPS = "9"
 TRAVELTIME_DPS = "10"
+UNKNOWN12_DPS = "12"
+UNKNOWN101_DPS = "101"
 
 
 class TestM027Curtains(MultiSensorTests, BasicSelectTests, TuyaDeviceTestCase):
@@ -82,6 +84,9 @@ class TestM027Curtains(MultiSensorTests, BasicSelectTests, TuyaDeviceTestCase):
         self.assertTrue(self.subject.is_opening)
         self.dps[ACTION_DPS] = "closing"
         self.assertFalse(self.subject.is_opening)
+        self.dps[ACTION_DPS] = "opening"
+        self.dps[CURRENTPOS_DPS] = None
+        self.assertFalse(self.subject.is_opening)
 
     def test_is_closing(self):
         self.dps[ACTION_DPS] = "closing"
@@ -91,12 +96,18 @@ class TestM027Curtains(MultiSensorTests, BasicSelectTests, TuyaDeviceTestCase):
         self.assertTrue(self.subject.is_closing)
         self.dps[ACTION_DPS] = "opening"
         self.assertFalse(self.subject.is_closing)
+        self.dps[ACTION_DPS] = "closing"
+        self.dps[CURRENTPOS_DPS] = None
+        self.assertFalse(self.subject.is_closing)
 
     def test_is_closed(self):
         self.dps[CURRENTPOS_DPS] = 100
         self.assertFalse(self.subject.is_closed)
         self.dps[CURRENTPOS_DPS] = 0
         self.assertTrue(self.subject.is_closed)
+        self.dps[ACTION_DPS] = "closing"
+        self.dps[CURRENTPOS_DPS] = None
+        self.assertTrue(self.subject.is_closed)
 
     async def test_open_cover(self):
         async with assert_device_properties_set(