前回のおさらい
GoogleHomeとiftttとDialogflowとfirebaseとLINEBOTとラズパイを使って子供と音声によるLINE交換をしてみる(前編)
以下の外部サービス設定とラズパイに自作サービスを登録して、LINEから自作サービスにメッセージを蓄えてGooleHomeに通知を送るところまで書きました。
- 外部サービス
- LINE Messaging API
- Dialogflow
- Cloud Functions
- Firebase
- 自作サービス
今回はGoogleHomeとIFTTTを連携して、GoogleHomeにメッセージを読んでもらいます。
その前に・・・
前回の記事では、自作サービスについての情報が少なかったので補足します。
自作サービスの責務としては以下となります。
- LINE BOTとの連携
- LINE BOTから送られてきたメッセージをFirebaseかWebhookが受ける
- 受け取ったメッセージを内部DBに格納する
- GoogleHomeへLINEが届いた通知メッセージを送る
- GoogleHomeとの連携
- イベントタイプ毎に以下の処理に振り分ける
- メッセージ送信
- LINE BOTへメッセージ送信
- 未読メッセージの読み上げ
- 内部DBから未読のメッセージを読み上げて既読にする
- 既読メッセージの読み上げ
- 内部DBから既読になってから数分前までのメッセージを読み上げる
- メッセージ送信
- イベントタイプ毎に以下の処理に振り分ける
これらを実装します。
自作サービス
Node.jsで作成し、自宅サーバーとしてラズパイにデプロイします。
GoogleHomeとLANで繋がっている必要があるので自宅サーバーとしていますが、VPNでAWSなどのクラウドと繋がっているのであれば、クラウドにデプロイするのもいいと思います。
以下のモジュールをインストールする必要があります。
- google-home-notifier
- line-bot-sdk-nodejs
- firebase-admin
とくに問題なくインストールできると思いますが、開発用のwindows環境で一部トラブルがありましので、情報共有しておきます。
- google-home-notifier
- Apple Bonjourというサービスを入れないとInstallが失敗しました。
- iTunesなどApple製のソフトが入っている方は問題ないと思いますが、使っていない方はエラーが出たらBonjour疑ってみたら如何でしょうか?
Node.jsとECMAScript2015をちゃんと弄ったのは初めてですが、非常に便利ですね。
ただ、書きっぷりにかなり不安を覚えたのも確かです。これから勉強ですね。
Promiseの多用
今回、非同期個所がどうしても多くなるので、Promiseパターンで実装したのですが、正直こんなPromiseだらけでいいのかと不安になりました。
もっといい書き方があれば教えてください。
試作段階のゴミ
あと、試作段階のロジック(ngrokなど)も残ってたりしますが、目をつぶりましょう。
Databaseとして、NeDBbというのを使ってみました。
最初の大きな構想では、メッセージを全て格納する想定でしたので、最終的には専用DBへ移行することを想定して、MongoDBのapiと互換性のあるNeDBを採用しました。
・・・ですが、そもそも音声インターフェースで過去のメッセージを聞くことは意味がないことを悟り、結果的に1日でデータをパージする実装にしています。
そうなると、mapで実装しても全然よかったですね。
モジュール分割も勉強不足で出来ていないですが、時間があればリファクタリングして綺麗にしたいですね。
IFTTT連携
なかなか本題に入りませんでしたが、やっと本題に入ります。
ここで次回に持ち越したら、やるやる詐欺になってしまいますしね。
GoogleHomeとIFTTTの連携では以下を実現します。
- LINEで"ただいま" → "ただいま"メッセージをLINEへ送る
- 新しいLINE → 未読のメッセージを読み上げて既読にする
- さっきのLINE → 既読になってから数分前までのメッセージを読み上げる
この左の言葉を解釈して各機能へ連携させるのがIFTTTの役目です。
IFTTTの設定は以下のようになります。
未読のメッセージを読み上げる
this
Google Assistant
いろいろ登録しましたが、「新しいLINE」が一番認識率が高そうです。
that
Webhook
Firebaseの場合はURLにFirebaseのDatabaseURLを指定します。
未読を読み上げるので、「/googolehome/unread/」へ現在のタイムスタンプを更新するようにします。
Bodyに**{".sv":"timestamp"}**を設定すると、Firebaseが自動でタイムスタンプを設定しれくれます。
自作サービス側が「/googolehome/unread/」を監視していますので、タイムスタンプに変更があれば内部DBから未読を読み上げる機能が発火する仕組みです。
ついでに未読から既読にしますので、もう一回「新しいLINE」と呼んでも、もう読み上げられることはありません。
ただし、もう一度聞きたいというニーズもありますので、「さっきのLINE」という機能も作っています。この後で紹介します。
FirebaseのRESTによる認証
FirebaseをRESTでアクセスする場合、認証情報が必要となります。最新の公式ではGoogle OAuth2を渡せとあったので、試したのですが有効期限がすぐ切れてしまうため、IFTTTのように固定して使うのには向いていないと思いました。※使い方が間違っているかもしれないので、もし違えていればご指摘ください。
そこで現在は非推奨となっているFirebaseの認証キーを使います。
データベースのシークレットをコピーして、URLの最後にauth=●●●●を追加します。
ちなみにFirebaseを使わずに、自作サービスに直接Webhookする場合は、そのURLを設定します。
Content Typeを「application/x-www-form-urlencoded」に変更し、Bodyには「events='[{"type":"unread"}]'」を設定してください。多分いけます・・・。
さっき読んだメッセージを読み上げる
this
Google Assistant
that
Webhook
少し前の読み上げるので、「/googolehome/before/」へ現在のタイムスタンプを更新するようにします。
この機能は「新しいLINE」を聞き漏らしたりしてもう一度読み上げたい場合に使います。
現在の仕様は、未読が既読になったら時点から5分間であれば何回でも聞ける機能となっています。
IFTTTの設定はURL以外は変わっていませんので割愛しますね。
Webhookで呼び出したい場合は、Bodyに「events='[{"type":"before"}]'」を設定してください。
GoogleHomeからLINEにメッセージを飛ばす
this
Google Assistant
that
Webhook
thisが先ほどと比べると少し複雑になっています。
「LINEで $」が一番認識率が高いですね。
thatもほとんど変わっていませんが、URLとBodyが変わっています。途中の階層も「googlehome」から「linebot」に変えていますので注意してください。
Bodyは、読み取った「$」部分を送信したいので、**"{{TextField}}"**を設定します。
Webhookで呼び出したい場合は、Bodyに「events='[{"type":"send","text":"{{TextField}}"}]'」を設定してください。
IFTT連携後のイメージ
子供がGoogleHomeでLINEメッセージを送信
子供がGoogleHomeから未読、少し前のLINEメッセージを聞く
さいごに
今回、GoogleHomeの地位を向上すべく始まったプロジェクトですが、はたして結果はどうなったか・・・
子供は大喜びです。大成功です。
夜な夜な寝る時間を削ってやった甲斐があったというもんです。
ただ嫁がボソッと、「安いスマホ買えば済むよね」という身も蓋もないことを言ったことは伏せておきます。
次は、王道で行くとリモコン制御ですかね。
せっかくラズパイも買ったのですから満喫しないと。
2回に渡り長文でしたが最後まで読んでいただき、ありがとうございました!