Clova Wave を Google Home のサーヴァントとして使役する

この記事は、株式会社ACCESS Advent Calendar 2017の4日目の記事です！

# 何をしたの？

terminal からコマンドで Google Home を喋らせて Clova Wave に命令してみました。＼(^o^)／

# なぜそんなことを…？

Clova Wave は国内向けでは、他社スマートスピーカーに先行して発売されましたが、
依然として SDK が公開されてないため、いじって遊んだりライフハックに活用することができなかったのです。

そう、**SDK が公開されていなければエンジニアとて無力・・・**

そんな風に考えていた時期が僕にもありました。

![speakers.png](https://qiita-image-store.s3.amazonaws.com/0/38837/9d5ddc6c-d524-ba18-aae7-7c341b05c061.png)

そう、この **Clova Wave の隣に置いてある Google Home を利用** して、
Google Home から Clova へ指示を出せば、Clova を自由にコントロールできる。

それは **実質 SDK が公開されている** も同然…！

ククク、やったぜ！(ぐるぐる目)

# どんな力を使ったの？

[google-home-notifier](https://github.com/noelportugal/google-home-notifier) を利用しています。

## google-home-notifier とは一体・・・？

Chromecast Client 向けのプロトコルを利用することで、
**Google Home に任意のキーワードを喋らせたり、** 音声ファイルを再生させることができる Node.js ライブラリです。

実装当初は下記記事が詳しく参考にさせていただきました！
[帰宅したらGoogleHomeから好きな音声で「おかえり」って言ってもらいます](https://qiita.com/taaaaaaak22/items/abfb9f2a672689c20505)

現在は google-home-notifier 自体を直接解説されている記事もありますので、参考にされるととてもよいと思います。
[Google Home開発入門 / google-home-notifier解説](https://qiita.com/SatoTakumi/items/c9de7ff27e5b70508066)

## なぜ IFTTT に頼らなかった？

[布団から腕すら出さずに会社を休む [Google Home]](https://qiita.com/diescake/items/4f354a5dc7cb738efd4f) では、
IFTTT によるビジュアルプログラミング(といっていい？)によってコードを書かずに実現しましたが、**今回は IFTTT は利用できません。**

というのは、**IFTTT の "that" に Google Assistant が対応していない**からです。

Google Home をトリガーに何かをすることはできますが、
何かをトリガーに Google Home に喋らせるということができないのです。

なので、今回は素直に**コードを書く必要**があります。やったね！＼(^o^)／

# できたコード

近いうちにライブラリにして npm に登録する予定ですが、
ざっくり以下のようなコードを実装して、ローカルネットワークで動作確認しました。

* sample-google-home-notifier
    * https://github.com/aDaisukeKondo/sample-google-home-notifier

```js

const ghome = require('google-home-notifier');

ghome.ip('192.168.0.2'); // Google Home の IP アドレス
ghome.device('Google Home', 'ja');

// 下記配列に指定したキーワードを google-home が sequential に喋る
const phrases = [
  'くろーばあ', // (※1)
  '音楽を流して',
  'バイバイ' // Clova Wave は listen を継続するので念のため終了指示
];

const asyncNotify = async (aPhrase, aWait) => {
  return new Promise((resolve) => {
    ghome.notify(aPhrase, (aRes) => {
      const wait = aWait || 2000 + phrase.length * 500; // (※2)
      setTimeout(() => {
        resolve(aRes);
      }, wait);
    });
  });
};

const execNotifications = async (aPhrases) => {
  for (phrase of aPhrases) {
    await asyncNotify(phrase)
      .then((aRes) => {
        console.log(`"${phrase}" said: ${aRes}`);
      });
  }
};

execNotifications(phrases);
```

## くろーばあ？(※1)

実は google-home-notifier では、いつもの Google Assistant の "声" では喋ってくれず、
**google text to speech api が利用される**ようで、なんか機械的な声になります。

そのためなのか、素直に "クローバー" と喋らせても Clova Wave は反応したり、しなかったりとかなり不安定なのです。
そこで、どんなキーワードが最も認識率が良いのか、

くろうば、くろうばー、クローバー、クローバ、clova、clober、クロバー、苦労バー、苦労婆…

などなど、30～40種程度の組み合わせを試してみたところ、
**「くろーばあ」**これが圧倒的に認識率が高くベストという結論に辿り着きました。(大袈裟

## wait (※2)

これは sequential に通知されるワード間の wait[ms] です。
苦肉の策ですが、問題が少なかろうと思われる待ち時間をそれっぽく計算しています。

google-home-notifier は Google Home に notify を投げっぱなしで、
Google Home から callback を受け取る仕組みがないため、自身が指示した文字長から喋り終わるまでの時間を推測するしかないのです。

2000 ms というのは Google Home が喋り始めるまでの delay の測定値ですが、
当然ネットワーク環境によって変わってくるのでイケてない実装です。

phrase.length * 500 ms も、漢字やアルファベットを同列に扱ってしまっているので、
キーワードによっては大きく誤差が広がると思います。

ちなみに、wait が短い場合は、
Google Home が喋っている言葉が中断されて、次の言葉を喋り出すような動作になります。

### ではどうすれば…？

**Clova の SDK 公開を待ちましょう＼(^o^)／**

…で終わってしまうのもアレなのでちょっと考えてみました。
ここはやはり、第3のスマートスピーカーに頼る方法はどうでしょうか。

Google Home が1つのキーワードを喋り終わった後に、続けて Alexa へ伝えた旨の指示を出して、
Alexa がネットワーク経由でコールバックさせるとか・・。

スマートスピーカー3台も使って、どんだけ無駄なことをやっているんだという気持ちで胸がいっぱいになりそうですが、
個人的にはこういった**努力の方向音痴**の類の取り組みは好きなのでちょっと試してみたいところ（╹◡╹）

# 注意点

実装したコードは**ローカルネットワークでのみ動作確認**しています。

このままでは(も？)ユースケースが全く浮かばないので、
実際に何か活用するとすれば IFTTT から Clova を叩きたくなると思いますが、

そのためには IFTTT から reach できる ローカルサーバを立てる必要があるので、ちょっと面倒ですね。

google-home-notifier 自体にサーバーを立てる example が提供されていますので、
実現すること自体は難しくないとは思います。(動作未確認)

# 所感 & 雑記

ちゃんと、ライブラリ化して npm に登録してから記事にしようと思ってましたが、時間が取れず悔しい限りです。

ちなみに、微力ながら google-home-notifier.js にも Contribute しています。
実は OSS に Contribute するのは初めてだったのですが PR がマージされるの楽しく癖になりますねー。（╹◡╹）

それでは、明日は @aKatsuhiroMihara さんです！


# 再現環境

* Ubuntu 14.04 LTS
* google-home-notifier 1.2.0
* node v8.6.0