- 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
11 KiB
Alfred Mobile Alarm System - Implementation Complete
Date: 2026-02-04
Status: ✅ Fully Working
Integration: Alfred App → Proxy → Firebase → Mobile Device
Overview
Implemented a complete alarm and notification system that allows Alfred to send alerts, alarms, and notifications to your mobile device even when the app is closed, screen is locked, or device is asleep.
What Was Fixed Today
1. Firebase Permissions Issue ✅
Problem:
- Service account had wrong IAM role
- Was using
firebasenotifications.*(legacy API) instead ofcloudmessaging.messages.create(v1 API) - FCM notifications failing with permission denied errors
Solution:
- Updated IAM role to: Firebase Admin SDK Administrator Service Agent
- This role includes the correct
cloudmessaging.messages.createpermission - Regenerated service account key
- Updated documentation in
FCM_SETUP.md
Files Modified:
alfred-proxy/service-account.json(replaced with fresh key)alfred-mobile/FCM_SETUP.md(documented correct role)
2. FCM Token Persistence ✅
Problem:
- FCM tokens stored in memory only
- Lost when proxy restarted
- Required app reconnection to re-register
- Alarms wouldn't work after proxy restarts if tablet was asleep
Solution:
- Added token persistence to
fcm-tokens.json - Tokens automatically saved on registration
- Tokens automatically loaded on proxy startup
- Alarms now work even after proxy restarts
Files Modified:
alfred-proxy/server.js(added loadFcmTokens/saveFcmTokens functions)alfred-proxy/.gitignore(added fcm-tokens.json)
Code Changes:
// Load tokens on startup
loadFcmTokens();
// Save tokens when registered
function saveFcmTokens() {
const data = {};
fcmTokens.forEach((tokens, userId) => {
data[userId] = Array.from(tokens);
});
writeFileSync(tokensFile, JSON.stringify(data, null, 2), 'utf8');
}
3. App Token Registration ✅
Problem:
- App only sent FCM token once using
fcm_token_needs_syncflag - Token not re-sent after proxy restarts
- Required manual reconnection to fix
Solution:
- Removed
fcm_token_needs_syncflag logic - App now sends FCM token on every connection
- Token automatically re-registered when app reconnects after proxy restart
Files Modified:
alfred-mobile/app/src/main/java/com/openclaw/alfred/ui/screens/MainScreen.kt
Code Changes:
// Before (wrong):
if (fcmToken != null && needsSync) {
gatewayClient?.sendFCMToken(fcmToken)
prefs.edit().putBoolean("fcm_token_needs_sync", false).apply()
}
// After (correct):
if (fcmToken != null) {
Log.d("MainScreen", "Sending FCM token to proxy on connect")
gatewayClient?.sendFCMToken(fcmToken)
}
4. Created alfred-notify CLI Wrapper ✅
Problem:
- No easy way to send alarms/notifications from command line or cron
- Had to manually craft curl commands
- Error handling not user-friendly
Solution:
- Created
alfred-notifybash script - Simple CLI with intuitive options
- Color-coded output
- Helpful error messages
- Support for alarms, custom titles, sound/vibrate control
Files Created:
alfred-proxy/alfred-notify(executable bash script)
Usage:
# Simple alarm
alfred-notify --alarm "Wake up!"
# Custom title
alfred-notify --title "Kitchen" "Oven ready"
# Silent notification
alfred-notify --no-sound "Background task done"
5. Documentation Updates ✅
Updated Files:
alfred-mobile/FCM_SETUP.md- Correct Firebase IAM role documentationalfred-proxy/README.md- Added FCM architecture and notification API docsalfred-mobile/AGENT_TOOLS.md- Updated to use alfred-notify instead of mobile-notifyTOOLS.md- Updated alarm/notification section with alfred-notify usageandroid-app-todo.md- Documented fixes and added OAuth token refresh as new bugalfred-proxy/SETUP_COMPLETE.md- Created comprehensive setup guide
New Files:
alfred-proxy/SETUP_COMPLETE.md- Complete setup and troubleshooting guidealfred-mobile/ALARM_SYSTEM_COMPLETE.md- This file
How It Works
Architecture
Alfred (AI Agent)
↓ (via cron or exec)
alfred-notify CLI
↓ (HTTP POST)
Alfred Proxy (Node.js)
- Validates OAuth
- Stores FCM tokens (fcm-tokens.json)
- Dual delivery:
↓ ↓
WebSocket Firebase Admin SDK
(if connected) (always works)
↓ ↓
Mobile App Firebase Cloud Messaging
(instant) ↓
Mobile Device
(even if asleep)
Token Flow
-
App Connects:
- Authenticates with OAuth
- Sends FCM token via WebSocket
- Proxy saves token to
fcm-tokens.json
-
Proxy Restarts:
- Loads tokens from
fcm-tokens.json - Ready to send notifications immediately
- Loads tokens from
-
App Reconnects:
- Sends FCM token again (always)
- Updates token in memory and disk
-
Notification Sent:
- Proxy broadcasts via WebSocket (if app connected)
- Proxy sends via FCM (always)
- Device receives notification even if asleep
Usage Examples
From Alfred AI
Set an alarm:
User: "Set an alarm for 5 minutes"
Alfred: [creates cron job with alfred-notify]
Send instant notification:
User: "Notify me when the build finishes"
Alfred: [monitors build, then runs alfred-notify]
From Command Line
# Test alarm
alfred-notify --alarm "Test"
# Kitchen timer
alfred-notify --title "Kitchen" "Oven is ready!"
# Silent notification
alfred-notify --no-sound --no-vibrate "Background task complete"
From Cron Jobs
{
"name": "Morning alarm",
"schedule": {
"kind": "cron",
"expr": "0 7 * * *", // 7 AM daily
"tz": "America/Los_Angeles"
},
"payload": {
"kind": "agentTurn",
"message": "Run: alfred-notify --alarm '⏰ Good morning!'",
"deliver": false
},
"sessionTarget": "isolated"
}
Verification
Test 1: Send Alarm
cd ~/.openclaw/workspace/alfred-proxy
./alfred-notify --alarm "Test alarm"
Expected:
- ✅ Tablet receives notification (even if locked)
- ✅ Console shows:
✓ Notification sent - ✅ Logs show:
[fcm] Successfully sent 1 message(s)
Test 2: Verify Token Persistence
# Restart proxy
systemctl --user restart alfred-proxy.service
# Check tokens loaded
tail -20 /tmp/alfred-proxy.log | grep fcm
# Expected: [fcm] Loaded 1 token(s) for 1 user(s) from disk
Test 3: Send Alarm After Restart
# Send alarm without reconnecting app
./alfred-notify --alarm "After restart test"
Expected:
- ✅ Alarm delivered via FCM
- ✅ Works even if app hasn't reconnected yet
Known Limitations & Future Work
Current Limitations
-
OAuth Token Expiration
- Access tokens expire after ~10-60 minutes
- App doesn't implement token refresh yet
- Workaround: Logout/login when connection fails
- TODO: Implement automatic token refresh flow
-
No Notification History
- Past notifications not stored
- Can't view missed notifications in app
- TODO: Add notification history screen
-
Single Notification Sound
- Uses default alarm/notification sound
- No custom sound selection
- TODO: Add sound customization
Roadmap
- OAuth token refresh implementation
- Notification history in app
- Custom notification sounds
- Notification action buttons (snooze, dismiss)
- Rich notifications (images, progress bars)
- Do Not Disturb mode
- Multi-device notification sync
Security
Sensitive Files (Git-Ignored)
alfred-proxy/service-account.json- Firebase credentialsalfred-proxy/fcm-tokens.json- User device tokensalfred-proxy/.env- Configuration with API tokens
Permissions
- Service account has minimal required permissions
- Role: Firebase Admin SDK Administrator Service Agent
- Only includes:
cloudmessaging.messages.create+ basic Firebase access - No admin, billing, or other sensitive permissions
Best Practices
- Rotate service account keys every 90 days
- Monitor FCM usage in Firebase Console
- Review notification logs regularly
- Use
chmod 600for sensitive files
Troubleshooting Guide
Issue: Permission Denied
Error: Permission 'cloudmessaging.messages.create' denied
Fix:
- Verify service account role in IAM
- Should be: Firebase Admin SDK Administrator Service Agent
- Regenerate service account key if needed
- Restart proxy
Issue: No Tokens Registered
Error: [fcm] No FCM tokens registered
Fix:
- Open Alfred app
- Verify "Connected ✅" status
- Check logs for token registration
- Verify
fcm-tokens.jsonexists
Issue: Notifications Not Arriving
Checklist:
- ✅ Proxy running?
systemctl --user status alfred-proxy.service - ✅ Tokens persisted?
cat alfred-proxy/fcm-tokens.json - ✅ FCM API enabled? Check Google Cloud Console
- ✅ Service account key valid? Check expiry
- ✅ Test succeeds?
alfred-notify --alarm "Test"
Success Metrics
All Tests Passing ✅
- Send alarm via CLI
- Alarm received on locked tablet
- Token persists across proxy restart
- Alarm works after restart (without app reconnect)
- App auto-registers token on connect
- Dual delivery (WebSocket + FCM)
- Correct Firebase permissions
- Documentation complete
Files Modified Summary
New Files
alfred-proxy/alfred-notify- CLI wrapperalfred-proxy/fcm-tokens.json- Token storage (git-ignored)alfred-proxy/SETUP_COMPLETE.md- Setup guidealfred-mobile/ALARM_SYSTEM_COMPLETE.md- This document
Modified Files
alfred-proxy/server.js- Token persistence logicalfred-proxy/.gitignore- Added fcm-tokens.jsonalfred-proxy/service-account.json- Updated with fresh keyalfred-mobile/app/src/main/java/com/openclaw/alfred/ui/screens/MainScreen.kt- Always send tokenalfred-mobile/FCM_SETUP.md- Correct IAM role documentationalfred-proxy/README.md- Added FCM architecturealfred-mobile/AGENT_TOOLS.md- Updated to alfred-notifyTOOLS.md- Updated alarm sectionandroid-app-todo.md- Documented fixes
Next Steps
-
Build and Deploy
- ✅ App already rebuilt and installed
- ✅ Proxy running with new code
- ✅ FCM configured correctly
-
Test in Production
- Set real alarms via Alfred
- Monitor reliability over next few days
- Check token persistence after natural restarts
-
Future Enhancements
- Implement OAuth token refresh
- Add notification history
- Custom notification sounds
Implementation Date: 2026-02-04
Status: ✅ Complete and Working
Tested: Yes - All scenarios passing
Documentation: Complete
Ready for Production: Yes