Skip to content

Add iOS Live Activity push support#278

Open
rwarner wants to merge 6 commits intohome-assistant:mainfrom
rwarner:feat/ios-live-activity
Open

Add iOS Live Activity push support#278
rwarner wants to merge 6 commits intohome-assistant:mainfrom
rwarner:feat/ios-live-activity

Conversation

@rwarner
Copy link

@rwarner rwarner commented Mar 20, 2026

Summary

Adds iOS Live Activity push support using FCM's native liveActivityToken field (Firebase Admin SDK v13.5.0+).

When HA core sends a notification with live_activity_token in the request body, the relay server's ios.js payload builder detects it and constructs an FCM message with apns.liveActivityToken. FCM then automatically sets apns-push-type: liveactivity and routes the notification to Apple's push servers — no custom APNs client, JWT signing, or HTTP/2 session management needed.

Changes:

  • ios.js: createPayload detects live_activity_token in the request body and delegates to buildLiveActivityPayload(), which sets the FCM apns.liveActivityToken field with the correct aps structure for start/update/end events
  • package.json: Bumps firebase-admin from ^12.1.0 to ^13.5.0 (minimum version with liveActivityToken support) and firebase-functions from ^5.0.1 to ^6.1.1 (required peer dependency for firebase-admin v13)
  • index.js: Updated import from firebase-functions to firebase-functions/v1 — firebase-functions v6 changes the default export to v2 APIs, but the v1 import retains functions.config(), functions.region(), and functions.runWith() used by existing Cloud Function definitions
  • 20 tests covering fixture-driven payload validation, FCM integration via handleRequest, and backward compatibility with normal notifications

No new files, no new endpoints — Live Activity notifications flow through the existing /api/sendPush/iOS/v1 endpoint alongside normal iOS notifications. The APNS_TEAM_ID, APNS_KEY_ID, and APNS_PRIVATE_KEY environment variables are not needed.

Test plan

  • All 20 Live Activity tests pass
  • All 129 non-Live-Activity tests pass (2 pre-existing timezone failures in valkey-rate-limiter unrelated to this PR)
  • Fixture-driven tests validate payload structure for start, update, end, and full-field scenarios
  • Integration tests verify messaging.send() receives correct liveActivityToken

for: home-assistant/iOS#4444
for: home-assistant/core#166072

rwarner and others added 3 commits March 20, 2026 10:33
Live Activity push tokens from ActivityKit are direct APNs tokens
(hex-encoded), not FCM tokens. The existing iOSV1 endpoint cannot
handle them — this adds a dedicated iOSLiveActivityV1 endpoint that
sends directly to the APNs HTTP/2 API using JWT authentication,
bypassing FCM entirely.

New files:
- apns.js: APNs HTTP/2 client with ES256 JWT auth (Node built-ins
  only, no new dependencies). JWT cached and rotated every 45 min.
  HTTP/2 sessions cached per environment (sandbox/production).
- live-activity.js: payload builder for start/update/end events.
  Maps content_state fields to the HALiveActivityAttributes struct
  expected by the iOS companion app. Sets apns-push-type: liveactivity
  and routes to correct APNs topic using bundle ID from registration_info.

Updated files:
- handlers.js: adds handleLiveActivityRequest(), which validates hex
  APNs tokens, applies rate limiting, sends via apns.js, and handles
  BadDeviceToken (400) without error reporting.
- index.js: exports iOSLiveActivityV1 Cloud Function.
- webapp.js: adds /api/sendPush/iOS/liveActivity/v1 route for local dev.

Tests: 20 tests across fixture-driven createPayload tests and
handleLiveActivityRequest integration tests covering success, token
validation, rate limiting, APNs error mapping, and end-event
rate-limit bypass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves createLiveActivityPayload() from the standalone live-activity.js
into ios.js as a second export, keeping all iOS-specific logic in one
place consistent with the existing android.js/ios.js pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Follows the existing test naming convention (legacy.test.js -> legacy.js)
now that the Live Activity payload builder lives in ios.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 20, 2026 14:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class iOS Live Activity push support by introducing a dedicated endpoint that sends directly to APNs (HTTP/2 + JWT), along with an iOS payload builder and tests/fixtures to validate live-activity payload generation and handler behavior.

Changes:

  • Introduces a new iOSLiveActivityV1 endpoint (Cloud Functions export + local Fastify route) backed by a new handleLiveActivityRequest handler.
  • Adds APNs sender module (apns.js) implementing ES256 JWT generation and HTTP/2 session reuse.
  • Extends ios.js with createLiveActivityPayload and adds fixture-driven + integration tests for Live Activity pushes.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
functions/webapp.js Adds local Fastify route for the new live-activity endpoint.
functions/index.js Exposes iOSLiveActivityV1 Cloud Function and exports the new handler for tests.
functions/handlers.js Implements handleLiveActivityRequest with APNs token validation + rate limiting + APNs send/error mapping.
functions/ios.js Adds createLiveActivityPayload (APS liveactivity structure + headers/environment).
functions/apns.js New APNs HTTP/2 client module with JWT auth + session caching.
functions/test/ios.test.js Adds fixtures + integration/unit coverage for live-activity payload creation and handler behavior.
functions/test/fixtures/live-activity/*.json Adds fixture inputs/expectations for start/update/end live-activity payload generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix misleading comment in webapp.js (was "Import the functions from index.js")
- Validate APNs token length (must be exactly 64 hex chars) in isValidApnsToken; add test
- Normalize apnsEnvironment to 'sandbox'|'production' to prevent unbounded session cache growth
- Guard JSON.parse in apns.js against non-JSON APNs responses
- Fix test: pass body overrides flat to createLiveActivityRequest (not nested under body:)
- Add explanatory comments throughout apns.js, ios.js, and handlers.js for non-obvious decisions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rwarner added a commit to rwarner/core that referenced this pull request Mar 20, 2026
Add support for iOS Live Activities in the mobile_app integration:

- Add `supports_live_activities`, `supports_live_activities_frequent_updates`,
  `live_activity_push_to_start_token`, and
  `live_activity_push_to_start_apns_environment` fields to SCHEMA_APP_DATA
  for explicit validation during device registration
- Add `update_live_activity_token` webhook handler: stores per-activity APNs
  push tokens reported by the iOS companion app when a Live Activity is
  created locally via ActivityKit
- Add `live_activity_dismissed` webhook handler: cleans up stored tokens when
  a Live Activity ends on the device
- Both handlers fire bus events so automations can react to activity lifecycle
- Add `supports_live_activities()` utility helper
- Add 4 tests covering token storage, default environment, dismissal cleanup,
  and nonexistent tag dismissal

for: home-assistant/mobile-apps-fcm-push#278
for: home-assistant/iOS#4444

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rwarner and others added 2 commits March 20, 2026 23:21
Firebase Admin SDK v13.5.0+ supports the liveActivityToken field in the
apns config object, which tells FCM to automatically set
apns-push-type: liveactivity and route the notification to APNs. This
eliminates the need for a custom direct APNs HTTP/2 client.

Changes:
- Delete apns.js (custom HTTP/2 + JWT APNs client) — no longer needed
- Bump firebase-admin from ^12.1.0 to ^13.5.0
- Rewrite ios.js: createPayload detects live_activity_token in request
  body and builds FCM payload with apns.liveActivityToken (camelCase)
- Remove handleLiveActivityRequest from handlers.js — existing
  handleRequest + messaging.send() handles everything
- Remove iOSLiveActivityV1 endpoint from index.js and webapp.js
- Rewrite tests for FCM-based delivery path
- Update fixtures to use FCM payload structure

The relay server no longer needs APNS_TEAM_ID, APNS_KEY_ID, or
APNS_PRIVATE_KEY environment variables. HA core sends both the FCM
registration token (push_token) and the Live Activity APNs token
(live_activity_token) to the same /api/sendPush/iOS/v1 endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
firebase-admin v13.5.0+ (required for liveActivityToken) has a peer
dependency on firebase-functions v6.1.1+. firebase-functions v6 changes
the default export to v2 APIs, so index.js now imports from
firebase-functions/v1 to retain functions.config(), functions.region(),
and functions.runWith().

Test mocks updated to mock both firebase-functions and
firebase-functions/v1 paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
rwarner added a commit to rwarner/core that referenced this pull request Mar 24, 2026
Add support for iOS Live Activities in the mobile_app integration:

- Add `supports_live_activities`, `supports_live_activities_frequent_updates`,
  `live_activity_push_to_start_token`, and
  `live_activity_push_to_start_apns_environment` fields to SCHEMA_APP_DATA
  for explicit validation during device registration
- Add `update_live_activity_token` webhook handler: stores per-activity APNs
  push tokens reported by the iOS companion app when a Live Activity is
  created locally via ActivityKit
- Add `live_activity_dismissed` webhook handler: cleans up stored tokens when
  a Live Activity ends on the device
- Both handlers fire bus events so automations can react to activity lifecycle
- Add `supports_live_activities()` utility helper
- Add 4 tests covering token storage, default environment, dismissal cleanup,
  and nonexistent tag dismissal

for: home-assistant/mobile-apps-fcm-push#278
for: home-assistant/iOS#4444

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants