今年の大垣でのハッカソンが開催されたので参加しました。今回のエントリーでは使用したLINE対応ビーコン(以下LINEビーコンと表記)に関してメモしたいと思います。
LINEビーコンとは?
【通販サイトはこちら】 beacon.theshop.jp
LINEビーコンの仕様はこんな感じです。
ハードウエア仕様 サイズ: 約41mm x 79mm x 約27mm 重さ: 約42g(電池取付時:約90g) BLEモジュール: hosiden社HRM1017
Buletoothなビーコンといえば、以前に使用したMAMORIOを思い出します。昨年のNagoyaハッカソンで使用したのでビーコンのイメージはなんとなく覚えています。
MAMORIOは単純なビーコンという形で使用するのに対して(検出したらどう処理するかを直接的に記述する)、今回使用する LineビーコンはLINEの機能を経由して(LINEbotなどを介してバックエンドへの通信)通信を行うことになります。機能的には大きく変化はないのですが、距離や電波強度といった部分関してはラッピングされた形でのセンシングなどが行われます。
ビーコンを使用するボットを準備する
基本的にはLINEMessagingAPIを使用したBotを作成し、その機能を拡張するだけでLINEビーコンへの対応ができます。
LINEBotの作り方に関しては過去の2つのエントリーを参考にしてもらえれば問題ないかなと思います。
まずはLINE Developersに移動して
ログインを行います。
すると新規のプロバイダー作成画面またはすでに作成されたプロバイダーのリストが表示されます。自分はすでに作成しているので作成したプロバイダーが表示されています。新規に作成する場合には新規プロバイダー作成ボタンをクリックしてプロバイダーの作成をおこなってください。(詳細は過去のエントリーを参照)
今回はすでに存在しているプロバイダーであるMashup名古屋
というプロバイダーを使用する想定で作業を進めます。プロバイダー名をクリックします。
するとチャンネルの選択画面に画面が遷移します。ここでは新しくチャンネルを作成するので、【新規チャンネルの作成】ボタンをクリックします。
ボタンにマウスオーバーするとアクティブになりますのでそのままクリックします。
次に使用するAPIを尋ねれれるのでそこで【Messaging API】を選択します。
ここからチャンネル情報の登録を行っていきます。詳細は過去のエントリーを参照してください。
必要となる情報は * アプリアイコン画像 * アプリ名 * アプリ説明 * プラン(Developer Trialを選択してください) * 業種 * メールアドレス
となります。値の確認を行い、規約への同意を行うとBotが作成できます
作成したBotは【チャンネル基本設定】画面で以下の項目を設定してください。(設定方法に関しては過去エントリーを参照)
- メッセージ送受信設定(アクセストークンは再発行してください)
- LINE@機能の利用
こんな感じになります。(WebhookURLに関してはまだ設定していないので未設定で問題ありません。)
Herokuの設定
続いてはBotのバックエンド側の設定になります。ローカルでやってもいいんですが、あえてHerokuを使用します。
以前のエントリーに従ってGitでデプロイしていきます。Git CMD
を起動してソースフォルダ(ディレクトリ)まで移動します。
事前に作成するファイルは
- runtime.txt(Pythonのバージョンを記載)
- requirements.txt(依存するライブラリの記載)
- Procfile(プログラムの実行方法を定義)
- main.py(アプリケーションのプログラム)
となります。
今回は単純なオウム返しbot
からビーコン対応のBotに変更することを考えていますので、main.py
に関してはオウム返しのものを使用します。
あとの設定ファイルに関しては過去のエントリーそのままで大丈夫です。
【main.py】
from flask import Flask, request, abort import os from linebot import ( LineBotApi, WebhookHandler ) from linebot.exceptions import ( InvalidSignatureError ) from linebot.models import ( MessageEvent, TextMessage, TextSendMessage, ) app = Flask(__name__) #環境変数取得 YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"] YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"] line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN) handler = WebhookHandler(YOUR_CHANNEL_SECRET) @app.route("/") def hello_world(): return "hello world!" @app.route("/callback", methods=['POST']) def callback(): # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # handle webhook body try: handler.handle(body, signature) except InvalidSignatureError: abort(400) return 'OK' @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) if __name__ == "__main__": # app.run() port = int(os.getenv("PORT")) app.run(host="0.0.0.0", port=port)
このソースファイルと設定ファイルから、以下のようにGitでpushすればデプロイはできています。
(base) C:\sample> heroku create 【アプリケーション名】 (base) C:\sample> heroku config:set YOUR_CHANNEL_ACCESS_TOKEN="【LineDevelopersのChannelの設定ページで表示されたAccessトークン】" --app 【アプリケーション名】 (base) C:\sample> heroku config:set YOUR_CHANNEL_SECRET="【LineDevelopersのChannelの設定ページで表示されたSECRET】" --app 【アプリケーション名】 (base) C:\sample> git init (base) C:\sample> git add . (base) C:\sample> git commit -m 'commit' (base) C:\sample> git config --global user.email "【メールアドレス】" (base) C:\sample> git config --global user.name "【名前】" (base) C:\sample> git remote add heroku https://git.heroku.com/【アプリケーション名】.git (base) C:\sample> git push heroku master (base) C:\sample> heroku ps:scale web=1
ブラウザから作成したサービスのURL(https://【アプリケーション名】.herokuapp.com/
)にアクセスして
Hello World
と表示されればOKです!無事にデプロイがされています。
デプロイがうまく行ったらLINEbot側のチャンネル設定で【メッセージ送受信設定】>【Webhook URL】の設定を行います。
https://【アプリケーション名】.herokuapp.com/callback
になるようにします。
あとはこのbotと友達登録をおこなってメッセージを送ればBotの雛形は完成です。
ようやくビーコンのお話
LINE Developerのサイトには以下のURLビーコンを使用するための情報が記載されています。
ボットをビーコンとリンクするには、LINE@マネージャーのビーコン登録ページを開きます。LINE Beacon対応端末とボットをリンクするか、LINE Simple BeaconハードウェアIDを端末に発行できます。
ということなのでLINE@マネージャーにアクセスをします。すると以下のような画面になるので【ビーコン端末とbotの連携】ボタンをクリックします。
次に【アカウント一覧】の画面に遷移します。ここで作成したBotとビーコンの紐づけを行うことができます。
今回作成したBotの右にある【選択】ボタンをクリックします。 続いてアカウント連携と連携するビーコン情報を登録します。 Botとは登録したビーコンのみアクセスされる形になりますので、その他のビーコンに近づいてもなにも反応はしません。 また、注意点になりますが、Botに複数のビーコンを登録することができますが、1つのビーコンで複数のBotに重複した登録することはできません。そのためLINE@Managerではビーコンの登録解除することもできます。
HWIDとCODEを入力して【連携】ボタンをクリックすると連携は完了です。
連携が完了すると以下のようなメッセージが表示されます。
LINE BeaconのEvent Web hook を受信する方法 1. LINEがインストールされているスマートホンのbluetoothをonにしてください。 2.「LINE Beaconを利用」にチェックを入れてください。 a. 設定 → プライバシー管理 → LINE Beaconを利用。 3. Botアカウントと友達になってください。 a. 連携していないBotアカウントには、ビーコンの情報は送られません。 4. ビーコンの電源が入っていることを確認し、スマートホンを近づけて下さい。 a. LINEアプリがビーコンを検知し、その情報をLINEプラットフォームに送信します。
LINEさんのアプリのUIが変わってしまっているので、この説明では設定できません。具体的には以下の部分は読み替えてください。
(以前のUI)a. 設定 → プライバシー管理 → LINE Beaconを利用。
↓
(現在のUI)a. 設定 → プライバシー管理 → 情報の提供 → LINE Beaconをチェック
2018.09.03時点でのUIは以下のようになっています。
【設定画面】を開きます。【プライバシー管理】をタップします。
その中から【情報の提供】をタップします。
【LINE Beacon】のラジオボタンがあるのでタップしてチェックを入れます。
【同意して利用開始】というダイアログが表示されるのでタップして利用を開始します。
端末によってはBluetoothをONにするダイアログがでるかもしれません。【許可】してください。 (画面が表示されなくても別途BluetoothをONにしてください)
これでLINEアプリ上でビーコンが検出されるようになりました。
あとは、LINEBot上でビーコンの検出イベントが通知するようにソースコードを変更することになります。
【main.py】
from flask import Flask, request, abort, send_file import os from linebot import ( LineBotApi, WebhookHandler ) from linebot.exceptions import ( InvalidSignatureError ) from linebot.models import ( MessageEvent, TextMessage, TextSendMessage, BeaconEvent, ) app = Flask(__name__) statusDict = {} status = 0 #環境変数取得 YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"] YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"] line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN) handler = WebhookHandler(YOUR_CHANNEL_SECRET) @app.route("/") def hello_world(): return "hello world!" @app.route("/callback", methods=['POST']) def callback(): # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # handle webhook body try: handler.handle(body, signature) except InvalidSignatureError: abort(400) return 'OK' @handler.add(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token,[ TextSendMessage(text=event.message.text), ]) @handler.add(BeaconEvent) def handle_beacon(event): print(event) line_bot_api.reply_message( event.reply_token,[ TextSendMessage(text='beaconを検出しました. event.type={}, hwid={}, device_message(hex string)={}'.format(event.beacon.type, event.beacon.hwid, event.beacon.dm)), ]) if __name__ == "__main__": port = int(os.getenv("PORT")) app.run(host="0.0.0.0", port=port)
ソース変更部分の説明
LINEBotはビーコンの変化検出するとenter/leaveといったtriggerをもとにイベントバックエンドに送信します。そのため以下のようにBeaconEvent
をlinebot.modelsからimportする必要があります。
from linebot.models import ( MessageEvent, TextMessage, TextSendMessage, BeaconEvent, )
その後にイベントハンドラーをflask
側に登録します。
@handler.add(BeaconEvent) def handle_beacon(event): print(event) line_bot_api.reply_message( event.reply_token,[ TextSendMessage(text='beaconを検出しました. event.type={}, hwid={}, device_message(hex string)={}'.format(event.beacon.type, event.beacon.hwid, event.beacon.dm)), ])
これでBot側でイベントはを受信できるようになります。
Herokuのログでは以下のようにメッセージが送信されてきます。
(base) C:\sample> heroku logs --tail 2018-09-13T07:42:18.680976+00:00 heroku[router]: at=info method=POST path="/callback" host=【アプリ名】.herokuapp.com request_id=4466883f-6570-4da2-b4ec-c9dcd591cf14 fwd="203.104.146.154" dyno=web.1 connect=0ms service=445ms status=200 bytes=155 protocol=https 2018-09-13T07:42:18.679620+00:00 app[web.1]: 10.95.189.103 - - [13/Sep/2018 07:42:18] "POST /callback HTTP/1.1" 200 - 2018-09-13T07:42:18.238877+00:00 app[web.1]: {"beacon": {"dm": "", "hwid": "00000*****", "type": "enter"}, "replyToken": "248b7d10a5bc4977aab930004cacd320", "source": {"type": "user", "userId": "*********************************"}, "timestamp": 1536824537481, "type": "beacon"}
(注意) LINEさんのビーコンではビーコンの状態をbotが管理しています。そのため、エリアにはいったり出たりという変化がHerokuで作成したバックエンドのデプロイとは関係ない状態になります。(考えてみれば当然なのですが)つまり、一度Bot側でEnterの状態になると、バックエンドのデプロイをしたからといってEnter状態トリガーが繰り返し発生されることはありません。一般のBeaconでは常に位置情報が取り出されるのでこの部分が大きく異なります。あとLeaveに関する通知に関しては、ビーコンから離れて、思った以上に遅く送信されます。基本的にはEnterのみを使用する方向で実装したほうがいいでしょう。
イベントで取得するeventオブジェクトに関しては以下のドキュメントを参照してください。
(注意) ドキュメントをみるとJsonのサンプルは以下の様になっていますが
{ "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA", "type": "beacon", "timestamp": 1462629479859, "source": { "type": "user", "userId": "U4af4980629..." }, "beacon": { "hwid": "d41d8cd98f", "type": "enter" } }
このなかでsource.userId
という属性があるように見えますが、これ間違いですsource.user_id
が正しいようです。
Githubのソースをみると正解だと確信しました。
終わりに
LINEBotの作成をちょっと変更することでビーコンの機能を追加できるのはなかなか便利だなと思いました。(アプリの外装を作らなくてもいいのも便利) ただ、デバックが結構大変だなというのも実感しました。これはBluetoothのビーコン全体の話かなと思いますが、それに加えて状態管理をLINEアプリ側にゆだねているのが LINEビーコン側のデバックを難しくしているような気はします。あと、LINEビーコンを使用可能にする手順ももう少し簡単にしたらいいなあと。
とはいうものの、ここまでLINEが普及しているのであれば何らかの形で使っていくアイデアがあるのであれば積極的に使ったほうがいいですかね。
LINEさんはサンプルが充実しているので興味ある方はこちらのGithubを参考にしてください。
【参考】 github.com