- Track Wi-Fi TCP clients, liveness pings, disconnect broadcast, bind errors via gather\n- Device list/get include connected; POST identify with __identify preset\n- Presets push/send delivery helpers; bump led-driver hello type Made-with: Cursor
53 lines
1.5 KiB
Python
53 lines
1.5 KiB
Python
"""Push Wi-Fi TCP connect/disconnect updates to browser WebSocket clients."""
|
|
|
|
import json
|
|
import threading
|
|
from typing import Any, Set
|
|
|
|
# Threading lock: safe across asyncio tasks and avoids binding asyncio.Lock to the wrong loop.
|
|
_clients_lock = threading.Lock()
|
|
_clients: Set[Any] = set()
|
|
|
|
|
|
async def register_device_status_ws(ws: Any) -> None:
|
|
with _clients_lock:
|
|
_clients.add(ws)
|
|
|
|
|
|
async def unregister_device_status_ws(ws: Any) -> None:
|
|
with _clients_lock:
|
|
_clients.discard(ws)
|
|
|
|
|
|
async def broadcast_device_tcp_status(ip: str, connected: bool) -> None:
|
|
from models.tcp_clients import normalize_tcp_peer_ip
|
|
|
|
ip = normalize_tcp_peer_ip(ip)
|
|
if not ip:
|
|
return
|
|
msg = json.dumps({"type": "device_tcp", "ip": ip, "connected": bool(connected)})
|
|
with _clients_lock:
|
|
targets = list(_clients)
|
|
dead = []
|
|
for ws in targets:
|
|
try:
|
|
await ws.send(msg)
|
|
except Exception as exc:
|
|
dead.append(ws)
|
|
print(f"[device_status_broadcaster] ws.send failed: {exc!r}")
|
|
if dead:
|
|
with _clients_lock:
|
|
for ws in dead:
|
|
_clients.discard(ws)
|
|
|
|
|
|
async def broadcast_device_tcp_snapshot_to(ws: Any) -> None:
|
|
from models import tcp_clients as tcp
|
|
|
|
ips = tcp.list_connected_ips()
|
|
msg = json.dumps({"type": "device_tcp_snapshot", "connected_ips": ips})
|
|
try:
|
|
await ws.send(msg)
|
|
except Exception as exc:
|
|
print(f"[device_status_broadcaster] snapshot send failed: {exc!r}")
|