いろんなスマートスピーカーを使ってみた

この記事は、[おうちハック Advent Calendar 2017](https://qiita.com/advent-calendar/2017/ouch-hack)の16日目の記事です。

#1. はじめに

自作の照明制御サービスを作って、スマートスピーカーと連携させてみましたので、その内容をここでまとめていきます。

#2. スマートスピーカーの概要

説明するまでもないでしょうが、スマートスピーカーとは対話型の音声操作に対応したスピーカーのことです。たまたにAIスピーカーと表現されている場合がありますが、スピーカー自体にAIが搭載されているわけではないです。AIスピーカーがというかたは[こちら](https://aiyprojects.withgoogle.com/)をどうぞ。

さて、本題。

現在、我が家では以下の４つのスマートスピーカーを使っています。

|名称|会社|サービス開発プラットフォーム|赤外線|
|---|---|---|---|
|[Clova WAVE](https://clova.line.me/)|Line|Clova Extension Kit|あり|
|[Echo dot](https://www.amazon.co.jp/Amazon-Echo-Dot-New%E3%83%A2%E3%83%87%E3%83%AB-%E3%80%81%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF/dp/B072B5BTLK/)|Amazon|Alexa, Lambda|なし|
|[Google Home/Google Home mini](https://store.google.com/product/google_home)|Google|Action on Google, Dialogflow|なし|
![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/59e7fdb8-f538-7bb1-3bf2-70ab64308206.png)

Clova Waveは唯一、赤外線を搭載しており、この記事を書いている時点(2017.12上旬)ではテレビ（電源・チャンネル・音量）と照明（オンオフ）が可能です。ただ、学習リモコンではないので、公式が対応したものだけになります。残念ながら我が家の照明リモコンには対応していませんでした。がっくり。

Clova Waveのサービス開発プラットフォームであるClova Extension Kitは、現状では一般ユーザには解放されていないので、Clova Waveでは自作のサービスを使うことができません。

私はもってませんが、[Echo Plus](https://www.amazon.co.jp/Amazon-Echo-Plus-New%E3%83%A2%E3%83%87%E3%83%AB-%E3%80%81%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%E3%83%9B%E3%83%BC%E3%83%A0%E3%83%8F%E3%83%96%E5%86%85%E8%94%B5%E3%80%81%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF/dp/B01J4IY8WE/)はスマートホーム・ハブを内蔵しZigBeeでの通信が可能です。ただし、発売時に対応しているのはPhilipseのHueのみ。Hueだけなら、Hueブリッジを買えば他のAmazon Echo機種やGoogle Homeでもつなげられますが、EchoとEcho Plusの価格差は6000円、Hueブリッジの価格は7400円とみるとお得ではあります。Hueブリッジにしておくと、他機種に乗り換えた際（って携帯電話会社の話みたいですが）も使えますがね。

#3. 家の照明のオン・オフをしてみる

残念ながらClova WAVEは手段がないのですが、Amazon EchoとGoogle Homeについては外部サービスと連携することができます。

そこで、ラズベリーパイをベースに赤外線リモコンを制御するサービスを作成し、スマートスピーカーと連携させてみます。お手軽にやるなら、[IRKit](http://getirkit.com/)やその後継の[Nature Remo](http://nature.global/)、[i-Remocon](http://i-remocon.com/)をIFTTT連携させるのをお勧めします。

##3.1 全体構成

まず、全体のシステム構成は以下になります。
![system.jpg](https://qiita-image-store.s3.amazonaws.com/0/51272/60c44395-ea81-cb25-f880-2b6bf38c7842.jpeg)

そして、以下の３つの仕組みを作る必要があります。

![system2.jpg](https://qiita-image-store.s3.amazonaws.com/0/51272/16bc2940-c893-30c7-d697-7c4e4893ae51.jpeg)

|番号|処理概要|利用するプラットフォーム等|
|---|---|---|
|①|スマートスピーカーからの入力を処理し、外部サービスを呼び出す|Alexa/Lambda または Action on Google/Dialogflow|
|②|スマートスピーカーからの外部サービス呼び出しをラズベリーパイへ伝える|ngrok, AWS IoT, Beebotte|
|③|呼び出されたサービスを実行して照明を赤外線で制御する|Node.js(Express)|

##3.2 ラズベリーパイで照明をON/OFFするサービスを作る
まず最初に③のサービスを作ります。赤外線制御用のデバイスとしては、[「Raspberry Piではじめるおうちハック」](https://www.amazon.co.jp/dp/4839960682?tag=megane1600-22&camp=243&creative=1615&linkCode=as1&creativeASIN=4839960682&adid=0QF26R0HWGETBEHVPG47&)で扱った[irMagician](http://www.omiya-giken.com/?page_id=837)を使用します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/699bff81-b19d-d79a-d4f0-5a3c67dfb721.png)


今回、Node.jsは8.9.1を使用しました。サービスはNode.js上でExpressを使用して作成します。

###3.2.1. irMagicianをNode.jsで制御する

irMagicianはNode.jsのモジュール[irmagician](https://www.npmjs.com/package/irmagician)使用します。モジュールの使い方は[モジュール作者さんのサイト](https://qiita.com/tamaki/items/f4127249ec211f7d70cd)がありますので、そちらを参考に。

照明のONとOFFの赤外線コマンド情報をそれぞれ`light_on.json`と`light_off.json`に保存しておきます。そして、これらを使ってオンとオフを実行するためのコード`lightModule.js`を作成します。`light_on.json`と`light_off.json`は`lightModule.js`を同じディレクトリに置いておきます。

```lightModule.js
'use strict'

exports.on = function(){
    var irmagician = require('irmagician');
    irmagician.write('light_on.json');

    console.log("light on");

    setTimeout( function(){
        irmagician.play();
    }, 2000);
}

exports.off = function(){
    var irmagician = require('irmagician');
    irmagician.write('light_off.json');

    console.log("light off");

    setTimeout( function(){
        irmagician.play();
    }, 2000);
}
```
これを必要なところで

```js
  var light = require('lightModule.js')
  light.on();
```
というように呼び出します。

###3.2.2. Expressを準備する

[Express](http://expressjs.com/ja/)は、Node.js向けのWeb アプリケーション・フレームワークです。簡単にWebサービスを作成して実行することができます。

まずはExpressをグローバルにインストールします。

```
npm install express-generator -g
```

その後、

```
express --view=pug kadenCtrl
```
とすると`kadenCtrl`ディレクトリが作成され、その配下に必要なファイル一式が生成されます。`--view=pug`はつけなくても今回の範囲では問題ありませんが、つけないと実行時にワーニング(JadeはPugになったので云々）が出ます。

```
cd kadenCtrl
npm install
```
で準備ができました。次に、公開するサービスのコードを書きます。

###3.2.3. サービスを作成して公開する

次に、呼び出すサービスのパス(URL)を決定します。

Amazon Echo(スマートホームスキルAPI使用)の場合とGoogle Homeの場合では、呼び出しの粒度に若干の差がでてきます。

例えば、「XXXをつけて」という命令を出したとします。XXXが操作対象物（今回は照明機器）の名称です。

詳細は後述しますが、Alexa(Amazon Eco)でスマートホームスキルAPIを使う場合、AlexaからはXXXについて`TurnOn`というメッセージがLambda側に通知されます。一方、Google Homeの場合は、Dialogflowにはそのまま「XXXをつけて」というメッセージが通知され、Dialogflowで"XXX"と"つける"に分割してそれぞれをラズベリーパイ側にパラメータとして送信することができます。

このイメージを、照明機器を`light`、制御サービスを`KadenCtrl`として、そのままでパス(URL)を決めるとするとして、以下の２つを用意しました。

|対象|パラメータ1|パラメータ2|パス(URL)|メソッド|メッセージタイプ|呼び出し元|
|---|---|---|---|---|---|---|
|light|on/off|-|/lights|GET|-|Lambda|
|KadenCtrl|light|on/off|/kadenCtrl|POST|JSON|Dialogflow|

Lambda側からの呼び出しは自作するのでどのようにもできるのですが、Google HomeがPOSTなので、対抗してGETにしてみました。RESTに従うなら、両方ともPUTになるとは思うのですが。

この２つのパス(URL)に対応したサービスの実装を行います。`KadenCtrl`ディレクトリ配下の`routes`ディレクトリに移動します。

ここに、`/light`の処理を実装する`light.js`と`/kadenCtrl`を実装する`kadenCtrl.js`を作成します。先ほど作成した、lightModule.js等のファイルは`KadenCtrl`ディレクトリ配下に配置します。

```light.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  var act = req.query.action;
  if(act == "on"){
    var light = require('../lightModule.js');
    light.on();
  } else {
    var light = require('../lightModule.js');
    light.off();
  }

  var ret = {"num" : 1, "location" : "Living room"};
  res.header('Content-Type', 'application/json; charset=utf-8');
  res.send(ret);
});

module.exports = router;
```

```kadenCtrl.js
var express = require('express');
var router = express.Router();

router.post('/', function(req, res, next) {
  var ret = "";
  if(req.body.result.parameters["KadenCtrl"] == "照明"){
    if(req.body.result.parameters["KadenCtrl1"] == "つけて"){
      var light = require('../lightModule.js')
      light.on();
      ret = {"speech" : "つけたよ", "displayText": "つけたよ"};
    } else {
      var light = require('../lightModule.js')
      light.off();
      ret = {"speech" : "けしたよ", "displayText": "けしたよ"};
    }
  }
  res.header('Content-Type', 'application/json; charset=utf-8');
  res.send(ret);
});

module.exports = router;
```

つぎに、`KadenCtrl`ディレクトリ配下のapp.jsを編集し、先ほど作成したファイルの登録処理

```js
var lights = require('./routes/lights');
var kadenCtrl = require('./routes/kadenCtrl');
```

と、そのオブジェクトへのルート設定

```js
app.use('/lights', lights);
app.use('/kadenCtrl', kadenCtrl);
```

を追加します。結果、以下のようになります。

```app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var index = require('./routes/index');
var users = require('./routes/users');
var lights = require('./routes/lights');
var kadenCtrl = require('./routes/kadenCtrl');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);
app.use('/users', users);
app.use('/lights', lights);
app.use('/kadenCtrl', kadenCtrl);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
```

そして、KadenCtrlディレクトリ配下に移動して、

```
npm start
```

と入力します。

```
pi@raspberry:~/kadenCtrl $ npm start

> kadenctrl@0.0.0 start /home/pi/kadenCtrl
> node ./bin/www
```

と表示されれば、サービスが正常に起動されています。

##3.3. 外部サービス呼び出しをラズベリーパイへ伝える

次に②ですが、このラズベリーパイのサービスを外部から呼び出すのが一番やっかいです。が、これを簡単に行うことができるサービスが[ngrok](https://ngrok.com/product)です。

###3.3.1. ngrokのインストール

インストールは簡単。[downloadページ](https://ngrok.com/download)からLinux ARMをダウンロードします。ターミナルから入手するなら、wgetをインストールした後に、

```
sudo apt install wget
```
wgetを使って直接取得します。URLは変更になるかもしれないので[downloadページ](https://ngrok.com/download)で確認してください。

```
wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip
```

その後、zipを解凍します。

```
unzip ngrok-stable-linux-arm.zip
```

`ngrok`という実行ファイルができていれば完了です。


###3.3.2. ngrokでサービスを公開する

Expressの3000ポートを公開します。

```
$ ./ngrok http 3000
```
と入力すると、以下のようにサービスへのアクセスURLが画面に表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/8a3e844e-bce5-cb4b-6ed7-4ec269be49cd.png)


このURLを呼び出し元に設定すればよいです。Google Homeの場合、呼び出し元として使用するDialogflowのWebhookでは、Google Assistantと連携する場合にはhttpsしか指定できません。また、メソッドはPOSTになります。

無料版では一時的なURL（終了すると無効になって次回は別のものが割り当てられる）ですが、有料版（5$/monthから）ではngrok.ioのサブドメンを使用することができます。

注意しなければならないのはセキュリティ。家の中にあるラブベリーパイのサービスを簡単に呼び出せるということは、イコール他人にも簡単に呼び出せるということです（URLを知っていればですが）。無料でも[Basic認証](https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC)が利用できますので、テストや検証で一時的に使う時以外は設定しましょう。ただし、Basic認証は盗聴や改竄が簡単な部類ですので、最終的には有料のTLSを活用するとか、他のセキュリティを確保した方法を採用した方がよいかと思います。私はここにMQTT(AWS IoTもしくはBeebotte)を使用することを考えています。とはいえ、注意するのがこの公開ポート１つだけでよいというのはお手軽ですね。

##3.4. スマートスピーカーからの入力を処理して外部サービスを呼び出す
最後に①の、スマートスピーカーからの入力を処理して外部サービスを呼び出す部分を作ります。当然ながら、これはAmazon EchoとGoogle Homeで使用する開発プラットフォームが異なります。

まだまだ勉強中ですが、現状での理解をざっくり図にすると以下のような感じでしょうか。
![middle.jpg](https://qiita-image-store.s3.amazonaws.com/0/51272/95095d4f-7dae-0b4f-a69e-e19d3ae64343.jpeg)

Alexaは使用するスキルの種類でLambdaとの境界線が変わります。カスタムスキル（カスタム対話モデル）を使用すると、会話制御の多くをLambdaで作る必要がありますが、カスタマイズができます。一方、スマートホームスキルAPIを使うと、会話制御をAlexa側でやってくれる感じです。今回は、スマートホームスキルAPIを使用しました。
また、Amazon EchoではAlexaにデバイス管理の機能もあり、そのためのデバイス発見の対応処理がLambda側に必要になります。

Google Homeのケースでは、Google Assistant SDKの機能を使うのですが、その多くをAction on Googleがラップしてくれる感じかと思います。そのため、Google Assistant SDKを直接さわることはありませんでした。

###3.4.1. Amazon Echoの場合

まず、[Login with Amazon](https://developer.amazon.com/ja/login-with-amazon)にログインします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/ad4e093b-d4f5-d557-8e4e-e6e9be6a52f0.png)

ここで、OAuth2のクレデンシャルを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/d504ae10-7f39-0389-bb7a-c6d81c5bf02a.png)

|項目|内容|
|---|---|
|Security Profile Name|プロファイル名。例えばAlexaKadenとか。|
|Security Profile Description|プロファイルの説明。今回は適当で大丈夫。|
|Consent Privacy Notice URL|今回は適当で大丈夫。http://www.example.com/privacy.html とか。|

入力後にSaveすると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/cad7f03a-94a2-5a59-28de-13d1159a8718.png)

`Client ID`と`Client Secret`が作成されるのでメモっておく。

ここからは、公式の[スマートホームスキルの作成手順](https://developer.amazon.com/ja/docs/smarthome/steps-to-build-a-smart-home-skill.html)を参考にしてますので、詳細はそちらを参照ください。

[Alexa skills kit(ASK)](https://developer.amazon.com/ja/alexa-skills-kit)にログインし、新しいスキルを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/7eb43be2-7d61-20a2-896d-f1e36e18b382.png)

|項目|内容|
|---|---|
|スキルの種類|スマートホームスキルAPI|
|言語|Japanese|
|スキル名|KadenCtrlとしました|
|ペイロードのバージョン|v3|

以上を入力して保存すると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/e6309c5f-2150-9d4a-f0c7-de8ced7307d7.png)

`アプリケーションID`が作成されるのでメモっておく。次を選択すると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/da6218aa-9398-d008-1003-35a2cc6d74d7.png)

対話モデルの設定になるが、スマートホームスキルAPIを選択した場合は何もすることはなく次へ。

ここで、いったんASKをそのまま置いておき、IAMロールの作成とLambdaの設定をします。

まずはIAMロールを作成します。これについては、[こちら](https://developer.amazon.com/ja/docs/smarthome/steps-to-build-a-smart-home-skill.html#create-an-iam-role-for-lambda)を参考に作成します。

次に、Lambdaに移動します。まずリージョンを選択します。日本語対応のスキルではOregonリージョンを選択します。次に、新しく関数を作成し、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/f0029a8b-572a-832d-f689-75302c194593.png)

設計図を選択して、検索欄に`home-skill`と入力して検索すると、`alexa-smart-home-skill-adapter`が見つかるので選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/e7764574-0e8f-d073-cbe9-9ff7e212f7df.png)

|項目|内容|
|---|---|
|名前|KadenCtrlとしました|
|ロール|既存のロールを選択|
|既存のロース|先ほど作成したIAMロールを選択|
|アプリケーションID|先ほどASKで作成したアプリケーションID|
|トリガーの有効化|チェックボックスにチェックをつけて有効にする|

入力後に作成を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/0407bc6a-a3b5-11f9-189a-05e8ec14ce2d.png)

|項目|内容|
|---|---|
|ランタイム|Node.js 6.10|

と設定します。また、右上にARNが記載されているのでメモっておきます。

次にLambdaのコードを記載します。

Lambdaのコードは[こちら](https://developer.amazon.com/ja/docs/smarthome/steps-to-build-a-smart-home-skill.html#create-a-lambda-function)を参考に作成しました。

また、サービス呼び出し用に、Node.jsのHTTP APIを使用しました。オフの処理は未実装です。http.get()のURLはngrokで表示されているものに変更してください。

```js
var http = require ('http');

exports.handler = function (request, context) {
    if (request.directive.header.namespace === 'Alexa.Discovery' && request.directive.header.name === 'Discover') {
        log("DEGUG:", "Discover request",  JSON.stringify(request));
        handleDiscovery(request, context, "");
    }
    else if (request.directive.header.namespace === 'Alexa.PowerController') {
        if (request.directive.header.name === 'TurnOn' || request.directive.header.name === 'TurnOff') {
            log("DEBUG:", "TurnOn or TurnOff Request", JSON.stringify(request));
            handlePowerControl(request, context);
        }
    }

    function handleDiscovery(request, context) {
        var payload = {
            "endpoints":
            [
                {
                    "endpointId": "demo_id",
                    "manufacturerName": "Smart Device Company",
                    "friendlyName": "Kaden Control",
                    "description": "Smart Device Switch",
                    "displayCategories": ["SWITCH"],
                    "cookie": {
                        "key1": "arbitrary key/value pairs for skill to reference this endpoint.",
                        "key2": "There can be multiple entries",
                        "key3": "but they should only be used for reference purposes.",
                        "key4": "This is not a suitable place to maintain current endpoint state."
                    },
                    "capabilities":
                    [
                        {
                          "type": "AlexaInterface",
                          "interface": "Alexa",
                          "version": "3"
                        },
                        {
                            "interface": "Alexa.PowerController",
                            "version": "3",
                            "type": "AlexaInterface",
                            "properties": {
                                "supported": [{
                                    "name": "powerState"
                                }],
                                 "retrievable": true
                            }
                        }
                    ]
                }
            ]
        };
        var header = request.directive.header;
        header.name = "Discover.Response";
        log("DEBUG", "Discovery Response: ", JSON.stringify({ header: header, payload: payload }));
        context.succeed({ event: { header: header, payload: payload } });
    }

    function log(message, message1, message2) {
        console.log(message + message1 + message2);
    }

    function handlePowerControl(request, context) {
        // get device ID passed in during discovery
        var requestMethod = request.directive.header.name;
        // get user token pass in request
        var powerResult;

        if (requestMethod === "TurnOn") {
            log("DEBUG", "*** TurnOn2 *** ");
            
            http.get("http://xxxxxxxxx.ngrok.io/lights?action=on", function(res) {
                log("DEBUG", "Got response: ", res.statusCode);

                res.on("data", function(chunk) {
                    powerResult = "ON";
                    doResponse(powerResult)
                    context.done(null, chunk);
                });
            }).on('error', function(e) {
                context.done('error', e);
            });
            
        }
       else if (requestMethod === "TurnOff") {
            log("DEBUG", "*** TurnOff2 *** ");

　　　　　　 // TO DO

            powerResult = "OFF";
            doResponse(powerResult)
        }
    }
    
    function doResponse(powerResult){
        var contextResult = {
            "properties": [{
                "namespace": "Alexa.PowerController",
                "name": "powerState",
                "value": powerResult,
                "timeOfSample": "2017-09-03T16:20:50.52Z", //retrieve from result.
                "uncertaintyInMilliseconds": 500
            }]
        };
        var responseHeader = request.directive.header;
        responseHeader.namespace = "Alexa";
        responseHeader.name = "Response";
        responseHeader.messageId = responseHeader.messageId + "-R";
        var response = {
            context: contextResult,
            event: {
                header: responseHeader
            },
            payload: {}

        };
        log("DEBUG", "Alexa.PowerController ", JSON.stringify(response));
        context.succeed(response);
    }
};
```
handleDiscovery()は機器発見時に呼ばれます。ここにあるJSONの`friendlyName`キーに指定したものは呼び出す際の名前になるようです。音声入力があった際の処理をhandlePowerControl()で行っています。

ここでまた、ASKに戻ります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/17d4ca7c-79bd-5e7a-dd22-9586c778d7f5.png)

|項目|内容|
|---|---|
|デフォルト|先ほどLambdaで作成した関数のARN|
|エンドポイントの地理的リージョンを設定しますか？|いいえ|
|認証URL|Login with Amazonの場合は、https://www.amazon.com/ap/oa?redirect_uri=... redirect_uriはリダイレクトURLの項目に書かれているものを指定|
|クライアント ID|Login with Amazonで作成したClient ID|
|スコープ|profile|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/a3cb25ca-e391-c17a-f4e1-c95c711671a7.png)

|項目|内容|
|---|---|
|認可の承諾タイプ|Auth Code Grantを選択|
|アクセストークンURL|https://api.amazon.com/auth/o2/token|
|クライアントシークレット|Login with Amazonで作成したClient Secret|
|クライアント認証スキーム|HTTPベーシック認証（推奨）|
|ユーザーにリソースと機能へのアクセス権限をリクエストする|チェックをつけない|

入力して次へ。これで完成です。

Alexaに「Kaden Controlをつけて」というと、ラズベリーパイの照明ON処理が動きます。Alexaからは「はい」という応答がかえってきます。

###3.4.2. Google Homeの場合

まず、[Action on Google](https://console.actions.google.com/)にログインします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/164d3551-a2b0-0e2f-8b0d-30f25d008e14.png)

次に、"Add/import project"を選択すると、プロジェクト作成ダイアログが出るので、プロジェクト名とリージョンを入力します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/bf0962f5-2a8a-4e76-fa26-0c0c787ecb77.png)

次に、Dialogflow(のBUILDボタン)を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/e029227c-012a-2a35-9688-2e07ea402ea3.png)

すると、以下のダイアログが表示されますので、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/8eaf1a97-a65f-be2a-b13a-27062dc09d95.png)


「CREATE ACTIONS ON DIALOGFLOW」を選択すると以下の画面になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/1a483d79-0d4b-1e5c-d234-d47d00288b93.png)

言語を日本語にしてCREATEを作成します。

次に、`Entites`と`Intents`を設定します。

まずは`Entites`を設定します。認識させたい単語を記載します。`Define synonyms`にチェックを入れると、
同義語(synonym)を登録することができます。今回は、「照明」「つけて」「けして」を登録しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/b747a1d7-0f9b-17db-f553-dc45641ca0d6.png)

次に`Intents`を設定します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/d7ce2d7d-0d97-0f22-c1a2-29e8cecb8d78.png)

Actionで設定した内容がパラメータとして付与されます。

次に、`Fulfillment`を選択し、Webhookを有効にして、URLにngrokで表示されたもの+"/kadenCtrl"を記載します。httpsを指定する必要があることに注意ください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/01f03dd7-d47e-01fb-f6f1-8fdce156a09e.png)

再度、`Intents`に戻って`Use webgook`にチェックを入れます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/b0d888ca-c45d-85f7-3df1-0e2d8cb255d6.png)

その後、`Integrations`に移動して`Google Assistant`を選ぶと以下のダイアログが出ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/ead849f4-f61f-7fd4-2c14-1798b3014706.png)

ここの`Additional triggering intents`にて先ほどの`intents`を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/51272/433254e9-1ecf-e73c-e11e-7d34ae8d8b33.png)

これで完成です。

Google Homeに
　私：「OK, Google. テスト用アプリにつないで。」
と言うと
　Google Home：「こんにちは」
という返答が返ればサービスが開始されてます。

その後は、
　私：「電気けして」
　Google Home：「けしたよ」
　私：「電気をつけて」
　Google Home：「つけたよ」
という感じで制御できます。

サービスを終了するには、
　私：「おしまい」
　Google Home：「すいません、おてつだいできません」
とう感じで終了になります。

サービス名（アプリ名）の「テスト用アプリ」はAction on Googleの設定で変更できます。

#4. 最後に

今回、Amazon EchoとGoogle Homeの両方から、ラズベリーパイのサービスを呼び出してみました。

基本、どちらも同じようなサービスを作ることができると思いますが、実現の仕組みの違いから、サービス形態によっては作りやすい・作りにくいがでる気がします。

当然ながら、Amazon EchoはAWSのサービスをフルに活用できるのが魅力です。
一方、Google HomeはDialoflowの力でわりと簡単に会話処理ができますし、ラズベリーパイ（サービス処理側）にそのままパラメータとして内容を送ることができるのが魅力です。
ただし、Google Homeはサービスの開始に「ＸＸＸにつないで」と１ステップ入るので、一言で指示するようなサービス形式には向いてない気がします。もしかしたらやり方があるのかもしれませんが。

どちらにせよ、音声認識・合成を非常に簡単に使うことができるのは素晴らしいです。今後、どんどん活用していきたいと感じました。

（おしまい）