Linux
Node.js
RaspberryPi
GoogleHome
google-home-notifier

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

概要

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

前提条件

Raspberry Pi

  • モデル : Raspberry Pi Zero W
  • OS : Raspbian Stretch Lite
$ uname -a
Linux raspberry 4.14.32+ #1106 Wed Apr 4 17:54:59 BST 2018 armv6l GNU/Linux

GoogleHome

  • GoogleHomeMini

事前準備

お約束作業です。

$ sudo apt-get update
$ sudo apt-get upgrade

Node.jsとnpmをインストール

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

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

$ 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

$ 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アドレスです。アプリの設定画面等で確認できます。できれば固定しておきたいですね。

$ vi test.js
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がしゃべりだせば成功です!!

$ node test.js

動作確認その2

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

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

$ vi web.js
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する際の例文が表示されます。

$ node web.js

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

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

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

foreverをインストール

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

$ 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

$ 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
$ sudo systemctl list-unit-files --type=service | grep googlehomenotifier
$ sudo systemctl enable googlehomenotifier
$ sudo systemctl status googlehomenotifier
$ sudo systemctl start googlehomenotifier

最後に、再起動して自動起動していることを確認してください。

使用例

GoogleHomeでClovaを操作する。

radiko_1242.sh
curl -X POST -d "text=クローバ。。。ラジコでニッポン放送をつけて。" http://localhost:8091/google-home-notifier
radiko_off.sh
curl -X POST -d "text=クローバ。。。ラジコをとめて。" http://localhost:8091/google-home-notifier
$ 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関連のエラー対処方法

【原因】
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」を修正する
以下の内容に書き換える

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];
  });
};