fix(routes): rebind sticky health route on reload; defensive resolve
After a config reload a new HealthCoordinator is created, but the sticky /station/health route kept calling the previous (stale) instance. Add Routes.rebind_handler() and call it from async_setup_entry's reload branch so the endpoint always serves the current coordinator. Also make _resolve_route() tolerant of requests without match_info/route/ resource instead of raising AttributeError. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent
b777296b52
commit
e20c23a3af
|
|
@ -167,6 +167,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: SWSConfigEntry) -> bool:
|
|||
_LOGGER.debug("We have routes registered, will try to switch dispatcher.")
|
||||
routes.switch_route(coordinator.received_data, DEFAULT_URL if not _wslink else WSLINK_URL, enabled=_legacy)
|
||||
routes.set_ecowitt_enabled(_ecowitt_path, coordinator.received_ecowitt_data, _ecowitt_enabled)
|
||||
# Rebind the sticky health route to the new coordinator so /station/health
|
||||
# does not keep serving the previous (stale) HealthCoordinator after a reload.
|
||||
routes.rebind_handler(HEALTH_URL, coordinator_health.health_status)
|
||||
routes.set_ingress_observer(coordinator_health.record_dispatch)
|
||||
coordinator_health.update_routing(routes)
|
||||
_LOGGER.debug("%s", routes.show_enabled())
|
||||
|
|
|
|||
|
|
@ -77,7 +77,12 @@ class Routes:
|
|||
if key in self.routes:
|
||||
return self.routes[key]
|
||||
|
||||
resource = request.match_info.route.resource
|
||||
# Fallback to the aiohttp resource canonical URL (for routes with a path
|
||||
# parameter such as {webhook_id}). Resolve defensively: a request without
|
||||
# match_info/route/resource simply has no canonical match.
|
||||
match_info = getattr(request, "match_info", None)
|
||||
route = getattr(match_info, "route", None)
|
||||
resource = getattr(route, "resource", None)
|
||||
if resource is not None:
|
||||
canonical_key = f"{request.method}:{resource.canonical}"
|
||||
if canonical_key in self.routes:
|
||||
|
|
@ -103,6 +108,24 @@ class Routes:
|
|||
)
|
||||
return
|
||||
|
||||
def rebind_handler(self, url_path: str, handler: Handler) -> None:
|
||||
"""Repoint an always-on sticky route to a new handler after a reload.
|
||||
|
||||
Sticky routes (e.g. health) stay enabled across reloads, but their stored
|
||||
handler is a bound method tied to a specific coordinator instance. When the
|
||||
integration reloads, a new coordinator is created, so the handler must be
|
||||
repointed - otherwise the route keeps calling the old (stale) instance.
|
||||
|
||||
Unlike `set_ecowitt_enabled`, this never changes `enabled`; it only rebinds
|
||||
currently enabled sticky routes for `url_path`.
|
||||
"""
|
||||
|
||||
for route in self.routes.values():
|
||||
if route.url_path == url_path and route.sticky and route.enabled:
|
||||
route.handler = handler
|
||||
_LOGGER.debug("Rebound sticky route handler for %s", url_path)
|
||||
return
|
||||
|
||||
def set_ingress_observer(self, observer: IngressObserver | None) -> None:
|
||||
"""Set a callback notified for every incoming dispatcher request."""
|
||||
self._ingress_observer = observer
|
||||
|
|
|
|||
Loading…
Reference in New Issue