Sensores Bluetooth (BLE): abrir el camino a una integración externa

Tras una conversación con el creador de Theengs / OpenMQTTGateway, se identificó una carencia: Gladys sí tiene una integración Bluetooth, pero solo cubre unos pocos dispositivos, mientras que el ecosistema de sensores BLE (temperatura, humedad, plantas, etc.: Xiaomi, SwitchBot, RuuviTag…) es enorme, y proyectos como Theengs saben decodificar cientos de ellos.

Para mí, esto debe pasar por una integración externa, no por código en el núcleo. Pero hoy, el framework de integraciones externas no da acceso al Bluetooth de la máquina, y por buenas razones: a diferencia de un dongle Zigbee (un simple dispositivo USB que se puede montar en el contenedor), dar acceso al Bluetooth a un contenedor implica darle privilegios de red muy amplios en la máquina, incompatibles con el modelo de seguridad de la tienda (integraciones de terceros no auditadas, instalables en un clic).

La solución considerada: un acceso Bluetooth mediado, bajo el mismo modelo que la detección de red ya en funcionamiento, el núcleo escucha la radio y reenvía las tramas brutas, la integración las decodifica y publica sensores y estados a través de las API existentes. El adaptador sigue bajo el control del núcleo, lo cual será de todos modos indispensable el día en que hagamos comisionamiento Matter/Thread en BLE.

Nota: para aquellos que utilizan pasarelas ESP32 con OpenMQTTGateway, una integración externa ya es posible hoy en día a través de MQTT, sin cambiar nada en el framework.

El tema aquí es el uso del adaptador Bluetooth de la máquina que ejecuta Gladys.

Hola,

De hecho, si el acceso a Bluetooth es posible, esto debería permitirnos decodificar los tramas y hacer que Gladys sea compatible en modo lectura con todos estos dispositivos

Aquí está lo que un decodificador como Theengs necesita, del lado de la API:

1. Es la integración la que controla el escaneo — el núcleo lo ejecuta y arbitra. La misma filosofía que scanNetwork(): la integración solicita una ventana de escaneo delimitada, por ejemplo gladys.scanBluetooth({ mode, durationSeconds, filters }), y el núcleo mantiene el control sobre el adaptador. Dos matices con respecto a scanNetwork:

  • Los tramas deben ser transmitidos durante la ventana (callback en tiempo real), no solo entregados en bloque al final, la latencia es importante para los estados;
  • La ventana debe ser renovable (modelo de arrendamiento): es la integración la que decide su ritmo — sostenido durante un descubrimiento, ciclo de trabajo relajado en modo crucero (ej. 10 s de escaneo cada 60 s), parada cuando el usuario lo desactiva.

El núcleo sigue siendo el único árbitro: duración máxima por ventana, cuotas, reparto entre integraciones, y preemptión inmediata cuando Matter/Thread necesita la radio.

2. El contenido mínimo de la trama. Para decodificar, Theengs necesita:

  • mac (+ tipo de dirección pública/aleatoria) — es el identificador estable del dispositivo
  • rssi
  • name (nombre local) si está presente
  • manufacturerdata (bytes, hex o base64)
  • servicedata + servicedatauuid
  • serviceUuids anunciados
  • una marca de tiempo y la indicación de trama de advertising vs scan response

Un campo bruto (payload_base64 de las estructuras AD, coherente con scanNetwork) en complemento de los campos analizados sería ideal: cubre los casos exóticos sin congelar el análisis del lado del núcleo.

3. Escaneo pasivo Y activo, a elección de la solicitud. Algunos sensores muy extendidos solo exponen sus datos en la scan response (por lo tanto, se requiere escaneo activo). El mode es un parámetro de la solicitud de escaneo; el manifiesto, por su parte, declara la capacidad máxima autorizada (ej. "bluetooth": { "modes": ["passive", "active"] }) que el usuario concede a la instalación.

4. Filtro del lado del núcleo, parametrizado por la solicitud. Opcional pero valioso para no saturar el WebSocket: filtros por prefijo MAC, manufacturer_id o service_uuid pasados en las opciones de escaneo, y un throttle por MAC (ej. máximo 1 trama/MAC/segundo, los sensores repiten la misma trama en ráfaga).

5. Gestión de adaptadores: a nivel del núcleo, propietario único. Este es el punto que hace que el modelo sea realmente superior a un acceso directo:

  • El núcleo inventaria los adaptadores (hci0, dongles USB…), los mantiene actualizados (hotplug), supervisa su salud y los reinicia en caso de bloqueo, los adaptadores BLE económicos se congelan regularmente, un watchdog centralizado beneficia a todos;
  • El núcleo es el único propietario de cada adaptador y multiplexa todos los consumidores: servicio Bluetooth núcleo (GATT/presencia), commissioning Matter/Thread, y ventanas de escaneo de las integraciones, se acabaron los conflictos de propiedad entre procesos;
  • Del lado del usuario: si hay varios adaptadores presentes, es en la configuración del núcleo donde asigna los roles (ej. hci0 reservado para Matter, dongle USB para el escaneo de las integraciones);
  • Del lado de la integración: nunca un camino /dev ni un nombre de adaptador físico, como máximo un identificador lógico opcional en las opciones de escaneo cuando el usuario ha asignado varios adaptadores. La integración solicita « un escaneo », no « el adaptador X ».

Bonus a largo plazo: varios adaptadores asignados al escaneo = varias antenas, mejor cobertura —y la API no tiene que cambiar, el núcleo agrega los tramas.

6. Consentimiento del usuario. Como para las clases de materiales existentes: la escucha BLE revela la presencia de personas (teléfonos, wearables) — una pantalla de autorización durante la instalación, distinta de otros permisos, parece la granularidad adecuada.

7. Fuera del alcance para una v1 del relay (desde nuestro punto de vista): sin conexión GATT ni escritura desde las integraciones, el advertising en modo solo lectura ya cubre la inmensa mayoría de los sensores, y esto mantiene el modelo de seguridad simple.

Algunos recursos:

https://www.npmjs.com/package/theengs-decoder

¡Hola a todos!

Este tema ahora está en desarrollo.

Se ha abierto una PR para especificar el streaming de anuncios BLE (sensores Bluetooth):

https://github.com/GladysAssistant/Gladys/pull/2935

No duden en seguir la PR, probar (opcional, especialmente para pequeñas solicitudes) y dar sus comentarios aquí si es necesario.

@1technophile He propuesto una especificación con Fable 5, ¿qué opinas? :slight_smile:

@pierre-gilles ¿Esto abrirá el camino para una integración externa OTBR?

Gracias, algunos comentarios pero nada bloqueante

¡Gracias por tus comentarios @1technophile!

¡No lo creo!