こんにちは。楽楽精算開発チームの岡本です。 会社で購入したGoogleHomeで自由に遊べる権利を頂いたので、色々と遊ばせてもらいました。 先日その内容を社内でプレゼンする機会があったのですが、せっかくなのでGoogleHome自身にプレゼンしてもらいました。 今回は、その際に実施した設定を説明しようと思います。 ※設定方法については west-cさん が既に書かれているので、設定した内容だけ書いていきます。 今回やりたいこと スマートフォン でプレゼン開始を指示する。 ディスプレイにスライドが表示される。 GoogleHomeがスライドの内容を喋り始める。 スライドの内容を喋り終えると、次のスライドに切り替わる。 スライドが終了するまで2~4を繰り返す。 準備するもの Wan側 Action on Google Googleアシスタント 内で呼出し可能な チャットアプリ *1 を構築できるサービスです。 構築した チャットアプリ から更に別の Webサービス を呼び出すこともできるので、この仕組を用いれば Googleアシスタント から直接 Webサービス を呼び出せます。 今回は Dialogflow を呼び出すように構築しています。 Dialogflow 自然言語 の解析エンジンです。受取った音声データを解析してテキストに変換してくれます。 また、 Firebase の ホスティング を用いた スクリプト 実行も可能なので、変換したテキストを他の Webサービス に連携させたりもできます。 Firebase いわゆるBaasです。 今回は ホスティング 機能とDB機能を使用します。 Lan側 RaspberryPi 超小型のコンピューターです。 今回は以下の機能を実装しています。 ディスプレイへのスライド切替え通知 GoogleHomeへのメッセージ通知 GoogleHome いわゆる スマートスピーカー 。今回は喋らせるだけなのでスマートな機能は使いません。 ざっくりとした処理の流れ スマートフォン の Googleアシスタント から Action on Google で構築した チャットアプリ を呼び出す。 チャットアプリ で入力した音声データを Dialogflow に連携する。 Dialogflow の解析結果テキストを Firebase のDBに保存する。 保存したテキストをRaspberryPiに通知する。 (通知されたテキストがプレゼン開始キーワードの場合は、プレゼンを開始する。) スライドの表示/切り替えをディスプレイに通知する。 喋らせるメッセージをGoogleHomeに通知する。 Action on Google の設定 Action on Google で新規プロジェクトを作成し Action と App information を設定します。 Action ADD ACTIONS から Dialogflow を選択します。 App information 重要なのは Assistant app name の項目だけです。 Assistant app name で設定した名前で Googleアシスタント で チャットアプリ を呼び出すことになります。 今回は「発表アプリ」にしたので、 スマホ に「 OK Google 発表アプリにつないで」と話しかけると チャットアプリ が起動します。 それ以外の項目については適当に設定してしまって問題ないです。 以上で Action on Google の設定は完了です。 Dialogflowの設定 Action on Google の Action 設定から、Dialogflow画面に遷移できるので、新規プロジェクトを作成し Intents と Fulfillment を設定します。 Intentsの設定 チャットアプリ で入力した音声データに対して、どのように振舞うのかを設定します。今回は以下の2つを設定します。 Default Welcome Intent チャットアプリ が起動した時の振舞いを設定します。 今回は、アプリ起動時に チャットアプリ が「発表アプリを起動しました。」と応答するように Text response を設定しました。 プレゼン開始 チャットアプリ にプレゼン開始を指示した時の振舞いを設定します。今回は以下のような振舞いを想定しています。 1.チャットアプリに「プレゼン開始」と音声入力する。 2.「プレゼン開始」のキーワードをFirebaseに保存する。 3.チャットアプリが「プレゼンを開始しました。」と応答する。 「プレゼン開始」の音声入力で saveData というアクションが実行されるように設定します。 saveData の内容については Fulfillment で設定します。 アクション実行後、 チャットアプリ が「プレゼンを開始しました。」と応答するように設定します。 Fulfillmentの設定 Inline Editor を ENABLED に変更し、 index.js と package.json を以下のように設定します。 /** index.js **/ 'use strict' ; const firebase = require( "firebase" ); const functions = require( "firebase-functions" ); const DialogflowApp = require( "actions-on-google" ).DialogflowApp; // Firebaseへの接続情報 var config = { apiKey: "XXXXXXXXXXXXXXXXXXXXXX" , authDomain: "XXXXXXXXXXXXXXXXXXXXXX" , databaseURL: "XXXXXXXXXXXXXXXXXXXXXX" , projectId: "XXXXXXXXXXXXXXXXXXXXXX" , storageBucket: "XXXXXXXXXXXXXXXXXXXXXX" , messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX" } ; firebase.initializeApp(config); exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { var app = new DialogflowApp( { request, response } ); let actionMap = new Map(); // saveDataアクションを登録 actionMap.set( "saveData" , function (app) { // Firebaseにキーワードを保存する firebase.database().ref( "/googlehome" ).set( { word: "プレゼンを開始します" } ); } ); app.handleRequest(actionMap); } ); Firebaseへの接続情報 は Firebase の画面で ウェブアプリに Firebase を追加 をクリックして表示されるコードを使用します。 /** package.json **/ { "name" : "dialogflowFirebaseFulfillment" , "description" : "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase" , "version" : "0.0.1" , "private" : true , "license" : "Apache Version 2.0" , "author" : "Google Inc." , "engines" : { "node" : "~6.0" } , "scripts" : { "start" : "firebase serve --only functions:dialogflowFirebaseFulfillment" , "deploy" : "firebase deploy --only functions:dialogflowFirebaseFulfillment" } , "dependencies" : { "actions-on-google" : "^1.5.x" , "firebase" : "^4.8.0" , "firebase-admin" : "^4.2.1" , "firebase-functions" : "^0.5.7" , "apiai" : "^4.0.3" } } 設定後、 DEPLOY をクリックし Firebase へ スクリプト をデプロイします。 以上で Dialogflow の設定は完了です。 Firebaseの設定 Firebase の Database にデータ保存場所とアクセスルールを設定します。 データ保存場所の設定 Database > データ から保存場所を作成します。今回は /googlehome/word に作成します。 作成した保存場所には、PUTでデータを保存出来ます。 curl -X PUT \ https://XXXXXX.firebaseio.com/googlehome/word.json \ -H 'content-type: application/json' \ -d '"保存キーワード"' 保存したデータはGETで取得できます。 curl -X GET \ https://XXXXXX.firebaseio.com/googlehome/word.json \ -H 'content-type: application/json' アクセススールの設定 Database > ルール からDBへのアクセスルールを設定します。 今回は設定簡易化のため /googlehome 配下のデータには認証無しでアクセスできるように設定しています。 { "rules" : { "googlehome" : { ".read" : true , ".write" : true } , ".read" : "auth != null" , ".write" : "auth != null" } } 以上で Firebase の設定は完了です。 RaspberryPiの設定 以下の内容を スクリプト 化し、RaspberryPiのNode.js上で実行します。 GoogleHomeへのメッセージ通知 ディスプレイへのスライド表示通知 Firebase のDB更新通知の受信 ※RaspberryPiに初期インストールされているNode.jsはバージョンが古いので、最新の安定バージョンに更新する必要があります。 GoogleHomeへのメッセージ通知 GoogleHome任意のメッセージをプッシュするNode.js用ライブラリが公開されているので、ありがたく使わせていただきます。 → google-home-notifier ライブラリのインストール $ npm init $ npm install google-home-notifier 動作確認 /** testMessage.js **/ const googlehome = require( "google-home-notifier" ); const language = "ja" ; // デバイス設定(Google-Homeで始まる全デバイスにメッセージが通知される。) googlehome.device( "Google-Home" , language); // 通知するメッセージ googlehome.notify( "こんにちは。私はグーグルホームです。" , function (res) { console.log(res); } ); ※test.jsの 文字コード はUTF8で作成する。 $ node testMessage.js Device "Google-Home-XXXXXXXXXXXXXXXXXXXXXX" at 192.168.1.11:8009 <- 応答したGoogleHomeのデバイス名 Device notified ディスプレイへのスライド表示通知 スライドの表示には fbi というコマンドを使います。 コマンドのインストール $ sudo apt-get install fbi 動作確認 $ sudo fbi -nocomments -noverbose -a -T 1 -d {出力場所} {画像ファイルのパス} ※コンソール出力の場合は、{出力場所}に /dev/fb0 を、 HDMI 出力の場合は /dev/fb1 を設定します。 FirebaseのDB更新通知の受信 Firebaseのドキュメント を元に設定します。 ライブラリのインストール $ npm install firebase 動作確認 /** testFirebase.js **/ // Firebaseへの接続情報(Dialogflowの項目で設定したものと同じ) var config = { apiKey: "XXXXXXXXXXXXXXXXXXXXXX" , authDomain: "XXXXXXXXXXXXXXXXXXXXXX" , databaseURL: "XXXXXXXXXXXXXXXXXXXXXX" , projectId: "XXXXXXXXXXXXXXXXXXXXXX" , storageBucket: "XXXXXXXXXXXXXXXXXXXXXX" , messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX" } ; firebase.initializeApp(config); const path = "/googlehome" ; const key = "word" ; const db = firebase.database(); // 更新通知を受信した時の処理を記述 db.ref(path).on( "value" , function (changedSnapshot) { // 更新された値をログに表示 console.log( "取得キーワード:" + changedSnapshot.child(key).val()); } ); $ node testFirebase.js # Firebaseに「テスト」をPUTしてDBを更新する 取得キーワード:テスト index.js作成 実行ファイルとしてindex.jsを作成します。 /** index.js **/ const firebase = require( "firebase" ); const exec = require( "child_process" ).exec; const googlehome = require( "google-home-notifier" ); // メッセージを通知するGoogleHomeの設定 const language = "ja" ; const deviceName = "Google-Home-XXXXXXXXXXXXXXXXXXXXXX" ; googlehome.device(deviceName, language); // Firebaseへの接続情報を設定 var config = { apiKey: "XXXXXXXXXXXXXXXXXXXXXX" , authDomain: "XXXXXXXXXXXXXXXXXXXXXX" , databaseURL: "XXXXXXXXXXXXXXXXXXXXXX" , projectId: "XXXXXXXXXXXXXXXXXXXXXX" , storageBucket: "XXXXXXXXXXXXXXXXXXXXXX" , messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXX" } ; firebase.initializeApp(config); // GoogleHomeへメッセージを通知 var notifyGoogleHome = function (word) { googlehome.notify(word, function (res) { console.log(res); } ); } ; var totalSpeakTIme = 0; var speak = function (word, speakTime) { // 前回メッセージの終了後、メッセージを通知 setTimeout( function () { notifyGoogleHome(word); } , totalSpeakTIme); // 今回メッセージの秒数分カウントアップ totalSpeakTIme += (speakTime * 1000); } // ディスプレイへスライドの表示を通知 var notifyDisplay = function (imgPath) { // fbiコマンドを使ってディスプレイに画像を表示 exec( "fbi -nocomments -noverbose -a -T 1 -1 -d /dev/fb0 " + imgPath, function (err, stdout, stderr) { if (err) { console.log(err); } } ); } ; var totalDisplayTIme = 0; var show = function (imgPath, speakTime) { // 前回スライドの終了後、表示を通知 setTimeout( function () { notifyDisplay(imgPath); } , totalDisplayTIme); // 今回スライドの表示秒数分カウントアップ totalDisplayTIme += (speakTime * 1000); } // Firebaseの更新通知を受取った時の処理 const path = "/googlehome" ; const key = "word" ; const db = firebase.database(); db.ref(path).on( "value" , function (changedSnapshot) { // 更新された値を取得 const value = changedSnapshot.child(key).val(); if (value === "プレゼンを開始します" ) { // 開始キーワードの場合はプレゼン開始 // 登録された値をFirebaseから消しておく db.ref(path).set( {[ key ] : "" } ); // プレゼン中は何もしない if (totalDisplayTIme > 0 || totalSpeakTIme > 0) { return ; } // プレゼン開始メッセージをGoogleHomeに喋らせる speak( "承知しました。" + value, 10); // 1枚目のスライドを表示する show( "./img/001.jpg" , 15); // 以下、プレゼン用スクリプトを記述 } } ); ファイルが作成できたら、実行します。 $ sudo node index.js ※ fbi コマンドの実行時にroot権限が必要なので sudo 付きで実行します。 以上で RaspberryPi の設定は完了です。 実際にやってみる 設定完了後、 スマホ に対して「発表アプリにつないで」「プレゼン開始」と指示すると、index.jsに書かれた内容でGoogleHomeがプレゼンをしてくれます。 長いので冒頭部分だけですが、実際にこんな感じでGoogleHomeにプレゼンしてもらいました。 参考 Google Home開発入門 / google-home-notifier解説 Raspberry Pi でTFT液晶モジュールにいろいろ表示する *1 : Googleアシスタント と会話できるアプリ