1. O czym to jest?

Z tego ćwiczenia dowiesz się, jak sterować świecą LED PLAYBULB za pomocą samego JavaScriptu dzięki interfejsowi Web Bluetooth API. Po drodze poznasz też funkcje JavaScript ES2015, takie jak klasy, funkcje strzałkowe, Map i Promises.
Czego się nauczysz
- Jak wchodzić w interakcje z urządzeniem Bluetooth w pobliżu za pomocą JavaScriptu
- Jak korzystać z klas ES2015, funkcji strzałkowych, map i obietnic
Czego potrzebujesz
- Podstawowa wiedza o tworzeniu stron internetowych
- Podstawowa wiedza na temat Bluetooth Low Energy (BLE) i Generic Attribute Profile (GATT).
- wybrany edytor tekstu,
- Mac, Chromebook lub urządzenie z Androidem M z aplikacją przeglądarki Chrome i kablem USB micro na USB.
2. Odtwórz pierwszy
Zanim zaczniesz wykonywać to ćwiczenie, możesz sprawdzić ostateczną wersję aplikacji, którą zamierzasz utworzyć, na stronie https://googlecodelabs.github.io/candle-bluetooth i pobawić się urządzeniem PLAYBULB Candle Bluetooth, które masz do dyspozycji.
Możesz też zobaczyć, jak zmieniam kolory, na stronie https://www.youtube.com/watch?v=fBCPA9gIxlU
3. Konfiguracja
Pobierz przykładowy kod
Przykładowy kod możesz pobrać w tym pliku ZIP:
lub sklonuj to repozytorium Git:
git clone https://github.com/googlecodelabs/candle-bluetooth.git
Jeśli źródło zostało pobrane jako plik ZIP, po jego rozpakowaniu powinien pojawić się folder główny candle-bluetooth-master.
Instalowanie i weryfikowanie serwera WWW
Możesz używać własnego serwera WWW, ale ten przewodnik został zaprojektowany tak, aby dobrze współpracował 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 na pasku zakładek:

W wyświetlonym oknie kliknij ikonę serwera internetowego:

Następnie zobaczysz to okno dialogowe, w którym możesz skonfigurować lokalny serwer WWW:

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

Teraz otwórz witrynę w przeglądarce (klikając wyróżniony adres URL serwera internetowego). Powinna wyświetlić się strona podobna do tej:

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 pod adresem http://localhost:8887 na telefonie z Androidem.
Dalsze czynności
Obecnie ta aplikacja internetowa nie ma wielu funkcji. 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 po prostu znacznie bardziej przejrzystą składnię do tworzenia obiektów i obsługi dziedziczenia, o czym możesz przeczytać poniżej.
Najpierw zdefiniujmy klasę PlaybulbCandle w 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 pobliskiego urządzenia Bluetooth, musimy wywołać funkcję navigator.bluetooth.requestDevice. Urządzenie PLAYBULB Candle reklamuje się nieustannie (jeśli nie jest jeszcze sparowane), używając stałego identyfikatora UUID usługi Bluetooth GATT, znanego w skrócie jako 0xFF02. Możemy więc 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. Ponieważ funkcja 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();
})();
Ze względów bezpieczeństwa wykrywanie urządzeń Bluetooth w pobliżu za pomocą navigator.bluetooth.requestDevice musi być wywoływane przez gest użytkownika, np. 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
W tym momencie otwórz 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 deweloperskich za pomocą skrótu klawiszowego Ctrl + Shift + J. Zwróć uwagę na zalogowany obiekt BluetoothDevice.

Jeśli Bluetooth jest wyłączony 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.
Bonus obowiązkowy
Nie wiem, jak Ty, ale ja widzę w tym kodzie za dużo znaków function() {}. Zamiast tego użyjmy () => {} funkcji strzałkowych JavaScript ES2015. Są one niezwykle przydatne: 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. Przeczytaj coś
Co więc zrobić, gdy otrzymasz BluetoothDevice w ramach obietnicy navigator.bluetooth.requestDevice? Połączmy się ze zdalnym serwerem GATT Bluetooth, który zawiera definicje usługi i charakterystyki Bluetooth, wywołując funkcję 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();
});
}
}
Odczytaj nazwę urządzenia
W tym momencie jesteśmy połączeni z serwerem GATT urządzenia Bluetooth PLAYBULB Candle. Teraz chcemy uzyskać dostęp do głównej usługi GATT (wcześniej reklamowanej jako 0xFF02) i odczytać charakterystykę nazwy urządzenia (0xFFFF) należącą do tej usługi. Możesz 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 w rzeczywistości wartość DataView, którą po prostu zdekodujemy za pomocą funkcji 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, wywołując 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;
}
W tym momencie otwórz 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 lampka PLAYBULB Candle 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
Urządzenie Bluetooth PLAYBULB Candle ma też standardową charakterystykę Bluetooth poziomu baterii, która zawiera poziom baterii urządzenia. Oznacza to, że możemy używać standardowych nazw, takich jak battery_service w przypadku identyfikatora UUID usługi Bluetooth GATT i battery_level w przypadku 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 JavaScript options, aby dodać usługę baterii do klucza optionalServices, ponieważ nie jest ona reklamowana przez urządzenie Bluetooth PLAYBULB Candle, ale jest niezbędna do uzyskania do niej dostępu.
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
Jak wcześniej, podłączmy to do app.js, wywołując playbulbCandle.getBatteryLevel po uzyskaniu nazwy 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 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. Powinny się wyświetlić nazwa urządzenia i poziom baterii.
Dalsze czynności
– Jak mogę zmienić kolor tej żarówki? Dlatego tu jestem!
– Obiecuję, że jesteś już blisko…
Najczęstsze pytania
6. Zmień kolor
Zmiana koloru jest tak prosta, jak wpisanie określonego zestawu poleceń do charakterystyki Bluetooth (0xFFFC) w podstawowej usłudze GATT reklamowanej jako 0xFF02. Na przykład zmiana koloru świecy PLAYBULB na czerwony wymagałaby wpisania tablicy 8-bitowych liczb całkowitych bez znaku równej [0x00, 255, 0, 0], gdzie 0x00 to nasycenie bieli, a 255, 0, 0 to odpowiednio wartości czerwieni, zieleni i niebieskiego .
Użyjemy characteristic.writeValue, aby zapisać dane w charakterystyce Bluetooth w nowej setColor publicznej metodzie klasy PlaybulbCandle. Gdy obietnica zostanie spełniona, zwrócimy też rzeczywiste wartości czerwonego, zielonego i niebieskiego, 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” (Brak efektu). Globalne zmienne koloru r, g, b są już ustawione, gdy użytkownik kliknie obszar 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);
}
}
W tym momencie otwórz 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 lampy PLAYBULB Candle tyle razy, ile chcesz.
Moar candle effects
Jeśli kiedykolwiek zapaliłeś(-aś) świecę, wiesz, że jej światło nie jest statyczne. Na szczęście istnieje inna charakterystyka Bluetooth (0xFFFB) w podstawowej usłudze GATT, która jest reklamowana jako 0xFF02 i umożliwia użytkownikowi ustawienie niektórych efektów świecy.
Aby ustawić „efekt świecy” dla instancji, wpisz [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 też funkcję changeColor w app.js, aby wywoływała funkcję playbulbCandle.setCandleEffectColor, gdy jest zaznaczony przycisk „Efekt świecy”, i funkcję playbulbCandle.setFlashingColor, gdy jest zaznaczony przycisk „Miganie”. Tym razem użyjemy atrybutu switch, jeśli nie masz nic przeciwko.
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;
}
}
W tym momencie otwórz 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 migania.
Dalsze czynności
– To wszystko? 3 słabe efekty świecy? Is this why I'm here?
– jest ich więcej, ale tym razem musisz je znaleźć samodzielnie.
7. Dołóż dodatkowych starań
I oto jesteśmy. Może Ci się wydawać, że to już koniec, ale aplikacja jeszcze się nie skończyła. Sprawdźmy, czy rozumiesz, co zostało skopiowane i wklejone w tym laboratorium. Oto co możesz teraz zrobić samodzielnie, aby ta aplikacja działała jeszcze lepiej.
Dodawanie brakujących efektów
Oto dane dotyczące brakujących efektów:
- Puls:
[0x00, r, g, b, 0x01, 0x00, 0x09, 0x00](możesz dostosować tam wartościr, g, b) - Tęcza:
[0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00](osoby z epilepsją powinny unikać tego efektu) - Tęcza:
[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.
Rozwiązywanie problemu „brak efektu”
Jak być może zauważysz, opcja „bez efektu” nie resetuje żadnego trwającego efektu. To drobna, ale jednak istotna kwestia. Naprawmy to. W 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! Wpisanie niestandardowej nazwy urządzenia jest tak proste, jak wpisanie poprzedniej nazwy urządzenia Bluetooth. Zalecam użycie metody TextEncoder encode, aby uzyskać Uint8Array zawierający nazwę urządzenia.
Następnie dodam „input” eventListener do document.querySelector('#deviceName') i nazwę playbulbCandle.setDeviceName, aby uprościć proces.
Moja ma nazwę PLAY💡 CANDLE!
8. To wszystko.
Czego się nauczysz
- Jak wchodzić w interakcje z urządzeniem Bluetooth w pobliżu za pomocą JavaScriptu
- Jak korzystać z klas ES2015, funkcji strzałkowych, map i obietnic
Następne kroki
- Dowiedz się więcej o Web Bluetooth API
- Przejrzyj oficjalne przykłady Web Bluetooth i wersje demonstracyjne.
- Zobacz latającego zrzędliwego kota

