feat(garmin): add Garmin Connect adapter#2006
Open
yixin-1024 wants to merge 5 commits into
Open
Conversation
Garmin Connect's modern web app reads its data from same-origin JSON services under /gc-api/...; those authenticate off the logged-in session cookie plus a `connect-csrf-token` header (published in a <meta> tag on every Connect page) and `NK: NT`. This adapter (Strategy.COOKIE) reproduces exactly that from the bound tab — a clean JSON-API adapter, no DOM scraping. Commands (10): - whoami / login identity via userprofile-service/socialProfile (shared site-auth) - activities recent activities (id, name, type, distance, duration, calories, hr) - activity <id> one activity in detail (+ max hr, elevation, avg speed, location) - stats [--date] daily wellness summary (steps, calories, distance, floors, intensity) - sleep [--date] sleep breakdown (deep / light / rem / awake) - heartrate [--date] resting / max / min heart rate - prs personal records - gear registered gear - devices registered devices Pure formatters (metersToKm / secondsToHms / isoDate / id-parse) live in utils.js with unit tests. Dates default to today; ids accept bare/path/URL forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expands the Garmin adapter from 10 to 20 commands, all over the same /gc-api JSON services: - status training status, fitness trend, VO2 max (run + bike) - load acute (short-term) / chronic training load, ACWR, load focus - powercurve cycling power curve (best watts per duration) — the FTP curve - bodybattery Body Battery charged / drained / current - stress daily average / max stress - weight body-weight log (kg, BMI) - badges earned badges - courses saved routes (路书) — name, type, distance, elevation - course <id> one route in detail (distance, gain/loss, geo route id) - connections Garmin Connect friends status/load/VO2 max all come from the trainingstatus/aggregated service; power curve from fitnessstats-service. Athlete search was left out — Garmin's web search endpoints are gated (403) and don't return query-matched results. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the social side (25 commands total):
- search <keyword> find athletes — POSTs the "Find Friends" usersearch-service
/search/v3 form endpoint, returns name/location/follow status
- follow <athlete> follow an athlete (POST follower-service), guarded by --execute
- unfollow <athlete> stop following (DELETE), guarded by --execute
- following athletes you follow
- followers athletes who follow you
- connections fixed to the connection-service v2 pagination endpoint
garminApi() now supports POST/DELETE with form bodies and 204 responses;
adds requireExecute() and a displayName (GUID / profile-URL) normalizer.
Note: following a private athlete creates a pending follow *request* (it only
appears in `following` once approved); `unfollow` removes an active follow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Four more /gc-api wellness reads (29 commands total): - hrv [--date] heart-rate variability (last night / weekly avg, status) - hydration [--date] water intake vs goal, sweat loss - respiration [--date] breaths per minute (waking / sleep / high / low) - spo2 [--date] blood-oxygen / pulse ox (avg / lowest / latest) Each returns EmptyResultError cleanly when the day has no measurement (e.g. HRV / SpO2 need a compatible watch worn overnight). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two PR CI gates flagged the new adapter; both are satisfied without touching adapter behaviour: - docs/adapters/browser/garmin.md: the doc-coverage --strict gate requires every clis/* adapter to have a doc page. Documents all 29 commands (Account / Activities / Wellness / Social grouping, examples, --execute notes, login prerequisite). - silent-column-drop baseline: the gate's heuristic scans every object literal in a command's source file. Because related commands share one file (profile.js, health.js, social.js, ...), each command's row object is also audited against its siblings' columns and flagged. Every row this adapter actually returns matches its declared columns exactly. Adopt the 25 false-positive signatures into the baseline, matching the existing entries for the twitter / xiaohongshu / bilibili / weibo / zhihu scrapers. Purely additive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hi @jackwener 👋 gentle nudge on this one. Why it matters: Garmin has the largest installed base of dedicated sports / wearable devices in the world, and Garmin Connect is where all of that activity, health and device data lives. An adapter makes that data reachable for agents (activities, daily metrics, device data, etc.) using the user's own logged-in session. Clean and mergeable, no conflicts. Glad to iterate on anything you'd like changed. Thanks! 🙏 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Garmin Connect adapter
Adds a Garmin Connect adapter (
clis/garmin/). Garmin's modern web app reads its data from same-origin JSON services under/gc-api/.... Those authenticate off the logged-in session cookie plus aconnect-csrf-tokenrequest header (published in a<meta>tag on every Connect page) andNK: NT. This adapter (Strategy.COOKIE) reproduces exactly that from the bound tab — so it's a clean JSON-API adapter, no DOM scraping.Commands (10)
whoami/loginuserprofile-service/socialProfileactivities [--limit] [--start]activitylist-serviceactivity <id>activity-service/activitystats [--date]usersummary-servicesleep [--date]wellness-service/dailySleepDataheartrate [--date]wellness-service/dailyHeartRateprspersonalrecord-servicegeargear-servicedevicesdevice-serviceDates default to today; activity ids accept bare/path/URL forms. Pure formatters (
metersToKm/secondsToHms/isoDate/ id-parse) live inutils.jswith unit tests.Testing
opencli validate garmin→ PASS (10 commands, 0 errors / 0 warnings)vitest run clis/garmin/utils.test.js→ 4 passingsleepreturnsEmptyResultErrorcleanly for accounts with no sleep-tracking device.🤖 Generated with Claude Code