LINEはほんとにいろんなサービスをしていますよね。
LINEアプリを使うようになってから、便利なことが増えました。そして、そんなたくさんのサービスをWebの技術の勉強のネタに使わせてもらってます。
今回は、LINE Beaconで遊びたいと思います。
どんなことをするかというと、自宅に帰ると「おかえりなさい。今日も一日お疲れさま。」とLINE通知してくれたり、自宅を出ると「いってらっしゃい!今日の天気は晴れだよ。」なんてことをLINE通知してくれるようにしたいと思います。
使うのは、以下の通りです。
[ハードウェア]
- micro:bit
BLEビーコン端末として使います。LINE Beaconの仕様を満たすものであれば、なんでもよいですが、ちょうど手元に使っていないmicro:bitがあったので、それを使いました。BLEのアドバタイズデータを自由にできるのであれば、なんでもLINE Beacon対応になれそうです。
[ソフトウェア]
-
LINE Beacon
https://developers.line.biz/ja/docs/messaging-api/using-beacons/
今回の主題であり、LINE Beaconを検出してLINEボットに通知してくれるサービスです。 -
気象庁の天気予報情報を XML で配信
https://www.drk7.jp/weather/
天気予報を教えてくれます。ほかにもいろんな種類がありますが、降水確率も教えてくれたのでこれを選びました。 -
docomo 自然対話(雑談対話)
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=natural_dialogue&p_name=api_4_usage_scenario#tag01
Botっぽく振舞えるようにしたくて、これで簡単な話し相手をしてくれます。
[RESTful実行環境]
そして、ハードウェアからの通知を受け取って、ソフトウェアを組み合わせてLINE通知を返すRESTful実行環境が必要です。SSLサーバである必要があります。
(2020/2/11 修正)
・ハードウェアIDの払い出しのURLを追記しました。(こめんといただきありがとうございました!)
・docomo自然対話(雑談対話)が使えなくなるとのことで、UserLocalの人工知能チャットボットも併記しました。
micro:bitをLINE Beacon化する
以下の方が、micro:bitをLINE Beacon化してくださっています(ありがとうございます!)
① LINE Simple Beacon with BBC micro:bit
② pxt-linebeacon
丁寧に説明していただいていますので、特に問題なくできると思います。
説明の中でも説明がありますが、サンプルプログラムを実行(コンパイル含む)をする前に、これから使うLINE Beacon端末にユニークな番号を付与する必要があります。
LINE Beacon端末には、どのLINEアプリにも反応するのではなく、いづれかのLINEボット(Messaging API)に紐づけられ、そのボットを友達にすることで、LINEアプリにLINE Beaconからの通知が行くようになります。
ということで、まずはボットを作成しておきます。
以下で、プロバイダを作成します。(未作成の場合)
https://developers.line.biz/console/
それから、Messaging APIの新規チャネル(ようするにLINEボット)を作成します。(未作成の場合)
たとえば、「TestBeaconBot」という名前を付けます。
プランはフリーを選択しました。
そして、いよいよ、ユニークな番号であるハードウェアIDを払い出します。
https://admin-official.line.me/beacon/register#/
または
https://manager.line.biz/beacon/register
LINE Simple BeaconのハードウェアIDの払い出しを選択します。
アカウント一覧から、先ほど作成したボットを選択します。
ハードウェアID発行 ボタンを押下すると、5バイトの16進数文字列が表示されました。
[Arduino IDEの場合]
①を採用した場合です。
使用させていただいたライブラリはこちらです。
LINE Simple Beacon with BBC micro:bit (pizayanz/arduino-LINESimpleBeacon)
https://github.com/pizayanz/arduino-LINESimpleBeacon
依存するライブラリは以下です。上記のライブラリのReadmeに丁寧に記載していただいています。
・Nordic Semiconductor nRF5 based boards
・BLEPeripheral by Sandeep Mistry
このハードウェアIDを、Arduino IDEから、以下のソースの該当部分に入力して、マイコンボード(micro:bit)に書き込みを行います。
// Copyright (c) Pizayanz. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <LINESimpleBeacon.h>
// https://admin-official.line.me/beacon/register#/
// LINEから払い出されたhw idを2桁区切りにして0x__の部分に入れる
unsigned char hwid[5] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
LINESimpleBeacon lineSimpleBeacon = LINESimpleBeacon(hwid);
void setup() {
Serial.begin(9600);
// deviceMessage は 13文字まで
lineSimpleBeacon.begin("foobarbaz");
Serial.println(F("Line Simple Beacon"));
}
void loop() {
lineSimpleBeacon.loop();
}
※書き込むときのボード設定は以下のようにしてください。(忘れやすいので)
・ボード:BBC micro:bit
・Softdevice:S110またはS130
[MakeCodeの場合]
②を採用した場合です。
Microsoft MakeCode for micro:bit
https://makecode.microbit.org/
LINE Beacon for micro:bit (pizayanz/pxt-linebeacon)
https://github.com/pizayanz/pxt-linebeacon
ハードウェアIDを「LINE Beacon start HWID is」のところに入力しておきます。
ボタンAを押すと10秒間だけアドバタイズされるようにしています。
以上、書き込みが終わったら、電源を入れなおせば、LINE Beaconとして動作しているはずです。
Androidアプリ「nRF Connect」などを使えば、SCANNERに引っかかっているかと思います。(ちょっと見方が難しいですが)
※最近MakeCodeで書き込んだらなぜかエラーが発生するようになったなあ。。。
LINE Beaconからの通知を受け取る
実はもうこれで、紐づけたボット(TestBeaconBot)を友達に加えれば、LINE Beaconからの通知が出るのですが、受け取る側を用意していませんでした。
そのためには、TestBeaconBotのチャネル基本設定で設定しておく必要があります。
Channel Secret は後で使いますので、覚えておきます。
アクセストークン(ロングターム)を使いますので、まだ発行していなければ発行して、それを覚えておきます。
Webhook送信は「利用する」に編集しておきます。
Webhook URLは、これから立ち上げるRESTful実行環境のエンドポイントのURLを指定します。
自動応答メッセージは「利用しない」に設定しておきましょう。
編集が終わったら、QRコード(アプリ検証時や友達に紹介する際にご活用ください)をAndroidからスキャンして、このボットを友達登録しておきます。
ちょっと横道にそれて、docomo自然対話(雑談対話)のアプリ登録
ボットっぽく会話をしたいため、docomo 自然対話(雑談対話)を利用します。
(参考)
https://dev.smt.docomo.ne.jp/?p=about.index
以下のページから、「申請する」に進み、アカウント登録とアプリ登録を済ませます。
それにより、API KEYが払い出されます。
次に、以下のページにある通り、雑談対話のためのユーザ登録をします。
Postmanやcurlを使って、実施し、アプリケーションIDを取得しておきます。
RESTful実行環境の構築
これで準備が整いましたので、LINE Beaconからの通知を受け付けるRESTful実行環境を構築します。
毎度の通り、Swaggerを使ってRESTful実行環境を構築します。
以下をご参考にしてください。
SwaggerでRESTful環境を構築する
Swagger定義ファイルは以下の通りです。
適当に「/linebeacon」としています。TestBeaconBotのチャネル基本設定で指定したWebhook URLと同じにしてください。
/linebeacon:
post:
x-swagger-router-controller: routing
operationId: linebeacon
parameters:
- in: body
name: body
schema:
type: object
responses:
200:
description: Success
schema:
type: object
以下が、実装部分です。解説は後程。
const config = {
channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN,
channelSecret: process.env.LINE_CHANNEL_SECRET,
};
const DOCOMO_API_KEY = process.env.DOCOMO_API_KEY || 【docomo Developer supportのアプリのAPI KEY】;
const DOCOMO_APP_ID = process.env.DOCOMO_APP_ID || 【docomo Developer supportのアプリのID】;
const LineUtils = require('../../helpers/line-utils');
const app = new LineUtils(config);
const fetch = require('node-fetch');
app.message(async (event, client) =>{
console.log(event);
var message = await do_talk(event.message.text);
const echo = { type: 'text', text: message };
return client.replyMessage(event.replyToken, echo);
});
app.beacon(async (event, client) =>{
console.log(event);
var message;
if( event.beacon.type == 'enter'){
message = 'おかえりなさい。';
message += '今日も一日お疲れさまでした。';
}else{
message = 'いってらっしゃい!';
var weather = await do_get_wether(14);
var weather_message = parse_weather(weather.pref.area['東部'].info[0]);
message += '\n横浜の天気は、' + weather_message.weather + '\n' + weather_message.rainfallchance;
}
const echo = { type: 'text', text: message };
return client.replyMessage(event.replyToken, echo);
});
/* location: 13:東京、14:神奈川 */
function do_get_wether(location){
return fetch('https://www.drk7.jp/weather/json/' + location + '.js', {
method : 'GET'
})
.then((response) => {
return response.text();
})
.then(text =>{
text = text.trim();
if( text.startsWith('drk7jpweather.callback(') )
text = text.slice(23, -2);
return JSON.parse(text);
});
}
function parse_weather(info){
var weather = info.weather + ' です。';
var rainfallchance = "降水確率は、" +
"0-6時 " + info.rainfallchance.period[0].content + info.rainfallchance.unit +
"、6-12時 " + info.rainfallchance.period[1].content + info.rainfallchance.unit +
"、12-18時 " + info.rainfallchance.period[2].content + info.rainfallchance.unit +
"、17-24時 " + info.rainfallchance.period[3].content + info.rainfallchance.unit +
" です。";
return { weather: weather, rainfallchance: rainfallchance };
}
function do_talk(message){
var url = 'https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/dialogue?APIKEY=' + DOCOMO_API_KEY;
var body = {
language: 'ja-JP',
botId: 'Chatting',
appId: DOCOMO_APP_ID,
voiceText: message
};
return do_post(url, body)
.then(result =>{
console.log(result);
return result.systemText.expression;
});
}
function do_post(url, body){
return fetch(url, {
method : 'POST',
body : JSON.stringify(body),
headers: { "Content-Type" : "application/json; charset=utf-8" }
})
.then((response) => {
if(!response.ok)
throw "status is not 200.";
return response.json();
});
}
exports.handler = app.lambda();
ユーティリティも示しておきます。line-utils.jsとresponse.jsです。
'use strict';
const line = require('@line/bot-sdk');
const Response = require('./response');
class LineUtils{
constructor(config){
this.client = new line.Client(config);
this.map = new Map();
}
message(handler){
this.map.set('message', handler);
}
follow(handler){
this.map.set('follow', handler);
}
unfollow(handler){
this.map.set('unfollow', handler);
}
join(handler){
this.map.set('join', handler);
}
leave(handler){
this.map.set('leave', handler);
}
memberJoined(handler){
this.map.set('memberJoined', handler);
}
memberLeft(handler){
this.map.set('memberLeft', handler);
}
postback(handler){
this.map.set('postback', handler);
}
beacon(handler){
this.map.set('beacon', handler);
}
accountLink(handler){
this.map.set('accountLink', handler);
}
things(handler){
this.map.set('things', handler);
}
lambda(){
return async (event, context, callback) => {
var body = JSON.parse(event.body);
return Promise.all(body.events.map((event) =>{
if( (event.type == 'message') &&
(event.replyToken === '00000000000000000000000000000000' || event.replyToken === 'ffffffffffffffffffffffffffffffff' ))
return;
var handler = this.map.get(event.type);
if( handler )
return handler(event, this.client);
else
console.log(event.type + ' is not defined.');
}))
.then((result) =>{
// console.log(result);
// return new Response(result);
return new Response({});
})
.catch((err) => {
console.error(err);
const response = new Response();
response.set_error(err);
return response;
});
}
}
};
module.exports = LineUtils;
class Response{
constructor(context){
this.statusCode = 200;
this.headers = {'Access-Control-Allow-Origin' : '*'};
if( context )
this.set_body(context);
else
this.body = "";
}
set_error(error){
this.body = JSON.stringify({"err": error});
}
set_body(content){
this.body = JSON.stringify(content);
}
get_body(){
return JSON.parse(this.body);
}
}
module.exports = Response;
以下のnpmモジュールを使います。
- @line/bot-sdk
- node-fetch
また、各自で、以下の環境変数を設定する必要があります。
LINE_CHANNEL_ACCESS_TOKEN
さきほど覚えておいたLINEのアクセストークン(ロングターム)のことです。
LINE_CHANNEL_SECRET
さきほど覚えておいたLINEのChannel Secretのことです。
DOCOMO_API_KEY
さきほど覚えておいたdocomo Developer supportのAPI_KEYです。
DOCOMO_APP_ID
さきほど覚えておいたdocomo Developer supportのアプリケーションIDのことです。
line-utils.js は、LINE BeaconからくるMessaging API形式の通知の受付を、Actions on Google SDKっぽくするために作りました。
以下のURLで示す「イベントのタイプを表す識別子」ごとに、受信後の処理を記述できるようにしています。
例えば、メッセージイベントの場合には、
app.message( (event, client) =>{
/* 処理内容 */
});
と定義します。docomo 自然対話(雑談対話) では、ここに記述しています。
function do_talk(message)
に実装をまとめています。
そして、LINE Beaconからの通知は、ビーコンイベントであり、
app.beacon( (event, client) =>{
/* 処理内容 */
});
という感じで実装します。
event.beacon.type が 'enter' の場合には、ビーコンの受信圏内に入ったことを示し、'leave' の場合には、ビーコンの受信圏外に出たことを示します。(このleaveは廃止予定だそうです。残念!!!)
'leave' のときには、天気予報も返しています。
function do_get_wether(location)
で、天気情報を取得して、
function parse_weather(info)
で欲しい情報を抽出しています。
locationには、都市の番号を指定するのですが、神奈川が14で、東京が13でした。
以下のページに行くとたくさんリンクがありますが、所望の地域のURLから判断してください。今回は、神奈川を選択し、取得した情報のうちの東部地方(横浜近辺)を使っています。
気象庁の天気予報情報を XML で配信
https://www.drk7.jp/weather/
動作確認
さっそく、RESTful実行環境を起動しましょう。
スマホのBluetoothは有効になっていますか?
そして、micro:bitの電源をOnにしましょう。
以下のようなLINE通知が来ましたでしょうか?
今度は、micro:bitの電源をOffにします。今度は以下のようなLINE通知が来ましたでしょうか?(ちょっと1分ほど時間がかかります)
(わかりやすくするために、Botの画像を初〇ミ〇にしています。。。)
適当に話しかけると、適当に返してくれますよ。
帰宅時、出社時に声をかけて欲しいことがほかにもありますか?
世の中には便利なWeb APIがたくさんありますので、いろいろ組み合わせてみると、ボットに愛着がわいてくるかもしれませんね。
(追記) UserLocalの人工知能チャットボット(chatbot)に置き換える
docomo自然対話(雑談対話)が使えなくなるそうで、UserLocalのチャットボットに変えてみます。
まずは、以下から、個人開発者向け:ボットAPI利用申請をします。
人工知能チャットボット(chatbot)
https://ai.userlocal.jp/document/free/top/
そうすると、API Keyが払い出されますので、それをメモっておきます。
あとは、関数do_talkを以下に置き換えれば、完成です。
const USERLOCAL_API_KEY = '【UserLocalのAPIKey】';
function do_talk(message){
var body = {
message: message,
key: USERLOCAL_API_KEY,
};
return do_post('https://chatbot-api.userlocal.jp/api/chat', body)
.then(json =>{
console.log(json);
return json.result;
});
}
USERLOCAL_API_KEYのところは、取得したAPI Keyに置き換えてください。
以上です。