ある部屋に誰がいるのかという情報を取得できるシステムを作ろうとしたけど、一旦頓挫した。着手する前から懸念していたけど、スマホの省電力設定でLINEアプリが動作を停止された場合に意図した動作をしてくれなかった。紛失防止タグなんかのBluetoothを発するために作られたデバイスを使う方が筋がいいと思う。結果的にはイマイチだったけど、その過程でいくらか発見もあったから雑に書き残しておく。

目標

たとえば就労時間が定まっていない職場のような特定の多人数が集まる空間において、いま誰がいるのかをリアルタイムに確認できる仕組みを作ろうとした。タイムカードのように来た時と帰る時に本人が記録する仕組みでもいいけど、 おもしろくないから 記録し忘れる可能性が大いにあるため、人間は何もしなくても勝手に検知してくれるのが望ましい。世の中の状況を考慮すると、時間ごとのおおまかな在室状況をログとして保存しておけると、いざというときの濃厚接触者の情報提供にも繋がるかも。

方針

Bluetoothビーコンを使うのがなんとなく現実的な気がするし、おもしろそうだから使ってみたい。BluetoothビーコンはBluetooth Low Energyに対応したデバイスの接近を検出する技術で、たとえばローソンの店内でLINEアプリを開くとクーポンが貰える、みたいな用途で使われている。
今回は通信を行うのはあくまでLINEアプリがインストールされたスマホ側で、Bluetoothビーコンは自身の存在を主張するだけ。結果的にはこの部分が課題だと思っていて、部屋に設置したデバイスが近づいた人間(が持っているデバイス)を検知して、サーバと通信する構成にした方が都合がよさそう。

使ったもの

  • LINE Messaging API
    • LINE Simple Beacon
  • Raspberry Pi 3B+
  • Google Apps Script

やったこと

図にするとこんな感じ。
図にするとこんな感じ。

今回は、机の上に置いてあったRaspberry PiをBluetoothビーコンとして採用した。Bluetoothビーコンには規格が複数あるけど、LINE Beaconという法人向けの規格の一部の機能がLINE Simple Beacon として提供されている。この機能はLINE Messaging APIに組み込まれていて、作成した公式アカウントにBluetoothビーコンを連携させる。Bluetooth Low Energyに対応しているデバイスならなんでも使えるらしい。
LINE Messaging APIを用いて作成した公式アカウントを友達に追加したアカウントでログインしたLINEアプリがインストールされたスマートフォン (くどい) がBluetoothビーコンに接近すると、その公式アカウントに対してスマホからWebhookが送られる。つまり、ユーザからは見えないけど、ユーザがその公式アカウントに対してメッセージ(HTTP POSTリクエスト)を送信する。このメッセージを受信した公式アカウント(に紐付けられたサーバ)が、その内容に応じた処理を行うという仕組み。理解しさえすれば難しいことをしているわけではないような気がするけど、書き起こすと長ったらしい。

LINE公式アカウントの作成

ネットを漁れば良質な情報がたくさん出てくるので割愛。次の項の公式ブログの記事にも作成手順へのリンクがある。

Raspberry PiをBluetooth Beaconとして動作させる

公式ブログの記事を参考にした。

特筆することはないけど、Raspberry Pi OS(Raspbianから最近変わったらしい)ではなくUbuntu 20.04 Serverをインストールしていたので、Bluetoothを有効化するのに手間取った。過去の情報ではリポジトリを追加してあれこれという過程が必要だったけど、sudo apt install pi-bluetoothと実行して再起動するとうまくいった。

Webhookの設定

何で実装してもいいけど、そこそこ慣れていて手軽だったからGoogle Apps Scriptを使った。動作確認として作成したアカウントを数人に友達登録してもらって、(名前)が入室/退室しましたというメッセージを管理者である自分宛に送信するようにした。ユーザから送信されるWebhookにはビーコンに近づいた/離れたという情報が含まれているので、判別にはこの情報を利用した。ちなみに、公式のドキュメント に書かれているように、この情報は廃止予定らしい。
ちなみに、初めてGASでLINE botを作った時はこの記事を参考にした記憶がある。

ユーザ名の取得

前述した公式のドキュメント に記述があるけど、送信されたWebhookに含まれるユーザについての情報は、userIdと呼ばれるランダムな文字列だけ。今回の用途ではその部屋に誰がいるのかを知りたいので、この情報からユーザ名としてLINEでの表示名を取得したい。
ドキュメントを探して見つかったのはLINE Messaging APIではなくSocial APIを使用している場合についての手順 だったけど、調べてみるとやりたいことをしている人が見つかった。というか、ドキュメントに示されている手順でユーザ情報を取得できた。

今回は使わないけど、アイコン画像やステータスメッセージ(いわゆるひとこと)なんかもついでに取得できた。このユーザ情報の取得に使用するuserIdは公式アカウントごとに別のものが発行されるようなので、仮にあるユーザが友達登録した公式アカウントがそのユーザのuserIdを保存していたとして、それが流出したとしても、この公式アカウントに対して発行されたトークンを保有しない第三者はこのuserIdからユーザのプロフィールを取得することはできなさそう (くどい) 。詳細はドキュメントを読んでほしい。

参考としてGASのソースコードを置いておく。

課題

とりあえず作って動かしてみたけど、全体的にイマイチな印象。ここに並べる問題点はスマホの設定を変更すれば解消できるものもあるけど、それをユーザに強いることが最適なやり方だとは自分には思えない。

動作が不安定

ビーコンに近づいたときにメッセージを送信するのはスマホ側なので、LINEアプリがバックグラウンド(もちろんフォアグラウンドでもよい)で動作していないとメッセージが送信されない。また、ずっと同じ場所にいるのに、LINEアプリのバックグラウンド動作がOSに停止されたときにビーコンから離れたと判定されて、後にLINEアプリを起動するまで退出したことになっていることが多々あった。特に、省電力のためにバックグラウンドのアプリの動作を積極的に断つような設計がされているメーカーのスマホで頻繁に発生した。

LINEアプリが必須

当然ではあるけど、今回の構成ではユーザがスマホにLINEアプリをインストールしている必要がある。

位置情報へのアクセス許可が必要

比較的最近のiOSおよびAndroidでは、アプリがBluetoothの情報を取得するためには位置情報の権限が必要らしい。これは、おそらくGPS・Wi-Fi・Bluetoothの使用に関する権限をOS側では位置情報に関する権限というくくりで扱っているからだろうと推測している。
今回の用途で取得したいのはあくまでBluetoothの情報であって、位置情報は取得していない。にも関わらず、(LINEアプリを使って実装しているせいで)ユーザはLINEアプリがデバイスの位置情報を取得する権限を許可しないといけない。さらに、バックグラウンドでビーコンと通信させるためには、アプリの起動中だけでなく常に位置情報の取得を許可する必要がある。プライバシーの観点からこれを受け入れがたいと感じる人がいる可能性は十分にある。

雑感

過去にLINE Messaging APIを使ってLINE botを作ったことがあったから、全体的には作っていて楽だと感じた。LINE (Simple) Beaconに関しては、今回のような受動的な用途ではなく、半能動的な用途で使うと活用できそう。たとえば、屋外でのイベントの展示物がある場所にBluetoothビーコンを仕込んでおいて、そのイベントのLINE公式アカウントの画面を表示させて近づくとその展示に関するメッセージが飛んでくるとか。

今回の経験を踏まえると、Bluetooth Low Energyを使用しているTileあたりの紛失防止タグをユーザに持ってもらうようにして、今回で言えばRaspberry Pi側でサーバと通信するようにすれば、意図通りに動くシステムができそうな気がしている。新たに想定される問題としては、初期費用が必要になるのと、もし持ち運ぶのを忘れたら部屋にいないことになってしまうことがある。それでも手動で記録するよりはマシだと思うし、薄いので財布に入れておいたり、自宅や車などの鍵とセットにしておけばそれほど負担にはならなさそう。あと、ついでに紛失防止タグにもなるという利点もある。