1. Sobre o que é?
Neste codelab, você vai aprender a controlar uma vela sem chamas de LED PLAYBULB com apenas JavaScript, graças à API Web Bluetooth. Ao longo do caminho, você também testará recursos do JavaScript ES2015, como Classes, Funções de seta, Mapa e Promessas.
O que você vai aprender
- Como interagir no JavaScript com um dispositivo Bluetooth por perto
- Como usar as classes ES2015, as funções de seta, o Map e as promessas.
O que é necessário
- Noções básicas de desenvolvimento da Web
- Conhecimento básico sobre o Bluetooth de baixa energia (BLE) e o perfil de atributo genérico (GATT, na sigla em inglês)
- Um editor de texto de sua escolha
- Um Mac, Chromebook ou um dispositivo Android M com o app do navegador Chrome e um micro USB para cabo USB.
2. Jogue primeiro
Confira a versão final do app que você está prestes a criar em https://googlecodelabs.github.io/candle-bluetooth (link em inglês) e teste o dispositivo PLAYBULB Candle Bluetooth que está à sua disposição antes de começar este codelab.
Você também pode me assistir mudando de cor em https://www.youtube.com/watch?v=fBCPA9gIxlU
3. Começar a configuração
Fazer o download do exemplo de código
Você pode obter o exemplo de código para este código fazendo o download do zip aqui:
ou clonando este repositório do Git:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
Se você fez o download da origem como um ZIP, descompactá-lo fornece uma pasta raiz candle-bluetooth-master
.
Instalar e verificar o servidor da Web
Você pode usar seu próprio servidor da Web, mas este codelab foi projetado para funcionar bem com o Chrome Web Server. Se você ainda não tem esse app instalado, faça isso pela Chrome Web Store.
Depois de instalar o app do servidor da Web para Chrome, clique no atalho "Apps" na barra de favoritos:
Na janela seguinte, clique no ícone do Web Server:
Você verá esta caixa de diálogo, que permite configurar seu servidor da Web local:
Clique no botão Escolher pasta e selecione a raiz do repositório clonado (ou desarquivado). Isso permitirá que você exiba o trabalho em andamento pelo URL destacado na caixa de diálogo do servidor da Web (na seção URLs do servidor da Web).
Em "Opções", marque a caixa ao lado de "Mostrar index.html automaticamente", conforme mostrado abaixo:
Agora acesse seu site no navegador da Web (clicando no URL do servidor da Web destacado) e você verá uma página como esta:
Para conferir a aparência desse app no seu smartphone Android, ative a Depuração remota no Android e configure o encaminhamento de portas. Por padrão, o número da porta é 8887. Depois disso, basta abrir uma nova guia do Chrome para http://localhost:8887 no seu smartphone Android.
A seguir
Neste ponto, o aplicativo da web não tem muita utilidade. Vamos adicionar compatibilidade com Bluetooth!
4. Descubra a vela
Vamos começar escrevendo uma biblioteca que usa uma classe JavaScript ES2015 para o dispositivo PLAYBULB Candle Bluetooth.
Mantenha a calma. A sintaxe da classe não está introduzindo um novo modelo de herança orientado a objetos para JavaScript. Ele simplesmente fornece uma sintaxe muito mais clara para criar objetos e lidar com heranças, como você pode ler abaixo.
Primeiro, vamos definir uma classe PlaybulbCandle
em playbulbCandle.js
e criar uma instância playbulbCandle
que será disponibilizada no arquivo app.js
depois.
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Para solicitar acesso a um dispositivo Bluetooth próximo, precisamos chamar navigator.bluetooth.requestDevice
. Como o dispositivo PLAYBULB Candle anuncia continuamente (se ainda não estiver pareado) um UUID de serviço GATT Bluetooth constante conhecido na forma abreviada como 0xFF02
, podemos simplesmente definir uma constante e adicioná-la ao parâmetro de serviços de filtros em um novo método connect
público da classe PlaybulbCandle
.
Também vamos rastrear internamente o objeto BluetoothDevice
para que possamos acessá-lo mais tarde, se necessário. Como navigator.bluetooth.requestDevice
retorna uma promessa ES2015 JavaScript, faremos isso no método then
.
playbulbCandle.js
(function() {
'use strict';
const CANDLE_SERVICE_UUID = 0xFF02;
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(function(device) {
this.device = device;
}.bind(this));
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Como um recurso de segurança, a descoberta de dispositivos Bluetooth por perto com navigator.bluetooth.requestDevice
precisa ser chamada por um gesto do usuário, como um toque ou um clique do mouse. Por isso, vamos chamar o método connect
quando o usuário clicar no botão "Conectar". no arquivo app.js
:
app.js
document.querySelector('#connect').addEventListener('click', function(event) {
document.querySelector('#state').classList.add('connecting');
playbulbCandle.connect()
.then(function() {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
})
.catch(function(error) {
console.error('Argh!', error);
});
});
Executar o app
Neste ponto, acesse o site no navegador da Web (clicando no URL do servidor da Web destacado no aplicativo do servidor da Web) ou simplesmente atualize a página. Clique no botão verde "Conectar". selecione o dispositivo no seletor, abra seu console favorito das Ferramentas para desenvolvedores usando o atalho de teclado Ctrl + Shift + J e observe que o objeto BluetoothDevice
foi registrado.
Você pode receber um erro se o Bluetooth estiver desativado e/ou o dispositivo Bluetooth PLAYBULB Candle estiver desativado. Nesse caso, ative-a e prossiga de novo.
Bônus obrigatório
Não sei você, mas já vejo muitas function() {}
neste código. Em vez disso, vamos mudar para as funções de seta do JavaScript ES2015 () => {}
. Eles são um grande benefício: Toda a beleza das funções anônimas, sem a tristeza da vinculação.
playbulbCandle.js
(function() {
'use strict';
const CANDLE_SERVICE_UUID = 0xFF02;
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(device => {
this.device = device;
});
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
})
.catch(error => {
console.error('Argh!', error);
});
});
A seguir
- Certo... Posso falar com essa vela ou o quê?
- Claro... passe para a próxima etapa
Perguntas frequentes
5. Ler algo
O que fazer agora que há um BluetoothDevice
retornado da promessa de navigator.bluetooth.requestDevice
? Vamos nos conectar ao servidor GATT remoto Bluetooth que contém o serviço Bluetooth e as definições de características chamando device.gatt.connect()
:
playbulbCandle.js
class PlaybulbCandle {
constructor() {
this.device = null;
}
connect() {
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}]};
return navigator.bluetooth.requestDevice(options)
.then(device => {
this.device = device;
return device.gatt.connect();
});
}
}
Ler o nome do dispositivo
Estamos conectados ao servidor GATT do dispositivo Bluetooth PLAYBULB Candle. Agora, queremos obter o serviço GATT principal (anunciado como 0xFF02
anteriormente) e ler a característica do nome do dispositivo (0xFFFF
) que pertence a esse serviço. Isso pode ser feito facilmente adicionando um novo método getDeviceName
à classe PlaybulbCandle
e usando device.gatt.getPrimaryService
e service.getCharacteristic
. O método characteristic.readValue
vai retornar um DataView
que simplesmente vamos decodificar com TextDecoder
.
playbulbCandle.js
const CANDLE_DEVICE_NAME_UUID = 0xFFFF;
...
getDeviceName() {
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_DEVICE_NAME_UUID))
.then(characteristic => characteristic.readValue())
.then(data => {
let decoder = new TextDecoder('utf-8');
return decoder.decode(data);
});
}
Vamos adicionar isso a app.js
chamando playbulbCandle.getDeviceName
quando estivermos conectados e exibirmos o nome do dispositivo.
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
return playbulbCandle.getDeviceName().then(handleDeviceName);
})
.catch(error => {
console.error('Argh!', error);
});
});
function handleDeviceName(deviceName) {
document.querySelector('#deviceName').value = deviceName;
}
Neste ponto, acesse o site no navegador da Web (clicando no URL do servidor da Web destacado no aplicativo do servidor da Web) ou simplesmente atualize a página. Verifique se a vela PLAYBULB está ativada e clique no botão "Conectar" na página, e você verá o nome do dispositivo abaixo do seletor de cores.
Ler o nível de bateria
Há também uma característica Bluetooth padrão de nível de bateria disponível no dispositivo Bluetooth PLAYBULB Candle com o nível de bateria do dispositivo. Isso significa que podemos usar nomes padrão como battery_service
para o UUID do serviço Bluetooth GATT e battery_level
para o UUID de característica do Bluetooth GATT.
Vamos adicionar um novo método getBatteryLevel
à classe PlaybulbCandle
e ler o nível de bateria em porcentagem.
playbulbCandle.js
getBatteryLevel() {
return this.device.gatt.getPrimaryService('battery_service')
.then(service => service.getCharacteristic('battery_level'))
.then(characteristic => characteristic.readValue())
.then(data => data.getUint8(0));
}
Também precisamos atualizar o objeto JavaScript options
para incluir o serviço de bateria para a chave optionalServices
, já que ela não é anunciada pelo dispositivo Bluetooth PLAYBULB Candle, mas ainda é obrigatório para acessá-lo.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
Como antes, vamos conectar isso a app.js
chamando playbulbCandle.getBatteryLevel
quando tivermos o nome do dispositivo e mostrarmos o nível de bateria.
app.js
document.querySelector('#connect').addEventListener('click', event => {
playbulbCandle.connect()
.then(() => {
console.log(playbulbCandle.device);
document.querySelector('#state').classList.remove('connecting');
document.querySelector('#state').classList.add('connected');
return playbulbCandle.getDeviceName().then(handleDeviceName)
.then(() => playbulbCandle.getBatteryLevel().then(handleBatteryLevel));
})
.catch(error => {
console.error('Argh!', error);
});
});
function handleDeviceName(deviceName) {
document.querySelector('#deviceName').value = deviceName;
}
function handleBatteryLevel(batteryLevel) {
document.querySelector('#batteryLevel').textContent = batteryLevel + '%';
}
Agora, acesse o site no navegador da Web (clicando no URL do servidor da Web destacado no aplicativo do servidor da Web) ou simplesmente atualize a página. Clique no botão "Conectar". na página. Você verá o nome do dispositivo e o nível da bateria.
A seguir
- Como posso mudar a cor dessa lâmpada? É por isso que estou aqui.
- Você está tão perto, prometo...
Perguntas frequentes
6. Mudar a cor
Mudar a cor é tão fácil quanto escrever um conjunto específico de comandos para uma característica Bluetooth (0xFFFC
) no serviço GATT principal anunciado como 0xFF02
. Por exemplo, mudar a vela do PLAYBULB para vermelho significa escrever uma matriz de números inteiros não assinados de 8 bits iguais a [0x00, 255, 0, 0]
, em que 0x00
é a saturação branca e 255, 0, 0
são, respectivamente, os valores vermelho, verde e azul .
Vamos usar o characteristic.writeValue
para gravar alguns dados na característica do Bluetooth no novo método público setColor
da classe PlaybulbCandle
. Também vamos retornar os valores reais de vermelho, verde e azul quando a promessa for atendida para que possamos usá-los em app.js
mais tarde:
playbulbCandle.js
const CANDLE_COLOR_UUID = 0xFFFC;
...
setColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_COLOR_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
Vamos atualizar a função changeColor
no app.js
para chamar playbulbCandle.setColor
quando a mensagem "Sem efeito" está marcado. As variáveis de cor globais r, g, b
já estão definidas quando o usuário clica na tela do seletor de cores.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
if (effect === 'noEffect') {
playbulbCandle.setColor(r, g, b).then(onColorChanged);
}
}
Neste ponto, acesse o site no navegador da Web (clicando no URL do servidor da Web destacado no aplicativo do servidor da Web) ou simplesmente atualize a página. Clique no botão "Conectar". na página e clique no seletor de cores para mudar a cor da vela do PLAYBULB quantas vezes quiser.
Efeitos de vela moar (link em inglês)
Se você já acendeu uma vela antes, sabe que a luz não é estática. Felizmente, há outra característica de Bluetooth (0xFFFB
) no serviço GATT principal anunciado como 0xFF02
que permite ao usuário definir alguns efeitos de vela.
Como definir um "efeito de vela" por exemplo, pode ser conseguido escrevendo [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]
. Também é possível definir o "efeito intermitente" com [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]
.
Vamos adicionar os métodos setCandleEffectColor
e setFlashingColor
à classe PlaybulbCandle
.
playbulbCandle.js
const CANDLE_EFFECT_UUID = 0xFFFB;
...
setCandleEffectColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
setFlashingColor(r, g, b) {
let data = new Uint8Array([0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00]);
return this.device.gatt.getPrimaryService(CANDLE_SERVICE_UUID)
.then(service => service.getCharacteristic(CANDLE_EFFECT_UUID))
.then(characteristic => characteristic.writeValue(data))
.then(() => [r,g,b]);
}
Vamos atualizar a função changeColor
no app.js
para chamar playbulbCandle.setCandleEffectColor
quando o "Efeito vela" botão de opção estiver marcado e playbulbCandle.setFlashingColor
quando a mensagem está marcado. Desta vez, switch
se você estiver de acordo com você.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
switch(effect) {
case 'noEffect':
playbulbCandle.setColor(r, g, b).then(onColorChanged);
break;
case 'candleEffect':
playbulbCandle.setCandleEffectColor(r, g, b).then(onColorChanged);
break;
case 'flashing':
playbulbCandle.setFlashingColor(r, g, b).then(onColorChanged);
break;
}
}
Neste ponto, acesse o site no navegador da Web (clicando no URL do servidor da Web destacado no aplicativo do servidor da Web) ou simplesmente atualize a página. Clique no botão "Conectar". na página e teste os efeitos de vela e flash.
A seguir
- Só isso? Três efeitos ruins de vela? É por isso que estou aqui?
- Há mais, mas desta vez você vai ficar por conta própria.
7. Vá além
Aqui estamos nós. Talvez você ache que está quase no fim, mas o app ainda não acabou. Vamos ver se você realmente entendeu o que copiou e colou durante este codelab. Confira o que você quer fazer para destacar este app.
Adicionar efeitos ausentes
Estes são os dados dos efeitos ausentes:
- Pulso:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00]
(convém ajustar os valores der, g, b
aqui) - Arco-íris:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00]
(epiléticos podem evitar esse problema) - Esmaecimento do arco-íris:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
Isso significa adicionar novos métodos setPulseColor
, setRainbow
e setRainbowFade
à classe PlaybulbCandle
e chamá-los em changeColor
.
Corrigir a mensagem "Sem efeito"
Como você deve ter notado, a expressão "sem efeito" não redefine nenhum efeito em andamento. Isso é pequeno, mas ainda assim. Vamos corrigir isso. No método setColor
, é necessário verificar primeiro se um efeito está em andamento usando uma nova variável de classe _isEffectSet
e, se for true
, desativar o efeito antes de definir uma nova cor com estes dados: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00]
.
Gravar o nome do dispositivo
Essa é fácil! Escrever um nome personalizado de dispositivo é tão simples quanto escrever na característica anterior do nome do dispositivo Bluetooth. Recomendamos usar o método TextEncoder
encode
para receber um Uint8Array
contendo o nome do dispositivo.
Depois, adicionaria uma "entrada" eventListener
para document.querySelector('#deviceName')
e chame playbulbCandle.setDeviceName
para simplificar.
Eu pessoalmente nomeei o meu como PLAY💡 CANDLE!
8. Pronto!
O que você aprendeu
- Como interagir no JavaScript com um dispositivo Bluetooth por perto
- Como usar as classes ES2015, as funções de seta, o Map e as promessas.
Próximas etapas
- Saiba mais sobre a API Web Bluetooth.
- Acesse as amostras do Web Bluetooth e as Demonstrações (links em inglês) oficiais
- Confira o gato mal-humorado voando