前回の記事では、 Node-REDでECHONET LiteのエアコンON/OFF操作と状態確認Getリクエストを自動送信する方法 を紹介しました。
しかし、実際のスマートホームではリモコンを人が操作してON/OFFした時の状態もコントローラー側で取得する必要があります。ECHONET Liteではデバイス側で動作状態(例:ON/OFF)が変わったときに、自動で通知(プッシュ)をする仕組みが用意されています。
それが、 インフォメーション送信(INF送信)と呼ばれる機能です。
今回はNode-REDでインフォメーション送信を受信する方法を紹介します。
Node-REDのフロー

[{"id":"function_create_set_packet","type":"function","z":"af2181cd6b94e0c2","name":"Setリクエスト作成","func":"// Setリクエスト作成\nconst buffer = Buffer.alloc(15);\nbuffer[0] = 0x10; buffer[1] = 0x81;\nbuffer[2] = 0x00; buffer[3] = 0x02;\nbuffer[4] = 0x05; buffer[5] = 0xFF; buffer[6] = 0x01;\nbuffer[7] = 0x01; buffer[8] = 0x30; buffer[9] = 0x01;\nbuffer[10] = 0x61;\nbuffer[11] = 0x01;\nbuffer[12] = 0x80;\nbuffer[13] = 0x01;\n\nif (msg.payload === true) {\n buffer[14] = 0x30;\n flow.set('lastCommand', true);\n} else {\n buffer[14] = 0x31;\n flow.set('lastCommand', false);\n}\n\nmsg.payload = buffer;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":280,"wires":[["udp_out_set"]]},{"id":"udp_out_set","type":"udp out","z":"af2181cd6b94e0c2","name":"UDP送信(Set)","addr":"192.168.1.14","iface":"","port":"3610","ipv":"udp4","outport":"","base64":false,"multicast":"false","x":760,"y":280,"wires":[]},{"id":"udp_in_common","type":"udp in","z":"af2181cd6b94e0c2","name":"UDP受信(共通)","iface":"","port":"3610","ipv":"udp4","multicast":"true","group":"224.0.23.0","datatype":"buffer","x":260,"y":380,"wires":[["function_parse_inf","function_classify_response"]]},{"id":"5611bd4b70772e2b","type":"inject","z":"af2181cd6b94e0c2","name":"ON","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":250,"y":240,"wires":[["function_create_set_packet"]]},{"id":"092626da080407d6","type":"inject","z":"af2181cd6b94e0c2","name":"OFF","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":250,"y":300,"wires":[["function_create_set_packet"]]},{"id":"function_parse_inf","type":"function","z":"af2181cd6b94e0c2","name":"INF受信解析","func":"// INF(0x73)の受信を解析する\n\nconst buf = Buffer.from(msg.payload);\n\nif (buf.length < 14) {\n return null; // 異常パケットは無視\n}\n\nconst esv = buf[10];\n\nif (esv !== 0x73) {\n return null; // INF(0x73)以外は無視\n}\n\nconst epc = buf[12];\nconst pdc = buf[13];\nconst edt = buf[14];\n\nif (epc === 0x80 && pdc === 0x01) {\n if (edt === 0x30) {\n msg.payload = \"【INF通知】エアコンがONになりました\";\n } else if (edt === 0x31) {\n msg.payload = \"【INF通知】エアコンがOFFになりました\";\n } else {\n msg.payload = \"【INF通知】未知の状態 (0x\" + edt.toString(16).toUpperCase() + \")\";\n }\n return msg;\n}\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":480,"wires":[["debug_inf_result"]]},{"id":"debug_inf_result","type":"debug","z":"af2181cd6b94e0c2","name":"INF通知表示","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","statusVal":"","statusType":"auto","x":730,"y":480,"wires":[]},{"id":"debug_result","type":"debug","z":"af2181cd6b94e0c2","name":"状態表示","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","statusVal":"","statusType":"auto","x":940,"y":360,"wires":[]},{"id":"function_parse_get_response","type":"function","z":"af2181cd6b94e0c2","name":"Get応答解析","func":"// Get応答解析\nconst buf = Buffer.from(msg.payload);\n\nconst epc = buf[12];\nconst edt = buf[14];\n\nif (epc === 0x80) {\n if (edt === 0x30) {\n msg.payload = \"【状態確認】エアコンは ON です\";\n } else if (edt === 0x31) {\n msg.payload = \"【状態確認】エアコンは OFF です\";\n } else {\n msg.payload = \"【状態確認】未知の値 (0x\" + edt.toString(16).toUpperCase() + \")\";\n }\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":360,"wires":[["debug_result"]]},{"id":"function_classify_response","type":"function","z":"af2181cd6b94e0c2","name":"受信パケット分類","func":"// UDP-Inから受信したパケットを分類する\nconst buf = Buffer.from(msg.payload);\n\nif (buf.length < 14) {\n return null;\n}\n\nconst esv = buf[10];\n\nif (esv === 0x72) {\n // Get応答\n msg.topic = \"get_response\";\n return [msg, null];\n} else if (esv === 0x71) {\n // SetC応答(成功時)\n // Getリクエストを自動で発行\n const buffer = Buffer.alloc(14);\n buffer[0] = 0x10; buffer[1] = 0x81;\n buffer[2] = 0x00; buffer[3] = 0x03;\n buffer[4] = 0x05; buffer[5] = 0xFF; buffer[6] = 0x01;\n buffer[7] = 0x01; buffer[8] = 0x30; buffer[9] = 0x01;\n buffer[10] = 0x62;\n buffer[11] = 0x01;\n buffer[12] = 0x80;\n buffer[13] = 0x00;\n\n msg.payload = buffer;\n return [null, msg];\n}\n\nreturn null;","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":360,"wires":[["function_parse_get_response"],["udp_out_set"]]}]
フローの解説

前回と違うのはUDP INノードをマルチキャストに設定し、グループをECHONET Lite標準マルチキャストの224.0.23.0にしたこと。
「既にポートが使用されています」と表示されますが、特に問題なく動作しました。
実行結果

Node-RED側からINF通知と状態確認(GET)はできたのですが、受信できる時とできない時がありました。

Emulatorのパケットモニターでも確認できます。

UDP送信するとレスポンスが返ってきますが、念の為にUDP INノードの後にdelayノードをいれ200msの遅延をいれました。
INF通知があればGETしなくてもよい?
そもそも状態が変わったときにINF通知があればGETしなくてもよさそうです。
また、先ほどdelayノードを入れましたが、INF通知が来ないこともありました。(原因不明)

シンプルにUDPーINノードはマルチキャストだけに変更。

Emulatorから動作状態をOFFに変更。

このようにINF通知を受け取ることができました。
ECHONET Lite用のアプリケーション開発
上記の修正フローだとリクエストが成功したかどうかの確認がされていないので、今後、アプリケーション開発するにあたっては、引き続き検証が必要のようです。