# Agent Tools - Mobile Notifications & Alarms **Alfred can send notifications, alerts, and alarms directly to your mobile device using FCM!** ## Overview The Alfred mobile app supports receiving notifications even when the app is closed, screen is locked, or device is asleep. This enables: - **Alarms** - High-priority notifications that wake the device - **Instant alerts** - Get notified when tasks complete - **Timers** - Set countdown notifications - **Reminders** - Schedule notifications for specific times - **Background work** - Alfred can notify you when long-running tasks finish **Key Features:** - ✅ **FCM Push Notifications** - Delivery guaranteed even when app is closed - ✅ **Token Persistence** - Works after proxy restarts - ✅ **Dual Delivery** - WebSocket (if connected) + FCM (always) - ✅ **Wake on Alarm** - Device wakes for high-priority alarms ## Features ### ✅ What's Working 1. **Instant Notifications** - Alfred can send alerts immediately 2. **System Notifications** - Show even when app is backgrounded 3. **In-App Display** - Notifications appear in chat when app is open 4. **Multiple Notification Types** - Alert (⚠️), Timer (⏰), Reminder (🔔) 5. **Optional TTS** - Speak notifications when TTS is enabled (foreground only) 6. **WebSocket Broadcast** - All connected mobile devices receive notifications ### 📱 Mobile App Features - **Background Notifications** - Receive notifications even when app is closed - **Notification Icons** - Visual indicators for different notification types - **System Tray** - Notifications appear in Android notification shade - **Tap to Open** - Tapping notification opens the app - **Auto-dismiss** - Notifications clear when tapped ## Usage from Alfred ### Send Alarms **Use `alfred-notify` for all notifications and alarms.** ```bash # High-priority alarm (wakes device) alfred-notify --alarm "Wake up!" # Alarm with custom title alfred-notify --alarm --title "⏰ Morning Alarm" "Time to get up!" # Silent alarm (vibrate only) alfred-notify --alarm --no-sound "Silent wake-up" ``` ### Send Notifications ```bash # Basic notification alfred-notify "Task completed successfully" # Notification with custom title alfred-notify --title "Build System" "Compilation finished" # Silent notification alfred-notify --no-sound --no-vibrate "Background update complete" ``` ### Schedule via Cron For scheduled alarms/reminders, use the `cron` tool: ```javascript // Set alarm for 5 minutes from now { "name": "5 minute alarm", "schedule": { "kind": "at", "atMs": Date.now() + (5 * 60 * 1000) }, "payload": { "kind": "agentTurn", "message": "Run: alfred-notify --alarm '⏰ 5 minute alarm!'", "deliver": false }, "sessionTarget": "isolated" } ``` **Important:** Always use `sessionTarget: "isolated"` + `agentTurn` for alarms, never `systemEvent`. ## Alfred Integration Alfred uses `alfred-notify` from cron jobs or via exec tool: **Example conversation:** ``` You: "Set an alarm for 5 minutes" Alfred: *schedules cron job* "I'll send an alarm in 5 minutes!" ``` Behind the scenes, Alfred creates a cron job: ```javascript cron.add({ schedule: { kind: "at", atMs: Date.now() + 300000 }, payload: { kind: "agentTurn", message: "Run: alfred-notify --alarm '⏰ 5 minute alarm!'" }, sessionTarget: "isolated" }) ``` **Another example:** ``` You: "Let me know when the build finishes" Alfred: "Sure, I'll notify you when it completes" ``` Alfred monitors the build and executes: ```javascript exec(['alfred-notify', '--title', 'Build System', 'Build completed! ✅']) ``` ## Technical Details ### Architecture ``` ┌─────────────────┐ │ Alfred │ (OpenClaw agent) │ exec/cron │ └────────┬────────┘ │ executes ▼ ┌─────────────────┐ │ alfred-notify │ (CLI wrapper) │ bash script │ └────────┬────────┘ │ HTTP POST ▼ ┌─────────────────┐ │ Alfred Proxy │ (port 18790) │ + Firebase │ │ Admin SDK │ └────────┬────────┘ │ ├──────────────────────┬─────────────────────┐ │ WebSocket │ FCM Push │ │ (if connected) │ (always) │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Mobile App(s) │ │ Firebase Cloud │ │ fcm-tokens │ │ GatewayClient │ │ Messaging │ │ .json (disk) │ └────────┬────────┘ └────────┬────────┘ └─────────────────┘ │ │ │ ▼ │ ┌─────────────────┐ │ │ Mobile Device │ │ │ (even asleep) │ │ └────────┬────────┘ │ │ └─────────────────────┘ │ ▼ ┌─────────────────┐ │ Android OS │ (system notifications) └─────────────────┘ ``` **Key Features:** - **Dual Delivery:** WebSocket (instant if connected) + FCM (guaranteed) - **Token Persistence:** FCM tokens saved to `fcm-tokens.json`, survive restarts - **Wake Device:** FCM can wake locked/sleeping devices for alarms - **Firebase Admin SDK:** Uses `cloudmessaging.messages.create` permission ### Event Format ```json { "type": "event", "event": "mobile.notification", "payload": { "notificationType": "alert|timer|reminder", "title": "Alfred", "message": "Notification text", "priority": "default", "sound": true, "vibrate": true, "timestamp": 1706918400000, "action": null } } ``` ### Mobile App Handling 1. **GatewayClient** receives `mobile.notification` event via WebSocket 2. **MainScreen** `onNotification()` callback processes the event 3. **Notification icons** added based on type (⏰ ⚠️ 🔔 📢) 4. **System notification** shown if timer/reminder OR if app is backgrounded 5. **Chat message** added if app is in foreground 6. **Optional TTS** speaks the notification if enabled (foreground only) ## Testing ### Test Instant Notification From terminal: ```bash cd ~/.openclaw/workspace/alfred-proxy ./alfred-notify "Test notification!" ``` ### Test Alarm ```bash ./alfred-notify --alarm "Test alarm!" ``` ### Test with Custom Title ```bash ./alfred-notify --title "Kitchen Timer" "Oven is ready!" ``` ### Test Silent Notification ```bash ./alfred-notify --no-sound --no-vibrate "Silent test" ``` ### Test via Alfred Ask Alfred: ``` "Send me a test alarm" "Set an alarm for 1 minute" "Notify me when this finishes" ``` ## Monitoring ### Check Proxy Status ```bash # Service status systemctl --user status alfred-proxy.service # Watch logs tail -f /tmp/alfred-proxy.log # Check for FCM token registration grep "fcm.*Registering" /tmp/alfred-proxy.log # Check for successful sends grep "fcm.*Successfully" /tmp/alfred-proxy.log ``` ### Check Connected Clients & Tokens When app connects, you'll see: ``` [proxy] New connection from ::ffff:192.168.1.20 [auth] Token validated for user: shadow@dao-mail.com [fcm] Registering token for user a2b49b91...: ewqRvIsOTuiWJk... [fcm] Saved tokens to disk ``` ### Check Token Persistence ```bash # View persisted FCM tokens cat ~/.openclaw/workspace/alfred-proxy/fcm-tokens.json # Should show: { "user-id-hash": [ "fcm-token-string" ] } ``` ### Test Notification Delivery ```bash # Via CLI (recommended) alfred-notify --alarm "Test" # Via HTTP API curl -X POST http://localhost:18790/api/notify \ -H "Content-Type: application/json" \ -d '{ "notificationType": "alarm", "title": "Test", "message": "Hello!", "priority": "high", "sound": true, "vibrate": true }' ``` Expected response: ```json {"success":true,"clients":0,"fcm":1} ``` - `clients`: WebSocket connections (0 if app closed) - `fcm`: FCM devices notified (should be 1+) ## Troubleshooting ### No notifications appearing 1. **Check FCM tokens persisted**: ```bash cat alfred-proxy/fcm-tokens.json # Should show registered tokens ``` 2. **Check proxy logs for FCM success**: ```bash tail -f /tmp/alfred-proxy.log | grep fcm # Should show: [fcm] Successfully sent X message(s) ``` 3. **If seeing "Permission denied" error**: ``` [fcm] Error: Permission 'cloudmessaging.messages.create' denied ``` - Check service account has correct role: **Firebase Admin SDK Administrator Service Agent** - Verify FCM API is enabled in Google Cloud Console - Regenerate service account key if needed 4. **If "No FCM tokens registered"**: - Open Alfred app to trigger token registration - Check logs for `[fcm] Registering token` - Verify app sends token on connect (should happen automatically) 5. **Test directly**: ```bash alfred-notify --alarm "Test" ``` ### WebSocket vs FCM - **WebSocket**: Instant delivery when app is open and connected - **FCM**: Always works, even when app is closed or device is asleep - Both should show in response: `{"clients":1,"fcm":1}` If `clients:0, fcm:1` → App closed but FCM working (normal) If `clients:0, fcm:0` → No tokens registered (open app to fix) ### Cron alarms not firing 1. **Verify cron job uses correct format**: ```javascript // ✅ CORRECT { "sessionTarget": "isolated", "payload": { "kind": "agentTurn", "message": "Run: alfred-notify --alarm 'Message'" } } // ❌ WRONG { "sessionTarget": "main", "payload": { "kind": "systemEvent", "text": "Alarm message" } } ``` 2. **Check cron job exists**: ```bash # Ask Alfred in chat /cron list ``` 3. **Check cron execution logs**: ```bash tail -f /tmp/alfred-proxy.log # Look for cron execution and alfred-notify calls ``` ### Firebase Permission Errors **Error:** `Permission 'cloudmessaging.messages.create' denied` **Solution:** 1. Verify service account role: **Firebase Admin SDK Administrator Service Agent** (NOT "Firebase Cloud Messaging Admin") 2. Enable FCM API: https://console.cloud.google.com/apis/library/fcm.googleapis.com 3. Download fresh service account key 4. Place at: `alfred-proxy/service-account.json` 5. Restart proxy: `systemctl --user restart alfred-proxy.service` See `FCM_SETUP.md` for full setup instructions. ## Future Enhancements Potential additions: - [ ] **Custom sounds** - Per-notification sound selection - [ ] **Action buttons** - "Snooze", "Mark as done", etc. - [ ] **Deep links** - Open specific app screens from notification - [ ] **Rich content** - Images, progress bars, etc. - [ ] **Notification groups** - Collapse similar notifications - [ ] **Do Not Disturb** - Respect user quiet hours - [ ] **Multi-device sync** - Mark as read across devices ## Version 1.0.0 - Initial release with alert/timer/reminder support