From a87795e0ae5a83b7dea775dc6517c32e8882adc5 Mon Sep 17 00:00:00 2001 From: SPLBendrix <12345678+SPLBendrix@users.noreply.github.com> Date: Mon, 24 Aug 2026 17:19:50 -0400 Subject: [PATCH] Fix: retry entity subscription that arrives before the entity exists On Remote restart, remote-core sends 'subscribe_events' before 'connect' over the websocket. _subscribe_events() checks _available_entities immediately, but for hub-mode integrations the entity is only created once the device driver's own connect() completes a moment later. The failed lookup just logs a warning and gives up permanently - the entity_id is never re-checked once it becomes available, so it never enters _configured_entities and every subsequent command 404s with 'no configured entity found' for the rest of that session. Fix: when an entity isn't available yet, spawn a short retry task that polls _available_entities for up to ~10s and configures the entity as soon as it appears, instead of dropping the subscription on the first miss. --- ucapi/api.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/ucapi/api.py b/ucapi/api.py index d7f9b8d..a003bf2 100644 --- a/ucapi/api.py +++ b/ucapi/api.py @@ -1031,6 +1031,34 @@ async def set_device_state(self, state: uc.DeviceStates) -> None: uc.EventCategory.DEVICE, ) + async def _retry_subscribe_entity( + self, entity_id: str, attempts: int = 20, delay: float = 0.5 + ) -> None: + """ + PATCHED: retry configuring an entity that wasn't available yet + at subscribe time (e.g. because subscribe_events arrived before + the device's connect() had finished creating its entities). + + Polls _available_entities briefly and configures the entity as + soon as it appears, instead of silently dropping the + subscription forever on a single early miss. + """ + for _ in range(attempts): + await asyncio.sleep(delay) + entity = self._available_entities.get(entity_id) + if entity is not None: + self._configured_entities.add(entity) + _LOG.info( + "Entity %s became available - configured on retry " + "(post-restart fix)", + entity_id, + ) + return + _LOG.warning( + "Entity %s still not available after retrying - giving up", + entity_id, + ) + async def _subscribe_events( self, websocket: Any, msg_data: dict[str, Any] | None ) -> None: @@ -1043,9 +1071,11 @@ async def _subscribe_events( self._configured_entities.add(entity) else: _LOG.warning( - "WARN: cannot subscribe entity %s: entity is not available", + "WARN: cannot subscribe entity %s: entity is not available " + "- will retry for a few seconds (post-restart fix)", entity_id, ) + self._loop.create_task(self._retry_subscribe_entity(entity_id)) self._events.emit( uc.Events.SUBSCRIBE_ENTITIES,