Skip to content

Latest commit

 

History

685 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Garmin Local Archive

Platform License No Cloud Release Tests Garmin Local Archive MCP server – quality and maintenance score on Glama

Archive and analyze your Garmin Health data local.

Privacy first — inspired by European principles.


A look at the app

Home · Files · Settings · Chat · MCP Server — click any tab to view full size.


Download

Version Description Requires
Garmin_Local_Archive_Standalone.zip Recommended — no setup needed Nothing
Garmin_Local_Archive.zip Standard version Python 3.10+

No install, no terminal. Download, unzip, run. Standard version: install dependencies first — pip install -r requirements.txt.

The app checks GitHub for a new version on startup. Updates can be downloaded and applied automatically; on the Standalone version this includes a restart, on the standard version you start it again yourself.


Try it first

No Garmin account yet, or just want to see it in action? A synthetic 180-day demo archive (fictional data, clearly marked as such) lets you explore dashboards, Chat and the MCP server without connecting a real account. See QUICKSTART.txt, "Try it first".


Project status & disclaimer

GNU General Public License v3.0 — provided as-is, without warranty of any kind, express or implied.

  • Not an official Garmin product: This tool is not affiliated with, endorsed, or supported by Garmin.
  • Not medical advice: All health metrics, reference ranges, and dashboard data are for personal informational use only — not a substitute for medical advice.
  • AI and health data — handle with care: If you use an external AI service (ChatGPT, Claude, Gemini) to interpret your data: never upload documents containing your name, date of birth, or other identifying information. Cloud AI services store what you send — linked to your account. Use a local model (Ollama) or at minimum a session without login. This includes the app's own in-app Chat tab — its optional Cloud backend (Anthropic/OpenAI) sends your question and any archive data the model requests to that provider's API, same as pasting into their website; with the Ollama backend, local models stay fully local, while an Ollama cloud model (name ending in -cloud) is processed on Ollama's servers. AI responses on health topics are statistically generated — not medically validated. Treat them as a first orientation, not a conclusion.
  • Context data: Weather data is provided by Open-Meteo and Brightsky (DWD), pollen data and air quality data by Open-Meteo — accuracy and availability are not guaranteed. Air quality data (CAMS dataset) is available from approximately 2020 onwards.
  • Early stage: Core functionality is stable. APIs and internal structure may still change.
  • No guaranteed support: Development happens when time and interest allow.
  • Use at your own risk: I am not responsible for data loss or Garmin account issues.
  • Feedback welcome: If something feels off — logic, structure, results — open an issue.

Scope & limitations: Local-first, personal use, no enterprise ambitions.

  • Relies on python-garminconnect. Since Garmin does not offer a public API for personal use, moste non-commercial tools share this dependency. To mitigate unannounced upstream changes, GLA automatically detects and logs structural API shifts.
  • Local test suites cover the full pipeline plus a separate build-output validation suite. The full suite also runs automatically via GitHub Actions on every push to main (Windows runner), alongside dead-code and CVE-relevance checks; CodeQL security scanning runs on every push/PR to main
  • HTML dashboards require a one-time internet connection to download Plotly (~3 MB) — cached locally after that
  • Per-day checkpointing: an interrupted sync resumes from the last completed day, no full re-sync required
  • Historical data quality depends on Garmin servers

This project is built for my own use. If it happens to be useful to others, feel free to use it — but evaluate it like any other unverified open-source tool.

What this is not: Garmin Connect is still required — the app pulls data from there via API.

A note on cloud folders: the archive itself is stored as plaintext on disk — if garmin_data/ lives inside a cloud-synced folder, that data gets uploaded automatically. See SECURITY.md for details and the encrypted Mirror alternative. This tool does not replace Connect, the Garmin app, or your device sync. It has no cloud component, no remote access, and no sharing features. The GUI and EXE are Windows-only.


Why this exists

I wanted to ask an AI questions about my health data without sending that data to another cloud service. So I built a local alternative instead.

There's a second reason that matters more over time: Garmin silently degrades intraday data resolution. Empirical analysis of archive data (April–June 2026) shows the threshold at approximately 120–135 days, and appears to trend lower over time. Once full resolution is lost, it's gone permanently. This tool exists to capture it while it's still available.

What "intraday resolution" actually means in practice:

Metric API resolution Data points / day
Heart Rate ~1 minute up to 1,440
Stress ~3 minutes up to 480
Body Battery ~15 minutes up to 96
SpO2 ~1 hour up to 24
Respiration variable variable

After ~120 days, Garmin stops serving this data entirely. The daily summary (resting HR, average stress, etc.) remains — but the curves, the detail, the full timeline: gone. GLA captures it while it's still there.

→ For the full story, see MINDSET.md.


What makes this different

This project is as much a statement as it is a tool.

This is not a data export script — it maintains a complete, consistent local copy of your Garmin data over time. Your data stays in open formats, readable and analyzable with any tool you choose. Local AI, cloud AI, or no AI at all. Your data, your call.

Feature Garmin Connect Cloud-AI Bridges Garmin Local Archive
Data storage Garmin servers (USA) US AI servers Your machine
Privacy risk Medium High (training data risk) Minimal
Access Online only Requires subscription 100% offline
History Erodes over time Depends on source Permanent local copy

AI-assisted development

I can't write Python. The architecture, module boundaries, and decisions are mine. Every line of code is Claude's.

→ How this collaboration actually worked — who had which idea, where Claude was wrong — is documented in MINDSET.md.


How it works

The app works in two modes: live sync pulls recent data directly from Garmin Connect via API; Bulk Import loads your complete history from a Garmin GDPR export ZIP — this is the primary path for recovering years of data that the API no longer serves.

Everything is stored locally in structured formats (JSON, Excel, HTML dashboards). Once downloaded, nothing is transmitted anywhere.


Bulk Import

The GDPR export from Garmin contains your complete daily history — but in our testing, no intraday data was found (no heart rate curves, no stress timelines, no body battery graphs). That resolution appears to be available only through the API, and only for recent days.

The Bulk Import feature fills in the rest: request your full data export from Garmin (typically ready in 20–30 minutes), point the app at the ZIP, and your complete daily history lands in the local archive — in the same format as live API data. Days already present with good quality are skipped automatically.


Dashboards

The built-in dashboards cover roughly 90% of what most users are looking for — without any AI at all. For deeper analysis, your data is prepared in a format any local AI can work with directly.

Dashboard What it shows Output
Health Analysis HRV, Resting HR, SpO2, Sleep, Body Battery, Stress — daily values vs 90-day personal baseline vs age/fitness-adjusted reference ranges. Flags days outside range. HTML, Mobile HTML, JSON + AI prompt
Timeseries Intraday heart rate, stress, SpO2, body battery and respiration as zoomable charts across any date range. HTML, Excel
Heatmap Six intraday metrics (Heart Rate, Steps, Stress, Body Battery, SpO2, Respiration) as time-of-day × date grids — spot daily rhythms and irregularities at a glance. HTML
Daily Overview All summary fields in one flat table, one row per day. Excel
Health + Context Garmin health metrics alongside local weather and pollen data. HTML, Excel
Sleep Dashboard One row per night — segmented phase bar (Deep / Light / REM / Awake), sleep duration, score, quality badge, feedback label, HRV, Body Battery, and 7-day HRV moving average (computed from archive, no extra API call). Color-coded numbers via continuous gradient against personal reference ranges. Inspired by Garmin's own HRV pattern guide. HTML, Excel
Sleep & Recovery HRV, Body Battery, Sleep duration and phase breakdown (Deep / Light / REM / Awake) alongside weather and pollen context. Intraday detail per day. HTML
Explorer Free metric exploration — choose up to 4 metrics from all Garmin daily fields plus weather, pollen, and air quality on a shared time axis. Sleep phase breakdown and sleep quality log included. Built-in field descriptions and air quality interpretation guide. HTML
Custom Dashboard Pick any combination of Garmin daily fields and Context fields, set a date range, and build a one-off dashboard — no fixed field list, no specialist file written to disk. Field selections can be saved as named presets for reuse. Optional AES-256 encryption for the HTML output. HTML, Excel

Garmin Local Archive — Create Report


Create Dashboards — select dashboards and export as HTML, Excel or JSON.

Garmin Health Analysis Dashboard


Analysis dashboard — daily values vs 90-day personal baseline vs age/fitness-adjusted reference ranges.

Garmin Health Analysis Dashboard


One row per night — segmented phase bar, duration, sleep score, quality badge, Garmin feedback text, HRV, and Body Battery. Numbers are color-coded against personal reference ranges.

Garmin Health Analysis Dashboard


Analysis dashboard mobile version — daily values vs 90-day personal baseline vs age/fitness-adjusted reference ranges.

Live Tracking — a separate, always-current view: today's progression (Body Battery, Heart Rate, Steps, Stress) plus last night's sleep summary, refreshed automatically after every sync and on demand via an "Update Live" button in the Home tab. Not part of the Create Dashboards selection above — it has its own trigger, by design.


Export

Export Data writes your archive to external formats for other tools to consume — a separate layer from the dashboards above, reading the same broker data. Available from the Dashboards section's "Export Data" button, per-adapter grid (toggle Health/Context/Metadata independently per format):

Adapter Format Notes
JSON .json Full archive, 1:1 broker output
CSV .csv One row per day, health_<field>/context_<source>_<field> columns — built for Excel/LibreOffice. Intraday/series fields excluded (too large for a usable spreadsheet). Metadata in a separate file. Delimiter (;/,) and decimal separator chosen together, so German Excel opens it correctly.
InfluxDB Line Protocol .txt Full resolution incl. intraday/series data — for feeding garmin-grafana or similar tools without a Garmin API call of their own.

"Save & Add to Daily Sync" persists your selection and runs it automatically as part of every daily sync — no need to export by hand after each sync.

Garmin Local Archive — Export Data


Export Data — per-adapter grid for Health/Context/Metadata, JSON/CSV/InfluxDB.

Chat

A native chat panel is built into the app (Chat tab, renamed from "Ollama-Chat") — no separate setup beyond having Ollama itself installed and a model pulled, or a Cloud API key configured. Two independent choices, made before Start:

  • Backend — a local Ollama model (fully local, no setup beyond Ollama itself), or a Cloud LLM (Anthropic or OpenAI — requires an API key, configured on the MCP Server tab; see the AI disclaimer above for what that means for your data).
  • Source — the archived daily-summary snapshot (fast, no live query, the original behavior), or live MCP tool-calling — the model queries your archive on demand via the same MCP server external tools use, for questions the daily snapshot alone can't answer.

Replies stream in as they're generated for every combination except Ollama + MCP tool-calling (Ollama's own streaming support for tool-calling is not yet reliable upstream, so that one path waits for the complete answer). Chat sessions auto-save and can be reloaded later via the Chat History button — a session using live MCP data can always be resumed; a session using the daily snapshot can only be resumed if that snapshot hasn't changed since, otherwise it opens read-only. For connecting external tools (Open WebUI, AnythingLLM, Claude Desktop) for more advanced document/RAG workflows instead, see docs/README_APP.md and the MCP Server section below.


MCP Server

Garmin Local Archive exposes your archive to local LLMs via the Model Context Protocol — ask an LLM about your health and context data directly, without exporting files or copy-pasting into a chat window. Runs as its own standalone process, independent of the main app.

Two ways to run it: a ▶️ Start MCP Server button on the app's own MCP Server tab (uses the same archive path and settings as the rest of the app), or the standalone mcp_server.exe — a self-contained tool with its own small window, usable even without the main app installed.

Garmin Local Archive — Standalone MCP Server


Standalone MCP server window (mcp_server.exe) — runs independently of the main app, with its own archive path, backend selection, and live log.

Running Open WebUI (or any other MCP client) inside Docker? An opt-in "Extra allowed hosts" field on the MCP Server tab lets you add host.docker.internal — pre-filled by default — to the server's allowed- host list, since the underlying MCP SDK otherwise rejects connections that don't arrive as 127.0.0.1/localhost.

Works with Ollama (local models, default) or an optional cloud LLM backend — your choice, no default push toward either. Point your MCP-compatible client (Claude Desktop, Open WebUI, or similar) at the server and start asking questions.

Connect: Streamable HTTP at http://127.0.0.1:<port>/mcp (default port 8756, configurable on the MCP Server tab) — bound to localhost only, not reachable from other machines by default. See "Extra allowed hosts" above for Docker clients.

Tools exposed (see docs/REFERENCE_MCP.md for full signatures):

Tool What it does
query_health Daily health metrics (e.g. sleep, stress, heart rate, body battery) for a date or date range
query_context Weather, pollen and air-quality data for a date or date range
query_fit_activities FIT activity data — planned, not yet available (see docs/ROADMAP.md, "FIT Pipeline")
query_raw Unprocessed archive data for a given domain, for deeper inspection
get_archive_metadata Archive coverage and quality info (date ranges, missing days, data quality)
list_available_fields Which fields the archive actually contains, per domain
refresh_cache Rebuilds the server's local query cache on demand, e.g. right after a sync

Example: "How did my sleep and stress compare over the last two weeks?" — the model calls the tools above itself and answers from your real archive, no manual export needed. No archive yet? See Try it first above.


Architecture

Token security & Login

Garmin login works via SSO — logging in with email and password on every run triggers Captcha or MFA. The solution: log in once manually, and Garmin returns an OAuth token that handles all subsequent runs for approximately one year. This token is equivalent to a logged-in session and must not sit unprotected on disk.

The token is encrypted at rest. Details on the encryption design and threat model: SECURITY.md


Pipeline

Live sync and Bulk Import both flow through the same validation and quality pipeline before anything lands in the local archive — the diagram below shows the full picture.

Tip

Pipeline Architecture: For a detailed view of the v1.3.4 data flow including the validation layer and self-healing loop, open screenshots/flowchart_v134.html in your browser.


System Architecture

The diagram below shows how all components relate to each other as of v1.7.x — from API ingestion and context collection through the broker layer to dashboard export. The broker layer also includes gateway_map, a cross-domain routing layer used by the local MCP server among other consumers — existing dashboards are unaffected and continue to query health_map/context_map directly.

System Architecture v1.7.x


What is included

The project is structured into six focused layers — Garmin pipeline, Context pipeline, Data brokers, Dashboard layer, Export/MCP layer, Desktop app. Each layer has a single responsibility — collect, validate, assess, broker, or render. No crossover between layers.

The diagram above shows how the layers connect. Each module is self-contained and designed to be extended — for a script-by-script reference (what each module does, owns, and how to add new ones), see docs/MAINTENANCE_GLOBAL.md.

The desktop app includes a Background Timer — once started, it automatically repairs failed/incomplete days, upgrades bulk-imported days within Garmin's intraday resolution window (~120 days), fills missing days, keeps a raw API-response backup current, retroactively adds newly supported data fields (like step count) to already-archived days, and re-fetches bulk-imported days for the handful of data fields the official Garmin export never contains at all (HRV, SpO2, Body Battery, respiration, training status, race predictions, max metrics) — regardless of how old the day is, since that gap has nothing to do with intraday resolution — with no further manual steps in between. The timer must be started manually and only runs while the app is open — it does not resume automatically after a restart.

Data is stored in two root folders:

garmin_data/
├── raw/        – complete API dumps (~500 KB/day) — permanent archive / basis for dashboards and analysis
├── source/     – unmodified API responses (~250 KB/day) — replay-safe intraday backup
├── summary/    – compact daily JSONs (~2 KB/day)  — basis for dashboards and analysis
└── log/        – session logs, quality register, encrypted token

context_data/
├── weather/summary/     – daily weather archive (Open-Meteo)
├── pollen/summary|raw/  – daily / hourly pollen archive (Open-Meteo Air Quality)
├── brightsky/summary|raw/    – daily / hourly weather archive (Brightsky DWD)
└── airquality/summary|raw/   – daily / hourly air quality archive (Open-Meteo Air Quality)

See docs/MAINTENANCE_GLOBAL.md for full technical documentation, how to add new fields, troubleshooting, and developer notes.


Testing

Seventeen test suites cover the full pipeline — no network, no API required (the Chat tab's Cloud LLM/MCP-tool-calling tests mock every SDK/HTTP call, same as everything else here):

python tests/test_local.py                    # Garmin pipeline
python tests/test_local_context.py            # Context pipeline (external APIs mocked)
python tests/test_dashboard.py                # Dashboard pipeline
python tests/test_broker.py                   # Broker layer (health_map / gateway_map routing, metadata_map)
python tests/test_mcp.py                      # MCP layer (mcp_map protocol translation)
python tests/test_export.py                   # Export Layer
python tests/test_app_logic.py                # App layer (entry points, path resolution)
pytest tests/test_qt_app.py                   # PyQt6 App layer
python tests/test_static.py                   # ruff + bandit + regression guards
python tests/test_build_output.py             # Build output validation (run after build)
pytest tests/test_cloud_llm.py                # Chat tab — Cloud LLM connector
pytest tests/test_mcp_tool_chat.py            # Chat tab — Ollama + MCP tool-calling
pytest tests/test_cloud_tool_chat.py          # Chat tab — Cloud + MCP tool-calling, streaming
pytest tests/test_chat_session_store.py       # Chat tab — session save/load/resume
pytest tests/test_cloud_credential_store.py   # Chat tab — API key storage in Windows Credential Manager
pytest tests/test_mcp_process.py              # MCP Server Start/Stop process control
python tests/test_updater.py                  # T2 + T3 self-updater

build_all.py runs test_local.py, test_local_context.py, test_dashboard.py, test_broker.py, and test_static.py as pre-build gates — a failing test aborts the build before either target is built. test_build_output.py and test_app_logic.py run automatically after both builds complete, as post-build gates. test_qt_app.py and the six Chat-tab/MCP-process suites above are run manually via pytest.

GUI changes are verified manually before release. Automated builds and release packaging run via a manually-triggered GitHub Actions workflow (build-release.yml) — not on every push, kept as a deliberate one-click step so not every commit cuts a release.


⚠️ API Usage Notice: This project uses an unofficial interface. Large-scale data retrieval (e.g., syncing long time ranges in a single run) may trigger rate limiting or temporary IP blocks by Garmin (HTTP 429).

It is recommended to:

  • fetch data in smaller increments
  • include delays between requests
  • allow cool-down periods between sync sessions

Built with Claude · ☕ buy me a coffee

About

Secure, local, offline database for Garmin Connect health data. Windows desktop app, no setup needed. Built for long-term local analysis, dashboards, local AI and MCP-Server. Export your data as JSON, CSV and InfluxDB Line Protocol. Preserve your data before Garmin's historical data decay.

Topics

Resources

Security policy

Stars

21 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages