From 3adbfc46e9ca2a77e412b5c21836a3cdf98b7df4 Mon Sep 17 00:00:00 2001 From: aaronspruit Date: Fri, 10 Mar 2023 16:22:52 -0800 Subject: [PATCH] added refactored google timeline import script --- contrib/google-import/README.md | 12 ++++ contrib/google-import/google-import.py | 96 ++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 contrib/google-import/README.md create mode 100644 contrib/google-import/google-import.py diff --git a/contrib/google-import/README.md b/contrib/google-import/README.md new file mode 100644 index 0000000..6a1874a --- /dev/null +++ b/contrib/google-import/README.md @@ -0,0 +1,12 @@ +# Google Location History Importer +Reads a JSON export of Google Location History from +[Google Takeout](https://takeout.google.com/settings/takeout) and publishes all of its locations +to MQTT. Useful for importing Google Location History into OwnTracks Recorder. + +## How to use +1. Ensure you have python3 installed +1. `pip install paho.mqtt` +1. `python3 import-google.py` for basic instructions. + * 'filename' is the records.json in the takeout zip file + * 'topic' default is "owntracks//" + * 'tid' is the 2 letter tracker ID \ No newline at end of file diff --git a/contrib/google-import/google-import.py b/contrib/google-import/google-import.py new file mode 100644 index 0000000..cb92c54 --- /dev/null +++ b/contrib/google-import/google-import.py @@ -0,0 +1,96 @@ +## original script by smmr0 as found in the deprecated owncloud/tools repo +## updated to make tid value configurable +## updated to work with new takeout json format + +#!/usr/bin/env python3 +from datetime import datetime +import time +import argparse +import json +from paho.mqtt import client, publish + +class ProtocolAction(argparse.Action): + def __call__(self, parser, namespace, value, option_string=None): + setattr(namespace, self.dest, getattr(client, value)) + +## Custom type for tid argument to allow only 2 letters +def tid_type(arg_value, pat=re.compile(r"^[A-Za-z]{2}$")): + if not pat.match(arg_value): + raise argparse.ArgumentTypeError("invalid value, must be 2 letters (e.g. 'aa')") + return arg_value + + +parser = argparse.ArgumentParser(description='Import Google Location History into OwnTracks') +parser.add_argument('-H', '--host', default='localhost', help='MQTT host (localhost)') +parser.add_argument('-p', '--port', type=int, default=1883, help='MQTT port (1883)') +parser.add_argument('--protocol', action=ProtocolAction, default=client.MQTTv31, help='MQTT protocol (MQTTv31)') +parser.add_argument('--cacerts', help='Path to files containing trusted CA certificates') +parser.add_argument('--cert', help='Path to file containing TLS client certificate') +parser.add_argument('--key', help='Path to file containing TLS client private key') +parser.add_argument('--tls-version', help='TLS protocol version') +parser.add_argument('--ciphers', help='List of TLS ciphers') +parser.add_argument('-u', '--username', help='MQTT username') +parser.add_argument('-P', '--password', help='MQTT password') +parser.add_argument('-i', '--clientid', help='MQTT client-ID') +parser.add_argument('-t', '--topic', required=True, help='MQTT topic (e.g. owntracks//)') +parser.add_argument('--tid', required=True, type=tid_type, help="Two letter Tracker ID you want these associated to (e.g. 'aa')") +parser.add_argument('filename', help='Path to file containing JSON-formatted data from Google Location History exported by Google Takeout') +args = parser.parse_args() + +messages = [] +with open(args.filename) as lh: + lh_data = json.load(lh) + for location in lh_data['locations']: + location_keys = location.keys() + payload = { + '_type': 'location' + } + payload['tid'] = args.tid + if 'timestamp' in location_keys: + #some of the google timestamps did not include milliseconds. + time_format = "%Y-%m-%dT%H:%M:%S.%fZ" if '.' in location['timestamp'] else "%Y-%m-%dT%H:%M:%SZ" + payload['tst'] = int(time.mktime(datetime.strptime(location['timestamp'], time_format).timetuple())) + if 'latitudeE7' in location_keys: + payload['lat'] = location['latitudeE7'] / 10000000 + if 'longitudeE7' in location_keys: + payload['lon'] = location['longitudeE7'] / 10000000 + if 'accuracy' in location_keys: + payload['acc'] = location['accuracy'] + if 'altitude' in location_keys: + payload['alt'] = location['altitude'] + messages.append( + { + 'topic': args.topic, + 'payload': json.dumps(payload), + 'qos': 2 + } + ) + del lh_data + +if args.username != None: + auth={ + 'username': args.username, + 'password': args.password + } +else: + auth = None + +if args.cacerts != None: + tls = { + 'ca_certs': args.cacerts, + 'certfile': args.cert, + 'keyfile': args.key, + 'tls_version': args.tls_version, + 'ciphers': args.ciphers + } +else: + tls = None + +publish.multiple( + messages, + hostname=args.host, + port=args.port, + client_id=args.clientid, + auth=auth, + tls=tls +)