雨が降りそうになったらGoogle Homeに教えてもらえるようにした

やったこと

雨が降りそうになったらGoogle Homeが「雨が降りそうです」と教えてくれるようにした。

環境

ハードウェア

ミドルウェア

  • raspbian 9.4
  • node.js 9.11.1
  • google-home-notifier 1.2.0

WebAPI

環境づくり

Raspberry Pi のセットアップ

以下の記事を参考にOSのインストールからsshでの接続まで。

MacユーザがRaspberry Pi2をセットアップする-1 | scribble warehouse

node.jsはこちらの記事を参考に9.11.1を入れた。
第三回 Raspberry Pi 3に最新のNode.jsをインストールする

$ apt-get update
$ apt-get install -y nodejs npm
$ npm cache clean
$ npm install n -g
$ n 9.11.1

Google Homeを能動的に喋らせる

Google Homeを喋らせるにはgoogle-home-notifierを使うのが楽とのことで、以下の記事を参考にセットアップ。
Google Home開発入門 / google-home-notifier解説

$ npm init
$ npm install  google-home-notifier

dns_sd.h がありません的なエラーが出たので以下を実行。

$ sudo apt-get install libavahi-compat-libdnssd-dev

天気の取得

天気取得APIはいくつかあるが、以下の要件を満たせていて手軽に叩けたので今回は YOLP(地図):気象情報API - Yahoo!デベロッパーネットワーク を使う。
API自体シンプルでcurlサンプルもあったのが嬉しい。

  • 緯度・経度で指定できる
  • 現在の天気が取れる
  • 少しあとの天気予報が取れる

nodeの環境変数

APIの呼び出しにYahoo!のAppIDが必要だったので、以下の記事を参考に dotenv をインストール。
Nodeプロジェクトで環境依存の設定の管理方法

$ npm install dotenv --save

.envは忘れず.gitignoreへ…。
せっかくなのでgitで晒すのは憚られる以下の情報も.envに書いた。

コード

Github

GitHub - ergooo/GoogleHomeWeatherNotifier: 雨が振りそうになったらGoogleHomeに喋ってもらう

javascript初心者なのでいろいろ有り得ないところがあるかもしれませんが…。

GoogleHomeを喋らせる

// GoogleHome.js
module.exports = class GoogleHome {
  constructor(ipAddress) {
    this._ipAddress = ipAddress
  }
  tell(message) {
    const googlehome = require('google-home-notifier')
    const language = 'ja';

    googlehome.device('Google-Home', language); 
    googlehome.ip(this._ipAddress);
    googlehome.notify(message, function(res) {
      console.log(res);
    });
  }
}

ほぼサンプルそのまま。IPアドレスとメッセージを受け取って喋らせる。

天気取得

// WeatherApi.js
module.exports = class WeatherApi {
  constructor(appId) {
    this._appId = appId
  }

  /**
  * @param {WeatherApi~RequestCallback} callback
  */
  request(latitude, longitude, callback) {
    const request = require('request');
    const url = this._buildUrl(latitude, longitude, this._appId)
    console.log('url: ' + url)
    const options = {
      url: url,
      method: 'GET'
    }

    request(options, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        const json = JSON.parse(body);
        callback(error, json.Feature[0].Property.WeatherList.Weather)
      } else {
        callback(error, null)
      }
    })
  }

  _buildUrl(latitude, longitude, appId) {
    return 'https://map.yahooapis.jp/weather/V1/place?' + 
      'output=json' + 
      '&coordinates=' + longitude + ',' + latitude +
      '&appid=' + appId
  }
}

javascriptでHTTPするのは require('http') する方法と requeire('request') する方法があるようだったが、後者のほうがナウくて楽なようだったので後者で。 JSONのパーズは何も考えなくてもやってくれるので非常に手早く書ける。

天気をチェックして喋らせる

// WeatherCheckerMain.js
module.exports = class WeatherCheckerMain {
  static check() {
    require('dotenv').config()
    const GoogleHome = require('./GoogleHome')
    const googleHome = new GoogleHome(process.env.NODE_GOOGLE_HOME_IP_ADDRESS)

    const WeatherApi = require('./WeatherApi')
    const weatherApi = new WeatherApi(process.env.NODE_YAHOO_APP_ID)
    weatherApi.request(process.env.NODE_LATITUDE, process.env.NODE_LONGITUDE, function(error, weathers) {
      if(!error) {
        console.log('request ok.')
        const current = weathers[0]
        const next = weathers[1]
        if(current.Rainfall == 0 && next.Rainfall > 0) {
          googleHome.tell("雨が振りそうです。")
        }
      } else {
        console.log(error)
      }
    })
  }
}

定期実行させやすそうだったのでメインの処理もクラスにしてみた。 Yahoo!APIでは、現在とその後10分おきの降水予報が取れたので、現在降水0で10分後降水0以上なら通知するようにした。

定期実行

node.jsのいろいろなモジュール14 – node-cronでcron的にプログラムを実行する | Developers.IO こちらの記事を参考にほぼコピペで。タイムゾーン部分だけエラーになったので直した。

$ npm install cron time
// cron.js
const cronJob = require('cron').CronJob;
const cronTime = "00 00,10,20,30,40,50 00,01,09-23 * * *";
const job = new cronJob({
  cronTime: cronTime
   , onTick: function() {
    console.log('onTick!');
    let Main = require('./WeatherCheckerMain')
    Main.check()
  }
   , onComplete: function() {
    console.log('onComplete!')
  }
  , start: true
  , timeZone: "Asia/Tokyo"
})
 
job.start();

実行

pm2 でcron.jsを実行するようにした。

$ npm install pm2 -g
$ pm2 start cron.js

感想

なんだかんだ1ヶ月ぐらいかかるかと思ったけどほぼコピペでトラブルなく行けたので1日でできてしまった。 nodeは手軽でサクッとできるというとても良い印象が得られた。 まだ雨が降ってないので本当にちゃんと動くかわからないけど、とりあえず満足。