Google HomeにNetatmoで測った部屋の温度・湿度・二酸化炭素濃度をしゃべらせる

# 概要

Google Homeを導入してから音声IFで色々できるのは便利だなーと思いながら使っています。
せっかくなので、Netatmoで室温・湿度・二酸化炭素濃度をGoogle Homeにしゃべらせられないかやってみました。Netatmoは直接のUIを持たないので、アプリを開かないと温度・湿度・二酸化炭素濃度が見られなくて不便なのが解決しそうです。

# 開発の準備

[チュートリアルが充実している](https://developers.google.com/actions/dialogflow/first-app) ので、普段使ってるGoogle AccountにGoogle homeが紐付いていれば、チュートリアルに従って幾つかの開発用の環境（Firebase、Diagflow）とヒモ付をしてあげるだけでした。

# 開発

## Google Homeに喋らせるまで

### Webhook用のFullfillment開発

定型文を応答させるだけならコードを書く必要はないのですが、定型文を応答させたいことってあまりないような気がします。回答を変数に入れてそれを使う、みたいなことはできるっぽい。

何か動的に文章を組み立てて応答させたいときはバックエンド側にFunction (AWS lambdaみたいなやつ？）をデプロイして、そのAPIを叩くようにします。

といってもサンプルのコードを少しいじって、

```
firebase deploy --only functions
```

とするだけでデプロイされてAPIエンドポイントも生えます。なんて便利なんだ。

[以前のようにNetatmoから測定値を取得](https://qiita.com/anzaitetsu/items/60a99e5a631fa99b0ac3) して、文章を組み立てて返すだけの簡単なコードを書いてデプロイ。

```js:index.js
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//  response.send("Hello from Firebase!");
// });

'use strict';

process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').DialogflowApp;
const functions = require('firebase-functions');


var https = require('https');
const querystring = require('querystring');

var params = querystring.stringify({
    'access_token': 'xxxxx',
    'device_id': '70:ee:50:xx:xx:xx',
    'scale': "max",
    'type': "Temperature,CO2,Humidity,Pressure",
    'date_end': "last"
});

const util = require('util')


var options = {
    hostname: 'api.netatmo.com',
    path: '/api/getmeasure',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
};

// a. the action name from the make_name Dialogflow intent
const NAME_ACTION = 'make_name';
// b. the parameters that are parsed from the make_name intent
const COLOR_ARGUMENT = 'color';
const NUMBER_ARGUMENT = 'number';


exports.NetatmoHome = functions.https.onRequest((request, response) => {
  const app = new App({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));


  // c. The function that generates the living env 
  function makeName (app) {
    var callback = function(response) {
        response.on('error', function(e) {
            console.log('error', e);
        });
        var res = '';

        response.on('data', function (chunk) {
            res += chunk;
        });

        response.on('end', function () {
            res = JSON.parse(res);
            if (response.statusCode == '200') {
                var data = res.body;
                console.log(util.inspect(data, false, null))

                // google home says
                // app.tell('リビングの温度は' + data[0]['value'][0][0] + "℃です");

                app.tell('リビングの温度は' + data[0]['value'][0][0] + "℃。湿度は" + data[0]['value'][0][2] + "%。二酸化炭素濃度は" + data[0]['value'][0][1] + 'ppmです。');
            } else {
                console.log('status code:', response.statusCode, '\n', res);
                app.tell('エラーです');
            }
        });
    };

    var req = https.request(options, callback);
    req.on('error', function(e) {
        console.log('There is a problem with your request:', e.message);
        app.tell('エラーです'+e.message);
    });

    req.write(params);
    req.end();

  }
  // d. build an action map, which maps intent names to functions
  let actionMap = new Map();
  actionMap.set(NAME_ACTION, makeName);


app.handleRequest(actionMap);
});
```

### Fullfillment設定

Fullfillment -> webhookでデプロイしたエンドポイントを指定すると、シミュレータで応答を確認できるようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/54356/50517459-4444-08f1-823d-7b72d1c61e44.png)

驚いたのが、この時点で家の実機のGoogle Homeもこの応答をするようになってました。なんて便利なんだ。
シミュレータと実機の開発がシームレスにつながってできる感じ。


# わからないこと、課題

- うちのNetatmoにしか対応してない

この開発した状態だと、うちに設置してあるNetatmoの設定値を読み上げることしかできないので、Publicにリリースできません。開発したアプリごとにAccessTokenみたいな設定値を設定できるような仕組みがよくわからずコードに埋め込んでいます。

このあたりパブリックに配布するにあたって個別のAPI keyとかが必要な場合ってどうやるんだろう？

- 起動するのが不便

Google Homeのネイティブアプリのように、「ねぇGooogle、今日の天気は？」みたいな起動のやり方ができなくて、「ねぇGoogle、テスト用アプリに繋いで」「部屋の温度は？」みたいに一度「テスト用アプリに繋いで」というステップが必要です。前述のパブリックにしたアプリならできるような気がするのですが、「ねぇGoogle、部屋の温度は？」って聞きたい。

- デバッグしづらい

デプロイ後に通信系のエラーが起きたりするとデバッグしづらいです。
ローカルで動かす方法もよくわからなかった。
ハマったのは無料プランだとFunctionから外部APIを呼べないことで、有料プランを契約する必要がありましたorz もっとも、従量制だから家庭用ならほぼタダで使えます。

# 所感

Google Homeに喋らせること自体はBaaSも全部揃ってるのでただ少しコードを書くだけでとても簡単でした。
このあたりのサーバのセットアップとかが一切いらない形でサービス開発に集中できるようになってるのは素晴らしいですね。

ただ、このぐらいの簡単なIntegrationはコードなしでもできてほしいですね。おそらくすぐにそういうふうになるのでしょうけど、IFTTTみたいにポチポチしたらすぐにGoogle Homeから読み上げられる、ぐらいの簡便さであってほしいなと思いました。

AlexaことAmazon echoも楽しみにしているのですが未だに招待メールが来ません。これまでAmazonにすごい金額をつぎ込んできたのになぜだ。