Uses Suunto App debug data and app database (Android) to extract information from non synced workouts. This helps in case the app lost data on and you need to restore it.
Created
July 17, 2024 13:22
-
-
Save webratz/ff95663fe9aa159231ae1e26e54b383e to your computer and use it in GitHub Desktop.
Suunto Android App extractor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import json | |
import sqlite3 | |
from datetime import datetime | |
import polyline as pl | |
import gpxpy.gpx | |
from tabulate import tabulate | |
import zipfile | |
import math | |
con = sqlite3.connect("stt-dump/amer_app.db") | |
cur = con.cursor() | |
table = [] | |
res = cur.execute("SELECT id, totalDistance, activityId, startTime, totalTime, energyConsumption, totalAscent, totalDescent, polyline FROM workout_headers where key is null order by startTime") | |
for id, totalDistance, activityId, startTime, totalTime, energyConsumption, totalAscent, totalDescent, polyline in res.fetchall(): | |
start_time = datetime.utcfromtimestamp(startTime/1000).strftime('%Y-%m-%d %H:%M:%S') | |
sml_path = f"smlzip/entry_{id}_{str(startTime)[:-3]}.zip" | |
smlfile = os.path.isfile(sml_path) | |
if smlfile: | |
with zipfile.ZipFile(sml_path, 'r') as zip_open: | |
with zip_open.open("samples.json") as samples_files: | |
data = json.loads(samples_files.read()) | |
TRKPT = ('Latitude', 'Longitude', 'UTC') | |
gpx = gpxpy.gpx.GPX() | |
# -- create first track in our GPX: | |
gpx_track = gpxpy.gpx.GPXTrack() | |
gpx.tracks.append(gpx_track) | |
# -- create first segment in our GPX track: | |
gpx_segment = gpxpy.gpx.GPXTrackSegment() | |
gpx_track.segments.append(gpx_segment) | |
gpx.tracks[0].name = f"Activity SML from {start_time}" | |
for s in data["Samples"]: | |
if "Sample" in s["Attributes"]["suunto/sml"]: | |
sample = s["Attributes"]["suunto/sml"]["Sample"] | |
# See if we have enough for a trackpoint | |
if all(key in sample for key in TRKPT): | |
# Lat and long are radians | |
# To decimal degrees = * 180 / PI | |
lat = (sample["Latitude"] * 180) / math.pi | |
lon = (sample["Longitude"] * 180) / math.pi | |
# I'm assuming this is in meters, and we can use it as is | |
ele = sample['GPSAltitude'] if 'GPSAltitude' in sample else 0 | |
time = datetime.fromisoformat(sample['UTC']) | |
# Create the gpx point | |
point = gpxpy.gpx.GPXTrackPoint(latitude=lat, longitude=lon, | |
time=time, elevation=ele) | |
gpx_segment.points.append(point) | |
with open(f"activity-{startTime}.gpx", 'w') as f: | |
f.write(gpx.to_xml()) | |
elif polyline: | |
gpx = gpxpy.gpx.GPX() | |
# -- create first track in our GPX: | |
gpx_track = gpxpy.gpx.GPXTrack() | |
gpx.tracks.append(gpx_track) | |
# -- create first segment in our GPX track: | |
gpx_segment = gpxpy.gpx.GPXTrackSegment() | |
gpx_track.segments.append(gpx_segment) | |
gpx.tracks[0].name = f"Activity from {start_time}" | |
for lat, lon in pl.decode(polyline): | |
track_point = gpxpy.gpx.GPXTrackPoint( | |
latitude=lat, | |
longitude=lon | |
) | |
gpx_segment.points.append(track_point) | |
with open(f"activity-{startTime}.gpx", 'w') as f: | |
f.write(gpx.to_xml()) | |
print(startTime, start_time, bool(polyline)) | |
table.append([startTime, id, smlfile, start_time, totalDistance, activityId, totalTime, energyConsumption, totalAscent, totalDescent]) | |
print(tabulate(table, headers=["startTime", "id", "smlFile", "start_time", "totalDistance", "activityId", "totalTime", "energyConsumption", "totalAscent", "totalDescent"])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[[source]] | |
url = "https://pypi.org/simple" | |
verify_ssl = true | |
name = "pypi" | |
[packages] | |
polyline = "*" | |
gpxpy = "*" | |
tabulate = "*" | |
[dev-packages] | |
[requires] | |
python_version = "3.10" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment