Skip to content

Home Assistant add-on ​

The add-on runs BLE Scale Sync on Home Assistant OS and Supervised. It builds its config from the add-on options, finds the Mosquitto broker and publishes every metric as an MQTT auto-discovery sensor. Home Assistant 2026.2 and later lists add-ons under Settings > Apps.

Install ​

Add BLE Scale Sync repository to your Home Assistant

  1. Click the badge, or add https://github.com/KristianP26/ble-scale-sync under Settings > Apps > Install app > three-dot menu > Repositories.
  2. Open BLE Scale Sync in the store and click Install.
  3. If you have no MQTT broker yet, install and start the Mosquitto broker add-on.

Images exist for amd64, aarch64 and armv7. To reach the host's Bluetooth adapter through BlueZ, the add-on uses host_network, host_dbus and the NET_ADMIN and NET_RAW capabilities. It runs without AppArmor, because the default profile blocks the D-Bus calls BlueZ needs.

First weigh-in ​

  1. On the Configuration tab, fill in the user profile (name, height, birth date, gender), and leave Enable MQTT and Auto-detect MQTT broker on.
  2. Leave Scale MAC address empty for the first weigh-in.
  3. If the host's Bluetooth adapter is out of range of the scale or missing, pick another Bluetooth transport (below).
  4. Start the add-on on the Info tab and step on the scale.
  5. Copy the scale's address from the Auto-discovered: or Matched: log line into Scale MAC address and restart the add-on. It then never reads a neighbor's scale.

The sensors appear under Settings > Devices & services > MQTT after the first reading.

Home Assistant sensors lists them.

Bluetooth transports ​

The Bluetooth transport option picks the radio: local (default, this host's adapter), ha-bluetooth, esphome-proxy or mqtt-proxy. Choose a transport compares them. Each proxy page has an add-on section: Home Assistant Bluetooth, ESPHome proxy and ESP32 proxy. The option's key is ble_transport.

With any transport but local:

  • the options that steer this host's adapter are left out of the config, and the log names any you set: ble_adapter, auto_clear_stale_bond, preemptive_adapter_reset, adapter_privacy and pairing_retry_max_min;
  • the adapter reset at startup is skipped;
  • scan_cooldown, idle_rescan_delay and no_reading_rescan_delay do nothing, because the add-on listens all the time.

A transport that cannot run with the given options stops the add-on with an ERROR line naming the option to fix. It never falls back to the host adapter.

Options ​

The add-on writes /data/config.yaml from these options on every start, so a change applies after a restart. The third column names the config.yaml key each option sets.

Scale and Bluetooth ​

OptionDefaultconfig.yaml key and details
scale_macemptyble.scale_mac, BLE
ble_transportlocalble.handler, Bluetooth transports
ble_adapteremptyble.adapter, local only
reset_bluetoothtrueAdd-on only: power-cycles the adapter at startup, local only
force_scale_adapteremptyble.force_scale_adapter, needs scale_mac
qn_protocol_byte, qn_report_byte, qn_weight_ack, qn_user_key, qn_user_slot, qn_algorithm, qn_a4_prelude, qn_time_sync_long, qn_config_long, qn_result_ackemptyble.qn_*, QN scales
rmsc04_profile_byteemptyble.rmsc04_profile_byte, Renpho scales
auto_clear_stale_bondfalseble.auto_clear_stale_bond, Beurer scales
preemptive_adapter_resettrueble.preemptive_adapter_reset, local only
adapter_privacyfalseble.adapter_privacy, Beurer scales
pairing_retry_max_min60ble.pairing_retry_max_min, Beurer scales
esphome_proxy_host, esphome_proxy_port, esphome_proxy_encryption_keyempty, 6053, emptyble.esphome_proxy, ESPHome proxy
mqtt_proxy_brokersharedshared uses the MQTT options below, embedded a broker in the add-on
mqtt_proxy_device_id, mqtt_proxy_topic_prefixesp32-ble-proxy, ble-proxyble.mqtt_proxy, ESP32 proxy
mqtt_proxy_embedded_broker_port, mqtt_proxy_username, mqtt_proxy_password1883, empty, emptyEmbedded broker only; username and password are required there
proxy_liveness_timeout_min30ble.proxy_liveness_timeout_min, proxy transports only

Units and user ​

OptionDefaultconfig.yaml key and details
weight_unitkgscale.weight_unit, the unit of the log and notifications
height_unitcmscale.height_unit, the unit of user_height
display_unitweight_unitscale.display_unit, the unit on the scale's own display
user_name, user_height, user_birth_date, user_gender, user_is_athleteDefault, 170, 1990-01-01, male, falseThe first entry of users, Users
user_weight_min, user_weight_max40, 150weight_range, always in kg
user_estimate_body_compositiontrueestimate_body_composition, Users
out_of_rangewarnout_of_range, Out-of-range readings

Calculations use kg and cm whatever the units are, and MQTT sends masses in kg. To see pounds in Home Assistant, change the unit in the sensor's settings.

MQTT and Garmin ​

OptionDefaultconfig.yaml key and details
mqtt_enabledtrueAdds the mqtt exporter, MQTT
mqtt_autotrueTakes the broker and login from the Mosquitto add-on, see below
mqtt_broker_url, mqtt_username, mqtt_passwordemptybroker_url, username, password; used when auto-detection is off or fails
mqtt_topicscale/body-compositiontopic
mqtt_ha_discoverytrueha_discovery
mqtt_ha_device_nameBLE Scaleha_device_name
garmin_enabledfalseAdds the garmin exporter, Garmin Connect
garmin_email, garmin_passwordemptyemail, password
garmin_weight_onlyfalseweight_only, Garmin
garmin_upload_timeout_sec180upload_timeout_sec, Garmin

With mqtt_auto on, the add-on reads the broker address and login from the Supervisor and logs MQTT auto-detected: mqtt://core-mosquitto:1883. If the Mosquitto add-on is missing or the Supervisor refuses, the log gives the HTTP status and the add-on falls back to the manual MQTT options.

Runtime ​

OptionDefaultconfig.yaml key and details
scan_cooldown30runtime.scan_cooldown, local only, Runtime
idle_rescan_delay5runtime.idle_rescan_delay, local only
no_reading_rescan_delay0runtime.no_reading_rescan_delay, local only
retry_failed_exportstrueruntime.retry_failed_exports; the queue is kept in /data
debugfalseruntime.debug, verbose log for issue reports
update_checktrueupdate_check, Update check
custom_configfalseUses your own file instead of these options, Custom config mode

Garmin Connect ​

  1. Turn on Enable Garmin Connect and enter your Garmin email and password.
  2. Start the add-on.

On the first start the add-on signs in with these credentials and saves the token in /data/garmin-tokens/; later starts reuse it. The log says Garmin authentication successful. When you turn Garmin off, the token stays, so turning it on again needs no new sign-in.

Sign in to Garmin with MFA ​

The add-on has no terminal for an MFA code. If your Garmin account uses MFA, or Garmin blocks your Home Assistant host's IP address, create the token on another computer:

  1. On a computer with Docker, or with BLE Scale Sync and Python 3.12 installed, run setup-garmin as in Garmin authentication and enter the MFA code when asked.
  2. Copy garmin_tokens.json from the token directory it wrote to into /share/ble-scale-sync/garmin-tokens/ on the Home Assistant host, for example with the Samba share or File editor add-on.
  3. Restart the add-on. The log shows IMPORTING a Garmin token from /share/ble-scale-sync/garmin-tokens/garmin_tokens.json.

The import runs only while /data/garmin-tokens/ holds no token. Otherwise anything that can write to /share could redirect your measurements to another Garmin account. A different token waiting in /share is not imported, and the log says so. To switch accounts, reinstall the add-on and import again. Reinstalling also clears the remembered weights and the retry queue in /data.

Custom config mode ​

The options cover one user, MQTT and Garmin Connect. For several users, another exporter, or transport settings the options lack, turn on custom_config and put a full config.yaml at /share/ble-scale-sync/config.yaml. The missing transport settings are several ESPHome proxies, the source filter of ha-bluetooth and the legacy ESPHome API password. config.yaml.example and the config.yaml reference show every key.

  • The add-on copies the file at startup. The live reload watches that copy, so after editing the file under /share, restart the add-on.
  • The other options are ignored, the transport options included: the file's ble.handler decides. The log warns about a ble or transport option you left set. Two exceptions: proxy_liveness_timeout_min is applied on top of the file unless the file sets ble.proxy_liveness_timeout_min, and ble_adapter with reset_bluetooth controls the adapter reset at startup.
  • The add-on does not sign in to Garmin. Import a token as in Sign in to Garmin with MFA; it lands in /data/garmin-tokens, where every garmin exporter without its own token_dir looks. Only that default directory is imported, so give further Garmin accounts a token_dir you can write to, such as /share/ble-scale-sync/garmin-tokens/<name>.
  • A strava exporter without its own token_dir keeps its tokens in /data/strava-tokens, which survives restarts and updates.
  • In this mode the adapter reset at startup runs only when ble_adapter is set. Turn off reset_bluetooth if your file uses a proxy.
  • For Home Assistant Bluetooth, the add-on's own Supervisor access works without a long-lived token. Write the URL in full, because http://supervisor/core alone does not reach the websocket:
yaml
ble:
  handler: ha-bluetooth
  ha_bluetooth:
    url: ws://supervisor/core/websocket
    token: '${SUPERVISOR_TOKEN}'

Files in /share are readable by other add-ons

Every add-on with share access and every Samba user can read and change files under /share/, including a custom config.yaml with passwords in it.

Test a development build ​

Fixes reach the dev branch before a release. Adding the repository as https://github.com/KristianP26/ble-scale-sync#dev gives you the dev branch's add-on options and run.sh. But build.yaml pins the application image to the last release on both branches, so the application code stays the released one. An option that appears only in the dev UI does nothing.

To run unreleased code, start the Docker image ghcr.io/kristianp26/ble-scale-sync:dev outside the Supervisor, with the flags from Docker. Every push to dev updates it and publishes an immutable dev-<sha> tag. You lose MQTT auto-detection and the Supervisor integration.

When you report back, paste the Version line from the top of the log; on a dev image it ends with (image dev @ <commit>).

Restarts and stored data ​

If the app exits on its own, for example after repeated Bluetooth failures, the add-on starts it again after a short, growing delay. The Supervisor's Watchdog switch is not needed.

PathContent
/data/config.yamlThe generated config, rewritten on every start with each user's last_known_weight kept
/data/garmin-tokens/Garmin token
/data/strava-tokens/Strava tokens of a strava exporter without its own token_dir
/data/.*Retry queue, de-duplication marks, last live weigh-ins for the replay check, update check date
/share/ble-scale-sync/config.yamlYour file for custom config mode
/share/ble-scale-sync/garmin-tokens/A Garmin token to import (Sign in to Garmin with MFA)

/data survives restarts and updates and is cleared when you uninstall the add-on. The remembered weight is stored per user slug, so renaming the user resets it.

Limitations ​

  • The options cover a single user. For several users or per-user exporters use custom config mode.
  • HACS cannot install the add-on, because it does not distribute Supervisor add-ons. Add the repository as under Install.

Troubleshooting ​

  • No scale found: turn on debug, step on the scale and look for it in the log. See Troubleshooting and Supported scales.
  • MQTT entities do not appear: check that the Mosquitto add-on runs, that the log shows MQTT auto-detected, and that a reading was exported; discovery happens with the first reading.
  • The add-on exits with DBusError and AccessDenied: reinstall the add-on so the Supervisor applies its manifest; see Docker issues.
  • A Beurer scale asks to pair again or shows a code over and over: Beurer scales.
  • Discovery stops after hours: BlueZ stuck state.
  • The add-on stops with ERROR: about a transport: fix the option the line names. The add-on's Documentation tab lists each message.

Released under the GPL-3.0 License.
Software made in Europe