Files
alfred-mobile/CROSS_DEVICE_ALARMS.md
jknapp 6d4ae2e5c3 Initial commit: Alfred Mobile - AI Assistant Android App
- 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
2026-02-09 11:12:51 -08:00

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):

  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):

{
  "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

  1. 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}'
    
  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):

# 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:

  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
  • 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