Parcourir la source

Clear pending updates when received back from the device

- When a match for a sent pending update is received from the device,
  clear it immediately to prevent shadowing a later update.
- When clearing pending updates due to timeout, don't clear unsent ones

Issue #1001
Jason Rumney il y a 2 ans
Parent
commit
42e70826e1
2 fichiers modifiés avec 30 ajouts et 1 suppressions
  1. 9 1
      custom_components/tuya_local/device.py
  2. 21 0
      tests/test_device.py

+ 9 - 1
custom_components/tuya_local/device.py

@@ -207,6 +207,8 @@ class TuyaLocalDevice(object):
                     full_poll = poll.pop("full_poll", False)
                     self._cached_state = self._cached_state | poll
                     self._cached_state["updated_at"] = time()
+                    self._remove_properties_from_pending_updates(poll)
+
                     for entity in self._children:
                         # clear non-persistant dps that were not in a full poll
                         if full_poll:
@@ -451,6 +453,12 @@ class TuyaLocalDevice(object):
             log_json(pending_updates),
         )
 
+    def _remove_properties_from_pending_updates(self, data):
+        self._pending_updates = {
+            key: value for key, value in self._pending_updates.items()
+            if key not in data or not value["sent"] or data[key] != value["value"]
+        }
+
     async def _debounce_sending_updates(self):
         now = time()
         since = now - self._last_connection
@@ -560,7 +568,7 @@ class TuyaLocalDevice(object):
         self._pending_updates = {
             key: value
             for key, value in self._pending_updates.items()
-            if now - value.get("updated_at", 0) < self._FAKE_IT_TIMEOUT
+            if not value["sent"] or now - value.get("updated_at", 0) < self._FAKE_IT_TIMEOUT
         }
         return self._pending_updates
 

+ 21 - 0
tests/test_device.py

@@ -360,6 +360,27 @@ class TestDevice(IsolatedAsyncioTestCase):
         self.subject._lock.acquire.assert_called_once()
         self.subject._lock.release.assert_called_once()
 
+    def test_pending_updates_cleared_on_receipt(self):
+        # Set up the preconditions
+        now = time()
+        self.subject._pending_updates = {
+            "1": {"value": True, "updated_at": now, "sent": True},
+            "2": {"value": True, "updated_at": now, "sent": False},  # unsent
+            "3": {"value": True, "updated_at": now, "sent": True},  # unmatched
+            "4": {"value": True, "updated_at": now, "sent": True},  # not received
+        }
+        self.subject._remove_properties_from_pending_updates(
+            {"1": True, "2": True, "3": False}
+        )
+        self.assertDictEqual(
+            self.subject._pending_updates,
+            {
+                "2": {"value": True, "updated_at": now, "sent": False},
+                "3": {"value": True, "updated_at": now, "sent": True},
+                "4": {"value": True, "updated_at": now, "sent": True},
+            },
+        )
+
     def test_actually_start(self):
         # Set up the preconditions
         self.subject.receive_loop = Mock()