Files
iot-dboard/backend/events.py
T
2026-08-06 11:11:11 +08:00

50 lines
1.7 KiB
Python

"""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)
def broadcast(self):
"""Send heartbeat to all subscribers."""
self.publish("heartbeat", {"ts": __import__("time").time()})