TypescriptでWebHIDを扱う
WebHID+Typescriptについての簡単な紹介記事です.
chromeで設定不要で使えるようになる、との事で一部で話題になったりならなかったりしているものです.
気になる方や要望が多かったらもう少し具体的な実装まで踏み込んでまともなbookにします.
動機は不純で,zennやQではじまる所などの記事投稿サイトで拡散力のない人間がviewをのばすにはjs
,web
,react
についての記事を書くのが早いと聞いたからです.
はじめに
jsで使う記事は英語/日本語ともに記事が幾つかあるので、Typescriptの型付きのものについての記事を書きます.
簡単な紹介とtypescriptと使う場合の手順についての記載のみをします.
まだbookを書いていないので,素のJSから使う場合についてやjoyconなどの実際の具体的なコマンドについては、この記事よりも細かく踏み入った記事(記事末尾参照)を参考にしてください.
WebHID
って
そもそもw3cでdraftとして定義されている仕様で、シンプルに言えば「webサイトがbrowserからlocalのhidデバイスにアクセスする仕組み」です.
何が出来るか
- jsからlocalにサーバーとなるプログラムやライブラリを用意することなくHIDデバイスを制御出来る.
- Keyboardを自作する際の動作確認ツールをリッチなwebサイトとして実装する、とかですかね.
- 今まで
Gamepad API
などのAPIで出来なかった通信や非対応だったデバイスの機能が使える.- 例えば
Nintendo Switch
のjoycon
ならジャイロの情報を得たりIRセンサーを取り扱ったり出来る. - 「wasm +
WebHID
でゲーム(エンジン)作りました」とかイケイケに見えません?(他人任せ)
- 例えば
注意
記事末尾の関連記事のほうぼうに記載されていますが、
- あくまでWebHIDはdraftでありw3c標準ではないこと.
- いつ仕様が変わってもおかしくはないです.
- 「browser経由でweb側のjsがHID規格のデバイスにアクセスできる」というまあまあ悪用できそうなAPIであること.
- draftの中でもこの辺りについて長々と説明が書いてあります. 一応、browserでユーザーにデバイスを必ず選択させる、等の縛りを付けていますが......
- この為、Safariは他の幾つかのAPIと共に非搭載を宣言しています. それ以外のブラウザは今のところ搭載するかのアナウンスをしていないようです.
-
一方でGoogle Chromeではversion 89~ でデフォルトで利用可能になります. それ以前の場合は
Experimental Web Platform features
をenableにする必要があります.
WebHID有効化
-
Google Chrome
- version 89 or later -> そのまま使えます.
- version 88 or earlier
-
chrome://flags/#enable-experimental-web-platform-features
をchromeに直接入力してアクセス.[1] -
Experimental Web Platform features
を enable にする.
-
2020/02/14 現在でversion 89 beta以降のchromeを使っている人間は相当なモノ好きか職業戦士の方だと思いますので、多くの方は
chrome://flags
を開いて設定する必要があると思います.
Typescriptと使う
導入
以前は独自に型定義をしないといけませんでした[2]が、最近DefenitelyTyped
に型が追加されたのでそれを使うことが出来ます.
作成者のkkiyama117さんに感謝ですね......
w3c-web-hid
をdependenciesに追加します.
yarn add --dev @types/w3c-web-hid
実装
今回は他の多くの例同様にNintendo switchのコントローラーを使用するシンプルな実装を用意しました.
実際に利用する場合は各OSの設定でbluetooth接続をする必要があります.
基本的に [コントローラーの新規接続用の操作](https://support.nintendo.co.jp/app/answers/detail/a_id/36558/
)を行い、OSのbluetoothの設定からペアリングをするだけです.
(linuxでつながらない場合はudev
を弄る必要があると思います.)
仕様は以下の通りです.
-
open
をクリックしてコントローラーにHIDとして接続でき、send
でいくつかのコマンドをoutput出来ます. - そのままだとlogが貯まって重くなるので
close
でdeviceを切断できます. - また,
chrome dev tools
のconsole
で通信によるデバイスからのinputが確認できます. -
joycon
/procon
の両対応(のはず)です.
実際のcodeはこちらです.
(ReactのuseEffect
やcustom hook
、consoleへの雑な出力ではない適切なreportの処理等をすべきですが、簡単なdemoにつき一部省略しています. この実装をそのまま使う際は注意してください.)
基本的なデバイスとの通信の流れは以下の通りです.
-
navigator.hid.requestDevice
/navigator.hid.getDevices
でHIDDevice
をユーザーに指定させたり選択したりする. -
HIDDevice.open
で接続を開く. -
HIDDevice.addEventListener
でHIDDevice
からのinputを取得できるようにする. -
HIDDevice.sendReport
でHIDDevice
にreport(hidの通信の単位)を送信する.
// 1. `navigator.hid.requestDevice`/`navigator.hid.getDevices` で`HIDDevice`をユーザーに指定させたり選択したりする.
const devices: HIDDevice[] = await navigator.hid.requestDevice({
filters: [{
// deviceのvendorとproductのID
//vendorId?: number;
//productId?: number;
vendorId: 0x057e,
productId: 0x2009,
}]
});
const device = devices[0];
if (device) {
// 2. `HIDDevice.open`で接続を開く.
await device.open();
// 3. `HIDDevice.addEventListener`で`HIDDevice`からのinputを取得できるようにする.
device.addEventListener("inputreport", (e)=>{console.log(e)}); // event handler
// 4. `HIDDevice.sendReport` で`HIDDevice`にreport(hidの通信の単位)を送信する.
// await device.sendReport(reportId :number, sendData: BufferSource);
await device.sendReport(0x01, Uint8Array.from( ... ));
}
型定義があるのでこれだけでTypescriptでWebHIDを取り扱うことが出来ます.
後は必要なデバイスのHIDの規格さえ把握すれば簡単に処理することができますね.
終わりに
元々昨年(2020)末頃からwebHID+React関連の仕事をしていたところ、1月末から動きがあったので今回記事にしました.
記事を書くのって時間がすごいかかりますね. 出典が合っているかを再度確認するだけで数時間はかかります. 毎日書くような方にコツを聞きたいくらいです.
記事に間違いや誤字脱字があった際は優しく教えてくださると嬉しいです。
また、記事を良く読むと気づくのですが、実は僕がw3c-web-hid
の型定義のcode ownerでもあるので、そちらに間違いがあった時も優しくIssueやPull Requestを投げてください.
型定義作成時にvim-jp
のslackで相談に乗ってくださった方へのお礼を持ちまして、この記事の〆とさせていただきます.
Discussion
今も基本的には同じようにすれば良いのですが、少しだけapiの細かい仕様が変わっています。都度w3cのドキュメントやtypescriptのtypesのレポジトリを確認してから使うと良いと思います。