前回(LINEをトリガーに家のGoogleHomeを喋らせる - Qiita)で、LINEから何かメッセージを受信したら家のGoogleHomeを喋らせることができた。しかし、どんなメッセージを受信しても同じ言葉しか喋らないため、今度はちゃんとLINEで送信したメッセージを喋らせることにした。
設計
LINE Messaging API - Webhookイベントオブジェクト によると、Webhookで下記のようなJSONデータが送られるようだ。
{
"events": [
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "message",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
"message": {
"id": "325708",
"type": "text",
"text": "Hello, world"
}
},
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "follow",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
}
}
]
}
このJSONデータから、events>message>textと構造体をたどれば、LINEで送信したテキストが取得できそうである。
どこかにこのWebhookを受け取るWebアプリを作ってJSONを解析すればよい。巷ではこういった用途だと「heroku」なるものがよく使われているようだが、今回はAzureの勉強も兼ねて「Azure Functions」で作成することにした。
最終的な構成は以下のようになった。
前回(LINEをトリガーに家のGoogleHomeを喋らせる)のIFTTTがAzure Functionsに置き換わっているだけ。
実装
beebotte 設定
talkerという名前のチャンネルを作成し、その中にmessagetextというリソースを作成した。
Azure Functions
AzureはMicrosoftが提供しているクラウドサービスで、Azure Functionsは「サーバレスプログラム」と言われており、要するに関数を実行するAPIを簡単に作れるサービスである。
Azureに登録すると、3ヶ月無料で試用できるサブスクリプションがもらえるので、気軽に試せる。筆者は無料期間は終わっていたので従量課金のサブスクリプションを契約したが、この記事で作ったアプリを3週間ほど使って課金額は30円程度だった。
Python関数の作成
- Azureのダッシュボードから「新規」をクリックし、Function Appを選択し、「作成」。
- Function Appの情報を入力し、「作成」
- 「デプロイしています…」で少し時間がかかるが、待つと作成が完了する。
- 「関数」の横の「+」をクリックする。右側にクイックスタートが出てくるが、今回はPythonの関数を作るので「カスタム関数を作成する」をクリックする。
- 関数の名前を入力して「作成」をクリックする。
- 最初のサンプルコードが表示されている。「実行」で関数を実行してテストできる。
- 右上のほうにある「関数のURLを取得」で、この関数のURLを表示できる。後にLINEbotに設定するので、控えておく。
beebotte用Pythonモジュールのインストール
Pythonでbeebotteを操作するのに、REST APIを使って操作してもよかったが、Beebotte Python SDK というのがあることを知ったので、これを使うことにした。
Azure FunctionsのPython環境にはこのモジュールはインストールされていなかったので、まずはモジュールをインストールするところから。
Function Appの環境設定は、KuduというWeb上のコンソールツールを使って行う。
Function Appのページから「プラットフォーム機能」のタブの中の「高度なツール(Kudu)」をクリックすると、Kuduが起動できる。
- Python3.6をインストール(2018/04/08 追記)
- 下に出てくるコードはAzure FunctionsのデフォルトのPython2.7ではうまくないので、Azure FunctionsでPython3とpipを使う - Qiita を参考にPython3.6をインストールする。
Kuduを使ってpipを使用してモジュールをインストールしようとしたが、単にpipとやろうとしたら、パーミッションがないと怒られたりしてなかなかハマった。
最終的に https://stackoverflow.com/questions/43970307/azure-functions-installing-python-modules-and-extensions-on-consumption-plan/43984890 あたりを参考に、virtualenvを使う下記の方法でできた。
Kuduの上のメニューからDebug ConsoleのCMDで、コマンドプロンプトが表示できる。
- 関数のディレクトリに移動
>cd D:\home\site\wwwroot\HttpTriggerPython31
- 仮想環境を作成、有効化
>python -m virtualenv env
- しばらく時間がかかるが、待っていると完了するので完了したら仮想環境を有効化する。
>cd env\Scripts
>activate.bat
- pip更新とbeebotteモジュールインストール
>python -m pip install -U pip
>python -m pip install beebotte
コード(Python)
コード編集画面で、コードを編集する。
冒頭で、モジュールをインストールしたvirtualenvのパスを追加している。これをしないと「モジュールが見つかりません」となる。環境の設定でPYTHONPATHを変更する方法もあるようだが、簡単のためコード内でパスを追加した。
# pythonのモジュール検索パスに、beebotteインストールしたパスを追加
import sys, os.path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname( __file__ ), 'env/Lib/site-packages')))
import os
import json
from beebotte import *
postreqdata = json.loads(open(os.environ['req']).read())
response = open(os.environ['res'], 'w')
messagetext = postreqdata['events'][0]['message']['text'] # JSONからメッセージのテキストを抽出
response.write(messagetext.encode('utf-8'))
print(messagetext.encode('utf-8'))
#print(postreqdata)
_hostname = 'api.beebotte.com'
_token = 'token_###################' # beebotteのトークンを記載
bbt = BBT(token = _token, hostname = _hostname)
bbt.write('talker', 'messagetext', messagetext)
response.close()
LINEbot 設定
Webhook URLに、作成したAzure FunctionsのURLを設定。
Raspberry Pi 側プログラム(Node.js)
前回とほぼ変わらないが、リソース「messagetext」に書かれたテキストを読み上げるようにしている。
const googlehome = require('google-home-notifier');
const mqtt = require('mqtt');
const language = 'ja';
googlehome.device("Google-Home", language);
googlehome.ip("192.168.xxx.xxx"); // GoogleHomeのIPアドレスを記載
const client = mqtt.connect('mqtt://mqtt.beebotte.com',
{username: 'token:token_###############', password: ''} // beebotteのトークンを記載
);
client.on('connect', function() {
client.subscribe('talker/messagetext'); //
});
client.on('message', function(topic, message) {
console.log(message.toString());
const json = JSON.parse(message);
console.log(json.data);
googlehome.notify(json.data, function(res) {
console.log(res);
});
});
完成形
動かしたときの動画がこちら。
LINEからGoogleHomeに好きな言葉を喋らせることができるように!
— テツオ (@ktetsuo) 2018年1月22日
LINEbotの通知先を自作のAzure Functionsに変えて、JSONを解釈しています。
これて家に遅くなる連絡をしたのですが、妻は鳴ったら帰ると思ってたみたいで、期待して損したと言っていました(´・ω・`)#LINE #googlehome #azure pic.twitter.com/ZdHNmMJaP4
感想
- Azure Functionsを初めて使ったが、WebAPIのためのサーバーを用意したりApatchを動かしたりといったことが不要なので、こういう用途には良いと感じた。
- IFTTTのときよりも応答に時間がかかるようになってしまった。Azure FunctionsはHTTPリクエストが来た時点でモジュールの読み込みなどから動くため、時間がかかるらしい。応答速度を早くしたければ、モジュールのロードなどは終わった状態で、常時リクエストを受け付けるようなWebAPIを作成したりする必要がある。
- 3週間ほど家に帰るときにGoogleHomeに喋らせていたら、1歳半の娘がGoogleHomeを指して「おとーしゃん」と言うようになった。