ウェブ Bluetooth で PLAYBULB キャンドルを操作する

1. それについて

IMG_19700101_023537~2~2.jpg

この Codelab では、Web Bluetooth API を活用して、JavaScript だけで PLAYBULB LED フレームレス キャンドルを操作する方法を学びます。また、クラスアロー関数マップPromise などの JavaScript ES2015 機能も実際に試してみます。

学習内容

  • JavaScript で近くの Bluetooth デバイスを操作する方法
  • ES2015 のクラス、アロー関数、マップ、Promise の使用方法

必要なもの

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 用ウェブサーバー アプリをインストールしたら、ブックマーク バーのアプリのショートカットをクリックします。

Screen Shot 2016-11-16 at 4.10.42 PM.png

表示されるウィンドウで、ウェブサーバー アイコンをクリックします。

9f3c21b2cf6cbfb5.png

次にこのダイアログが表示され、ローカル ウェブサーバーを構成できます。

Screen Shot 2016-11-16 at 3.40.47 PM.png

[フォルダを選択] ボタンをクリックし、クローン(またはアーカイブ解除された)リポジトリのルートを選択します。これにより、ウェブサーバー ダイアログ([ウェブサーバーの URL] セクション)でハイライト表示されている URL から、進行中の作業を提供できるようになります。

[オプション] で、[index.html を自動的に表示] のチェックボックスをオンにします(以下を参照)。

Screen Shot 2016-11-16 at 3.40.56 PM.png

ウェブブラウザで、ハイライト表示されているウェブサーバーの URL をクリックしてサイトにアクセスすると、次のようなページが表示されます。

Screen Shot 2016-11-16 at 3.20.22 PM.png

Android スマートフォンでこのアプリがどのように表示されるのかを確認するには、[Android でのリモート デバッグ] を有効にして、ポート転送を設定する必要があります(デフォルトのポート番号は 8887 です)。その後、Android スマートフォンで新しい Chrome タブを開いて http://localhost:8887 にアクセスするだけです。

次のステップ

この時点では、このウェブアプリはあまり機能しません。Bluetooth サポートを追加しましょう。

4. キャンドルについて

まず、PLAYBULB Candle Bluetooth デバイス用の JavaScript ES2015 クラスを使用するライブラリを作成します。

冷静になってください。クラス構文によって、新しいオブジェクト指向の継承モデルが JavaScript に導入されることはありません。以下で説明するように、オブジェクトを作成して継承を処理するための構文がはるかに明確になります。

まず、playbulbCandle.jsPlaybulbCandle クラスを定義し、後で 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.requestDeviceJavaScript 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 オブジェクトがログに記録されることを確認します。

Screen Shot 2016-11-16 at 3.27.12 PM.png

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.getPrimaryServiceservice.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] をクリックします] ボタンを押すと、カラー選択ツールの下にデバイス名が表示されます。

Screen Shot 2016-11-16 at 3.29.21 PM.png

バッテリー残量を確認する

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 をクリックして、ウェブブラウザでサイトにアクセスするか、単に既存のページを更新します。[接続] をクリックします。ボタンをタップすると、デバイス名とバッテリー残量の両方が表示されます。

Screen Shot 2016-11-16 at 3.29.21 PM.png

次のステップ

- この電球の色を変更するにはどうすればよいですか?だからこそ、ここにいるのです!

- あともう少しで完了です...

よくある質問

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.jschangeColor 関数を更新して、「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 の色を好きなだけ変更できます。

Screen Shot 2016-11-16 at 3.31.37 PM.png

モア キャンドル効果

すでにキャンドルに点火したことがある場合は、そのライトが静止していないことがわかります。幸いなことに、プライマリ 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.jschangeColor 関数を更新して、「キャンドル効果」の発生時に 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 をクリックして、ウェブブラウザでサイトにアクセスするか、既存のページを更新します。[接続] をクリックします。キャンドル効果と点滅効果を試します。

Screen Shot 2016-11-16 at 3.33.23 PM.png

次のステップ

- これで終わりですか?キャンドル効果の低さを 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]

基本的には、新しい setPulseColorsetRainbowsetRainbowFade のメソッドを PlaybulbCandle クラスに追加し、changeColor で呼び出すことを意味します。

「エフェクトなし」を修正する

お気づきかと思いますが、「効果なし」の進行中の効果はリセットされません。これは軽微なものです。この問題を修正しましょう。setColor メソッドでは、まず、新しいクラス変数 _isEffectSet を介して効果が進行中かどうかをチェックする必要があります。true の場合は、これらのデータを使用して新しい色を設定する前に効果をオフにします: [0x00, r, g, b, 0x05, 0x00, 0x01, 0x00]

デバイス名の書き込み

これなら簡単!カスタムのデバイス名の書き込みは、以前の Bluetooth デバイス名の特性に書き込むのと同じくらい簡単です。TextEncoder encode メソッドを使用して、デバイス名を含む Uint8Array を取得することをおすすめします。

次に「input」をシンプルにするために、eventListenerdocument.querySelector('#deviceName') に設定し、playbulbCandle.setDeviceName を呼び出します。

個人的には PLAY💡? CANDLE と名付けました!

8. これで完了です。

学習した内容

  • JavaScript で近くの Bluetooth デバイスを操作する方法
  • ES2015 のクラス、アロー関数、マップ、Promise の使用方法

次のステップ