- OAuth authentication via Authentik - WebSocket connection to OpenClaw gateway - Configurable gateway URL with first-run setup - User preferences sync across devices - Multi-user support with custom assistant names - ElevenLabs TTS integration (local + remote) - FCM push notifications for alarms - Voice input via Google Speech API - No hardcoded secrets or internal IPs in tracked files
6.6 KiB
6.6 KiB
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):
-
AlarmManager.kt - Singleton with
onAlarmDismissedcallback- When
dismissAlarm(alarmId)is called, triggers callback - Callback is set by MainScreen to notify GatewayClient
- When
-
GatewayClient.kt - Added
dismissAlarm(alarmId)method- Sends
{"type":"alarm.dismiss","alarmId":"..."}via WebSocket - Receives
mobile.alarm.dismissedevents - Calls
listener.onAlarmDismissed(alarmId)
- Sends
-
GatewayListener interface - Added
onAlarmDismissed(alarmId)callback -
MainScreen.kt - Wires everything together
- Sets
alarmManager.onAlarmDismissedcallback to callgatewayClient.dismissAlarm() - Implements
onAlarmDismissed()to dismiss alarm locally without re-broadcasting
- Sets
Proxy Server (alfred-proxy):
- Message Interception - Checks for
alarm.dismissmessages from clients - Broadcasting - Creates
mobile.alarm.dismissedevent and sends to all connected clients - No OpenClaw Forwarding - Dismiss events are handled internally, not sent to OpenClaw
Message Format
Client → Proxy (dismiss):
{
"type": "alarm.dismiss",
"alarmId": "alarm-1770148325914",
"timestamp": 1770148325914
}
Proxy → All Clients (broadcast):
{
"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
-
Set an alarm:
# 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}' -
Verify alarm triggers on all devices
- Sound and vibration should start on all devices
- Full-screen AlarmActivity should appear on all devices
-
Dismiss on ONE device
- Use either the notification dismiss button OR the full-screen dismiss button
- Watch the other devices
-
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:andBroadcasted 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
onAlarmDismissedcallback 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):
# 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:
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:
- State sync on connect - Send active alarm list when device connects
- Dismiss persistence - Store recent dismissals (last 5 minutes) and send to new connections
- Snooze sync - Extend cross-device sync to snooze actions
- 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 documentationARCHITECTURE.md- Overall system architectureAGENT_TOOLS.md- Mobile-notify and alarms skill integration~/.openclaw/workspace/skills/alarms/SKILL.md- Alarm scheduling skill