数年越しのNode-RED入門 (その3)

ファームウェアエンジニアの井田です。前回 に引き続き、Node-RED でNature Cloud APIを触るお話です。

Node-REDのフローのデータ

本記事で使用しているNode-REDのフローのデータはこちらのリポジトリに置いてありますので、適宜参照してください。

github.com

Remo3/mini2のセンサデータの取得

前回までで、Node-REDから部屋の照明の点灯・消灯の制御ができるようになりました。 いよいよセンサによる自動化を行うために、Remo3/mini2に搭載されているセンサ情報の読み出しを行います。

センサデータの取得

Remo3/mini2のセンサデータは、 /1/devices などで返ってくるデバイス情報の中にある newest_event フィールドの中にあります。

newest_event フィールドは、 {"val": 値, "created_at": "時刻"} の形式の値を0個以上持つ配列です。 例えば、Remo3は、温度センサ、湿度センサ、輝度センサ、人感センサを持っているので以下のような値が返ってきます。

{
    "name": "自宅Remo3",
    // (省略)
    "newest_events": {
        "hu": {
            "val": 50,
            "created_at": "2023-08-23T01:02:08Z"
        },
        "il": {
            "val": 39,
            "created_at": "2023-08-23T01:20:34Z"
        },
        "mo": {
            "val": 1,
            "created_at": "2023-08-23T01:20:03Z"
        },
        "te": {
            "val": 28.1,
            "created_at": "2023-08-23T01:18:09Z"
        }
    }
}

各フィールドの内容は次の通りです。

フィールド名 valの内容 備考
hu 相対湿度(%) 50
il 照度レベル (0~100) 39
mo 人感センサ (値は常に1) 1 created_atのみ更新される
te 気温 (℃) 28.1

人感センサ(mo) の値 (val) は常に 1 を返します。代わりにイベントの発生時刻である created_at のみ人感センサに反応があった時刻に更新されます。 Remo mini2は温度センサのみなので、 te フィールドのみ返ってきます。また Remo nanoの場合はセンサが無いのでなにも返ってきません。

ということなので早速センサ値を取得・抽出してみましょう。アクセスするのは /1/devices ですので、前に作った devices 取得用のノードをコピーして、センサ情報抽出用のchangeノードをつけておきます。

センサー更新 injectノードは今まで通り、 GET https://api.nature.global/1/devices にアクセスする設定です。

センサーデータ抽出 changeノードは、JSONata式をつかって /1/devices からのレスポンスから、目的のセンサデータを抽出します。JSONata式の中身は以下の通りです。 デバイスIDの抽出処理での条件と同様に、まずはセンサデータ取得元のRemoを名前とユーザー名から特定します。その後、 .newest_events フィールドのみを抽出します。

payload[
    name="自宅Remo3" 
    and 
    users[
        nickname="Kenta Ida" 
        and 
        superuser=true
    ]
].newest_events

最後に センサーデータ debugノードでは、抽出した newest_events フィールドの内容を解析して、分かりやすい形で出力します。解析のためのJSONata式は次の通りです。

"室温 " & payload.te.val & " 湿度 " & payload.hu.val & (payload.mo.created_at ? " 人感 " & (($millis() - $toMillis(payload.mo.created_at))/1000) & "秒前" : "")

payload.te.valpayload.hu.val はそれぞれ抽出した newest_eventste および hu フィールドの値を取得して表示しているだけです。

人感センサの場合は val フィールドは常に 1 ですが、人感センサが反応した場合に created_at が更新されます。 現在からどれくらい前に反応したかを扱うことが多いと思いますので、最後に反応した時刻からの経過秒数を計算しています。

$toMillis(payload.mo.created_at) で最後に反応した時刻のUNIX Epoch 1 からのミリ秒単位の経過時間が求まるので、あとは現在時刻のUNIX Epochからのミリ秒単位の経過時間を返す $millis() の値から引いたものを1000で割れば、現在時刻までの経過秒数が求まります。

センサ値を取得するノード構成

ここまででデプロイして センサー更新injectノードを起動すると、センサーデータ debugノードにセンサ情報が表示されます。

センサデータで照明の自動化

ここまでで各種センサデータが取得できるようになったので、センサデータで条件分岐して照明を制御してみましょう。

まずは先ほどdebugノードの出力で使ったのと同様のJSONata式をつかって、人感センサの反応時刻で分岐するswitchノードを追加します。 分岐に使用する値を抽出するJSONata式は以下の通りです。

payload.mo.created_at ? (($millis() - $toMillis(payload.mo.created_at))/1000) : null

payload.mo.created_at に値が設定されていれば、最後に人感センサが反応してからの経過秒数を計算します。センサの反応時間が不明の場合は null とします。 センサの状態に応じた処理を一つだけ実行したいので、下の方にあるリストボックスで 最初に合致した条件で終了 を選択します。

照明をON/OFFするためのボタンを設定するchangeノードを2つ追加し、msg.payload への設定値を on off それぞれの値に設定しておきます。またOFFにするchangeノードの入力をswitchノードの2番目の出力、ONにするchangeノードの入力をswitchノードの3番目の出力に接続しておきます。 これにより、対応する条件を満たしたときにそれぞれのノードが実行されます。

あとは、2つのchangeノードの出力を、前回作成した照明制御用ノード群の中のchangeノードの入力に接続すれば終わりです。

照明の自動化

デプロイして実行すると、人感センサの反応があった時刻が120秒より前なら、照明が消えます。 人感センサの反応があった時刻が120秒以内なら、照明が点きます。

動作を確認したら、 センサーを更新injectノードの設定を変更して、30秒ごとに自動実行するようにします。これで30秒ごとに自動的にセンサー情報を取得して、人感センサの反応時刻に応じて照明を点灯・消灯できるようになりました。

センサ更新を自動実行

エアコンの制御

エアコンの手動制御

さて、では本題のエアコンの制御をしましょう。

まずは手動でエアコンの制御をしてみます。エアコンの制御、つまり、温度設定や運転モード (冷房・暖房・除湿など)の切り替え、風量設定を行うためには、 POST /1/appliances/{applianceid}/aircon_settings を使います。 APIの仕様はこちら

基本的には照明のときと同様に、パラメータをPOSTで送信するだけです。その前にまずはエアコンのアプライアンスIDを取得しておきます。 照明のときと同様に、エアコンの登録名をつかってIDを抽出し、 flow.acApplianceId に保存しておきます。抽出用のJSONata式は、私の部屋の場合はエアコンの登録名がそのまま エアコン なので以下のようになります。

payload[
    nickname="エアコン" 
].id

エアコンのアプライアンスIDを抽出

エアコンのパラメータとしては以下の項目があります。

名前 内容 備考
air_direction 上下風向 空欄なら自動
air_direction_h 左右風向 空欄なら自動
air_volume 風量 3, auto 空欄なら自動
button 固定ボタン power_off 空欄なら電源ON
operation_mode 運転モード cool, warm, dry
temperature 温度 26, 26.5
temperature_unit 温度単位 (℃ or ℉) c, f

例えば、エアコンの風量を 6 運転モードを冷房 (cool) 温度を 26℃ に設定するには次のパラメータを送信します。

{
    "air_volume": "6",
    "operation_mode": "cool",
    "temperature": "26",
    "temperature_unit": "c"
}

設定できるパラメータの内容はエアコンの機種によるので、詳しくはアプライアンス情報の下の aircon フィールドの中にある range フィールドの内容を確認してください。 画像の例の場合、運転モードとしては auto blow cool dry warm が使用できます。また固定ボタンとして airdir-swing から power-off までの5種類のボタンが使えます。

エアコンの設定値

injectノードの msg.payloadJSON形式で上記のパラメータを設定しておきます。 また、URL構築用のchangeノードで、以下のJSONata式を使ってURLを構築します。

"https://api.nature.global/1/appliances/" & $flowContext("acApplianceId") & "/aircon_settings"

エアコンの設定を変更するフロー

デプロイしてinjectノードを実行すると、エアコンの動作状態が指定した通りに変化します。

エアコンの自動制御

ここまででエアコンの設定をNode-RED上から手動で変更できるようになりました。 最後に、エアコンの設定をRemo3/mini2の温度センサに基いて制御してみましょう。

まずは温度センサ te.val の値を条件として、エアコンのパラメータを選択します。例として、以下の条件でパラメータを設定するようにします。

条件 運転モード 風量 温度
> 28℃ 冷房 6 26
27℃ <, <= 28℃ 冷房 3 27
<= 27℃ 冷房 1 27

温度でのエアコンの操作選択

さて、このままパラメータを送信すればとりあえずエアコンのパラメータが条件に応じて変化します。ただしこの場合、現在のエアコンの状態がパラメータの状態と同じであっても、指示があればRemoは赤外線信号を送出します。 (勝手にマスクすると赤外線がうまく届かなかった時のリトライなどが難しくなるので) 対策として、Node-RED上で最後の操作結果を保持しておき、最後の操作から一定時間経過するか、異なるパラメータに設定したい場合のみ、エアコンのパラメータを送信するようにします。

まず、/1/appliances/{applianceid}/aircon_settings からのレスポンスを flow.lastAcSettings に設定して保存しておきます。 つぎに、設定したいエアコンのパラメータと、最後に設定したときのエアコンのパラメータ (flow.lastAcSettings) から、パラメータの設定の要・不要を判定します。 判定のJSONata式は以下の通りです。

(
$lastAcSettings := $flowContext("lastAcSettings");
$not($exists($lastAcSettings))
or
($millis() - $toMillis($lastAcSettings.updated_at)) > (60*60*1000)
or
$lastAcSettings.temp != payload.temperature
or
$lastAcSettings.mode != payload.operation_mode
or
$lastAcSettings.vol != payload.air_volume
)

JSONata式では、 := を使って $ から始まる名前の変数に値を設定することができます。ここでは記述の簡略化のために $flowContext("lastAcSettings") で取得した flow.lastAcSettings の内容を $lastAcSettings という名前の変数に設定しておきます。また、複数行の式を記述する場合は、全体を () で囲んで、各式の末尾を ; で区切ります。

次の行からは、更新が必要となる条件を or で繋いで、いずれかの条件を満たした場合にパラメータ設定が必要と判定するようにします。

まず、 $not($exists($lastAcSettings)) で最後のエアコンのパラメータが存在しない場合は更新を行うようにします。 次に、($millis() - $toMillis($lastAcSettings.updated_at)) > (60*60*1000) で、最後にパラメータを更新した時刻から (60 * 60 * 1000) ミリ秒、つまり 1時間経過した場合は更新します。

残りの条件は、 温度設定 temp, 運転モード設定 mode , 風量設定 vol がエアコンのパラメータの対応するフィールド temperature operation_mode air_volume と異なる場合にも更新するようにします。

判定結果は msg.payload ではなく、 msg.needAcUpdate に設定しておきます。 msg.payload には設定したいエアコンのパラメータが入っており、リクエスト送信ノードにそのまま伝搬する必要があるためです。

エアコンの操作の要・不要を判定

最後に、 msg.needAcUpdate に入っているエアコンの設定更新の要・不要に基づいて処理を分岐します。

判定結果で分岐

デプロイすると、設定した条件に基いてエアコンが制御されるのを確認できるはずです。

まとめ

以上、3回に分けて Node-RED をつかったNature Remo Cloud APIの呼び出し方と、それを使った照明およびエアコンの自動制御方法について解説しました。

今回解説した自動制御の内容は、Nature Remoアプリからの オートメーション設定 でも可能な内容ですが、 Nature Remo Cloud APIと Node-RED を用いると、現時点でオートメーション設定ではできないような複雑な条件による制御も可能となります。

ぜひこの機会に Nature Remo Cloud APIをご活用いただけますと幸いです。

Nature開発者Community

Natureのプロダクトに関する開発者向けの情報を交換する場として、 Nature開発者コミュニティ をDiscord上に立ち上げています。 今回の話題であるNature Cloud APIに関する話をする apiチャネル もありますので、ご興味がある方はぜひご確認いただければと思います。

ブログの内容に関するご質問などもお待ちしております。

discord.com


  1. 1970/1/1 00:00:00 UTCのこと