# 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 of `cloudmessaging.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.create` permission
- 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:**
```javascript
// 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_sync` flag
- Token not re-sent after proxy restarts
- Required manual reconnection to fix

**Solution:**
- Removed `fcm_token_needs_sync` flag 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:**
```kotlin
// 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-notify` bash 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:**
```bash
# 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 documentation
- `alfred-proxy/README.md` - Added FCM architecture and notification API docs
- `alfred-mobile/AGENT_TOOLS.md` - Updated to use alfred-notify instead of mobile-notify
- `TOOLS.md` - Updated alarm/notification section with alfred-notify usage
- `android-app-todo.md` - Documented fixes and added OAuth token refresh as new bug
- `alfred-proxy/SETUP_COMPLETE.md` - Created comprehensive setup guide

**New Files:**
- `alfred-proxy/SETUP_COMPLETE.md` - Complete setup and troubleshooting guide
- `alfred-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

1. **App Connects:**
   - Authenticates with OAuth
   - Sends FCM token via WebSocket
   - Proxy saves token to `fcm-tokens.json`

2. **Proxy Restarts:**
   - Loads tokens from `fcm-tokens.json`
   - Ready to send notifications immediately

3. **App Reconnects:**
   - Sends FCM token again (always)
   - Updates token in memory and disk

4. **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

```bash
# 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

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

```bash
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

```bash
# 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

```bash
# 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

1. **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

2. **No Notification History**
   - Past notifications not stored
   - Can't view missed notifications in app
   - **TODO:** Add notification history screen

3. **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 credentials
- `alfred-proxy/fcm-tokens.json` - User device tokens
- `alfred-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

1. Rotate service account keys every 90 days
2. Monitor FCM usage in Firebase Console
3. Review notification logs regularly
4. Use `chmod 600` for sensitive files

## Troubleshooting Guide

### Issue: Permission Denied

**Error:** `Permission 'cloudmessaging.messages.create' denied`

**Fix:**
1. Verify service account role in IAM
2. Should be: Firebase Admin SDK Administrator Service Agent
3. Regenerate service account key if needed
4. Restart proxy

### Issue: No Tokens Registered

**Error:** `[fcm] No FCM tokens registered`

**Fix:**
1. Open Alfred app
2. Verify "Connected ✅" status
3. Check logs for token registration
4. Verify `fcm-tokens.json` exists

### Issue: Notifications Not Arriving

**Checklist:**
1. ✅ Proxy running? `systemctl --user status alfred-proxy.service`
2. ✅ Tokens persisted? `cat alfred-proxy/fcm-tokens.json`
3. ✅ FCM API enabled? Check Google Cloud Console
4. ✅ Service account key valid? Check expiry
5. ✅ Test succeeds? `alfred-notify --alarm "Test"`

## Success Metrics

**All Tests Passing ✅**

- [x] Send alarm via CLI
- [x] Alarm received on locked tablet
- [x] Token persists across proxy restart
- [x] Alarm works after restart (without app reconnect)
- [x] App auto-registers token on connect
- [x] Dual delivery (WebSocket + FCM)
- [x] Correct Firebase permissions
- [x] Documentation complete

## Files Modified Summary

### New Files
- `alfred-proxy/alfred-notify` - CLI wrapper
- `alfred-proxy/fcm-tokens.json` - Token storage (git-ignored)
- `alfred-proxy/SETUP_COMPLETE.md` - Setup guide
- `alfred-mobile/ALARM_SYSTEM_COMPLETE.md` - This document

### Modified Files
- `alfred-proxy/server.js` - Token persistence logic
- `alfred-proxy/.gitignore` - Added fcm-tokens.json
- `alfred-proxy/service-account.json` - Updated with fresh key
- `alfred-mobile/app/src/main/java/com/openclaw/alfred/ui/screens/MainScreen.kt` - Always send token
- `alfred-mobile/FCM_SETUP.md` - Correct IAM role documentation
- `alfred-proxy/README.md` - Added FCM architecture
- `alfred-mobile/AGENT_TOOLS.md` - Updated to alfred-notify
- `TOOLS.md` - Updated alarm section
- `android-app-todo.md` - Documented fixes

## Next Steps

1. **Build and Deploy**
   - ✅ App already rebuilt and installed
   - ✅ Proxy running with new code
   - ✅ FCM configured correctly

2. **Test in Production**
   - Set real alarms via Alfred
   - Monitor reliability over next few days
   - Check token persistence after natural restarts

3. **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
