Vue.js と Node-RED を WebSocket でつなぐメモです。
やりたいこと
Vue.js と Node-RED を WebSocket でつないでリアルタイムに動かします。Vue.jsからは入力されたメッセージをNode-REDに送り、Node-REDからは1秒ごとにタイムスタンプを送って Vue.js の表示に反映させます。
フロントエンド側 WebSocket の文献
MDN の文献がよくまとまってますね。ありがたい。
そして、
WebSocket クライアントアプリケーションの記述 – Web API | MDN
こちらの方に具体的な実装が書いていて素晴らしい。
webSocket = new WebSocket(url, protocols);
サブプロトコルを指定できる第二引数とか知らなかった。
Node-RED の WebSocket 実装
さて、 Node-RED の良いところとして WebSocket かんたんに立ち上げられることです。
もちろん標準で HTTP ノードもあるので、 REST な API をつくってやり取りするのもアリですが、今回はリアルタイムなやり取りをしたいので WebSocket ノードを使います。
フローを作る
ごくシンプルに、タイムスタンプを WebSocket でデータ送り、WebSocket で受信したデータを debug ノードでやり取りするフローを作ります。
websocket out ノードの設定
websocket out ノードの設定です。
種類は 待ち受け
に設定し、パスの設定は
送信受信設定を ペイロードの送信/受信
、パスの設定を /ws/vue
にしています。
websocket in ノードの設定
websocket in ノードの設定です。
その上でパスの設定は websocket out ノードと同じものを指定しています。
inject ノードの設定
injectノードの設定です。
1秒ごとタイムスタンプを送る設定にしておきます。こうすると、Vueでリアルタイムに変わり分かりやすいです。
- 繰り返し
- 指定した時間間隔
- 時間間隔 1 秒
で設定します。
JSONのフローデータはこちらです。
[{"id":"8778eae4.9f9b28","type":"websocket out","z":"a25f5900.269728","name":"","server":"d0e2edc9.b103f","client":"","x":740,"y":240,"wires":[]},{"id":"a0b6642d.9dac78","type":"websocket in","z":"a25f5900.269728","name":"","server":"d0e2edc9.b103f","client":"","x":530,"y":300,"wires":[["d7a0b10a.d7789"]]},{"id":"f24170c7.5b2fb","type":"inject","z":"a25f5900.269728","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"onceDelay":0.1,"x":510,"y":240,"wires":[["8778eae4.9f9b28"]]},{"id":"d7a0b10a.d7789","type":"debug","z":"a25f5900.269728","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":740,"y":300,"wires":[]},{"id":"d0e2edc9.b103f","type":"websocket-listener","z":"","path":"/ws/vue","wholemsg":"false"}]
Vue のソースコード
今回は、サッとテストしたかったのでローカルでつなげていますが、もちろん、Node-REDをどこかに置いて公開されているサーバー間で対応することも可能です。
ソースコードはこちらです。見た目の調整に BootStrapVue を使っています。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue.js collaborate to Node-RED WebSocket</title> <!-- Load required Bootstrap and BootstrapVue CSS --> <link rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.css" /> <!-- Load polyfills to support older browsers --> <script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver" crossorigin="anonymous"></script> <!-- Load Vue followed by BootstrapVue --> <script src="//unpkg.com/vue@latest/dist/vue.min.js"></script> <script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script> <!-- Load the following for BootstrapVueIcons support --> <script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue-icons.min.js"></script> </head> <body> <div class="container"> <div id="appWebsocket"> <div class="row"> <div class="col"> <h1>Vue.js collaborate to Node-RED WebSocket</h1> </div> </div> <div class="form-row"> <div class="col"> <input type="email" class="form-control" id="inputMessage" v-model="message"> </div> <div class="col"> <b-button v-on:click="hanlderSendMessage">SendMessage</b-button> </div> </div> <div class="row"> <div class="col"> <h2>Message from Node-RED WebSocket : </h2> <pre><code>{{ response }}</code></pre> </div> </div> </div> </div> <script> const app = new Vue({ el: '#appWebsocket', data: { response: '', message: '', ws: null }, methods: { // データを送るボタン hanlderSendMessage: async function () { // JSON データは JSON.stringify で文字列にしてから送っています let _message = { 'message': this.message }; await this.ws.send(JSON.stringify(_message)); }, // WebSocket が接続された open イベント hanlderWebSocketOpen: function (event) { this.ws.send('Hello! Node-RED!'); }, // WebSocket からメッセージを受け取った message イベント hanlderWebSocketMessage: function (event) { console.log('timestamp : ', event.data); this.response = event.data; } } , mounted() { console.log('mounted'); // Node-RED のURL+WebSocketのパスでつなげる this.ws = new WebSocket('ws://localhost:1880/ws/vue'); // WebSocket が接続された open イベント設定 this.ws.onopen = this.hanlderWebSocketOpen; // WebSocket からメッセージを受け取った message イベント設定 this.ws.onmessage = this.hanlderWebSocketMessage; } }) </script> </body> </html>
動かしてみる
まず表示するだけで、Vue側から接続され 接続数 1
と表示されます。
Node-RED → Vue.js
Node-REDからは1秒ごとにタイムスタンプが送られています。
Message from Node-RED WebSocket :
のところに、タイムスタンプが1秒ごと表示され、無事にNode-REDからデータを受信できていることが確認できます。
Vue.js → Node-RED
つづいて、Vue.jsからデータを送ってみましょう。
たとえば「やっほー!Node-RED!」とVue側で打ち込んでSendMessageボタンをクリックすると、Node-RED側でデータが受信されNode-REDのデバッグタブには以下のように表示されます。
JSON ノードで受信データを変換するとより扱いやすい
このままですと、Node-REDはJSONではなく文字列と把握しています。
ですので、JSON ノードを加えると扱いやすくなります。
このような形で、JSONデータが Node-RED 内部でオブジェクトとして使えます。
JSONのフローデータはこちらです。
[{"id":"8778eae4.9f9b28","type":"websocket out","z":"a25f5900.269728","name":"","server":"d0e2edc9.b103f","client":"","x":740,"y":240,"wires":[]},{"id":"a0b6642d.9dac78","type":"websocket in","z":"a25f5900.269728","name":"","server":"d0e2edc9.b103f","client":"","x":390,"y":300,"wires":[["34732b65.9f31c4"]]},{"id":"f24170c7.5b2fb","type":"inject","z":"a25f5900.269728","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"onceDelay":0.1,"x":510,"y":240,"wires":[["8778eae4.9f9b28"]]},{"id":"d7a0b10a.d7789","type":"debug","z":"a25f5900.269728","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":740,"y":300,"wires":[]},{"id":"34732b65.9f31c4","type":"json","z":"a25f5900.269728","name":"","property":"payload","action":"","pretty":false,"x":570,"y":300,"wires":[["d7a0b10a.d7789"]]},{"id":"d0e2edc9.b103f","type":"websocket-listener","z":"","path":"/ws/vue","wholemsg":"false"}]