From f28a116ff64faed58b9a97b3aff5d50326856784 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Thu, 18 Dec 2025 18:53:44 +0100 Subject: [PATCH] Another fix for reconnections to signaling server --- .../webserver/remote_access/__init__.py | 73 ++++++++----------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/music_assistant/controllers/webserver/remote_access/__init__.py b/music_assistant/controllers/webserver/remote_access/__init__.py index c4520aea..94015d92 100644 --- a/music_assistant/controllers/webserver/remote_access/__init__.py +++ b/music_assistant/controllers/webserver/remote_access/__init__.py @@ -64,7 +64,6 @@ class RemoteAccessManager: self._certificate: RTCCertificate self._enabled: bool = False self._using_ha_cloud: bool = False - self._starting: bool = False # Prevents concurrent gateway starts self._on_unload_callbacks: list[Callable[[], None]] = [] async def setup(self) -> None: @@ -78,7 +77,7 @@ class RemoteAccessManager: self._register_api_commands() self.mass.subscribe(self._on_providers_updated, EventType.PROVIDERS_UPDATED) if self._enabled: - self._schedule_start() + await self._schedule_start() async def close(self) -> None: """Cleanup on exit.""" @@ -87,8 +86,13 @@ class RemoteAccessManager: for unload_cb in self._on_unload_callbacks: unload_cb() - def _schedule_start(self) -> None: - """Schedule a debounced gateway start.""" + async def _schedule_start(self) -> None: + """Schedule a debounced gateway start, cancelling any existing connection first.""" + # Cancel any pending timer + self.mass.cancel_timer(TASK_ID_START_GATEWAY) + # Stop any existing gateway + await self.stop() + # Schedule new start self.logger.debug("Scheduling remote access gateway start in %s seconds", STARTUP_DELAY) self.mass.call_later( STARTUP_DELAY, @@ -98,42 +102,35 @@ class RemoteAccessManager: async def _start_gateway(self) -> None: """Start the remote access gateway (internal implementation).""" - if self.is_running or self._starting: - self.logger.debug("Gateway already running or starting, skipping start") - return if not self._enabled: self.logger.debug("Remote access disabled, skipping start") return - self._starting = True - try: - base_url = self.mass.webserver.base_url - local_ws_url = base_url.replace("http", "ws") - if not local_ws_url.endswith("/"): - local_ws_url += "/" - local_ws_url += "ws" + base_url = self.mass.webserver.base_url + local_ws_url = base_url.replace("http", "ws") + if not local_ws_url.endswith("/"): + local_ws_url += "/" + local_ws_url += "ws" - ha_cloud_available, ice_servers = await self._get_ha_cloud_status() - self._using_ha_cloud = bool(ha_cloud_available and ice_servers) - - mode = "optimized" if self._using_ha_cloud else "basic" - self.logger.info("Starting remote access in %s mode", mode) - - self.gateway = WebRTCGateway( - http_session=self.mass.http_session, - remote_id=self._remote_id, - certificate=self._certificate, - signaling_url=SIGNALING_SERVER_URL, - local_ws_url=local_ws_url, - ice_servers=ice_servers, - # Pass callback to get fresh ICE servers for each client connection - # This ensures TURN credentials are always valid - ice_servers_callback=self.get_ice_servers if ha_cloud_available else None, - ) + ha_cloud_available, ice_servers = await self._get_ha_cloud_status() + self._using_ha_cloud = bool(ha_cloud_available and ice_servers) + + mode = "optimized" if self._using_ha_cloud else "basic" + self.logger.info("Starting remote access in %s mode", mode) + + self.gateway = WebRTCGateway( + http_session=self.mass.http_session, + remote_id=self._remote_id, + certificate=self._certificate, + signaling_url=SIGNALING_SERVER_URL, + local_ws_url=local_ws_url, + ice_servers=ice_servers, + # Pass callback to get fresh ICE servers for each client connection + # This ensures TURN credentials are always valid + ice_servers_callback=self.get_ice_servers if ha_cloud_available else None, + ) - await self.gateway.start() - finally: - self._starting = False + await self.gateway.start() async def stop(self) -> None: """Stop the remote access gateway.""" @@ -149,19 +146,13 @@ class RemoteAccessManager: if not self._enabled: return - # If not running yet and not currently starting, schedule start (debounced) - if not self.is_running and not self._starting: - self._schedule_start() - return - # Check if HA Cloud status changed ha_cloud_available, ice_servers = await self._get_ha_cloud_status() new_using_ha_cloud = bool(ha_cloud_available and ice_servers) if new_using_ha_cloud != self._using_ha_cloud: self.logger.info("HA Cloud status changed, restarting remote access") - await self.stop() - self._schedule_start() + await self._schedule_start() async def _get_ha_cloud_status(self) -> tuple[bool, list[dict[str, str]] | None]: """Get Home Assistant Cloud status and ICE servers. -- 2.34.1