INITIAL COMMIT
This commit is contained in:
133
README.md
Normal file
133
README.md
Normal file
@ -0,0 +1,133 @@
|
||||
# Garmin Coach to Calendar Sync
|
||||
|
||||
This tool clones Garmin Coach/adaptive cycling workouts into normal Garmin Connect cycling workouts and schedules the generated clone on your Garmin calendar. The target use case is an original Garmin Edge 1030: the Edge can sync and run the generated structured workout like a normal workout while still using normal Edge navigation.
|
||||
|
||||
It uses unofficial Garmin Connect APIs. Run it locally or on a private network unless you harden it yourself.
|
||||
|
||||
## Current Status
|
||||
|
||||
Implemented:
|
||||
|
||||
- Feasibility research in [docs/feasibility.md](/home/yannik/repos/garmin-coach-to-cal-sync/docs/feasibility.md).
|
||||
- CLI probe scripts for Garmin login, discovery, Coach extraction, dummy upload/schedule, and clone dry-run.
|
||||
- Dockerized FastAPI web app with first-launch local login setup, Garmin login/MFA, manual sync, scheduled change detection, logs, traces, search, and schedule editing.
|
||||
- SQLite state under `/data/app.db`.
|
||||
- Persistent Garmin tokens under `/data/garmin-tokens`.
|
||||
- Coolify-compatible `Dockerfile` and `docker-compose.yml`.
|
||||
|
||||
Still fragile:
|
||||
|
||||
- Garmin can change private API endpoints or authentication behavior.
|
||||
- Garmin Coach completion may not be marked when you complete the generated clone.
|
||||
- Edge 1030 acceptance must be verified on your device after upload/schedule.
|
||||
- FIT export fallback is not implemented because Garmin Connect scheduling is the primary path.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Open [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
First launch:
|
||||
|
||||
1. Create the local app username/password.
|
||||
2. Open `Garmin`.
|
||||
3. Enter Garmin credentials.
|
||||
4. Enter MFA if Garmin asks for it.
|
||||
5. Use `Search` to inspect Coach/calendar/workout visibility.
|
||||
6. Use `Dashboard > Dry-run today` first, then `Check today` or `Check tomorrow`.
|
||||
|
||||
## Coolify Deployment
|
||||
|
||||
Deploy from the repository link.
|
||||
|
||||
Set:
|
||||
|
||||
- Container port: `8000`
|
||||
- Persistent storage mount: `/data`
|
||||
- `APP_SECRET_KEY`: a long random secret
|
||||
- `TZ`: `Europe/Berlin`
|
||||
- Optional bootstrap `APP_USERNAME` / `APP_PASSWORD`
|
||||
|
||||
The compose file uses a named volume for local Docker. In Coolify, configure persistent storage for `/data` instead of a repo bind mount. Runtime state, Garmin tokens, SQLite, logs, and traces all live under `/data`.
|
||||
|
||||
## Environment
|
||||
|
||||
Important defaults are in [.env.example](/home/yannik/repos/garmin-coach-to-cal-sync/.env.example):
|
||||
|
||||
```text
|
||||
TZ=Europe/Berlin
|
||||
DATA_DIR=/data
|
||||
APP_USERNAME=admin
|
||||
APP_PASSWORD=change-me
|
||||
APP_SECRET_KEY=replace-with-a-long-random-secret
|
||||
SYNC_ENABLED=true
|
||||
SYNC_DAYS_AHEAD=1
|
||||
CLONE_PREFIX=GCClone
|
||||
OVERWRITE_EXISTING=true
|
||||
DELETE_OLD_CLONES=false
|
||||
CHANGE_DETECTION_INTERVAL_MINUTES=30
|
||||
CHANGE_DETECTION_ACTIVE_WINDOW=05:00-22:00
|
||||
CHANGE_DETECTION_FIXED_TIMES=05:15,06:15,07:15,08:15,09:15,10:15,11:15,12:15,13:15
|
||||
```
|
||||
|
||||
Scheduled runs are change-detection runs. They create a missing clone, skip an unchanged clone, and replace only when the Coach source hash changes. If a Coach day becomes rest/empty while a generated clone exists, scheduled sync records a warning and leaves the clone alone.
|
||||
|
||||
## Web UI
|
||||
|
||||
- `Dashboard`: Garmin status, last run, next run, clone state, manual sync actions.
|
||||
- `Garmin`: store encrypted Garmin credentials and complete MFA.
|
||||
- `Search`: list calendar entries, normal workouts, plans, Coach tasks, and generated clones.
|
||||
- `Schedule`: edit sync enabled, interval window, fixed `:15` checks, days ahead, or restore defaults.
|
||||
- `Traces`: inspect source Coach task/detail, generated payload, source hash, and upload/schedule result.
|
||||
- `Logs`: sync events and warnings.
|
||||
|
||||
Passwords, MFA codes, cookies, headers, and tokens are not logged. Trace JSON is redacted before storage.
|
||||
|
||||
## CLI Probe Commands
|
||||
|
||||
The CLI remains useful for low-level debugging:
|
||||
|
||||
```sh
|
||||
uv run python scripts/probe_garmin.py login
|
||||
uv run python scripts/probe_garmin.py report --dump-json
|
||||
uv run python scripts/probe_garmin.py coach --date today --dump-json
|
||||
uv run python scripts/clone_today_workout.py --date today --dry-run --dump-json
|
||||
uv run python scripts/clone_today_workout.py --date today --schedule
|
||||
```
|
||||
|
||||
Run local verification:
|
||||
|
||||
```sh
|
||||
uv run ruff check .
|
||||
uv run ty check
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
## Edge 1030 Testing
|
||||
|
||||
After a web or CLI sync creates a clone:
|
||||
|
||||
1. Sync the Edge 1030.
|
||||
2. Check `Training > Workouts`.
|
||||
3. Check `Training > Training Plan > calendar icon > scheduled date`.
|
||||
4. Start a route/course.
|
||||
5. Start the generated workout and verify prompts run while navigation remains active.
|
||||
|
||||
More detail is in [docs/edge1030-testing.md](/home/yannik/repos/garmin-coach-to-cal-sync/docs/edge1030-testing.md).
|
||||
|
||||
## Cleanup
|
||||
|
||||
Generated workouts use the `GCClone YYYY-MM-DD ...` prefix by default. Scheduled automation never deletes a current clone just because a check ran. Replacement only happens when the source hash changes and the stored mapping points to a generated clone.
|
||||
|
||||
Use the CLI guarded cleanup helpers if you need manual cleanup:
|
||||
|
||||
```sh
|
||||
uv run python scripts/probe_garmin.py generated --prefix GCClone
|
||||
uv run python scripts/probe_garmin.py generated-calendar --date today --prefix GCClone
|
||||
```
|
||||
|
||||
Delete or unschedule only IDs shown by those generated-prefix listings.
|
||||
Reference in New Issue
Block a user