この記事は UG Advent Calendar 2020 24日目の記事です。
早速ですが記事タイトルの動作イメージからご紹介しましょう。
動作イメージ
Yahoo気象情報APIを15分毎に参照して自宅の座標に一定数以上の降水量予測が検知された場合、Slackのhome_speakerチャンネルに予測日時と予測降水量を投稿しつつ、Alexaに読み上げてもらうというものです。
UG Advent Calendar 2020 24日目のネタ 「Node-RED×Yahoo気象情報API×Alexaで雨雲通知システムを作る」の実験中。#EchoShow5 の上に寝ている #合成人間 がいい味出してる。#NodeRed #Alexa pic.twitter.com/qwq547Iyyf
— marumo (@marumo0014) 2020年12月23日
Slack通知はこんなかんじ。実験なので過去の日時になっていますが基本は現在日時以降の予測情報からメッセージを生成します。
動機
以前、ヤフーが提供してくれていたmyThingsというサービスはご存知でしょうか。
サービスとサービスをつないでトリガーとアクションを設定する所謂iPaaSの部類でして、「地図上のある地点がヤフーの雨雲レーダー範囲内に入ったらSlackに通知を投げる」みたいなのをサクッと作れる楽しいサービスで、我が家ではSlackを経由してGoogle Home Notifierによって家中にアナウンスするという環境を構築していました。
しかし、2019年1月31日にサービス終了となってしまい僕は雨予報を知る術を失ったのです...(天気予報見るなりアプリ入れるなりしろよという話ですけどね)。
もう約2年ほど雨雲通知システムがない状態を過ごしましたが、Node-REDの扱いにもなれてきたので作ったろ と思った次第です。
概要
Node-REDフローはこうです。
injectを15分ごとに実行されるようにしておいて、http requestでAPIから情報を取得、雨予報検知の関数で結果のオブジェクトを生成して投稿文字列テンプレートに渡す。
その後はSlackへの投稿とAlexaへの通知の2つに分岐させています。
Yahoo 気象情報API
YOLP(地図):気象情報API - Yahoo!デベロッパーネットワーク
アプリケーションIDの取得
APIを利用するにあたってYahoo!デベロッパーネットワークのアカウントおよびアプリケーションIDが必要になります。
YahooのIDで登録後、適当なアプリケーションを作成してアプリケーションの詳細ページからIDをコピーしておきます。
GETリクエストで気象情報を取得
緯度経度はGoogleマップで確認できます。
リクエスト例は以下の通り。ここで一つ罠があります。
coordinates
の値が何故か「緯度,経度」ではなく「経度,緯度」の順で指定することになっています。これに気付くのに結構時間がかかりましたw
https://map.yahooapis.jp/weather/V1/place?coordinates=経度,緯度&past=1&output=json&appid=アプリケーションID
ブラウザのアドレスバーに入れるだけで試せるので確認しましょう。雨の日に確認すると Rainfall
が0じゃないはず。
Node-REDの設定
infect (実行スケジュール設定)
infect ノードを追加して以下のように設定します。
気象情報API
http request ノードを追加してGETメソッドを選択、URLには先程ブラウザで試したものをコピペします。出力形式はJSONオブジェクトです。
雨予報検知
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番目の接続ノード)に移動するよう設定します。
投稿文字列生成
template ノード を追加して以下のように設定します。
受け取ったプロパティ値は {{payload.date}}
のように波括弧2つで囲むことで内容を展開できます。
自宅に雨雲が近づいて来ています。 予測日時は {{payload.date}}。 予測降水量は {{payload.rainfall}}mm です。
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を設定する。
Convert Alexa
change ノードを追加。
Alexaのroutineノードはmsg.message
でメッセージを受け付けるので msg.payload
を変換しています。
Routine Speak
Alexa Routine ノードを追加。Speak Regular にて Textに msg.message
をセット、Devicesに任意のAlexaデバイスまたはデバイスグループをセット。
Alexa用の事前設定は過去記事を参照ください
あとがき
また、今回記事にするために色々見直しているとぽろぽろバグが出てきてなんやかんやで2時間くらい掛かっちゃいましたが、これで安定して雨雲通知してくれるかな!
新たな機能が実装された我が家のNode-RED。
実は子供に今何をするべき時間なのかを知らせるために以下のような形で定時アナウンスもさせていたりします。
もう欠かせないシステムですね。