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') ..addOption('continue', abbr: 'c', help: 'The number of record to continue import from (Starting from 1)') ..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 '); print(argParser.usage); } void main(List 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 failedList = []; final rawContinueFrom = results.option('continue'); final continueFrom = rawContinueFrom == null ? 1 : int.parse(rawContinueFrom); int n = 1; if (continueFrom > recordsList.length) { throw FormatException('There are only ${recordsList.length} in the list'); } for (final record in recordsList) { if (n < continueFrom) { if (verbose) { print(' '); print('Record $n of ${recordsList.length} SKIPPED'); } n++; continue; } if (verbose) { print(' '); print('Record $n of ${recordsList.length}'); } final Map 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']; double? batt = recordProps['battery'] != null ? double.tryParse(recordProps['battery'].toString()) : null; if (batt != null && batt <= 1) { batt = batt * 100; } else if (batt != null && batt > 100) { batt = batt / 100; } payload['batt'] = batt?.toInt(); payload['bs'] = recordProps['battery_status']; payload['inregions'] = recordProps['in_regions']; payload['SSID'] = recordProps['ssid']; payload['conn'] = recordProps['connection']; if (verbose) { 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('...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); } }