自動化厨のプログラミングメモブログ │ CODE:LIFE

Python/ExcelVBA/JavaScript/Raspberry Piなどで色んなことを自動化

Node-RED×Yahoo気象情報API×Slack×Alexaで雨雲通知システムを作る

f:id:maru0014:20201223232055p:plain この記事は UG Advent Calendar 2020 24日目の記事です。

早速ですが記事タイトルの動作イメージからご紹介しましょう。

動作イメージ

Yahoo気象情報APIを15分毎に参照して自宅の座標に一定数以上の降水量予測が検知された場合、Slackのhome_speakerチャンネルに予測日時と予測降水量を投稿しつつ、Alexaに読み上げてもらうというものです。

Slack通知はこんなかんじ。実験なので過去の日時になっていますが基本は現在日時以降の予測情報からメッセージを生成します。

f:id:maru0014:20201223232223p:plain
Slackへの通知イメージ

動機

以前、ヤフーが提供してくれていたmyThingsというサービスはご存知でしょうか。

サービスとサービスをつないでトリガーとアクションを設定する所謂iPaaSの部類でして、「地図上のある地点がヤフーの雨雲レーダー範囲内に入ったらSlackに通知を投げる」みたいなのをサクッと作れる楽しいサービスで、我が家ではSlackを経由してGoogle Home Notifierによって家中にアナウンスするという環境を構築していました。

しかし、2019年1月31日にサービス終了となってしまい僕は雨予報を知る術を失ったのです...(天気予報見るなりアプリ入れるなりしろよという話ですけどね)。

もう約2年ほど雨雲通知システムがない状態を過ごしましたが、Node-REDの扱いにもなれてきたので作ったろ と思った次第です。

概要

Node-REDフローはこうです。

injectを15分ごとに実行されるようにしておいて、http requestでAPIから情報を取得、雨予報検知の関数で結果のオブジェクトを生成して投稿文字列テンプレートに渡す。

その後はSlackへの投稿とAlexaへの通知の2つに分岐させています。

f:id:maru0014:20201223232351p:plain
雨雲通知システムのNode-REDフロー

Yahoo 気象情報API

YOLP(地図):気象情報API - Yahoo!デベロッパーネットワーク

アプリケーションIDの取得

APIを利用するにあたってYahoo!デベロッパーネットワークのアカウントおよびアプリケーションIDが必要になります。

YahooのIDで登録後、適当なアプリケーションを作成してアプリケーションの詳細ページからIDをコピーしておきます。

f:id:maru0014:20201223232424p:plain
Yahoo!デベロッパーネットワークのアプリID

GETリクエストで気象情報を取得

緯度経度はGoogleマップで確認できます。

f:id:maru0014:20201223232454p:plain
Googleマップを右クリックして緯度経度を取得

リクエスト例は以下の通り。ここで一つ罠があります。

coordinates の値が何故か「緯度,経度」ではなく「経度,緯度」の順で指定することになっています。これに気付くのに結構時間がかかりましたw

https://map.yahooapis.jp/weather/V1/place?coordinates=経度,緯度&past=1&output=json&appid=アプリケーションID

ブラウザのアドレスバーに入れるだけで試せるので確認しましょう。雨の日に確認すると Rainfall が0じゃないはず。

f:id:maru0014:20201223232530p:plain
ブラウザでGETリクエストを試す

Node-REDの設定

infect (実行スケジュール設定)

infect ノードを追加して以下のように設定します。

f:id:maru0014:20201223232602p:plain

気象情報API

http request ノードを追加してGETメソッドを選択、URLには先程ブラウザで試したものをコピペします。出力形式はJSONオブジェクトです。

f:id:maru0014:20201223232608p:plain

雨予報検知

function ノードを追加してコードタブに書いたJavaScript によって通知を実行するかどうかを判別します。ノーコードじゃないんかーい って思うかもしれませんがfunctionノード使わないほうが大変な気がしたので書いちゃいました。

後続の文字列テンプレートにわたすデータとして予測降水量と予測日時を msg.payload に入れます。

// 通知する降水量のしきい値
const threshold = 2;

// 10分ごとの降水量実測値、予測値を取得
const data = msg.payload.Feature[0].Property.WeatherList.Weather;

// 実測値配列
const observations = data.filter(w => w.Type == 'observation');

// 最新の実測値
const observationRainfall = observations[observations.length - 1].Rainfall;

// 降水量が0より大きい予測を探す
const forecast = data.find(w => w.Type == 'forecast' && 0 < w.Rainfall);

// forecastがnullでなければ降水量にセット
const forecastRainfall = forecast ? forecast.Rainfall : 0;

// 実測値が0で予測値がしきい値以上の場合は降水量と日時を返す
if(observationRainfall == 0 && threshold <= forecastRainfall){
    msg.payload = {
        rainfall: forecastRainfall,
        date: forecast.Date.replace(/\d{4}(\d{2})(\d{2})(\d{2})(\d{2})/,'$1月$2日 $3時$4分')
    };
} else {
    msg.payload = null;
}

return msg;

isNotNull

Node-REDにはIFは無いので Switch ノードを追加。

msg.payload != null の場合 1 (1番目の接続ノード)に移動するよう設定します。

f:id:maru0014:20201223232625p:plain

投稿文字列生成

template ノード を追加して以下のように設定します。

受け取ったプロパティ値は {{payload.date}} のように波括弧2つで囲むことで内容を展開できます。

自宅に雨雲が近づいて来ています。
予測日時は {{payload.date}}。
予測降水量は {{payload.rainfall}}mm です。

f:id:maru0014:20201223232637p:plain

Convert Slack

function ノードを追加して コードタブに以下をセット。

SlackのIncoming Webhook用のJSONフォーマットに合わせます。

const body = msg.payload;
msg.payload = {
    "text": body
}
return msg;

Post Slack

http request ノードを追加。

POSTメソッドにてSlackのIncomingWebhook URLを設定する。

f:id:maru0014:20201223232651p:plain

Convert Alexa

change ノードを追加。

Alexaのroutineノードはmsg.message でメッセージを受け付けるので msg.payload を変換しています。

f:id:maru0014:20201223232701p:plain

Routine Speak

Alexa Routine ノードを追加。Speak Regular にて Textに msg.message をセット、Devicesに任意のAlexaデバイスまたはデバイスグループをセット。

Alexa用の事前設定は過去記事を参照ください

codelife.cafe

f:id:maru0014:20201223232710p:plain

あとがき

また、今回記事にするために色々見直しているとぽろぽろバグが出てきてなんやかんやで2時間くらい掛かっちゃいましたが、これで安定して雨雲通知してくれるかな!

新たな機能が実装された我が家のNode-RED。

実は子供に今何をするべき時間なのかを知らせるために以下のような形で定時アナウンスもさせていたりします。

f:id:maru0014:20201223232719p:plain

もう欠かせないシステムですね。