No description
  • TypeScript 87.3%
  • Python 5.8%
  • C 4.5%
  • Shell 2%
  • Dockerfile 0.2%
  • Other 0.2%
Find a file
Kristian Partl f66c40b75d
Some checks are pending
CI / typecheck-and-test (22) (push) Waiting to run
CI / typecheck-and-test (24) (push) Waiting to run
CI / typecheck-and-test (26) (push) Waiting to run
CI / python-check (push) Waiting to run
Deploy Docs / deploy (push) Waiting to run
Release Please / release-please (push) Waiting to run
chore(main): release 1.15.0 (#209)
🤖 I have created a release *beep* *boop*
---


##
[1.15.0](https://github.com/KristianP26/ble-scale-sync/compare/v1.14.1...v1.15.0)
(2026-05-21)


### Added

* **exporter:** add Intervals.icu exporter
([#203](https://github.com/KristianP26/ble-scale-sync/issues/203))
([60af3d0](60af3d0e98))
* **exporter:** add Telegram exporter
([#207](https://github.com/KristianP26/ble-scale-sync/issues/207))
([0b73e0f](0b73e0f766))
* **firmware:** support generic ESP-WROOM-32 boards
([487e482](487e482084))


### Fixed

* **ble:** route dual-mode adapters to GATT when no broadcast data
([#201](https://github.com/KristianP26/ble-scale-sync/issues/201))
([9de2dee](9de2deea0f))
* **exporters:** fail fast on non-retryable HTTP errors + validate
required config
([6ebea77](6ebea77b81))
* **firmware:** harden constrained ESP32 boards against scan-buffer OOM
([c99605e](c99605e22f))
* **firmware:** parse 128-bit / 32-bit service UUIDs and service data
([bf918ad](bf918ad75f))
* **firmware:** publish service-UUID-only devices in scan results
([c1f9723](c1f9723013)),
closes [#201](https://github.com/KristianP26/ble-scale-sync/issues/201)


### Docs

* bump adapter and exporter counts to 25 and 8
([0e21abe](0e21abe88f))
* **scales:** note Mi Scale 2 works on all BLE transports
([e126848](e126848ad1))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
2026-05-21 15:53:02 +02:00
.github test(firmware): add host-runnable tests for AD parser 2026-05-20 16:44:30 +02:00
ble-scale-sync-addon chore(main): release 1.15.0 (#209) 2026-05-21 15:53:02 +02:00
docs feat(exporter): add Intervals.icu exporter (#203) 2026-05-21 15:26:12 +02:00
drivers Clean up dead code, fix flash.sh newline quoting, add comments 2026-02-25 11:46:27 +11:00
firmware fix(exporters): fail fast on non-retryable HTTP errors + validate required config 2026-05-21 15:47:07 +02:00
garmin-scripts feat: replay cached offline frames with timestamps (#164) 2026-05-13 11:22:48 +02:00
src fix(exporters): fail fast on non-retryable HTTP errors + validate required config 2026-05-21 15:47:07 +02:00
tests fix(exporters): fail fast on non-retryable HTTP errors + validate required config 2026-05-21 15:47:07 +02:00
worker Merge main (v1.7.2) into fix/bluez-orphaned-discovery 2026-04-01 12:23:23 +02:00
.dockerignore feat: Home Assistant Add-on with MQTT auto-detection 2026-03-29 19:30:28 +02:00
.env.example feat(exporter): add Intervals.icu exporter (#203) 2026-05-21 15:26:12 +02:00
.gitattributes chore: add .gitattributes to enforce LF line endings for shell scripts 2026-03-29 19:39:18 +02:00
.gitignore fix(addon): unignore add-on config.yaml, warn on missing MQTT broker 2026-03-29 19:46:46 +02:00
.nvmrc chore: upgrade Node.js to v22 and sync GitHub Actions workflow (#195) 2026-05-18 20:09:19 +02:00
.prettierrc chore: add endOfLine auto to Prettier for cross-platform compatibility 2026-02-25 06:03:55 +01:00
.release-please-config.json ci(release): switch addon config.yaml to generic updater with annotation markers 2026-05-01 20:27:31 +02:00
.release-please-manifest.json chore(main): release 1.15.0 (#209) 2026-05-21 15:53:02 +02:00
CHANGELOG.md chore(main): release 1.15.0 (#209) 2026-05-21 15:53:02 +02:00
CODE_OF_CONDUCT.md Add Contributor Covenant Code of Conduct 2026-02-10 20:48:09 +01:00
config.yaml.example feat(exporter): add Intervals.icu exporter (#203) 2026-05-21 15:26:12 +02:00
CONTRIBUTING.md feat(exporter): add Intervals.icu exporter (#203) 2026-05-21 15:26:12 +02:00
docker-compose.example.yml fix: preemptive btmgmt reset after GATT to clear BlueZ zombie state (#80) 2026-04-15 19:50:44 +02:00
docker-compose.mqtt-proxy.yml Consolidate Renpho broadcast parsing into QN scale adapter 2026-02-25 11:48:18 +11:00
docker-entrypoint.sh fix(docker): add missing setup-strava command to entrypoint (#188) (#189) 2026-05-18 14:08:56 +02:00
Dockerfile feat: drop Node 20 support, require Node 22+ 2026-05-12 15:51:53 +02:00
eslint.config.js Add ESLint + Prettier, .env validation, and scale adapter tests 2026-02-09 20:28:45 +01:00
LICENSE Rename project references from 'blescalesync' to 'ble-scale-sync' for consistency 2026-02-10 17:59:54 +01:00
package-lock.json chore(main): release 1.15.0 (#209) 2026-05-21 15:53:02 +02:00
package.json chore(main): release 1.15.0 (#209) 2026-05-21 15:53:02 +02:00
PORTING.md #139 Add BLE Proxy support for ESP-WROOM-32 2026-04-27 22:28:43 +10:00
README.md feat(exporter): add Intervals.icu exporter (#203) 2026-05-21 15:26:12 +02:00
repository.yaml feat: Home Assistant Add-on with MQTT auto-detection 2026-03-29 19:30:28 +02:00
requirements.txt fix(garmin): migrate Python bridge to garminconnect 0.3.x (#114) 2026-04-20 10:20:26 +02:00
SECURITY.md Add repository hardening: security policy, templates, dependabot 2026-02-14 13:46:51 +01:00
tsconfig.eslint.json Add ESLint + Prettier, .env validation, and scale adapter tests 2026-02-09 20:28:45 +01:00
tsconfig.json Add TypeScript support and implement BLE scale integration 2026-02-08 21:09:19 +01:00

BLE Scale Sync

CI GitHub Release License: GPL-3.0 TypeScript Node.js Docker

A cross-platform CLI tool that reads body composition data from 25+ BLE smart scales and exports to Garmin Connect, Strava, Intervals.icu, MQTT (Home Assistant), InfluxDB, Webhooks, Ntfy, Telegram, and local files (CSV/JSONL). No phone app needed. Your data stays on your device.

Documentation · Getting Started · Supported Scales · Exporters · FAQ

Why This Exists

Most BLE smart scales measure weight and body impedance over Bluetooth, but their companion apps have no way to sync data to Garmin Connect. The only workflow was: open the phone app, wait for it to sync, then manually type the numbers into Garmin. Every single time.

I didn't want to depend on a phone app. So I built this tool. A Raspberry Pi Zero 2W sits next to the scale, always on, always listening. Step on the scale, wait a few seconds, and the reading appears in Garmin Connect - no phone needed, no app, no manual entry. It just works.

If you can't have a Pi next to your scale, a cheap ESP32 proxy can sit nearby and relay BLE data over WiFi to a Docker server anywhere on your network. See the ESP32 BLE proxy guide.

Quick Start

Docker (Linux)

# Configure
docker run --rm -it --network host --cap-add NET_ADMIN --cap-add NET_RAW \
  --group-add 112 -v /var/run/dbus:/var/run/dbus:ro \
  -v ./config.yaml:/app/config.yaml \
  -v ./garmin-tokens:/app/garmin-tokens \
  ghcr.io/kristianp26/ble-scale-sync:latest setup

# Run (continuous mode, auto-restart)
docker run -d --restart unless-stopped --network host \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --group-add 112 --device /dev/rfkill \
  -v /var/run/dbus:/var/run/dbus:ro \
  -v ./config.yaml:/app/config.yaml:ro \
  -v ./garmin-tokens:/app/garmin-tokens:ro \
  -e CONTINUOUS_MODE=true \
  ghcr.io/kristianp26/ble-scale-sync:latest

Ideal for Raspberry Pi, NAS, and headless servers. Works alongside any Home Assistant install (Container, Core, OS) via MQTT auto-discovery.

Home Assistant Add-on

If you run Home Assistant OS or Supervised, one click is all it takes:

Add BLE Scale Sync repository to your Home Assistant

The badge opens your Home Assistant instance, confirms the repository, and shows BLE Scale Sync in the Add-on Store ready to install.

Prefer manual steps?
  1. Settings > Add-ons > Add-on Store > three-dot menu > Repositories
  2. Add https://github.com/KristianP26/ble-scale-sync and install BLE Scale Sync

The add-on handles config through the UI, auto-detects the Mosquitto broker for Home Assistant auto-discovery, and bootstraps Garmin tokens on first start. See the Home Assistant Add-on guide for the full option reference, MFA workaround, and custom config mode.

Note: Add-ons are not available on HA Container or HA Core installs (no Supervisor). Use the Docker method above instead. Sensors still appear in HA via MQTT auto-discovery.

Standalone (Node.js, Linux/macOS/Windows)

Runs natively on all major desktop and server operating systems. No containers required.

git clone https://github.com/KristianP26/ble-scale-sync.git
cd ble-scale-sync && npm install
npm run setup                       # interactive wizard
CONTINUOUS_MODE=true npm start      # always-on

Requires Node.js v22+ and a BLE adapter. See the full install guide for prerequisites and systemd service setup.

Features

  • 25+ scale brands. Xiaomi (Mi Scale 2 passive broadcast), Renpho (Elis 1, FITINDEX, Sencor, QN-Scale), Eufy, Yunmai, Beurer (incl. BF720 / BF105), Sanitas, Medisana, and more.
  • 9 export targets. Garmin Connect, Strava, Intervals.icu, MQTT (Home Assistant), InfluxDB, Webhook, Ntfy, Telegram, File (CSV/JSONL).
  • 10 body metrics. BIA-based body composition from weight + impedance.
  • Multi-user. Automatic weight-based identification with per-user exporters.
  • Historical sync. Replays a scale's onboard cache of offline measurements with their original timestamps to exporters that support back-dating (Garmin Connect, InfluxDB, File).
  • Interactive setup wizard. Scale discovery, exporter config, connectivity tests.
  • BLE diagnostic tool. npm run diagnose for detailed BLE troubleshooting.
  • Home Assistant Add-on. One-click install via My Home Assistant badge, MQTT auto-discovery, UI-driven config, Garmin token bootstrap, and MFA workaround.
  • ESP32 BLE proxy. Use a remote ESP32 as a BLE radio over MQTT, with a built-in embedded broker for zero-config setup, simplified Docker deployment, and optional display.
  • ESPHome Bluetooth proxy. Reuse an existing ESPHome BT proxy mesh (Home Assistant) as a BLE radio via Native API: broadcast and GATT scales, multi-proxy with RSSI auto-pick.
  • BLE adapter selection. ble.adapter: hci1 for multi-adapter setups (Linux).
  • Broadcast mode. Supports non-connectable scales that only advertise weight via BLE advertisements.
  • Linux stability hardening. Auto-recovery for the BlueZ "stuck discovery" state via a consecutive-failure watchdog, plus optional systemd Type=notify integration for whole-loop freezes.
  • Update check. Optional, anonymous version check after each measurement (opt-out via update_check: false); see the auto update guide for Watchtower, systemd timer, and HA add-on recipes.
  • Cross-platform. Linux (Docker + native), macOS, Windows.
  • Private. Your data stays on your device, no vendor cloud.

Credits

Contributors

KristianP26
KristianP26
APIUM
APIUM
marcelorodrigo
marcelorodrigo
fromport
fromport
boildead
boildead
alexw23
alexw23

Contributing

See CONTRIBUTING.md for development setup, project structure, and how to add new scale adapters or exporters.

License

GPL-3.0. See LICENSE for details.

Star History

Star History Chart