Skip to content

Instantly share code, notes, and snippets.

@webratz
Created July 17, 2024 13:22
Show Gist options
  • Save webratz/ff95663fe9aa159231ae1e26e54b383e to your computer and use it in GitHub Desktop.
Save webratz/ff95663fe9aa159231ae1e26e54b383e to your computer and use it in GitHub Desktop.
Suunto Android App extractor

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.

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"]))
[[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