You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When multiple clients (phones, M5 dials, web) are connected, a temperature change from one client takes up to 30s to appear on others. The WebSocket server should broadcast state changes immediately after mutations succeed.
Additionally, the claim_processing / activeClient / processingState code is dead — no client sends claim_processing. It should be removed, simplifying the WebSocket to a pure read-only pub/sub channel.
Architecture Change
graph LR
subgraph "Clients (read-only WS + write via HTTP)"
PhoneA["Phone A"]
PhoneB["Phone B"]
DialL["M5 Dial Left"]
DialR["M5 Dial Right"]
Web["Web App"]
end
subgraph "sleepypod-core"
API["tRPC API :3000\n(all writes)"]
WS["WebSocket :3001\n(read-only pub/sub)"]
EB["broadcastFrame\n(event bus)"]
DAC["DAC Socket\n(hardware)"]
MON["dacMonitor\n(2s poll)"]
end
PhoneA -- "POST /device/temperature" --> API
PhoneB -- "POST /device/power" --> API
DialL -- "POST /device/temperature" --> API
API -- "after success" --> EB
MON -- "status:updated" --> EB
EB -- "deviceStatus frame" --> WS
WS -- "subscribe" --> PhoneA & PhoneB & DialL & DialR & Web
MON -- "poll 2s" --> DAC
API -- "SequentialQueue" --> DAC
src/server/routers/device.ts: After setTemperature/setPower/setAlarm/clearAlarm/snoozeAlarm succeed, overlay the mutation onto dacMonitor.getLastStatus() and call broadcastFrame() with a deviceStatus frame
Fire-and-forget, no extra hardware call needed
dacMonitor 2s poll still broadcasts authoritative status as consistency backstop
3. Client cleanup (optional)
src/hooks/useSensorStream.ts: Remove any claim_processing send logic if present
Why This Works
broadcastFrame() already sends to ALL subscribed WS clients
deviceStatus frame type already consumed by web (useDeviceStatus) and iOS (DeviceManager)
Summary
When multiple clients (phones, M5 dials, web) are connected, a temperature change from one client takes up to 30s to appear on others. The WebSocket server should broadcast state changes immediately after mutations succeed.
Additionally, the
claim_processing/activeClient/processingStatecode is dead — no client sendsclaim_processing. It should be removed, simplifying the WebSocket to a pure read-only pub/sub channel.Architecture Change
graph LR subgraph "Clients (read-only WS + write via HTTP)" PhoneA["Phone A"] PhoneB["Phone B"] DialL["M5 Dial Left"] DialR["M5 Dial Right"] Web["Web App"] end subgraph "sleepypod-core" API["tRPC API :3000\n(all writes)"] WS["WebSocket :3001\n(read-only pub/sub)"] EB["broadcastFrame\n(event bus)"] DAC["DAC Socket\n(hardware)"] MON["dacMonitor\n(2s poll)"] end PhoneA -- "POST /device/temperature" --> API PhoneB -- "POST /device/power" --> API DialL -- "POST /device/temperature" --> API API -- "after success" --> EB MON -- "status:updated" --> EB EB -- "deviceStatus frame" --> WS WS -- "subscribe" --> PhoneA & PhoneB & DialL & DialR & Web MON -- "poll 2s" --> DAC API -- "SequentialQueue" --> DACChanges Required
1. Remove claim_processing from WS protocol
2. Add broadcastMutationStatus() to device router
3. Client cleanup (optional)
Why This Works
Follow-up