1. それについて
この Codelab では、Web Bluetooth API を活用して、JavaScript だけで PLAYBULB LED フレームレス キャンドルを操作する方法を学びます。また、クラス、アロー関数、マップ、Promise などの JavaScript ES2015 機能も実際に試してみます。
学習内容
- JavaScript で近くの Bluetooth デバイスを操作する方法
- ES2015 のクラス、アロー関数、マップ、Promise の使用方法
必要なもの
- ウェブ開発の基本知識
- Bluetooth Low Energy(BLE)と Generic Attribute Profile(GATT)に関する基本的な知識
- 任意のテキスト エディタ
- Mac、Chromebook、または Android M デバイス。Chrome ブラウザアプリと USB マイクロ - USB ケーブル。
2. 最初にプレイ
この Codelab を実際に使ってみる前に、これから作成するアプリの最終バージョンを https://googlecodelabs.github.io/candle-bluetooth で確認できます。手持ちの PLAYBULB Candle Bluetooth デバイスを使って操作することもできます。
また、https://www.youtube.com/watch?v=fBCPA9gIxlU で色が変わる様子もご覧いただけます。
3. セットアップする
サンプルコードをダウンロードする
このコードのサンプルコードは、次の場所で zip をダウンロードして取得できます。
または、次の git リポジトリのクローンを作成します。
git clone https://github.com/googlecodelabs/candle-bluetooth.git
ソースを zip としてダウンロードした場合、解凍するとルートフォルダ candle-bluetooth-master
が作成されます。
ウェブサーバーをインストールして確認する
独自のウェブサーバーは自由に使用できますが、この Codelab は Chrome ウェブサーバーと適切に連携するように設計されています。アプリをまだインストールしていない場合は、Chrome ウェブストアからインストールできます。
Chrome 用ウェブサーバー アプリをインストールしたら、ブックマーク バーのアプリのショートカットをクリックします。
表示されるウィンドウで、ウェブサーバー アイコンをクリックします。
次にこのダイアログが表示され、ローカル ウェブサーバーを構成できます。
[フォルダを選択] ボタンをクリックし、クローン(またはアーカイブ解除された)リポジトリのルートを選択します。これにより、ウェブサーバー ダイアログ([ウェブサーバーの URL] セクション)でハイライト表示されている URL から、進行中の作業を提供できるようになります。
[オプション] で、[index.html を自動的に表示] のチェックボックスをオンにします(以下を参照)。
ウェブブラウザで、ハイライト表示されているウェブサーバーの URL をクリックしてサイトにアクセスすると、次のようなページが表示されます。
Android スマートフォンでこのアプリがどのように表示されるのかを確認するには、[Android でのリモート デバッグ] を有効にして、ポート転送を設定する必要があります(デフォルトのポート番号は 8887 です)。その後、Android スマートフォンで新しい Chrome タブを開いて http://localhost:8887 にアクセスするだけです。
次のステップ
この時点では、このウェブアプリはあまり機能しません。Bluetooth サポートを追加しましょう。
4. キャンドルについて
まず、PLAYBULB Candle Bluetooth デバイス用の JavaScript ES2015 クラスを使用するライブラリを作成します。
冷静になってください。クラス構文によって、新しいオブジェクト指向の継承モデルが JavaScript に導入されることはありません。以下で説明するように、オブジェクトを作成して継承を処理するための構文がはるかに明確になります。
まず、playbulbCandle.js
で PlaybulbCandle
クラスを定義し、後で app.js
ファイルで使用できるように playbulbCandle
インスタンスを作成します。
playbulbCandle.js
(function() {
'use strict';
class PlaybulbCandle {
constructor() {
this.device = null;
}
}
window.playbulbCandle = new PlaybulbCandle();
})();
付近の Bluetooth デバイスへのアクセスをリクエストするには、navigator.bluetooth.requestDevice
を呼び出す必要があります。PLAYBULB Candle デバイスは、(まだペア設定されていない場合)常に一定の Bluetooth GATT サービス UUID(0xFF02
という短い形式)をアドバタイズするため、定数を定義して、PlaybulbCandle
クラスの新しいパブリック connect
メソッドのフィルタ サービス パラメータに追加できます。
また、必要に応じて後でアクセスできるように、BluetoothDevice
オブジェクトも内部で追跡します。navigator.bluetooth.requestDevice
は JavaScript ES2015 Promise を返すため、これは 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();
})();
セキュリティ機能として、navigator.bluetooth.requestDevice
を使用した付近の Bluetooth デバイスの検出は、タップやマウスクリックなどのユーザー操作で呼び出す必要があります。そのため、ユーザーが [Connect] をクリックしたときに 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 のキーボード ショートカットを使用してお気に入りの Dev Tools コンソールを開くと、BluetoothDevice
オブジェクトがログに記録されることを確認します。
Bluetooth がオフの場合や、PLAYBULB Candle Bluetooth デバイスがオフの場合、エラーが発生することがあります。その場合は、有効にしてからもう一度手順を進めてください。
必須のボーナス
私はあなたが知らないのですが、このコードにはすでに多くの function() {}
が表示されています。代わりに、() => {}
JavaScript ES2015 Arrow 関数に切り替えます。これらは絶対的な命の救世者です。匿名関数の愛情はそのままに、バインディングの悲しみはまったくありません。
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);
});
});
次のステップ
- OK... このキャンドルに実際に話しかけていい?
- はい、次のステップに進みます
よくある質問
5. 何かを読む
navigator.bluetooth.requestDevice
の Promise から BluetoothDevice
が返されたら、どうすればよいでしょうか。device.gatt.connect()
を呼び出して、Bluetooth サービスと特性定義を保持する Bluetooth リモート GATT サーバーに接続します。
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();
});
}
}
デバイス名の読み上げ
ここでは、PLAYBULB Candle Bluetooth デバイスの GATT サーバーに接続します。次に、プライマリ GATT サービス(以前は 0xFF02
としてアドバタイズされていたサービス)を取得し、このサービスに属するデバイス名の特性(0xFFFF
)を読み取ります。これは、PlaybulbCandle
クラスに新しいメソッド getDeviceName
を追加し、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);
});
}
接続し、デバイス名を表示したら、playbulbCandle.getDeviceName
を呼び出して app.js
に追加しましょう。
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 がオンになっていることを確認し、[Connect] をクリックします] ボタンを押すと、カラー選択ツールの下にデバイス名が表示されます。
バッテリー残量を確認する
PLAYBULB Candle Bluetooth デバイスでは、デバイスのバッテリー残量を示す標準の Bluetooth 特性も利用できます。つまり、Bluetooth GATT サービス UUID には battery_service
、Bluetooth GATT 特性 UUID には battery_level
などの標準名を使用できます。
新しい 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));
}
また、電池サービスを optionalServices
キーに含めるように options
JavaScript オブジェクトを更新する必要があります。これは、PLAYBULB Candle Bluetooth デバイスではアドバタイズされませんが、このオブジェクトへのアクセスには必要です。
playbulbCandle.js
let options = {filters:[{services:[ CANDLE_SERVICE_UUID ]}],
optionalServices: ['battery_service']};
return navigator.bluetooth.requestDevice(options)
前回と同様に、デバイス名を取得し、バッテリー残量を表示したら、playbulbCandle.getBatteryLevel
を呼び出して app.js
に接続しましょう。
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. 色を変更する
色の変更は、0xFF02
としてアドバタイズされるプライマリ GATT サービスの Bluetooth 特性(0xFFFC
)に特定のコマンドセットを書き込むだけで簡単に行えます。たとえば、PLAYBULB Candle を赤に変えるには、[0x00, 255, 0, 0]
に等しい 8 ビットの符号なし整数の配列を書き込むことができます。ここで、0x00
は白の彩度、255, 0, 0
はそれぞれ赤、緑、青の値を表します。
characteristic.writeValue
を使用して、PlaybulbCandle
クラスの新しい setColor
パブリック メソッドで Bluetooth 特性に実際にデータを書き込みます。また、Promise が履行されたときに実際の赤、緑、青の値を返して、後で 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]);
}
app.js
の changeColor
関数を更新して、「No Effect」が発生したときに 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 Candle の色を好きなだけ変更できます。
モア キャンドル効果
すでにキャンドルに点火したことがある場合は、そのライトが静止していないことがわかります。幸いなことに、プライマリ GATT サービスには 0xFF02
としてアドバタイズされる別の Bluetooth 特性(0xFFFB
)があり、ユーザーはキャンドル効果を設定できます。
「キャンドル効果」を設定するたとえば、[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]);
}
さらに、app.js
の changeColor
関数を更新して、「キャンドル効果」の発生時に 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 をクリックして、ウェブブラウザでサイトにアクセスするか、既存のページを更新します。[接続] をクリックします。キャンドル効果と点滅効果を試します。
次のステップ
- これで終わりですか?キャンドル効果の低さを 3 つ挙げてください。だからいるの?
- まだまだありますが、今回は一人でいただきます。
7. さらなる高みへ
さっそくアプリはまだ終わりではありません。この Codelab でコピーして貼り付けた内容を理解できたかどうか確認してみましょう。このアプリを目立たせるために、今一人でやりたいことを紹介します。
不足しているエフェクトを追加する
表示されない効果のデータは次のとおりです。
- Pulse:
[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 デバイス名の特性に書き込むのと同じくらい簡単です。TextEncoder
encode
メソッドを使用して、デバイス名を含む Uint8Array
を取得することをおすすめします。
次に「input」をシンプルにするために、eventListener
を document.querySelector('#deviceName')
に設定し、playbulbCandle.setDeviceName
を呼び出します。
個人的には PLAY💡? CANDLE と名付けました!
8. これで完了です。
学習した内容
- JavaScript で近くの Bluetooth デバイスを操作する方法
- ES2015 のクラス、アロー関数、マップ、Promise の使用方法
次のステップ
- Web Bluetooth API の詳細を確認する
- 公式のウェブ Bluetooth サンプルとデモを参照する
- 空飛ぶ不機嫌な猫