Help us understand the problem. What is going on with this article?

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/

h-takauma
様々なIoT機器の研究開発を行っています。 AWSの研究開発がメインです。LINE API Expert('19〜) 最近はスマートスピーカーに夢中です! 著書「スマートスピーカーアプリ開発入門」https://amzn.to/2o0KGWs
i-enter
「効果」をつねに提供します。スマホアプリ開発No.1の実績。最新のIoTに対応した開発も行います。
https://www.i-enter.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした