find-my-iphone: Alexa/REST bridge to ring iPhone via iCloud
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# find-my-iphone image
|
||||
|
||||
Node/Express bridge exposing Alexa + REST endpoints over the find-my-iphone
|
||||
module (icloudjs SRP auth). Dockerfile lives in the source repo -
|
||||
https://git.alexzaw.dev/alexz/find-my-iphone (working copy:
|
||||
~/projects/find-my-iphone). Build + push with ./build.sh.
|
||||
|
||||
Gotchas:
|
||||
- APPLE_ID/APPLE_PASSWORD/API_KEY are required at boot or the container exits.
|
||||
- The 2FA trust token is written to /data (mounted from ${APP_DATA_DIR}/data);
|
||||
wiping that volume forces a new MFA round via POST /api/mfa.
|
||||
- /alexa signature verification requires the raw request body - never put a
|
||||
JSON body parser in front of it (server.js mounts express.json on /api only).
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
IMAGE_NAME="${IMAGE_NAME:-git.alexzaw.dev/alexz/find-my-iphone}"
|
||||
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
||||
SRC="${SRC:-$HOME/projects/find-my-iphone}"
|
||||
docker build "$@" -t "${IMAGE_NAME}:${IMAGE_TAG}" "$SRC"
|
||||
docker push "${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"$schema": "../app-info-schema.json",
|
||||
"name": "Find My iPhone Bridge",
|
||||
"id": "find-my-iphone",
|
||||
"available": true,
|
||||
"port": 8590,
|
||||
"exposable": true,
|
||||
"dynamic_config": true,
|
||||
"no_gui": true,
|
||||
"tipi_version": 1,
|
||||
"version": "1.2.0",
|
||||
"categories": [
|
||||
"automation",
|
||||
"utilities"
|
||||
],
|
||||
"description": "Self-hosted bridge that makes your iPhone ring via iCloud Find My. Exposes an Alexa custom-skill endpoint (/alexa, signature-verified) and API-key-protected REST endpoints (/api/alert, /api/devices, /api/location). Apple credentials stay on this server; 2FA trust token persists in the app volume.",
|
||||
"short_desc": "Ring your iPhone from Alexa or curl",
|
||||
"author": "alexz (fork of matt-kruse/find-my-iphone)",
|
||||
"source": "https://git.alexzaw.dev/alexz/find-my-iphone",
|
||||
"form_fields": [
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Apple ID",
|
||||
"env_variable": "APPLE_ID",
|
||||
"required": true,
|
||||
"hint": "The iCloud account email that owns the devices"
|
||||
},
|
||||
{
|
||||
"type": "password",
|
||||
"label": "Apple Password",
|
||||
"env_variable": "APPLE_PASSWORD",
|
||||
"required": true,
|
||||
"hint": "Stored only in Runtipi's app env - never leaves this server"
|
||||
},
|
||||
{
|
||||
"type": "random",
|
||||
"label": "API Key",
|
||||
"env_variable": "API_KEY",
|
||||
"required": true,
|
||||
"min": 32,
|
||||
"encoding": "hex",
|
||||
"hint": "Auto-generated. Send as X-Api-Key header on /api/* requests. View in Settings tab."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Alexa Skill ID",
|
||||
"env_variable": "ALEXA_SKILL_ID",
|
||||
"required": false,
|
||||
"hint": "amzn1.ask.skill.xxxx from the Alexa developer console. Locks /alexa to your skill. Leave blank until the skill exists, then fill in and save."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"label": "Default Device Name",
|
||||
"env_variable": "DEFAULT_DEVICE",
|
||||
"required": false,
|
||||
"hint": "Device to ring when none is specified (e.g. Alex's iPhone). Blank = first Find-My-capable device."
|
||||
}
|
||||
],
|
||||
"supported_architectures": [
|
||||
"amd64"
|
||||
],
|
||||
"created_at": 1754993000000,
|
||||
"updated_at": 1754993000000,
|
||||
"deprecated": false,
|
||||
"min_tipi_version": "4.5.0"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"$schema": "https://schemas.runtipi.io/dynamic-compose.json",
|
||||
"services": [
|
||||
{
|
||||
"name": "find-my-iphone",
|
||||
"image": "git.alexzaw.dev/alexz/find-my-iphone:latest",
|
||||
"isMain": true,
|
||||
"internalPort": 8080,
|
||||
"environment": [
|
||||
{ "key": "PORT", "value": "8080" },
|
||||
{ "key": "DATA_DIR", "value": "/data" },
|
||||
{ "key": "APPLE_ID", "value": "${APPLE_ID}" },
|
||||
{ "key": "APPLE_PASSWORD", "value": "${APPLE_PASSWORD}" },
|
||||
{ "key": "API_KEY", "value": "${API_KEY}" },
|
||||
{ "key": "ALEXA_SKILL_ID", "value": "${ALEXA_SKILL_ID:-}" },
|
||||
{ "key": "DEFAULT_DEVICE", "value": "${DEFAULT_DEVICE:-}" }
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
"hostPath": "${APP_DATA_DIR}/data",
|
||||
"containerPath": "/data",
|
||||
"readOnly": false
|
||||
}
|
||||
],
|
||||
"healthCheck": {
|
||||
"test": "curl -f http://127.0.0.1:8080/health || exit 1",
|
||||
"interval": "30s",
|
||||
"timeout": "5s",
|
||||
"retries": 3,
|
||||
"startPeriod": "10s"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Find My iPhone Bridge
|
||||
|
||||
Ring your iPhone by voice ("Alexa, open phone finder") or curl, self-hosted.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Route | Auth | Purpose |
|
||||
|-------|------|---------|
|
||||
| `POST /alexa` | Alexa signature + skill ID | Custom skill endpoint |
|
||||
| `GET /api/status` | X-Api-Key | Auth state: ready / mfa_required / error |
|
||||
| `POST /api/mfa` | X-Api-Key | One-time 2FA bootstrap `{"code":"123456"}` |
|
||||
| `GET /api/devices` | X-Api-Key | Device list with battery/status/location |
|
||||
| `POST /api/alert` | X-Api-Key | Ring a device `{"device":"name"}` (optional) |
|
||||
| `GET /api/location?device=` | X-Api-Key | Reverse-geocoded address |
|
||||
|
||||
## First run (one time)
|
||||
|
||||
1. Install with your Apple ID + password.
|
||||
2. `GET /api/status` shows `mfa_required`; a code push arrives on your Apple devices.
|
||||
3. `curl -X POST https://<domain>/api/mfa -H 'X-Api-Key: <key>' -H 'Content-Type: application/json' -d '{"code":"123456"}'`
|
||||
4. Trust token persists in the app volume - no more codes needed.
|
||||
|
||||
Source: https://git.alexzaw.dev/alexz/find-my-iphone
|
||||
Reference in New Issue
Block a user