PaperangをJavaScriptで動かしたい

# はじめに

[Hamamatsu Micro Maker Faire 2025](https://makezine.jp/event/makerfaire/hmmf2025/)に出展するアプリがあるんですが、Paperang接続して得点とか印刷できたら、来場者の方にちょっとは記念になるかなぁなどと思ったのでやってみる。

ただ、SafariはWeb Bluetoothに非対応ということを途中で思い出して、出展するアプリへの実装はちょっと悩み中。(Androidのタブレットを持ってない・・・)

とはいえ、興味はあるのでテスト印刷までやってみる。

現時点ではお試し実装なので雑な実装ですが大目に見てください。
実際に利用する場合には、非同期部分含め見直し予定です。

# 実装

## 接続

ベースは数年前に作成したAndroid用の[paperangsample](https://github.com/project-dev/paperangsample)になります。

で、ベースがあので、ChatGPYでコードを変換。
いやぁ、便利。GATTのUUID以外はあまり修正することなく行けました。

肝となる部分を紹介。

対象デバイスと接続を行うために、デバイスを選択する必要があります。
navigator.bluetooth.[requestDevice](https://developer.mozilla.org/ja/docs/Web/API/Bluetooth/requestDevice)()メソッドを利用して行います。

```javascript
    service_uuid = '49535343-fe7d-4ae5-8fa9-9fafd205e455';
    characteristic_uuid = '49535343-8841-43f4-a8d4-ecbe34729bb3';

    ・・・略・・・

    async selectDevice()  {
        this.device = await navigator.bluetooth.requestDevice({
          filters: [{ namePrefix: 'Paperang' }],
          optionalServices: [this.service_uuid]
        });
    }
```

filterにはプレフィックスを指定します。
今回は```Paperang```を指定。
optionalServicesは省略可能ですが、依然[調べていた](https://qiita.com/krohigewagma/items/e765cffc1f2055c7ff7f)PaperangのサービスUUIDを指定します。

このUUIDは何度か試してとりあえず決めました。

デバイスの選択が完了したら接続です。

1. デバイスに接続
2. GATTサービスを取得
3. Characteristicを取得

という順番に行っていきます。
ちゃんとした実装なら、各処理の成功・失敗をチェックすべきですがお試しなので手抜き実装です。
あ、クラス内のメソッドに実装しているので、this使ってます。
```javascript
    this.server = await this.device.gatt.connect();
            
    this.service = await this.server.getPrimaryService(this.service_uuid);
            
    this.characteristic  = await this.service.getCharacteristic(this.characteristic_uuid);
```

まずは、gattに接続してBluetoothRemoteGATTServerオブジェクトを取得します。

次にBluetoothRemoteGATTServerの[getPrimaryService](https://developer.mozilla.org/ja/docs/Web/API/BluetoothRemoteGATTServer/getPrimaryService)()メソッドを使い、BluetoothRemoteGATTServiceオブジェクトを取得します。

最後に、BluetoothRemoteGATTServiceの[getCharacteristic](https://developer.mozilla.org/ja/docs/Web/API/BluetoothRemoteGATTService/getCharacteristics)()メソッドで、Characteristicを取得します。

ここまで動かせれば、テスト印刷までもう少しです。


## テスト印刷

テスト印刷のためにコマンドを実行するメソッドを実装します。

```javascript
    /**
     * コマンドを実行
     * @param {Command} command 
     * @returns 
     */
    async execute(command, value){
        try{
            console.log("execute command " + command.name + " : " + command.val);
            let cmd = this.createCommand(command, value);
            await this.characteristic.writeValue(cmd);
            console.log("execute command success");
            return true;
        }catch(e){
            console.error(e);
            return false;
        }
    }
```

Javaでも実装しているCommand列挙型をJavaScriptに移植したCommandクラスとコマンドのデータを引数に指定可能になっています。
ただ、現時点ではテスト印刷のコマンドが呼び出され売いので、データは未検証の仮自走になります。
Commandクラスは行数が多いので、[GitHubのコード](https://github.com/project-dev/paperang_js/blob/main/script/command.js)を参照してください。

コマンドはcreateCommand()メソッドでPaperangのコマンドの形式で作成します。
作成したコマンドをBluetoothRemoteGATTCharacteristicの[writeValue](https://developer.mozilla.org/ja/docs/Web/API/BluetoothRemoteGATTCharacteristic/writeValue)()メソッドで書き込みます。
※writeValue()メソッドはどうやら非推奨らしいので注意

で、実際に呼び出しているコードはこんな感じ

```javascript
paperang.execute(Command.PRINT_TEST_PAGE, []);
```

で、動いている様子は以下のリンクで確認できます。

https://drive.google.com/file/d/10tgIxNG5H8bhpOSrHx-uOUVaRa0sBVlm/view?usp=sharing

# 最後に
今回はざっくり実装で試してみました。
iOS/iPadOSでは動かないのが残念ですが・・・
コードは[GitHubで公開](https://github.com/project-dev/paperang_js)しております。
作りかけなので動作も怪しいかもしれませんが・

何かのイベントとかで使えたらいいなと思っているので、ちゃんと作って使えるといいなぁ・・・