A Flutter app that runs next to DJI Fly without the DJI SDK. It receives the aircraft's Remote ID (Bluetooth, BT5, Wi-Fi NaN and Beacon) in a foreground service, streams the paired aircraft to the hub over /ws/device, and turns take-off to landing into a logbook entry filed through /api/flights, queued when offline. Covers phases A0-A3 of the Fly Agent plan; needs no server change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
1.7 KiB
Dart
55 lines
1.7 KiB
Dart
// Display formatting shared by the screens. Unknown values render as "—"
|
|
// rather than 0, so a missing Remote ID field never reads as a real reading.
|
|
|
|
String fmtMeters(double? m, {int digits = 0}) => m == null ? '—' : '${m.toStringAsFixed(digits)} m';
|
|
|
|
String fmtDistance(double? m) {
|
|
if (m == null) return '—';
|
|
if (m < 1000) return '${m.round()} m';
|
|
return '${(m / 1000).toStringAsFixed(2)} km';
|
|
}
|
|
|
|
String fmtSpeed(double? ms) => ms == null ? '—' : '${ms.toStringAsFixed(1)} m/s';
|
|
|
|
String fmtDuration(Duration d) {
|
|
final int h = d.inHours;
|
|
final String m = (d.inMinutes % 60).toString().padLeft(2, '0');
|
|
final String s = (d.inSeconds % 60).toString().padLeft(2, '0');
|
|
return h > 0 ? '$h:$m:$s' : '$m:$s';
|
|
}
|
|
|
|
String fmtAgo(DateTime t, [DateTime? now]) {
|
|
final Duration d = (now ?? DateTime.now()).difference(t);
|
|
if (d.inSeconds < 2) return 'now';
|
|
if (d.inSeconds < 60) return '${d.inSeconds} s ago';
|
|
if (d.inMinutes < 60) return '${d.inMinutes} min ago';
|
|
return '${d.inHours} h ago';
|
|
}
|
|
|
|
String fmtClock(DateTime t) {
|
|
final DateTime l = t.toLocal();
|
|
return '${l.hour.toString().padLeft(2, '0')}:${l.minute.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
const Map<String, String> purposeLabels = <String, String>{
|
|
'commercial': 'Commercial',
|
|
'hobby': 'Hobby',
|
|
'research': 'Research',
|
|
'public': 'Public service',
|
|
'club_area': 'Club area',
|
|
};
|
|
|
|
const Map<String, String> categoryLabels = <String, String>{
|
|
'open': 'Open',
|
|
'specific': 'Specific',
|
|
'certified': 'Certified',
|
|
};
|
|
|
|
String transportLabel(String t) => switch (t) {
|
|
'ble' => 'Bluetooth',
|
|
'ble5' => 'Bluetooth 5 LR',
|
|
'nan' => 'Wi-Fi NaN',
|
|
'beacon' => 'Wi-Fi Beacon',
|
|
_ => t,
|
|
};
|