Skip to content

Latest commit

 

History

141 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DroneOpsSync

Android companion app for DroneOpsCommand — syncs DJI flight logs from smart controllers directly into the DroneOpsCommand Flight Management System.

Part of the DroneOps platform. No separate server required — DroneOpsCommand IS the backend.


Overview

DroneOpsSync runs on your DJI smart controller (sideloaded APK). It scans the controller's local storage for flight log files and uploads them in a single batch to your DroneOpsCommand instance over plain HTTP — either directly on your local network, or remotely via WireGuard VPN. Once confirmed on the server, logs can be deleted from the controller with a single tap.


Prerequisites

  1. DroneOpsCommand deployed and running (see DroneOpsCommand)
  2. Device API key generated in DroneOpsCommand → Settings → Device Access
  3. Network access to the server — LAN (direct IP) or WireGuard VPN for remote

Network Setup

On the same LAN (most common)

The controller connects directly to the server's local IP over HTTP. No tunnel, no TLS, no extra config. Just make sure both devices are on the same network and the server port is reachable.

DJI Controller → http://192.168.1.50:8080

Remote access via WireGuard

Connect the controller to your WireGuard VPN before uploading. The app then talks to the server's VPN IP exactly like LAN — plain HTTP, no certificate issues.

DJI Controller → WireGuard VPN → http://10.8.0.1:8080

WireGuard has an official Android APK that runs on Android 8+, compatible with all DJI smart controllers.


Setup

Step 1 — Generate a Device API Key

In your DroneOpsCommand web UI:

  1. Go to Settings → Device Access
  2. Copy the auto-generated Device API Key

Step 2 — Build & Sideload the APK

cd android
./gradlew assembleRelease
# APK at: app/build/outputs/apk/release/app-release.apk

Sideload the APK onto each DJI smart controller via ADB or a file manager.

Step 3 — Configure the App

Open DroneOpsSync on the controller, tap the Settings (gear) icon, and enter:

Field Value
DroneOpsCommand URL Local IP (e.g. http://192.168.1.50:8080) or WireGuard VPN IP
Device API Key The key copied from DroneOpsCommand Settings

Tap Save. The status indicator will turn green when the server is reachable.

Step 4 — Grant Flight Log Folder (RC Pro 2 / Android 11+ only)

On the DJI RC Pro 2 (stock-AOSP Android 11) and any other Android 11+ controller, the OS sandboxes Android/data/<other-pkg> directories — MANAGE_EXTERNAL_STORAGE does not grant access to them. DroneOpsSync uses the system Storage Access Framework (SAF) picker as a one-time grant per install.

On first launch, the home screen shows a "DJI Fly logs need a one-time folder grant" banner. Tap it, and the SAF picker opens pre-seeded at the DJI flight-record folder. Tap Use this folder to grant.

The grant is persisted across launches with both read and write access — read for scanning, write for delete-after-sync to actually remove the original from the controller. See ADR-0005 and ADR-0006.

If a delete ever reports failure, open Diagnostics and look at the [DELETE] channel — it now names the exact provider response.


Sync Flow

  1. SCAN FOR LOGS — finds .txt / .log files in all configured paths
  2. SYNC ALL — uploads all pending logs to DroneOpsCommand. The server parses them, deduplicates by hash, and imports into the Flight Library. Each file is uploaded and tracked independently, so a slow or failed file never blocks the rest of the sortie.
  3. DELETE — after confirmation, removes synced files from the controller only

Files are never deleted automatically — explicit confirmation is always required.

Upload reliability — async ingest + per-file isolation

The upload path is resilient to slow field connections and slow server-side log parses:

  • Per-file isolation. A SocketTimeoutException on one file fails only that file and the batch continues. Only genuinely batch-wide conditions abort the run: an unreachable host (UnknownHostException) or a bad device key (HTTP 401/403). Failed files stay as ERROR cards you can long-press or swipe to retry.
  • Async upload (202 + poll). When the server advertises async_upload_available on the device-health preflight, the client POSTs to …/device-upload/async, receives a 202 Accepted with a batch_id (the connection is released as soon as the bytes are uploaded — the parse no longer happens in-request), then polls …/device-upload/status/{batch_id} until each file reaches a terminal state. SHA-256 dedup short-circuits files already on the server with no poll.
  • Graceful fallback. async_upload_available defaults off, so a new APK against an older/legacy DroneOpsCommand transparently uses the unchanged synchronous upload path. Closing the app mid-poll is safe — the job completes server-side and dedup reconciles on the next launch.

Pairs with DroneOpsCommand v2.71.0. See ADR-0008 (client) and DroneOpsCommand ADR-0023 (the canonical cross-repo contract). Live progress is visible in Diagnostics → [UPLOAD] channel (async submit HTTP code, batch_id, poll status).


Default Scan Paths

Pre-configured in the app; additional paths can be added in Settings.

Controller / Device DJI App Path
RC Plus 2 (4TD), RC Plus (M30T) DJI Pilot 2 /storage/emulated/0/DJI/com.dji.industry.pilot/FlightRecord
RC Pro (M3P), RC 2 (M5P) DJI GO 5 /storage/emulated/0/Android/data/dji.go.v5/files/FlightRecord
Phone (DJI GO 5) DJI GO 5 /storage/emulated/0/Android/data/dji.go.v5/files/FlightRecord
Phone (DJI Fly) DJI Fly /storage/emulated/0/Android/data/com.dji.fly/files/FlightRecord

Upload Status Reference

Badge Meaning
PENDING Found locally, not yet uploaded
SYNCING Upload in progress
SYNCED Confirmed in DroneOpsCommand Flight Library
ON SERVER Already existed on server (deduplicated)
ERROR Upload or parse failed — check connection/key
DELETED Removed from controller after sync

Backend Integration

DroneOpsSync talks directly to DroneOpsCommand over plain HTTP. There is no middleware, relay server, or tunnel required. Endpoints used:

Endpoint Method Purpose
/api/flight-library/device-health GET Preflight — reachability, device-key validity, key-rotation hint, and the async_upload_available capability flag
/api/flight-library/device-upload POST Legacy synchronous upload (parses in-request) — fallback path when the server does not advertise async support
/api/flight-library/device-upload/async POST Async upload — streams bytes, returns 202 + {batch_id}, parses off-request
/api/flight-library/device-upload/status/{batch_id} GET Status poll for an async batch (per-file state)

The client capability-detects: if the preflight reports async_upload_available: true, it uses the async route; otherwise it falls back to the synchronous endpoint.

The DroneOpsCommand stack includes:

  • flight-parser — Rust service that parses DJI .txt log format
  • PostgreSQL — persistent flight record storage

Logo

Place your logo PNG at:

android/app/src/main/res/drawable/droneops_sync_logo.png

Recommended size: 1024 × 410 px on a transparent background.


License

MIT

About

Android companion app for DroneOpsCommand — syncs DJI flight logs from smart controllers to self-hosted server via Cloudflare tunnel. Sideloaded APK for DJI RC controllers.

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages