GoogleHomeでポケモン図鑑作ってみた

Google Homeを購入したので、何か作ってみたくて作りました。
ざっと2時間程度で作れました。

GitHub: [google-home-pokemon-dictionary]
(https://github.com/reireias/google-home-pokemon-dictionary)

## 概要

- 「ポケモン図鑑 ピカチュウ」とGoogle Homeに聞くとポケモン図鑑の説明をしゃべってくれる

- 「ピカチュウ。ねずみポケモン。はじめて見るものには〜」というようにアニメのポケモン図鑑風に返してくれる
  - ただし、音声はGoogle Homeのそれ

## 構成
こんな感じです。
![GoogleHome.png](https://qiita-image-store.s3.amazonaws.com/0/214526/cfed2081-60ea-cf46-19d0-657cb23ff557.png)

IFTTTのGoogle Assistantを使うと、複雑なレスポンスを即座にGoogle Homeに喋らすことはできません。
そこで、MQTTというIoTデバイスでよく使われる軽量なプロトコルを利用して自宅PCに通知を送っています。

beebotteとい無料のMQTTホスティングサービスを利用し、自宅PC上のnodeからメッセージを監視しています。
メッセージにはポケモンの名前が渡ってきますので、[pokeapi](https://pokeapi.co/)というAPIサービスを利用して図鑑説明を取得しています。

最後はnodeの[google-home-notifier](https://github.com/noelportugal/google-home-notifier)を利用して、Google Homeに任意のセリフを喋らせています。

ちなみに、ほぼ「[IFTTTとBeebotteを使ってGoogleHomeからRaspberryPiを操作する](https://qiita.com/msquare33/items/9f0312585bb4707c686b)」の構成をパクっています。

## 動作環境
以下の環境で確認しています。

- Linux Mint 18.2
- node v9.5.0

## 構築手順

### BeeBotte
まずはGoogle Homeと自宅PCとをつなぐためのBeeBotteというMQTTのサービスを設定します。
と、言っても複雑な設定はなく、アカウントを作った後は、適当な名前のchannelとresourceを作成して終わりです。
以下のように作れるかと思います。

![Screenshot from 2018-02-15 21-55-18_censored.jpg](https://qiita-image-store.s3.amazonaws.com/0/214526/db68f807-67da-505f-e833-139bb8aad29b.jpeg)

**後で利用するのでChannel Tokenの値を控えておきます。**

### IFTTT
New Appletからappletを作成しています。

thisにはGoogle Assistantを利用します。
![Screenshot from 2018-02-15 21-47-06.png](https://qiita-image-store.s3.amazonaws.com/0/214526/fe00f863-6f9a-14d7-c150-dea48169e20d.png)

「任意のフレーズ + ポケモンの名前」で起動したいので、左から3番目の`Say a phrase with a text ingredient`を選択します。
![Screenshot from 2018-02-15 21-47-47.png](https://qiita-image-store.s3.amazonaws.com/0/214526/06e456f2-bd40-a207-0fa4-39e33f52fb42.png)

設定はこんな感じ。
![Screenshot from 2018-02-15 21-50-13.png](https://qiita-image-store.s3.amazonaws.com/0/214526/381c5a79-dab5-30be-ae34-aa9dcdcc1fc7.png)

あとの設定は適当で大丈夫です。(たぶん)

続いてthatの設定です。
BeeBotteのアプリはIFTTTには無いのでWebhooksを使います。
![Screenshot from 2018-02-15 21-52-37.png](https://qiita-image-store.s3.amazonaws.com/0/214526/d123aef5-4f72-03b7-da8c-4995a67fce6c.png)

設定は次のようになります。(BeeBotteの情報を入力します。)

| 項目         | 値                                                                                    |
|--------------|---------------------------------------------------------------------------------------|
| URL          | http://api.beebotte.com/v1/data/publish/${channel}/${resource}?token=${channel_token} |
| Method       | POST                                                                                  |
| Content Type | application/json                                                                      |
| Body         | {"data": "{{TextField}}"}                                                             |

### node
最後はBeeBotteからメッセージを取り出してポケモン図鑑の説明を取得し結果をGoogleHomeに通知する部分です。

ソースコードは次の通りです。

```javascript:main.js
'use strict';
const beebotte = require('beebotte');
const config = require('config');
const fs = require('fs');
const googlehome = require('google-home-notifier');
const request = require('request');

const searchUrl = 'https://pokeapi.co/api/v2/pokemon-species/';
const nameIdMap = JSON.parse(fs.readFileSync('./pokemon.json', 'utf-8'));

/**
 * main function
 */
const main = () => {
    googlehome.ip(config.googlehome.ip, config.googlehome.language);
    subscribe();
};

/**
 * subscribe beebotte channel
 */
const subscribe = () => {
    let transport = {
        type: 'mqtt',
        token: config.beebotte.token,
    };
    let channel = config.beebotte.channel;
    let resource = config.beebotte.resource;
    let client = new beebotte.Stream({transport: transport});

    client.on('connected', () => {
        client.subscribe(channel, resource, (message) => {
            let name = message.data;
            search(name, notify);
        }).on('subscribed', (sub) => {
            console.info('subscribed.');
        });
    });
};

/**
 * search flavor text from pokeapi
 * @param {string} name The name of target
 * @param {function} callback The callback function
 */
const search = (name, callback) => {
    if (nameIdMap[name]) {
        let options = {
            url: searchUrl + nameIdMap[name],
            json: true,
        };
        request.get(options, (error, response, body) => {
            if (response.statusCode == 200) {
                let message = createNotifyMessage(body);
                callback(message);
            } else {
                console.error(response);
                callback('エラーが発生しました。');
            }
        });
    } else {
        callback(name + 'は見つかりませんでした。');
    }
};

/**
 * create message for notify google home
 * @param {object} body Search response body (json)
 * @return {string}
 */
const createNotifyMessage = (body) => {
    let language = config.pokeapi.language;
    let ftLanguage = config.pokeapi.flavorText.language;
    let version = config.pokeapi.flavorText.version;
    let names = body.names.filter((name) => language == name.language.name);
    let genera = body.genera.filter((genus) => language == genus.language.name);
    let flavorTexts = body.flavor_text_entries.filter((text) => {
        return ftLanguage == text.language.name && version == text.version.name;
    });
    let message = names[0].name + '。' + genera[0].genus + '。'
        + flavorTexts[0].flavor_text;
    return message;
};

/**
 * notify to google home
 * @param {string} message The message for notification
 */
const notify = (message) => {
    console.info(message);
    googlehome.notify(message, (response) => {
        console.info(response);
    });
};

if (require.main === module) {
    main();
}
```

configファイルの中身はこんな感じです。
GoogleHomeのIPやBeeBotteの各種設定値を書きます。

```yaml:config/default.yaml
googlehome:
  ip: 'TODO your google home ip'
  language: 'ja'
pokeapi:
  language: 'ja'
  flavorText:
    language: 'ja'
    version: 'omega-ruby'
beebotte:
  channel: 'your channel'
  resource: 'your resource'
  token: 'your token'
```

BeeBottからのメッセージの取得は`beebotte`モジュールを使っています。

利用するAPIの都合上、ポケモンの名前 -> 図鑑番号に変換するため、対応が書かれたローカルファイルを読み込んで変換しています。

Google Homeへの通信はgoogle-home-notifierを使って行っています。
自宅の環境の都合でIP指定で接続していますが、デバイス名でも接続できるようです。

## 動かしてみる

起動

```sh
$ node main.js
```

私 「OK Google、ポケモン図鑑 ピカチュウ」

Google Home 「ピカチュウ。ねずみポケモン。はじめて見るものには電撃を当てる。黒こげのきの棒が落ちていたらそれは電撃の強さを間違えた証拠だよ。」

やったぜ！

## 最後に
ラズパイ買わなきゃ