ProtoOut LINE Things ハッカソンで挙がった技術ポイントのひとつ、LINE Things切断を検出してメッセージを送るナレッジ obniz編 をまとめました。

今回は、obnizでやってみます。

obnizのソースコードは変更しない

今回、obnizのソースは変更しません。LIFFのみで対応し切断を検出してLIFFからからチャットへメッセージを投げます。

Bluetooth切断のシチュエーション

LINE ThingsにおけるBluetooth切断のシチュエーションを想像してみましょう。Bluetooth切断が生じたときに、BluetoothデバイスでもスマートフォンのLINEアプリ側でも検出は可能です。

しかし、問題はそこからどうやって情報を伝えるかです。

  • Bluetoothデバイスでも検出可能だが、インターネットに繋がっていないのでその状況をメッセージで外に出すのは難しい
    • もちろんobnizの場合は、インターネット経由で動作させている前提で動いていますが、その仕組の部分は割愛
    • obnizが何かしらのハードウェアと連携していて切断時に何かをするときには有効
  • スマートフォンのLINEアプリ側もLIFFの仕組みで切断取得できる。
    • こちらはスマホがオンラインで場合が多くLINEへメッセージを伝えることもできるケースが多い。

ということを考えると、スマートフォンのLINEアプリ側でLIFFを使うやり方のほうが、データを取り回す場合でも扱いやすいので、今回は採用します。

切断メッセージにメッセージ加える

切断イベント自体は、LIFFのサンプルにも既に書かれています。liffConnectToDeviceのところで、gattserverdisconnectedイベントが発生しています。

gattserverdisconnectedイベント https://developers.line.biz/ja/reference/liff/#events

ここにメッセージを出すようにします。liff.sendMessagesを使って、以下のように変更して、メッセージ接続時と切断時にメッセージを出すようにしています。

LIFFからその時写っているチャットにメッセージを打ち込むイメージです。

function liffConnectToDevice(device) {
    device.gatt.connect().then(() => {
        document.getElementById("device-name").innerText = device.name;
        document.getElementById("device-id").innerText = device.id;

        // Show status connected
        uiToggleDeviceConnected(true);

        // 接続時メッセージ
        liff.sendMessages([
            {
                type:'text',
                text:'LINE Thingsデバイスがつながりました!'
            }
            ])
            .then(() => {
                console.log('message sent');
            })
            .catch((err) => {
                console.log('error', err);
            });

        // Get service
        device.gatt.getPrimaryService(USER_SERVICE_UUID).then(service => {
            liffGetUserService(service);
        }).catch(error => {
            uiStatusError(makeErrorMsg(error), false);
        });
        device.gatt.getPrimaryService(PSDI_SERVICE_UUID).then(service => {
            liffGetPSDIService(service);
        }).catch(error => {
            uiStatusError(makeErrorMsg(error), false);
        });

        // Device disconnect callback
        // BLE切断イベント時でメッセージをLIFFから出す
        // LIFFであればスマホがオンラインで場合が多くLINEへメッセージを伝えることもできるケースが多い。
        const disconnectCallback = () => {
            // Show status disconnected
            uiToggleDeviceConnected(false);
            
            // メッセージ
            liff.sendMessages([
            {
                type:'text',
                text:'LINE ThingsデバイスがBluetooth切断されました!'
            }
            ])
            .then(() => {
                console.log('message sent');
            })
            .catch((err) => {
                console.log('error', err);
            });

            // Remove disconnect callback
            device.removeEventListener('gattserverdisconnected', disconnectCallback);

            // Reset LED state
            ledState = false;
            // Reset UI elements
            uiToggleLedButton(false);
            uiToggleStateButton(false);

            // Try to reconnect
            initializeLiff();
        };

        device.addEventListener('gattserverdisconnected', disconnectCallback);
    }).catch(error => {
        uiStatusError(makeErrorMsg(error), false);
    });
}

実際に動かしてみる

まず、メッセージを受け付けたいチャットでLIFFを開きたいので、LIFF URLをメモしておきます。

image.png

チャットにこのLIFF URLを送ります。

image.png

チャットでこのURLをクリックしてLIFFを出します。

image.png

LIFFが出ます。接続されます。

image.png

この時点でLIFFを閉じてみると接続時のメッセージが表示されてます。2つ出たのは一度閉じて2回目をつなげたからです。

image.png

遠くへ行ってBLE切断を再現します。

image.png

切断メッセージが送信されました!

ソースコード

動くソースコードはこちらにあります。

line-things-obniz-src/liff-app-disconnect at master · 1ft-seabass/line-things-obniz-src

余談:obniz側の切断検出

obniz側の切断検出はこちらです。

line-things-obniz-src/obniz.js at master · 1ft-seabass/line-things-obniz-src

obniz BLE側で切断は検出できます。obnizで動かすハードウェアのほうで、なにかしら切断に応じた操作をする場合には使えると思います。

  // obniz BLE側で切断は検出できるが、問題はどう出すか。
  // 閉じたBLEの状況では素直にやると他に伝えるすべがないので、検出のみ。
  // obnizの特性を利用すれば、Wi-Fi経由でメッセージが出せなくもない。
  obniz.ble.peripheral.onconnectionupdates = async function(data){
    console.log("remote device ", data.address, data.status);

    if (data.status === "connected") {
      console.log("connected!!")
      // 99で区別する
      // await notifyCharacteristic.writeWait([99]);
      // notifyCharacteristic.notify();
    } else if (data.status === "disconnected") {
      console.log("disconnected!!")
      // 98で区別する
      // await notifyCharacteristic.writeWait([98]);
      // notifyCharacteristic.notify();
    }
  };