1. ¿De qué se trata?

En este codelab, aprenderás a controlar una vela LED sin llama PLAYBULB solo con JavaScript gracias a la API de Web Bluetooth. En el proceso, también experimentarás con funciones de JavaScript ES2015, como clases, funciones de flecha, mapas y promesas.
Qué aprenderás
- Cómo interactuar con un dispositivo Bluetooth cercano en JavaScript
- Cómo usar clases, funciones de flecha, mapas y promesas de ES2015
Requisitos
- Conocimientos básicos de desarrollo web
- Conocimientos básicos de Bluetooth de bajo consumo (BLE) y perfil de atributos genéricos (GATT)
- Un editor de texto de tu elección
- Una Mac, una Chromebook o un dispositivo Android M con la app del navegador Chrome y un cable micro USB a USB
2. Reproducir primero
Antes de comenzar con este codelab, te recomendamos que consultes la versión final de la app que estás por crear en https://googlecodelabs.github.io/candle-bluetooth y que pruebes el dispositivo Bluetooth PLAYBULB Candle que tienes a tu disposición.
También puedes verme cambiar de color en https://www.youtube.com/watch?v=fBCPA9gIxlU
3. Prepárate
Descarga el código de muestra
Puedes obtener el código de muestra para este codelab descargando el archivo ZIP aquí:
o clonando este repositorio de Git:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
Si descargaste el código fuente como un archivo ZIP, cuando lo descomprimas, deberías obtener una carpeta raíz candle-bluetooth-master.
Instala y verifica el servidor web
Si bien puedes usar tu propio servidor web, este codelab está diseñado para funcionar bien con el servidor web de Chrome. Si aún no tienes esa app instalada, puedes instalarla desde Chrome Web Store.
Después de instalar la app de Web Server for Chrome, haz clic en el acceso directo Apps de la barra de favoritos:

En la ventana que aparece, haz clic en el ícono de Web Server:

Luego, verás este diálogo, que te permitirá configurar tu servidor web local:

Haz clic en el botón choose folder y selecciona la raíz del repositorio clonado (o desarchivado). Esto te permitirá entregar el trabajo en curso a través de la URL destacada en el diálogo de servidor web, en la sección Web Server URL(s).
En Opciones, marca la casilla junto a "Automatically show index.html", como se muestra a continuación:

Ahora, visita tu sitio en el navegador web (haz clic en la URL del servidor web destacada) y deberías ver una página similar a la siguiente:

Si quieres ver cómo se ve esta app en tu teléfono Android, deberás habilitar la depuración remota en Android y configurar la redirección de puertos (el número de puerto predeterminado es 8887). Después de eso, puedes abrir una nueva pestaña de Chrome en http://localhost:8887 en tu teléfono Android.
Cuál es el próximo paso
En este punto, esta app web no hace mucho. Comencemos a agregar compatibilidad con Bluetooth.
4. Descubre la vela
Comenzaremos escribiendo una biblioteca que use una clase de JavaScript ES2015 para el dispositivo Bluetooth PLAYBULB Candle.
Mantén la calma. La sintaxis de la clase no introduce un nuevo modelo de herencia orientado a objetos en JavaScript. Simplemente proporciona una sintaxis mucho más clara para crear objetos y trabajar con la herencia, como puedes leer a continuación.
Primero, definamos una clase PlaybulbCandle en playbulbCandle.js y creemos una instancia de playbulbCandle que estará disponible en el archivo app.js más adelante.
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Para solicitar acceso a un dispositivo Bluetooth cercano, debemos llamar a navigator.bluetooth.requestDevice. Dado que el dispositivo PLAYBULB Candle anuncia de forma continua (si aún no está vinculado) un UUID de servicio de GATT de Bluetooth constante conocido en su forma corta como 0xFF02, podemos simplemente definir una constante y agregarla al parámetro de servicios de filtros en un nuevo método público connect de la clase PlaybulbCandle.
También haremos un seguimiento interno del objeto BluetoothDevice para poder acceder a él más adelante si es necesario. Como navigator.bluetooth.requestDevice devuelve una promesa de JavaScript ES2015, lo haremos en el 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 función de seguridad, el descubrimiento de dispositivos Bluetooth cercanos con navigator.bluetooth.requestDevice se debe llamar a través de un gesto del usuario, como un toque o un clic del mouse. Por eso, llamaremos al método connect cuando el usuario haga clic en el botón "Connect" en el archivo 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);
});
});
Ejecuta la app
En este punto, visita tu sitio en el navegador web (haz clic en la URL del servidor web destacada en la app del servidor web) o simplemente actualiza la página existente. Haz clic en el botón verde "Connect", elige el dispositivo en el selector y abre tu consola de Herramientas para desarrolladores favorita con la combinación de teclas Ctrl + Mayúsculas + J. Observa el objeto BluetoothDevice registrado.

Es posible que recibas un error si el Bluetooth o el dispositivo Bluetooth PLAYBULB Candle están desactivados. En ese caso, actívalos y vuelve a intentarlo.
Bonificación obligatoria
No sé tú, pero yo ya veo demasiados function() {} en este código. En su lugar, cambiemos a las () => {}funciones de flecha de JavaScript ES2015. Son salvavidas absolutos: Toda la belleza de las funciones anónimas, nada de la tristeza de la vinculación.
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);
});
});
Cuál es el próximo paso
- OK... ¿puedo hablar con esta vela o qué?
- Claro, pasa al siguiente paso.
Preguntas frecuentes
5. Leer algo
Entonces, ¿qué haces ahora que tienes un BluetoothDevice que se muestra de la promesa de navigator.bluetooth.requestDevice? Llamemos a device.gatt.connect() para conectarnos al servidor GATT remoto de Bluetooth que contiene las definiciones de características y servicios de Bluetooth:
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();
});
}
}
Cómo leer el nombre del dispositivo
Aquí, nos conectamos al servidor GATT del dispositivo Bluetooth PLAYBULB Candle. Ahora, queremos obtener el servicio de GATT principal (anunciado como 0xFF02 anteriormente) y leer la característica del nombre del dispositivo (0xFFFF) que pertenece a este servicio. Esto se puede lograr fácilmente agregando un método getDeviceName nuevo a la clase PlaybulbCandle y usando device.gatt.getPrimaryService y service.getCharacteristic. El método characteristic.readValue devolverá un DataView que simplemente decodificaremos con 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);
});
}
Agreguemos esto a app.js llamando a playbulbCandle.getDeviceName una vez que nos conectemos y mostremos el nombre del 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;
}
En este punto, visita tu sitio en el navegador web (haz clic en la URL del servidor web destacada en la app del servidor web) o simplemente actualiza la página existente. Asegúrate de que la vela PLAYBULB esté encendida, haz clic en el botón "Conectar" de la página y deberías ver el nombre del dispositivo debajo del selector de color.

Cómo leer el nivel de batería
También hay una característica de Bluetooth de nivel de batería estándar disponible en el dispositivo Bluetooth PLAYBULB Candle que contiene el nivel de batería del dispositivo. Esto significa que podemos usar nombres estándar, como battery_service para el UUID del servicio de GATT de Bluetooth y battery_level para el UUID de la característica de GATT de Bluetooth.
Agreguemos un nuevo método getBatteryLevel a la clase PlaybulbCandle y leamos el nivel de batería en porcentaje.
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));
}
También debemos actualizar el objeto de JavaScript options para incluir el servicio de batería en la clave optionalServices, ya que el dispositivo Bluetooth PLAYBULB Candle no lo anuncia, pero sigue siendo obligatorio para acceder a él.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
Como antes, conectemos esto a app.js llamando a playbulbCandle.getBatteryLevel una vez que tengamos el nombre del dispositivo y mostremos el nivel de batería.
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 + '%';
}
En este punto, visita tu sitio en el navegador web (haz clic en la URL del servidor web destacada en la app del servidor web) o simplemente actualiza la página existente. Haz clic en el botón "Conectar" de la página. Deberías ver el nombre del dispositivo y el nivel de batería.
Cuál es el próximo paso
- ¿Cómo puedo cambiar el color de esta bombilla? ¡Por eso estoy aquí!
- Te prometo que estás muy cerca…
Preguntas frecuentes
6. Cambia el color
Cambiar el color es tan fácil como escribir un conjunto específico de comandos en una característica de Bluetooth (0xFFFC) en el servicio de GATT principal anunciado como 0xFF02. Por ejemplo, para cambiar el color de tu PLAYBULB Candle a rojo, se escribiría un array de números enteros sin firma de 8 bits igual a [0x00, 255, 0, 0], donde 0x00 es la saturación de blanco y 255, 0, 0 son, respectivamente, los valores de rojo, verde y azul .
Usaremos characteristic.writeValue para escribir algunos datos en la característica de Bluetooth en el nuevo método público setColor de la clase PlaybulbCandle. También devolveremos los valores reales de rojo, verde y azul cuando se cumpla la promesa para que podamos usarlos en app.js más adelante:
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]);
}
Actualicemos la función changeColor en app.js para llamar a playbulbCandle.setColor cuando se marque el botón de selección "No Effect". Las variables de color globales r, g, b ya están configuradas cuando el usuario hace clic en el lienzo del selector de color.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
if (effect === 'noEffect') {
playbulbCandle.setColor(r, g, b).then(onColorChanged);
}
}
En este punto, visita tu sitio en el navegador web (haz clic en la URL del servidor web destacada en la app del servidor web) o simplemente actualiza la página existente. Haz clic en el botón "Conectar" de la página y, luego, en el selector de color para cambiar el color de tu PLAYBULB Candle tantas veces como quieras.
Más efectos de velas
Si ya encendiste una vela antes, sabes que la luz no es estática. Por suerte, hay otra característica de Bluetooth (0xFFFB) en el servicio de GATT principal anunciado como 0xFF02 que permite al usuario establecer algunos efectos de vela.
Por ejemplo, para establecer un "efecto de vela", puedes escribir [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]. También puedes configurar el "efecto de parpadeo" con [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00].
Agreguemos los métodos setCandleEffectColor y setFlashingColor a la clase 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]);
}
Y actualicemos la función changeColor en app.js para llamar a playbulbCandle.setCandleEffectColor cuando se marque el botón de selección "Candle Effect" y a playbulbCandle.setFlashingColor cuando se marque el botón de selección "Flashing". Esta vez, usaremos switch si te parece bien.
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;
}
}
En este punto, visita tu sitio en el navegador web (haz clic en la URL del servidor web destacada en la app del servidor web) o simplemente actualiza la página existente. Haz clic en el botón "Connect" de la página y juega con los efectos de vela y parpadeo.
Cuál es el próximo paso
- ¿Eso es todo? ¿3 efectos de velas de baja calidad? ¿Por eso estoy aquí?
- Hay más, pero esta vez tendrás que encontrarlas por tu cuenta.
7. Motívate aún más
¡Aquí estamos! Podrías pensar que casi llegamos al final, pero la app aún no está terminada. Veamos si realmente entendiste lo que copiaste y pegaste durante este codelab. Esto es lo que debes hacer por tu cuenta para que esta app se destaque.
Agrega los efectos que faltan
Estos son los datos de los efectos faltantes:
- Pulso:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00](es posible que quieras ajustar los valores der, g, ballí) - Arcoíris:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00](las personas con epilepsia tal vez prefieran evitar este efecto) - Atenuación de arcoíris:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
Básicamente, esto significa agregar nuevos métodos setPulseColor, setRainbow y setRainbowFade a la clase PlaybulbCandle y llamarlos en changeColor.
Cómo corregir el error "sin efecto"
Como habrás notado, la opción "sin efecto" no restablece ningún efecto en curso, lo que es un detalle menor, pero de todos modos es importante. Tiene una solución. En el método setColor, primero deberás verificar si hay un efecto en curso a través de una nueva variable de clase _isEffectSet y, si es true, desactivar el efecto antes de establecer un color nuevo con estos datos: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00].
Escribe el nombre del dispositivo
Esta es fácil. Escribir un nombre de dispositivo personalizado es tan simple como escribir en la característica de nombre de dispositivo Bluetooth anterior. Te recomiendo que uses el método TextEncoder encode para obtener un Uint8Array que contenga el nombre del dispositivo.
Luego, agregaría un "input" eventListener a document.querySelector('#deviceName') y llamaría a playbulbCandle.setDeviceName para que sea simple.
Yo llamé a la mía PLAY💡 CANDLE.
8. Eso es todo.
Qué aprendiste
- Cómo interactuar con un dispositivo Bluetooth cercano en JavaScript
- Cómo usar clases, funciones de flecha, mapas y promesas de ES2015
Próximos pasos
- Obtén más información sobre la API de Web Bluetooth
- Explora los ejemplos y las demostraciones oficiales de Web Bluetooth.
- Mira el gato gruñón volador

