La API de Web Bluetooth permite que los sitios web se comuniquen con dispositivos Bluetooth.
¿Qué pasaría si te dijera que los sitios web pueden comunicarse con dispositivos Bluetooth cercanos de una manera segura y que preserva la privacidad? De esta manera, los monitores de frecuencia cardíaca, las bombillas que cantan y hasta las tortugas podrían interactuar directamente con un sitio web.
Hasta ahora, la capacidad de interactuar con dispositivos Bluetooth solo era posible para apps específicas de la plataforma. El objetivo de la API de Web Bluetooth es cambiar esto y también llevarlo a los navegadores web.
Antes de comenzar
En este documento, se supone que tienes algunos conocimientos básicos sobre el funcionamiento de Bluetooth de bajo consumo (BLE) y el perfil de atributos genéricos.
Aunque la especificación de la API de Web Bluetooth aún no está finalizada, los autores de la especificación están buscando activamente desarrolladores entusiastas que prueben esta API y proporcionen comentarios sobre la especificación y comentarios sobre la implementación.
Un subconjunto de la API de Bluetooth web está disponible en ChromeOS, Chrome para Android 6.0, Mac (Chrome 56) y Windows 10 (Chrome 70). Esto significa que deberías poder solicitar y conectarte a dispositivos Bluetooth de bajo consumo cercanos, leer/escribir características de Bluetooth, recibir notificaciones GATT, saber cuándo se desconecta un dispositivo Bluetooth y hasta leer y escribir en descriptores de Bluetooth. Consulta la tabla Compatibilidad con navegadores de MDN para obtener más información.
Para Linux y versiones anteriores de Windows, habilita la marca #experimental-web-platform-features
en about://flags
.
Disponible para pruebas de origen
Para obtener la mayor cantidad posible de comentarios de los desarrolladores que usan la API de Bluetooth web en el campo, Chrome agregó esta función en Chrome 53 como una prueba de origen para ChromeOS, Android y Mac.
La prueba finalizó correctamente en enero de 2017.
Requisitos de seguridad
Para comprender las compensaciones de seguridad, te recomiendo la entrada Web Bluetooth Security Model de Jeffrey Yasskin, ingeniero de software del equipo de Chrome, que trabaja en la especificación de la API de Web Bluetooth.
Solo HTTPS
Debido a que esta API experimental es una nueva función potente que se agrega a la Web, solo está disponible para contextos seguros. Esto significa que deberás compilar teniendo en cuenta TLS.
Se requiere un gesto del usuario
Como medida de seguridad, la detección de dispositivos Bluetooth con navigator.bluetooth.requestDevice
debe activarse con un gesto del usuario, como un toque o un clic con el mouse. Nos referimos a escuchar eventos pointerup
, click
y touchend
.
button.addEventListener('pointerup', function(event) {
// Call navigator.bluetooth.requestDevice
});
Cómo ingresar al código
La API de Bluetooth Web depende en gran medida de las promesas de JavaScript. Si no estás familiarizado con ellas, consulta este excelente instructivo sobre promesas. Otra cosa, () => {}
son funciones de flecha de ECMAScript 2015.
Cómo solicitar dispositivos Bluetooth
Esta versión de la especificación de la API de Web Bluetooth permite que los sitios web, que se ejecutan en el rol central, se conecten a servidores GATT remotos a través de una conexión BLE. Admite la comunicación entre dispositivos que implementan Bluetooth 4.0 o versiones posteriores.
Cuando un sitio web solicita acceso a dispositivos cercanos con navigator.bluetooth.requestDevice
, el navegador le muestra al usuario un selector de dispositivos en el que puede elegir uno o cancelar la solicitud.
La función navigator.bluetooth.requestDevice()
toma un objeto obligatorio que define los filtros. Estos filtros se usan para mostrar solo los dispositivos que coinciden con algunos servicios GATT de Bluetooth anunciados o el nombre del dispositivo.
Filtro de servicios
Por ejemplo, para solicitar dispositivos Bluetooth que promocionen el servicio de batería GATT de Bluetooth, haz lo siguiente:
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Sin embargo, si tu servicio GATT de Bluetooth no está en la lista de servicios GATT de Bluetooth estandarizados, puedes proporcionar el UUID de Bluetooth completo o un formulario corto de 16 o 32 bits.
navigator.bluetooth.requestDevice({
filters: [{
services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
}]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Filtro de nombre
También puedes solicitar dispositivos Bluetooth según el nombre del dispositivo que se anuncia con la clave de filtros name
o incluso un prefijo de este nombre con la clave de filtros namePrefix
. Ten en cuenta que, en este caso, también deberás definir la clave optionalServices
para poder acceder a cualquier servicio que no se incluya en un filtro de servicios. De lo contrario, recibirás un error más adelante cuando intentes acceder a ellos.
navigator.bluetooth.requestDevice({
filters: [{
name: 'Francois robot'
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Filtro de datos de fabricantes
También es posible solicitar dispositivos Bluetooth según los datos específicos del fabricante que se anuncian con la clave de filtros manufacturerData
. Esta clave es un array de objetos con una clave obligatoria de identificador de empresa Bluetooth llamada companyIdentifier
. También puedes proporcionar un prefijo de datos que filtre los datos del fabricante de los dispositivos Bluetooth que comiencen con él. Ten en cuenta que también deberás definir la clave optionalServices
para poder acceder a cualquier servicio que no se incluya en un filtro de servicios. De lo contrario, recibirás un error más adelante cuando intentes acceder a ellos.
// Filter Bluetooth devices from Google company with manufacturer data bytes
// that start with [0x01, 0x02].
navigator.bluetooth.requestDevice({
filters: [{
manufacturerData: [{
companyIdentifier: 0x00e0,
dataPrefix: new Uint8Array([0x01, 0x02])
}]
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
También se puede usar una máscara con un prefijo de datos para que coincida con algunos patrones en los datos del fabricante. Consulta la explicación de los filtros de datos de Bluetooth para obtener más información.
Filtros de exclusión
La opción exclusionFilters
en navigator.bluetooth.requestDevice()
te permite excluir algunos dispositivos del selector de navegadores. Se puede usar para excluir dispositivos que coinciden con un filtro más amplio, pero que no son compatibles.
// Request access to a bluetooth device whose name starts with "Created by".
// The device named "Created by Francois" has been reported as unsupported.
navigator.bluetooth.requestDevice({
filters: [{
namePrefix: "Created by"
}],
exclusionFilters: [{
name: "Created by Francois"
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Sin filtros
Por último, en lugar de filters
, puedes usar la clave acceptAllDevices
para mostrar todos los dispositivos Bluetooth cercanos. También deberás definir la clave optionalServices
para poder acceder a algunos servicios. De lo contrario, recibirás un error más adelante cuando intentes acceder a ellos.
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Conexión a un dispositivo Bluetooth
¿Qué debes hacer ahora que tienes un BluetoothDevice
? Conectémonos al servidor GATT remoto de Bluetooth, que contiene las definiciones de servicio y característica.
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
// Human-readable name of the device.
console.log(device.name);
// Attempts to connect to remote GATT Server.
return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });
Cómo leer una característica Bluetooth
Aquí, nos conectamos al servidor GATT del dispositivo Bluetooth remoto. Ahora, queremos obtener un servicio GATT principal y leer una característica que pertenezca a este servicio. Por ejemplo, probemos leer el nivel de carga actual de la batería del dispositivo.
En el siguiente ejemplo, battery_level
es la característica estandarizada de nivel de batería.
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.gatt.connect())
.then(server => {
// Getting Battery Service…
return server.getPrimaryService('battery_service');
})
.then(service => {
// Getting Battery Level Characteristic…
return service.getCharacteristic('battery_level');
})
.then(characteristic => {
// Reading Battery Level…
return characteristic.readValue();
})
.then(value => {
console.log(`Battery percentage is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });
Si usas una característica GATT de Bluetooth personalizada, puedes proporcionar el UUID de Bluetooth completo o un formato corto de 16 o 32 bits a service.getCharacteristic
.
Ten en cuenta que también puedes agregar un objeto de escucha de eventos characteristicvaluechanged
a una característica para controlar la lectura de su valor. Consulta el ejemplo de cambio de valor de la característica de lectura para ver cómo controlar de forma opcional las próximas notificaciones de GATT.
…
.then(characteristic => {
// Set up event listener for when characteristic value changes.
characteristic.addEventListener('characteristicvaluechanged',
handleBatteryLevelChanged);
// Reading Battery Level…
return characteristic.readValue();
})
.catch(error => { console.error(error); });
function handleBatteryLevelChanged(event) {
const batteryLevel = event.target.value.getUint8(0);
console.log('Battery percentage is ' + batteryLevel);
}
Cómo escribir en una característica Bluetooth
Escribir en una característica GATT de Bluetooth es tan fácil como leerla. Esta vez, vamos a usar el punto de control de frecuencia cardíaca para restablecer el valor del campo Energía gastada a 0 en un dispositivo monitor de frecuencia cardíaca.
Te prometo que no hay magia aquí. Todo se explica en la página de características del punto de control de la frecuencia cardíaca.
navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_control_point'))
.then(characteristic => {
// Writing 1 is the signal to reset energy expended.
const resetEnergyExpended = Uint8Array.of(1);
return characteristic.writeValue(resetEnergyExpended);
})
.then(_ => {
console.log('Energy expended has been reset.');
})
.catch(error => { console.error(error); });
Cómo recibir notificaciones GATT
Ahora, veamos cómo recibir una notificación cuando cambie la característica Medición de frecuencia cardíaca en el dispositivo:
navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
characteristic.addEventListener('characteristicvaluechanged',
handleCharacteristicValueChanged);
console.log('Notifications have been started.');
})
.catch(error => { console.error(error); });
function handleCharacteristicValueChanged(event) {
const value = event.target.value;
console.log('Received ' + value);
// TODO: Parse Heart Rate Measurement value.
// See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}
En el ejemplo de notificaciones, se muestra cómo detener las notificaciones con stopNotifications()
y quitar correctamente el objeto de escucha de eventos characteristicvaluechanged
agregado.
Cómo desconectarse de un dispositivo Bluetooth
Para ofrecer una mejor experiencia del usuario, te recomendamos que escuches los eventos de desconexión y que invites al usuario a volver a conectarse:
navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
.then(device => {
// Set up event listener for when device gets disconnected.
device.addEventListener('gattserverdisconnected', onDisconnected);
// Attempts to connect to remote GATT Server.
return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });
function onDisconnected(event) {
const device = event.target;
console.log(`Device ${device.name} is disconnected.`);
}
También puedes llamar a device.gatt.disconnect()
para desconectar tu app web del dispositivo Bluetooth. Esto activará los objetos de escucha de eventos gattserverdisconnected
existentes. Ten en cuenta que NO detendrá la comunicación del dispositivo Bluetooth si otra app ya se está comunicando con él. Consulta el ejemplo de desconexión de dispositivos y el ejemplo de reconexión automática para obtener más información.
Lee y escribe en descriptores Bluetooth
Los descriptores GATT de Bluetooth son atributos que describen un valor de característica. Puedes leerlos y escribirlos de manera similar a las características GATT de Bluetooth.
Veamos, por ejemplo, cómo leer la descripción del usuario del intervalo de medición del termómetro de estado del dispositivo.
En el siguiente ejemplo, health_thermometer
es el servicio de Health Thermometer, measurement_interval
es la característica Intervalo de medición y gatt.characteristic_user_description
es el descriptor de descripción del usuario de la característica.
navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => descriptor.readValue())
.then(value => {
const decoder = new TextDecoder('utf-8');
console.log(`User Description: ${decoder.decode(value)}`);
})
.catch(error => { console.error(error); });
Ahora que leímos la descripción del usuario del intervalo de medición del termómetro de estado del dispositivo, veamos cómo actualizarlo y escribir un valor personalizado.
navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => {
const encoder = new TextEncoder('utf-8');
const userDescription = encoder.encode('Defines the time between measurements.');
return descriptor.writeValue(userDescription);
})
.catch(error => { console.error(error); });
Muestras, demostraciones y codelabs
Todos los ejemplos de Bluetooth web que se indican a continuación se probaron correctamente. Para disfrutar de estos ejemplos al máximo, te recomiendo que instales la [app para Android de BLE Peripheral Simulator] que simula un periférico BLE con un servicio de batería, un servicio de frecuencia cardíaca o un servicio de termómetro de salud.
Principiante
- Device Info: Recupera información básica del dispositivo BLE.
- Nivel de batería: Recupera información de la batería de un dispositivo BLE que anuncia información de la batería.
- Restablece la energía: Restablece la energía que se consume de un dispositivo BLE que anuncia la frecuencia cardíaca.
- Characteristic Properties: Muestra todas las propiedades de una característica específica de un dispositivo BLE.
- Notificaciones: Inicia y detiene las notificaciones de características desde un dispositivo BLE.
- Desconectar dispositivo: Desconecta un dispositivo BLE y recibe una notificación cuando se desconecte después de conectarte a él.
- Get Characteristics: Obtén todas las características de un servicio anunciado desde un dispositivo BLE.
- Get Descriptors: Obtén todos los descriptores de características de un servicio anunciado desde un dispositivo BLE.
- Filtro de datos del fabricante: Recupera información básica del dispositivo desde un dispositivo BLE que coincida con los datos del fabricante.
- Filtros de exclusión: Recupera información básica del dispositivo desde un dispositivo BLE con filtros de exclusión básicos.
Combina varias operaciones
- Características de GAP: Obtén todas las características de GAP de un dispositivo BLE.
- Device Information Characteristics: Obtén todas las características de información del dispositivo de un dispositivo BLE.
- Pérdida de vínculo: Establece la característica de nivel de alerta de un dispositivo BLE (readValue y writeValue).
- Discover Services & Characteristics: Descubre todos los servicios principales accesibles y sus características desde un dispositivo BLE.
- Vuelve a conectar automáticamente: Vuelve a conectarte a un dispositivo BLE desconectado con un algoritmo de retirada exponencial.
- Read Characteristic Value Changed: Lee el nivel de batería y recibe notificaciones de los cambios desde un dispositivo BLE.
- Lee descriptores: Lee todos los descriptores de una característica de un servicio desde un dispositivo BLE.
- Escribe el descriptor: Escribe en el descriptor "Descripción del usuario de la característica" en un dispositivo BLE.
Consulta también nuestras demostraciones seleccionadas de Bluetooth web y los codelabs oficiales de Bluetooth web.
Bibliotecas
- web-bluetooth-utils es un módulo de npm que agrega algunas funciones convenientes a la API.
- Hay un shim de la API de Bluetooth Web disponible en noble, el módulo central de BLE más popular de Node.js. Esto te permite usar webpack/browserify noble sin necesidad de un servidor WebSocket ni otros complementos.
- angular-web-bluetooth es un módulo para Angular que abstrae todo el código de plantilla necesario para configurar la API de Web Bluetooth.
Herramientas
- Comienza a usar Web Bluetooth es una app web simple que generará todo el código de plantilla de JavaScript para comenzar a interactuar con un dispositivo Bluetooth. Ingresa un nombre de dispositivo, un servicio, una característica, define sus propiedades y listo.
- Si ya eres desarrollador de Bluetooth, el complemento Web Bluetooth Developer Studio también generará el código JavaScript de Web Bluetooth para tu dispositivo Bluetooth.
Sugerencias
En Chrome, hay una página Bluetooth Internals disponible en about://bluetooth-internals
para que puedas inspeccionar todo sobre los dispositivos Bluetooth cercanos: estado, servicios, características y descriptores.
También te recomiendo que consultes la página oficial Cómo informar errores de Bluetooth web, ya que, a veces, la depuración de Bluetooth puede ser difícil.
¿Qué sigue?
Primero, verifica el estado de implementación del navegador y la plataforma para saber qué partes de la API de Bluetooth web se están implementando actualmente.
Aunque aún no está completa, aquí tienes un adelanto de lo que puedes esperar en un futuro cercano:
- La búsqueda de anuncios BLE cercanos se realizará con
navigator.bluetooth.requestLEScan()
. - Un nuevo evento
serviceadded
hará un seguimiento de los servicios GATT de Bluetooth recién descubiertos, mientras que el eventoserviceremoved
hará un seguimiento de los que se quitaron. Se activará un nuevo eventoservicechanged
cuando se agregue o quite cualquier característica o descriptor de un servicio GATT de Bluetooth.
Cómo mostrar compatibilidad con la API
¿Piensas usar la API de Web Bluetooth? Tu apoyo público ayuda al equipo de Chrome a priorizar las funciones y les muestra a otros proveedores de navegadores lo importante que es admitirlas.
Envía un tuit a @ChromiumDev con el hashtag #WebBluetooth
y cuéntanos dónde y cómo lo usas.
Recursos
- Stack Overflow
- Estado de las funciones de Chrome
- Errores de implementación de Chrome
- Especificaciones de Web Bluetooth
- Problemas de especificación en GitHub
- App de simulador de periféricos BLE
Agradecimientos
Gracias a Kayce Basques por revisar este artículo. Imagen hero de SparkFun Electronics de Boulder, EE.UU.