Browse Source

fix (device): slow down excessive heartbeats

Observed in some logs that data is coming very fast from the device, with multiple
received messages within a second window in the debug log, including some very large
diagnostic messages with one particular device.
This appears to be because we wait only 0.1s each time around the loop which things
are going smoothly, only waiting 5s when there has been an error, to give the device
some time to recover. Although the gap between full polls is set at 30s, we were sending
a heartbeat every time around the loop when it was not due for a full poll, which forces
the device to reply straight away. Actually we only want to send heartbeats often enough
to stop the connection dropping (<30s, so every 10s is often enough), and just listen for
unsolicited data from the device the rest of the time.

Default timeout for receive is 5s, so we should still get a heartbeat in after a maximum
15.1s after this change.
Jason Rumney 1 week ago
parent
commit
50308ed4d2
1 changed files with 10 additions and 4 deletions
  1. 10 4
      custom_components/tuya_local/device.py

+ 10 - 4
custom_components/tuya_local/device.py

@@ -149,6 +149,7 @@ class TuyaLocalDevice(object):
         # its switches.
         self._FAKE_IT_TIMEOUT = 5
         self._CACHE_TIMEOUT = 30
+        self._HEARTBEAT_INTERVAL = 10
         # More attempts are needed in auto mode so we can cycle through all
         # the possibilities a couple of times
         self._AUTO_CONNECTION_ATTEMPTS = len(API_PROTOCOL_VERSIONS) * 2 + 1
@@ -315,6 +316,7 @@ class TuyaLocalDevice(object):
         while self._running:
             error_count = self._api_working_protocol_failures
             force_backoff = False
+            last_heartbeat = self._cached_state.get("updated_at", 0)
             try:
                 await self._api_lock.acquire()
                 last_cache = self._cached_state.get("updated_at", 0)
@@ -355,11 +357,15 @@ class TuyaLocalDevice(object):
                         dps_updated = False
                         full_poll = True
                     self._last_full_poll = now
+                    last_heartbeat = now  # reset heartbeat timer on full poll
                 elif persist:
-                    await self._hass.async_add_executor_job(
-                        self._api.heartbeat,
-                        True,
-                    )
+                    if now - last_heartbeat > self._HEARTBEAT_INTERVAL:
+                        await self._hass.async_add_executor_job(
+                            self._api.heartbeat,
+                            True,
+                        )
+                        last_heartbeat = now
+
                     poll = await self._hass.async_add_executor_job(
                         self._api.receive,
                     )