mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
docs(garmin): document the Personal API integration
The Garmin page now explains how to create a key, what syncs and where it lands, how key expiry is handled, and what the Personal API does not carry. The Home Assistant guide shows how to push Training Readiness and the acute training load, the Apple Health and Google Health pages point at the direct integration, and the changelog records the release notes.
This commit is contained in:
1 parent
13e232c836
commit
73f16e5389
6 files changed
+152
-49
No files matched your search
@@ -30,6 +30,23 @@
|
||||
Each injected dose now carries a chip with the site next to its status,
|
||||
in the table and on the mobile cards, so the rotation can be read back
|
||||
from the history. Doses without a site look as before.
|
||||
- **Garmin, directly.** Garmin Connect now offers a Personal API, and
|
||||
HealthLog reads it: create a key in Garmin Connect (Settings, Personal
|
||||
API), paste it into the new Garmin card, and steps, distance, floors,
|
||||
active calories, resting heart rate, sleep stages and sleep score,
|
||||
overnight SpO₂, breathing rate and HRV, weigh-ins with body composition,
|
||||
activities and logged period days sync every hour, with the whole
|
||||
history filled in behind. Body Battery and Garmin's all-day stress level
|
||||
arrive as types of their own (`BODY_BATTERY`, `STRESS_LEVEL`), never mixed
|
||||
into the computed scores. Garmin keys expire after 30 to 365 days; the
|
||||
card warns a week ahead and asks for a new key when Garmin stops
|
||||
accepting the old one. Accounts on Garmin's China service work too. The
|
||||
key is stored encrypted and only ever sent to Garmin. (#1333)
|
||||
- **Training Readiness and acute training load.** The Personal API does
|
||||
not carry them, so they are new types (`TRAINING_READINESS`,
|
||||
`ACUTE_TRAINING_LOAD`) a Home Assistant bridge can push through an ingest
|
||||
token, and they chart on the recovery page next to Body Battery. The Home
|
||||
Assistant guide has the automation. (#1333)
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -96,6 +113,8 @@
|
||||
- @Oguz3003 for staying with the Raspberry Pi 4 until it reads PDFs
|
||||
(#1311).
|
||||
- @mills1975 for asking how blood pressure differs over the day (#1331).
|
||||
- @kruemelro for asking for a direct Garmin connection on Garmin's new
|
||||
Personal API (#1333).
|
||||
|
||||
## [1.46.6] — 2026-11-02
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ notes, and machine-readable specs that ship alongside the source:
|
||||
<https://docs.healthlog.dev>.
|
||||
- [`integrations/`](./integrations/) — per-integration reference:
|
||||
Withings, WHOOP, Fitbit, Google Health, Strava, Nightscout, Dawarich and
|
||||
the Garmin route; the Apple Health, Health Connect, document and data
|
||||
Garmin; the Apple Health, Health Connect, document and data
|
||||
imports; AI providers; and Home Assistant with its blueprint. Oura and
|
||||
Polar are covered on the docs site only.
|
||||
- [`adr/`](./adr/) — architecture decision records.
|
||||
|
||||
@@ -314,7 +314,7 @@ id for debugging.
|
||||
|
||||
## Garmin owners
|
||||
|
||||
Garmin has no direct connector for a self-hosted instance. Garmin Connect writes
|
||||
to Apple Health automatically, so the import path on this page is how Garmin data
|
||||
reaches HealthLog on iOS. See [garmin.md](./garmin.md) for what comes through and
|
||||
what Garmin withholds.
|
||||
Garmin Connect writes to Apple Health automatically, so the import path on this
|
||||
page brings Garmin data in on iOS. The direct [Garmin](./garmin.md) integration
|
||||
reads more (Body Battery, stress, Garmin's sleep score) with a key from your
|
||||
own Garmin account.
|
||||
+92
-40
@@ -1,52 +1,104 @@
|
||||
# Garmin (via Apple Health or Google Health Connect)
|
||||
# Garmin
|
||||
|
||||
**There is no direct Garmin connector for a self-hosted HealthLog instance, and
|
||||
there cannot be one.** Garmin's data-sharing program is business-partner-only:
|
||||
it is approval-gated, requires a public callback endpoint, and is not the
|
||||
"register your own app, paste a key" model the wearable integrations here rely
|
||||
on. So instead of a broken half-integration, HealthLog reads Garmin data the
|
||||
honest way — through the phone health platform your Garmin already feeds.
|
||||
HealthLog reads Garmin Connect directly through Garmin's **Personal API**: you
|
||||
create a key for your own account in Garmin Connect, paste it into HealthLog,
|
||||
and the server pulls your data every hour. Nothing is pushed by Garmin, and
|
||||
nothing is written back.
|
||||
|
||||
## The path that works
|
||||
The Personal API is for your own data only. Each person on an instance
|
||||
creates and pastes their own key; there is no shared app and no operator
|
||||
setup.
|
||||
|
||||
```
|
||||
Garmin device → Garmin Connect → Apple Health (iOS) ─┐
|
||||
→ Google Health Connect ├─→ HealthLog
|
||||
(Android) ─┘
|
||||
```
|
||||
## Create a key
|
||||
|
||||
- **iOS:** Garmin Connect writes to Apple Health automatically. Import it with
|
||||
the existing [Apple Health](./apple-health.md) path (the `export.zip` upload or
|
||||
the native client's background sync).
|
||||
- **Android:** since Garmin Connect 5.14.1 (July 2025) the app writes to **Google
|
||||
Health Connect**. Connect HealthLog through the existing
|
||||
[Google Health](./google-health.md) integration.
|
||||
1. Open [Garmin Connect → Settings → Personal API](https://connect.garmin.com/app/settings/personalApiToken).
|
||||
2. Create a key and choose how long it stays valid: 30, 90, 180 days or one
|
||||
year.
|
||||
3. Copy it right away. Garmin shows the key once.
|
||||
|
||||
No extra code, no Garmin developer account, no approval queue — if your Garmin
|
||||
already syncs to your phone's health platform, HealthLog already reads it.
|
||||
## Connect
|
||||
|
||||
Open **Settings → Integrations → Garmin**, paste the key, and pick the same
|
||||
validity you chose in Garmin Connect. HealthLog checks the key with Garmin
|
||||
before storing anything, so a mistyped or revoked key is refused on the spot.
|
||||
The key is stored encrypted at rest and is never shown again, logged or sent
|
||||
anywhere but Garmin.
|
||||
|
||||
Accounts registered with Garmin's China service work too: a key the global
|
||||
service does not know is tried there once, and the service that accepts it is
|
||||
remembered.
|
||||
|
||||
After connecting, the last 30 days arrive within a minute and the rest of your
|
||||
history follows in the background. The card says "Older history is still
|
||||
loading" until it is done.
|
||||
|
||||
## Key expiry
|
||||
|
||||
Garmin keys expire and cannot be extended. Garmin does not report a key's end
|
||||
date through the API, so HealthLog works it out from the validity you picked
|
||||
at connect, and replaces that estimate with Garmin's own date once the key is
|
||||
in its last day (the short-lived tokens it buys are cut off at the key's end).
|
||||
|
||||
From seven days before the end the card says when the key expires. When it
|
||||
has expired, or you revoked it in Garmin Connect, syncing stops and the card
|
||||
asks for a new key: create one, open **Replace key** in the card's menu and
|
||||
paste it. Nothing imported is lost. If you did not pick a validity, the card
|
||||
says that it cannot warn ahead.
|
||||
|
||||
## What comes through
|
||||
|
||||
Everything Garmin shares with Apple Health / Health Connect flows in: steps,
|
||||
heart rate (including beat-to-beat variability), sleep stages (REM / deep /
|
||||
light), calories (active and total), distance, weight and body composition,
|
||||
cadence, and — partially — blood-oxygen saturation.
|
||||
| Garmin data | HealthLog type | Unit |
|
||||
| --------------------------------------------------------------------- | -------------------------- | ------------- |
|
||||
| Steps | `ACTIVITY_STEPS` | steps |
|
||||
| Step distance | `WALKING_RUNNING_DISTANCE` | m |
|
||||
| Floors climbed | `FLIGHTS_CLIMBED` | flights |
|
||||
| Active calories | `ACTIVE_ENERGY_BURNED` | kcal |
|
||||
| Resting heart rate | `RESTING_HEART_RATE` | bpm |
|
||||
| All-day average stress level | `STRESS_LEVEL` | 0–100 |
|
||||
| Body Battery (the day's high) | `BODY_BATTERY` | 0–100 |
|
||||
| Sleep stages (light, deep, REM, awake) | `SLEEP_DURATION` per stage | minutes |
|
||||
| Sleep score | `SLEEP_SCORE` | 0–100 |
|
||||
| Average overnight SpO₂ | `OXYGEN_SATURATION` | % |
|
||||
| Average overnight breathing rate | `RESPIRATORY_RATE` | breaths/min |
|
||||
| Overnight HRV (RMSSD) | `HRV_RMSSD` | ms |
|
||||
| Weight, BMI, body fat, body water, muscle and bone mass, visceral fat | the body-composition types | kg, %, rating |
|
||||
| Activities | workouts | |
|
||||
| Logged period days | the cycle tracker | |
|
||||
|
||||
## What Garmin withholds
|
||||
Notes:
|
||||
|
||||
These are Garmin's **proprietary** scores. Garmin does **not** share them with
|
||||
Apple Health or Health Connect, so no self-hosted integration — and no amount of
|
||||
configuration — can obtain them. They are only available inside Garmin Connect
|
||||
itself:
|
||||
- Garmin's HRV is RMSSD and lands on `HRV_RMSSD`. Garmin reports no SDNN, so
|
||||
nothing is written to `HEART_RATE_VARIABILITY`.
|
||||
- Stress and Body Battery are Garmin's own scores and keep types of their own.
|
||||
They are not mixed into HealthLog's computed stress score.
|
||||
- Days you did not wear the watch import nothing, not zeros.
|
||||
- Period days are only added on days you have not logged yourself; your own
|
||||
entries are never changed. Cycles Garmin only predicts are not imported.
|
||||
- When the same data also arrives through Apple Health or Health Connect, the
|
||||
source order in **Settings → Sources** decides which one a chart shows.
|
||||
Garmin ranks with the other watches.
|
||||
|
||||
- Body Battery
|
||||
- HRV Status
|
||||
- VO₂ max
|
||||
- Training Load and Training Effect
|
||||
- Recovery Time
|
||||
- Intensity Minutes
|
||||
- Stress score
|
||||
- Running dynamics
|
||||
## What the Personal API does not carry
|
||||
|
||||
If you rely on those specific numbers, keep the Garmin Connect app for them;
|
||||
everything else lands in HealthLog through the platform path above.
|
||||
Training Readiness, Training Status, acute training load and recovery time
|
||||
are not part of the Personal API. If you read them elsewhere, for example
|
||||
through a Home Assistant integration, push them through an ingest token: see
|
||||
[Home Assistant](./home-assistant.md#garmin-training-figures). They land as
|
||||
`TRAINING_READINESS` and `ACUTE_TRAINING_LOAD` and show on the recovery page
|
||||
beside Body Battery.
|
||||
|
||||
## Limits
|
||||
|
||||
- Garmin sets how many requests a key may make. HealthLog reads one resource
|
||||
at a time and stops the hour's run at Garmin's first rate-limit answer; the
|
||||
history backfill waits as long as Garmin asks before it goes on.
|
||||
- Disconnecting removes the key from HealthLog. It stays valid at Garmin until
|
||||
you revoke it there.
|
||||
|
||||
## Without a key
|
||||
|
||||
Garmin Connect also writes to Apple Health on iOS and to Google Health Connect
|
||||
on Android, so the [Apple Health](./apple-health.md) and
|
||||
[Google Health](./google-health.md) paths keep working for anyone who prefers
|
||||
not to create a key. Body Battery, stress and Garmin's sleep score only come
|
||||
through the Personal API.
|
||||
@@ -315,7 +315,7 @@ Previously synced measurements stay in the database.
|
||||
|
||||
## Garmin owners
|
||||
|
||||
Garmin has no direct connector for a self-hosted instance. Since Garmin Connect
|
||||
5.14.1 (July 2025) the app writes to Google Health Connect, so this integration
|
||||
is how Garmin data reaches HealthLog on Android. See [garmin.md](./garmin.md) for
|
||||
what comes through and what Garmin withholds.
|
||||
Since Garmin Connect 5.14.1 (July 2025) the app writes to Google Health Connect,
|
||||
so this path brings Garmin data in on Android. The direct [Garmin](./garmin.md)
|
||||
integration reads more (Body Battery, stress, Garmin's sleep score) with a key
|
||||
from your own Garmin account.
|
||||
@@ -149,6 +149,38 @@ Units are derived server-side from the type, so send the number in the unit
|
||||
above and do not send a `unit` field. The full enum is in
|
||||
`docs/api/openapi.yaml` under `MeasurementType`.
|
||||
|
||||
## Garmin training figures
|
||||
|
||||
The Garmin Personal API does not carry Training Readiness or the acute
|
||||
training load (see [Garmin](./garmin.md)). If Home Assistant reads them, for
|
||||
example through a Garmin Connect integration, send them through the same
|
||||
ingest token. They have types of their own and appear on the recovery page:
|
||||
|
||||
| What the sensor reports | `type` | Unit expected |
|
||||
| -------------------------- | --------------------- | ------------- |
|
||||
| Garmin Training Readiness | `TRAINING_READINESS` | score 0–100 |
|
||||
| Garmin acute training load | `ACUTE_TRAINING_LOAD` | load, no unit |
|
||||
|
||||
Both change about once a day. This automation pushes Training Readiness
|
||||
whenever the sensor changes, through the `healthlog_measurement` command
|
||||
above; the acute load is the same with its own sensor and type:
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "HealthLog: Garmin training readiness"
|
||||
triggers:
|
||||
- trigger: state
|
||||
entity_id: sensor.garmin_training_readiness
|
||||
conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.to_state.state | is_number }}"
|
||||
actions:
|
||||
- action: rest_command.healthlog_measurement
|
||||
data:
|
||||
type: TRAINING_READINESS
|
||||
value: "{{ trigger.to_state.state | float }}"
|
||||
```
|
||||
|
||||
## Room climate
|
||||
|
||||
Indoor CO₂, temperature and humidity are their own types, kept apart from the
|
||||
|
||||
Reference in new issue
Block a user