こんにちは! BiTTチームのキー坊です。
以前にGoogle Homeに「時報」と「天気の変わり目」を自動で通知させてみよう!という記事をお届けしました。しかし、「google-home-notifier」が不安定に感じるので、Node-REDを使って作り変えることに。
前回の記事では、その事前準備をしてきました。
Node-REDを使ってGoogle Homeをもっと便利に(準備と開発方法基礎編)
もし、前回を読んでない方や、もう忘れちゃったという方は上記記事を先にご覧ください。
それでは、引き続き作っていきましょー!
目次
やることの確認
まずは、残タスクの確認からやっていきましょう!
- 時間ごとに起動
- 天気情報取得フローの作成
- キャストフローの作成
大まかに分類するとこの3つです。
実際に作業するにあたってはもう少し分解した方が良いでしょう。細かく見ていくと以下のようなタスクがあることがわかります。
時間ごとに起動
前回紹介したnode-red-contrib-cronを使うので、とくにタスクはありません。細かい設定等は後述します。また、今回は毎時起動させるようにしています。
天気情報取得フローの作成
- APIのリクエスト(ここは前回やってますね)
- JSONパース
- 降水量の抽出
キャストフローの作成
- 喋らせる内容をまとめる
- Google Homeにキャスト
これは、上から順に「入力」「処理」「出力」にあたります。大抵のシステムはこの3つからできているので、ここを意識すると作ろうとしているものへの理解とタスクの紐付けが早くできます。
では、それぞれをどのように作るかを見ていきましょう。
時間ごとに起動(入力)
上でもお伝えしたように、前回紹介したnode-red-contrib-cronを使って設定していきます。
これにcron式を以下のように設定すれば完了です。
0 0 */1 * * *
といってもこれだけでは何も起きないので、ちゃんと動いたのかわかりにくいですよね。
なので、以下のようにするとわかりやすいです。
functionノードには、以下のソースを貼り付けてください。
msg.payload = "test"
return msg;
これをデプロイすると、injectノードのボタンを押すたびにデバッグメッセージとして「test」と出力がされます。
これにcronノードをくっつけて、cron式を1分毎に書き換えます。
0 */1 * * * *
これで1分待つと、cronが実行されてtestがデバッグメッセージが出力されるようになります。cronノードが正しく動くことが確認できましたので、cron式を1時間に戻しておいてください。
時間毎の起動はこれで完成になります!
天気情報取得フローの作成(処理)
この部分はいくつかのタスクに分かれていますが、APIのリクエストの部分は前回やったので割愛しています。
JSONパース
APIから返ってきたレスポンスはJSONになっています。正確にはJSON「文字列」です。このままではノードが扱いづらいので、JSON「オブジェクト」に変換しましょうってことです。まあプログラムを書く上で、「文字列」だと扱いづらいから「オブジェクト」という形式に変換したいってだけです。
文字列のままでも書き方を変えればもちろん不可能ではありません。「何文字目から、何文字目にどんな値が書いてある」ということをちゃんと定義して取り出すことは可能です。でも聞いただけで面倒な作業であることがわかりますよね(笑)。
それを自動でやってくれるのがパーサというプログラムなんですね。自動でやってくれるという言葉通り、作業自体も簡単です。
左側のノードの中からパーサのカテゴリにあるJSONパーサを置く。それだけ!
前回のAPIリクエストの続きからやると、下の画像のようになると思います。
実行すると、デバッグメッセージとして、JSONのオブジェクトが入っているのが確認できると思います。
これで、JSONパースも完成です!
降水量の抽出
これは今回使っているAPIのレスポンス仕様を確認すると、降水量が格納されている箇所が把握できます。
これを元に作成したフローが以下のようになります。
もともとあったデバッグノードを上に移動して、「降水量の抽出」という名前でfunctionノードと、抽出した降水量を出力するためにもう一つデバッグノードをつけてあります。画像の「降水量の抽出」というノードのソースコードは、下のようになります。
var weather_list = msg.payload.Feature[0].Property.WeatherList.Weather
var len = weather_list.length
var rainfall = 0
for (let i = 0; i < len; ++i) {
rainfall = rainfall + weather_list[i].Rainfall
}
if (rainfall > 0) {
rainfall = Math.ceil(rainfall / len)
}
msg.payload.Rainfall = rainfall
return msg;
中身は単純に配列から降水量を取り出して、総数出して個数で割る(つまり平均値を出す)てなことをやっています。ただ晴れのときは降水量は当然0なので、それだけ除外しています。
ここまでで処理部分も完成です。
キャストフローの作成(出力)
喋らせる内容をまとめる
完成形から考えると「これから1時間の天気は晴れです」てな感じに教えてくれたら良いなと思ったので、これに沿うような形で作っていきます!
ただ、「1時間に10ミリの雨」と言われても降水量のイメージがぜんぜんわかなかったので、こちらのサイトを参考にさせていただきました。
さて、完成形に沿うと以下のようなフローができ上がります。
3つのfunctionノードのソースはそれぞれこんな感じ。
msg.message = "これから1時間の天気は"
return msg;
msg.message = msg.message + "晴れ"
return msg;
msg.message = msg.message + "です"
return msg;
もちろんこのままでは、固定の文字列しか出力できないので、処理部で出した降水量の値で分岐するように変更したのがコチラ!
ポイントはいくつかあって、まずはswitchノードは以下のように設定してください。
プロパティのところをmsg.payload.Rainfallと入力します。「msg.」の部分は固定で入ってますので、実際に入力するのはpayload.Rainfallです。
そして、もう一つのポイントがinjectノードを以下のようにしてください。
プロパティの名前をmsg.payload.Rainfallにしてください(これも実際に入力するのはpayload.Rainfallです)。
ここでテストに送信する降水量の値を決めることができます。画像では1が設定してありますので、switchノードを見ると1から3の間は2番目の分岐に入ると書いてありますので、「小雨」となります。
このように分岐のテストをしたいときはinjectノードの値を変更して、自分の設定した降水量で正しいメッセージが出力されるかを確認してください。
Google Homeにキャスト
これはそんなに難しいことではないのですが、castノードを繋げてGoogle HomeのIPアドレスをセットして、言語を日本語(ja)にするだけです。
IPアドレスの調べ方は公式サイトを参照してください。
これで出力部分もできました。
完成
ようやく「入力」「処理」「出力」の3つができました。それぞれ繋げてみると画像のようになります。
入力、処理、出力のどこの部分からでも容易にデバッグやテストができる構造をもたせているので、なにか不具合が見つかった場合でも対処しやすくできたかなと思います。
まとめ
Node-REDは「ちょっと遊んでみた」くらいの経験しかなかったので、楽しめました。
完成してから1週間くらい放置してみましたが、その間Node-REDが落ちることもなく安定した動作を見せています。そして情報を発信するトリガーをラズパイ側に持たせたことで、アイディア次第で自分の欲しい情報を欲しいタイミングで流せるようになりました。
また、こちらの記事でお伝えしたように、Node-REDで簡単にAPI作成もできます。たとえばSlackのOutgoing Webhooksを使って、投稿されたメッセージを読み上げさせることもできます。それを使えばサーバの死活監視の結果を渡して、「助けて! サーバが息をしてないの!」とかできそう(笑)。
と、ネタに走ってしまいましたが、また作ってみたいものを思いついたら記事にしてお届けしようと思います。