├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── socketio.html └── socketio.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 wperw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-red-contrib-socketio 2 | Implementation for [Node-RED](https://nodered.org/) of the popular [Socket.IO](http://socket.io/). 3 | 4 | ## Installation 5 | To install node-red-contrib-socketio use this command 6 | 7 | `npm i node-red-contrib-socketio` 8 | 9 | ## Composition 10 | The Socket.IO implementation is made with 11 | * 1 configuration Node that holds the server definitions and the user can decide to bind the SocketIO server on the Node-RED port or bind it to another port 12 | * 1 input node where the user adds all the `topic`s in which they are interested 13 | * 1 output node that sends the data received into `msg.payload` 14 | * 1 node to join a Socket IO room 15 | * 1 node to leave a Socket IO room 16 | 17 | ## Usage 18 | To see an example usage go to [Example Chat App](https://flows.nodered.org/flow/71f7da3a14951acb67f94bac1f71812a) 19 | 20 | ## License 21 | MIT 22 | 23 | ## Thanks 24 | Thank to: 25 | * @nexflo for translating the comments in English and for pre-sending control data 26 | * @bimalyn-IBM for implementig rooms 27 | * @essuraj for implementig rooms listing node 28 | * @cazellap for pushong adding compatibility to socketIO 3.0 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-socketio", 3 | "version": "1.1.0", 4 | "description": "Implementation for Node-RED of a SocketIO Sever", 5 | "dependencies": { 6 | "socket.io": ">=3.1.0" 7 | }, 8 | "node-red": { 9 | "nodes": { 10 | "socket-io": "socketio.js" 11 | } 12 | }, 13 | "main": "socketio.js", 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/wperw/node-red-contrib-socketio.git" 20 | }, 21 | "keywords": [ 22 | "node-red" 23 | ], 24 | "author": "Telemaco Gallimberti", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/wperw/node-red-contrib-socketio/issues" 28 | }, 29 | "homepage": "https://github.com/wperw/node-red-contrib-socketio#readme" 30 | } 31 | -------------------------------------------------------------------------------- /socketio.html: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 110 | 111 | 124 | 125 | 139 | 140 | 141 | 157 | 158 | 168 | 169 | 187 | 188 | 189 | 205 | 206 | 216 | 217 | 226 | 227 | 228 | 244 | 245 | 255 | 256 | 265 | 266 | 282 | 283 | 284 | 285 | 296 | 297 | 301 | -------------------------------------------------------------------------------- /socketio.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Gallimberti Telemaco 02/02/2017 3 | **/ 4 | 5 | module.exports = function(RED) { 6 | const { Server } = require("socket.io"); 7 | var io; 8 | var customProperties = {}; 9 | 10 | function socketIoConfig(n) { 11 | RED.nodes.createNode(this, n); 12 | var node = this; 13 | this.port = n.port || 80; 14 | this.sendClient = n.sendClient; 15 | this.path = n.path || "/socket.io"; 16 | this.bindToNode = n.bindToNode || false; 17 | 18 | if (this.bindToNode) { 19 | io = new Server(RED.server); 20 | } else { 21 | io = new Server(); 22 | io.serveClient(node.sendClient); 23 | io.path(node.path); 24 | io.listen(node.port); 25 | } 26 | var bindOn = this.bindToNode 27 | ? "bind to Node-red port" 28 | : "on port " + this.port; 29 | node.log("Created server " + bindOn); 30 | 31 | node.on("close", function() { 32 | io.close(); 33 | }); 34 | } 35 | 36 | function socketIoIn(n) { 37 | RED.nodes.createNode(this, n); 38 | var node = this; 39 | this.name = n.name; 40 | this.server = RED.nodes.getNode(n.server); 41 | this.rules = n.rules || []; 42 | 43 | this.specialIOEvent = [ 44 | // Events emitted by the Manager: 45 | { v: "open" }, 46 | { v: "error" }, 47 | { v: "close" }, 48 | { v: "ping" }, 49 | { v: "packet" }, 50 | { v: "reconnect_attempt" }, 51 | { v: "reconnect" }, 52 | { v: "reconnect_error" }, 53 | { v: "reconnect_failed" }, 54 | 55 | // Events emitted by the Socket: 56 | { v: "connect" }, 57 | { v: "connect_error" }, 58 | { v: "disconnect" } 59 | ]; 60 | 61 | function addListener(socket, val, i) { 62 | socket.on(val.v, function(msgin) { 63 | var msg = {}; 64 | RED.util.setMessageProperty(msg, "payload", msgin, true); 65 | RED.util.setMessageProperty(msg, "socketIOEvent", val.v, true); 66 | RED.util.setMessageProperty(msg, "socketIOId", socket.id, true); 67 | if ( 68 | customProperties[RED.util.getMessageProperty(msg, "socketIOId")] != 69 | null 70 | ) { 71 | RED.util.setMessageProperty( 72 | msg, 73 | "socketIOStaticProperties", 74 | customProperties[RED.util.getMessageProperty(msg, "socketIOId")], 75 | true 76 | ); 77 | } 78 | node.send(msg); 79 | }); 80 | } 81 | 82 | io.on("connection", function(socket) { 83 | node.rules.forEach(function(val, i) { 84 | addListener(socket, val, i); 85 | }); 86 | //Adding support for all other special messages 87 | node.specialIOEvent.forEach(function(val, i) { 88 | addListener(socket, val, i); 89 | }); 90 | }); 91 | } 92 | 93 | function socketIoOut(n) { 94 | RED.nodes.createNode(this, n); 95 | var node = this; 96 | this.name = n.name; 97 | this.server = RED.nodes.getNode(n.server); 98 | 99 | node.on("input", function(msg) { 100 | //check if we need to add properties 101 | if (RED.util.getMessageProperty(msg, "socketIOAddStaticProperties")) { 102 | //check if we have already added some properties for this socket 103 | if ( 104 | customProperties[RED.util.getMessageProperty(msg, "socketIOId")] != 105 | null 106 | ) { 107 | //check if object as property 108 | var keys = Object.getOwnPropertyNames( 109 | RED.util.getMessageProperty(msg, "socketIOAddStaticProperties") 110 | ); 111 | var tmp = 112 | customProperties[RED.util.getMessageProperty(msg, "socketIOId")]; 113 | for (var i = 0; i < keys.length; i++) { 114 | tmp[keys[i]] = RED.util.getMessageProperty( 115 | msg, 116 | "socketIOAddStaticProperties" 117 | )[keys[i]]; 118 | } 119 | } else { 120 | //add new properties 121 | customProperties[ 122 | RED.util.getMessageProperty(msg, "socketIOId") 123 | ] = RED.util.getMessageProperty(msg, "socketIOAddStaticProperties"); 124 | } 125 | } 126 | 127 | 128 | switch (RED.util.getMessageProperty(msg, "socketIOEmit")) { 129 | case "broadcast.emit": 130 | //Return to all but the caller 131 | if ( 132 | io.sockets.sockets.get(RED.util.getMessageProperty(msg, "socketIOId")) 133 | ) { 134 | io.sockets.sockets.get( 135 | RED.util.getMessageProperty(msg, "socketIOId") 136 | ).broadcast.emit(msg.socketIOEvent, msg.payload); 137 | } 138 | break; 139 | case "emit": 140 | //Return only to the caller 141 | if ( 142 | io.sockets.sockets.get(RED.util.getMessageProperty(msg, "socketIOId")) 143 | ) { 144 | io.sockets.sockets.get( 145 | RED.util.getMessageProperty(msg, "socketIOId") 146 | ).emit(msg.socketIOEvent, msg.payload); 147 | } 148 | break; 149 | case "room": 150 | //emit to all 151 | if (msg.room) { 152 | io.to(msg.room).emit(msg.socketIOEvent, msg.payload); 153 | } 154 | break; 155 | default: 156 | //emit to all 157 | io.emit(msg.socketIOEvent, msg.payload); 158 | } 159 | }); 160 | } 161 | 162 | function socketIoJoin(n) { 163 | RED.nodes.createNode(this, n); 164 | var node = this; 165 | this.name = n.name; 166 | this.server = RED.nodes.getNode(n.server); 167 | 168 | node.on("input", function(msg) { 169 | if (io.sockets.sockets.get(RED.util.getMessageProperty(msg, "socketIOId"))) { 170 | io.sockets.sockets.get(RED.util.getMessageProperty(msg, "socketIOId")).join( 171 | msg.payload.room 172 | ); 173 | node.send(msg); 174 | } 175 | }); 176 | } 177 | 178 | function socketIoRooms(n) { 179 | RED.nodes.createNode(this, n); 180 | var node = this; 181 | this.name = n.name; 182 | this.server = RED.nodes.getNode(n.server); 183 | 184 | node.on("input", function(msg) { 185 | node.send({ payload: io.sockets.adapter.rooms }); 186 | }); 187 | } 188 | 189 | function socketIoLeave(n) { 190 | RED.nodes.createNode(this, n); 191 | var node = this; 192 | this.name = n.name; 193 | this.server = RED.nodes.getNode(n.server); 194 | 195 | node.on("input", function(msg) { 196 | if (io.sockets.sockets.get(RED.util.getMessageProperty(msg, "socketIOId"))) { 197 | io.sockets.sockets.get( 198 | RED.util.getMessageProperty(msg, "socketIOId") 199 | ).leave(msg.payload.room); 200 | } 201 | }); 202 | } 203 | 204 | RED.nodes.registerType("socketio-config", socketIoConfig); 205 | RED.nodes.registerType("socketio-in", socketIoIn); 206 | RED.nodes.registerType("socketio-out", socketIoOut); 207 | RED.nodes.registerType("socketio-join", socketIoJoin); 208 | RED.nodes.registerType("socketio-rooms", socketIoRooms); 209 | RED.nodes.registerType("socketio-leave", socketIoLeave); 210 | }; 211 | --------------------------------------------------------------------------------