Enrolling devices
How-to · Last verified against firmware v4.0.6 on 2026-05-11.
Some devices Just Work — drop in a Blue Charm beacon and ESPresense finds it. Others need an IRK, a setting flipped in a vendor app, or a workaround that the community has spent years arguing about. This page is the per-device recipe sheet, scoped to the recipes that have actually been confirmed in Discussions.
Source matrix: canonical pin #2324 “Enrolling devices: what works, what doesn’t, and where to look” by @Terastar-Paperclip.
30-second triage
Section titled “30-second triage”| Device | Path | Status |
|---|---|---|
| iPhone / iPad | Pair to ESPresense node, IRK auto-captured | ✅ Settled |
| iPhone (iOS 17+, in-firmware pair fails) | Mac → Keychain Access fallback | ✅ Settled (fallback) |
| Apple Watch | Pair from watch Settings → Bluetooth (firmware emulates an HRM) | ✅ Settled |
| Apple Watch (fallback) | Mac → Keychain Access lookup | ✅ Settled (fallback) |
| AirPods / BeatsX / HomePod | none | ❌ No settled path |
| Withings ScanWatch | Android HCI snoop → Wireshark → IRK | ✅ Settled (involved) |
| Polar HR straps (H10 / H9) | Broadcast HR mode on, generic name: enrollment | ✅ Works |
| Polar Loop (2025) | none | ❌ No settled path |
| Moto Tag / Galaxy SmartTag / Find-My-network | none | ⚠️ Limited; identifier rotates |
| Android phones | HA Companion app ble_transmitter | ✅ Settled |
| Amazfit / Mi Band / Zepp watches | Vendor app → enable Discoverable | ✅ Settled |
If your device is not in the table, start at /devices for the known-working list. If it advertises an iBeacon or Eddystone profile, no enrollment is needed — the firmware picks it up on first sight.
iPhone
Section titled “iPhone”Source: #1348 · existing recipe lives on /apple. Confirmed by @DTTerastar (maintainer) and multiple users.
The settled path is to put the firmware in enrollment mode and pair the phone over BLE. The phone exchanges its IRK during the secure pairing handshake; the firmware reads it from the BLE bond store and publishes a retained config to MQTT.
-
Open
http://<node-ip>/ui/#/deviceson the same network as the node. -
Type a friendly name (e.g.,
dt-phone) in the Enroll field and click Enroll. The node starts advertising a Heart Rate Monitor service namedESPresensefor 120 seconds. -
On the iPhone: Settings → Bluetooth. Tap the new
ESPresenseentry and accept the secure pairing prompt. -
The enroll prompt clears automatically. The firmware publishes:
espresense/settings/irk:<32-hex>/config (retained){"id": "dt-phone", "name": "dt-phone"} -
Other nodes pick up the retained config and resolve the same iPhone the next time it advertises.
iOS 17+ fallback (Mac Keychain)
Section titled “iOS 17+ fallback (Mac Keychain)”A subset of iPhones on iOS 17+ accept the pair but never deliver the IRK to the firmware (#1348, tracked). The settled fallback is to read the IRK out of iCloud Keychain on a paired Mac. The full procedure (Keychain Access → search bluetooth → Public: XX:XX:... → Show Password) is on /apple → Lookup Method. The decoded 32-character hex IRK goes into the same espresense/settings/irk:<hex>/config topic as above.
iPhone screen-off tracking
Section titled “iPhone screen-off tracking”Source: #492. Partial — not a calibration knob. The community has not settled this.
iPhones suppress nearby BLE advertisements when locked unless something on the device has a reason to talk. Reports:
- @DTTerastar: “My iPhone blasts out nearby info every 500ms no matter what” — works without intervention on his unit.
- @51av0sh: a paired Apple Watch keeps the phone broadcasting for ~20 minutes after lock.
- @huytd2k: installing the
room-assistantiOS app keeps it advertising. - Universal Clipboard / Handoff, iCloud Family Sharing, and iCloud Photo backup have all been reported to keep an iPhone broadcasting through lock — none of them is a guaranteed fix.
If you hit this, the IRK enrollment is not the problem. Add an Apple Watch on the same Apple ID, or accept a “presence with hysteresis” approach in Home Assistant.
Apple Watch
Section titled “Apple Watch”Source: #2099 · canonical recipe on /apple → Apple Watch. Confirmed by @DTTerastar (maintainer): current firmware emulates a Heart Rate Monitor well enough that watchOS pairs to it natively from the watch’s Bluetooth settings.
The settled path is identical to the iPhone path: put the firmware in enrollment mode, then pair from the watch.
-
Open
http://<node-ip>/ui/#/deviceson the same network as the node. -
Type a friendly name (e.g.,
dt-watch) in the Enroll field and click Enroll. The node starts advertising a Heart Rate Monitor service namedESPresensefor 120 seconds. -
On the Apple Watch, open Settings → Bluetooth. Wait for
ESPresenseto appear (some watchOS versions surface it under “Health Devices”) and tap it. Accept the pair request. -
The Enroll prompt clears automatically. The firmware publishes:
espresense/settings/irk:<32-hex>/config (retained){"id": "dt-watch", "name": "dt-watch"} -
Other nodes pick up the retained config and resolve the same watch the next time it advertises.
If pairing doesn’t start within ~30 seconds, toggle Bluetooth off and back on (Settings → Bluetooth) on the watch and click Enroll again to restart the 2-minute window.
Mac Keychain fallback
Section titled “Mac Keychain fallback”If the watch never produces the pairing prompt (older firmware, edge-case watchOS version), read the watch’s IRK from your iCloud Keychain on a paired Mac. The full procedure (Keychain Access → search bluetooth → Public: XX:XX:... → Show Password) is on /apple → Lookup Method. The watch’s Bluetooth address is at Watch → Settings → About. The decoded 32-character hex IRK goes into the same espresense/settings/irk:<hex>/config topic as the iPhone path.
AirPods / BeatsX / HomePod
Section titled “AirPods / BeatsX / HomePod”Source: #1531. Contributors: @DrSpaldo, @dxmnkd316, @nonanonymousanon.
What’s true:
- AirPods are visible in the fingerprints list as
apple:0707:<len>or similar Continuity payloads — but multiple sets of AirPods produce the same fingerprint, so they’re not uniquely identifiable. - @dxmnkd316 reports their AirPods kept the same Bluetooth address for ~8 hours instead of the expected ~15 minutes. Address persistence appears generation- and firmware-dependent, so even pinning by MAC is unreliable.
- macOS-extracted IRKs can sometimes be obtained for AirPods Pro 2 and later (the same
Public: XX:XX:...Keychain entry trick as iPhone), but the success rate varies and the key may stop resolving after a firmware update.
If you need “is somebody wearing their AirPods” presence, an apple:0707:* count over time can be useful as a soft signal, but don’t wire it to identity.
Withings ScanWatch
Section titled “Withings ScanWatch”Source: #1247. Contributors: @ginkel (asker), @max00346 (procedure), @rkrkrk0987 (Android 15 path), @wanghuangjie (MQTT example), @vincenttor (Wireshark help).
Withings devices don’t pair to the ESPresense node — they pair only to the Withings app, which lives on the phone. The settled path is to capture the pairing handshake on an Android phone you control, extract the IRK from the HCI snoop log, and publish it manually.
You will need: an Android phone with developer options, a USB cable, adb, and Wireshark.
-
Android → Settings → System → Developer options. Turn on:
- Enable Bluetooth HCI snoop log (a.k.a. “Bluetooth Debugging to File” on some OEM skins)
- USB debugging
-
Pair the ScanWatch to that Android phone through the Withings app. Wear the watch close enough that the pairing actually completes.
-
Pull the bug report:
Terminal window adb bugreport scanwatchThe output is a
.zip. -
Find the HCI log inside the zip at
FS/data/log/bt/*_hci.log(Android 14 and below) orFS/data/misc/bluetooth/logs/*(Android 15+, per @rkrkrk0987). -
Open the log in Wireshark. Filter on
btsmp. Find the Identity Information packet — that’s the IRK delivered by the watch during the secure-pairing handshake. -
Copy the 32-character hex IRK out of the packet (little-endian — Wireshark shows it big-endian for some captures; if resolution fails, byte-reverse).
-
Publish to MQTT with retain:
espresense/settings/irk:<32-hex>/config (retained){"id": "scanwatch", "name": "Withings ScanWatch"}
Same irk: topic shape as the iPhone path — once it’s published retained, every ESPresense node in the deployment picks it up.
Source: #2054 “Polar loop compatibility?”. Asker: @jacksaturn. The community has not validated the 2025 Polar Loop.
Polar HR straps (H10, H9, Verity Sense)
Section titled “Polar HR straps (H10, H9, Verity Sense)”These work when Broadcast Heart Rate is enabled in Polar Flow / Polar Beat. With broadcast on, the strap advertises with a stable BLE MAC and a recognizable manufacturer/service signature, and ESPresense fingerprints it as name:polar-h10-… (or similar — the kebabified BLE-advertised name). Enroll exactly like a generic named device:
espresense/settings/name:polar-h10-12345678/config (retained){"id": "polar-strap", "name": "Polar H10"}Same caveat as Garmin Instinct Solar on /devices: the strap only broadcasts while Heart Rate Broadcast is on. If you take it off and put it back, broadcast is off until you turn it on again.
Polar Loop (2025)
Section titled “Polar Loop (2025)”Moto Tag / Galaxy SmartTag / Find-My-network
Section titled “Moto Tag / Galaxy SmartTag / Find-My-network”Source: canonical pin #2324. Greenfield — the community has not settled a path.
What you can do:
- Count, don’t identify. A rolling count of distinct
apple:findmyMACs near a node can tell you “more than usual” but not “Bob’s tag is here.” - Track the carrier, not the tag. If the tag rides in your bag and your phone is in your pocket, track the phone via IRK.
- OpenHaystack beacons: if you flash an ESP32 or an nRF as an OpenHaystack tag, the BLE address is yours — pin it in ESPresense by MAC like any other beacon. This is on /devices.
Apple AirTags are explicitly on the /devices “known to not work” list for individual tracking. Galaxy SmartTags are listed as “randomized mac addresses.”
Android phones
Section titled “Android phones”Source: /android · #880 (asker @moblsu, thread unanswered).
Android does not deliver an IRK to a non-bonded peripheral and rotates the BLE MAC. The settled path is to run the Home Assistant Companion App with BLE Transmitter enabled — the phone broadcasts an iBeacon with a UUID you choose, and ESPresense fingerprints that UUID as a stable identity.
- Install Home Assistant Companion App on the phone.
- In the app: Settings → Manage Sensors → BLE Transmitter → enable. Optionally configure the UUID (default is per-device).
- Whitelist the app from battery optimization (Android will kill it otherwise).
- ESPresense sees the phone as an iBeacon-shaped identity. Enroll it the same way you would any iBeacon — by the UUID/major/minor.
Beacon Scope (standalone Android app, Play Store) is the alternative if you don’t run Home Assistant.
Amazfit / Mi Band / Zepp watches
Section titled “Amazfit / Mi Band / Zepp watches”Source: #1202. Contributor: @SteveDockar.
These devices keep a static BLE MAC and fingerprint as mifit:<mac>. The catch is they don’t pair to ESPresense — you have to flip them to broadcast and then write the config to MQTT yourself.
-
In the Zepp / Mi Fitness app: Profile → watch/band → Bluetooth visibility (also labeled “Discoverable” on some models): on. Without this, the watch only talks to a paired phone and ESPresense never sees it.
-
Find the fingerprint. Open
http://<node-ip>/ui/#/devicesand look for amifit:<mac>entry that appears when the watch is nearby. -
Publish a retained config to MQTT:
espresense/settings/mifit:<aa:bb:cc:dd:ee:ff>/config (retained){"id": "amazfit-balance", "name": "Amazfit Balance"} -
The device now shows up as
amazfit-balanceacross every node in the deployment.
Confirmed working units from the community: Amazfit Balance, Band, Bip S, Bip 3 Pro, GTS 2 Mini, GTS 4 Mini, GTR 2e, Xiaomi Mi Band. Full list on /devices.
After enrollment
Section titled “After enrollment”Whatever path got you here, the device should now show up under its friendly name at:
http://<node-ip>/ui/#/devices(per-node view)espresense/devices/<id>/<room>MQTT topic (RSSI + distance per node)- Companion’s device list, if you run it
If distances look off, head to Calibration — that’s where to fix RSSI@1m, per-node offsets, and absorption. Enrollment only gets the device seen; calibration is what makes distances accurate.
If you find a working recipe for a device that’s flagged ⚠️ or ❌ above, please post to the linked Discussion. The matrix at the top is maintained from confirmed community reports — your one-line “this worked for me” is what moves a device from ⚠️ to ✅.
Reference
Section titled “Reference”- Canonical pin: #2324 Enrolling devices: what works, what doesn’t, and where to look
- Known-working device list: /devices
- Apple IRK details and
IrkDecodeform: /apple - Android app options: /android
- MQTT settings topic schema: /configuration/mqtt
- Calibration once the device is enrolled: /guides/calibration