From 50de7f7ba14c55482d0e1f1e3ccfb27cd72c403c Mon Sep 17 00:00:00 2001 From: Hebezo Date: Mon, 11 May 2026 18:14:30 +0200 Subject: [PATCH 1/2] fix: prevent HA 2026.5 crash caused by double-reload and executor exhaustion - Remove add_update_listener + async_reload pattern which caused a race condition double-reload in HA 2026.5, exhausting executor threads until the HA watchdog fired and restarted the process - Cap SIP register() socket timeout at 10s (was up to 30s for TCP) so executor threads are freed faster if the SIP server is slow - Call _end_call() in _health_monitor_loop before reconnecting Siedle SIP to stop a running RTP bridge when the connection drops mid-call Co-Authored-By: Claude Sonnet 4.6 --- custom_components/siedle/__init__.py | 9 --------- custom_components/siedle/sip_manager.py | 14 +++++++++++++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/custom_components/siedle/__init__.py b/custom_components/siedle/__init__.py index 7680e6e..81c9d5c 100644 --- a/custom_components/siedle/__init__.py +++ b/custom_components/siedle/__init__.py @@ -417,18 +417,9 @@ async def _start_fcm_handler(): # Register services await async_setup_services(hass, siedle) - # Reload integration when options change (e.g. external SIP enabled/disabled) - entry.async_on_unload(entry.add_update_listener(_async_reload_on_options_change)) - return True -async def _async_reload_on_options_change(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Reload the config entry when options are updated via the UI.""" - _LOGGER.info("Options changed, reloading Siedle integration...") - await hass.config_entries.async_reload(entry.entry_id) - - async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/custom_components/siedle/sip_manager.py b/custom_components/siedle/sip_manager.py index 1dd1352..3f31bcb 100644 --- a/custom_components/siedle/sip_manager.py +++ b/custom_components/siedle/sip_manager.py @@ -643,7 +643,13 @@ def register(self) -> bool: if not self._connected: if not self.connect(): return False - + + # Cap recv timeout at 10s so executor threads don't block longer than needed + try: + self._socket.settimeout(10) + except Exception: + pass + try: # Send initial REGISTER _LOGGER.info(f"{self.name}: Sending initial REGISTER to {self.config.host}:{self.config.port} " @@ -2242,6 +2248,12 @@ def _health_monitor_loop(self): # Check Siedle connection if self._siedle_conn and self._siedle_conn.connection_lost: _LOGGER.warning("Siedle SIP connection lost — attempting reconnect...") + # Clean up any active call so RTP bridge doesn't keep running + if self._siedle_call is not None: + try: + self._end_call() + except Exception as e: + _LOGGER.warning(f"Error ending call on connection loss: {e}") for attempt in range(3): if not self._running: break From 1dc825ca0259a3d0cfa4624399e307dd8bd10efd Mon Sep 17 00:00:00 2001 From: Hebezo Date: Mon, 11 May 2026 18:16:17 +0200 Subject: [PATCH 2/2] fix: restore options reload with double-reload guard Re-add add_update_listener + async_reload, but protect it with a module-level set that blocks a second concurrent reload for the same entry. In HA 2026.5 the OptionsFlow may trigger an additional reload after async_create_entry; the guard drops that duplicate instead of racing two simultaneous setup/unload cycles. Co-Authored-By: Claude Sonnet 4.6 --- custom_components/siedle/__init__.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/custom_components/siedle/__init__.py b/custom_components/siedle/__init__.py index 81c9d5c..19efbfc 100644 --- a/custom_components/siedle/__init__.py +++ b/custom_components/siedle/__init__.py @@ -83,6 +83,8 @@ PLATFORMS = [Platform.SENSOR, Platform.BINARY_SENSOR, Platform.BUTTON, Platform.CAMERA] +_reloading_entries: set = set() + # This integration is config entry only CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) @@ -417,9 +419,30 @@ async def _start_fcm_handler(): # Register services await async_setup_services(hass, siedle) + # Reload integration when options change (e.g. SIP/FCM settings) + entry.async_on_unload(entry.add_update_listener(_async_reload_on_options_change)) + return True +async def _async_reload_on_options_change(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Reload the config entry when options are updated via the UI. + + Guard against double-reload: HA 2026.5 may trigger an additional reload after + OptionsFlow completes. If a reload for this entry is already in progress we skip + the second trigger instead of racing. + """ + if entry.entry_id in _reloading_entries: + _LOGGER.debug("Siedle reload already in progress for %s, skipping duplicate", entry.entry_id) + return + _reloading_entries.add(entry.entry_id) + try: + _LOGGER.info("Options changed, reloading Siedle integration...") + await hass.config_entries.async_reload(entry.entry_id) + finally: + _reloading_entries.discard(entry.entry_id) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)