A API Web Bluetooth permite que os 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 que cantam 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 enviar 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/gravar 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
flag #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 foi encerrado em janeiro de 2017.
Requisitos de segurança
Para entender as compensações de segurança, recomendamos a postagem Modelo de segurança da API 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ê precisará criar pensando na TLS.
Gesto do usuário obrigató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
conhece esse recurso, confira este ótimo tutorial sobre 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 do Bluetooth anunciados e/ou ao nome do dispositivo.
Filtro de serviços
Por exemplo, para solicitar dispositivos Bluetooth que anunciam o serviço de bateria GATT Bluetooth:
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 Bluetooth GATT padronizados, você poderá fornecer o UUID completo do Bluetooth ou um formulário 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 de 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 de 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. Você também vai precisar definir a chave optionalServices
para acessar alguns serviços. Caso contrário, você vai 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 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 acessar um serviço GATT principal 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 GATT do Bluetooth é 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 "Energy Expended" 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.`);
}
Também é possível chamar device.gatt.disconnect()
para desconectar o 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. É possível ler e gravar essas informações de forma semelhante às características do GATT do Bluetooth.
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.
- Desconexão do dispositivo: desconecte e receba uma notificação de desconexão de um dispositivo BLE depois de se conectar a ele.
- Obter características: confira todas as características de um serviço anunciado de um dispositivo BLE.
- Get Descriptors: recebe os descritores de todas as características de um serviço anunciado de um dispositivo BLE.
- Filtro de dados do fabricante: extrai informações básicas de um dispositivo BLE que correspondem 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 link: defina a característica "Alert Level" 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.
- Read Characteristic Value Changed: leia o nível da bateria e seja notificado sobre mudanças em um dispositivo BLE.
- Ler descritores: leia todos os descritores de uma característica 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 shim da API Web Bluetooth está disponível no noble, o módulo central BLE do Node.js mais conhecido. 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 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
Confira 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 para a 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ção 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.