Files
mywhoosh-garmin-sync/README.md
Bastian Wagner e0ea9efe92 env angepasst
2026-05-05 20:08:15 +02:00

4.7 KiB

MyWhoosh Garmin Sync

Containerized background sync for personal MyWhoosh activities:

  1. Log in to MyWhoosh with Playwright.
  2. Find and download new .fit activity files.
  3. Rewrite FIT device metadata locally to Garmin Edge 1030 Plus.
  4. Upload the converted activity to Garmin Connect.
  5. Persist sessions, downloaded files, converted files, and sync state under /data.

Quick Start

Create your local env file:

cp .env.example .env

Edit .env, then run:

docker compose -f docker-compose.debug.yml up --build

If this runs on a remote Linux server, keep the default localhost-only port binding and tunnel it:

ssh -L 6080:localhost:6080 user@your-server

Then open noVNC:

http://localhost:6080/vnc.html

Login into your MyWhoosh account with keep me signed in (Important!). Exit.

Start the production container

docker compose up -d --build
docker compose logs -f sync

The default polling interval is hourly.

Configuration

Required values:

GARMIN_EMAIL=
GARMIN_PASSWORD=

Useful optional values:

POLL_INTERVAL_SECONDS=3600
DATA_DIR=/data
LOG_LEVEL=INFO
DRY_RUN=false
MYWHOOSH_LOGIN_URL=https://www.mywhoosh.com/login/
MYWHOOSH_ACTIVITY_URL=https://www.mywhoosh.com/profile/
MYWHOOSH_ACTIVITIES_BUTTON_TEXT=ACTIVITIES
MYWHOOSH_DOWNLOAD_BUTTON_SELECTOR=.btnDownload
TARGET_GARMIN_PRODUCT_ID=3578
TARGET_GARMIN_SERIAL_NUMBER=

If Garmin MFA is required on first login, set GARMIN_MFA_CODE for one run, let the token store persist under /data/garmin_tokens, then remove the variable.

Commands

Inside the container:

python -m mywhoosh_garmin_sync run-once
python -m mywhoosh_garmin_sync serve
python -m mywhoosh_garmin_sync convert --input /data/raw/example.fit --output /data/converted/example.fit

DRY_RUN=true downloads and converts files but does not upload to Garmin.

Run tests from the built image:

docker build -t mywhoosh-garmin-sync:test .
docker run --rm --entrypoint sh mywhoosh-garmin-sync:test -c "pip install -r requirements-dev.txt && pytest"

Browser Debugging

Use the debug compose file when MyWhoosh blocks automated login with cookies, captcha, or bot checks:

docker compose -f docker-compose.debug.yml up --build

Then open noVNC:

http://localhost:6080/vnc.html

Click Connect. You should see Chromium inside the container. Cookie consent is accepted automatically where the banner uses a normal consent button. Captcha/bot checks are left for you to solve manually. The debug run uses:

MYWHOOSH_HEADLESS=false
MYWHOOSH_MANUAL_LOGIN_WAIT_SECONDS=900
MYWHOOSH_SLOW_MO_MS=250
MYWHOOSH_DEBUG_SCREENSHOTS=true
DRY_RUN=true

If this runs on a remote Linux server, keep the default localhost-only port binding and tunnel it:

ssh -L 6080:localhost:6080 user@your-server

Then open http://localhost:6080/vnc.html on your own machine. During the 900-second manual-login window, accept cookies, solve the challenge, and finish the MyWhoosh login in the visible browser. The browser profile is stored in ./data/browser, so the normal headless service can reuse the session later.

Debug screenshots are written to:

./data/debug

After a successful manual login, stop the debug container and run the normal service:

docker compose up -d --build

Session Persistence

MyWhoosh auth is persisted in two places under the mounted ./data directory:

./data/browser
./data/mywhoosh_auth_state.json

Keep that directory when recreating containers. Use docker compose down when switching between debug and normal mode, but do not use docker compose down -v and do not delete ./data unless you want to log in again.

If Chromium reports that the profile is locked after a crash, stop all containers and remove only these root files:

./data/browser/SingletonCookie
./data/browser/SingletonLock
./data/browser/SingletonSocket

Notes

MyWhoosh does not appear to publish a stable public activity export API. This project uses a persisted headless browser session and conservative hourly polling. If MyWhoosh changes its page structure, adjust these .env values before changing code:

MYWHOOSH_LOGIN_URL=
MYWHOOSH_ACTIVITY_URL=
MYWHOOSH_DOWNLOAD_TEXT_HINTS=fit,download
MYWHOOSH_ACTIVITIES_BUTTON_TEXT=ACTIVITIES
MYWHOOSH_DOWNLOAD_BUTTON_SELECTOR=.btnDownload

The FIT conversion patches file_id and device_info messages where Garmin manufacturer/product fields are present, then recalculates header and file CRCs. The default Garmin product ID is configurable so it can be corrected without a rebuild if Garmin FIT profile values change.

This automates personal account actions. Keep polling conservative and make sure the way you use it is compatible with the services' terms.