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

200 lines
6.6 KiB
Markdown

# 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