From 3a42a20d3106219e9d813be2869169eac81b5e8a Mon Sep 17 00:00:00 2001 From: xZetsubou Date: Tue, 23 Apr 2024 06:32:27 +0300 Subject: [PATCH] Improve the speed of reloading the entry. --- custom_components/localtuya/__init__.py | 10 ++---- custom_components/localtuya/coordinator.py | 36 ++++++++++------------ custom_components/localtuya/entity.py | 7 ++--- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index c9050b12a..227efa335 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -319,7 +319,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): if no_cloud: _LOGGER.info("Cloud API account not configured.") # wait 1 second to make sure possible migration has finished - await asyncio.sleep(1) + # await asyncio.sleep(1) else: entry.async_create_background_task( hass, tuya_api.async_connect(), "localtuya-cloudAPI" @@ -389,10 +389,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Unload the platforms. await hass.config_entries.async_unload_platforms(entry, platforms) - # Close all connection to the devices. - if disconnect_devices: - await asyncio.wait(disconnect_devices) - hass.data[DOMAIN].pop(entry.entry_id) return True @@ -471,8 +467,8 @@ async def _async_reconnect(now): dev_id = dev._device_config.id if check_if_device_disabled(hass, entry, dev_id): return - if not dev.connected: - asyncio.create_task(dev.async_connect()) + # "async_connect" has to check if the device is already connected, then stop. + asyncio.create_task(dev.async_connect()) # Add unsub callbeack in unsub_listeners object. hass_localtuya.unsub_listeners.append( diff --git a/custom_components/localtuya/coordinator.py b/custom_components/localtuya/coordinator.py index 96fc0065d..ab4a6bb44 100644 --- a/custom_components/localtuya/coordinator.py +++ b/custom_components/localtuya/coordinator.py @@ -68,9 +68,7 @@ def __init__( self.dps_to_request = {} self._is_closing = False self._connect_task: asyncio.Task | None = None - self._disconnect_task: Callable[[], None] | None = None - self._unsub_interval: CALLBACK_TYPE[[], None] = None - self._shutdown_entities_delay: CALLBACK_TYPE[[], None] = None + self._unsub_on_close: list[CALLBACK_TYPE] = [] self._entities = [] self._local_key: str = self._device_config.local_key self._default_reset_dpids: list | None = None @@ -146,6 +144,9 @@ async def async_connect(self, _now=None) -> None: await self._connect_task except (TimeoutError, asyncio.CancelledError): ... + elif self.connected: + # In-case device is connected but somehow the status hasn't been sent. + self._dispatch_status() async def _make_connection(self): """Subscribe localtuya entity events.""" @@ -183,7 +184,7 @@ async def _make_connection(self): break # Succeed break while loop except Exception as ex: # pylint: disable=broad-except await self.abort_connect() - if not retry < self._connect_max_tries and not self.is_sleep: + if retry >= self._connect_max_tries and not self.is_sleep: self.warning(f"Failed to connect to {host}: {str(ex)}") if "key" in str(ex): update_localkey = True @@ -204,7 +205,7 @@ async def _make_connection(self): self.debug("Retrieving initial state") # Usually we use status instead of detect_available_dps, but some device doesn't reports all dps when ask for status. status = await self._interface.status(cid=self._node_id) - if status is None: # and not self.is_subdevice + if status is None: raise Exception("Failed to retrieve status") self._interface.start_heartbeat() @@ -238,13 +239,15 @@ def _new_entity_handler(entity_id): self._dispatch_status() signal = f"localtuya_entity_{self._device_config.id}" - self._disconnect_task = async_dispatcher_connect( - self._hass, signal, _new_entity_handler + self._unsub_on_close.append( + async_dispatcher_connect(self._hass, signal, _new_entity_handler) ) if (scan_inv := int(self._device_config.scan_interval)) > 0: - self._unsub_interval = async_track_time_interval( - self._hass, self._async_refresh, timedelta(seconds=scan_inv) + self._unsub_on_close.append( + async_track_time_interval( + self._hass, self._async_refresh, timedelta(seconds=scan_inv) + ) ) self._connect_task = None @@ -293,8 +296,9 @@ async def check_connection(self): async def close(self): """Close connection and stop re-connect loop.""" self._is_closing = True - if self._shutdown_entities_delay is not None: - self._shutdown_entities_delay() + for callback in self._unsub_on_close: + callback() + if self._connect_task is not None: self._connect_task.cancel() await self._connect_task @@ -302,8 +306,6 @@ async def close(self): if self._interface is not None: await self._interface.close() self._interface = None - if self._disconnect_task: - self._disconnect_task() self.debug(f"Closed connection with {self._device_config.name}", force=True) async def update_local_key(self): @@ -427,7 +429,6 @@ def fire_event(event, data: dict): def _shutdown_entities(self, now=None): """Shutdown device entities""" - self._shutdown_entities_delay = None if self.is_sleep: return if not self.connected: @@ -452,9 +453,6 @@ def disconnected(self): """Device disconnected.""" sleep_time = self._device_config.sleep_time - if self._unsub_interval is not None: - self._unsub_interval() - self._unsub_interval = None self._interface = None if self._sub_devices: @@ -471,8 +469,8 @@ def disconnected(self): async_call_later(self._hass, 1, self.async_connect) if not self._is_closing: - self._shutdown_entities_delay = async_call_later( - self._hass, sleep_time + 3, self._shutdown_entities + self._unsub_on_close.append( + async_call_later(self._hass, sleep_time + 3, self._shutdown_entities) ) diff --git a/custom_components/localtuya/entity.py b/custom_components/localtuya/entity.py index 3b8e71963..def12156a 100644 --- a/custom_components/localtuya/entity.py +++ b/custom_components/localtuya/entity.py @@ -157,9 +157,9 @@ async def async_added_to_hass(self): if stored_data: self.status_restored(stored_data) - def _update_handler(_status): + def _update_handler(new_status: dict | None): """Update entity state when status was updated.""" - status = _status.copy() if _status is not None else {} + status = self._status.clear() if new_status is None else new_status.copy() if status == RESTORE_STATES and stored_data: if stored_data.state not in (STATE_UNAVAILABLE, STATE_UNKNOWN): @@ -167,9 +167,8 @@ def _update_handler(_status): status[self._dp_id] = stored_data.state if self._status != status: - self._status = status.copy() - if status: + self._status.update(status) self.status_updated() # Update HA