How to Rebuild My OpenClaw Fitness Reports with Garmin, WHOOP, Speediance, and Public GitHub Projects
A reproducible implementation guide for building AI-readable fitness reports with OpenClaw, Garmin, WHOOP, Speediance, Cronometer, normalized JSON snapshots, and public GitHub repos.
How to Rebuild My OpenClaw Fitness Reports with Garmin, WHOOP, Speediance, and Public GitHub Projects
If you want to build a version of Aria’s fitness reports in your own OpenClaw setup, the key is not copying one private script tree. The key is reproducing the architecture with public projects, stable data snapshots, and connectors that can be swapped when vendors change their APIs.
This guide is the practical version of that build.
For an AI coding system, the core instruction is simple: treat each vendor as a connector, persist normalized JSON snapshots, and make the report generator consume only those snapshots. That separation is what makes the system reproducible instead of a pile of private scripts.
It focuses on the actual public building blocks behind the stack:
- Garmin for cardio, training load, pace, heart-rate zones, and activity detail
- WHOOP for recovery, HRV, RHR, sleep, strain, and readiness context
- Speediance for strength-session history, volume, calories, templates, and exercise detail
- Cronometer for nutrition context
- OpenClaw as the orchestration layer that runs syncs, normalizes data, and generates reports
It also covers the exact GitHub projects and public APIs that matter, what still works, what is deprecated, and how to wire everything into a report system another person can actually reproduce.
1. The architecture to copy
Do not build this as “one script that calls six APIs and renders HTML live.” That is fragile.
Build it in four layers:
- Connector layer
One sync job per platform. - Normalized data layer
Each sync writes stable JSON snapshots. - Analysis/report layer
Report code reads normalized JSON only. - Presentation layer
HTML, dashboards, daily briefings, Telegram posts, voice summaries.
That separation is what makes Aria-style reporting maintainable.
AI implementation contract
If you are giving this article to an AI coding agent, tell it to build this contract before it writes any dashboard UI:
- Inputs: environment variables and vendor credentials only; never hard-code secrets into source files.
- Sync outputs: one raw JSON snapshot per vendor pull, plus one normalized JSON file per vendor.
- Report inputs: normalized JSON only. The report builder should not call Garmin, WHOOP, Speediance, or Cronometer directly.
- Failure mode: if one connector fails, preserve yesterday’s last-known-good normalized file and mark that source as stale in the report.
- Auditability: keep enough raw payloads to debug vendor API changes without logging tokens, cookies, passwords, or private headers.
Minimum environment variables for a cloneable build:
GARMIN_EMAIL=
GARMIN_PASSWORD=
WHOOP_CLIENT_ID=
WHOOP_CLIENT_SECRET=
WHOOP_ACCESS_TOKEN=
WHOOP_REFRESH_TOKEN=
SPEEDIANCE_USER_ID=
SPEEDIANCE_TOKEN=
SPEEDIANCE_REGION=Global
CRONOMETER_EXPORT_PATH=
2. Public projects and sources used in the build
These are the public pieces that matter for a reproducible version.
Quick GitHub/API checklist:
- Garmin connector:
https://github.com/cyberjunky/python-garminconnect - Garmin legacy auth context:
https://github.com/matin/garth - WHOOP official developer API:
https://developer.whoop.com/api - Speediance public extraction for this build:
https://github.com/clawdassistant85-netizen/speediance-smartgym-workout-manager - Speediance working fork this was extracted from:
https://github.com/ANPC86/SmartGymWorkoutManager - Speediance upstream/original reference:
https://github.com/hbui3/UnofficialSpeedianceWorkoutManager
OpenClaw
- Platform/orchestrator: OpenClaw
- Role: schedule sync jobs, run transformations, generate reports, publish outputs
Garmin
- Primary Python client: cyberjunky/python-garminconnect
GitHub:https://github.com/cyberjunky/python-garminconnect - Legacy auth library that previously mattered: matin/garth
GitHub:https://github.com/matin/garth - Status note:
garthis deprecated;python-garminconnectnow uses newer Garmin auth flows and is the one to build around.
WHOOP
- Official developer API docs:
https://developer.whoop.com/api - Public API surface: OAuth2 + REST endpoints for recovery, cycles, sleep, workouts, profile, body measurements
- Optional community wrappers exist, but the reproducible build should anchor to the official WHOOP developer API wherever possible.
Speediance
- Actual GitHub repo used for Aria’s working Speediance connection: ANPC86/SmartGymWorkoutManager
GitHub:https://github.com/ANPC86/SmartGymWorkoutManager - Upstream project lineage / original public reference: hbui3/UnofficialSpeedianceWorkoutManager
GitHub:https://github.com/hbui3/UnofficialSpeedianceWorkoutManager - Aria’s stack is based on the ANPC86 SmartGymWorkoutManager fork because it contains practical fixes/features around history, exports, API debugging, timezone handling, and unit handling.
- This is an unofficial Speediance integration and should be treated as unstable by default.
Nutrition
- Cronometer product site / exports / integrations:
https://cronometer.com/ - For a public-reproducible build, treat Cronometer as a structured nutrition export source, not as a magic direct report dependency.
3. Garmin: the cardio and activity-detail layer
If WHOOP answers “how recovered am I?”, Garmin answers “what exactly did I do?”
Garmin is where the report gets:
- distance
- pace and speed
- duration
- average and max HR
- heart-rate zones
- cadence
- power
- training effect
- activity metadata
- broader cardio/training detail that WHOOP does not emphasize as richly
Public repo to use
Recommended: cyberjunky/python-garminconnect
GitHub:
https://github.com/cyberjunky/python-garminconnect
Why this matters:
- it is actively positioned as the Garmin Connect Python wrapper to use
- it exposes a very large Garmin endpoint surface
- it includes examples and token handling patterns
- it replaced older auth assumptions that broke in prior Garmin changes
Important Garmin compatibility note
Historically, many builds used garth.
GitHub:
https://github.com/matin/garth
But garth is now explicitly deprecated. That matters because anyone reading this to build their own stack should not center a new implementation on deprecated auth.
Minimal executable Garmin example
from garminconnect import Garmin
from datetime import date
from pathlib import Path
import json
import os
email = os.environ["GARMIN_EMAIL"]
password = os.environ["GARMIN_PASSWORD"]
client = Garmin(email=email, password=password)
client.login()
today = date.today().isoformat()
stats = client.get_stats(today)
activities = client.get_activities_by_date(today, today)
payload = {
"date": today,
"stats": stats,
"activities": activities,
}
out = Path("data/garmin/raw")
out.mkdir(parents=True, exist_ok=True)
(out / f"{today}.json").write_text(json.dumps(payload, indent=2))
What to normalize from Garmin
Do not dump raw Garmin payloads straight into your final report logic. Normalize them first into fields like:
calendarDatetotalStepsrestingHeartRatesleepingSecondsbodyBatteryactivityNameactivityTypedurationSecondsdistanceMetersdistanceMilesaverageHRmaxHRcaloriestrainingEffectcadencepower
Recommended storage pattern
data/garmin/raw/YYYY-MM-DD.jsondata/garmin/normalized/YYYY-MM-DD.jsondata/garmin/summary/latest.json
That gives you both replayability and fast report access.
4. WHOOP: the recovery and readiness layer
WHOOP is what makes the reports useful as a decision engine instead of just an activity log.
It contributes:
- recovery score
- HRV
- resting heart rate
- sleep performance
- strain
- cycle context
- readiness framing for morning and nightly recommendations
Public API to use
Use the official WHOOP developer API:
- Docs:
https://developer.whoop.com/api
Relevant endpoint groups:
/developer/v2/cycle/developer/v2/recovery/developer/v2/activity/sleep/developer/v2/activity/workout/developer/v2/user/profile/basic/developer/v2/user/measurement/body
Critical limitation
One of the most important implementation findings: journal data is not available from the WHOOP API.
If you want journal answers or habit annotations, you cannot rely on a public WHOOP API endpoint for that. The practical options are:
- manual CSV export from WHOOP
- your own parallel journaling layer
- separate metadata you attach after sync
That limitation should be stated plainly in the article because it affects any serious build.
Minimal executable WHOOP example
from pathlib import Path
import json
import os
import requests
BASE = "https://api.prod.whoop.com/developer/v2"
TOKEN = os.environ["WHOOP_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {TOKEN}"}
def get(path):
response = requests.get(f"{BASE}{path}", headers=headers, timeout=30)
response.raise_for_status()
return response.json()
payload = {
"recovery": get("/recovery"),
"sleep": get("/activity/sleep"),
"workouts": get("/activity/workout"),
}
out = Path("data/whoop/raw")
out.mkdir(parents=True, exist_ok=True)
(out / "latest.json").write_text(json.dumps(payload, indent=2))
What to normalize from WHOOP
Normalize into fields like:
recovery_scorehrv_rmssd_milliresting_heart_ratespo2_percentageskin_temp_celsiussleep_performance_percentagerespiratory_ratestraincycle_startcycle_endworkout_sport_name
Recommended storage pattern
data/whoop/raw/recovery.jsondata/whoop/raw/sleep.jsondata/whoop/raw/workouts.jsondata/whoop/normalized/latest.json
5. Speediance: the strength-training layer
Speediance is the most unusual connector in the whole stack.
Unlike Garmin and WHOOP, this is not a clean official public developer platform. The working connection pattern in Aria’s build is based on this public GitHub repo:
Public Aria extraction: https://github.com/clawdassistant85-netizen/speediance-smartgym-workout-manager
ANPC86/SmartGymWorkoutManager
GitHub: https://github.com/ANPC86/SmartGymWorkoutManager
That repo is itself a personal fork / continuation of the original public Speediance project:
hbui3/UnofficialSpeedianceWorkoutManager
GitHub: https://github.com/hbui3/UnofficialSpeedianceWorkoutManager
These repos are the important starting point because they show how to:
- authenticate against Speediance endpoints
- inspect workout data and API responses
- browse/export training history
- manage templates/workouts in a desktop-friendly way
- handle practical issues like timezone display and imperial/metric weight handling
Why these repos matter
Aria’s build uses the ANPC86 SmartGymWorkoutManager fork as the practical basis for the Speediance integration pattern, while preserving the hbui3 upstream reference for provenance. Together, they are the clearest public references for working with Speediance data outside the official app.
Stability warning
The original project notes that Speediance has been implementing security upgrades. That means:
- this integration can break
- headers and auth behavior can change
- endpoints can move
- you should isolate this connector behind a normalization step so your reports survive vendor-side churn
Minimal executable pattern for Speediance
If you use ANPC86/SmartGymWorkoutManager as your starting point, while checking the hbui3/UnofficialSpeedianceWorkoutManager upstream for original context, the clean approach is:
- Run its app or client layer locally
- Authenticate with your Speediance account
- Pull workout history from the API methods it exposes
- Export normalized JSON into your own data directory
Pseudo-example using that client pattern:
# Shape this around the api_client.py in:
# https://github.com/clawdassistant85-netizen/speediance-smartgym-workout-manager
from api_client import SpeedianceClient
from datetime import date
from pathlib import Path
import json
import os
client = SpeedianceClient()
success, msg, debug = client.login(
os.environ["SPEEDIANCE_USER_ID"],
os.environ["SPEEDIANCE_TOKEN"],
)
if not success:
raise RuntimeError(msg)
start_date = os.environ.get("SPEEDIANCE_START_DATE", "2026-01-01")
end_date = date.today().isoformat()
records = client.get_training_data(start_date, end_date)
out = Path("data/speediance/raw")
out.mkdir(parents=True, exist_ok=True)
(out / "history.json").write_text(json.dumps(records, indent=2))
What to normalize from Speediance
Normalize into fields like:
training_iddatetitleduration_secondscaloriestotal_volumeexercise_counttemplate_nameplanned_durationactual_durationexercise_breakdownestimated_1rm
Best-practice data model
For a serious report system, keep two indexes:
- bySession
- one record per completed workout
- byExercise
- one record stream per movement name
- includes weight, reps, side, session id, timestamp
That structure makes progression charts and PR detection trivial later.
Recommended storage pattern
data/speediance/raw/monthly/YYYY-MM.jsondata/speediance/normalized/history.jsondata/speediance/normalized/by_exercise.jsondata/speediance/dashboard/latest.json
6. Cronometer: the nutrition context layer
Whatever exact nutrition app you use, the role is the same: give the report context for energy intake.
This matters because training load without nutrition context leads to bad conclusions.
The report should be able to ask:
- Was recovery low because training load was high?
- Or because sleep was poor and calorie intake was low?
- Was the athlete under-fueled relative to output?
Practical build advice
For a reproducible version, do not depend on a live nutrition API at render time. Use one of:
- CSV export
- webhook ingestion
- scheduled sync into normalized JSON
Normalize into fields like:
calories_consumedprotein_gcarbs_gfat_gfiber_gtarget_caloriesestimated_deficit
7. What OpenClaw actually does in this stack
OpenClaw is not the data source. It is the orchestration and reasoning layer.
Its job is to:
- run sync jobs on schedule
- store stable outputs
- compare sources
- generate report HTML
- publish links
- produce human-friendly summaries from the normalized data
That means the reporting code should read from files like:
data/garmin/summary/latest.jsondata/whoop/normalized/latest.jsondata/speediance/normalized/history.jsondata/nutrition/latest.json
The report generator should never need to know how Garmin auth works or how Speediance headers changed this week.
8. A reproducible directory structure
Here is a public, cloneable structure someone else can actually use:
project/
data/
garmin/
raw/
normalized/
summary/
whoop/
raw/
normalized/
speediance/
raw/
normalized/
dashboard/
nutrition/
scripts/
sync_garmin.py
sync_whoop.py
sync_speediance.py
sync_nutrition.py
build_report.py
reports/
latest.html
frontend/
data/
This is the main lesson from the Aria build: make the file structure intelligible enough that anyone can debug it at 6 AM.
9. Example report builder pattern
Once each connector writes normalized JSON, the actual report code becomes simple.
import json
from pathlib import Path
base = Path("data")
garmin = json.loads((base / "garmin/summary/latest.json").read_text())
whoop = json.loads((base / "whoop/normalized/latest.json").read_text())
speediance = json.loads((base / "speediance/normalized/history.json").read_text())
nutrition = json.loads((base / "nutrition/latest.json").read_text())
summary = {
"recovery": whoop.get("recovery_score"),
"hrv": whoop.get("hrv_rmssd_milli"),
"rhr": whoop.get("resting_heart_rate"),
"steps": garmin.get("totalSteps"),
"body_battery": garmin.get("bodyBattery"),
"lifting_volume": speediance.get("today", {}).get("total_volume"),
"calories_in": nutrition.get("calories_consumed"),
}
html = f"""
<html>
<body>
<h1>Daily Fitness Report</h1>
<ul>
<li>Recovery: {summary['recovery']}</li>
<li>HRV: {summary['hrv']}</li>
<li>RHR: {summary['rhr']}</li>
<li>Steps: {summary['steps']}</li>
<li>Body Battery: {summary['body_battery']}</li>
<li>Lifting Volume: {summary['lifting_volume']}</li>
<li>Calories In: {summary['calories_in']}</li>
</ul>
</body>
</html>
"""
Path("reports/latest.html").write_text(html)
This is where the whole design pays off: once the sync layer is sane, the report layer gets boring in the best possible way.
10. What each connector is actually for
This is the simplest mental model:
WHOOP
Use for:
- recovery
- HRV
- RHR
- sleep performance
- strain
- readiness framing
Garmin
Use for:
- running and cardio detail
- pace, power, cadence
- HR zones
- training effect
- detailed activity history
Speediance
Use for:
- strength workout history
- total volume
- exercise detail
- planned vs actual session execution
- movement-level progression if you build by-exercise indexing
Nutrition app
Use for:
- calorie intake
- macro context
- under-fueling detection
OpenClaw then combines all of them into one report and recommendation surface.
11. API connection points to implement
This is the checklist I would hand to an AI agent before asking it to build the repo.
Garmin connection points
- Library:
garminconnectfromcyberjunky/python-garminconnect - Auth: Garmin Connect email/password with the library’s token/session handling
- Pull cadence: daily morning sync plus optional post-workout sync
- Minimum pulls:
- daily stats for steps, resting HR, sleep seconds, body battery, calories
- activities by date for runs/rides/cardio sessions
- activity detail when available for HR zones, pace, cadence, power, training effect
- Normalized output:
data/garmin/summary/latest.json
WHOOP connection points
- API docs:
https://developer.whoop.com/api - Auth: OAuth2 access token + refresh token flow
- Base URL:
https://api.prod.whoop.com/developer/v2 - Minimum endpoint groups:
/cyclefor strain/cycle context/recoveryfor recovery score, HRV, resting HR/activity/sleepfor sleep performance and sleep timing/activity/workoutfor workouts and WHOOP strain data/user/profile/basicand/user/measurement/bodyfor profile/body context when needed
- Normalized output:
data/whoop/normalized/latest.json
Speediance connection points
- Public extraction for this build:
https://github.com/clawdassistant85-netizen/speediance-smartgym-workout-manager - Upstream reference:
https://github.com/hbui3/UnofficialSpeedianceWorkoutManager - Auth: unofficial token/user-id based flow exposed by the SmartGym client layer
- Minimum pulls:
- workout history
- exercise/session detail
- custom workout/template metadata if you want planned-vs-actual reporting
- raw API/debug response capture without secrets
- Normalized outputs:
data/speediance/normalized/history.jsondata/speediance/normalized/by_exercise.json
Cronometer connection points
- Public source:
https://cronometer.com/exports/integrations - Recommended public-reproducible approach: CSV export or scheduled file drop, not a live render-time API dependency
- Minimum fields: date, calories, protein, carbs, fat, fiber, and any micronutrients you want in recovery analysis
- Normalized output:
data/nutrition/latest.json
Report-generation connection point
The report builder should read only these files:
data/garmin/summary/latest.json
data/whoop/normalized/latest.json
data/speediance/normalized/history.json
data/speediance/normalized/by_exercise.json
data/nutrition/latest.json
That is the actual connection surface. Everything upstream can break and be fixed independently.
12. The real implementation rules
If someone wants to reproduce this successfully, these rules matter more than any single code snippet:
-
Normalize every vendor into your own schema
Never let a report depend on vendor payload shape. -
Keep raw snapshots
When a sync breaks, raw payloads save you. -
Never render from live APIs if the report is time-sensitive
Sync first, render second. -
Treat unofficial integrations as disposable adapters
Especially Speediance. -
Make Garmin and WHOOP complement each other, not compete
WHOOP = readiness. Garmin = execution detail. -
Model strength data at both session and exercise level
Otherwise progression reporting stays shallow.
Final implementation reference list
These are the public references an AI coding agent should be handed first when recreating this system:
- OpenClaw as the orchestration layer
- Garmin connector:
https://github.com/cyberjunky/python-garminconnect - Garmin historical auth context:
https://github.com/matin/garth(deprecated; use as context, not as the center of a new build) - WHOOP official developer API:
https://developer.whoop.com/api - Speediance public extraction for this build:
https://github.com/clawdassistant85-netizen/speediance-smartgym-workout-manager - Speediance upstream/original reference:
https://github.com/hbui3/UnofficialSpeedianceWorkoutManager - Cronometer nutrition exports:
https://cronometer.com/
If you are handing this article to Claude Code, Codex, OpenClaw, or another implementation agent, the correct instruction is: build the connectors first, write normalized JSON snapshots second, then build the report renderer last. Do not start by designing the final HTML report.