200 lines
6.6 KiB
Markdown
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
|