Node-redでEchonetlite単機能照明（Panasonic アドバンスリンクプラス）を制御する 改良版

# [前回](https://qiita.com/fiddler/items/46d8c67e99f0b957c1ac)の反省
* ステータス反映時にESVをチェックしていなかったためEDTがない場合(ESV0x71など）に挙動がおかしかった
:::note
ESV0x72(プロパティ値読み出し応答)0x73(プロパティ値通知)をチェック
:::
* 設定値の確認をしていなった
:::note
tidをflowコンストラクタで設定して戻りのESV0x71で同一tidであることを確認(本来はGet0x62で確認推奨）
:::
:::note
同一tidとなるまでwait0.2secをかけてループ(本来は20sec空ける必要あり）
:::
* 並列処理ができないことを考慮せず
:::note
[node-red-contrib-simple-message-queue](https://flows.nodered.org/node/node-red-contrib-simple-message-queue)を利用
:::
# さらなる反省点・感想
最初から[規格書(pdf)](https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/AIF/lt/LT_app_interface_spec_ver1.00.pdf)を読んでいればおかしな挙動はなかった。panasonic謹製アプリでもエラーとなることから無線アダプタでの処理には時間がかかる模様。
# フロー図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2231449/651315bc-bdcc-4255-8301-d07fa051e472.png)
# flow.json
```
[{"id":"b85c21964717f67c","type":"tab","label":"sample","disabled":false,"info":"","env":[]},{"id":"107c117ed3e88b49","type":"function","z":"b85c21964717f67c","name":"Parse EL1","func":"// Parse ECHONET Lite packet\n// Assumption: OPC = 1\n\n//  OUTPUT:\n//  msg.elr.ip      // ip address (String)\n//  msg.elr.ehd     // EHD (Int)\n//  msg.elr.tid     // TID (Int)\n//  msg.elr.seoj    // SEOJ ([Int:device, Int:instance])\n//  msg.elr.deoj    // DEOJ ([Int:device, Int:instance])\n//  msg.elr.esv     // ESV (Int)\n//  msg.elr.opc     // OPC (Int)\n//  msg.elr.epc     // EPC (Int)\n//  msg.elr.pdc     // PDC (Int)\n//  msg.elr.edt     // EDT ([Int]) or [](no EDT data)\n\nconst buffer = msg.payload;\nmsg.elr = null;\n\nif (buffer.length >= 14) {\n  const ehd_buffer = buffer.slice(0, 2);\n  const tid_buffer = buffer.slice(2, 4);\n  const seoj_device_buffer = buffer.slice(4, 6);\n  const seoj_instance_buffer = buffer.slice(6, 7);\n  const deoj_device_buffer = buffer.slice(7, 9);\n  const deoj_instance_buffer = buffer.slice(9, 10);\n  const esv_buffer = buffer.slice(10, 11);\n  const opc_buffer = buffer.slice(11, 12);\n  const epc_buffer = buffer.slice(12, 13);\n  const pdc_buffer = buffer.slice(13, 14);\n\n  const ehd = ehd_buffer.readUInt16BE(0);\n  const tid = tid_buffer.readUInt16BE(0);\n  const seoj_device = seoj_device_buffer.readUInt16BE(0);\n  const seoj_instance = seoj_instance_buffer.readUInt8(0);\n  const seoj = [seoj_device, seoj_instance];\n  const deoj_device = deoj_device_buffer.readUInt16BE(0);\n  const deoj_instance = deoj_instance_buffer.readUInt8(0);\n  const deoj = [deoj_device, deoj_instance];\n  const esv = esv_buffer.readUInt8(0);\n  const opc = opc_buffer.readUInt8(0);\n  const epc = epc_buffer.readUInt8(0);\n  const pdc = pdc_buffer.readUInt8(0);\n  let edt = [];\n  \n  if (buffer.length >= 15) {\n    const edt_buffer = buffer.slice(14);\n    for (var i = 0; i < edt_buffer.length; i++) {\n        edt[i] = edt_buffer.readUInt8(i);\n    }\n  }\n\n  // ECHONET Lite header check\n  if (ehd == 0x1081) {\n    msg.elr = {\n        \"ip\"            : msg.ip,\n        \"ehd\"           : ehd,\n        \"tid\"           : tid,\n        \"seoj\"          : seoj,\n        \"deoj\"          : deoj,\n        \"esv\"           : esv,\n        \"opc\"           : opc,\n        \"epc\"           : epc,\n        \"pdc\"           : pdc,\n        \"edt\"           : edt\n    };\n  }\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":230,"y":480,"wires":[["625ebf051f146439"]]},{"id":"3f9cf3c7fce15c9a","type":"udp in","z":"b85c21964717f67c","name":"EL receive","iface":"","port":"3610","ipv":"udp4","multicast":"true","group":"224.0.23.0","datatype":"buffer","x":80,"y":480,"wires":[["107c117ed3e88b49"]]},{"id":"5d2c38d0e47d2d88","type":"function","z":"b85c21964717f67c","name":"EL set","func":"//  msg.els.ip_string   // ip address (String)\n//  msg.els.tid         // TID (Int)\n//  msg.els.seoj        // SEOJ ([Int:device, Int:instance])\n//  msg.els.deoj        // DEOJ ([Int:device, Int:instance])\n//  msg.els.esv         // ESV (Int)\n//  msg.els.epc         // EPC (Int)\n//  msg.els.edt_int     // EDT ([Int])\n\nmsg.els = {\n    \"ip\" : flow.get(\"ip\"),\n    \"tid\" : flow.get(\"tid\"),\n    \"seoj\" : [0x05ff,1],\n    \"deoj\" : [0x0291,flow.get(\"num\")],\n    \"esv\" : 0x61,\n    \"epc\" : 0x80,\n    \"edt\" : [msg.payload]\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":250,"y":340,"wires":[["60024e3da3add6fc","ade4ebce158594b1"]]},{"id":"60024e3da3add6fc","type":"function","z":"b85c21964717f67c","name":"Send EL1","func":"// Send ECHONET Lite packet\n// Assumption: OPC = 1\n//\n//  Usage:\n//      Input following data\n//\n//  msg.els.ip      // ip address (String)\n//  msg.els.tid     // TID (Int)\n//  msg.els.seoj    // SEOJ ([Int:device, Int:instance])\n//  msg.els.deoj    // DEOJ ([Int:device, Int:instance])\n//  msg.els.esv     // ESV (Int)\n//  msg.els.epc     // EPC (Int)\n//  msg.els.edt     // EDT ([Int])\n//          // in case of no EDT data, no edt or []\n\nconst tid_buffer = Buffer.allocUnsafe(2);\nlet   seoj_buffer = Buffer.allocUnsafe(3);\nconst seoj = msg.els.seoj;\nlet   deoj_buffer = Buffer.allocUnsafe(3);\nconst deoj = msg.els.deoj;\nconst esv_buffer = Buffer.allocUnsafe(1);\nconst opc_buffer = Buffer.from([1]);\nconst epc_buffer = Buffer.allocUnsafe(1);\nconst pdc_buffer = Buffer.allocUnsafe(1);\nlet edt = msg.els.edt;\n\ntid_buffer.writeUInt16BE(msg.els.tid, 0);\nesv_buffer.writeUInt8(msg.els.esv, 0);\nepc_buffer.writeUInt8(msg.els.epc, 0);\n\nseoj_buffer.writeUInt16BE(seoj[0], 0);\nseoj_buffer.writeUInt8(seoj[1], 2);\ndeoj_buffer.writeUInt16BE(deoj[0], 0);\ndeoj_buffer.writeUInt8(deoj[1], 2);\n\n// edt[]からedt_bufferを作成\nif ((typeof edt) == \"undefined\") {\n    edt = [];\n}\n\nconst edt_buffer = Buffer.allocUnsafe(edt.length);\nfor (var i = 0; i < edt.length; i++) {\n    edt_buffer.writeUInt8(edt[i], i);\n}\n\npdc_buffer.writeUInt8(edt_buffer.length, 0);\n\n// Create ECHONET Lite packet\nconst el_buffer = Buffer.from([\n\t0x10, 0x81,\n\ttid_buffer[0], tid_buffer[1],\n\tseoj_buffer[0], seoj_buffer[1], seoj_buffer[2],\n\tdeoj_buffer[0], deoj_buffer[1], deoj_buffer[2],\n\tesv_buffer[0], opc_buffer[0], epc_buffer[0], pdc_buffer[0]]);\n\n// add EDT data\nif (edt_buffer.length !== 0) {\n    msg.payload = Buffer.concat([el_buffer, edt_buffer], \n        (el_buffer.length + edt_buffer.length));\n} else {\n    msg.payload = el_buffer;\n}\n\nmsg.ip = msg.els.ip;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":400,"y":340,"wires":[["086e9c37aeaeb73f"]]},{"id":"086e9c37aeaeb73f","type":"udp out","z":"b85c21964717f67c","name":"","addr":"","iface":"","port":"3610","ipv":"udp4","outport":"","base64":false,"multicast":"multi","x":560,"y":340,"wires":[]},{"id":"1bcfe5e32bfb7ee8","type":"inject","z":"b85c21964717f67c","name":"init.","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payloadType":"date","x":90,"y":40,"wires":[["477f97c0baea1ab2"]]},{"id":"477f97c0baea1ab2","type":"change","z":"b85c21964717f67c","name":"set const","rules":[{"t":"set","p":"tid","pt":"flow","to":"0","tot":"num"},{"t":"set","p":"rtntid","pt":"flow","to":"0","tot":"num"},{"t":"set","p":"ip","pt":"flow","to":"xxx.xxx.xxx.xxx","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":40,"wires":[["38f51eccb6f2d466"]]},{"id":"a3a6770475bca34a","type":"change","z":"b85c21964717f67c","name":"tid up","rules":[{"t":"set","p":"tid","pt":"flow","to":"$flowContext(\"tid\") > 254 ? 1 :$flowContext(\"tid\")+1\t","tot":"jsonata"},{"t":"set","p":"num","pt":"flow","to":"num","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":90,"y":340,"wires":[["5d2c38d0e47d2d88"]]},{"id":"dcbb3d3bd2864920","type":"change","z":"b85c21964717f67c","name":"trigger","rules":[{"t":"set","p":"trigger","pt":"msg","to":"1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":420,"wires":[["717bdb12c53fca9f"]]},{"id":"ade4ebce158594b1","type":"delay","z":"b85c21964717f67c","name":"","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":110,"y":420,"wires":[["3e68f05414f9ae49"]]},{"id":"3e68f05414f9ae49","type":"switch","z":"b85c21964717f67c","name":"loop?","property":"tid","propertyType":"flow","rules":[{"t":"eq","v":"rtntid","vt":"flow"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":270,"y":420,"wires":[["dcbb3d3bd2864920"],["5d2c38d0e47d2d88"]]},{"id":"625ebf051f146439","type":"switch","z":"b85c21964717f67c","name":"filter light","property":"elr.seoj[0]","propertyType":"msg","rules":[{"t":"eq","v":"0x0291","vt":"num"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":380,"y":480,"wires":[["0f40e70fdff1d4ac"],[]]},{"id":"0f40e70fdff1d4ac","type":"switch","z":"b85c21964717f67c","name":"ESV","property":"elr.esv","propertyType":"msg","rules":[{"t":"eq","v":"0x71","vt":"num"},{"t":"eq","v":"0x72","vt":"num"},{"t":"eq","v":"0x73","vt":"num"}],"checkall":"true","repair":false,"outputs":3,"x":510,"y":480,"wires":[["e9bd206124b655b2"],["eeeebc86a9fcfd46"],["eeeebc86a9fcfd46"]]},{"id":"e9bd206124b655b2","type":"change","z":"b85c21964717f67c","name":"set rtntid","rules":[{"t":"set","p":"rtntid","pt":"flow","to":"elr.tid","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":440,"wires":[[]]},{"id":"1e174b2edd341b8c","type":"ui_switch","z":"b85c21964717f67c","name":"","label":"switch","tooltip":"","group":"f13025dc.ef0068","order":4,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"className":"","x":130,"y":100,"wires":[["7d40b967aae0c187"]]},{"id":"7d40b967aae0c187","type":"change","z":"b85c21964717f67c","name":"set var","rules":[{"t":"change","p":"payload","pt":"msg","from":"true","fromt":"bool","to":"0x30","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"false","fromt":"bool","to":"0x31","tot":"str"},{"t":"set","p":"num","pt":"msg","to":"1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":100,"wires":[["717bdb12c53fca9f"]]},{"id":"eeeebc86a9fcfd46","type":"switch","z":"b85c21964717f67c","name":"EPC","property":"elr.epc","propertyType":"msg","rules":[{"t":"eq","v":"0x80","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":630,"y":480,"wires":[["f942085872416cdb"]]},{"id":"f942085872416cdb","type":"change","z":"b85c21964717f67c","name":"set sw","rules":[{"t":"set","p":"payload","pt":"msg","to":"elr.edt[0]","tot":"msg"},{"t":"change","p":"payload","pt":"msg","from":"0x30","fromt":"num","to":"true","tot":"bool"},{"t":"change","p":"payload","pt":"msg","from":"0x31","fromt":"num","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":70,"y":560,"wires":[["0c3fafda21ad5275"]]},{"id":"0c3fafda21ad5275","type":"switch","z":"b85c21964717f67c","name":"sw sort","property":"elr.seoj[1]","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"},{"t":"eq","v":"4","vt":"num"}],"checkall":"true","repair":false,"outputs":4,"x":220,"y":560,"wires":[["1e174b2edd341b8c"],["355136b001189a5e"],["95feeb20a4b43e0c"],["f6ae3629061dc573"]]},{"id":"717bdb12c53fca9f","type":"simple-queue","z":"b85c21964717f67c","name":"","firstMessageBypass":true,"bypassInterval":"0","x":460,"y":180,"wires":[["a3a6770475bca34a"]]},{"id":"355136b001189a5e","type":"ui_switch","z":"b85c21964717f67c","name":"","label":"switch","tooltip":"","group":"f13025dc.ef0068","order":4,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"className":"","x":130,"y":160,"wires":[["47fbee85f4135747"]]},{"id":"47fbee85f4135747","type":"change","z":"b85c21964717f67c","name":"set var","rules":[{"t":"change","p":"payload","pt":"msg","from":"true","fromt":"bool","to":"0x30","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"false","fromt":"bool","to":"0x31","tot":"str"},{"t":"set","p":"num","pt":"msg","to":"2","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":160,"wires":[["717bdb12c53fca9f"]]},{"id":"38f51eccb6f2d466","type":"function","z":"b85c21964717f67c","name":"status","func":"//  msg.els.ip_string   // ip address (String)\n//  msg.els.tid         // TID (Int)\n//  msg.els.seoj        // SEOJ ([Int:device, Int:instance])\n//  msg.els.deoj        // DEOJ ([Int:device, Int:instance])\n//  msg.els.esv         // ESV (Int)\n//  msg.els.epc         // EPC (Int)\n//  msg.els.edt_int     // EDT ([Int])\n\nmsg.els = {\n    \"ip\" : flow.get(\"ip\"),\n    \"tid\" : 0,\n    \"seoj\" : [0x05ff,1],\n    \"deoj\" : [0x0291,0],\n    \"esv\" : 0x62,\n    \"epc\" : 0x80,\n    \"edt\" : [0x30]\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":40,"wires":[["60024e3da3add6fc"]]},{"id":"95feeb20a4b43e0c","type":"ui_switch","z":"b85c21964717f67c","name":"","label":"switch","tooltip":"","group":"f13025dc.ef0068","order":4,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"className":"","x":130,"y":220,"wires":[["b94d8e1d738f9cb9"]]},{"id":"b94d8e1d738f9cb9","type":"change","z":"b85c21964717f67c","name":"set var","rules":[{"t":"change","p":"payload","pt":"msg","from":"true","fromt":"bool","to":"0x30","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"false","fromt":"bool","to":"0x31","tot":"str"},{"t":"set","p":"num","pt":"msg","to":"3","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":220,"wires":[["717bdb12c53fca9f"]]},{"id":"f6ae3629061dc573","type":"ui_switch","z":"b85c21964717f67c","name":"","label":"switch","tooltip":"","group":"f13025dc.ef0068","order":4,"width":0,"height":0,"passthru":false,"decouple":"true","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"className":"","x":130,"y":280,"wires":[["c0f226aa7e24935c"]]},{"id":"c0f226aa7e24935c","type":"change","z":"b85c21964717f67c","name":"set var","rules":[{"t":"change","p":"payload","pt":"msg","from":"true","fromt":"bool","to":"0x30","tot":"str"},{"t":"change","p":"payload","pt":"msg","from":"false","fromt":"bool","to":"0x31","tot":"str"},{"t":"set","p":"num","pt":"msg","to":"4","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":280,"wires":[["717bdb12c53fca9f"]]},{"id":"f13025dc.ef0068","type":"ui_group","name":"buttons","tab":"67ec7f3f.c65bb8","order":6,"disp":false,"width":1,"collapse":false},{"id":"67ec7f3f.c65bb8","type":"ui_tab","name":"ホーム","icon":"dashboard","disabled":false,"hidden":false}]
```