|
| 1 | +# Worked example: withings-export |
| 2 | + |
| 3 | +**Audience:** Withings device owners (scale, sleep tracker, activity watch) who want their health data in a terminal or piped to an LLM agent. |
| 4 | + |
| 5 | +**Prerequisites:** `withings-export` installed (see [GETTING_STARTED.md](https://github.com/quantcli/common/blob/main/GETTING_STARTED.md)), a Withings account, `withings-export auth login` completed. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 1. Authenticate |
| 10 | + |
| 11 | +Withings uses OAuth2. Run the login flow once: |
| 12 | + |
| 13 | +```sh |
| 14 | +withings-export auth login |
| 15 | +``` |
| 16 | + |
| 17 | +This opens a browser tab. Complete the OAuth consent and the token is stored at `~/.config/withings-export/auth.json`. |
| 18 | + |
| 19 | +Check readiness: |
| 20 | + |
| 21 | +```sh |
| 22 | +withings-export auth status |
| 23 | +``` |
| 24 | + |
| 25 | +**Expected when logged in:** |
| 26 | + |
| 27 | +``` |
| 28 | +logged in |
| 29 | +``` |
| 30 | + |
| 31 | +Exit 0. **Expected when not logged in:** |
| 32 | + |
| 33 | +``` |
| 34 | +Error: not logged in — run: withings-export auth login |
| 35 | +``` |
| 36 | + |
| 37 | +Exit 1. `auth status` makes no network call — it only reads the local token file. |
| 38 | + |
| 39 | +**HTTPS callback workaround:** If your Withings OAuth app requires HTTPS, register `https://redirectmeto.com/http://localhost:8128/oauth/authorize` as the callback URL and set: |
| 40 | + |
| 41 | +```sh |
| 42 | +export WITHINGS_CALLBACK_URL="https://redirectmeto.com/http://localhost:8128/oauth/authorize" |
| 43 | +``` |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## 2. Orient with `prime` |
| 48 | + |
| 49 | +```sh |
| 50 | +withings-export prime |
| 51 | +``` |
| 52 | + |
| 53 | +**Output** (reproduced at HEAD, 2026-05-19): |
| 54 | + |
| 55 | +``` |
| 56 | +withings-export — primer for LLM agents |
| 57 | +======================================= |
| 58 | +
|
| 59 | +WHAT IT IS |
| 60 | + CLI for personal Withings data: activity, sleep, workouts, body |
| 61 | + measurements, minute-level intraday samples (HR/HRV/SpO2/steps). |
| 62 | +
|
| 63 | +I/O |
| 64 | + stdout: data in --format markdown (default), json, or csv. |
| 65 | + stderr: errors. Exit 0 on success including empty results. |
| 66 | +
|
| 67 | +AUTH |
| 68 | + withings-export auth login OAuth2 in browser; tokens stored locally. |
| 69 | + withings-export auth status Exit 0 if usable, 1 with reason. No network call. |
| 70 | + withings-export auth refresh|logout |
| 71 | +
|
| 72 | + Optional env: WITHINGS_CLIENT_ID, WITHINGS_CLIENT_SECRET, WITHINGS_CALLBACK_URL. |
| 73 | + HTTPS-callback workaround: register https://redirectmeto.com/http://localhost:8128/oauth/authorize |
| 74 | + (verbatim) and set WITHINGS_CALLBACK_URL to the same string. |
| 75 | +
|
| 76 | +DATE FLAGS (every subcommand) |
| 77 | + --since VALUE / --until VALUE |
| 78 | + VALUE: today | yesterday | YYYY-MM-DD | Nd/Nw/Nm/Ny |
| 79 | + See https://github.com/quantcli/common/blob/main/CONTRACT.md#3-date-flags |
| 80 | +
|
| 81 | +SUBCOMMANDS (defaults in parens) |
| 82 | + activity (30d) daily steps/distance/calories/HR zones |
| 83 | + sleep (30d) stages, score, HR/RR; --derive polyfills missing nights |
| 84 | + workouts (90d) runs/walks/bikes/lifts with calories/HR/distance |
| 85 | + measurements (30d) weight/fat/BP/SpO2/temp; --types LIST filters |
| 86 | + intraday (1d) minute-level HR/HRV/SpO2/steps; dense — keep windows narrow |
| 87 | +
|
| 88 | + Inspect any subcommand's row schema with: <subcommand> --since 1d --format json |
| 89 | +
|
| 90 | +EXAMPLES |
| 91 | + withings-export sleep --since 7d |
| 92 | + withings-export workouts --since 30d --format json | |
| 93 | + jq '.[] | {date, category, hr: .data.hr_average}' |
| 94 | + withings-export measurements --since 30d --types 1 --format json | |
| 95 | + jq 'sort_by(.date) | last' |
| 96 | +
|
| 97 | +GOTCHAS |
| 98 | + - Times are LOCAL; JSON epoch seconds are zone-agnostic. |
| 99 | + - 'intraday' is a firehose — wide windows take minutes. |
| 100 | + - Withings rate-limits aggressive callers (HTTP 601). 'sleep --derive' throttles itself. |
| 101 | + - Sleep score / apnea fields appear only on supported devices. |
| 102 | + - 'workouts.category' is an integer code in JSON; markdown/CSV map common codes to names. |
| 103 | +``` |
| 104 | + |
| 105 | +--- |
| 106 | + |
| 107 | +## 3. Export last week's activity |
| 108 | + |
| 109 | +```sh |
| 110 | +withings-export activity --since 7d |
| 111 | +``` |
| 112 | + |
| 113 | +**Expected output** (markdown, one row per day): |
| 114 | + |
| 115 | +``` |
| 116 | +| Date | Steps | Distance (km) | Calories | Active (min) | |
| 117 | +|------------|-------|---------------|----------|--------------| |
| 118 | +| 2026-05-12 | 9240 | 6.8 | 2341 | 42 | |
| 119 | +| 2026-05-13 | 7110 | 5.2 | 2190 | 28 | |
| 120 | +``` |
| 121 | + |
| 122 | +--- |
| 123 | + |
| 124 | +## 4. Export sleep summaries |
| 125 | + |
| 126 | +```sh |
| 127 | +withings-export sleep --since 7d |
| 128 | +``` |
| 129 | + |
| 130 | +For structured data including HR and sleep stages: |
| 131 | + |
| 132 | +```sh |
| 133 | +withings-export sleep --since 7d --format json | jq '.[0]' |
| 134 | +``` |
| 135 | + |
| 136 | +--- |
| 137 | + |
| 138 | +## 5. Get most recent body weight measurement |
| 139 | + |
| 140 | +```sh |
| 141 | +withings-export measurements --since 30d --types 1 --format json \ |
| 142 | + | jq 'sort_by(.date) | last' |
| 143 | +``` |
| 144 | + |
| 145 | +`--types 1` filters to weight measurements (Withings measurement type 1). Omit `--types` to get all measurement types. |
| 146 | + |
| 147 | +--- |
| 148 | + |
| 149 | +## 6. Export workout HR data |
| 150 | + |
| 151 | +```sh |
| 152 | +withings-export workouts --since 30d --format json \ |
| 153 | + | jq '.[] | {date, category, hr: .data.hr_average}' |
| 154 | +``` |
| 155 | + |
| 156 | +`category` is an integer in JSON; markdown and CSV output maps common codes to names like `Running`, `Cycling`. |
| 157 | + |
| 158 | +--- |
| 159 | + |
| 160 | +## 7. Check flag validation (contract §4, §7) |
| 161 | + |
| 162 | +These run without credentials (hermetic by [CONTRACT.md §7](https://github.com/quantcli/common/blob/main/CONTRACT.md#7-hermeticity)): |
| 163 | + |
| 164 | +```sh |
| 165 | +withings-export activity --help # exits 0; no network call |
| 166 | +``` |
| 167 | + |
| 168 | +**Expected:** help text including `--since`, `--until`, `--format` flags; exit 0. |
| 169 | + |
| 170 | +```sh |
| 171 | +withings-export activity --format lol 2>&1; echo "exit: $?" |
| 172 | +``` |
| 173 | + |
| 174 | +**Expected:** |
| 175 | + |
| 176 | +``` |
| 177 | +Error: invalid argument "lol" for "--format" flag: must be one of: markdown, json, csv |
| 178 | +exit: 1 |
| 179 | +``` |
| 180 | + |
| 181 | +Error on stderr, nothing on stdout, exit 1. |
| 182 | + |
| 183 | +--- |
| 184 | + |
| 185 | +## 8. Run the contract conformance suite |
| 186 | + |
| 187 | +`withings-export` has the most complete compat coverage of the three CLIs: the suite stands up a stub HTTP server and a fake token so the data-path subtests run fully without real Withings credentials. |
| 188 | + |
| 189 | +```sh |
| 190 | +git clone https://github.com/quantcli/withings-export-cli |
| 191 | +cd withings-export-cli |
| 192 | +go build -o /tmp/withings-export . |
| 193 | +WITHINGS_EXPORT_BIN=/tmp/withings-export go test -tags=compat ./... |
| 194 | +``` |
| 195 | + |
| 196 | +**Expected:** |
| 197 | + |
| 198 | +``` |
| 199 | +ok github.com/quantcli/withings-export-cli 0.003s |
| 200 | +ok github.com/quantcli/withings-export-cli/internal/auth 0.003s |
| 201 | +``` |
| 202 | + |
| 203 | +The suite covers CONTRACT.md §4 (format flag surface, `--format json` returns a JSON array, `--format csv` returns a header row, default equals `--format markdown`) and §7 (hermeticity) across all five data subcommands. All cells in the CONTRACT.md Status table for `withings-export` are **machine**-attested. |
| 204 | + |
| 205 | +--- |
| 206 | + |
| 207 | +## What to look at next |
| 208 | + |
| 209 | +- `withings-export intraday --help` — minute-level HR/HRV/SpO2 (keep windows narrow) |
| 210 | +- `withings-export prime` — jq recipes and rate-limit gotchas |
| 211 | +- [CONTRACT.md §3](https://github.com/quantcli/common/blob/main/CONTRACT.md#3-date-flags) — date flag semantics |
| 212 | +- [CONTRACT.md §4](https://github.com/quantcli/common/blob/main/CONTRACT.md#4-output-format) — output format contract (`csv` is withings-only today) |
| 213 | +- [crono-export example](https://github.com/quantcli/crono-export-cli/blob/main/docs/example.md) — if you also track nutrition |
| 214 | +- [liftoff-export example](https://github.com/quantcli/liftoff-export-cli/blob/main/docs/example.md) — if you also track gym workouts |
0 commit comments