下林明正のブログ

個人的かつ雑多なブログです。

Google HomeがRaspberry Piとngrok経由で室温など教えてくれるようにした

shimobayashi.hatenablog.com

でうまくいかなかったので放置していたけど、再挑戦した。 基本的には

blog.yuu26.com

を参考にしてるので、そっち見ながらこの記事も読んでください。

Raspberry Piからgoolgle-home-notifierを使えるようにする

まずはnodejsの実行環境をつくることにした。 適当に調べて以下の記事を参考にした。

Raspberry Pi への Node.js インストールとnvmでのバージョン管理 - Qiita

インストール可能バージョン一覧を見たときにLTSってなんじゃいとなったので適当に調べた(nodejsを避けて生きてきたので……)。

backport.net

雰囲気的にはLTSってなってる最新のバージョンを使えば良さそうということで、 v10.15.0 を採用した。

次にgoogle-home-notifierをインストールする。

github.com

Raspberry Piセクションがあるのでその通りにしましょう。 そしてサンプルスクリプトを実行するとエラーが出たので、以下の通りに修正したところ動きました。

qiita.com

google-home-notifierってメンテナンス止まってるんですかね、かといって他に代替手段も無い気がするし不安。

これでひとまずRaspberry Piからgoogle-home-notifierを利用できるようになりました。 ハード的にもソフト的にも古いRaspberry Piだったので手間をかけずに動くか不安だったんですが、今の所大丈夫そうです。

HTTP GETしたJSONをパースして喋らせる

request モジュールは普通にインストールできたんですが node-jq

github.com

などを参照しつつもうまくいかなくて諦めて、 request の機能を使うことにしました。

webClient.get({
  url: "http://example.com",
  json: true,
}, function(error, response, json){

みたいな感じ。

ngrokとかもインストールして結局こうなった(nodejs知らないのとRaspberry Pi上のviで適当に書いていたので汚いのは勘弁)。

const googlehome = require('../google-home-notifier');
const webClient = require('request');
const ngrok = require('ngrok');
const express    = require('express');
const config = require('config');

// ngrok設定
var app = express();
var port = 8091;

// ライブラリ初期化
googlehome.device(config.googlehome.deviceName, config.googlehome.language);
googlehome.ip(config.googlehome.ipAddress, config.googlehome.language);

// GETリクエスト受信時の処理
app.get('/', function(req, res) {
  // 室温を取得
  webClient.get({
    url: config.apiUrl,
    json: true,
  }, function(error, response, settings){
    var temp = Math.round(parseFloat(settings.temperature) * 10) / 10;
    var humd = Math.round(parseFloat(settings.humidity) * 10) / 10;
    var di = Math.round(parseFloat(settings.discomfort_index) * 10) / 10;
    var notifyText = '現在の部屋の温度は' + temp + '度、湿度は' + humd + '%、不快指数は' + di + 'です 。';
    googlehome.notify(notifyText, function(notifyRes) {
      console.log(notifyRes);
      res.json({success: true, res: notifyRes});
    });
  });
});

// ngrok起動
app.listen(port, async function() {
  const url = await ngrok.connect(port);
  console.log('Endpoint: ' + url);
});

とりあえず雑にnohupでバックグラウンド実行しましょう。

nohup node notify-temp.js &

これで最低限動くようにはなりました。

今後の課題

今日はもう疲れてきたのでやめときますが、以下の課題が残っています。

  • 当該のスクリプトが自動実行されないので、Raspberry Piの電源が落ちるたびにいちいち手動で実行する必要がある
    • daemon化などすれば自動実行できそう
  • ngrokのエンドポイントが実行毎に変わってしまい、IFTTTの設定を手動で変更する必要がある
    • お金を払えば固定できるらしい?けどこのためだけにお金を払う気も起きないので、強い気持ちでダイナミックDNSとかを使いつつグローバルに公開するとかしないとダメそう。だけど雑にやると危なさそう

この2つが組み合わさって結構だるそうですね。

追記

古いRaspberry Piにnodejs+expressみたいないかにも重そうなプロセス常駐させるのはさすがに厳しいのかも、対策を考えなければ……。 cronで外部のジョブキューを読みに行って、そこにあるテキストを単に読み上げるとかだと大丈夫かな……?

追記

とりあえずめんどいのでcronで定期実行して通知したほうが良さそうな雰囲気だったら喋らせるということにした(自分用の仕組みのコードなのであまり参考にならないかも)。 1分待つ感じだとだるすぎるのですげー雑にsetTimeoutで複数回実行するようにした、ひどい。

const googlehome = require('../google-home-notifier');
const webClient = require('request');
const config = require('config');

// ライブラリ初期化
googlehome.device(config.googlehome.deviceName, config.googlehome.language);
googlehome.ip(config.googlehome.ipAddress, config.googlehome.language);

// 室温を取得
var run = function() {
        console.log('run');
        webClient.get({
                url: config.apiUrl,
                json: true,
        }, function(error, response, settings){
                if (settings.notify_temp_scheduled == '1') {
                        var temp = Math.round(parseFloat(settings.temperature) * 10) / 10;
                        var humd = Math.round(parseFloat(settings.humidity) * 10) / 10;
                        var di = Math.round(parseFloat(settings.discomfort_index) * 10) / 10;
                        var notifyText = '現在の部屋の温度は' + temp + '度、湿度は' + humd + '%、不快指数は' + di + 'です 。';
                        googlehome.notify(notifyText, function(notifyRes) {
                                console.log(notifyRes);
                        });
                        webClient.get({
                                url: config.apiUrl + '?notify_temp_scheduled=0',
                                json: true,
                        }, function(error, response, settings){
                                console.log('notify_temp_scheduled is updated');
                        });
                };
        });
};
for (var i = 0; i < 5; i++) {
        setTimeout(run, i * 10 * 1000);
}