【Raspberry Pi Zero W】おしゃべりGoogleHome

##概要
ラズベリーパイにgoogle-home-notifierを導入してGoogleHomeをしゃべらせたいと思います。

##前提条件
Raspberry Pi

- モデル : Raspberry Pi Zero W
- OS : Raspbian Stretch Lite

```Bash:
$ uname -a
Linux raspberry 4.14.32+ #1106 Wed Apr 4 17:54:59 BST 2018 armv6l GNU/Linux
```

GoogleHome

- GoogleHomeMini

##事前準備
お約束作業です。

```Bash:
$ sudo apt-get update
$ sudo apt-get upgrade
```

##Node.jsとnpmをインストール
google-home-notifierはNode.jsとnpmを使用するため、事前にインストールします。

参考サイト：
https://qiita.com/mascii/items/77c685df65c4cbca9315

```Bash:
$ sudo apt-get update
$ sudo apt-get install -y nodejs npm
$ sudo npm cache clean
$ sudo npm install npm n -g
$ sudo n stable

$ node -v # バージョン確認
v9.10.1
$ npm -v # バージョン確認
5.6.0
```

##google-home-notifierをインストール
google-home-notifierをインストールします。

参考サイト：
https://qiita.com/ktetsuo/items/8c9cd5714e231aa6ae09

```Bash:
$ mkdir googlehome
$ cd googlehome
$ sudo apt-get install libavahi-compat-libdnssd-dev
$ npm init # すべてデフォルト
$ npm install google-home-notifier
$ cat package.json # 何となく確認
{
  "name": "googlehome",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "google-home-notifier": "^1.2.0"
  }
}
```

##動作確認
テスト用プログラムを作成します。
”xxx.xxx.xxx.xxx”はGoogle-HomeのIPアドレスです。アプリの設定画面等で確認できます。できれば固定しておきたいですね。

```Bash:
$ vi test.js
```
```Javascript:test.js
const googlehome = require('google-home-notifier');
const language = 'ja';
googlehome.device("Google-Home", language);
googlehome.ip("xxx.xxx.xxx.xxx");
googlehome.notify('こんにちは。私はグーグルホームです。', function(res) {
  console.log(res);
});
```

実行して（WARNINGは出るけど・・・）GoogleHomeがしゃべりだせば成功です！！

```Bash:
$ node test.js
```

##動作確認その２
コマンドで実行できたりすると可能性が広がりますよね。ほぼ参考サイトそのままです。

参考サイト：
https://qiita.com/azipinsyan/items/db4606aaa51426ac8dac

```Bash:
$ vi web.js
```
```Javascript:web.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 = "Google-Home";
var ip = 'xxx.xxx.xxx.xxx'; // 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');
  });
})
```

実行すると（今回もWARNINGは出るけど・・・）POSTする際の例文が表示されます。

```Bash:
$ node web.js
```

もう１セッション接続して、以下のコマンドを実行してGoogleHomeがしゃべりだせば成功です！！

```Bash:
$ curl -X POST -d "text=こんにちは。私はグーグルホームです。" http://localhost:8091/google-home-notifier
```

動作確認ができたら、CTRL+Cで終了します。

##foreverをインストール
foreverを導入すると、nodeをバックグランドで起動することができるので便利です。

```Bash:
$ sudo npm install -g forever
$ forever start web.js
$ curl -X POST -d "text=こんにちは。私はグーグルホームです。" http://localhost:8091/google-home-notifier
```
ちなみにforever stop web.jsで終了できます。

##systemd登録
再起動しても自動で起動するように、systemdに登録します。

参考サイト：
https://qiita.com/you21979@github/items/588bddb59378ce7303a2
https://qiita.com/udon11/items/fef44cec7b243f93151b

```Bash:
$ sudo vi /etc/systemd/system/googlehomenotifier.service
```
```:googlehomenotifier.service
[Unit]
Description=google-home-notifier Server
After=syslog.target network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/forever start /home/pi/googlehome/web.js
WorkingDirectory=/home/pi/googlehome
KillMode=process
Restart=no
User=pi

[Install]
WantedBy = multi-user.target
```
```Bash:
$ sudo systemctl list-unit-files --type=service | grep googlehomenotifier
$ sudo systemctl enable googlehomenotifier
$ sudo systemctl status googlehomenotifier
$ sudo systemctl start googlehomenotifier
```
最後に、再起動して自動起動していることを確認してください。

##使用例
GoogleHomeでClovaを操作する。

```Bash:radiko_1242.sh
curl -X POST -d "text=クローバ。。。ラジコでニッポン放送をつけて。" http://localhost:8091/google-home-notifier
```
```Bash:radiko_off.sh
curl -X POST -d "text=クローバ。。。ラジコをとめて。" http://localhost:8091/google-home-notifier
```
```Bash:
$ crontab -l
00 22 * * 0 radiko_1242.sh # 毎週日曜日22:00にラジコをつける
30 22 * * 0 radiko_off.sh # 毎週日曜日22:30にラジコをとめる
```
なんか、すごい遠回りしてるような。。。


##2018/11/22更新
しゃべらなくなった？

下記参考サイトの「「Error: get key failed from google」エラー」参照
[google-home-notifier関連のエラー対処方法](https://keenny.com/google-home-notifier%E9%96%A2%E9%80%A3%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E5%AF%BE%E5%87%A6%E6%96%B9%E6%B3%95/)

>【原因】
google翻訳(https://translate.google.com)の返すキーが、「TKK = ‘405291.1334555331’」から「TKK=’12345678.90123’」になり、それをチェックする「node_modules/google-tts-api/lib/key.js」でエラーになっている
>【解決方法】
Githubに対応ソースがあがっていたので、その内容で
「node_modules/google-tts-api/lib/key.js」を修正する
>以下の内容に書き換える

```Bash:
var fetch = require('isomorphic-fetch');
var host = 'https://translate.google.com';
 
/**
 * Get Key from https://translate.google.com
 *
 * @param   {Number!} timeout  default is 10000ms
 * @return  Promise(key: String)
 */
module.exports = function (timeout) {
  return fetch(host, {
    timeout: timeout || 10 * 1000
  })
  .then(function (res) {
    if (res.status !== 200) {
      throw new Error('request to ' + host + ' failed, status code = ' + res.status + ' (' + res.statusText + ')');
    }
    return res.text();
  })
  .then(function (html) {
    var match = html.match("TKK='(\\d+.\\d+)';");
 
    if (!match) throw new Error('get key failed from google');
 
    return match[1];
  });
};
```