logging_path is now manual, automatic_flyapp or automatic_flyagentapp. The Fly App's entries are backed by the aircraft's flight recorder and keep the missing-FDR red flag; the Fly Agent App's come from Remote ID, which is not a flight recorder, so they must carry the § 5 fields like a manual entry. The API maps the old "automatic" onto automatic_flyapp so Fly App builds already on devices keep filing. The migration has been applied to the remote PocketBase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66 lines
3.3 KiB
Markdown
66 lines
3.3 KiB
Markdown
# PilotVault Fly Agent
|
||
|
||
Android companion app that runs **next to DJI Fly**. DJI Fly flies the aircraft;
|
||
the Agent listens to the aircraft's **Remote ID** broadcast, streams the paired
|
||
aircraft's position to PilotVault, and files the flight in the logbook. It uses
|
||
no DJI SDK and never sends a command, so it works with every DJI Fly aircraft
|
||
(Mavic 4 Pro, Air 3S, Mini 5 Pro, …) as long as Remote ID is broadcasting.
|
||
|
||
Plan and research: `REA/DJI Fly/PilotVault_FlyAgentApp_Plan.md` (outside this repo).
|
||
Built so far: phases **A0–A3** (login, Remote ID receiver, live uplink, flight
|
||
session → logbook). Not yet: RTMP video (A4), flight-record import (A5), media
|
||
metadata (A6), missions to DJI Fly (A7).
|
||
|
||
## Build
|
||
|
||
```bash
|
||
flutter build apk --debug
|
||
```
|
||
|
||
`build/app/outputs/flutter-apk/app-debug.apk`, package `com.pilotvault.flyagent`.
|
||
|
||
- `permission_handler` is pinned to 12.x: 13.x needs compileSdk 37.
|
||
- `kotlin.incremental=false` in `android/gradle.properties`: the project and the
|
||
pub cache are on different drives, which breaks Kotlin's incremental caches.
|
||
|
||
## How it fits
|
||
|
||
| Piece | Where |
|
||
|---|---|
|
||
| Remote ID scanners (BLE legacy + BT5, Wi-Fi NaN, Wi-Fi Beacon) in a foreground service | `android/.../flyagent/*.kt` |
|
||
| OpenDroneID decoder (ASTM F3411 / EN 4709-002) | `lib/rid/odid_decoder.dart` |
|
||
| Frames → aircraft (merged by serial across transports), pairing | `lib/rid/rid_aircraft.dart`, `rid_service.dart` |
|
||
| Live uplink to the hub, `/ws/device?id=agent-<serial>` | `lib/uplink/uplink.dart` |
|
||
| Session state machine (armed → active → landed), auto start/stop | `lib/session/flight_session.dart` |
|
||
| Filing to `POST /api/flights`, offline queue | `lib/session/session_controller.dart` |
|
||
|
||
The scanners only strip transport framing; all decoding is in Dart and unit
|
||
tested against byte-built messages (`test/`).
|
||
|
||
**API Server contract.** The only server change is the logging-path split:
|
||
|
||
- Live map: the hub already ingests `telemetry` events (`latitude`, `longitude`,
|
||
`altitude` = height above take-off, `velocityX/Y/Z` = north/east/down,
|
||
`isFlying`, `phoneLatitude/Longitude`). The Agent adds `source: "remote-id"`,
|
||
`ridSerial`, `pilotLatitude/Longitude`, `uaClass`, which ride along in the raw
|
||
event. It never sends `flightControllerSerial`, so the Web App's fleet
|
||
auto-add does not mint a drone from a Remote ID serial.
|
||
- Logbook: entries are filed with `loggingPath: "automatic_flyagentapp"`
|
||
(migration `1720301200_split_logging_path.js`; the paths are `manual`,
|
||
`automatic_flyapp`, `automatic_flyagentapp`). Remote ID is not the aircraft's
|
||
own flight recorder, so the API Server still requires every § 5 field on this
|
||
path, and the session fills them all in.
|
||
- Pairing lives on the phone (SharedPreferences), not on the drone record; a
|
||
`remote_id_serial` drone field is a later server change.
|
||
|
||
**Privacy.** Every aircraft in range is decoded, but only the paired one is
|
||
uploaded or logged. Other operators' broadcasts stay in memory on the phone.
|
||
|
||
## Limits worth knowing
|
||
|
||
- Wi-Fi Beacon Remote ID needs Android 11+ and Android throttles Wi-Fi scans to
|
||
4 per 2 minutes (turn off "Wi-Fi scan throttling" in Developer options).
|
||
An aircraft that only broadcasts over Beacon updates every ~30 s.
|
||
- Not yet checked on a real aircraft: which transport each DJI Fly aircraft uses
|
||
(research R1), and NaN on the SHT-AL09 tablet (Android 9: no Beacon IEs).
|