1. О чём идёт речь?

В этом познавательном практическом занятии вы научитесь управлять светодиодной беспламенной свечой PLAYBULB, используя только JavaScript и Web Bluetooth API . Попутно вы также поэкспериментируете с возможностями JavaScript ES2015, такими как классы , стрелочные функции , Map и промисы .
Что вы узнаете
- Как взаимодействовать с находящимся рядом устройством Bluetooth в JavaScript
- Как использовать классы ES2015, стрелочные функции, Map и промисы.
Что вам понадобится
- Базовое понимание веб-разработки
- Базовые знания Bluetooth Low Energy (BLE) и Generic Attribute Profile (GATT)
- Текстовый редактор на ваш выбор
- Для работы вам потребуется компьютер Mac, Chromebook или устройство Android M с установленным браузером Chrome и кабелем micro-USB.
2. Сыграйте первым.
Возможно, вам захочется ознакомиться с финальной версией приложения, которое вы собираетесь создать, по адресу https://googlecodelabs.github.io/candle-bluetooth и поэкспериментировать с имеющимся у вас устройством PLAYBULB Candle Bluetooth, прежде чем приступать к выполнению этого практического задания.
Вы также можете посмотреть, как я меняю цвета, по ссылке: https://www.youtube.com/watch?v=fBCPA9gIxlU
3. Подготовка к работе
Скачать пример кода
Пример кода можно получить, скачав архив здесь:
или клонировав этот репозиторий Git:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
Если вы скачали исходный код в виде ZIP-архива, после его распаковки вы должны получить корневую папку candle-bluetooth-master .
Установите и проверьте веб-сервер.
Хотя вы можете использовать свой собственный веб-сервер, этот практический урок разработан для эффективной работы с веб-сервером Chrome. Если у вас еще не установлено это приложение, вы можете установить его из Chrome Web Store.
После установки приложения «Веб-сервер для Chrome» нажмите на ярлык «Приложения» на панели закладок:

В открывшемся окне щелкните значок «Веб-сервер»:

Далее вы увидите диалоговое окно, позволяющее настроить локальный веб-сервер:

Нажмите кнопку «Выбрать папку» и выберите корневую папку клонированного (или расархивированного) репозитория. Это позволит вам предоставлять доступ к вашей текущей работе через URL-адрес, выделенный в диалоговом окне веб-сервера (в разделе «URL-адреса веб-сервера» ).
В разделе «Параметры» установите флажок рядом с пунктом « Автоматически отображать index.html », как показано ниже:

Теперь откройте свой сайт в веб-браузере (щелкнув по выделенному URL-адресу веб-сервера), и вы должны увидеть страницу, которая выглядит примерно так:

Чтобы посмотреть, как это приложение выглядит на вашем Android-смартфоне, вам нужно включить удаленную отладку на Android и настроить переадресацию портов (номер порта по умолчанию — 8887). После этого вы можете просто открыть новую вкладку Chrome по адресу http://localhost:8887 на своем Android-смартфоне.
Далее
На данном этапе это веб-приложение мало что умеет. Давайте начнём добавлять поддержку Bluetooth!
4. Найдите свечу.
Начнём с написания библиотеки, использующей класс JavaScript ES2015 для Bluetooth-устройства PLAYBULB Candle.
Сохраняйте спокойствие. Синтаксис классов не вводит в JavaScript новую объектно-ориентированную модель наследования. Он просто предоставляет гораздо более понятный синтаксис для создания объектов и работы с наследованием, как вы можете прочитать ниже.
Для начала определим класс PlaybulbCandle в playbulbCandle.js и создадим экземпляр playbulbCandle , который будет доступен позже в файле app.js
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Для запроса доступа к ближайшему устройству Bluetooth нам необходимо вызвать метод navigator.bluetooth.requestDevice . Поскольку устройство PLAYBULB Candle постоянно передает (если оно еще не сопряжено) постоянный UUID службы Bluetooth GATT, сокращенно обозначаемый как 0xFF02 , мы можем просто определить константу и добавить ее в параметр filters services в новом публичном методе connect класса PlaybulbCandle .
Мы также будем отслеживать объект BluetoothDevice внутри системы, чтобы при необходимости получить к нему доступ позже. Поскольку метод navigator.bluetooth.requestDevice возвращает промис JavaScript ES2015 , мы сделаем это в методе 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();
})();
В целях безопасности обнаружение ближайших устройств Bluetooth с помощью navigator.bluetooth.requestDevice должно осуществляться посредством жеста пользователя, например, касания экрана или щелчка мыши. Поэтому мы будем вызывать метод connect , когда пользователь нажимает кнопку "Подключиться" в файле 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);
});
});
Запустите приложение
На этом этапе откройте свой сайт в веб-браузере (щелкнув по URL-адресу веб-сервера, выделенному в приложении веб-сервера) или просто обновите существующую страницу. Нажмите зеленую кнопку «Подключиться», выберите устройство в списке выбора и откройте свою любимую консоль инструментов разработчика с помощью сочетания клавиш Ctrl + Shift + J, после чего обратите внимание на зарегистрированный объект BluetoothDevice .

Ошибка может возникнуть, если Bluetooth выключен и/или устройство PLAYBULB Candle с Bluetooth выключено. В этом случае включите его и продолжите.
Обязательный бонус
Не знаю, как вам, но я уже вижу слишком много function() {} в этом коде. Давайте вместо этого перейдём к стрелочным функциям JavaScript ES2015 () => {} . Они просто спасают жизнь: всё очарование анонимных функций, никаких недостатков привязки.
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);
});
});
Далее
— Хорошо... я могу поговорить с этой свечой, или как?
— Конечно... переходите к следующему шагу
Часто задаваемые вопросы
5. Прочитайте что-нибудь.
Итак, что же делать теперь, когда промис из navigator.bluetooth.requestDevice возвращает объект BluetoothDevice ? Давайте подключимся к удалённому GATT-серверу Bluetooth, который содержит определения службы и характеристик Bluetooth, вызвав 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();
});
}
}
Прочитайте название устройства.
Здесь мы подключены к GATT-серверу Bluetooth-устройства PLAYBULB Candle. Теперь нам нужно получить основной GATT-сервис (ранее объявленный как 0xFF02 ) и прочитать характеристику имени устройства ( 0xFFFF ), которая относится к этому сервису. Этого легко добиться, добавив новый метод getDeviceName в класс PlaybulbCandle и используя device.gatt.getPrimaryService и service.getCharacteristic . Метод characteristic.readValue фактически вернет DataView мы просто декодируем с помощью 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);
});
}
Добавим это в app.js , вызвав метод playbulbCandle.getDeviceName после подключения и отобразив имя устройства.
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;
}
На этом этапе откройте свой сайт в веб-браузере (щелкнув по URL-адресу веб-сервера, выделенному в приложении веб-сервера) или просто обновите существующую страницу. Убедитесь, что PLAYBULB Candle включен, затем нажмите кнопку «Подключить» на странице, и вы увидите название устройства под палитрой цветов.

Проверьте уровень заряда батареи.
В устройстве PLAYBULB Candle Bluetooth также доступна стандартная характеристика уровня заряда батареи , содержащая информацию об уровне заряда устройства. Это означает, что мы можем использовать стандартные имена, такие как battery_service для UUID службы Bluetooth GATT и battery_level для UUID характеристики Bluetooth GATT.
Добавим новый метод getBatteryLevel в класс PlaybulbCandle и будем считывать уровень заряда батареи в процентах.
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));
}
Нам также необходимо обновить объект JavaScript options , добавив службу управления батареей в ключ optionalServices , поскольку она не анонсируется устройством PLAYBULB Candle Bluetooth, но тем не менее является обязательной для доступа к ней.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
Как и прежде, давайте добавим это в app.js , вызвав playbulbCandle.getBatteryLevel , как только получим имя устройства, и отобразим уровень заряда батареи.
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 + '%';
}
На этом этапе откройте свой сайт в веб-браузере (щелкнув по URL-адресу веб-сервера, выделенному в приложении веб-сервера) или просто обновите существующую страницу. Нажмите кнопку «Подключиться» на странице, и вы увидите название устройства и уровень заряда батареи.
Далее
— Как я могу изменить цвет этой лампочки? Именно поэтому я здесь!
— Ты уже совсем близко, обещаю...
Часто задаваемые вопросы
6. Измените цвет.
Изменить цвет так же просто, как записать определенный набор команд в характеристику Bluetooth ( 0xFFFC ) в основной службе GATT, объявленной как 0xFF02 . Например, чтобы перевести вашу свечу PLAYBULB в красный цвет, нужно записать массив 8-битных беззнаковых целых чисел, равных [0x00, 255, 0, 0] , где 0x00 — насыщенность белого цвета, а 255, 0, 0 — соответственно значения красного, зеленого и синего цветов.
Мы будем использовать characteristic.writeValue для фактической записи данных в характеристику Bluetooth в новом открытом методе setColor класса PlaybulbCandle . А также мы вернем фактические значения красного, зеленого и синего цветов, когда промис будет выполнен, чтобы мы могли использовать их позже в app.js :
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]);
}
Давайте обновим функцию changeColor в app.js , чтобы она вызывала playbulbCandle.setColor , когда выбрана радиокнопка "Без эффекта". Глобальные цветовые переменные r, g, b уже установлены, когда пользователь щелкает по холсту выбора цвета.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
if (effect === 'noEffect') {
playbulbCandle.setColor(r, g, b).then(onColorChanged);
}
}
На этом этапе откройте свой сайт в веб-браузере (щелкнув по URL-адресу веб-сервера, выделенному в приложении веб-сервера) или просто обновите существующую страницу. Нажмите кнопку «Подключиться» на странице и воспользуйтесь палитрой цветов, чтобы изменить цвет вашей свечи PLAYBULB столько раз, сколько захотите.
Больше эффектов свечей
Если вы уже зажигали свечу, то знаете, что свет не статичен. К счастью, в основной службе GATT ( 0xFFFB ), обозначенной как 0xFF02 есть еще одна характеристика Bluetooth, позволяющая пользователю устанавливать некоторые эффекты свечи.
Например, установить «эффект свечи» можно, написав [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00] . А «эффект мерцания» можно установить с помощью [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00] .
Добавим методы setCandleEffectColor и setFlashingColor в класс 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]);
}
Давайте обновим функцию changeColor в app.js , чтобы она вызывала playbulbCandle.setCandleEffectColor , когда выбран переключатель "Эффект свечи", и playbulbCandle.setFlashingColor , когда выбран переключатель "Мигающий". На этот раз мы будем использовать switch если вас это устраивает.
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;
}
}
На этом этапе откройте свой сайт в веб-браузере (щелкнув по URL-адресу веб-сервера, выделенному в приложении веб-сервера) или просто обновите существующую страницу. Нажмите кнопку «Подключиться» на странице и поэкспериментируйте с эффектами свечи и мерцания.
Далее
— И это всё? Три неудачных свечных эффекта? Поэтому я здесь?
— Есть и другие варианты, но на этот раз вы будете предоставлены сами себе.
7. Приложите дополнительные усилия
Итак, мы здесь! Вам может показаться, что это почти конец, но приложение ещё не готово. Давайте посмотрим, действительно ли вы поняли то, что скопировали и вставили во время этого практического занятия. Вот что вам нужно сделать самостоятельно, чтобы это приложение засияло.
Добавить недостающие эффекты
Вот данные по отсутствующим эффектам:
- Импульс:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00](возможно, вам потребуется изменить значенияr, g, bв этих местах) - Радуга:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00](Людям, страдающим эпилепсией, следует избегать этого варианта) - Радужное затухание:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
По сути, это означает добавление новых методов setPulseColor , setRainbow и setRainbowFade в класс PlaybulbCandle и вызов их в changeColor .
Исправить ошибку "нет эффекта"
Как вы, возможно, заметили, опция «без эффекта» не сбрасывает ни один из текущих эффектов, это мелочь, но всё же. Давайте это исправим. В методе setColor вам сначала нужно проверить, выполняется ли эффект, с помощью новой переменной класса _isEffectSet , и если true , отключить эффект перед установкой нового цвета с этими данными: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00] .
Запишите название устройства
Это очень просто! Написать собственное имя устройства так же легко, как и записать его в характеристику имени устройства Bluetooth. Я бы рекомендовал использовать метод encode TextEncoder , чтобы получить массив Uint8Array содержащий имя устройства.
Затем я бы добавил eventListener "input" к document.querySelector('#deviceName') и вызвал playbulbCandle.setDeviceName , чтобы упростить задачу.
Я лично назвала свою свечу PLAY💡 CANDLE!
8. Вот и всё!
Что вы узнали
- Как взаимодействовать с находящимся рядом устройством Bluetooth в JavaScript
- Как использовать классы ES2015, стрелочные функции, Map и промисы.
Следующие шаги
- Узнайте больше о веб-API Bluetooth.
- Просмотрите официальные веб-примеры и демо-версии Bluetooth .
- Посмотрите на летающего сердитого кота

