Edited at

Google Nest Hubの画面対応方法

2019年6月12日に日本でもGoogle Nest Hubが発売されました。日本初のGoogleアシスタント画面付きスマートスピーカーです。

やはり手に入れたからには、スマートスピーカーのアクション(スキル)を作りたいですよね!

開封の儀の記事はこちらを見てください。

https://note.mu/gaomar/n/n855952289aeb

画面付き対応はAlexaならAlexa Presentation Language(APL)を使ってリッチなコンテンツを作成することができます。Google Nest Hubも同様にSDKが公開されています。

Google Nest HubではInteractive Canvasという機能を使って実装していきます。

https://developers.google.com/actions/interactivecanvas/

■2019年7月22日

仕様が変わったので修正しました。

https://www.eisbahn.jp/yoichiro/2019/07/interactive_canvas_changes.html


完成動画

実際に組み込んだ動画です。BMI測定ボタンの画面はVue.jsで作成しています。身長と体重はDialogflowで処理を任せています。

結果画面で再度Vue.jsの画面に戻ってDialogflowから結果の値を取得して画面に反映させるという流れ。


1. Dialogflowの設定


1-1. Intentの設定

アクションを起動したら必ず最初に呼ばれるIntentを作成します。

Intent名はwelcomeとしました。Webhookを行うので、②のように有効化します。

s100.png

続いてBMI測定を行いたいので、BMIのIntentを作成します。

Intent名はbmiとしました。Training phrasesはbmiという言葉に反応させます。

パラメータは下記の通り

PARAMETER NAME
ENTITY
VALUE
PROMPTS

height
@sys.number
$height
身長をお答えください

weight
@sys.number
$weight
体重をお答えください

s101.png


1-2. Fulfillmentの設定

Inline Editorを有効にしてプログラムを記述します。HtmlResponseにスマートディスプレイ用の設定を記述します。

キー名
説明

url
画面に表示させたいホームページのURLを指定します

suppress
trueにするとマイクの応答が無くなります。falseにすると引き続きマイクの入力が有効になります

data
ここに渡したい値を設定します


index.js

'use strict';

const functions = require('firebase-functions');
const {dialogflow, HtmlResponse} = require('actions-on-google');

const app = dialogflow({debug: true});

// 起動時Intent
app.intent('welcome', (conv) => {
conv.ask('あなたのBMIを測定します。測定開始ボタンを押してください。');
conv.ask(new HtmlResponse({
url: `https://{表示させたいホームページのURL}`, /* 動作確認したい場合はこちらのURLを指定してください 「https://gaomar-nesthub-demo.netlify.com/」 */
suppress: true
}));
});

// BMI測定処理
app.intent('bmi', (conv, {height, weight}) => {
const bmiVal = (parseFloat(weight) / (parseFloat(height)/100 * parseFloat(height)/100)).toFixed(1);
var speechText = `あなたのBMIは${bmiVal}です。`;
conv.ask(speechText);
conv.ask(new HtmlResponse({
url: `https://{結果画面のURL}`, /* 動作確認したい場合はこちらのURLを指定してください 「https://gaomar-nesthub-demo.netlify.com/result」 */
suppress: true,
data: {
bmi: speechText,
},
}));
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);



1-3. package.jsonを編集

package.jsonは下記の設定をします。2019年6月現在、actions-on-googleはpreview版でしか動作しない模様です。

その他のバージョンやライブラリは下記を指定します。

※2019年8月1日に2.10.0が正式リリースされました。


package.json(抜粋)

  "dependencies": {

"actions-on-google": "2.10.0",
"firebase-admin": "~7.0.0",
"firebase-functions": "^2.2.0"
}



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": "8"
},
"scripts": {
"start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
"deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
},
"dependencies": {
"actions-on-google": "2.10.0",
"firebase-admin": "~7.0.0",
"firebase-functions": "^2.2.0"
}
}


2. Actions on Googleの設定

Actions on Googleの設定でInteractive Canvasを有効にしないと実際に使えません。


2-1. Interactive Canvasを有効化にする

左側メニューにあるDirectory informationをクリックします。Additional Information部分にある、カテゴリーを必ずGames & funにします。

このカテゴリーにすると、Interactive Canvasのチェックが出てくるのでこれを有効にします。

s102.png


3. 表示する画面の設定

Vue.jsで実装しています。デプロイ先はどこでも構いませんが、Netlifyが素晴らしいのでそちらにデプロイしています。


3-1. Vue.jsの設定

画面に表示する部分はVue.jsで実装しました。ドキュメントはこちらに詳しく掲載されています。

https://developers.google.com/actions/interactivecanvas/build/web-app

Interactive CanvasのAPIをindex.htmlに適用します。

12〜13行目でAPIを呼び出しています。UIライブラリーにVuetifyを設定しているので、余計なものもありますが、環境に応じて設定してください。


index.html(抜粋)

<script src="https://www.gstatic.com/assistant/immersivecanvas/js/immersive_canvas_api.js"></script>



index.html(全コード)

<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>nesthub-demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
<!-- Load Assistant JavaScript -->
<script src="https://www.gstatic.com/assistant/immersivecanvas/js/immersive_canvas_api.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but nesthub-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>


3-2. タイトルページ

アクション起動時に表示されているタイトルページの設定例です。単純にボタンを置いてるだけでのシンプルなページです。

キーポイントはボタンを押した時に実行されるinteractiveCanvas.sendTextQueryです。

これは、Google Assistant側にbmiという文字列を送っています。これは1-1で設定したbmiという言葉に反応して、BMI測定が開始してほしいためです。


src/components/HelloWorld.vue

<template>

<v-container>
<v-layout
text-xs-center
wrap
>
<v-flex mb-4>
<h1 class="display-2 font-weight-bold mb-3">
こんにちはNest Hub!
</h1>
<v-btn large color="success" @click="bmiStart">BMI測定開始</v-btn>
</v-flex>
</v-layout>
</v-container>
</template>

<script>
export default {
methods: {
bmiStart () {
// Google Assistantに 「bmi」 という文字列を送る
interactiveCanvas.sendTextQuery('bmi');
}
}
}
</script>



3-3. 結果画面

測定結果を表示させる画面です。キーポイントはinteractiveCanvas.ready(callbacks)です。

この画面が呼ばれたらcreated()部分が呼ばれます。これで、毎フレームonUpdate()が実行されます。

1-2で設定したFulfillmentのindex.jsでdataの値を投げている部分があったと思います。

そのdataで指定したbmiという値をここで受け取っています。


src/components/Result.vue

<template>

<v-container>
<v-layout
text-xs-center
wrap
>
<v-flex mb-4>
<h1 class="display-3 font-weight-bold mb-3">
【BMI測定結果】
</h1>
</v-flex>

<v-flex
xs12
mb-5
>
<h3 class="display-2 font-weight-bold mb-3">
// 結果を表示する
{{result}}
</h3>
</v-flex>

<v-flex
xs12
mb-5
>
<v-btn large color="success" @click="bmiStart">もう一度やる</v-btn>
</v-flex>

</v-layout>
</v-container>
</template>

<style>
</style>
<script>
export default {
data () {
return {
result: ''
}
},
created () {
var me = this
const callbacks = {
onUpdate(data) {
// bmiの値が飛んできたら取得する
if ('bmi' in data) {
me.result = data.bmi
}
},
}
interactiveCanvas.ready(callbacks)
},
methods: {
bmiStart () {
interactiveCanvas.sendTextQuery('bmi');
}
}
}
</script>



まとめ

シンプルな構成ですが、これでDialogflow ⇔ Actions on Google ⇔ Vue.jsの連携をすることができました。

カテゴリがゲームカテゴリーでないとInteractive Canvasが使えないのが辛いですが、ゲームの演出には使えるかと思いました。

ドキュメントにも記載されていますが、あくまで画面は補助的な役割をするので、メインはVUIということを忘れないようにしましょう!

AlexaのAPLよりやれることが増えているので、個人的にはInteractive Canvasの方が使いやすいと感じました。

今回使用したファイルはGitHubにあがっています。

https://github.com/gaomar/nesthub-demo

システム化のご検討やご相談は弊社までお問い合わせください。

https://i-enter.co.jp/contact/