This tool is a Python-based scraping application that collects daily health and activity data from my Garmin account, exposes it as Prometheus metrics, and provides endpoints for backfilling historical data, which is especially useful if I lose prometheus data. The metrics are designed for visualization and alerting in Grafana and Prometheus, helping me monitor my fitness, health, heart rate, stress, activity.
The inspiration came from when I was recovering from covid and was trying to pin point when my long covid had improved - by using my oxygen intake data on my watch. Garmin provide this data on their app and on weekly reports, but I wanted an in-depth approach to it.
- Python 3.11+
- Docker (optional but recommended for local testing)
- Garmin account credentials
- Prometheus and Grafana for metrics collection and visualization
Set the following environment variables for the app to function:
GARMIN_USER
: Your Garmin account usernameGARMIN_PASS
: Your Garmin account passwordGARTH_FOLDER
: Path to store authentication tokens - generated by the app and then used for 90 days.SLACK_CHANNEL
: (Optional) Slack channel for sync alertsSLACK_USER_ID
: (Optional) Slack user ID for notificationsSLACK_BOT_TOKEN
: (Optional) Slack bot token for sending messages
- Right now this is a public repo, so you can clone it and build it and run it locally.
- However, you won't be able to access the docker images that my GHA workflow builds.
- At a later stage I might push the image to a public docker repo, but until then you'll need to find your own route to deploying it.
- This project follows a modular, pythonic style - splitting them into easy maintable components
- The core of the app is written in Python, using Flask to expose the required endpoints
- It relies on a manual process to sync my watch, but there are alerts to remind me to do it.
- Metrics are exposed on
/metrics
using the prometheus-client library - these are then scraped by prometheus usingPod Annotations
- It includes Helm charts for easy deployment to my Raspberry Pi K3s cluster, including alerts
- The backfill endpoint is affectively my way of restoring data from a backup for my garmin data. The endpoint currently only generates the backfill timeseries chunks - it does not put them into the prometheus filesystem.
- /metrics: Exposes Prometheus metrics for scraping.
- /daily: Triggers a daily data scrape from Garmin and updates metrics. I'm triggering this as a raw cronjob on my Raspberr Pi. At a later date I may change the endpoint to run on a schedule.
- /backfill?days=N: Backfills historical data for the past N days and generates TSDB-compatible files. You'll then need to pass that to a folder where prometheus can ingest it to. Beware, the metric names can lead to different metrics appearing for the same name.
Metrics include heart rate, stress, activity, body battery, sleep, and derived metrics for richer analysis.
GET /metrics
: Prometheus metrics endpoint.GET /daily
: Fetches and exposes the latest daily Garmin data.GET /backfill?days=N
: Backfills and processes N days of historical data.
I've exported my dashboard as json, mostly for version control. A direct copy may not work as it's based on your datasource, but it's worth looking at for some examples. There's nothing too complicated like rates or deltas, it's mostly just standard queries on timeseries and pie chart panels.
As part of the helm chart I've got a PrometheusRule
set. This generates rules that my local alertmanager can alert on.
Example alerts you can configure in Prometheus:
- RestingHeartRateSpike: Detects spikes in 7-day resting heart rate (possible sickness).
- SedentaryBehavior: Alerts if you’ve been sedentary for over 12 hours.
- ElevatedStressLevels: Alerts if your average stress level is high over several days.
- LowBodyBatteryRecovery: Detects poor overnight recovery.
- InsufficientActivity: Alerts if you’ve been active less than 1 hour.
- PoorSleepQuality: Alerts if SPO2 during sleep is low.
- HighStressToActivityRatio: Alerts if stress duration is high relative to activity.
- OvertrainingWarning: Detects drops in heart rate variability.
- GarminDataStale: Alerts if your watch hasn’t synced in over 24 hours.
See helm/templates/garmin-alerts.yaml
for some rules you can copy.
- Fix the login so it works on ARM64. Right now I'm relying on a manual copy process.
- Better refinement of my alerts
- The backfill endpoint will automatically backfill right into prometheus, taking out the manual steps
This tool takes my garmin data and allows me to visualise it on Grafana, and alert on it where required. I learned a few new things from it that I never learned in industry
- How the prometheus backfill functionality works
- How request headers differ between Raspberry Pi (ARM64) vs my machine. The login returns a 401 on my Pi. The initial login won't work on a Raspberry Pi, so I need to run it locally first and then copy the oauth credentials up! It's a bit maddening and I suspect there's some difference in user-agent/other headers that I havne't had time to look into.