Sfoglia il codice sorgente

fix(device): move lock scope to inside retry function

This keeps it within the async context and reduces to just that
necessary.
Jason Rumney 6 ore fa
parent
commit
f2b3d490e5
1 ha cambiato i file con 61 aggiunte e 63 eliminazioni
  1. 61 63
      custom_components/tuya_local/device.py

+ 61 - 63
custom_components/tuya_local/device.py

@@ -344,66 +344,65 @@ class TuyaLocalDevice(object):
             error_count = self._api_working_protocol_failures
             force_backoff = False
             try:
-                async with self._api_lock:
-                    last_cache = self._cached_state.get("updated_at", 0)
-                    now = time()
-                    full_poll = False
-                    if (persist == self.should_poll) or (
-                        persist and (self._api.socket is None)
+                last_cache = self._cached_state.get("updated_at", 0)
+                now = time()
+                full_poll = False
+                if (persist == self.should_poll) or (
+                    persist and (self._api.socket is None)
+                ):
+                    # use persistent connections after initial communication
+                    # has been established.  Until then, we need to rotate
+                    # the protocol version, which seems to require a fresh
+                    # connection.
+                    persist = not self.should_poll
+                    _LOGGER.debug(
+                        "%s persistant connection set to %s", self.name, persist
+                    )
+                    self._api.set_socketPersistent(persist)
+                    if self._api.parent:
+                        self._api.parent.set_socketPersistent(persist)
+                    self._last_full_poll = 0  # ensure we start with a full poll
+
+                needs_full_poll = now - self._last_full_poll > self._CACHE_TIMEOUT
+                if now - last_cache > self._CACHE_TIMEOUT or (
+                    persist and needs_full_poll
+                ):
+                    if (
+                        self._force_dps
+                        and not dps_updated
+                        and self._api_protocol_working
                     ):
-                        # use persistent connections after initial communication
-                        # has been established.  Until then, we need to rotate
-                        # the protocol version, which seems to require a fresh
-                        # connection.
-                        persist = not self.should_poll
-                        _LOGGER.debug(
-                            "%s persistant connection set to %s", self.name, persist
+                        poll = await self._retry_on_failed_connection(
+                            lambda: self._api.updatedps(self._force_dps),
+                            f"Failed to update device dps for {self.name}",
                         )
-                        self._api.set_socketPersistent(persist)
-                        if self._api.parent:
-                            self._api.parent.set_socketPersistent(persist)
-                        self._last_full_poll = 0  # ensure we start with a full poll
-
-                    needs_full_poll = now - self._last_full_poll > self._CACHE_TIMEOUT
-                    if now - last_cache > self._CACHE_TIMEOUT or (
-                        persist and needs_full_poll
-                    ):
-                        if (
-                            self._force_dps
-                            and not dps_updated
-                            and self._api_protocol_working
-                        ):
-                            poll = await self._retry_on_failed_connection(
-                                lambda: self._api.updatedps(self._force_dps),
-                                f"Failed to update device dps for {self.name}",
-                            )
-                            dps_updated = True
-                        else:
-                            poll = await self._retry_on_failed_connection(
-                                lambda: self._api.status(),
-                                f"Failed to fetch device status for {self.name}",
-                            )
-                            dps_updated = False
-                            full_poll = True
-                        self._last_full_poll = now
-                        last_heartbeat = now  # reset heartbeat timer on full poll
-                    elif persist:
-                        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,
-                        )
-                        # Ignore Payload error 904, as 3.4 protocol devices seem to return
-                        # this when there is no new data, instead of just returning nothing.
-                        if poll and "Err" in poll and poll["Err"] == "904":
-                            poll = None
+                        dps_updated = True
                     else:
-                        force_backoff = True
+                        poll = await self._retry_on_failed_connection(
+                            lambda: self._api.status(),
+                            f"Failed to fetch device status for {self.name}",
+                        )
+                        dps_updated = False
+                        full_poll = True
+                    self._last_full_poll = now
+                    last_heartbeat = now  # reset heartbeat timer on full poll
+                elif persist:
+                    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,
+                    )
+                    # Ignore Payload error 904, as 3.4 protocol devices seem to return
+                    # this when there is no new data, instead of just returning nothing.
+                    if poll and "Err" in poll and poll["Err"] == "904":
                         poll = None
+                else:
+                    force_backoff = True
+                    poll = None
 
                 if poll:
                     if "Err" in poll:
@@ -562,8 +561,7 @@ class TuyaLocalDevice(object):
         self._last_full_poll = 0
 
     def _refresh_cached_state(self):
-        async with self._api_lock:
-            new_state = self._api.status()
+        new_state = self._api.status()
         if new_state:
             if "Err" not in new_state:
                 self._cached_state = self._cached_state | new_state.get("dps", {})
@@ -659,10 +657,9 @@ class TuyaLocalDevice(object):
         )
 
     def _set_values(self, properties):
-        async with self._api_lock:
-            self._api.set_multiple_values(properties, nowait=True)
-            now = time()
-            self._last_connection = now
+        self._api.set_multiple_values(properties, nowait=True)
+        now = time()
+        self._last_connection = now
         pending_updates = self._get_pending_updates()
         for key in properties.keys():
             pending_updates[key]["updated_at"] = now
@@ -690,7 +687,8 @@ class TuyaLocalDevice(object):
         for i in range(connections):
             try:
                 if not self._hass.is_stopping:
-                    retval = await self._hass.async_add_executor_job(func)
+                    async with self._api_lock:
+                        retval = await self._hass.async_add_executor_job(func)
                     if isinstance(retval, dict) and "Error" in retval:
                         last_err_code = retval.get("Err")
                         last_err_msg = retval.get("Error")