Edited at

google-home-notifier周りをほぼ自動化した


はじめに

GoogleHomeMiniを買って意気揚々とGoogle先生に喋らせようと思ったら意外に融通が利かないのでラズパイ買ってgoogle-home-notifierを入れた人も多いのでは無いだろうか。

そんな方で以下のような経験をした人はいないだろうか。

満を持してgoogle-home-notifierを起動すると下のようにngrokのサーバーが自動で立ち上がり、

GET example:

curl -X GET https://xxxxxx.ngrok.io/google-home-notifier?text=Hello+Google+Home
POST example:
curl -X POST -d "text=Hello Google Home" https://xxxxxx.ngrok.io/google-home-notifier

curl叩いてGoogle先生喋ってくれて、よゆーやん、と思ってたら、

サービスを立ち上げるたびにngrokのURLが変わってしまうことに気づき絶望した人。(前振りが長い..)

これだとサーバーを起動しなおしたり、予期せぬ再起動が発生したときにいきなり繋がらない、となってしまう。

なので、今回はこの問題を解消する

2018/5/23 追記「8時間でURLが失効しないようにする」

2018/12/15 追記「google-tts-apiを最新にする」


大雑把な方針

まずgoogle-home-notifier自体はnodejsなのでforeverを使って永続化する。さらにラズパイ起動時にそれを呼び出すようにすれば常に起動状態が維持されるようになる。

次に問題のURLについて。こいつはgoogle-spreadsheetを使うことにした。常に最新のURLをgoogle-spreadsheetに記録しておくことで、各サービスはそのシートを参照することで常にアクセスできるようになる、という寸法


google-home-notifierを永続起動させる

node.jsスクリプトの自動起動を参考にforeverをインストールして、ラズパイ起動時に自動で立ち上がるようにした。ソースはこんな感じ


/etc/rc.local

# Print the IP address

_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi

# 以下の1行を追加
sudo -u pi /usr/bin/node /usr/bin/forever start -p /var/run/forever --pidfile /var/run/node-app.pid -l /home/pi/google-home-notifier/out.log -a -d /home/pi/google-home-notifier/example.js
exit 0


あとはラズパイを再起動してあげて、サービスが起動しているかを確認すればOK

sudo reboot

forever list

info:    Forever processes running

data: uid command script forever pid id logfile uptime
data: [0] tYP0 /usr/bin/nodejs /home/pi/google-home-notifier/example.js 638 676 /home/pi/google-home-notifier/out.log 0:0:26:39.768


google-home-notifierからgoogle-spreadsheetにURLを書き出す


API認証キーを作成する

下のページを参考にAPIの認証キーを作成する。

使用するAPIはGoogle Sheets API

http://www.yoheim.net/blog.php?q=20160411

作成されたJsonの認証キーはラズパイの方で使うので転送しておく。

また、作成時に発行されるサービスアカウントIDをコピーしておくこと。

サービスアカウントIdってのは<プロジェクト名>@<プロジェクトId>.iam.gserviceaccount.comみたいなやつ。

コピーし忘れた場合は、「認証情報」タブ→「サービスアカウントの管理」から確認できる。


GoogleSpreadSheetを作って共有設定をする

Google Driveで任意のspreadsheetを一つ作り保存する。中身は空欄でOK

次に「共有」→入力欄にサービスアカウントIdをいれる。

スクリーンショット 2017-12-10 22.50.09.png

メールを送るかとか聞かれるが「いいえ」でよい。

付与する権限を「編集者」にする。

設定が終わったら完了を押す。

これでアカウントIdに紐付いた認証キーからは編集可能になった。

最後にスプレッドシートのKeyをコピーしておく。URLの以下のxxxの部分

https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/edit#gid=0


example.jsを修正してURLを書き出せるようにする

ほぼこの記事「node-google-spreadsheetを使う(超簡易的なテスト)」を参考にした。

使うモジュールをインストール

npm i google-spreadsheets

以下のようにexample.jsを修正


example.js

var ngrok = require('ngrok');

var bodyParser = require('body-parser');
var GoogleSpreadsheet = require('google-spreadsheet');
var ngrokUrlSheet = new GoogleSpreadsheet('xxxxxxxxxxxxxxxxxxxxxxxxxxx'); //コピーしたスプレッドシートのKey
var credentials = require('./GoogleHomeNotifier-xxxxxxx.json'); //作成した認証キーへのパス
var app = express();
const serverPort = 8091; // default port

var deviceName = 'Google Home';
var ip = '192.168.xx.xx'; // default IP

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

var sheet;
ngrokUrlSheet.useServiceAccountAuth(credentials, function(err){
ngrokUrlSheet.getInfo(function(err, data){
sheet = data.worksheets[0];
});
});

// 中略

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');
// sheetの一番左上のCellを取得
sheet.getCells({
'min-row': 1,
'max-row': 1,
'min-col': 1,
'max-col': 1,
'return-empty': true
}, function(error, cells) {
var cell = cells[0];
cell.value = url + '/google-home-notifier'; //アクセスしてほしいURLをセット
cell.save(); //保存
console.log('spread sheet update successful!!');
});
});
})


修正したら保存。foroeverが動いていれば自動で再起動がかかってくれるはず。かかってくれなかったらstop->startなど。

うまくいけばシートの左上のセルにURLがぶっこまれる。

あとはこのセルを見るようにしてあげれば常に最新のURLが参照できるようになる


トラブルシューティング

なんか動かないんだけど!って時に


8時間でURLが失効しないようにする

google-home-notifierが気づいたら動かなくなってた時の対処法

詳細は上記の記事にて。

この対応をしないとngrokの取得したURLが8時間で失効してしまい、GoogleHomeがレスポンスを受け取れなくなってしまう。なので、ほぼ必須。


google-tts-apiを更新する

google-home-notifierが気づいたら動かなくなってた時の対処法

詳細は上記の記事にて。

この対応をしないとそもそもgoogle-home-notifierが動かない。一応PRがあるけどまだマージされてないので対応は必須になる。


おまけ(GASからのアクセス例)

スプレッドシートと相性がいいのはやはりGAS(Google Apps Script)。

IFTTTからのWebhookも受けられるし、スクリプトの定期実行もできるのでおおよそやりたい放題できる。

なので、GASからgoogle-home-notifierへメッセージを飛ばすサンプルコードを載せておく


google-home-notifier-example.gs

function getNgrokUrl() {

if (getNgrokUrl.instance) { return getNgrokUrl.instance; }
var ngrokSheetId = "xxxxxxxxxxxxxxx"; //スプレッドシートのId
var url = SpreadsheetApp.openById(ngrokSheetId).getSheetByName("yyyyy").getDataRange().getValues()[0][0]; //yyyyyはワークシート名
return url;
}

function notify2GoogleHome(msg) {
// msg = "テストメッセージ"; //debug用
sendHttpPost(msg, getNgrokUrl());
}

/*
* messageをpostする関数
*/

function sendHttpPost(msg, url){
var payload =
{
"text" : msg
};
// var userid = "user"; //Basic認証をかけている場合にこの行を有効にする
// var password = "password"; //Basic認証をかけている場合にこの行を有効にする
var options =
{
"method" : "post",
// "headers" : {"Authorization" : " Basic " + Utilities.base64Encode(userid + ":" + password)}, //Basic認証をかけている場合にこの行を有効にする
"payload" : payload
};

UrlFetchApp.fetch(url, options);
}



終わりに

スマートスピーカーアドベントカレンダー間に合わなかった...