この記事は、PlayCanvas Advent Calendar 2022のカレンダーの4日目の記事でしたが、ギリギリの25日目にアップしました。
Vol.1では全体のシステムを説明しましたが、Vol.2ではPlayCanvas側とNode-REDのフローの説明をします。
PlayCanvasとNode-REDとKNXでSociety5.0(仮想空間と現実空間の融合)をした話 Vol.1|デジタルライト(Digital-light.jp)
PlayCanvas側
こちらがPlayCanvasのEditor画面。
mqtt.jsの準備
こちらのサイトからmqtt.jsをダウンロード。
このファイルをPlayCanvasの左下にある
Setting>SCRIPTSLOADING ORDER
にてmqttで通信するためのmqtt.jsを読み込む設定をします。
manage.jsを作成
ProjectのrootにManage.jsというスクリプトを追加してMQTTの通信を行えるようにします。
manage.jsはこちら
var Manage = pc.createScript('manage'); // initialize code called once per entity Manage.prototype.initialize = function () { let client = mqtt.connect('wss://public:public@public.cloud.shiftr.io:443'); let topic = "toPlayCanvas"; let topic2 = "toPlayCanvasSensors"; // Subscribeする client.subscribe(topic); client.subscribe(topic2); // プロジェクト全体で使えるようにする this.app.client = client; // 受信したら表示 client.on('message', (topic2, message) => this.sensors(message)); client.on('message', (topic, message) => this.control(message)); }; Manage.prototype.control = function (message) { // MQTTから受信したデータ(buffer)を文字列にする var data = message.toString(); // JSON形式にパース data = JSON.parse(data); //console.log(data); // どのデバイスから来たのかを確認 // fixtureとcommandを取得 var fixture = data.fixture; var command = data.command; // fixtureのentityを取るためにappを取得 var app = this.app; // LightのEntityを指定する this.spot = app.root.findByName(fixture); // commandでOn/Off if (command == "on" || command == "On") { this.spot.light.enabled = true; } else if (command == "off" || command == "Off") { this.spot.light.enabled = false; } }; Manage.prototype.sensors = function (message) { // MQTTから受信したデータ(buffer)を文字列にする var data = message.toString(); // JSON形式にパース data = JSON.parse(data); // fixtureとcommandを取得 var fixture = data.fixture; var value = data.value; // fixtureのentityを取るためにappを取得 var app = this.app; // LightのEntityを指定する app.root.findByName(fixture).findByName("Text").element.text = value; }; // update code called every frame Manage.prototype.update = function (dt) { };
2Dスクリーンのボタン設定
2Dスクリーンの中に、
– RoleScreen
– Sensors
– Btn-Fixture1
– Btn-Fixture2
というオブジェクトを設置。
RoleScreenとBtn-Fixtureにはbutton.jsというスクリプトを追加。
これで、ボタンを押したときのアクションを設定しています。
また、scriptのattributeで、ボタンの種類やコマンドを変更できるようにしました。
var Button = pc.createScript('button'); // Btnの器具とOn/Offのどちらか Button.attributes.add("fixture", { type: "string" }); Button.attributes.add("command", { type: "string" }); // initialize code called once per entity Button.prototype.initialize = function () { this.entity.button.on("click", this.onPress, this); }; // update code called every frame Button.prototype.update = function (dt) { }; Button.prototype.onPress = function (dt) { var fixture = this.fixture; var command = this.command; let client = this.app.client; let topic = "fromPlayCanvas"; let metric = '{"fixture":"' + fixture + '", "command":"' + command + '"}'; // Publishする client.publish(topic, metric); };
2DスクリーンのSensors
2DスクリーンにあるSensorsには、KNXのセンサーから来た情報(温度、湿度、CO2濃度)が表示されます。
これは、manage.jsにある下記のスクリプトにてアップデートしました。
Manage.prototype.sensors = function (message) { // MQTTから受信したデータ(buffer)を文字列にする var data = message.toString(); // JSON形式にパース data = JSON.parse(data); // fixtureとcommandを取得 var fixture = data.fixture; var value = data.value; // fixtureのentityを取るためにappを取得 var app = this.app; // LightのEntityを指定する app.root.findByName(fixture).findByName("Text").element.text = value; };
照明器具のOn/Off
PlayCanvas内にある照明器具のOn/Offについてもmanage.jsにある下記スクリプト部分で操作しています。
Manage.prototype.control = function (message) { // MQTTから受信したデータ(buffer)を文字列にする var data = message.toString(); // JSON形式にパース data = JSON.parse(data); //console.log(data); // どのデバイスから来たのかを確認 // fixtureとcommandを取得 var fixture = data.fixture; var command = data.command; // fixtureのentityを取るためにappを取得 var app = this.app; // LightのEntityを指定する this.spot = app.root.findByName(fixture); // commandでOn/Off if (command == "on" || command == "On") { this.spot.light.enabled = true; } else if (command == "off" || command == "Off") { this.spot.light.enabled = false; } };
start.jsは使っていない
プロジェクトにstart.jsというスクリプトがありますが、これは使っていません。消していないだけ。
Node-REDのフロー
まずはこちらがNode-REDのフローです。
[{"id":"5a24fa3c7c901b55","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/1/0","outputtopic":"","dpt":"1.001","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":640,"y":480,"wires":[["4ea0c1e1bf81f84f"]]},{"id":"4ea0c1e1bf81f84f","type":"debug","z":"6b88292094289bed","name":"KNX Msg","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":920,"y":580,"wires":[]},{"id":"9e28efce84fbeed4","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/1/2","outputtopic":"","dpt":"1.001","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"Fixture1","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":190,"y":720,"wires":[["8efe420c30794133"]]},{"id":"45c57ba18466255a","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/1/1","outputtopic":"","dpt":"1.001","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":640,"y":540,"wires":[["4ea0c1e1bf81f84f"]]},{"id":"73a957b59ffa2844","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/1/3","outputtopic":"","dpt":"1.001","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"Fixture2","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":190,"y":780,"wires":[["8efe420c30794133"]]},{"id":"ab949fb04cf74de4","type":"mqtt out","z":"6b88292094289bed","name":"","topic":"toPlayCanvas","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"48db53855e8c1474","x":640,"y":820,"wires":[]},{"id":"8efe420c30794133","type":"function","z":"6b88292094289bed","name":"fixForMQTT","func":"var fixture = msg.devicename;\nvar value = msg.payload;\nvar command = msg.payloadsubtypevalue;\n\nmsg.payload = {\n \"device\" : \"light\",\n \"fixture\": fixture,\n \"value\" : value,\n \"command\" : command\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":760,"wires":[["ab949fb04cf74de4","1c447979aa784221"]]},{"id":"e71b07318646d1cc","type":"mqtt in","z":"6b88292094289bed","name":"","topic":"fromPlayCanvas","qos":"0","datatype":"json","broker":"48db53855e8c1474","nl":false,"rap":true,"rh":0,"x":140,"y":220,"wires":[["f7c77fe62bf3b5ae"]]},{"id":"bf6c5bf401bfdb16","type":"switch","z":"6b88292094289bed","name":"Fixtures","property":"payload.fixture","propertyType":"msg","rules":[{"t":"eq","v":"Fixture1","vt":"str"},{"t":"eq","v":"Fixture2","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":280,"y":420,"wires":[["3fdce79917961677"],["4f4f0571766b89a5"]]},{"id":"e1ac979c383b71ba","type":"change","z":"6b88292094289bed","name":"TrueFalse","rules":[{"t":"change","p":"payload.command","pt":"msg","from":"off","fromt":"str","to":"false","tot":"bool"},{"t":"change","p":"payload.command","pt":"msg","from":"on","fromt":"str","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":240,"y":360,"wires":[["bf6c5bf401bfdb16"]]},{"id":"3fdce79917961677","type":"change","z":"6b88292094289bed","name":"Fixture1","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.command","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":400,"wires":[["5a24fa3c7c901b55"]]},{"id":"4f4f0571766b89a5","type":"change","z":"6b88292094289bed","name":"Fixture2","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.command","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":500,"wires":[["45c57ba18466255a"]]},{"id":"54bdc424c639aadd","type":"inject","z":"6b88292094289bed","name":"Login","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payloadType":"date","x":750,"y":100,"wires":[["ad69445f95fb6587"]]},{"id":"ad69445f95fb6587","type":"function","z":"6b88292094289bed","name":"nwk/n/r","func":"msg.payload = '';\nnode.send(msg);\nmsg.payload = 'nwk\\n\\r';\nnode.send(msg);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":900,"y":100,"wires":[["affdc43c79df2f38"]]},{"id":"affdc43c79df2f38","type":"tcp request","z":"6b88292094289bed","server":"192.168.1.231","port":"23","out":"sit","splitc":" ","name":"Lutron","x":1130,"y":280,"wires":[["219c3c858dffadc3"]]},{"id":"219c3c858dffadc3","type":"function","z":"6b88292094289bed","name":"buffer.toString","func":"var b=Buffer.from(msg.payload);\nvar s=b.toString();\nvar out=s;\nmsg.payload = out;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1240,"y":360,"wires":[["71279b9cbda1cb32"]]},{"id":"71279b9cbda1cb32","type":"debug","z":"6b88292094289bed","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1310,"y":420,"wires":[]},{"id":"912ad7bccb564438","type":"inject","z":"6b88292094289bed","name":"Morning","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":760,"y":140,"wires":[["18997bd976b0decc"]]},{"id":"5ddde5d4caf017e1","type":"inject","z":"6b88292094289bed","name":"Afternoon","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":760,"y":220,"wires":[["ce280085f9f01071"]]},{"id":"cdba7d9bd1156775","type":"inject","z":"6b88292094289bed","name":"Night","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":770,"y":300,"wires":[["d8bf841334352da9"]]},{"id":"18997bd976b0decc","type":"function","z":"6b88292094289bed","name":"#DEVICE,TESTQS,70,3","func":"msg.payload = '#DEVICE,TESTQS,70,3\\r\\n';\nnode.send(msg);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":180,"wires":[["affdc43c79df2f38"]]},{"id":"ce280085f9f01071","type":"function","z":"6b88292094289bed","name":"#DEVICE,TESTQS,71,3","func":"msg.payload = '#DEVICE,TESTQS,71,3\\r\\n';\nnode.send(msg);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":260,"wires":[["affdc43c79df2f38"]]},{"id":"d8bf841334352da9","type":"function","z":"6b88292094289bed","name":"#DEVICE,TESTQS,76,3","func":"msg.payload = '#DEVICE,TESTQS,76,3\\r\\n';\nnode.send(msg);","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":830,"y":340,"wires":[["affdc43c79df2f38"]]},{"id":"f7c77fe62bf3b5ae","type":"switch","z":"6b88292094289bed","name":"Facilities","property":"payload.fixture","propertyType":"msg","rules":[{"t":"eq","v":"Role","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":200,"y":300,"wires":[["34306b8e7462e4aa"],["e1ac979c383b71ba"]]},{"id":"34306b8e7462e4aa","type":"switch","z":"6b88292094289bed","name":"Scenes","property":"payload.command","propertyType":"msg","rules":[{"t":"eq","v":"morning","vt":"str"},{"t":"eq","v":"afternoon","vt":"str"},{"t":"eq","v":"night","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":440,"y":280,"wires":[["18997bd976b0decc"],["ce280085f9f01071"],["d8bf841334352da9"]]},{"id":"ed9c3ecc9694318c","type":"comment","z":"6b88292094289bed","name":"FromPlayCanvas","info":"","x":140,"y":160,"wires":[]},{"id":"61a361abf5d6a139","type":"comment","z":"6b88292094289bed","name":"Lutron RoleScreen","info":"","x":770,"y":60,"wires":[]},{"id":"f78842da7bb201ea","type":"comment","z":"6b88292094289bed","name":"toPlayCanvas","info":"","x":650,"y":780,"wires":[]},{"id":"aa13c38c67551606","type":"comment","z":"6b88292094289bed","name":"toKNX","info":"","x":630,"y":440,"wires":[]},{"id":"b7803c48257d8ad3","type":"comment","z":"6b88292094289bed","name":"FromKNX","info":"","x":180,"y":660,"wires":[]},{"id":"1c447979aa784221","type":"debug","z":"6b88292094289bed","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":680,"wires":[]},{"id":"37529ea891b14a1a","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/2/1","outputtopic":"","dpt":"9.007","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"Humidity","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":200,"y":940,"wires":[["70e19065c3e24fd1"]]},{"id":"60f01d488e29c326","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/2/0","outputtopic":"","dpt":"9.001","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"Temprature","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":200,"y":880,"wires":[["70e19065c3e24fd1"]]},{"id":"92ec77e0723f9b33","type":"knxUltimate","z":"6b88292094289bed","server":"7512ccc946517e75","topic":"0/2/2","outputtopic":"","dpt":"9.008","initialread":0,"notifyreadrequest":false,"notifyresponse":false,"notifywrite":true,"notifyreadrequestalsorespondtobus":false,"notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized":"0","listenallga":false,"name":"CO2","outputtype":"write","outputRBE":true,"inputRBE":false,"formatmultiplyvalue":1,"formatnegativevalue":"leave","formatdecimalsvalue":999,"passthrough":"no","x":180,"y":1000,"wires":[["70e19065c3e24fd1"]]},{"id":"70e19065c3e24fd1","type":"function","z":"6b88292094289bed","name":"fixForMQTT","func":"var fixture = msg.devicename;\nvar value = msg.payload;\nvar command = msg.payloadsubtypevalue;\n\nmsg.payload = {\n \"device\" : \"light\",\n \"fixture\": fixture,\n \"value\" : value,\n \"command\" : command\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":960,"wires":[["96f90508771c3e0b","fb7bd903b682592a"]]},{"id":"fb7bd903b682592a","type":"mqtt out","z":"6b88292094289bed","name":"","topic":"toPlayCanvasSensors","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"48db53855e8c1474","x":680,"y":960,"wires":[]},{"id":"96f90508771c3e0b","type":"debug","z":"6b88292094289bed","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":580,"y":1060,"wires":[]},{"id":"190ad6297698f57c","type":"inject","z":"6b88292094289bed","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":510,"y":900,"wires":[["fb7bd903b682592a"]]},{"id":"7512ccc946517e75","type":"knxUltimate-config","host":"192.168.1.102","port":"3671","physAddr":"15.15.22","hostProtocol":"TunnelUDP","suppressACKRequest":false,"csv":"","KNXEthInterface":"Auto","KNXEthInterfaceManuallyInput":"","statusDisplayLastUpdate":true,"statusDisplayDeviceNameWhenALL":true,"statusDisplayDataPoint":false,"stopETSImportIfNoDatapoint":"stop","loglevel":"error","name":"KNX Gateway","localEchoInTunneling":true,"delaybetweentelegrams":"50","delaybetweentelegramsfurtherdelayREAD":"1","ignoreTelegramsWithRepeatedFlag":false,"autoReconnect":"yes"},{"id":"48db53855e8c1474","type":"mqtt-broker","name":"MQTT","broker":"mqtt://public:public@public.cloud.shiftr.io","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""}]
* ごめんなさい。詳細はフローみてください。
まとめ
PlayCanvasアドベントカレンダーもやる気まんまんだったのですが、11月後半から仕事が激増し、この記事を含め2記事しかアップできませんでした。
仕事が増えて嬉しい悲鳴ですが、引き続きいろいろ勉強しながらがんばります。