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 camino, también experimentarás con funciones de JavaScript ES2015, como Clases, Funciones de flecha, Map y Promesas.
Qué aprenderás
- Cómo interactuar con un dispositivo Bluetooth cercano en JavaScript
- Cómo usar clases ES2015, funciones de flecha, mapas y promesas
Requisitos
- Conocimientos básicos sobre desarrollo web
- Conocimientos básicos de Bluetooth de bajo consumo (BLE) y perfil de atributo genérico (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 USB micro a USB
2. Reproducir primero
Antes de comenzar este codelab, te recomendamos que consultes la versión final de la app que crearás en https://googlecodelabs.github.io/candle-bluetooth y que pruebes el dispositivo PLAYBULB Candle Bluetooth que tienes a tu disposición.
También puedes verme cambiar los colores 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 código 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, al descomprimirlo, obtendrás 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 de Apps en la barra de favoritos:

En la ventana siguiente, haz clic en el ícono del servidor web:

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 repo clonado (o descomprimido). 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 Options, 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 como 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 el reenvío de puertos (el número de puerto predeterminado es 8887). Después, 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, la 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 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 tratar 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 continuamente (si aún no está vinculado) un UUID de servicio GATT de Bluetooth constante conocido en su forma abreviada como 0xFF02, podemos simplemente definir una constante y agregarla al parámetro de servicios de filtros en un nuevo método connect público 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 medida de seguridad, el descubrimiento de dispositivos Bluetooth cercanos con navigator.bluetooth.requestDevice debe llamarse 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 "Conectar" 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 apagados. En ese caso, enciéndelos y vuelve a intentarlo.
Bonificación obligatoria
No sé tú, pero yo ya veo demasiados function() {} en este código. Cambiemos a las funciones de flecha () => {} de JavaScript ES2015. Son una maravilla: 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
- Bien… ¿puedo hablar con esta vela o qué?
- Claro… avanza al siguiente paso
Preguntas frecuentes
5. Leer algo
Entonces, ¿qué haces ahora que tienes un BluetoothDevice que se devolvió 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 servicio y características 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();
});
}
}
Leer el nombre del dispositivo
Aquí nos conectamos al servidor GATT del dispositivo Bluetooth PLAYBULB Candle. Ahora queremos obtener el servicio 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 nuevo método getDeviceName a la clase PlaybulbCandle y usando device.gatt.getPrimaryService y service.getCharacteristic. El método characteristic.readValue en realidad 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 PLAYBULB Candle esté encendida y, luego, haz clic en el botón "Conectar" de la página. 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 estándar del nivel de batería 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 GATT de Bluetooth y battery_level para el UUID de la característica 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 options de JavaScript 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 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 y 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í.
- Ya casi llegas, te lo prometo…
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 GATT principal anunciado como 0xFF02. Por ejemplo, si quieres que tu PLAYBULB Candle se ponga de color rojo, deberás escribir un array de números enteros sin signo 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 "Sin efecto". Las variables de color r, g, b globales 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 en el selector de color para cambiar el color de la PLAYBULB Candle tantas veces como quieras.
Más efectos de velas
Si ya encendiste una vela, sabes que la luz no es estática. Por suerte, hay otra característica de Bluetooth (0xFFFB) en el servicio GATT principal que se anuncia como 0xFF02 y que permite al usuario establecer algunos efectos de la vela.
Para establecer un "efecto de vela", por ejemplo, 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]);
}
Actualicemos la función changeColor en app.js para llamar a playbulbCandle.setCandleEffectColor cuando se marque el botón de selección "Efecto de vela" y a playbulbCandle.setFlashingColor cuando se marque el botón de selección "Parpadeo". Esta vez, usaremos switch si está bien para ti.
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 “Conectar” de la página y prueba los efectos de parpadeo y de vela.
Cuál es el próximo paso
- ¿Eso es todo? ¿3 efectos de velas deficientes? ¿Por eso estoy aquí?
- Hay más, pero esta vez deberás encontrarlos por tu cuenta.
7. Motívate aún más
¡Y aquí estamos! Quizás creas que ya casi terminas, pero la app aún no está lista. Veamos si realmente comprendiste lo que copiaste y pegaste durante este codelab. Esto es lo que debes hacer por tu cuenta para que esta app se destaque.
Agregar efectos faltantes
Estos son los datos de los efectos faltantes:
- Pulso:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00](es posible que desees ajustar los valores der, g, ballí) - Arcoíris:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00](las personas con epilepsia tal vez quieran evitar este) - Rainbow Fade:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
Esto significa, básicamente, agregar nuevos métodos setPulseColor, setRainbow y setRainbowFade a la clase PlaybulbCandle y llamarlos en changeColor.
Corrección de "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 que vale la pena mencionar. 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 anterior del nombre del dispositivo Bluetooth. Te recomiendo que uses el método TextEncoder encode para obtener un Uint8Array que contenga el nombre del dispositivo.
Luego, agregaría una eventListener "input" a document.querySelector('#deviceName') y llamaría a playbulbCandle.setDeviceName para que sea simple.
Personalmente, la llamé PLAY💡 CANDLE.
8. Eso es todo.
Qué aprendiste
- Cómo interactuar con un dispositivo Bluetooth cercano en JavaScript
- Cómo usar clases ES2015, funciones de flecha, mapas y promesas
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

