Web Bluetoothモジュール for Angular
Web Bluetoothモジュール for Angular
ここ数ヶ月、私は新しいWeb Bluetooth APIを試してきました。これは2017年2月にChrome 56で提供される予定です。この新機能は、Webの新たな可能性を切り開いてくれました。
Web Advocateとして、私はこの機能にとても興奮し、AngularとWeb Bluetooth APIを組み合わせる方法を示すアプリケーションを作りたくてたまりませんでした(さらに、今後のWeb APIでも同様です。詳細は後日お楽しみに)。
AngularアプリケーションのためのWeb Bluetoothモジュール
それから、François Beaufort(彼に感謝!)と一緒に、Web BluetoothとAngularの統合を示すデモアプリケーションを作成しました。これは、Web BluetoothをAngularと統合する方法を示す概念実証の一部です。
いくつかのユースケースを実装した後、私はWeb Bluetooth APIを設定するためのボイラープレートを抽象化したAngularモジュールを作成しました。
注意事項
Web Bluetooth API
ここでは、Web Bluetooth API(GATTサーバー、サービス、特性など)についてすでに理解していると仮定します。次のセクションを読む前に、以下のリソースでこのトピックに慣れてください:
- https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web
- https://medium.com/@urish/start-building-with-web-bluetooth-and-progressive-web-apps-6534835959a6
Observables
また、Observables(Observable、Observer、Subject)について基本的な知識があると仮定しています。以下のリソースで理解を深めてください:
フィンランド記法
いくつかのメソッドの末尾に$記号が付いていることに気付くでしょう。これはObservablesの世界で使用されている慣習です。今後、このブログポストにより、$記号を削除する可能性もあります。
モジュールのインストール
このモジュールは、Yarn または NPM を使用して取得できます:
$ yarn add @manekinekko/angular-web-bluetooth$ npm i -S @manekinekko/angular-web-bluetooth
WebBluetoothModuleの使用
このモジュールは簡単に使用できます。まず、WebBluetoothModule モジュールを @manekinekko/angular-web-bluetooth からインポートします:
import { NgModule } from '@angular/core';
import {
WebBluetoothModule
} from '@manekinekko/angular-web-bluetooth';
@NgModule({
imports: [
//...,
WebBluetoothModule.forRoot()
],
//...
})
export class AppModule { }
WebBluetoothModule.forRoot() メソッドを呼び出すことで、BluetoothCore サービスが提供され、これを自分のサービスやコンポーネントで使用する必要があります。例えば、battery-level.component.ts での使用方法は以下の通りです:
import { Component, OnInit, NgZone } from '@angular/core';
import { BatteryLevelService } from '../battery-level.service';
import { BluetoothCore } from '@manekinekko/angular-web-bluetooth';
@Component({
selector: 'ble-battery-level',
//...
providers: [ BatteryLevelService, BluetoothCore ]
})
export class BatteryLevelComponent implements OnInit { //...
WebBluetoothModule.forRoot() は、内部で navigator.bluetooth を使用する BrowserWebBluetooth 実装も提供します。Angular Universal 用の ServerWebBluetooth 実装は後で提供される予定です。もちろん、Angular の DI を使用して、カスタム実装を提供することも可能です。
BatteryLevelService (battery-level.service.ts) サービスは、デバイスやセンサーのロジックを実装する場所です。以下の例では、接続されたデバイスのバッテリーレベルを読み取るバッテリーレベルサービスを実装しています:
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
// import BluetoothCore and types
import {
BluetoothCore,
BluetoothRemoteGATTServer,
BluetoothRemoteGATTService,
BluetoothRemoteGATTCharacteristic,
DataView
} from '@manekinekko/angular-web-bluetooth';
@Injectable()
export class BatteryLevelService {
// define your services and characteristics
static GATT_CHARACTERISTIC_BATTERY_LEVEL = 'battery_level';
static GATT_PRIMARY_SERVICE = 'battery_service';
constructor(
// inject the BluetoothCore
public ble: BluetoothCore
) {}
getDevice() {
// you can get ask for the device observable in order to be notified when the device has (dis)connected
return this.ble.getDevice$();
}
streamValues() {
// for realtime values, you can call this method and subscribe in order to receive the stream of realtime values
return this.ble.streamValues$()
.map( (value: DataView) => value.getUint8(0));
}
getBatteryLevel(): Observable<number> {
return this.ble
// 1) call this method to run the discovery process
.discover$({
optionalServices:[
BatteryLevelService.GATT_PRIMARY_SERVICE
]
})
// 2) you'll get the GATT server
.mergeMap( (gatt: BluetoothRemoteGATTServer) => {
// 3) get the primary service of that GATT server
return this.ble.getPrimaryService$(
gatt,
BatteryLevelService.GATT_PRIMARY_SERVICE
);
})
.mergeMap( (primaryService: BluetoothRemoteGATTService) => {
// 4) get a specific characteristic
return this.ble.getCharacteristic$(
primaryService,
BatteryLevelService
.GATT_CHARACTERISTIC_BATTERY_LEVEL
);
})
.mergeMap(
(characteristic: BluetoothRemoteGATTCharacteristic) => {
// 5) read the provided value (as DataView)
return this.ble.readValue$(characteristic);
}
)
// 6) get the right value from the DataView
.map( (value: DataView) => value.getUint8(0) );
}
}
説明します
では、getBatteryLevel() メソッド内で何が起こっているのか説明しましょう…
基本的に、デバイスから値を読み取るには、次のような流れを経る必要があります(一般的な使用ケースの場合):
-
discover$() メソッドを呼び出して、発見プロセスを実行します。
-
これにより、GATT サーバーが返されます。
-
次に、その GATT サーバーの主要なサービスを取得します。
-
次に、特定の特徴を取得します。
-
最後に、その特徴から抽出した値を読み取ります(DataView として)。
-
最後のステップでは、値が DataView タイプとして取得されます。デバイスやセンサーに特有の適切な値を読み取る必要があります。例えば、単純な値の場合(例えば バッテリーレベル)、value.getUint8(0) を呼び出すだけで十分です:
.map( (value: DataView) => value.getUint8(0) );
ただし、場合によっては、状況がもっと複雑になることがあります。一部のメーカーは、Bluetooth GATT 特徴を標準に従わずに独自に実装することがあります。例えば、Luxometer(一般的には光センサーと呼ばれる、LUX で測定するもの)の場合がこれに該当します。以下は Texas Instrument SensorTag CC2650 センサーに関連するサンプルコードです:
.map( (data: DataView) => { let value = data.getUint16(0, true /* little endian */); let mantissa = value & 0x0FFF; let exponent = value » 12; let magnitude = Math.pow(2, exponent); let output = (mantissa * magnitude); let lux = output / 100.0; return +lux.toFixed(2); });
これは通常、デバイスやセンサーのドキュメントやソースコードで見つけることができます、運が良ければ!