A API Web Bluetooth permite que sites se comuniquem com dispositivos Bluetooth.
E se eu dissesse que os sites podem se comunicar com dispositivos Bluetooth próximos de maneira segura e que preserva a privacidade? Dessa forma, monitores de frequência cardíaca, lâmpadas cantores e até tartarugas podem interagir diretamente com um site.
Até agora, a capacidade de interagir com dispositivos Bluetooth era possível apenas para apps específicos da plataforma. A API Web Bluetooth tem como objetivo mudar isso e também é compatível com navegadores da Web.
Antes de começar
Este documento pressupõe que você tenha algum conhecimento básico sobre como o Bluetooth Low Energy (BLE) e o perfil de atributo genérico funcionam.
Embora a especificação da API Web Bluetooth ainda não esteja finalizada, os autores da especificação estão procurando ativamente desenvolvedores entusiasmados para testar essa API e dar feedback sobre a especificação e sobre a implementação.
Um subconjunto da API Web Bluetooth está disponível no ChromeOS, no Chrome para Android 6.0, no Mac (Chrome 56) e no Windows 10 (Chrome 70). Isso significa que você precisa ser capaz de solicitar e se conectar a dispositivos Bluetooth de baixa energia próximos, ler/write características do Bluetooth, receber notificações GATT, saber quando um dispositivo Bluetooth é desconectado e até mesmo ler e gravar em descriptores do Bluetooth. Consulte a tabela de compatibilidade com navegadores do MDN para mais informações.
Para Linux e versões anteriores do Windows, ative a sinalização #experimental-web-platform-features
em about://flags
.
Disponível para testes de origem
Para receber o máximo de feedback possível dos desenvolvedores que usam a API Bluetooth da Web, o Chrome adicionou esse recurso no Chrome 53 como um teste de origem para ChromeOS, Android e Mac.
O teste terminou em janeiro de 2017.
Requisitos de segurança
Para entender os prós e contras da segurança, recomendo a postagem Modelo de segurança Web Bluetooth de Jeffrey Yasskin, engenheiro de software da equipe do Chrome, que trabalha na especificação da API Web Bluetooth.
Somente HTTPS
Como essa API experimental é um novo recurso poderoso adicionado à Web, ela só é disponibilizada para contextos seguros. Isso significa que você precisa criar pensando na TLS.
Gesto do usuário necessário
Como um recurso de segurança, a descoberta de dispositivos Bluetooth com
navigator.bluetooth.requestDevice
precisa ser acionada por um gesto do usuário, como
um toque ou um clique do mouse. Estamos falando sobre ouvir
os eventos pointerup
, click
e touchend
.
button.addEventListener('pointerup', function(event) {
// Call navigator.bluetooth.requestDevice
});
Acessar o código
A API Web Bluetooth depende muito das promessas do JavaScript. Se você não tem familiaridade com eles, confira este ótimo tutorial de promessas. Mais uma coisa:
() => {}
são funções de seta do ECMAScript 2015.
Solicitar dispositivos Bluetooth
Essa versão da especificação da API Web Bluetooth permite que sites, em execução na função central, se conectem a servidores GATT remotos por uma conexão BLE. Ele oferece suporte à comunicação entre dispositivos que implementam o Bluetooth 4.0 ou mais recente.
Quando um site solicita acesso a dispositivos próximos usando
navigator.bluetooth.requestDevice
, o navegador mostra ao usuário um seletor
de dispositivos, em que ele pode escolher um dispositivo ou cancelar a solicitação.
A função navigator.bluetooth.requestDevice()
usa um objeto obrigatório que
define filtros. Esses filtros são usados para retornar apenas dispositivos que correspondem a alguns
serviços GATT anunciados pelo Bluetooth e/ou ao nome do dispositivo.
Filtro de serviços
Por exemplo, para solicitar dispositivos Bluetooth que anunciam o Serviço de bateria Bluetooth GATT:
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });
No entanto, se o serviço Bluetooth GATT não estiver na lista de serviços GATT padronizados para Bluetooth, você poderá fornecer o UUID completo do Bluetooth ou um formato curto de 16 ou 32 bits.
navigator.bluetooth.requestDevice({
filters: [{
services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
}]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Filtro de nome
Também é possível solicitar dispositivos Bluetooth com base no nome do dispositivo anunciado
com a chave de filtros name
ou até mesmo um prefixo desse nome com a chave de filtros
namePrefix
. Nesse caso, também será necessário definir a chave optionalServices
para acessar os serviços que não estão incluídos em um filtro de serviço. Caso contrário, você vai receber um erro mais tarde ao tentar acessá-los.
navigator.bluetooth.requestDevice({
filters: [{
name: 'Francois robot'
}],
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Filtro de dados do fabricante
Também é possível solicitar dispositivos Bluetooth com base nos dados específicos do fabricante
que estão sendo anunciados com a chave de filtros manufacturerData
. Essa chave
é uma matriz de objetos com uma chave obrigatória de identificador da empresa Bluetooth chamada
companyIdentifier
. Você também pode fornecer um prefixo de dados que filtre
os dados do fabricante dos dispositivos Bluetooth que começam com ele. Você também
precisa definir a chave optionalServices
para acessar os serviços
que não estão incluídos em um filtro de serviço. Caso contrário, você vai receber um erro mais tarde ao tentar acessá-los.
// 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); });
Uma máscara também pode ser usada com um prefixo de dados para corresponder a alguns padrões nos dados do fabricante. Confira a explicação sobre os filtros de dados do Bluetooth para saber mais.
Filtros de exclusão
A opção exclusionFilters
em navigator.bluetooth.requestDevice()
permite
excluir alguns dispositivos do seletor de navegadores. Ele pode ser usado para excluir dispositivos que correspondem a um filtro mais amplo, mas não têm suporte.
// 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); });
Não há filtros
Por fim, em vez de filters
, use a chave acceptAllDevices
para mostrar todos
os dispositivos Bluetooth próximos. Também será necessário definir a chave optionalServices
para acessar alguns serviços. Caso contrário, você receberá um erro mais tarde ao tentar acessá-los.
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
Conectar-se a um dispositivo Bluetooth
O que você faz agora que tem uma BluetoothDevice
? Vamos nos conectar ao
servidor GATT remoto do Bluetooth, que contém as definições de serviço e
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); });
Ler uma característica do Bluetooth
Aqui, nos conectamos ao servidor GATT do dispositivo Bluetooth remoto. Agora queremos obter um serviço GATT primário e ler uma característica que pertence a esse serviço. Vamos tentar, por exemplo, ler o nível de carga atual da bateria do dispositivo.
No exemplo a seguir, battery_level
é a característica
padronizada do nível da bateria.
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); });
Se você usar uma característica GATT Bluetooth personalizada, poderá fornecer o
UUID completo do Bluetooth ou uma forma curta de 16 ou 32 bits para
service.getCharacteristic
.
Também é possível adicionar um listener de evento characteristicvaluechanged
a uma
característica para processar a leitura do valor dela. Confira o exemplo de mudança de
valor da característica de leitura para saber como processar opcionalmente as próximas notificações
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);
}
Gravar em uma característica do Bluetooth
Gravar em uma característica do Bluetooth GATT é tão fácil quanto ler. Dessa vez, vamos usar o ponto de controle da frequência cardíaca para redefinir o valor do campo "Energia gasta" para 0 em um dispositivo de monitor de frequência cardíaca.
Não há mágica aqui. Tudo isso é explicado na página Característica do ponto de controle da frequência 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); });
Receber notificações GATT
Agora vamos ver como receber uma notificação quando a característica Heart Rate Measurement mudar no 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
}
O Exemplo de notificações mostra como interromper as notificações com
stopNotifications()
e remover corretamente o listener de eventos characteristicvaluechanged
adicionado.
Desconectar de um dispositivo Bluetooth
Para oferecer uma melhor experiência do usuário, ouça eventos de desconexão e convide o usuário a se reconectar:
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.`);
}
Você também pode chamar device.gatt.disconnect()
para desconectar seu app da Web do
dispositivo Bluetooth. Isso vai acionar os listeners de evento
gattserverdisconnected
atuais. Ele NÃO vai interromper a comunicação do dispositivo Bluetooth se outro
app já estiver se comunicando com ele. Confira o exemplo de desconexão
do dispositivo e o exemplo de reconexão automática para saber mais.
Ler e gravar em descritores de Bluetooth
Os descritores GATT do Bluetooth são atributos que descrevem um valor de característica. Eles podem ser lidos e gravados de maneira semelhante às características do Bluetooth GATT.
Vamos ver, por exemplo, como ler a descrição do usuário do intervalo de medição do termômetro de integridade do dispositivo.
No exemplo abaixo, health_thermometer
é o serviço do Health Thermometer,
measurement_interval
é a característica Intervalo de medição e
gatt.characteristic_user_description
é o descriptor de descrição do usuário da
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); });
Agora que lemos a descrição do usuário do intervalo de medição do termômetro de integridade do dispositivo, vamos ver como atualizá-lo e gravar um 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); });
Exemplos, demonstrações e codelabs
Todas as amostras de Bluetooth da Web abaixo foram testadas. Para aproveitar ao máximo esses exemplos, recomendamos instalar o [app Android do simulador de periférico BLE], que simula um periférico BLE com um serviço de bateria, um serviço de frequência cardíaca ou um serviço de termômetro de saúde.
Iniciante
- Informações do dispositivo: extrai informações básicas de um dispositivo BLE.
- Nível da bateria: extrai informações da bateria de um dispositivo BLE que anuncia informações da bateria.
- Redefinir energia: redefina a energia gasta de um dispositivo BLE que anuncia a frequência cardíaca.
- Propriedades da característica: mostra todas as propriedades de uma característica específica de um dispositivo BLE.
- Notificações: inicie e pare notificações de características de um dispositivo BLE.
- Dispositivo desconectado: se um dispositivo BLE for desconectado, recebe uma notificação quando se conecta a ele.
- Get Characteristics: recebe todas as características de um serviço anunciado de um dispositivo BLE.
- Get Descriptors: recebe todos os descritores de características de um serviço anunciado de um dispositivo BLE.
- Filtro de dados do fabricante: recupere informações básicas de um dispositivo BLE que corresponda aos dados do fabricante.
- Filtros de exclusão: extrai informações básicas de um dispositivo BLE com filtros de exclusão básicos.
Combinar várias operações
- Características GAP: receba todas as características GAP de um dispositivo BLE.
- Características de informações do dispositivo: receba todas as características de informações de um dispositivo BLE.
- Perda de vínculo: defina a característica do nível de alerta de um dispositivo BLE (readValue e writeValue).
- Discover Services & Characteristics: descubra todos os serviços principais acessíveis e as características deles em um dispositivo BLE.
- Reconexão automática: reconecte a um dispositivo BLE desconectado usando um algoritmo de espera aleatória exponencial.
- Ler o valor da característica mudou: leia o nível da bateria e seja notificado sobre mudanças em um dispositivo BLE.
- Ler descritores: ler todos os descritores de características de um serviço de um dispositivo BLE.
- Descriptor de gravação: grava no descritor "Characteristic User Description" em um dispositivo BLE.
Confira também nossas demonstrações selecionadas do Web Bluetooth e os codelabs oficiais do Web Bluetooth.
Bibliotecas
- web-bluetooth-utils é um módulo npm que adiciona algumas funções de conveniência à API.
- Um paliativo da API Web Bluetooth está disponível no noble, o módulo central BLE mais usado do Node.js. Isso permite que você use o webpack/browserify noble sem precisar de um servidor WebSocket ou outros plug-ins.
- angular-web-bluetooth é um módulo do Angular que abstrai todo o boilerplate necessário para configurar a API Web Bluetooth.
Ferramentas
- Primeiros passos com o Web Bluetooth é um app da Web simples que gera todo o código boilerplate JavaScript para começar a interagir com um dispositivo Bluetooth. Insira um nome de dispositivo, um serviço, uma característica, defina as propriedades e está tudo pronto.
- Se você já for um desenvolvedor de Bluetooth, o Web Bluetooth Developer Studio Plugin também vai gerar o código JavaScript do Web Bluetooth para seu dispositivo Bluetooth.
Dicas
A página Bluetooth Internals está disponível no Chrome em
about://bluetooth-internals
para que você possa inspecionar tudo sobre os dispositivos Bluetooth
próximos: status, serviços, características e descritores.
Também recomendamos consultar a página oficial Como registrar bugs do Web Bluetooth, já que a depuração do Bluetooth pode ser difícil às vezes.
A seguir
Verifique o status de implementação do navegador e da plataforma primeiro para saber quais partes da API Web Bluetooth estão sendo implementadas.
Embora ainda não esteja completo, confira uma prévia do que esperar no futuro:
- A verificação de anúncios BLE próximos
vai acontecer com
navigator.bluetooth.requestLEScan()
. - Um novo evento
serviceadded
vai rastrear os serviços GATT Bluetooth recém-descobertos, enquanto o eventoserviceremoved
vai rastrear os removidos. Um novo eventoservicechanged
será acionado quando qualquer característica e/ou descritor for adicionado ou removido de um serviço GATT do Bluetooth.
Mostrar suporte à API
Você planeja usar a API Web Bluetooth? Seu apoio público ajuda a equipe do Chrome a priorizar recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.
Envie um tweet para @ChromiumDev usando a hashtag
#WebBluetooth
e informe onde e como você está usando.
Recursos
- Stack Overflow (em inglês)
- Status do recurso do Chrome
- Bugs de implementação do Chrome
- Especificações do Web Bluetooth
- Problemas de especificação no GitHub
- App de simulador de periféricos BLE
Agradecimentos
Agradecemos a Kayce Basques por revisar este artigo. Imagem principal da SparkFun Electronics, de Boulder, EUA.