Skip to content
<- ~/projects
eufy-sync/

Eufy Sync

My Eufy scale tracks weight, body fat, muscle mass - but it doesn't sync to Garmin or Strava. Every library that could talk to Garmin broke. So I built my own.

PythonPlaywrightSQLiteFIT ProtocolOAuth2

> cat eufy-sync/problem.md

## THE PROBLEM

My Eufy scale tracks everything - weight, body fat, muscle mass, bone mass, the whole picture. It syncs to Apple Health, Fitbit, and Google Fit. But not Garmin. Not Strava. If you use either one for training, your body comp data just sits in a separate app doing nothing. I figured someone had already solved this. They hadn't. Every Python library that talked to Garmin broke in March 2026 when Garmin put Cloudflare in front of their login. The usual approach of logging in programmatically was dead.

> cat eufy-sync/approach.md

## THE APPROACH

  • -Tried every existing Garmin library - garth, python-garminconnect - all broken or deprecated because of Cloudflare
  • -Built a workaround with Playwright: on first run, a real browser opens and you log in normally. The OAuth tokens get saved to your Mac's keychain and auto-refresh for about a year
  • -After that first login, no browser needed. Body comp goes up as FIT files through Garmin's upload endpoint
  • -Designed it to support multiple sync targets from the start, so adding Strava later was a one-file change
  • -Credentials go in the system keychain, not some config file sitting in plaintext

> cat eufy-sync/build.md

## THE BUILD

eufy-sync --verbose
eufy-sync --verbose
  • -Python CLI you install with pipx - first run walks you through setup
  • -Pulls your full measurement history from Eufy's cloud API
  • -Generates FIT files for Garmin with everything: weight, body fat %, muscle mass, bone mass, hydration, BMR, visceral fat, metabolic age
  • -Strava gets weight updates through their OAuth2 API with a local callback server
  • -SQLite tracks what's been synced per target so nothing gets uploaded twice
  • -A macOS Launch Agent syncs every 4 hours in the background - you get a notification if something fails
  • -Checks Garmin for existing entries before uploading so you don't get duplicates across machines
  • -No plaintext secrets anywhere. Keychain for credentials, 600 permissions on config files, outbound calls only to Eufy, Garmin, and Strava

> cat eufy-sync/outcome.md

## THE OUTCOME

eufy-sync --status
eufy-sync --status
  • -Published on PyPI as open source with 73 tests
  • -Fully automated. Weigh yourself, open your laptop later, it syncs on its own
  • -Went from v1.0 (Garmin only) to v1.7.0 - added Strava, auto-backfill for new targets, a history command, and security fixes from code review
  • -Filled a real gap. If you have a Eufy scale and use Garmin or Strava, this is the only tool that works