# Bluetooth (BLE) sensors: paving the way for external integration

**URL:** <https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555>\
**Category:** Demande de fonctionnalités\
**Tags:** in-progress\
**Created:** [August 14, 2026, 2:53pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555 "2026-08-14T14:53:10Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![pierre-gilles](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/pierre-gilles/32/5886_2.png) [@pierre-gilles](https://community.gladysassistant.com/u/pierre-gilles)\
**Post date:** [August 14, 2026, 2:53pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/1 "2026-08-14T14:53:10Z")

</div>

Following an exchange with the creator of [Theengs](https://www.theengs.io/) / [OpenMQTTGateway](https://docs.openmqttgateway.com), a gap was identified: Gladys does have a Bluetooth integration, but it only covers a handful of devices — whereas the ecosystem of BLE sensors (temperature, humidity, plants, etc.: Xiaomi, SwitchBot, RuuviTag…) is enormous, and projects like Theengs can decode hundreds of them.

For me, this needs to be done through an **external integration** , not through code in the core. But today, the external integration framework does not provide access to the machine’s Bluetooth, and for good reasons: unlike a Zigbee key (a simple USB device that can be mounted in the container), giving a container access to Bluetooth implies giving it very broad network privileges on the machine, which is incompatible with the store’s security model (third-party integrations that are not audited, installable with one click).

The envisaged approach: a **Bluetooth mediator access** , on the same model as the network discovery already in place, the core listens to the radio and relays the raw frames, the integration decodes them and publishes sensors and states via the existing APIs. The adapter remains under the control of the core, which will in any case be indispensable when we do **Matter/Thread commissioning in BLE**.

Note: for those using **ESP32 gateways with OpenMQTTGateway** , an external integration is already possible today via MQTT, without changing anything in the framework.

The issue here is the use of the Bluetooth adapter of the machine running Gladys.

---

<div class="post-metadata">

**Author:** ![1technophile](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/1technophile/32/15350_2.png) [@1technophile](https://community.gladysassistant.com/u/1technophile)\
**Post date:** [August 16, 2026, 3:58pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/2 "2026-08-16T15:58:11Z")

</div>

Hi,

Indeed, if Bluetooth access is possible, it should allow us to decode the frames and make Gladys compatible in read-only mode with all these [devices](https://decoder.theengs.io/devices/devices.html).

Here’s what a decoder like Theengs needs on the API side:

**1. It’s the integration that controls the scan — the core executes and arbitrates.** Same philosophy as `scanNetwork()`: the integration requests a bounded scan window, for example `gladys.scanBluetooth({ mode, durationSeconds, filters })`, and the core maintains control over the adapter. Two nuances compared to `scanNetwork`:

- The frames must be **streamed during the window** (callback as they come), not just returned in bulk at the end; latency matters for states;
- The window must be **renewable** (lease model): it’s the integration that decides its cadence — sustained during discovery, relaxed duty cycle in cruise mode (e.g., 10 seconds of scan every 60 seconds), stop when the user disables it.

The core remains the sole arbiter: maximum duration per window, quotas, sharing between integrations, and immediate preemption when Matter/Thread needs the radio.

**2. Minimal frame content.** To decode, Theengs needs:

- `mac` (+ address type public/random) — this is the stable identifier of the device
- `rssi`
- `name` (local name) if present
- `manufacturerdata` (bytes, hex, or base64)
- `servicedata` + `servicedatauuid`
- `serviceUuids` announced
- a timestamp, and the indication of advertising frame vs scan response

A raw field (`payload_base64` of the AD structures, consistent with `scanNetwork`) in addition to the parsed fields would be ideal: it covers exotic cases without freezing the parsing on the core side.

**3. Passive AND active scan, at the request’s discretion.** Some very common sensors only expose their data in the _scan response_ (thus active scan required). The `mode` is a parameter of the scan request; the manifest, on the other hand, declares the maximum authorized capacity (e.g., `"bluetooth": { "modes": ["passive", "active"] }`) that the user grants to the installation.

**4. Filtering on the core side, parameterized by the request.** Optional but valuable to avoid saturating the WebSocket: filters by MAC prefix, `manufacturer_id`, or `service_uuid` passed in the scan options, and a throttle by MAC (e.g., max 1 frame/MAC/second, sensors repeat the same frame in bursts).

**5. Adapter management: at the core level, sole owner.** This is the point that makes the mediated model really superior to direct access:

- The core **inventories** the adapters (hci0, USB dongles…), keeps them up to date (hotplug), monitors their health, and **resets** them in case of freezing; cheap BLE adapters regularly freeze, a centralized watchdog benefits everyone;
- The core is the **sole owner** of each adapter and multiplexes all consumers: core Bluetooth service (GATT/presence), Matter/Thread commissioning, and scan windows of integrations, no more ownership conflicts between processes;
- On the user side: if multiple adapters are present, it’s in the core config that they assign roles (e.g., hci0 reserved for Matter, USB dongle for integration scans);
- On the integration side: **never a `/dev` path nor a physical adapter name** , at most an optional logical identifier in the scan options when the user has assigned multiple adapters. The integration requests «&nbsp;a scan&nbsp;», not «&nbsp;adapter X&nbsp;».

Bonus in the long term: multiple adapters assigned to scanning = multiple antennas, better coverage — and the API doesn’t need to change, the core aggregates the frames.

**6. User consent.** As for existing hardware classes: BLE listening reveals the presence of people (phones, wearables) — an authorization screen during installation, separate from other permissions, seems to be the right granularity.

**7. Out of scope for a v1 of the relay** (from our point of view): no GATT connection or writing from integrations, advertising in read-only mode already covers the vast majority of sensors, and it keeps the security model simple.

Some resources:

> **[Using with Node.js | Theengs Decoder: BLE message decoder](https://decoder.theengs.io/use/nodejs.html)**
>
> Theengs Decoder project aims to provide an efficient, portable and lightweight library for BLE Internet of Things messages decoding.

[https://www.npmjs.com/package/theengs-decoder](https://www.npmjs.com/package/theengs-decoder)

---

<div class="post-metadata">

**Author:** ![cursor-bot](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/cursor-bot/32/22252_2.png) [@cursor-bot](https://community.gladysassistant.com/u/cursor-bot)\
**Post date:** [August 17, 2026, 1:33pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/3 "2026-08-17T13:33:37Z")

</div>

Hi everyone!

This topic is now **in development**.

A PR has been opened to specify BLE (Bluetooth sensor) advertisement streaming:

> <https://github.com/GladysAssistant/Gladys/pull/2935>
>
> \### Description
> 
> Docs-only PR: adds the living specification \`docs/specs/bluetoo…th-ble-sensors.md\`, answering the community proposal in topic 10555 (BLE sensors decoded by an external integration, e.g. Theengs). No code change — spec first, per the \`AGENTS.md\` spec-first process.
> 
> What the spec defines:
> 
> \- \*\*A. Core\*\* — the Bluetooth service stays the single owner of the radio. A \*\*scan-lease arbiter\*\* (\`burst\` leases for today's pairing/presence/lookup scans, expiring auto-renewed \`stream\` leases for continuous sensor listening) replaces the hand-rolled \`scanCounter\`/\`scanTimer\` arbitration; a single \*\*advertisement frame format\*\* (MAC, address type, RSSI, name, manufacturer data, service data + UUIDs, core timestamp — binary as base64, \`raw\`/\`adapter\` fields reserved for phase 2); \*\*core-side filters\*\* (MAC prefix, manufacturer id, service UUID) and \*\*throttling\*\* (per-lease token bucket + per-address dampener, with honest dropped-frame accounting).
> \- \*\*B. Internal consumer API (phase 1)\*\* — \`subscribeAdvertisements()\` on the service surface, consumed via \`getService('bluetooth')\` (the existing nuki/tasmota→mqtt pattern).
> \- \*\*C. External integration contract (phase 2)\*\* — the \`external-integrations.md\` §B.16 capture-and-relay principle applied to BLE: manifest \`bluetooth\` observer field with a filter ceiling, \`/api/integration/v1/bluetooth/lease\` host API (60 s TTL, one lease per integration), batched \`bluetooth-advertisement\` WS stream, and a \*\*distinct, revocable user consent\*\* (continuous BLE listening reveals people's presence).
> \- \*\*D. Deferred / out of scope\*\* — passive scan is tied to a future migration off \`@abandonware/noble\` (which exposes no passive-scan control); multi-adapter multiplexing deferred (noble is one-adapter-per-process); GATT over the external API and any decoding in the core are out of scope entirely.
> \- \*\*E. Verification\*\* — arbiter unit tests, no-regression rule for the lease refit of existing scans, external-contract e2e when section C ships.
> 
> Also in the diff, per the living-spec rules: a cross-reference at the end of \`external-integrations.md\` §B.16, and the new spec added to the \`AGENTS.md\` spec list.
> 
> \## Forum
> 
> Forum: https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555
> 
> \### Checklist
> 
> \- \[x\] Tests pass: docs-only change, no code touched (no server/front test impact)
> \- \[x\] Linter and prettier pass on both front and server: markdown files under \`docs/\` are outside the prettier/eslint scopes; no JS/JSON changed
> \- \[x\] No undocumented breaking change
> 
> \---
> \_Generated by \[Claude Code\](https://claude.ai/code/session\_011df3ZQmDqryy6fYJQMeCMM)\_

Feel free to follow the PR, test (optional, especially for small requests) and give your feedback here if needed.

---

<div class="post-metadata">

**Author:** ![pierre-gilles](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/pierre-gilles/32/5886_2.png) [@pierre-gilles](https://community.gladysassistant.com/u/pierre-gilles)\
**Post date:** [August 17, 2026, 1:34pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/4 "2026-08-17T13:34:30Z")

</div>

@1technophile I suggested a spec with Fable 5, what do you think? 🙂

---

<div class="post-metadata">

**Author:** ![ArMour85](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/armour85/32/7457_2.png) [@ArMour85](https://community.gladysassistant.com/u/ArMour85)\
**Post date:** [August 17, 2026, 6:47pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/5 "2026-08-17T18:47:59Z")

</div>

@pierre-gilles Will this pave the way for external OTBR integration?

---

<div class="post-metadata">

**Author:** ![1technophile](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/1technophile/32/15350_2.png) [@1technophile](https://community.gladysassistant.com/u/1technophile)\
**Post date:** [August 18, 2026, 1:13pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/6 "2026-08-18T13:13:00Z")

</div>

Thanks, some comments but nothing blocking

---

<div class="post-metadata">

**Author:** ![pierre-gilles](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/pierre-gilles/32/5886_2.png) [@pierre-gilles](https://community.gladysassistant.com/u/pierre-gilles)\
**Post date:** [August 20, 2026, 6:51pm UTC](https://community.gladysassistant.com/t/capteurs-bluetooth-ble-ouvrir-la-voie-a-une-integration-externe/10555/7 "2026-08-20T18:51:35Z")

</div>

Thanks for your feedback @1technophile!

> [@ArMour85](#):
>
> @pierre-gilles Will this pave the way for external OTBR integration?

No, I don’t think so!
