Automatically sync Withings scale weight and Garmin Connect workout data to a Google Sheet — daily, via a Raspberry Pi (or any always-on Linux box).
A Python script runs once a day via systemd timer. It:
- Fetches your latest weight from the Withings API
- Checks Garmin Connect for any logged activity today
- Finds today's row in your Google Sheet and writes the weight + an "x" in the workout column
- Raspberry Pi (or any always-on Linux box) with Python 3.9+
- A Withings scale syncing to the Withings app
- A Garmin watch syncing to Garmin Connect
- A Google Sheet with pre-filled dates in column A, weight in column B, and a workout marker in column J
git clone https://github.com/YOUR_USER/weight-sync.git
cd weight-sync
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp config.example.yaml config.yaml- Go to Google Cloud Console
- Create a project and enable the Google Sheets API
- Go to Credentials → Create Credentials → Service Account
- Choose "Application data" when asked
- Download the JSON key and save it to the project directory
- Share your Google Sheet with the service account email (the
client_emailin the JSON) - Copy the spreadsheet ID from the URL into
config.yaml
- Go to developer.withings.com and create an application
- Set the callback URL to
http://localhost:8585/callback - Copy the Client ID and Client Secret into
config.yaml - Run the authorization flow:
source venv/bin/activate
python3 setup_withings.pyFollow the prompts — open the URL, authorize, paste the code.
Add your Garmin email and password to config.yaml, then run the one-time login:
source venv/bin/activate
python3 setup_garmin.pyThis will prompt for MFA. If the interactive prompt doesn't work, use the hardcoded approach documented in setup_garmin.py.
Edit config.yaml with your credentials, spreadsheet ID, and worksheet name:
withings:
client_id: "..."
client_secret: "..."
garmin:
email: "..."
password: "..."
google:
credentials_path: "/path/to/google-credentials.json"
spreadsheet_id: "..."
sheet: "Sheet1"source venv/bin/activate
python3 sync.pyEdit weight-sync.service and replace YOUR_USER and YOUR_HOME with your actual values, then:
sudo cp weight-sync.service /etc/systemd/system/
sudo cp weight-sync.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now weight-sync.timerThe default schedule is 22:00 daily. Edit weight-sync.timer to change it.
Check status:
systemctl list-timers weight-sync.timer
journalctl -u weight-sync.service -n 30Column A has pre-filled dates. The script reads all of column A, matches today's date, and writes to that row. Weight goes to column B, workout marker to column J. If you rearrange columns, update the column letters in update_sheet().
- Withings: Tokens auto-refresh on every run.
- Garmin: Tokens are re-saved after each login. If they expire after extended downtime, re-run
setup_garmin.py.
| Problem | Fix |
|---|---|
| No row found for today | Check that column A dates match a supported format (dd.mm.yyyy, yyyy-mm-dd, etc.) |
| Withings token refresh fails | Re-run setup_withings.py |
| Garmin login fails | Re-run setup_garmin.py — MFA tokens may have expired |
| Sheet not updating | Make sure the sheet is shared with the service account email |
APIError: 400 |
Make sure the sheet is a native Google Sheet, not an uploaded .xlsx |
MIT