Add entity CRUD events over WebSocket with auto-refresh
Broadcast entity_changed and device_health_changed events via the event bus so the frontend can auto-refresh cards without polling. Adds exponential backoff on WS reconnect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -393,7 +393,7 @@ class AutomationEngine:
|
||||
|
||||
def _fire_event(self, automation_id: str, action: str) -> None:
|
||||
try:
|
||||
self._manager._fire_event({
|
||||
self._manager.fire_event({
|
||||
"type": "automation_state_changed",
|
||||
"automation_id": automation_id,
|
||||
"action": action,
|
||||
|
||||
@@ -158,7 +158,7 @@ class ProcessorManager:
|
||||
device_store=self._device_store,
|
||||
color_strip_stream_manager=self._color_strip_stream_manager,
|
||||
value_stream_manager=self._value_stream_manager,
|
||||
fire_event=self._fire_event,
|
||||
fire_event=self.fire_event,
|
||||
get_device_info=self._get_device_info,
|
||||
)
|
||||
|
||||
@@ -203,8 +203,8 @@ class ProcessorManager:
|
||||
if queue in self._event_queues:
|
||||
self._event_queues.remove(queue)
|
||||
|
||||
def _fire_event(self, event: dict) -> None:
|
||||
"""Push event to all subscribers (non-blocking)."""
|
||||
def fire_event(self, event: dict) -> None:
|
||||
"""Push event to all subscribers (non-blocking). Public API for route handlers."""
|
||||
for q in self._event_queues:
|
||||
try:
|
||||
q.put_nowait(event)
|
||||
@@ -854,7 +854,7 @@ class ProcessorManager:
|
||||
f"[AUTO-RESTART] Target {target_id} crashed {rs.attempts} times "
|
||||
f"in {now - rs.first_crash_time:.0f}s — giving up"
|
||||
)
|
||||
self._fire_event({
|
||||
self.fire_event({
|
||||
"type": "state_change",
|
||||
"target_id": target_id,
|
||||
"processing": False,
|
||||
@@ -872,7 +872,7 @@ class ProcessorManager:
|
||||
f"{_RESTART_MAX_ATTEMPTS}), restarting in {backoff:.1f}s"
|
||||
)
|
||||
|
||||
self._fire_event({
|
||||
self.fire_event({
|
||||
"type": "state_change",
|
||||
"target_id": target_id,
|
||||
"processing": False,
|
||||
@@ -916,7 +916,7 @@ class ProcessorManager:
|
||||
await self.start_processing(target_id)
|
||||
except Exception as e:
|
||||
logger.error(f"[AUTO-RESTART] Failed to restart {target_id}: {e}")
|
||||
self._fire_event({
|
||||
self.fire_event({
|
||||
"type": "state_change",
|
||||
"target_id": target_id,
|
||||
"processing": False,
|
||||
@@ -1050,11 +1050,21 @@ class ProcessorManager:
|
||||
state = self._devices.get(device_id)
|
||||
if not state:
|
||||
return
|
||||
prev_online = state.health.online
|
||||
client = await self._get_http_client()
|
||||
state.health = await check_device_health(
|
||||
state.device_type, state.device_url, client, state.health,
|
||||
)
|
||||
|
||||
# Fire event when online status changes
|
||||
if state.health.online != prev_online:
|
||||
self.fire_event({
|
||||
"type": "device_health_changed",
|
||||
"device_id": device_id,
|
||||
"online": state.health.online,
|
||||
"latency_ms": state.health.latency_ms,
|
||||
})
|
||||
|
||||
# Auto-sync LED count
|
||||
reported = state.health.device_led_count
|
||||
if reported and reported != state.led_count and self._device_store:
|
||||
|
||||
Reference in New Issue
Block a user