owntracks-import/bin/owntracks_import.dart

188 lines
4.4 KiB
Dart
Raw Normal View History

2025-02-04 13:38:17 +02:00
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:args/args.dart';
const String version = '0.0.1';
ArgParser buildParser() {
return ArgParser()
..addOption('source', abbr: 's', help: 'Source GeoJSON file')
..addOption('destination',
abbr: 'd', help: 'OwnTracks Recorder server address')
..addOption('user', abbr: 'u', help: 'User for OwnTrack record')
..addFlag(
'help',
abbr: 'h',
negatable: false,
help: 'Print this usage information.',
)
..addFlag(
'verbose',
abbr: 'v',
negatable: false,
help: 'Show additional command output.',
)
..addFlag(
'no-limits',
abbr: 'n',
defaultsTo: false,
help:
'Disable 2 seconds delay to not hit reverse geocoding API rate limits.',
)
..addFlag(
'version',
negatable: false,
help: 'Print the tool version.',
);
}
void printUsage(ArgParser argParser) {
print('Usage: dart owntracks_import.dart <options> <flags>');
print(argParser.usage);
}
void main(List<String> arguments) async {
final ArgParser argParser = buildParser();
try {
final ArgResults results = argParser.parse(arguments);
bool verbose = false;
if (results.wasParsed('help')) {
printUsage(argParser);
return;
}
if (results.wasParsed('version')) {
print('owntracks_import version: $version');
return;
}
if (results.wasParsed('verbose')) {
verbose = true;
}
final sourceFilePath = results.option('source');
if (sourceFilePath == null) {
throw FormatException('No source file specified');
}
final rawData = await File(sourceFilePath).readAsString();
final data = jsonDecode(rawData);
final recordsList = data['features'];
final List<dynamic> failedList = [];
int n = 1;
for (final record in recordsList) {
final Map<String, dynamic> payload = {'_type': 'location', 't': 'u'};
final recordProps = record['properties'];
payload['tst'] = recordProps['timestamp'];
payload['lat'] = double.tryParse(recordProps['latitude']);
payload['lon'] = double.tryParse(recordProps['longitude']);
payload['acc'] = recordProps['accuracy'];
payload['alt'] = recordProps['altitude'];
payload['vac'] = recordProps['vertical_accuracy'];
payload['vel'] = recordProps['velocity'];
2025-02-06 14:35:35 +02:00
double? batt = recordProps['battery'] != null
? double.tryParse(recordProps['battery'].toString())
2025-02-04 13:38:17 +02:00
: null;
if (batt != null && batt <= 1) {
batt = batt * 100;
}
2025-02-06 14:35:35 +02:00
payload['batt'] = batt?.toInt();
2025-02-04 13:38:17 +02:00
payload['bs'] = recordProps['battery_status'];
payload['inregions'] = recordProps['in_regions'];
payload['SSID'] = recordProps['ssid'];
payload['conn'] = recordProps['connection'];
if (verbose) {
print(' ');
print('Record $n of ${recordsList.length}');
print(payload);
}
String device = recordProps['tracker_id'] ?? 'phone';
if (device.startsWith('device_tracker.')) {
device = device.replaceAll('device_tracker.', '');
}
final user = results.option('user');
final url = '${results.option('destination')}/pub?u=$user&d=$device';
if (verbose) {
print(' ');
print('...Sending to: $url');
}
final response = await http.post(
Uri.parse('${results.option('destination')}/pub?u=$user&d=$device'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(payload),
);
if (response.statusCode == 200) {
if (verbose) {
print('Success: ${response.body}');
}
} else {
print('Error sending record. ${response.statusCode}: ${response.body}');
failedList.add(payload);
}
if (!results.flag('no-limits')) {
if (verbose) {
print('Waiting 2 seconds to not hit API rate limit');
}
await Future.delayed(Duration(seconds: 2));
}
n++;
}
if (verbose) {
print('Finished all records.');
}
if (failedList.isNotEmpty) {
print('The list of failed records:');
print(failedList);
}
} on FormatException catch (e) {
// Print usage information if an invalid argument was provided.
print(e.message);
print('');
printUsage(argParser);
} on Exception catch (e) {
print(e);
}
}