# Cross-Device Alarm Dismissal ## Overview When an alarm is dismissed on one device, it automatically dismisses on all other connected devices. This prevents the same alarm from ringing on multiple devices. ## How It Works ### Architecture ``` Device A (dismisses alarm) ↓ AlarmManager detects dismissal ↓ Calls onAlarmDismissed callback ↓ GatewayClient sends {"type":"alarm.dismiss","alarmId":"..."} ↓ OAuth Proxy receives message ↓ Proxy broadcasts {"type":"event","event":"mobile.alarm.dismissed","payload":{...}} to ALL clients ↓ Device B, C, D... receive broadcast ↓ Each device silently dismisses that alarm ID ``` ### Implementation Details **Mobile App (Alfred Mobile):** 1. **AlarmManager.kt** - Singleton with `onAlarmDismissed` callback - When `dismissAlarm(alarmId)` is called, triggers callback - Callback is set by MainScreen to notify GatewayClient 2. **GatewayClient.kt** - Added `dismissAlarm(alarmId)` method - Sends `{"type":"alarm.dismiss","alarmId":"..."}` via WebSocket - Receives `mobile.alarm.dismissed` events - Calls `listener.onAlarmDismissed(alarmId)` 3. **GatewayListener interface** - Added `onAlarmDismissed(alarmId)` callback 4. **MainScreen.kt** - Wires everything together - Sets `alarmManager.onAlarmDismissed` callback to call `gatewayClient.dismissAlarm()` - Implements `onAlarmDismissed()` to dismiss alarm locally without re-broadcasting **Proxy Server (alfred-proxy):** 1. **Message Interception** - Checks for `alarm.dismiss` messages from clients 2. **Broadcasting** - Creates `mobile.alarm.dismissed` event and sends to all connected clients 3. **No OpenClaw Forwarding** - Dismiss events are handled internally, not sent to OpenClaw ### Message Format **Client → Proxy (dismiss):** ```json { "type": "alarm.dismiss", "alarmId": "alarm-1770148325914", "timestamp": 1770148325914 } ``` **Proxy → All Clients (broadcast):** ```json { "type": "event", "event": "mobile.alarm.dismissed", "payload": { "alarmId": "alarm-1770148325914", "timestamp": 1770148325914 } } ``` ## Testing ### Prerequisites - Alfred Mobile app installed on 2+ devices - All devices connected to the same proxy (alfred-app.dnspegasus.net) - All devices authenticated with the same user account ### Test Procedure 1. **Set an alarm:** ```bash # Via Alfred or directly: curl -X POST http://localhost:18790/api/notify \ -H "Content-Type: application/json" \ -d '{"notificationType":"alarm","title":"Cross-Device Test","message":"Testing sync","priority":"high","sound":true,"vibrate":true}' ``` 2. **Verify alarm triggers on all devices** - Sound and vibration should start on all devices - Full-screen AlarmActivity should appear on all devices 3. **Dismiss on ONE device** - Use either the notification dismiss button OR the full-screen dismiss button - Watch the other devices 4. **Expected behavior:** - ✅ Alarm stops immediately on the device you dismissed - ✅ Alarm automatically stops on all other devices within 1-2 seconds - ✅ Notifications clear on all devices - ✅ No re-triggering or duplicate dismissals ### Troubleshooting **Alarm doesn't dismiss on other devices:** - Check proxy logs: `tail -f /tmp/alfred-proxy-new.log` - Look for: `[proxy] Alarm dismiss received:` and `Broadcasted alarm dismiss to X client(s)` - Verify all devices are connected: broadcast count should match device count **Alarm dismisses multiple times:** - Check for infinite loops in logs - Verify the `onAlarmDismissed` callback is properly cleared before local dismissal in broadcast handler **Delay in dismissal:** - Normal: 1-2 second delay is expected (network latency) - Check WebSocket connection health on all devices - Verify proxy is not rate-limiting or buffering messages ## Logging **Mobile App (adb logcat):** ```bash # Watch for alarm dismissal events adb logcat | grep -E "AlarmManager.*Dismiss|onAlarmDismissed|alarm.dismiss" # Example output: # AlarmManager: Dismissing alarm: alarm-1770148325914 # MainScreen: Alarm dismissed locally, broadcasting: alarm-1770148325914 # GatewayClient: Sending alarm dismiss: alarm-1770148325914 # MainScreen: Received alarm dismiss broadcast: alarm-1770148325914 ``` **Proxy Server:** ```bash tail -f /tmp/alfred-proxy-new.log | grep -i alarm # Example output: # [proxy] Alarm dismiss received: alarm-1770148325914 # [proxy] Broadcasted alarm dismiss to 2 client(s) ``` ## Edge Cases ### Same Device Dismissal - Device dismisses alarm → triggers broadcast → receives own broadcast - **Handled:** Callback is temporarily cleared before processing broadcast to avoid re-dismissal ### Partial Network Failure - One device offline when alarm is dismissed - **Behavior:** Alarm continues ringing on offline device until manually dismissed - **No persistence:** Dismiss events are not queued or stored ### Multiple Simultaneous Dismissals - Two devices dismiss the same alarm within milliseconds - **Behavior:** Each sends dismiss event, proxy broadcasts both - **Handled:** AlarmManager is idempotent - dismissing already-dismissed alarm is safe ### Connection During Active Alarm - Device connects while alarm is already ringing on other devices - **Behavior:** Newly connected device starts alarm (receives notification) - **No state sync:** Active alarm state is not synchronized on connect ## Future Enhancements ### Possible Improvements: 1. **State sync on connect** - Send active alarm list when device connects 2. **Dismiss persistence** - Store recent dismissals (last 5 minutes) and send to new connections 3. **Snooze sync** - Extend cross-device sync to snooze actions 4. **Offline queue** - Queue dismiss events when offline, send when reconnected ### Not Implemented: - Alarm creation sync (alarms created via Alfred cron, not device-local) - Snooze functionality - Custom alarm sounds per device - Volume control sync ## Security Considerations - **Authentication:** All clients must be authenticated via OAuth to connect to proxy - **Authorization:** Same user account across devices (enforced by OAuth) - **No spoofing:** Alarm IDs include timestamp - difficult to forge - **Rate limiting:** Consider adding if dismiss spam becomes an issue ## Version History - **v1.0.1** (2026-02-03) - Initial cross-device alarm dismissal implementation - **v1.0.0** (2026-02-03) - Basic alarm system with single-device dismissal ## Related Documentation - `ALARMS.md` - Full alarm system documentation - `ARCHITECTURE.md` - Overall system architecture - `AGENT_TOOLS.md` - Mobile-notify and alarms skill integration - `~/.openclaw/workspace/skills/alarms/SKILL.md` - Alarm scheduling skill