1. O co chodzi?

W tym ćwiczeniu dowiesz się, jak sterować bezpłomieniową świecą LED PLAYBULB za pomocą samego kodu JavaScript dzięki interfejsowi Web Bluetooth API. Przy okazji poznasz też funkcje JavaScriptu ES2015, takie jak klasy, funkcje strzałkowe, mapy i obiekty Promise.
Czego się nauczysz
- Jak wchodzić w interakcje z urządzeniem Bluetooth w pobliżu za pomocą JavaScriptu
- Jak używać klas ES2015, funkcji strzałkowych, map i obiektów Promise
Czego potrzebujesz
- podstawowa wiedza na temat tworzenia stron internetowych,
- Podstawowa wiedza na temat Bluetooth Low Energy (BLE) i profilu atrybutów ogólnych (GATT).
- wybrany edytor tekstu,
- Mac, Chromebook lub urządzenie z Androidem M z aplikacją przeglądarki Chrome i kablem micro USB na USB.
2. Odtwórz najpierw
Zanim zaczniesz korzystać z tego ćwiczenia, możesz sprawdzić ostateczną wersję aplikacji, którą zamierzasz utworzyć, na stronie https://googlecodelabs.github.io/candle-bluetooth, i wypróbować urządzenie Bluetooth PLAYBULB Candle, które masz do dyspozycji.
Możesz też zobaczyć, jak zmieniam kolory, na stronie https://www.youtube.com/watch?v=fBCPA9gIxlU.
3. Konfiguracja
Pobieranie przykładowego kodu
Przykładowy kod możesz uzyskać, pobierając plik ZIP:
lub przez sklonowanie tego repozytorium git:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
Jeśli źródło zostało pobrane jako plik ZIP, po rozpakowaniu powinien pojawić się folder główny candle-bluetooth-master.
Instalowanie i weryfikowanie serwera WWW
Możesz użyć własnego serwera WWW, ale ten ćwiczenie zostało zaprojektowane tak, aby działało dobrze z serwerem WWW Chrome. Jeśli nie masz jeszcze tej aplikacji, możesz ją zainstalować z Chrome Web Store.
Po zainstalowaniu aplikacji Web Server for Chrome kliknij skrót Aplikacje Chrome na pasku zakładek:

W wyświetlonym oknie kliknij ikonę serwera WWW:

Następnie zobaczysz to okno, które umożliwia skonfigurowanie lokalnego serwera WWW:

Kliknij przycisk Wybierz folder i wybierz katalog główny sklonowanego (lub rozpakowanego) repozytorium. Umożliwi to wyświetlanie Twojej pracy w toku za pomocą adresu URL wyróżnionego w oknie serwera WWW (w sekcji Adresy URL serwera WWW).
W sekcji Opcje zaznacz pole „Automatycznie wyświetlaj plik index.html”, jak pokazano poniżej:

Teraz otwórz swoją witrynę w przeglądarce (klikając podświetlony adres URL serwera WWW). Powinna wyświetlić się strona wyglądająca tak:

Jeśli chcesz zobaczyć, jak ta aplikacja wygląda na telefonie z Androidem, musisz włączyć zdalne debugowanie na Androidzie i skonfigurować przekierowanie portów (domyślny numer portu to 8887). Następnie możesz po prostu otworzyć nową kartę Chrome na stronie http://localhost:8887 na telefonie z Androidem.
Dalsze czynności
Na razie ta aplikacja internetowa nie robi zbyt wiele. Zacznijmy dodawać obsługę Bluetootha.
4. Odkryj świeczkę
Zaczniemy od napisania biblioteki, która używa klasy JavaScript ES2015 dla urządzenia Bluetooth PLAYBULB Candle.
Zachowaj spokój. Składnia klasy nie wprowadza do JavaScriptu nowego modelu dziedziczenia obiektowego. Zapewnia ona po prostu znacznie bardziej przejrzystą składnię do tworzenia obiektów i obsługi dziedziczenia, jak opisano poniżej.
Najpierw zdefiniujmy klasę PlaybulbCandle w pliku playbulbCandle.js i utwórzmy instancję playbulbCandle, która będzie później dostępna w pliku app.js.
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
Aby poprosić o dostęp do urządzenia Bluetooth w pobliżu, musimy wywołać navigator.bluetooth.requestDevice. Ponieważ urządzenie PLAYBULB Candle stale rozgłasza (jeśli nie jest jeszcze sparowane) stały identyfikator UUID usługi Bluetooth GATT, znany w krótkiej formie jako 0xFF02, możemy po prostu zdefiniować stałą i dodać ją do parametru usług filtrów w nowej publicznej metodzie connect klasy PlaybulbCandle.
Będziemy też wewnętrznie śledzić obiekt BluetoothDevice, aby w razie potrzeby mieć do niego dostęp później. Ponieważ navigator.bluetooth.requestDevice zwraca obietnicę JavaScript ES2015, zrobimy to w metodzie 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();
})();
Jako funkcja zabezpieczeń, wykrywanie urządzeń Bluetooth w pobliżu za pomocą navigator.bluetooth.requestDevice musi być wywoływane przez gest użytkownika, taki jak dotknięcie lub kliknięcie myszą. Dlatego wywołamy metodę connect, gdy użytkownik kliknie przycisk „Połącz” w pliku 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);
});
});
Uruchamianie aplikacji
Teraz otwórz swoją witrynę w przeglądarce (klikając adres URL serwera WWW wyróżniony w aplikacji serwera WWW) lub po prostu odśwież istniejącą stronę. Kliknij zielony przycisk „Połącz”, wybierz urządzenie w selektorze i otwórz ulubioną konsolę Narzędzi dla deweloperów za pomocą skrótu klawiszowego Ctrl + Shift + J. Zwróć uwagę na zarejestrowany obiekt BluetoothDevice.

Jeśli Bluetooth lub urządzenie Bluetooth PLAYBULB Candle jest wyłączone, może pojawić się błąd. W takim przypadku włącz je i spróbuj ponownie.
Mandatory Bonus
Nie wiem, jak Ty, ale ja widzę już zbyt wiele function() {} w tym kodzie. Zamiast tego przejdźmy na funkcje strzałkowe JavaScript ES2015 () => {}. Są one prawdziwym wybawieniem: mają wszystkie zalety funkcji anonimowych, ale nie mają wad związanych z wiązaniem.
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);
});
});
Dalsze czynności
– OK… Czy mogę porozmawiać z tą świecą?
- Jasne… przejdź do następnego kroku
Najczęstsze pytania
5. Czytanie
Co teraz zrobić, gdy obietnica navigator.bluetooth.requestDevice zwróciła BluetoothDevice? Połączmy się ze zdalnym serwerem GATT Bluetooth, który zawiera definicje usług i charakterystyk Bluetooth, wywołując 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();
});
}
}
Odczytywanie nazwy urządzenia
Tutaj jesteśmy połączeni z serwerem GATT urządzenia Bluetooth PLAYBULB Candle. Teraz chcemy uzyskać główną usługę GATT (wcześniej reklamowaną jako 0xFF02) i odczytać charakterystykę nazwy urządzenia (0xFFFF), która należy do tej usługi. Można to łatwo osiągnąć, dodając nową metodę getDeviceName do klasy PlaybulbCandle i używając device.gatt.getPrimaryService i service.getCharacteristic. Metoda characteristic.readValue zwróci obiekt DataView, który po prostu zdekodujemy za pomocą 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);
});
}
Dodajmy to do app.js, dzwoniąc pod numer playbulbCandle.getDeviceName po połączeniu i wyświetlając nazwę urządzenia.
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;
}
Teraz otwórz swoją witrynę w przeglądarce (klikając adres URL serwera WWW wyróżniony w aplikacji serwera WWW) lub po prostu odśwież istniejącą stronę. Upewnij się, że świeca PLAYBULB jest włączona, a następnie kliknij przycisk „Połącz” na stronie. Pod selektorem kolorów powinna pojawić się nazwa urządzenia.

Odczytywanie poziomu baterii
W urządzeniu Bluetooth PLAYBULB Candle dostępna jest też standardowa charakterystyka Bluetooth poziomu baterii, która zawiera poziom baterii urządzenia. Oznacza to, że możemy używać standardowych nazw, takich jak battery_service dla identyfikatora UUID usługi Bluetooth GATT i battery_level dla identyfikatora UUID charakterystyki Bluetooth GATT.
Dodajmy do klasy PlaybulbCandle nową metodę getBatteryLevel i odczytajmy poziom baterii w procentach.
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));
}
Musimy też zaktualizować obiekt w JavaScripcie options, aby uwzględnić usługę baterii w kluczu optionalServices, ponieważ nie jest ona rozgłaszana przez urządzenie Bluetooth PLAYBULB Candle, ale jest wymagana, aby uzyskać do niego dostęp.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
Tak jak wcześniej, podłączmy to do app.js, wywołując playbulbCandle.getBatteryLevel, gdy będziemy mieć nazwę urządzenia, i wyświetlmy poziom baterii.
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 + '%';
}
W tym momencie otwórz swoją witrynę w przeglądarce (klikając adres URL serwera WWW wyróżniony w aplikacji serwera WWW) lub po prostu odśwież istniejącą stronę. Kliknij przycisk „Połącz” na stronie. Powinna pojawić się nazwa urządzenia i poziom baterii.
Dalsze czynności
– Jak mogę zmienić kolor tej żarówki? Dlatego tu jestem.
– Jesteś już blisko, obiecuję…
Najczęstsze pytania
6. Zmienianie koloru
Zmiana koloru jest tak prosta jak zapisanie określonego zestawu poleceń w charakterystyce Bluetooth (0xFFFC) w podstawowej usłudze GATT reklamowanej jako 0xFF02. Na przykład, aby zmienić kolor świecy PLAYBULB na czerwony, należy zapisać tablicę 8-bitowych liczb całkowitych bez znaku równą [0x00, 255, 0, 0], gdzie 0x00 to nasycenie bieli, a 255, 0, 0 to odpowiednio wartości czerwieni, zieleni i błękitu .
Użyjemy characteristic.writeValue, aby zapisać dane w charakterystyce Bluetooth w nowej metodzie publicznej setColor klasy PlaybulbCandle. Zwrócimy też rzeczywiste wartości czerwonego, zielonego i niebieskiego, gdy obietnica zostanie spełniona, abyśmy mogli ich później użyć w 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]);
}
Zaktualizujmy funkcję changeColor w pliku app.js, aby wywoływała funkcję playbulbCandle.setColor, gdy zaznaczony jest przycisk „No Effect” (Bez efektu). Globalne zmienne kolorów r, g, b są już ustawione, gdy użytkownik klika obszar roboczy selektora kolorów.
app.js
function changeColor() {
var effect = document.querySelector('[name="effectSwitch"]:checked').id;
if (effect === 'noEffect') {
playbulbCandle.setColor(r, g, b).then(onColorChanged);
}
}
Teraz otwórz swoją witrynę w przeglądarce (klikając adres URL serwera WWW wyróżniony w aplikacji serwera WWW) lub po prostu odśwież istniejącą stronę. Kliknij przycisk „Połącz” na stronie i kliknij selektor kolorów, aby zmienić kolor świecy PLAYBULB Candle dowolną liczbę razy.
Więcej efektów świecy
Jeśli zdarzyło Ci się już zapalić świecę, wiesz, że światło nie jest statyczne. Na szczęście istnieje inna charakterystyka Bluetooth (0xFFFB) w głównej usłudze GATT, która jest rozgłaszana jako 0xFF02 i pozwala użytkownikowi ustawić pewne efekty świecy.
Na przykład, aby ustawić „efekt świecy”, możesz napisać [0x00, r, g, b, 0x04, 0x00, 0x01, 0x00]. Możesz też ustawić „efekt migania” za pomocą [0x00, r, g, b, 0x00, 0x00, 0x1F, 0x00].
Dodajmy metody setCandleEffectColor i setFlashingColor do klasy 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]);
}
Zaktualizujmy funkcję changeColor w pliku app.js, aby wywoływała funkcję playbulbCandle.setCandleEffectColor, gdy zaznaczony jest przycisk „Candle Effect” (Efekt świecy), i funkcję playbulbCandle.setFlashingColor, gdy zaznaczony jest przycisk „Flashing” (Migotanie). Jeśli nie masz nic przeciwko, tym razem użyjemy adresu 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;
}
}
Teraz otwórz swoją witrynę w przeglądarce (klikając adres URL serwera WWW wyróżniony w aplikacji serwera WWW) lub po prostu odśwież istniejącą stronę. Kliknij przycisk „Połącz” na stronie i wypróbuj efekty świecy i migotania.
Dalsze czynności
– To wszystko? 3 słabe efekty świecy? Is this why I'm here?
- Jest ich więcej, ale tym razem musisz poradzić sobie samodzielnie.
7. Dokładaj wszelkich starań
I oto jesteśmy! Może się wydawać, że to już koniec, ale aplikacja nie jest jeszcze gotowa. Sprawdźmy, czy rozumiesz, co zostało skopiowane i wklejone podczas tego ćwiczenia. Oto co możesz zrobić samodzielnie, aby ta aplikacja działała jeszcze lepiej.
Dodaj brakujące efekty
Oto dane dotyczące brakujących efektów:
- Pulse:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00](możesz dostosować wartościr, g, b) - Tęcza:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00](osoby z epilepsją powinny unikać tego efektu) - Zanikanie tęczy:
[0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x26, 0x00]
Oznacza to dodanie nowych metod setPulseColor, setRainbow i setRainbowFade do klasy PlaybulbCandle i wywołanie ich w changeColor.
Naprawianie błędu „brak efektu”
Jak być może zauważyliście, opcja „brak efektu” nie resetuje żadnego efektu w toku. To drobiazg, ale jednak. Naprawmy to. W metodzie setColor musisz najpierw sprawdzić, czy efekt jest w toku, za pomocą nowej zmiennej klasy _isEffectSet, a jeśli true, wyłączyć efekt przed ustawieniem nowego koloru za pomocą tych danych: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00].
Wpisz nazwę urządzenia
To proste. Zapisanie niestandardowej nazwy urządzenia jest tak proste jak zapisanie poprzedniej charakterystyki nazwy urządzenia Bluetooth. Zalecamy użycie metody TextEncoder encode, aby uzyskać Uint8Array zawierający nazwę urządzenia.
Następnie dodam „input” eventListener do document.querySelector('#deviceName') i wywołam playbulbCandle.setDeviceName, aby zachować prostotę.
Ja nazwałam swoją PLAY💡 CANDLE!
8. To wszystko.
Czego się nauczysz
- Jak wchodzić w interakcje z urządzeniem Bluetooth w pobliżu za pomocą JavaScriptu
- Jak używać klas ES2015, funkcji strzałkowych, map i obiektów Promise
Następne kroki
- Dowiedz się więcej o Web Bluetooth API.
- Przejrzyj oficjalne przykłady i wersje demonstracyjne Web Bluetooth.
- Zobacz latającego naburmuszonego kota

