153 lines
4.4 KiB
Markdown
153 lines
4.4 KiB
Markdown
# 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:
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
```
|
|
|
|
Edit `.env`, then run:
|
|
|
|
```sh
|
|
docker compose up -d --build
|
|
docker compose logs -f sync
|
|
```
|
|
|
|
The default polling interval is hourly.
|
|
|
|
## Configuration
|
|
|
|
Required values:
|
|
|
|
```env
|
|
MYWHOOSH_EMAIL=
|
|
MYWHOOSH_PASSWORD=
|
|
GARMIN_EMAIL=
|
|
GARMIN_PASSWORD=
|
|
```
|
|
|
|
Useful optional values:
|
|
|
|
```env
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
docker compose -f docker-compose.debug.yml up --build
|
|
```
|
|
|
|
Then open noVNC:
|
|
|
|
```text
|
|
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:
|
|
|
|
```env
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```text
|
|
./data/debug
|
|
```
|
|
|
|
After a successful manual login, stop the debug container and run the normal service:
|
|
|
|
```sh
|
|
docker compose up -d --build
|
|
```
|
|
|
|
## Session Persistence
|
|
|
|
MyWhoosh auth is persisted in two places under the mounted `./data` directory:
|
|
|
|
```text
|
|
./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:
|
|
|
|
```text
|
|
./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:
|
|
|
|
```env
|
|
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.
|