GoogleHomeスピーカーに外部からプッシュして自発的に話してもらいます

GoogleHomeスピーカから自発的話してもらうためにいろいろ細工をします。具体的にはraspberryPiからGoogleHomeスピーカーに話してほしいことを送ってしゃべってもらいます。
これができると、ある条件の時に好きなことをGoogleHomeからしゃべってもらえます。(例えば明日雨の場合に教えてくれたり、IFTTTを利用すると、ツイッターとかでリプライが来たら教えてくれます。)

2017/12/8 追記
・GitHub上の「google-home-notifier」のプログラムが更新されておりましたので、その更新に合わせて設定方法を見直しました。
※「google-home-notifier.js」に直接IPを記載しておりましたが、「example.js」から設定しています。

・「google-home-notifier」を詳しく知りたい場合は以下のページが参考になると思います。
Google Home開発入門 / google-home-notifier解説
※詳しい方が解説してくださるので理解が深まるのがQiitaのいい所ですね。

ざっくりな流れ

①Raspberry PiにGoogleHomeを操作するためのソフト(google-home-notifier.js)を導入
②自宅のGoogleHomeにリクエストを送るための設定を実施
③リスナーを実行して、RaspberryPi上にgoogle-home-notifierを常駐させる
④RaspberryPiにGoogleHomeにしゃべらせたいことをPOSTすることでGoogleHomeがしゃべる

必要な環境

・RaspberryPi(Node.jsとnpmを使います)
・GoogleHome

RaspberryPiに必要なものを導入

Node.jsとnpm

RaspberryPiにNode.jsとnpmを使うためにあらかじめインストールしておきます。以下のページを参照していただけばと思います。

https://qiita.com/mascii/items/77c685df65c4cbca9315

google-home-notifier

続いて、GoogleHomeを操作するための必要なプログラム「google-home-notifier」をGitHubから導入します。サイトは以下です。

https://github.com/noelportugal/google-home-notifier

上記プログラムは、前からあったChromecast Client向けのプロトコルを利用して、GoogleHomeにしゃべらせるという代物です。プロトコルの詳細はここを参照してください。

上記プログラムを導入します。リスナーを利用して実現するため、以下の通りのコマンドを実行してインストールします。

curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
sudo apt-get install nodejs
sudo apt-get install git-core libnss-mdns libavahi-compat-libdnssd-dev
git clone https://github.com/noelportugal/google-home-notifier
cd google-home-notifier
npm install

上記コマンドを実行することで、必要なパッケージ含めてインストールされます。

google-home-notifierの設定

本章では、自宅にあるGoogleHomeに接続するための設定を実行します。
設定を実施するにあたり、以下の条件および情報が必要です。
・GoogleHomeのIPアドレス(固定されている必要があります。)
  →今回は仮に192.168.1.10とします。

(2017/12/7追記)
「example.js」を編集します。今回はあらかじめ用意されているものを利用します。
以下の項目を編集します。
・serverPort
  →リスナーが利用するポートです。変更する場合は変更してください。
・deviceName
  →GoogleHomeのデバイス名を記載します。ただし、このデバイス名は任意で結構です。
  →今回は'ribihome'とつけてます。
・ip
  →GoogleHomeのIPを設定します。ここでは192.168.1.10を記載します。
・language
  →しゃべらせたい言語を設定します。日本語をしゃべらせるので、「ja」を設定します。本設定は全部で2箇所あります。
・googlehome.device(deviceName,language)
  →日本語設定を反映するため、上記命令文を2箇所に追加します。

example.js
var express = require('express');
var googlehome = require('./google-home-notifier');
var ngrok = require('ngrok');
var bodyParser = require('body-parser');
var app = express();
const serverPort = 8091; // default port

var deviceName = 'ribihome';
var ip = '192.168.1.10'; // default IP      //ここにIPを記載


var urlencodedParser = bodyParser.urlencoded({ extended: false });

app.post('/google-home-notifier', urlencodedParser, function (req, res) {

  if (!req.body) return res.sendStatus(400)
  console.log(req.body);

  var text = req.body.text;

  if (req.query.ip) {
     ip = req.query.ip;
  }

  var language = 'ja'; // default language code   //ここに「ja」を記載
  if (req.query.language) {
    language;
  }

  googlehome.ip(ip, language);
  googlehome.device(deviceName,language)       //ここに命令文を追加

  if (text){
    try {
      if (text.startsWith('http')){
        var mp3_url = text;
        googlehome.play(mp3_url, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will play sound from url: ' + mp3_url + '\n');
        });
      } else {
        googlehome.notify(text, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will say: ' + text + '\n');
        });
      }
    } catch(err) {
      console.log(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please GET "text=Hello Google Home"');
  }
})

app.get('/google-home-notifier', function (req, res) {

  console.log(req.query);

  var text = req.query.text;

  if (req.query.ip) {
     ip = req.query.ip;
  }

  var language = 'ja'; // default language code     //ここに「ja」を記載
  if (req.query.language) {
    language;
  }

  googlehome.ip(ip, language);
  googlehome.device(deviceName,language)        //ここに命令文を追加

  if (text) {
    try {
      if (text.startsWith('http')){
        var mp3_url = text;
        googlehome.play(mp3_url, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will play sound from url: ' + mp3_url + '\n');
        });
      } else {
        googlehome.notify(text, function(notifyRes) {
          console.log(notifyRes);
          res.send(deviceName + ' will say: ' + text + '\n');
        });
      }
    } catch(err) {
      console.log(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please GET "text=Hello+Google+Home"');
  }
})

app.listen(serverPort, function () {
  ngrok.connect(serverPort, function (err, url) {
    console.log('Endpoints:');
    console.log('    http://' + ip + ':' + serverPort + '/google-home-notifier');
    console.log('    ' + url + '/google-home-notifier');
    console.log('GET example:');
    console.log('curl -X GET ' + url + '/google-home-notifier?text=Hello+Google+Home');
        console.log('POST example:');
        console.log('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier');
  });
})

続いて、同ディレクトにある「google-home-notifier.js」を編集します。本ファイルには、GoogleHomeの接続先並びに言語設定を実施します。
~~・lang
~~  →言語を日本語に設定するため、「ja」に変更する
~~・googlettsaccent
~~  →言語を日本語に設定するため、「ja」に変更する
・deviceAddressを追記

  →GoogleHomeのIPアドレスを記載します。
  →「example.js」に記載したため必要無し(2017/12/8追記)

google-home-notifierの起動

設定完了したら、RaspberryPi上で起動します。

node example.js

起動すると、POSTする際の例文が表示されるはずです。

GoogleHomeにしゃべらせる

google-home-notifierの起動が完了したら、RaspberryPiにGoogleHomeにしゃべってほしい内容を送ります。送り方は、例文に倣って以下の通りです。試しに「明日は雨になります」としゃべらせてみます。

curl -X POST -d "text=明日は雨になります" http://(RaspberryPiのIP):8091/google-home-notifier

上記コマンドを実行すると、GoogleHomeから「明日は雨になります」と突然話だすはずです。

最後に

今回のやり方で実行すると、声がいつものGoogleHomeの声とは異なる声でしゃべりだします。これはおそらく「iSpeech API」の声を利用しているためです。詳細はここを参照いただけるといいと思います。

また、この方法は外部からPOSTでしゃべらせることができるため、IFTTTの「webhooks」を利用することで、様々なトリガーでしゃべらせることができます。IFTTTを利用する場合は、セキュリティ上今のままだと、誰でも実行できてしまうため、認証等をさらに付け加えたほうがいいかもしれません。

ともあれ、Googleが公式にPUSH型のサービスを提供してくれるまでのつなぎとしては利用できるかもしれません。

どなたかjsに明るい方がいたら、認証回りを強化したプログラムを公開していただけるとより多くの方が利用できるようになるので、なにとぞお願いします。