ちょっと前に switchbot 温湿度計をobnizと連携させたの次として、switchbot hand
(一番みんながswitchbotといわれて連想するやつ)を連携させました
Swtichbot APIを読む
前回はMeter(温湿度計)のを読んだので、今回はHandを読みます。
温湿度計の場合はデータの流れがSwitchbotデバイス→obniz→nodejsのみだったので簡単でしたが、
今回はその逆向きもあるので、ちょっとだけ複雑になります。
- ブロードキャスト(アドバタイズメント)のメッセージ
- 接続して行うコミュニケーションモード
のうち、コミュニケーションモードが必須になります。
ブロードキャストの方は温湿度計と同じ形で実装できますので、そちらを参考にしてください。
コミュニケーションモード
ドキュメントだとこのあたりですね。
BLEの知識がないと何を言っていることやら、となるわけですが、
要するに、コマンド方式(Request/Response方式)でいろいろコミュニケーション取れるよ、と書いてあります。
例えば、
- obniz → Switchbot にスイッチONにして!と依頼する
- Switchbot → obniz に OKと帰ってくる
とか、
- obniz → Switchbot に状態を尋ねる
- Switchbot → obniz で状態を教えてもらう
みたいな感じで、必ずobniz側からスタートする感じになります。
コマンドが当然バイナリ配列になるわけですが、例えばスイッチONにするコマンドはこの形のようです
つまり、 [0x57 0x01 0x00]
を送れば動く、ということです。
obniz的にはcharacteristics.write([0x57 0x01 0x00])
ですね。
これを書くだけだとResponseが受け取れてないので、responseも受け取れるように作れば、動かすことができます。
実装してみる
上記を踏まえて実装を抜粋するとこんな感じ
public async connectWait(
setting?: Pick<BleConnectSetting, 'retry' | 'forceConnect'>
) {
await this._peripheral.connectWait(setting);
this._peripheral.ondisconnect = (reason: any) => {
if (typeof this.ondisconnect === 'function') {
this.ondisconnect(reason);
}
};
const service = this._peripheral.getService(
'cba20d00-224d-11e6-9fb8-0002a5d5c51b'
);
if (!service) {
throw new Error(`no service found`);
}
const rxFromTargetCharacteristic = service.getCharacteristic(
'cba20003-224d-11e6-9fb8-0002a5d5c51b'
)!;
const txToTargetCharacteristic = service.getCharacteristic(
'cba20002-224d-11e6-9fb8-0002a5d5c51b'
)!;
this._commandSequence = new BleRemoteCommandSequence(
txToTargetCharacteristic,
rxFromTargetCharacteristic
);
await this._commandSequence.setupWait();
}
protected _createCommand(command: number, payload: number[]): number[] {
return [
0x57, // Magic Number
command,
...payload,
];
}
protected async executeActionWait(action: SwitchbotBotAction) {
if (!this._peripheral.connected || !this._commandSequence) {
throw new Error('connect device at first');
}
const results = await this._commandSequence.transactionWait(
this._createCommand(0x01, [action])
);
if (results[0] !== 0x01) {
throw new Error('execute action failed ' + results[0]);
}
return results;
}
public async pressWait() {
return await this.executeActionWait(SWITCHBOT_BOT_ACTION.PushAndPullBack);
}
connect直後にBleRemoteCommandSequence
を使ってコマンド方式の遣り取りをするインスタンスを作ってます。
pressWait
が押されたら [0x57 0x01 0x00]
のバイナリを_createCommand
で作り、_commandSequence.transactionWait
で送ってます。
実験してみる
ちゃんと動いているのが確認できました!
そのうちパーツライブラリ化して公開します。