Init
This commit is contained in:
152
README.md
Normal file
152
README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user