A API Web Bluetooth permite que sites se comuniquem com dispositivos Bluetooth.
E se eu disser que os sites podem se comunicar com dispositivos Bluetooth próximos de forma segura e preservando a privacidade? Assim, 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 só era possível para apps específicos da plataforma. A API Web Bluetooth pretende mudar isso e também levar o recurso para navegadores da Web.
Antes de começar
Este documento pressupõe que você tenha conhecimento básico de como o Bluetooth de baixa energia (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 entusiastas para testar essa API e dar feedback sobre a especificação e feedback sobre a implementação.
Um subconjunto da API Web Bluetooth está disponível no ChromeOS, Chrome para Android 6.0, Mac (Chrome 56) e Windows 10 (Chrome 70). Isso significa que você poderá solicitar e conectar-se a dispositivos Bluetooth de baixa energia (BLE) próximos, ler/gravar características do Bluetooth, receber notificações GATT, saber quando um dispositivo Bluetooth é desconectado e até mesmo ler e gravar descritores Bluetooth. Consulte a tabela Compatibilidade com navegadores da 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 Web Bluetooth em campo, 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 as compensações de segurança, recomendo a postagem Modelo de segurança do Web Bluetooth (em inglês) de Jeffrey Yasskin, um 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 potente adicionado à Web, ela está disponível apenas para contextos seguros. Isso significa que você precisa criar com o TLS em mente.
É necessário um gesto do usuá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 eventos de
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, confira este ótimo tutorial sobre Promises. Mais uma coisa: as () => {} são funções de seta do ECMAScript 2015.
Solicitar dispositivos Bluetooth
Esta versão da especificação da API Web Bluetooth permite que sites, executados na função Central, se conectem a servidores GATT remotos por uma conexão BLE. Ele permite a comunicação entre dispositivos que implementam o Bluetooth 4.0 ou versões mais recentes.
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 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 do Bluetooth:
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });
No entanto, se o serviço GATT Bluetooth não estiver na lista de serviços GATT Bluetooth padronizados, você poderá fornecer o UUID Bluetooth completo 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
Você também pode 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 é necessário definir a
chave optionalServices para acessar serviços não incluídos em um
filtro de serviço. Caso contrário, você vai receber um erro mais tarde ao tentar acessar
os arquivos.
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 Bluetooth company identifier chamada
companyIdentifier. Você também pode fornecer um prefixo de dados que filtra os dados do fabricante de dispositivos Bluetooth que começam com ele. Também é necessário definir a chave optionalServices para acessar serviços não incluídos em um filtro de serviço. Caso contrário, você vai receber um erro mais tarde ao
tentar acessar esses arquivos.
// 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 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 são compatíveis.
// 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); });
Nenhum filtro
Por fim, em vez de filters, use a tecla acceptAllDevices para mostrar todos os dispositivos Bluetooth
próximos. Também é necessário definir a chave optionalServices para acessar alguns serviços. Caso contrário, você vai receber um erro mais tarde
ao tentar acessar os arquivos.
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
Então, o que você faz agora que tem um 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 receber 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 de nível de bateria
padronizada.
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, forneça o UUID Bluetooth completo ou uma forma abreviada de 16 ou 32 bits para service.getCharacteristic.
Também é possível adicionar um listener de eventos characteristicvaluechanged a uma característica para processar a leitura do valor dela. Confira o Exemplo de leitura do valor da característica alterada para saber como processar notificações GATT futuras.
…
.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 Bluetooth
Escrever em uma característica GATT Bluetooth é tão fácil quanto ler. Desta vez, vamos usar o ponto de controle da frequência cardíaca para redefinir o valor do campo "Gasto de energia" para 0 em um dispositivo de monitoramento da frequência cardíaca.
Prometo que 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 Medição da frequência cardíaca 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 notificações com
stopNotifications() e remover corretamente o listener de eventos
characteristicvaluechanged adicionado.
Desconectar de um dispositivo Bluetooth
Para oferecer uma experiência melhor, convém detectar eventos de desconexão e convidar 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 o web app do
dispositivo Bluetooth. Isso vai acionar os listeners de eventos gattserverdisconnected atuais. Ele NÃO interrompe a comunicação do dispositivo Bluetooth se outro
app já estiver se comunicando com ele. Confira o exemplo de desconexão de dispositivo e o exemplo de reconexão automática para saber mais.
Ler e gravar descritores Bluetooth
Os descritores GATT do Bluetooth são atributos que descrevem um valor de característica. É possível ler e gravar nelas de maneira semelhante às características GATT do Bluetooth.
Por exemplo, vamos ver como ler a descrição do usuário sobre o intervalo de medição do termômetro de integridade do dispositivo.
No exemplo abaixo, health_thermometer é o serviço de termômetro de saúde,
measurement_interval a característica de intervalo de medição e
gatt.characteristic_user_description o descritor 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 sobre o intervalo de medição do termômetro de integridade do dispositivo, vamos ver como atualizar 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 do Web Bluetooth abaixo foram testadas com sucesso. Para aproveitar ao máximo essas amostras, recomendo instalar o [BLE Peripheral Simulator Android App] (link em inglês), 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: recupera informações básicas de um dispositivo BLE.
- Nível da bateria: recupera informações da bateria de um dispositivo BLE que anuncia informações da bateria.
- Redefinir energia: redefine a energia gasta por um dispositivo BLE que anuncia a frequência cardíaca.
- Propriedades características: mostram todas as propriedades de uma característica específica de um dispositivo BLE.
- Notificações: inicia e interrompe 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 após a conexão.
- 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: recupera informações básicas de um dispositivo BLE que correspondem aos dados do fabricante.
- Filtros de exclusão: recuperam informações básicas de um dispositivo BLE com filtros de exclusão básicos.
Combinar várias operações
- Características do GAP: recebe todas as características do GAP de um dispositivo BLE.
- Características das informações do dispositivo: recebe todas as características das informações de um dispositivo BLE.
- Perda de link: define a característica "Nível de alerta" de um dispositivo BLE (readValue e writeValue).
- Descobrir serviços e características: descubra todos os serviços principais acessíveis e as características deles em um dispositivo BLE.
- Reconexão automática: reconecte um dispositivo BLE desconectado usando um algoritmo de espera exponencial.
- Ler valor da característica alterado: leia o nível da bateria e receba notificações de mudanças em um dispositivo BLE.
- Ler descritores: lê todos os descritores de características de um serviço de um dispositivo BLE.
- Descritor de gravação: grava no descritor "Descrição do usuário característica" 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 convenientes à API.
- Um shim da API Web Bluetooth está disponível no noble, o módulo central BLE do Node.js mais usado. Isso permite que você webpack/browserify noble sem a necessidade de um servidor WebSocket ou outros plug-ins.
- O angular-web-bluetooth é um módulo para Angular que abstrai todo o boilerplate necessário para configurar a API Web Bluetooth.
Ferramentas
- O Guia de 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. Digite um nome de dispositivo, um serviço, uma característica, defina as propriedades e pronto.
- Se você já for um desenvolvedor Bluetooth, o plug-in Web Bluetooth Developer Studio também vai gerar o código JavaScript do Web Bluetooth para seu dispositivo Bluetooth.
Dicas
Uma página Bluetooth Internals está disponível no Chrome em
about://bluetooth-internals para que você possa inspecionar tudo sobre dispositivos
Bluetooth próximos: status, serviços, características e descritores.
Também recomendo conferir a página oficial Como registrar bugs do Web Bluetooth, já que a depuração do Bluetooth às vezes pode ser difícil.
A seguir
Primeiro, confira o status de implementação do navegador e da plataforma para saber quais partes da API Web Bluetooth estão sendo implementadas no momento.
Embora ainda esteja incompleta, confira uma prévia do que esperar no futuro próximo:
- A procura por anúncios BLE nas proximidades vai acontecer com o
navigator.bluetooth.requestLEScan(). - Um novo evento
serviceaddedvai rastrear os serviços GATT Bluetooth descobertos recentemente, enquanto o eventoserviceremovedvai rastrear os removidos. Um novo eventoservicechangedserá disparado quando uma característica e/ou um descritor for adicionado ou removido de um serviço GATT 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 dos recursos do Chrome
- Bugs de implementação do Chrome
- Especificação do Web Bluetooth
- Problemas de especificação no GitHub
- App simulador de periféricos BLE
Agradecimentos
Agradecemos a Kayce Basques pela revisão.