"""Server-Sent Event bus for pushing updates to displays. Events are scoped by user_id so that each user's display only receives updates about their own data (alarms, calendar, pomodoro, ...). """ import json import queue import threading class EventBus: """Simple pub/sub event system with SSE support. publish(event_type, data, user_id=None): when user_id is given, only subscribers registered for that user receive the event. """ def __init__(self): self._subscribers: list[tuple[int | None, queue.Queue]] = [] self._lock = threading.Lock() def subscribe(self, user_id: int | None = None) -> queue.Queue: """Subscribe to events. user_id=None receives ALL events (broadcast).""" q: queue.Queue = queue.Queue() with self._lock: self._subscribers.append((user_id, q)) return q def unsubscribe(self, q: queue.Queue): with self._lock: self._subscribers = [(uid, sq) for uid, sq in self._subscribers if sq is not q] def publish(self, event_type: str, data: dict, user_id: int | None = None): msg = json.dumps({"type": event_type, "data": data}) with self._lock: dead = [] for sub_uid, q in self._subscribers: if sub_uid is not None and user_id is not None and sub_uid != user_id: continue try: q.put_nowait(msg) except queue.Full: dead.append((sub_uid, q)) for item in dead: self._subscribers.remove(item)