INITIAL COMMIT
This commit is contained in:
194
docs/feasibility.md
Normal file
194
docs/feasibility.md
Normal file
@ -0,0 +1,194 @@
|
||||
# Garmin Coach to Edge 1030 Feasibility
|
||||
|
||||
Research date: 2026-06-16.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The most realistic path is an unofficial Garmin Connect API probe:
|
||||
|
||||
1. Read the user's Garmin Connect calendar/training-plan data.
|
||||
2. Determine whether Garmin Coach cycling workout steps are exposed in the private training-plan/adaptive-plan JSON.
|
||||
3. If step details are present, create a normal Garmin Connect cycling workout and schedule it.
|
||||
4. Let the Edge 1030 sync that normal scheduled workout.
|
||||
|
||||
What is proven from source documentation/code:
|
||||
|
||||
- The Edge 1030 supports normal Garmin Connect structured workouts and scheduled workouts.
|
||||
- `python-garminconnect` has methods for listing normal workouts, uploading a workout JSON, scheduling/unscheduling workouts, deleting workouts, listing calendar entries, and fetching training/adaptive plan details.
|
||||
- The Garmin Connect workout JSON schema for normal workouts is partially represented by `python-garminconnect.workout`.
|
||||
|
||||
What is proven from this user's account probe:
|
||||
|
||||
- Garmin login/token reuse works with `python-garminconnect`.
|
||||
- The account has an active `FBT_ADAPTIVE` Garmin Cycling Coach plan.
|
||||
- The plan/calendar APIs expose dated Coach workout summaries, including name, date, sport type, duration, compact description, training effect label, and `workoutUuid`.
|
||||
- The Garmin Connect browser uses `/gc-api/workout-service/fbt-adaptive/{workoutUuid}` to fetch the full Coach workout with `workoutSegments` and `workoutSteps`.
|
||||
- The same detail is available to `python-garminconnect` through the Connect API path `/workout-service/fbt-adaptive/{workoutUuid}` without the browser `/gc-api` prefix.
|
||||
|
||||
What is still not proven:
|
||||
|
||||
- Whether the Edge 1030 accepts every cloned target type after sync.
|
||||
- Whether the endpoint remains stable as Garmin changes private APIs.
|
||||
- Whether a summary-derived approximation is good enough for the user's training use case.
|
||||
|
||||
The official Garmin Developer Training API is not a good fit for this use case. It is business/approval-gated, designed to publish third-party workouts/plans to users, and does not provide a documented way to read a personal Garmin Coach/adaptive plan workout from Garmin Connect.
|
||||
|
||||
## Feasibility Matrix
|
||||
|
||||
| Feature | Supported / Unsupported / Unknown | Tested / documented / guessed | API/package/source | Notes |
|
||||
|---|---:|---:|---|---|
|
||||
| Edge 1030 runs normal structured workouts from Garmin Connect | Supported | Officially documented; not tested here | Edge 1030 manual: Workouts, Starting a Workout | Manual says workouts can be created in Garmin Connect, transferred, scheduled, and run on device. |
|
||||
| Edge 1030 shows scheduled workouts in calendar/training plan | Supported | Officially documented; not tested here | Edge 1030 manual: Training Calendar | Menu path documented as `Training > Training Plan > calendar icon`. |
|
||||
| Official API can read Garmin Coach/adaptive workouts from personal account | Unsupported | Documented absence/inferred from API direction | Garmin Training API, Health API docs | Training API publishes workouts/plans to Garmin Connect; Health API reads health/activity metrics, not Coach workout definitions. |
|
||||
| Official API can create/schedule workouts | Supported with approval | Officially documented | Garmin Training API | It can publish workouts/training plans to the Garmin Connect calendar. |
|
||||
| Official API available for personal app | Unsupported for this use case | Officially documented | Garmin Developer Program FAQ | Program is for enterprise/business use and requires approval. |
|
||||
| Authenticate unofficially with Garmin Connect | Supported/fragile | Tested with user's account | `python-garminconnect` login/tokenstore source | Login worked and wrote `.garmin-tokens`. Future Garmin SSO changes remain a risk. |
|
||||
| `garth` as standalone auth library | Unsupported/fragile | Project README | `matin/garth` README | `garth` says it is deprecated and new logins may not work after Garmin auth changes. Prefer current `python-garminconnect` probe. |
|
||||
| Store/reuse Garmin tokens | Supported in source | Source-supported; untested here | `python-garminconnect` login and demo | Token directory can be loaded/dumped. Probe defaults to `.garmin-tokens`. |
|
||||
| List training plans | Supported | Tested with user's account | `Garmin.get_training_plans()` | Returned one `FBT_ADAPTIVE` plan: `2026 Fitness Plan test`. |
|
||||
| Detect active Garmin Coach/adaptive plan | Supported/partial | Tested with user's account | `trainingPlanCategory == FBT_ADAPTIVE` | The active cycling Coach plan is detectable. |
|
||||
| Fetch adaptive training-plan detail | Supported summary only | Tested with user's account | `get_adaptive_training_plan_by_id()` | Returns `taskList` with dated `taskWorkout` summaries, but no step arrays. |
|
||||
| Fetch Coach workout step details for date | Supported/fragile private API | Tested with user's account | `/workout-service/fbt-adaptive/{workoutUuid}` | Returns full `workoutSegments`/`workoutSteps` via normal `python-garminconnect` token auth. Browser shows equivalent `/gc-api/workout-service/fbt-adaptive/{workoutUuid}`. |
|
||||
| Read compact Coach workout summary | Supported | Tested with user's account | Adaptive plan `taskList[].taskWorkout` | Exposes fields such as `workoutName`, `estimatedDurationInSecs`, `workoutDescription`, `trainingEffectLabel`, `workoutPhrase`, `workoutUuid`. |
|
||||
| List Garmin calendar entries | Supported | Tested with user's account | `get_scheduled_workouts(year, month)` | Calendar contains `fbtAdaptiveWorkout` entries with `workoutUuid` and `trainingPlanId`. |
|
||||
| List normal workouts | Supported | Tested with user's account | `get_workouts()` | Returned normal cycling workouts. |
|
||||
| Fetch normal workout by ID | Supported in source | Source-supported; untested here | `get_workout_by_id()` | Useful for schema inspection and clone comparisons. |
|
||||
| Create/upload normal cycling workout | Supported in source | Source-supported; untested here | `upload_workout()`, `upload_cycling_workout()` | Probe includes a minimal dummy cycling workout upload test. |
|
||||
| Schedule normal workout on date | Supported in source | Source-supported; untested here | `schedule_workout(workout_id, YYYY-MM-DD)` | This is the preferred path if upload works. |
|
||||
| Delete generated workout | Supported in source | Implemented with guard; not account-tested | `delete_workout()` | Probe refuses to delete unless the ID is in the generated-prefix workout list and `--confirm` is passed. |
|
||||
| Unschedule generated workout | Supported in source | Implemented with guard; not account-tested | `unschedule_workout()` | Probe refuses to unschedule unless the scheduled ID is in the generated-prefix calendar list and `--confirm` is passed. |
|
||||
| Avoid duplicate clones | Partially supported | Implemented by name prefix/date | Probe code | Uses `GCClone YYYY-MM-DD` prefix. Full app should persist mappings in SQLite. |
|
||||
| Generate local FIT workout file | Possible | Official SDK documented; not implemented in probe | Garmin FIT Python SDK | SDK has an encoder. Need workout-file-specific implementation and device testing. |
|
||||
| Copy `.fit` to `GARMIN/NewFiles` | Unknown | Common Garmin behavior; not verified here | Not accepted as primary source | Treat only as manual fallback until tested on Edge 1030. |
|
||||
| Use `GARMIN/Workouts/Schedule` for date scheduling | Unknown | Not proven | No reliable official source found | Garmin Connect calendar scheduling is safer. |
|
||||
| Coach clone marks Garmin Coach workout complete | Unknown/likely unsupported | Guessed | Separate cloned workout | A clone is a normal workout, not the original Coach workout. Completion linkage may not happen. |
|
||||
|
||||
## Official Garmin API Findings
|
||||
|
||||
Garmin's official Training API is aimed at approved integrations that publish workouts and training plans into Garmin Connect. The Training API page says it can publish workouts/training plans to the Garmin Connect calendar, then users sync compatible devices.
|
||||
|
||||
This is directionally opposite of the required first step: reading a personal Garmin Coach/adaptive workout from Garmin Connect. I found no official endpoint for reading Garmin Coach plan workout definitions from a user's account.
|
||||
|
||||
The Developer Program FAQ states the Garmin Connect Developer Program is for enterprise/business use and applications are reviewed. That makes it unrealistic for a local personal tool unless the user already controls an approved business integration.
|
||||
|
||||
## Unofficial Garmin Connect Findings
|
||||
|
||||
The current `python-garminconnect` source exposes the important private endpoints:
|
||||
|
||||
- Workouts: `/workout-service`
|
||||
- Calendar: `/calendar-service`
|
||||
- Training plans: `/trainingplan-service/trainingplan`
|
||||
|
||||
Relevant methods in source:
|
||||
|
||||
- `get_workouts(start=0, limit=100)`
|
||||
- `get_workout_by_id(workout_id)`
|
||||
- `upload_workout(workout_json)`
|
||||
- `upload_cycling_workout(workout)`
|
||||
- `schedule_workout(workout_id, date_str)`
|
||||
- `get_scheduled_workouts(year, month)`
|
||||
- `get_scheduled_workout_by_id(scheduled_workout_id)`
|
||||
- `delete_workout(workout_id)`
|
||||
- `unschedule_workout(scheduled_workout_id)`
|
||||
- `get_training_plans()`
|
||||
- `get_training_plan_by_id(plan_id)`
|
||||
- `get_adaptive_training_plan_by_id(plan_id)`
|
||||
|
||||
The package demo routes plans with `trainingPlanCategory == "FBT_ADAPTIVE"` to the adaptive endpoint. That is the strongest lead for Garmin Coach/adaptive plan extraction, but it does not prove that cycling Coach step details are present for this user's account.
|
||||
|
||||
Authentication is a risk. The current `python-garminconnect` package has token storage and MFA support, while the older `garth` project now says new logins may not work because Garmin changed auth. The probe therefore treats login itself as a testable capability.
|
||||
|
||||
The probe also includes a read-only metadata call to `/workout-service/workout/types` and a normal-workout-by-ID dump. Those are intended to compare Garmin's current accepted workout schema against any Coach/adaptive JSON found during account testing.
|
||||
|
||||
For account testing, `probe_garmin.py report --dump-json` runs the read-only discovery checks in one pass and writes `debug/probe_report.json`. That report is the most compact evidence bundle for deciding whether the proof-of-concept can continue to clone testing.
|
||||
|
||||
### Account Probe Evidence
|
||||
|
||||
The user's June 16, 2026 probe found an active Garmin Cycling Coach adaptive plan:
|
||||
|
||||
- Plan ID: `45692639`
|
||||
- Category: `FBT_ADAPTIVE`
|
||||
- Name: `2026 Fitness Plan test`
|
||||
- Today's Coach workout: `Sprint`
|
||||
- Workout UUID: `fe26ad37-69e8-495b-9d3d-2ab10fb64334`
|
||||
- Estimated duration: 3120 seconds
|
||||
- Compact description: `2x13x0:10@All Out`
|
||||
|
||||
The adaptive plan detail endpoint returned `taskList[].taskWorkout` summaries, not `workoutSegments` or `workoutSteps`. The calendar endpoint returned `itemType=fbtAdaptiveWorkout` entries with matching `trainingPlanId` and `workoutUuid`.
|
||||
|
||||
The missing endpoint was found in the browser network analyzer:
|
||||
|
||||
```text
|
||||
/gc-api/workout-service/fbt-adaptive/{workoutUuid}
|
||||
```
|
||||
|
||||
For the June 16 `Sprint` workout, that response contains `workoutSegments` and nested repeat groups. Testing showed that the equivalent Connect API route also works:
|
||||
|
||||
```text
|
||||
/workout-service/fbt-adaptive/{workoutUuid}
|
||||
```
|
||||
|
||||
The CLI now uses that Connect API route when a `workoutUuid` is available. The browser `/gc-api` route returned `403` with the package token session, so it remains a browser-specific diagnostic path.
|
||||
|
||||
This means exact Coach-to-normal-workout cloning is feasible with the unofficial API.
|
||||
|
||||
## Workout Schema Findings
|
||||
|
||||
`python-garminconnect.workout` encodes the normal Garmin Connect workout schema enough for a dummy cycling workout:
|
||||
|
||||
- Sport type: cycling has `sportTypeId=2`, `sportTypeKey="cycling"`.
|
||||
- Step types include warmup, cooldown, interval, recovery, rest, repeat, other, and main.
|
||||
- End conditions include lap button, time, distance, calories, power, heart rate, iterations, fixed rest, fixed repetition, and reps.
|
||||
- Target types include no target, power zone, cadence, heart-rate zone, speed zone, pace zone, grade, heart-rate lap, power lap, and resistance.
|
||||
|
||||
The typed helpers only create simple time-based no-target steps. More exact Coach cloning will require preserving or constructing `targetType`, target values, and repeat groups from the raw Coach/adaptive JSON after the probe shows what Garmin returns.
|
||||
|
||||
Edge 1030 acceptance for target details is untested. The dummy workout intentionally starts with simple time-based no-target steps to separate "can upload/schedule/sync" from "can preserve all Coach targets".
|
||||
|
||||
## Direct Device / FIT Fallback
|
||||
|
||||
A local FIT export fallback is plausible but lower priority:
|
||||
|
||||
- Garmin's official FIT Python SDK can encode FIT files.
|
||||
- FIT workout files can represent structured workout concepts through FIT profile messages, but the probe does not implement this yet.
|
||||
- `fitparse`/`fitdecode` are primarily decoders and are not enough by themselves for reliable workout generation.
|
||||
- Copying files to Garmin device folders is not a good primary automation path because scheduling behavior on `GARMIN/Workouts/Schedule` is not reliably documented for the Edge 1030.
|
||||
|
||||
The safer first proof is still Garmin Connect upload + calendar scheduling. A FIT export can become a manual fallback if Garmin Connect upload/schedule works but Coach extraction or automated scheduling remains blocked.
|
||||
|
||||
## Stop / Continue Decision
|
||||
|
||||
Do not build the web app yet.
|
||||
|
||||
Continue to Phase 2 probe because:
|
||||
|
||||
- Normal workout upload/scheduling is source-supported and can be tested with a dummy workout.
|
||||
- Adaptive/Coach endpoint access is source-supported enough to probe.
|
||||
|
||||
Stop before Phase 3 unless the user confirms at least:
|
||||
|
||||
- The dummy scheduled workout appears on the Edge 1030.
|
||||
- Garmin Coach/adaptive JSON contains enough step data to clone through the FBT adaptive endpoint, or the agreed fallback is summary-derived/manual template generation.
|
||||
|
||||
If the FBT adaptive endpoint changes or becomes unavailable, the best fallback architecture is:
|
||||
|
||||
- Keep Garmin Connect login/calendar/workout discovery.
|
||||
- Fall back to parsing adaptive plan `taskWorkout` summaries and producing clearly labeled approximate normal workouts.
|
||||
- Allow manual review/edit of generated steps before upload.
|
||||
- Optionally export FIT workouts for manual device copy.
|
||||
- Do not claim unattended exact Garmin Coach cloning unless browser endpoint authentication is stable.
|
||||
|
||||
## Sources
|
||||
|
||||
- Garmin Training API: <https://developer.garmin.com/gc-developer-program/training-api/>
|
||||
- Garmin Developer Program FAQ: <https://developer.garmin.com/gc-developer-program/program-faq/>
|
||||
- Garmin Developer Program Overview: <https://developer.garmin.com/gc-developer-program/overview/>
|
||||
- Garmin Health API: <https://developer.garmin.com/gc-developer-program/health-api/>
|
||||
- Edge 1030 Workouts manual: <https://www8.garmin.com/manuals/webhelp/edge1030/EN-US/GUID-99D42128-10E4-4AA4-B961-58FD70A431A0.html>
|
||||
- Edge 1030 Following a Workout From Garmin Connect: <https://www8.garmin.com/manuals/webhelp/edge1030/EN-US/GUID-D6E80F0C-F319-47D1-AF95-0884F3386635.html>
|
||||
- Edge 1030 Starting a Workout: <https://www8.garmin.com/manuals/webhelp/edge1030/EN-US/GUID-5CA69960-1471-47A1-867E-820FD0B240C8.html>
|
||||
- Edge 1030 Training Calendar: <https://www8.garmin.com/manuals/webhelp/edge1030/EN-US/GUID-104A175C-4F28-4995-8476-42405A543F11.html>
|
||||
- `python-garminconnect`: <https://github.com/cyberjunky/python-garminconnect>
|
||||
- `python-garminconnect` workout models: <https://github.com/cyberjunky/python-garminconnect/blob/master/garminconnect/workout.py>
|
||||
- `garth` deprecation notice: <https://github.com/matin/garth>
|
||||
- Garmin FIT Python SDK: <https://github.com/garmin/fit-python-sdk>
|
||||
Reference in New Issue
Block a user