From 772942f66ba03c35b1f8792b7c7741f3b8605886 Mon Sep 17 00:00:00 2001 From: Markus Birth Date: Thu, 26 Jun 2025 01:32:18 +0100 Subject: [PATCH] Initial import Signed-off-by: Markus Birth --- README.md | 33 +++++++++++ convert.py | 117 +++++++++++++++++++++++++++++++++++++ post-converted.sh | 11 ++++ traccar-export-example.csv | 20 +++++++ 4 files changed, 181 insertions(+) create mode 100644 README.md create mode 100755 convert.py create mode 100755 post-converted.sh create mode 100644 traccar-export-example.csv diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d98187 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +Traccar to DaWarIch Converter +============================= + + +Step 1 +------ + +Use e.g. [Adminer](https://www.adminer.org/en/) to export your [Traccar](https://www.traccar.org)'s `tc_points` +table into a **CSV** file. + + + +Step 2 +------ + +Change line #4 in `convert.py` to the filename of the CSV file you've just exported. +And put both into the same directory. + + +Step 3 +------ + +Run: + +``` +./convert.py > dawarich.sql +``` + + +Step 4 +------ + +Import the resulting `dawarich.sql` into your [DaWarIch](https://dawarich.app) database. diff --git a/convert.py b/convert.py new file mode 100755 index 0000000..5f7d09d --- /dev/null +++ b/convert.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +# Export traccar tc_positions table as CSV (e.g. using Adminer) +FILE="traccar-export-example.csv" + + +import csv +import json +import sys +from datetime import datetime + +# traccar_id --> topic +tcid_map = { + 1: 'owntracks/mb/iPhone 13 Pro', + 2: 'owntracks/mb/iPhone XS', + 3: 'owntracks/mb/iPhone 6S', + 4: 'Google Latitude', + 5: 'owntracks/mb/iPhone 16 Pro' +} + +with open(FILE, "rt", encoding="utf-8-sig") as f: + csvreader = csv.DictReader(f) + i = 0 + for row in csvreader: + attrs = json.loads(row["attributes"]) + + # OwnTracks format: https://owntracks.org/booklet/tech/json/#_typelocation + owntr = { + "_type": "location", + "acc": int(row["accuracy"]), + "alt": int(row["altitude"]), + "bs": 1, # 0=unknown, 1=unplugged, 2=charging, 3=full + "lat": float(row["latitude"]), + "lon": float(row["longitude"]), + "t": "p", # p=ping, u=manual, t=timer, etc. + "tid": "MB", + "tst": int(datetime.strptime(row["fixtime"], "%Y-%m-%d %H:%M:%S").timestamp()), + "topic": tcid_map[int(row["deviceid"])], + } + if "batteryLevel" in attrs: + owntr["batt"] = int(attrs["batteryLevel"]) + + if float(row["speed"]) > 0: + owntr["vel"] = float(row["speed"]) + owntr["cog"] = int(row["course"]) + + #print("### Source data:") + #print(repr(row)) + + #print("### Output:") + #print(json.dumps(owntr)) + + dwirec = { + "battery_status": 1, + "ping": None, + "battery": owntr["batt"] if "batt" in owntr else None, + "tracker_id": owntr["tid"], + "topic": owntr["topic"], + "altitude": owntr["alt"], + "longitude": owntr["lon"], + "velocity": str(owntr["vel"]) if "vel" in owntr else None, + "trigger": 0, + "bssid": None, + "ssid": None, + "connection": 0, + "vertical_accuracy": None, + "accuracy": owntr["acc"], + "timestamp": owntr["tst"], + "latitude": owntr["lat"], + "mode": None, + "inrids": None, + "in_regions": None, + "raw_data": json.dumps(owntr), + "import_id": None, + "city": None, + "country" :None, + "created_at": row["fixtime"], + "updated_at": row["fixtime"], + "user_id": 1, + "geodata": "{}", + "visit_id": None, + "reverse_geocoded_at": None, + "course": owntr["cog"] if "cog" in owntr else None, + "course_accuracy": None, + "external_track_id": None, + } + + if i%10 == 0: + if i>0: + print(";") + keys = dwirec.keys() + keystring = '"' + '", "'.join(keys) + '"' + print(f'INSERT INTO "points" ({keystring}) VALUES ') + elif i>0: + print(",") + + values = [] + for k in keys: + v = dwirec[k] + if type(v) is str: + if "'" in v: + v = v.replace("'", "\\'") + values.append("'" + v + "'") + elif v is None: + values.append("NULL") + else: + values.append(str(v)) + + valuestring = ", ".join(values) + print(f"({valuestring})", end="") + + i += 1 + #if i > 10: + # break + +print(";") +print(f"{f} records converted.", file=sys.stderr) diff --git a/post-converted.sh b/post-converted.sh new file mode 100755 index 0000000..496818d --- /dev/null +++ b/post-converted.sh @@ -0,0 +1,11 @@ +#!/bin/sh +DWI_HOST=dawarich.example.org +DWI_APIKEY=0123456789abcdef +cat converted.txt | while read line; do + #echo $line + curl "https://${DWI_HOST}/api/v1/owntracks/points?api_key=${DWI_APIKEY}" \ + --retry 99 \ + --retry-delay 5 \ + --retry-all-errors \ + --json "$line" +done diff --git a/traccar-export-example.csv b/traccar-export-example.csv new file mode 100644 index 0000000..7ea3f59 --- /dev/null +++ b/traccar-export-example.csv @@ -0,0 +1,20 @@ +id,protocol,deviceid,servertime,devicetime,fixtime,valid,latitude,longitude,altitude,speed,course,address,attributes,accuracy,network,geofenceids +1,owntracks,1,2023-12-24 18:47:47,2023-12-24 18:47:26,2023-12-24 18:47:26,1,52.502427,13.251705,7,"0","0",,"{""tid"":""1"",""batteryLevel"":64,""distance"":0.0,""totalDistance"":0.0,""motion"":false}",5,null,null +2,owntracks,1,2023-12-24 19:02:57,2023-12-24 19:02:48,2023-12-24 19:02:48,1,52.502427,13.251705,7,"0","0",,"{""tid"":""1"",""batteryLevel"":64,""distance"":0.0,""totalDistance"":0.0,""motion"":false}",5,null,null +3,owntracks,1,2023-12-24 19:03:27,2023-12-24 19:03:26,2023-12-24 19:03:26,1,52.502427,13.251705,7,"0","0",,"{""tid"":""1"",""batteryLevel"":64,""distance"":0.0,""totalDistance"":0.0,""motion"":false}",5,null,null +4,owntracks,1,2023-12-24 19:25:31,2023-12-24 19:25:24,2023-12-24 19:25:24,1,52.502427,13.251705,6,"0","0",,"{""tid"":""1"",""batteryLevel"":64,""distance"":0.0,""totalDistance"":0.0,""motion"":false}",5,null,null +5,owntracks,1,2023-12-24 19:56:45,2023-12-24 19:56:44,2023-12-24 19:56:44,1,52.502448,13.251687,6,"0","0",,"{""tid"":""1"",""batteryLevel"":55,""distance"":2.6496482557134002,""totalDistance"":2.6496482557134002,""motion"":false}",35,null,null +6,owntracks,1,2023-12-24 23:08:36,2023-12-24 23:08:36,2023-12-24 23:08:36,1,52.50243,13.25171,7,"0","0",,"{""tid"":""1"",""batteryLevel"":20,""distance"":2.5602962697448706,""totalDistance"":5.209944525458271,""motion"":false}",5,null,null +7,owntracks,1,2023-12-25 11:38:54,2023-12-25 11:38:53,2023-12-25 11:38:53,1,52.501312,13.252581,6,2.69978,233,,"{""tid"":""1"",""t"":""v"",""batteryLevel"":85,""distance"":138.31830028323412,""totalDistance"":143.5282448086924,""motion"":true}",5,null,null +8,owntracks,1,2023-12-25 11:46:30,2023-12-25 11:46:29,2023-12-25 11:46:29,1,52.498118,13.254032,-8,2.69978,159,,"{""tid"":""1"",""batteryLevel"":85,""distance"":369.49923295378073,""totalDistance"":513.0274777624732,""motion"":true}",10,null,null +9,owntracks,1,2023-12-25 11:51:14,2023-12-25 11:51:13,2023-12-25 11:51:13,1,52.495697,13.254832,7,"0",74,,"{""tid"":""1"",""t"":""v"",""batteryLevel"":85,""distance"":275.14814739262914,""totalDistance"":788.1756251551024,""motion"":false}",5,null,null +10,owntracks,1,2023-12-25 11:51:15,2023-12-25 11:46:29,2023-12-25 11:46:29,1,52.498118,13.254032,-8,2.69978,159,,"{""tid"":""1"",""batteryLevel"":85,""distance"":275.14814739262914,""totalDistance"":1063.3237725477316,""motion"":true}",10,null,null +11,owntracks,1,2023-12-25 13:54:15,2023-12-25 13:54:14,2023-12-25 13:54:14,1,52.493614,13.255329,6,"0","0",,"{""tid"":""1"",""batteryLevel"":75,""distance"":234.42292307499253,""totalDistance"":1022.5985482300948,""motion"":false}",40,null,null +12,owntracks,1,2023-12-25 13:58:56,2023-12-25 13:58:55,2023-12-25 13:58:55,1,52.493439,13.25523,7,"0","0",,"{""tid"":""1"",""t"":""v"",""batteryLevel"":75,""distance"":20.65395275868584,""totalDistance"":1043.2525009887806,""motion"":false}",35,null,null +13,owntracks,1,2023-12-25 13:58:57,2023-12-25 13:54:14,2023-12-25 13:54:14,1,52.493614,13.255329,6,"0","0",,"{""tid"":""1"",""batteryLevel"":75,""distance"":20.65395275868584,""totalDistance"":1063.9064537474665,""motion"":false}",40,null,null +14,owntracks,1,2023-12-25 14:00:01,2023-12-25 14:00:00,2023-12-25 14:00:00,1,52.493683,13.255418,2,2.69978,348,,"{""tid"":""1"",""t"":""v"",""batteryLevel"":69,""distance"":30.125558440026264,""totalDistance"":1073.3780594288069,""motion"":true}",4,null,null +15,owntracks,1,2023-12-25 14:00:02,2023-12-25 13:54:14,2023-12-25 13:54:14,1,52.493614,13.255329,6,"0","0",,"{""tid"":""1"",""batteryLevel"":69,""distance"":9.851263572220656,""totalDistance"":1083.2293230010275,""motion"":false}",40,null,null +16,owntracks,1,2023-12-25 14:13:26,2023-12-25 14:13:26,2023-12-25 14:13:26,1,52.497498,13.253328,5,"0","0",,"{""tid"":""1"",""t"":""v"",""batteryLevel"":69,""distance"":448.7059070673693,""totalDistance"":1522.0839664961761,""motion"":false}",40,null,null +17,owntracks,1,2023-12-25 14:13:27,2023-12-25 13:54:14,2023-12-25 13:54:14,1,52.493614,13.255329,6,"0","0",,"{""tid"":""1"",""batteryLevel"":69,""distance"":454.06082222124735,""totalDistance"":1976.1447887174236,""motion"":false}",40,null,null +18,owntracks,1,2023-12-25 14:13:54,2023-12-25 14:13:53,2023-12-25 14:13:53,1,52.497904,13.253075,-1,"0","0",,"{""tid"":""1"",""batteryLevel"":69,""distance"":48.47750466076835,""totalDistance"":1570.5614711569444,""motion"":false}",40,null,null +19,owntracks,1,2023-12-25 14:20:18,2023-12-25 14:20:18,2023-12-25 14:20:18,1,52.500702,13.252914,7,1.61987,1,,"{""tid"":""1"",""t"":""v"",""batteryLevel"":69,""distance"":311.6716997925153,""totalDistance"":1882.2331709494597,""motion"":true}",5,null,null