Browse Source

Reduce debounce on first call within the same second.

Issue #85

A debounce timer is used to reduce the load on the Tuya device when multiple commands come in quick succession, which was added upstream to improve reliability.

The one second delay it adds to every unbatched command makes the local control even slower in some cases than using cloud control, so for the majority of cases where a single command is given in isolation, it should be avoided.

- Update tinytuya
Jason Rumney 4 years ago
parent
commit
5cd6750822

+ 13 - 1
custom_components/tuya_local/device.py

@@ -162,6 +162,7 @@ class TuyaLocalDevice(object):
     def _reset_cached_state(self):
         self._cached_state = {"updated_at": 0}
         self._pending_updates = {}
+        self._last_connection = 0
 
     def _refresh_cached_state(self):
         new_state = self._api.status()
@@ -191,11 +192,21 @@ class TuyaLocalDevice(object):
         )
 
     def _debounce_sending_updates(self):
+        now = time()
+        since = now - self._last_connection
+        # set this now to avoid a race condition, it will be updated later
+        # when the data is actally sent
+        self._last_connection = now
+        # Only delay a second if there was recently another command.
+        # Otherwise delay 1ms, to keep things simple by reusing the
+        # same send mechanism.
+        waittime = 1 if since < 1.0 else 0.001
+
         try:
             self._debounce.cancel()
         except AttributeError:
             pass
-        self._debounce = Timer(1, self._send_pending_updates)
+        self._debounce = Timer(waittime, self._send_pending_updates)
         self._debounce.start()
 
     def _send_pending_updates(self):
@@ -216,6 +227,7 @@ class TuyaLocalDevice(object):
             self._api._send_receive(payload)
             self._cached_state["updated_at"] = 0
             now = time()
+            self._last_connection = now
             pending_updates = self._get_pending_updates()
             for key, value in pending_updates.items():
                 pending_updates[key]["updated_at"] = now

+ 2 - 2
custom_components/tuya_local/manifest.json

@@ -2,11 +2,11 @@
     "domain": "tuya_local",
     "iot_class": "local_polling",
     "name": "Tuya Local",
-    "version": "0.13.4",
+    "version": "0.14.0",
     "documentation": "https://github.com/make-all/tuya-local",
     "issue_tracker": "https://github.com/make-all/tuya-local/issues",
     "dependencies": [],
     "codeowners": ["@make-all"],
-    "requirements": ["pycryptodome==3.11.0","tinytuya==1.2.10"],
+    "requirements": ["pycryptodome==3.11.0","tinytuya==1.2.11"],
     "config_flow": true
 }

+ 1 - 1
requirements-dev.txt

@@ -6,4 +6,4 @@ pytest
 pytest-asyncio
 pytest-cov
 pycryptodome==3.11.0
-tinytuya==1.2.10
+tinytuya==1.2.11

+ 1 - 1
requirements.txt

@@ -1,2 +1,2 @@
 pycryptodome~=3.11.0
-tinytuya~=1.2.10
+tinytuya~=1.2.11

+ 1 - 1
tests/test_device.py

@@ -250,7 +250,7 @@ class TestDevice(IsolatedAsyncioTestCase):
     def test_debounces_multiple_set_calls_into_one_api_call(self):
         with patch("custom_components.tuya_local.device.Timer") as mock:
             self.subject.set_property("1", True)
-            mock.assert_called_once_with(1, self.subject._send_pending_updates)
+            mock.assert_called_once_with(0.001, self.subject._send_pending_updates)
 
             debounce = self.subject._debounce
             mock.reset_mock()