From d1569975bb18f4359fac18aa98f55b69c248a3ad Mon Sep 17 00:00:00 2001 From: "Chinthakayala, Sheshashailavas (sc2914)" Date: Mon, 28 Aug 2017 05:25:46 -0900 Subject: [CCSDK-28] populated the seed code for dgbuilder updated the code to point to the new package name for sli Change-Id: I3b5a1d05dc5193664fd4a667afdcd0b2354010a4 Issue-ID:{CCSDK-28} Signed-off-by: Chinthakayala, Sheshashailavas (sc2914) Signed-off-by: Chinthakayala, Sheshashailavas (sc2914) --- dgbuilder/core_nodes/analysis/72-sentiment.html | 49 +++ dgbuilder/core_nodes/analysis/72-sentiment.js | 33 ++ dgbuilder/core_nodes/core/20-inject.html | 437 ++++++++++++++++++++ dgbuilder/core_nodes/core/20-inject.js | 97 +++++ dgbuilder/core_nodes/core/58-debug.html | 248 ++++++++++++ dgbuilder/core_nodes/core/58-debug.js | 114 ++++++ dgbuilder/core_nodes/core/75-exec.html | 68 ++++ dgbuilder/core_nodes/core/75-exec.js | 84 ++++ dgbuilder/core_nodes/core/80-function.html | 110 +++++ dgbuilder/core_nodes/core/80-function.js | 79 ++++ dgbuilder/core_nodes/core/80-template.html | 102 +++++ dgbuilder/core_nodes/core/80-template.js | 61 +++ dgbuilder/core_nodes/core/89-delay.html | 167 ++++++++ dgbuilder/core_nodes/core/89-delay.js | 171 ++++++++ dgbuilder/core_nodes/core/89-trigger.html | 130 ++++++ dgbuilder/core_nodes/core/89-trigger.js | 91 +++++ dgbuilder/core_nodes/core/90-comment.html | 86 ++++ dgbuilder/core_nodes/core/90-comment.js | 23 ++ dgbuilder/core_nodes/core/98-unknown.html | 49 +++ dgbuilder/core_nodes/core/98-unknown.js | 23 ++ dgbuilder/core_nodes/deprecated/61-imap.html | 56 +++ dgbuilder/core_nodes/deprecated/61-imap.js | 139 +++++++ dgbuilder/core_nodes/deprecated/73-parsexml.html | 53 +++ dgbuilder/core_nodes/deprecated/73-parsexml.js | 47 +++ dgbuilder/core_nodes/deprecated/74-js2xml.html | 51 +++ dgbuilder/core_nodes/deprecated/74-js2xml.js | 39 ++ dgbuilder/core_nodes/deprecated/90-httpget.html | 61 +++ dgbuilder/core_nodes/deprecated/90-httpget.js | 53 +++ dgbuilder/core_nodes/hardware/35-arduino.html | 171 ++++++++ dgbuilder/core_nodes/hardware/35-arduino.js | 160 ++++++++ dgbuilder/core_nodes/hardware/36-rpi-gpio.html | 182 +++++++++ dgbuilder/core_nodes/hardware/36-rpi-gpio.js | 185 +++++++++ dgbuilder/core_nodes/io/10-mqtt.html | 157 +++++++ dgbuilder/core_nodes/io/10-mqtt.js | 119 ++++++ dgbuilder/core_nodes/io/21-httpin.html | 254 ++++++++++++ dgbuilder/core_nodes/io/21-httpin.js | 241 +++++++++++ dgbuilder/core_nodes/io/22-websocket.html | 163 ++++++++ dgbuilder/core_nodes/io/22-websocket.js | 185 +++++++++ dgbuilder/core_nodes/io/23-watch.html | 57 +++ dgbuilder/core_nodes/io/23-watch.js | 51 +++ dgbuilder/core_nodes/io/25-serial.html | 265 ++++++++++++ dgbuilder/core_nodes/io/25-serial.js | 310 ++++++++++++++ dgbuilder/core_nodes/io/31-tcpin.html | 299 ++++++++++++++ dgbuilder/core_nodes/io/31-tcpin.js | 472 ++++++++++++++++++++++ dgbuilder/core_nodes/io/32-udp.html | 212 ++++++++++ dgbuilder/core_nodes/io/32-udp.js | 171 ++++++++ dgbuilder/core_nodes/io/lib/mqtt.js | 254 ++++++++++++ dgbuilder/core_nodes/io/lib/mqttConnectionPool.js | 128 ++++++ dgbuilder/core_nodes/logic/10-switch.html | 198 +++++++++ dgbuilder/core_nodes/logic/10-switch.js | 78 ++++ dgbuilder/core_nodes/logic/15-change.html | 139 +++++++ dgbuilder/core_nodes/logic/15-change.js | 74 ++++ dgbuilder/core_nodes/logic/16-range.html | 81 ++++ dgbuilder/core_nodes/logic/16-range.js | 48 +++ dgbuilder/core_nodes/parsers/70-CSV.html | 123 ++++++ dgbuilder/core_nodes/parsers/70-CSV.js | 157 +++++++ dgbuilder/core_nodes/parsers/70-HTML.html | 73 ++++ dgbuilder/core_nodes/parsers/70-HTML.js | 60 +++ dgbuilder/core_nodes/parsers/70-JSON.html | 47 +++ dgbuilder/core_nodes/parsers/70-JSON.js | 46 +++ dgbuilder/core_nodes/parsers/70-XML.html | 48 +++ dgbuilder/core_nodes/parsers/70-XML.js | 46 +++ dgbuilder/core_nodes/social/27-twitter.html | 223 ++++++++++ dgbuilder/core_nodes/social/27-twitter.js | 347 ++++++++++++++++ dgbuilder/core_nodes/social/32-feedparse.html | 57 +++ dgbuilder/core_nodes/social/32-feedparse.js | 71 ++++ dgbuilder/core_nodes/social/61-email.html | 189 +++++++++ dgbuilder/core_nodes/social/61-email.js | 246 +++++++++++ dgbuilder/core_nodes/social/91-irc.html | 206 ++++++++++ dgbuilder/core_nodes/social/91-irc.js | 237 +++++++++++ dgbuilder/core_nodes/storage/28-tail.html | 58 +++ dgbuilder/core_nodes/storage/28-tail.js | 69 ++++ dgbuilder/core_nodes/storage/50-file.html | 110 +++++ dgbuilder/core_nodes/storage/50-file.js | 93 +++++ dgbuilder/core_nodes/storage/65-redisout.html | 105 +++++ dgbuilder/core_nodes/storage/65-redisout.js | 107 +++++ dgbuilder/core_nodes/storage/66-mongodb.html | 231 +++++++++++ dgbuilder/core_nodes/storage/66-mongodb.js | 233 +++++++++++ 78 files changed, 10557 insertions(+) create mode 100644 dgbuilder/core_nodes/analysis/72-sentiment.html create mode 100644 dgbuilder/core_nodes/analysis/72-sentiment.js create mode 100644 dgbuilder/core_nodes/core/20-inject.html create mode 100644 dgbuilder/core_nodes/core/20-inject.js create mode 100644 dgbuilder/core_nodes/core/58-debug.html create mode 100644 dgbuilder/core_nodes/core/58-debug.js create mode 100644 dgbuilder/core_nodes/core/75-exec.html create mode 100644 dgbuilder/core_nodes/core/75-exec.js create mode 100644 dgbuilder/core_nodes/core/80-function.html create mode 100644 dgbuilder/core_nodes/core/80-function.js create mode 100644 dgbuilder/core_nodes/core/80-template.html create mode 100644 dgbuilder/core_nodes/core/80-template.js create mode 100644 dgbuilder/core_nodes/core/89-delay.html create mode 100644 dgbuilder/core_nodes/core/89-delay.js create mode 100644 dgbuilder/core_nodes/core/89-trigger.html create mode 100644 dgbuilder/core_nodes/core/89-trigger.js create mode 100644 dgbuilder/core_nodes/core/90-comment.html create mode 100644 dgbuilder/core_nodes/core/90-comment.js create mode 100644 dgbuilder/core_nodes/core/98-unknown.html create mode 100644 dgbuilder/core_nodes/core/98-unknown.js create mode 100644 dgbuilder/core_nodes/deprecated/61-imap.html create mode 100644 dgbuilder/core_nodes/deprecated/61-imap.js create mode 100644 dgbuilder/core_nodes/deprecated/73-parsexml.html create mode 100644 dgbuilder/core_nodes/deprecated/73-parsexml.js create mode 100644 dgbuilder/core_nodes/deprecated/74-js2xml.html create mode 100644 dgbuilder/core_nodes/deprecated/74-js2xml.js create mode 100644 dgbuilder/core_nodes/deprecated/90-httpget.html create mode 100644 dgbuilder/core_nodes/deprecated/90-httpget.js create mode 100644 dgbuilder/core_nodes/hardware/35-arduino.html create mode 100644 dgbuilder/core_nodes/hardware/35-arduino.js create mode 100644 dgbuilder/core_nodes/hardware/36-rpi-gpio.html create mode 100644 dgbuilder/core_nodes/hardware/36-rpi-gpio.js create mode 100644 dgbuilder/core_nodes/io/10-mqtt.html create mode 100644 dgbuilder/core_nodes/io/10-mqtt.js create mode 100644 dgbuilder/core_nodes/io/21-httpin.html create mode 100644 dgbuilder/core_nodes/io/21-httpin.js create mode 100644 dgbuilder/core_nodes/io/22-websocket.html create mode 100644 dgbuilder/core_nodes/io/22-websocket.js create mode 100644 dgbuilder/core_nodes/io/23-watch.html create mode 100644 dgbuilder/core_nodes/io/23-watch.js create mode 100644 dgbuilder/core_nodes/io/25-serial.html create mode 100644 dgbuilder/core_nodes/io/25-serial.js create mode 100644 dgbuilder/core_nodes/io/31-tcpin.html create mode 100644 dgbuilder/core_nodes/io/31-tcpin.js create mode 100644 dgbuilder/core_nodes/io/32-udp.html create mode 100644 dgbuilder/core_nodes/io/32-udp.js create mode 100644 dgbuilder/core_nodes/io/lib/mqtt.js create mode 100644 dgbuilder/core_nodes/io/lib/mqttConnectionPool.js create mode 100644 dgbuilder/core_nodes/logic/10-switch.html create mode 100644 dgbuilder/core_nodes/logic/10-switch.js create mode 100644 dgbuilder/core_nodes/logic/15-change.html create mode 100644 dgbuilder/core_nodes/logic/15-change.js create mode 100644 dgbuilder/core_nodes/logic/16-range.html create mode 100644 dgbuilder/core_nodes/logic/16-range.js create mode 100644 dgbuilder/core_nodes/parsers/70-CSV.html create mode 100644 dgbuilder/core_nodes/parsers/70-CSV.js create mode 100644 dgbuilder/core_nodes/parsers/70-HTML.html create mode 100644 dgbuilder/core_nodes/parsers/70-HTML.js create mode 100644 dgbuilder/core_nodes/parsers/70-JSON.html create mode 100644 dgbuilder/core_nodes/parsers/70-JSON.js create mode 100644 dgbuilder/core_nodes/parsers/70-XML.html create mode 100644 dgbuilder/core_nodes/parsers/70-XML.js create mode 100644 dgbuilder/core_nodes/social/27-twitter.html create mode 100644 dgbuilder/core_nodes/social/27-twitter.js create mode 100644 dgbuilder/core_nodes/social/32-feedparse.html create mode 100644 dgbuilder/core_nodes/social/32-feedparse.js create mode 100644 dgbuilder/core_nodes/social/61-email.html create mode 100644 dgbuilder/core_nodes/social/61-email.js create mode 100644 dgbuilder/core_nodes/social/91-irc.html create mode 100644 dgbuilder/core_nodes/social/91-irc.js create mode 100644 dgbuilder/core_nodes/storage/28-tail.html create mode 100644 dgbuilder/core_nodes/storage/28-tail.js create mode 100644 dgbuilder/core_nodes/storage/50-file.html create mode 100644 dgbuilder/core_nodes/storage/50-file.js create mode 100644 dgbuilder/core_nodes/storage/65-redisout.html create mode 100644 dgbuilder/core_nodes/storage/65-redisout.js create mode 100644 dgbuilder/core_nodes/storage/66-mongodb.html create mode 100644 dgbuilder/core_nodes/storage/66-mongodb.js (limited to 'dgbuilder/core_nodes') diff --git a/dgbuilder/core_nodes/analysis/72-sentiment.html b/dgbuilder/core_nodes/analysis/72-sentiment.html new file mode 100644 index 00000000..c33b873b --- /dev/null +++ b/dgbuilder/core_nodes/analysis/72-sentiment.html @@ -0,0 +1,49 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/analysis/72-sentiment.js b/dgbuilder/core_nodes/analysis/72-sentiment.js new file mode 100644 index 00000000..747e079c --- /dev/null +++ b/dgbuilder/core_nodes/analysis/72-sentiment.js @@ -0,0 +1,33 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var sentiment = require('sentiment'); + + function SentimentNode(n) { + RED.nodes.createNode(this,n); + var node = this; + + this.on("input", function(msg) { + sentiment(msg.payload, msg.overrides || null, function (err, result) { + msg.sentiment = result; + node.send(msg); + }); + }); + } + RED.nodes.registerType("sentiment",SentimentNode); +} diff --git a/dgbuilder/core_nodes/core/20-inject.html b/dgbuilder/core_nodes/core/20-inject.html new file mode 100644 index 00000000..38aa6efe --- /dev/null +++ b/dgbuilder/core_nodes/core/20-inject.html @@ -0,0 +1,437 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/core/20-inject.js b/dgbuilder/core_nodes/core/20-inject.js new file mode 100644 index 00000000..dff0fb65 --- /dev/null +++ b/dgbuilder/core_nodes/core/20-inject.js @@ -0,0 +1,97 @@ +/** + * Copyright 2013, 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + var cron = require("cron"); + + function InjectNode(n) { + RED.nodes.createNode(this,n); + this.topic = n.topic; + this.payload = n.payload; + this.payloadType = n.payloadType; + this.repeat = n.repeat; + this.crontab = n.crontab; + this.once = n.once; + var node = this; + this.interval_id = null; + this.cronjob = null; + + if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) { + this.repeat = this.repeat * 1000; + this.log("repeat = "+this.repeat); + this.interval_id = setInterval( function() { + node.emit("input",{}); + }, this.repeat ); + } else if (this.crontab) { + if (cron) { + this.log("crontab = "+this.crontab); + this.cronjob = new cron.CronJob(this.crontab, + function() { + node.emit("input",{}); + }, + null,true); + } else { + this.error("'cron' module not found"); + } + } + + if (this.once) { + setTimeout( function(){ node.emit("input",{}); }, 100); + } + + this.on("input",function(msg) { + var msg = {topic:this.topic}; + if ( (this.payloadType == null && this.payload == "") || this.payloadType == "date") { + msg.payload = Date.now(); + } else if (this.payloadType == null || this.payloadType == "string") { + msg.payload = this.payload; + } else { + msg.payload = ""; + } + this.send(msg); + msg = null; + }); + } + + RED.nodes.registerType("inject",InjectNode); + + InjectNode.prototype.close = function() { + if (this.interval_id != null) { + clearInterval(this.interval_id); + this.log("inject: repeat stopped"); + } else if (this.cronjob != null) { + this.cronjob.stop(); + this.log("inject: cronjob stopped"); + delete this.cronjob; + } + } + + RED.httpAdmin.post("/inject/:id", function(req,res) { + var node = RED.nodes.getNode(req.params.id); + if (node != null) { + try { + node.receive(); + res.send(200); + } catch(err) { + res.send(500); + node.error("Inject failed:"+err); + console.log(err.stack); + } + } else { + res.send(404); + } + }); +} diff --git a/dgbuilder/core_nodes/core/58-debug.html b/dgbuilder/core_nodes/core/58-debug.html new file mode 100644 index 00000000..04aa5078 --- /dev/null +++ b/dgbuilder/core_nodes/core/58-debug.html @@ -0,0 +1,248 @@ + + + + + + + + + diff --git a/dgbuilder/core_nodes/core/58-debug.js b/dgbuilder/core_nodes/core/58-debug.js new file mode 100644 index 00000000..7436bf2c --- /dev/null +++ b/dgbuilder/core_nodes/core/58-debug.js @@ -0,0 +1,114 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + var util = require("util"); + var events = require("events"); + var debuglength = RED.settings.debugMaxLength||1000; + var useColors = false; + // util.inspect.styles.boolean = "red"; + + function DebugNode(n) { + RED.nodes.createNode(this,n); + this.name = n.name; + this.complete = n.complete; + this.console = n.console; + this.active = (n.active == null)||n.active; + var node = this; + + this.on("input",function(msg) { + if (this.complete == "true") { // debug complete msg object + if (this.console == "true") { + node.log("\n"+util.inspect(msg, {colors:useColors, depth:10})); + } + if (this.active) { + sendDebug({id:this.id,name:this.name,topic:msg.topic,msg:msg,_path:msg._path}); + } + } else { // debug just the msg.payload + if (this.console == "true") { + if (typeof msg.payload === "string") { + node.log((msg.payload.indexOf("\n") != -1 ? "\n" : "") + msg.payload); + } + else if (typeof msg.payload === "object") { node.log("\n"+util.inspect(msg.payload, {colors:useColors, depth:10})); } + else { node.log(util.inspect(msg.payload, {colors:useColors})); } + } + if (this.active) { + sendDebug({id:this.id,name:this.name,topic:msg.topic,msg:msg.payload,_path:msg._path}); + } + } + }); + } + + RED.nodes.registerType("debug",DebugNode); + + function sendDebug(msg) { + if (msg.msg instanceof Error) { + msg.msg = msg.msg.toString(); + } else if (msg.msg instanceof Buffer) { + msg.msg = "(Buffer) "+msg.msg.toString('hex'); + } else if (typeof msg.msg === 'object') { + var seen = []; + var ty = "(Object) "; + if (util.isArray(msg.msg)) { ty = "(Array) "; } + msg.msg = ty + JSON.stringify(msg.msg, function(key, value) { + if (typeof value === 'object' && value !== null) { + if (seen.indexOf(value) !== -1) { return "[circular]"; } + seen.push(value); + } + return value; + }," "); + seen = null; + } else if (typeof msg.msg === "boolean") { + msg.msg = "(boolean) "+msg.msg.toString(); + } else if (msg.msg === 0) { + msg.msg = "0"; + } else if (msg.msg == null) { + msg.msg = "(undefined)"; + } + + if (msg.msg.length > debuglength) { + msg.msg = msg.msg.substr(0,debuglength) +" ...."; + } + + RED.comms.publish("debug",msg); + } + + DebugNode.logHandler = new events.EventEmitter(); + DebugNode.logHandler.on("log",function(msg) { + if (msg.level == "warn" || msg.level == "error") { + sendDebug(msg); + } + }); + RED.log.addHandler(DebugNode.logHandler); + + RED.httpAdmin.post("/debug/:id/:state", function(req,res) { + var node = RED.nodes.getNode(req.params.id); + var state = req.params.state; + if (node != null) { + if (state === "enable") { + node.active = true; + res.send(200); + } else if (state === "disable") { + node.active = false; + res.send(201); + } else { + res.send(404); + } + } else { + res.send(404); + } + }); +} diff --git a/dgbuilder/core_nodes/core/75-exec.html b/dgbuilder/core_nodes/core/75-exec.html new file mode 100644 index 00000000..567a34c1 --- /dev/null +++ b/dgbuilder/core_nodes/core/75-exec.html @@ -0,0 +1,68 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/core/75-exec.js b/dgbuilder/core_nodes/core/75-exec.js new file mode 100644 index 00000000..a07b1401 --- /dev/null +++ b/dgbuilder/core_nodes/core/75-exec.js @@ -0,0 +1,84 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var spawn = require('child_process').spawn; + var exec = require('child_process').exec; + + function ExecNode(n) { + RED.nodes.createNode(this,n); + this.cmd = n.command.trim(); + this.append = n.append.trim() || ""; + this.useSpawn = n.useSpawn; + + var node = this; + this.on("input", function(msg) { + node.status({fill:"blue",shape:"dot"}); + if (this.useSpawn === true) { + // make the extra args into an array + // then prepend with the msg.payload + if (typeof(msg.payload !== "string")) { msg.payload = msg.payload.toString(); } + var arg = []; + if (node.append.length > 0) { arg = node.append.split(","); } + if (msg.payload.trim() !== "") { arg.unshift(msg.payload); } + node.log(node.cmd+" ["+arg+"]"); + if (node.cmd.indexOf(" ") == -1) { + var ex = spawn(node.cmd,arg); + ex.stdout.on('data', function (data) { + //console.log('[exec] stdout: ' + data); + msg.payload = data.toString(); + node.send([msg,null,null]); + }); + ex.stderr.on('data', function (data) { + //console.log('[exec] stderr: ' + data); + msg.payload = data.toString(); + node.send([null,msg,null]); + }); + ex.on('close', function (code) { + //console.log('[exec] result: ' + code); + msg.payload = code; + node.status({}); + node.send([null,null,msg]); + }); + ex.on('error', function (code) { + node.warn(code); + }); + } + else { node.error("Spawn command must be just the command - no spaces or extra parameters"); } + } + else { + var cl = node.cmd+" "+msg.payload+" "+node.append; + node.log(cl); + var child = exec(cl, function (error, stdout, stderr) { + msg.payload = stdout; + var msg2 = {payload:stderr}; + var msg3 = null; + //console.log('[exec] stdout: ' + stdout); + //console.log('[exec] stderr: ' + stderr); + if (error !== null) { + msg3 = {payload:error}; + //console.log('[exec] error: ' + error); + } + node.status({}); + node.send([msg,msg2,msg3]); + }); + } + }); + } + + RED.nodes.registerType("exec",ExecNode); +} diff --git a/dgbuilder/core_nodes/core/80-function.html b/dgbuilder/core_nodes/core/80-function.html new file mode 100644 index 00000000..442c391d --- /dev/null +++ b/dgbuilder/core_nodes/core/80-function.html @@ -0,0 +1,110 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/core/80-function.js b/dgbuilder/core_nodes/core/80-function.js new file mode 100644 index 00000000..e1413a7a --- /dev/null +++ b/dgbuilder/core_nodes/core/80-function.js @@ -0,0 +1,79 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var util = require("util"); + var vm = require("vm"); + + function FunctionNode(n) { + RED.nodes.createNode(this,n); + this.name = n.name; + this.func = n.func; + var functionText = "var results = null; results = (function(msg){"+this.func+"\n})(msg);"; + this.topic = n.topic; + var sandbox = { + console:console, + util:util, + Buffer:Buffer, + context: { + global:RED.settings.functionGlobalContext || {} + } + }; + var context = vm.createContext(sandbox); + try { + this.script = vm.createScript(functionText); + this.on("input", function(msg) { + try { + var start = process.hrtime(); + context.msg = msg; + this.script.runInContext(context); + var results = context.results; + if (results == null) { + results = []; + } else if (results.length == null) { + results = [results]; + } + if (msg._topic) { + for (var m in results) { + if (results[m]) { + if (util.isArray(results[m])) { + for (var n=0; n < results[m].length; n++) { + results[m][n]._topic = msg._topic; + } + } else { + results[m]._topic = msg._topic; + } + } + } + } + this.send(results); + var duration = process.hrtime(start); + if (process.env.NODE_RED_FUNCTION_TIME) { + this.status({fill:"yellow",shape:"dot",text:""+Math.floor((duration[0]* 1e9 + duration[1])/10000)/100}); + } + } catch(err) { + this.error(err.toString()); + } + }); + } catch(err) { + this.error(err); + } + } + + RED.nodes.registerType("function",FunctionNode); + RED.library.register("functions"); +} diff --git a/dgbuilder/core_nodes/core/80-template.html b/dgbuilder/core_nodes/core/80-template.html new file mode 100644 index 00000000..dc014d37 --- /dev/null +++ b/dgbuilder/core_nodes/core/80-template.html @@ -0,0 +1,102 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/core/80-template.js b/dgbuilder/core_nodes/core/80-template.js new file mode 100644 index 00000000..7c84142d --- /dev/null +++ b/dgbuilder/core_nodes/core/80-template.js @@ -0,0 +1,61 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var mustache = require("mustache"); + + function TemplateNode(n) { + RED.nodes.createNode(this,n); + this.name = n.name; + this.field = n.field || "payload"; + this.template = n.template; + var node = this; + + var b = node.field.split("."); + var i = 0; + var m = null; + var rec = function(obj) { + i += 1; + if ((i < b.length) && (typeof obj[b[i-1]] === "object")) { + rec(obj[b[i-1]]); // not there yet - carry on digging + } + else { + if (i === b.length) { // we've finished so assign the value + obj[b[i-1]] = mustache.render(node.template,m); + node.send(m); + } + else { + obj[b[i-1]] = {}; // needs to be a new object so create it + rec(obj[b[i-1]]); // and carry on digging + } + } + } + + node.on("input", function(msg) { + try { + m = msg; + i = 0; + rec(msg); + } catch(err) { + node.error(err.message); + } + }); + } + + RED.nodes.registerType("template",TemplateNode); + RED.library.register("templates"); +} diff --git a/dgbuilder/core_nodes/core/89-delay.html b/dgbuilder/core_nodes/core/89-delay.html new file mode 100644 index 00000000..dcb0a5b9 --- /dev/null +++ b/dgbuilder/core_nodes/core/89-delay.html @@ -0,0 +1,167 @@ + + + + + + + + + + diff --git a/dgbuilder/core_nodes/core/89-delay.js b/dgbuilder/core_nodes/core/89-delay.js new file mode 100644 index 00000000..3c4e1c01 --- /dev/null +++ b/dgbuilder/core_nodes/core/89-delay.js @@ -0,0 +1,171 @@ +/** + * Copyright 2013, 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +//Simple node to introduce a pause into a flow +module.exports = function(RED) { + "use strict"; + + var MILLIS_TO_NANOS = 1000000; + var SECONDS_TO_NANOS = 1000000000; + + function random(n) { + var wait = n.randomFirst + (n.diff * Math.random()); + if (n.buffer.length > 0) { + n.send(n.buffer.pop()); + n.randomID = setTimeout(function() {random(n);},wait); + } else { + n.randomID = -1; + } + } + + function DelayNode(n) { + RED.nodes.createNode(this,n); + + this.pauseType = n.pauseType; + this.timeoutUnits = n.timeoutUnits; + this.randomUnits = n.randomUnits; + this.rateUnits = n.rateUnits; + + if (n.timeoutUnits === "milliseconds") { + this.timeout = n.timeout; + } else if (n.timeoutUnits === "seconds") { + this.timeout = n.timeout * 1000; + } else if (n.timeoutUnits === "minutes") { + this.timeout = n.timeout * (60 * 1000); + } else if (n.timeoutUnits === "hours") { + this.timeout = n.timeout * (60 * 60 * 1000); + } else if (n.timeoutUnits === "days") { + this.timeout = n.timeout * (24 * 60 * 60 * 1000); + } + + if (n.rateUnits === "second") { + this.rate = 1000/n.rate; + } else if (n.rateUnits === "minute") { + this.rate = (60 * 1000)/n.rate; + } else if (n.rateUnits === "hour") { + this.rate = (60 * 60 * 1000)/n.rate; + } else if (n.rateUnits === "day") { + this.rate = (24 * 60 * 60 * 1000)/n.rate; + } + + if (n.randomUnits === "milliseconds") { + this.randomFirst = n.randomFirst; + this.randomLast = n.randomLast; + } else if (n.randomUnits === "seconds") { + this.randomFirst = n.randomFirst * 1000; + this.randomLast = n.randomLast * 1000; + } else if (n.randomUnits === "minutes") { + this.randomFirst = n.randomFirst * (60 * 1000); + this.randomLast = n.randomLast * (60 * 1000); + } else if (n.randomUnits === "hours") { + this.randomFirst = n.randomFirst * (60 * 60 * 1000); + this.randomLast = n.randomLast * (60 * 60 * 1000); + } else if (n.randomUnits === "days") { + this.randomFirst = n.randomFirst * (24 * 60 * 60 * 1000); + this.randomLast = n.randomLast * (24 * 60 * 60 * 1000); + } + + this.diff = this.randomLast - this.randomFirst; + this.name = n.name; + this.idList = []; + this.buffer = []; + this.intervalID = -1; + this.randomID = -1; + this.lastSent; + this.drop = n.drop; + var node = this; + + if (this.pauseType === "delay") { + this.on("input", function(msg) { + var id; + id = setTimeout(function(){ + node.idList.splice(node.idList.indexOf(id),1); + node.send(msg); + }, node.timeout); + this.idList.push(id); + }); + + this.on("close", function() { + for (var i=0; i 0) { + node.status({text:node.buffer.length}); + } + if (node.buffer.length > 1000) { + node.warn(this.name + " buffer exceeded 1000 messages"); + } + } else { + node.send(msg); + node.intervalID = setInterval(function() { + if (node.buffer.length === 0) { + clearInterval(node.intervalID); + node.intervalID = -1; + node.status({text:""}); + } + + if (node.buffer.length > 0) { + node.send(node.buffer.shift()); + node.status({text:node.buffer.length}); + } + },node.rate); + } + } else { + var timeSinceLast; + if (node.lastSent) { + timeSinceLast = process.hrtime(node.lastSent); + } + if (!node.lastSent) { // ensuring that we always send the first message + node.lastSent = process.hrtime(); + node.send(msg); + } else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) { + node.lastSent = process.hrtime(); + node.send(msg); + } + } + }); + + this.on("close", function() { + clearInterval(this.intervalID); + this.buffer = []; + }); + + } else if (this.pauseType === "random") { + this.on("input",function(msg){ + node.buffer.push(msg); + if (node.randomID === -1) { + var wait = node.randomFirst + (node.diff * Math.random()); + node.randomID = setTimeout(function() {random(node);},wait); + } + }); + + this.on("close", function (){ + if (this.randomID !== -1) { + clearTimeout(this.randomID); + } + }); + } + } + RED.nodes.registerType("delay",DelayNode); +} diff --git a/dgbuilder/core_nodes/core/89-trigger.html b/dgbuilder/core_nodes/core/89-trigger.html new file mode 100644 index 00000000..f3ec530d --- /dev/null +++ b/dgbuilder/core_nodes/core/89-trigger.html @@ -0,0 +1,130 @@ + + + + + + + + diff --git a/dgbuilder/core_nodes/core/89-trigger.js b/dgbuilder/core_nodes/core/89-trigger.js new file mode 100644 index 00000000..d86a2130 --- /dev/null +++ b/dgbuilder/core_nodes/core/89-trigger.js @@ -0,0 +1,91 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var mustache = require("mustache"); + function TriggerNode(n) { + RED.nodes.createNode(this,n); + this.op1 = n.op1 || "1"; + this.op2 = n.op2 || "0"; + this.op1type = n.op1type || "val"; + this.op2type = n.op2type || "val"; + this.extend = n.extend || false; + this.units = n.units || "ms"; + this.duration = n.duration || 250; + if (this.duration <= 0) { this.duration = 0; } + else { + if (this.units == "s") { this.duration = this.duration * 1000; } + if (this.units == "min") { this.duration = this.duration * 1000 * 60; } + if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; } + } + this.op1Templated = this.op1.indexOf("{{") != -1; + this.op2Templated = this.op2.indexOf("{{") != -1; + if (!isNaN(this.op1)) { this.op1 = Number(this.op1); } + if (!isNaN(this.op2)) { this.op2 = Number(this.op2); } + if (this.op1 == "true") { this.op1 = true; } + if (this.op2 == "true") { this.op1 = true; } + if (this.op1 == "false") { this.op2 = false; } + if (this.op2 == "false") { this.op2 = false; } + if (this.op1 == "null") { this.op1 = null; } + if (this.op2 == "null") { this.op1 = null; } + try { this.op1 = JSON.parse(this.op1); } + catch(e) { this.op1 = this.op1; } + try { this.op2 = JSON.parse(this.op2); } + catch(e) { this.op2 = this.op2; } + + var node = this; + var tout = null; + var m2; + this.on("input", function(msg) { + if (msg.hasOwnProperty("reset")) { + clearTimeout(tout); + tout = null; + } + else { + if (!tout) { + if (node.op2type === "pay") { m2 = msg.payload; } + else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); } + else { m2 = node.op2; } + if (node.op1type === "pay") { } + else if (node.op1Templated) { msg.payload = mustache.render(node.op1,msg); } + else { msg.payload = node.op1; } + if (node.op1type !== "nul") { node.send(msg); } + if (node.duration === 0) { tout = "infinite"; } + else { + tout = setTimeout(function() { + msg.payload = m2; + if (node.op2type !== "nul") { node.send(msg); } + tout = null; + },node.duration); + } + } + else if ((node.extend == "true") && (node.duration > 0)) { + clearTimeout(tout); + tout = setTimeout(function() { + msg.payload = m2; + if (node.op2type !== "nul") { node.send(msg); } + tout = null; + },node.duration); + } + } + }); + this.on("close", function() { + if (tout) { clearTimeout(tout); } + }); + } + RED.nodes.registerType("trigger",TriggerNode); +} diff --git a/dgbuilder/core_nodes/core/90-comment.html b/dgbuilder/core_nodes/core/90-comment.html new file mode 100644 index 00000000..3638fdaa --- /dev/null +++ b/dgbuilder/core_nodes/core/90-comment.html @@ -0,0 +1,86 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/core/90-comment.js b/dgbuilder/core_nodes/core/90-comment.js new file mode 100644 index 00000000..ef5f0800 --- /dev/null +++ b/dgbuilder/core_nodes/core/90-comment.js @@ -0,0 +1,23 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + function CommentNode(n) { + RED.nodes.createNode(this,n); + } + RED.nodes.registerType("comment",CommentNode); +} diff --git a/dgbuilder/core_nodes/core/98-unknown.html b/dgbuilder/core_nodes/core/98-unknown.html new file mode 100644 index 00000000..19a4ad59 --- /dev/null +++ b/dgbuilder/core_nodes/core/98-unknown.html @@ -0,0 +1,49 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/core/98-unknown.js b/dgbuilder/core_nodes/core/98-unknown.js new file mode 100644 index 00000000..ed4716b8 --- /dev/null +++ b/dgbuilder/core_nodes/core/98-unknown.js @@ -0,0 +1,23 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + function UnknownNode(n) { + RED.nodes.createNode(this,n); + } + RED.nodes.registerType("unknown",UnknownNode); +} diff --git a/dgbuilder/core_nodes/deprecated/61-imap.html b/dgbuilder/core_nodes/deprecated/61-imap.html new file mode 100644 index 00000000..9702cd64 --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/61-imap.html @@ -0,0 +1,56 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/deprecated/61-imap.js b/dgbuilder/core_nodes/deprecated/61-imap.js new file mode 100644 index 00000000..aa2b4beb --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/61-imap.js @@ -0,0 +1,139 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var RED = require(process.env.NODE_RED_HOME+"/red/red"); +var Imap = require('imap'); +var util = require('util'); + +try { + var emailkey = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js"); +} catch (err) { + //util.log("[61-imap.js] Info : No Email credentials found."); +} + +if (emailkey) { + var imap = new Imap({ + user: emailkey.user, + password: emailkey.pass, + host: emailkey.server||"imap.gmail.com", + port: emailkey.port||"993", + tls: true, + tlsOptions: { rejectUnauthorized: false } + }); + + function openInbox(cb) { + imap.openBox('INBOX', true, cb); + } +} + +function ImapNode(n) { + RED.nodes.createNode(this,n); + this.warn("This node has been deprecated and will be deleted in a future release. Please update your flow to use the 'e-mail in' node."); + this.name = n.name; + this.repeat = n.repeat * 1000 || 300000; + var node = this; + this.interval_id = null; + var oldmail = {}; + + if (!isNaN(this.repeat) && this.repeat > 0) { + node.log("repeat = "+this.repeat); + this.interval_id = setInterval( function() { + node.emit("input",{}); + }, this.repeat ); + } + + this.on("input", function(msg) { + if (imap) { + imap.once('ready', function() { + var pay = {}; + openInbox(function(err, box) { + if (box.messages.total > 0) { + var f = imap.seq.fetch(box.messages.total + ':*', { bodies: ['HEADER.FIELDS (FROM SUBJECT DATE)','TEXT'] }); + f.on('message', function(msg, seqno) { + node.log('message: #'+ seqno); + var prefix = '(#' + seqno + ') '; + msg.on('body', function(stream, info) { + var buffer = ''; + stream.on('data', function(chunk) { + buffer += chunk.toString('utf8'); + }); + stream.on('end', function() { + if (info.which !== 'TEXT') { + pay.from = Imap.parseHeader(buffer).from[0]; + pay.topic = Imap.parseHeader(buffer).subject[0]; + pay.date = Imap.parseHeader(buffer).date[0]; + } else { + var parts = buffer.split("Content-Type"); + for (var p in parts) { + if (parts[p].indexOf("text/plain") >= 0) { + pay.payload = parts[p].split("\n").slice(1,-2).join("\n").trim(); + } + if (parts[p].indexOf("text/html") >= 0) { + pay.html = parts[p].split("\n").slice(1,-2).join("\n").trim(); + } + } + //pay.body = buffer; + } + }); + }); + msg.on('end', function() { + //node.log('Finished: '+prefix); + }); + }); + f.on('error', function(err) { + node.warn('fetch error: ' + err); + }); + f.on('end', function() { + if (JSON.stringify(pay) !== oldmail) { + node.send(pay); + oldmail = JSON.stringify(pay); + node.log('sent new message: '+pay.topic); + } + else { node.log('duplicate not sent: '+pay.topic); } + imap.end(); + }); + } + else { + // node.log("you have achieved inbox zero"); + imap.end(); + } + }); + }); + imap.connect(); + } + else { node.warn("No Email credentials found. See info panel."); } + }); + + if (imap) { + imap.on('error', function(err) { + util.log(err); + }); + } + + this.on("error", function(err) { + node.log("error: ",err); + }); + + this.on("close", function() { + if (this.interval_id != null) { + clearInterval(this.interval_id); + } + if (imap) { imap.destroy(); } + }); + + node.emit("input",{}); +} +RED.nodes.registerType("imap",ImapNode); diff --git a/dgbuilder/core_nodes/deprecated/73-parsexml.html b/dgbuilder/core_nodes/deprecated/73-parsexml.html new file mode 100644 index 00000000..b6fc16fe --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/73-parsexml.html @@ -0,0 +1,53 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/deprecated/73-parsexml.js b/dgbuilder/core_nodes/deprecated/73-parsexml.js new file mode 100644 index 00000000..92850cb6 --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/73-parsexml.js @@ -0,0 +1,47 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var util = require("util"); + var parseString = require('xml2js').parseString; + var useColors = true; + //util.inspect.styles.boolean = "red"; + + function Xml2jsNode(n) { + RED.nodes.createNode(this,n); + this.warn("This node has been deprecated and will be deleted in a future release. Please update your flow to use the 'xml' node."); + this.useEyes = n.useEyes||false; + var node = this; + this.on("input", function(msg) { + try { + parseString(msg.payload, {strict:true,async:true}, function (err, result) { + //parseString(msg.payload, {strict:false,async:true}, function (err, result) { + if (err) { node.error(err); } + else { + msg.payload = result; + node.send(msg); + if (node.useEyes == true) { + node.log("\n"+util.inspect(msg, {colors:useColors, depth:10})); + } + } + }); + } + catch(e) { util.log("[73-parsexml.js] "+e); } + }); + } + RED.nodes.registerType("xml2js",Xml2jsNode); +} diff --git a/dgbuilder/core_nodes/deprecated/74-js2xml.html b/dgbuilder/core_nodes/deprecated/74-js2xml.html new file mode 100644 index 00000000..f614579b --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/74-js2xml.html @@ -0,0 +1,51 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/deprecated/74-js2xml.js b/dgbuilder/core_nodes/deprecated/74-js2xml.js new file mode 100644 index 00000000..164bafad --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/74-js2xml.js @@ -0,0 +1,39 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var js2xmlparser = require("js2xmlparser"); + + function Js2XmlNode(n) { + RED.nodes.createNode(this,n); + this.warn("This node has been deprecated and will be deleted in a future release. Please update your flow to use the 'xml' node."); + this.root = n.root; + var node = this; + + this.on("input", function(msg) { + try { + var root = node.root || typeof msg.payload; + if (typeof msg.payload !== "object") { msg.payload = '"'+msg.payload+'"'; } + console.log(root, typeof msg.payload,msg.payload); + msg.payload = js2xmlparser(root, msg.payload); + node.send(msg); + } + catch(e) { console.log(e); } + }); + } + RED.nodes.registerType("json2xml",Js2XmlNode); +} diff --git a/dgbuilder/core_nodes/deprecated/90-httpget.html b/dgbuilder/core_nodes/deprecated/90-httpget.html new file mode 100644 index 00000000..b1f2e080 --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/90-httpget.html @@ -0,0 +1,61 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/deprecated/90-httpget.js b/dgbuilder/core_nodes/deprecated/90-httpget.js new file mode 100644 index 00000000..63e16b93 --- /dev/null +++ b/dgbuilder/core_nodes/deprecated/90-httpget.js @@ -0,0 +1,53 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var RED = require(process.env.NODE_RED_HOME+"/red/red"); + +function HttpGet(n) { + RED.nodes.createNode(this,n); + this.warn("This node has been deprecated and will be deleted in a future release. Please update your flow to use the 'http request' node."); + this.baseurl = n.baseurl || ""; + this.append = n.append || ""; + var node = this; + if (this.baseurl.substring(0,5) === "https") { var http = require("https"); } + else { var http = require("http"); } + this.on("input", function(msg) { + msg._payload = msg.payload; + //util.log("[httpget] "+this.baseurl+msg.payload+this.append); + http.get(this.baseurl+msg.payload+this.append, function(res) { + node.log("Http response: " + res.statusCode); + msg.rc = res.statusCode; + msg.payload = ""; + if ((msg.rc != 200) && (msg.rc != 404)) { + node.send(msg); + } + res.setEncoding('utf8'); + res.on('data', function(chunk) { + msg.payload += chunk; + }); + res.on('end', function() { + node.send(msg); + }); + }).on('error', function(e) { + //node.error(e); + msg.rc = 503; + msg.payload = e; + node.send(msg); + }); + }); +} + +RED.nodes.registerType("httpget",HttpGet); diff --git a/dgbuilder/core_nodes/hardware/35-arduino.html b/dgbuilder/core_nodes/hardware/35-arduino.html new file mode 100644 index 00000000..17f02892 --- /dev/null +++ b/dgbuilder/core_nodes/hardware/35-arduino.html @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/hardware/35-arduino.js b/dgbuilder/core_nodes/hardware/35-arduino.js new file mode 100644 index 00000000..795e9907 --- /dev/null +++ b/dgbuilder/core_nodes/hardware/35-arduino.js @@ -0,0 +1,160 @@ +/** + * Copyright 2013,2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var util = require("util"); + var ArduinoFirmata = require('arduino-firmata'); + var fs = require('fs'); + var plat = require('os').platform(); + var portlist = ArduinoFirmata.list(function (err, ports) { + portlist = ports; + }); + + // The Board Definition - this opens (and closes) the connection + function ArduinoNode(n) { + RED.nodes.createNode(this,n); + this.device = n.device || null; + this.repeat = n.repeat||25; + //node.log("opening connection "+this.device); + var node = this; + node.board = new ArduinoFirmata(); + if (portlist.indexOf(node.device) === -1) { + node.warn("Device "+node.device+" not found"); + } + else { + node.board.connect(node.device); + } + + node.board.on('boardReady', function(){ + node.log("version "+node.board.boardVersion); + }); + + node.on('close', function() { + if (node.board) { + try { + node.board.close(function() { + node.log("port closed"); + }); + } catch(e) { } + } + }); + } + RED.nodes.registerType("arduino-board",ArduinoNode); + + + // The Input Node + function DuinoNodeIn(n) { + RED.nodes.createNode(this,n); + this.buttonState = -1; + this.pin = n.pin; + this.state = n.state; + this.arduino = n.arduino; + this.serverConfig = RED.nodes.getNode(this.arduino); + if (typeof this.serverConfig === "object") { + this.board = this.serverConfig.board; + //this.repeat = this.serverConfig.repeat; + var node = this; + node.status({fill:"red",shape:"ring",text:"connecting"}); + + node.board.on('connect', function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + //console.log("i",node.state,node.pin); + if (node.state == "ANALOG") { + node.board.on('analogChange', function(e) { + if (e.pin == node.pin) { + var msg = {payload:e.value, topic:"A"+e.pin}; + node.send(msg); + } + }); + + } + else { + node.board.pinMode(node.pin, ArduinoFirmata.INPUT); + node.board.on('digitalChange', function(e) { + if (e.pin == node.pin) { + var msg = {payload:e.value, topic:e.pin}; + node.send(msg); + } + }); + } + }); + } + else { + util.log("[Firmata-arduino] port not configured"); + } + } + RED.nodes.registerType("arduino in",DuinoNodeIn); + + + // The Output Node + function DuinoNodeOut(n) { + RED.nodes.createNode(this,n); + this.buttonState = -1; + this.pin = n.pin; + this.state = n.state; + this.arduino = n.arduino; + this.serverConfig = RED.nodes.getNode(this.arduino); + if (typeof this.serverConfig === "object") { + this.board = this.serverConfig.board; + var node = this; + node.status({fill:"red",shape:"ring",text:"connecting"}); + + node.board.on('connect', function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + //console.log("o",node.state,node.pin); + node.board.pinMode(node.pin, node.state); + node.on("input", function(msg) { + if (node.state == "OUTPUT") { + if ((msg.payload == true)||(msg.payload == 1)||(msg.payload.toString().toLowerCase() == "on")) { + node.board.digitalWrite(node.pin, true); + } + if ((msg.payload == false)||(msg.payload == 0)||(msg.payload.toString().toLowerCase() == "off")) { + node.board.digitalWrite(node.pin, false); + } + } + if (node.state == "PWM") { + msg.payload = msg.payload * 1; + if ((msg.payload >= 0) && (msg.payload <= 255)) { + //console.log(msg.payload, node.pin); + node.board.servoWrite(node.pin, msg.payload); + } + } + if (node.state == "SERVO") { + msg.payload = msg.payload * 1; + if ((msg.payload >= 0) && (msg.payload <= 180)) { + //console.log(msg.payload, node.pin); + node.board.servoWrite(node.pin, msg.payload); + } + } + }); + }); + } + else { + util.log("[Firmata-arduino] port not configured"); + } + } + RED.nodes.registerType("arduino out",DuinoNodeOut); + + RED.httpAdmin.get("/arduinoports",function(req,res) { + ArduinoFirmata.list(function (err, ports) { + //console.log(JSON.stringify(ports)); + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write(JSON.stringify(ports)); + res.end(); + }); + }); +} diff --git a/dgbuilder/core_nodes/hardware/36-rpi-gpio.html b/dgbuilder/core_nodes/hardware/36-rpi-gpio.html new file mode 100644 index 00000000..9e705c2c --- /dev/null +++ b/dgbuilder/core_nodes/hardware/36-rpi-gpio.html @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/hardware/36-rpi-gpio.js b/dgbuilder/core_nodes/hardware/36-rpi-gpio.js new file mode 100644 index 00000000..93cbc4e0 --- /dev/null +++ b/dgbuilder/core_nodes/hardware/36-rpi-gpio.js @@ -0,0 +1,185 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var util = require("util"); + var exec = require('child_process').exec; + var fs = require('fs'); + + var gpioCommand = '/usr/local/bin/gpio'; + + if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi + throw "Info : Ignoring Raspberry Pi specific node."; + } + + if (!fs.existsSync(gpioCommand)) { // gpio command not installed + throw "Info : Can't find Raspberry Pi wiringPi gpio command."; + } + + // Map physical P1 pins to Gordon's Wiring-Pi Pins (as they should be V1/V2 tolerant) + var pintable = { + // Physical : WiringPi + "11":"0", + "12":"1", + "13":"2", + "15":"3", + "16":"4", + "18":"5", + "22":"6", + "7":"7", + "3":"8", + "5":"9", + "24":"10", + "26":"11", + "19":"12", + "21":"13", + "23":"14", + "8":"15", + "10":"16", + "27":"30", + "28":"31", + "29":"21", + "31":"22", + "32":"26", + "33":"23", + "35":"24", + "36":"27", + "37":"25", + "38":"28", + "40":"29" + } + var tablepin = { + // WiringPi : Physical + "0":"11", + "1":"12", + "2":"13", + "3":"15", + "4":"16", + "5":"18", + "6":"22", + "7":"7", + "8":"3", + "9":"5", + "10":"24", + "11":"26", + "12":"19", + "13":"21", + "14":"23", + "15":"8", + "16":"10", + "30":"27", + "31":"28", + "21":"29", + "22":"31", + "26":"32", + "23":"33", + "24":"35", + "27":"36", + "25":"37", + "28":"38", + "29":"40" + } + + function GPIOInNode(n) { + RED.nodes.createNode(this,n); + this.buttonState = -1; + this.pin = pintable[n.pin]; + this.intype = n.intype; + var node = this; + + if (node.pin !== undefined) { + exec(gpioCommand+" mode "+node.pin+" "+node.intype, function(err,stdout,stderr) { + if (err) { node.error(err); } + else { + node._interval = setInterval( function() { + exec(gpioCommand+" read "+node.pin, function(err,stdout,stderr) { + if (err) { node.error(err); } + else { + if (node.buttonState !== Number(stdout)) { + var previousState = node.buttonState; + node.buttonState = Number(stdout); + if (previousState !== -1) { + var msg = {topic:"pi/"+tablepin[node.pin], payload:node.buttonState}; + node.send(msg); + } + } + } + }); + }, 250); + } + }); + } + else { + node.error("Invalid GPIO pin: "+node.pin); + } + + node.on("close", function() { + clearInterval(node._interval); + }); + } + + function GPIOOutNode(n) { + RED.nodes.createNode(this,n); + this.pin = pintable[n.pin]; + var node = this; + + if (node.pin !== undefined) { + process.nextTick(function() { + exec(gpioCommand+" mode "+node.pin+" out", function(err,stdout,stderr) { + if (err) { node.error(err); } + else { + node.on("input", function(msg) { + if (msg.payload === "true") { msg.payload = true; } + if (msg.payload === "false") { msg.payload = false; } + var out = Number(msg.payload); + if ((out === 0)|(out === 1)) { + exec(gpioCommand+" write "+node.pin+" "+out, function(err,stdout,stderr) { + if (err) { node.error(err); } + }); + } + else { node.warn("Invalid input - not 0 or 1"); } + }); + } + }); + }); + } + else { + node.error("Invalid GPIO pin: "+node.pin); + } + + node.on("close", function() { + exec(gpioCommand+" mode "+node.pin+" in"); + }); + } + + var pitype = { type:"" }; + exec(gpioCommand+" -v | grep Type", function(err,stdout,stderr) { + if (err) { + util.log('[36-rpi-gpio.js] Error: "'+gpioCommand+' -v" command failed for some reason.'); + } + else { + pitype = { type:(stdout.split(","))[0].split(": ")[1], rev:(stdout.split(","))[1].split(": ")[1] }; + } + }); + + RED.nodes.registerType("rpi-gpio in",GPIOInNode); + RED.nodes.registerType("rpi-gpio out",GPIOOutNode); + + RED.httpAdmin.get('/rpi-gpio/:id',function(req,res) { + res.send( JSON.stringify(pitype) ); + }); +} diff --git a/dgbuilder/core_nodes/io/10-mqtt.html b/dgbuilder/core_nodes/io/10-mqtt.html new file mode 100644 index 00000000..2ff5eb29 --- /dev/null +++ b/dgbuilder/core_nodes/io/10-mqtt.html @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/io/10-mqtt.js b/dgbuilder/core_nodes/io/10-mqtt.js new file mode 100644 index 00000000..c8bc4901 --- /dev/null +++ b/dgbuilder/core_nodes/io/10-mqtt.js @@ -0,0 +1,119 @@ +/** + * Copyright 2013,2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var connectionPool = require("./lib/mqttConnectionPool"); + + function MQTTBrokerNode(n) { + RED.nodes.createNode(this,n); + this.broker = n.broker; + this.port = n.port; + this.clientid = n.clientid; + if (this.credentials) { + this.username = this.credentials.user; + this.password = this.credentials.password; + } + } + RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{ + credentials: { + user: {type:"text"}, + password: {type: "password"} + } + }); + + function MQTTInNode(n) { + RED.nodes.createNode(this,n); + this.topic = n.topic; + this.broker = n.broker; + this.brokerConfig = RED.nodes.getNode(this.broker); + if (this.brokerConfig) { + this.status({fill:"red",shape:"ring",text:"disconnected"}); + this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password); + var node = this; + this.client.subscribe(this.topic,2,function(topic,payload,qos,retain) { + var msg = {topic:topic,payload:payload,qos:qos,retain:retain}; + if ((node.brokerConfig.broker == "localhost")||(node.brokerConfig.broker == "127.0.0.1")) { + msg._topic = topic; + } + node.send(msg); + }); + this.client.on("connectionlost",function() { + node.status({fill:"red",shape:"ring",text:"disconnected"}); + }); + this.client.on("connect",function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + this.client.connect(); + } else { + this.error("missing broker configuration"); + } + this.on('close', function() { + if (this.client) { + this.client.disconnect(); + } + }); + } + RED.nodes.registerType("mqtt in",MQTTInNode); + + function MQTTOutNode(n) { + RED.nodes.createNode(this,n); + this.topic = n.topic; + this.qos = n.qos || null; + this.retain = n.retain; + this.broker = n.broker; + this.brokerConfig = RED.nodes.getNode(this.broker); + + if (this.brokerConfig) { + this.status({fill:"red",shape:"ring",text:"disconnected"},true); + this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password); + var node = this; + this.on("input",function(msg) { + if (msg.qos) { + msg.qos = parseInt(msg.qos); + if ((msg.qos !== 0) && (msg.qos !== 1) && (msg.qos !== 2)) { + msg.qos = null; + } + } + msg.qos = Number(node.qos || msg.qos || 0); + msg.retain = node.retain || msg.retain || false; + msg.retain = ((msg.retain === true) || (msg.retain === "true")) || false; + if (node.topic) { + msg.topic = node.topic; + } + if ((msg.hasOwnProperty("topic")) && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist + this.client.publish(msg); // send the message + } + else { node.warn("Invalid topic specified"); } + }); + this.client.on("connectionlost",function() { + node.status({fill:"red",shape:"ring",text:"disconnected"}); + }); + this.client.on("connect",function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + this.client.connect(); + } else { + this.error("missing broker configuration"); + } + this.on('close', function() { + if (this.client) { + this.client.disconnect(); + } + }); + } + RED.nodes.registerType("mqtt out",MQTTOutNode); +} diff --git a/dgbuilder/core_nodes/io/21-httpin.html b/dgbuilder/core_nodes/io/21-httpin.html new file mode 100644 index 00000000..059b8596 --- /dev/null +++ b/dgbuilder/core_nodes/io/21-httpin.html @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/io/21-httpin.js b/dgbuilder/core_nodes/io/21-httpin.js new file mode 100644 index 00000000..877ccc09 --- /dev/null +++ b/dgbuilder/core_nodes/io/21-httpin.js @@ -0,0 +1,241 @@ +/** + * Copyright 2013,2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var http = require("follow-redirects").http; + var https = require("follow-redirects").https; + var urllib = require("url"); + var express = require("express"); + var getBody = require('raw-body'); + var mustache = require("mustache"); + var querystring = require("querystring"); + + var cors = require('cors'); + var jsonParser = express.json(); + var urlencParser = express.urlencoded(); + + function rawBodyParser(req, res, next) { + if (req._body) { return next(); } + req.body = ""; + req._body = true; + getBody(req, { + limit: '1mb', + length: req.headers['content-length'], + encoding: 'utf8' + }, function (err, buf) { + if (err) { return next(err); } + req.body = buf; + next(); + }); + } + + + function HTTPIn(n) { + RED.nodes.createNode(this,n); + if (RED.settings.httpNodeRoot !== false) { + + this.url = n.url; + this.method = n.method; + + var node = this; + + this.errorHandler = function(err,req,res,next) { + node.warn(err); + res.send(500); + }; + + this.callback = function(req,res) { + if (node.method == "post") { + node.send({req:req,res:res,payload:req.body}); + } else if (node.method == "get") { + node.send({req:req,res:res,payload:req.query}); + } else { + node.send({req:req,res:res}); + } + } + + var corsHandler = function(req,res,next) { next(); } + + if (RED.settings.httpNodeCors) { + corsHandler = cors(RED.settings.httpNodeCors); + RED.httpNode.options(this.url,corsHandler); + } + + if (this.method == "get") { + RED.httpNode.get(this.url,corsHandler,this.callback,this.errorHandler); + } else if (this.method == "post") { + RED.httpNode.post(this.url,corsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); + } else if (this.method == "put") { + RED.httpNode.put(this.url,corsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); + } else if (this.method == "delete") { + RED.httpNode.delete(this.url,corsHandler,this.callback,this.errorHandler); + } + + this.on("close",function() { + var routes = RED.httpNode.routes[this.method]; + for (var i = 0; i + + + + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/io/22-websocket.js b/dgbuilder/core_nodes/io/22-websocket.js new file mode 100644 index 00000000..72eda502 --- /dev/null +++ b/dgbuilder/core_nodes/io/22-websocket.js @@ -0,0 +1,185 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var ws = require("ws"), + inspect = require("sys").inspect; + + // A node red node that sets up a local websocket server + function WebSocketListenerNode(n) { + // Create a RED node + RED.nodes.createNode(this,n); + + var node = this; + + // Store local copies of the node configuration (as defined in the .html) + node.path = n.path; + node.wholemsg = (n.wholemsg === "true"); + + node._inputNodes = []; // collection of nodes that want to receive events + + var path = RED.settings.httpNodeRoot || "/"; + path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path); + + // Workaround https://github.com/einaros/ws/pull/253 + // Listen for 'newListener' events from RED.server + node._serverListeners = {}; + + var storeListener = function(/*String*/event,/*function*/listener){ + if(event == "error" || event == "upgrade" || event == "listening"){ + node._serverListeners[event] = listener; + } + } + + node._clients = {}; + + RED.server.addListener('newListener',storeListener); + + // Create a WebSocket Server + node.server = new ws.Server({server:RED.server,path:path}); + + // Workaround https://github.com/einaros/ws/pull/253 + // Stop listening for new listener events + RED.server.removeListener('newListener',storeListener); + + node.server.on('connection', function(socket){ + var id = (1+Math.random()*4294967295).toString(16); + node._clients[id] = socket; + socket.on('close',function() { + delete node._clients[id]; + }); + socket.on('message',function(data,flags){ + node.handleEvent(id,socket,'message',data,flags); + }); + socket.on('error', function(err) { + node.warn("An error occured on the ws connection: "+inspect(err)); + }); + }); + + node.on("close", function() { + // Workaround https://github.com/einaros/ws/pull/253 + // Remove listeners from RED.server + var listener = null; + for(var event in node._serverListeners) { + if (node._serverListeners.hasOwnProperty(event)) { + listener = node._serverListeners[event]; + if(typeof listener === "function"){ + RED.server.removeListener(event,listener); + } + } + } + node._serverListeners = {}; + node.server.close(); + node._inputNodes = []; + }); + } + RED.nodes.registerType("websocket-listener",WebSocketListenerNode); + + WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler){ + this._inputNodes.push(handler); + } + + WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){ + var msg; + if (this.wholemsg) { + try { + msg = JSON.parse(data); + } + catch(err) { + msg = { payload:data }; + } + } else { + msg = { + payload:data + }; + } + msg._session = {type:"websocket",id:id}; + + for (var i = 0; i < this._inputNodes.length; i++) { + this._inputNodes[i].send(msg); + } + } + + WebSocketListenerNode.prototype.broadcast = function(data){ + try { + for (var i = 0; i < this.server.clients.length; i++) { + this.server.clients[i].send(data); + } + } + catch(e) { // swallow any errors + this.warn("ws:"+i+" : "+e); + } + } + + WebSocketListenerNode.prototype.send = function(id,data) { + var session = this._clients[id]; + if (session) { + try { + session.send(data); + } + catch(e) { // swallow any errors + } + } + } + + function WebSocketInNode(n) { + RED.nodes.createNode(this,n); + this.server = n.server; + var node = this; + this.serverConfig = RED.nodes.getNode(this.server); + if (this.serverConfig) { + this.serverConfig.registerInputNode(this); + } else { + this.error("Missing server configuration"); + } + } + RED.nodes.registerType("websocket in",WebSocketInNode); + + function WebSocketOutNode(n) { + RED.nodes.createNode(this,n); + var node = this; + this.server = n.server; + this.serverConfig = RED.nodes.getNode(this.server); + if (!this.serverConfig) { + this.error("Missing server configuration"); + } + this.on("input", function(msg) { + var payload; + if (this.serverConfig.wholemsg) { + delete msg._session; + payload = JSON.stringify(msg); + } else { + if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string. + payload = RED.util.ensureString(msg.payload); + } + else { + payload = msg.payload; + } + } + if (msg._session && msg._session.type == "websocket") { + node.serverConfig.send(msg._session.id,payload); + } else { + node.serverConfig.broadcast(payload,function(error){ + if (!!error) { + node.warn("An error occurred while sending:" + inspect(error)); + } + }); + } + }); + } + RED.nodes.registerType("websocket out",WebSocketOutNode); +} diff --git a/dgbuilder/core_nodes/io/23-watch.html b/dgbuilder/core_nodes/io/23-watch.html new file mode 100644 index 00000000..8bf22be5 --- /dev/null +++ b/dgbuilder/core_nodes/io/23-watch.html @@ -0,0 +1,57 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/io/23-watch.js b/dgbuilder/core_nodes/io/23-watch.js new file mode 100644 index 00000000..8a17f5ac --- /dev/null +++ b/dgbuilder/core_nodes/io/23-watch.js @@ -0,0 +1,51 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var Notify = require("fs.notify"); + var fs = require("fs"); + var sep = require("path").sep; + + function WatchNode(n) { + RED.nodes.createNode(this,n); + + this.files = n.files.split(","); + for (var f =0; f < this.files.length; f++) { + this.files[f] = this.files[f].trim(); + } + this.p = (this.files.length == 1) ? this.files[0] : JSON.stringify(this.files); + var node = this; + + var notifications = new Notify(node.files); + notifications.on('change', function (file, event, path) { + try { + if (fs.statSync(path).isDirectory()) { path = path + sep + file; } + } catch(e) { } + var msg = { payload: path, topic: node.p, file: file }; + node.send(msg); + }); + + notifications.on('error', function (error, path) { + node.warn(error); + }); + + this.close = function() { + notifications.close(); + } + } + RED.nodes.registerType("watch",WatchNode); +} diff --git a/dgbuilder/core_nodes/io/25-serial.html b/dgbuilder/core_nodes/io/25-serial.html new file mode 100644 index 00000000..225e4dc3 --- /dev/null +++ b/dgbuilder/core_nodes/io/25-serial.html @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/io/25-serial.js b/dgbuilder/core_nodes/io/25-serial.js new file mode 100644 index 00000000..96e4aca6 --- /dev/null +++ b/dgbuilder/core_nodes/io/25-serial.js @@ -0,0 +1,310 @@ +/** +* Copyright 2013,2014 IBM Corp. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +module.exports = function(RED) { + "use strict"; + var settings = RED.settings; + var events = require("events"); + var util = require("util"); + var serialp = require("serialport"); + var bufMaxSize = 32768; // Max serial buffer size, for inputs... + + // TODO: 'serialPool' should be encapsulated in SerialPortNode + + function SerialPortNode(n) { + RED.nodes.createNode(this,n); + this.serialport = n.serialport; + this.newline = n.newline; + this.addchar = n.addchar || "false"; + this.serialbaud = parseInt(n.serialbaud) || 57600; + this.databits = parseInt(n.databits) || 8; + this.parity = n.parity || "none"; + this.stopbits = parseInt(n.stopbits) || 1; + this.bin = n.bin || "false"; + this.out = n.out || "char"; + } + RED.nodes.registerType("serial-port",SerialPortNode); + + function SerialOutNode(n) { + RED.nodes.createNode(this,n); + this.serial = n.serial; + this.serialConfig = RED.nodes.getNode(this.serial); + + if (this.serialConfig) { + var node = this; + node.port = serialPool.get(this.serialConfig.serialport, + this.serialConfig.serialbaud, + this.serialConfig.databits, + this.serialConfig.parity, + this.serialConfig.stopbits, + this.serialConfig.newline); + node.addCh = ""; + if (node.serialConfig.addchar == "true") { + node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0"); + } + node.on("input",function(msg) { + var payload = msg.payload; + if (!Buffer.isBuffer(payload)) { + if (typeof payload === "object") { + payload = JSON.stringify(payload); + } else { + payload = payload.toString(); + } + payload += node.addCh; + } else if (node.addCh !== "") { + payload = Buffer.concat([payload,new Buffer(node.addCh)]); + } + node.port.write(payload,function(err,res) { + if (err) { + node.error(err); + } + }); + }); + node.port.on('ready', function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + node.port.on('closed', function() { + node.status({fill:"red",shape:"ring",text:"not connected"}); + }); + } else { + this.error("missing serial config"); + } + + this.on("close", function(done) { + if (this.serialConfig) { + serialPool.close(this.serialConfig.serialport,done); + } else { + done(); + } + }); + } + RED.nodes.registerType("serial out",SerialOutNode); + + + function SerialInNode(n) { + RED.nodes.createNode(this,n); + this.serial = n.serial; + this.serialConfig = RED.nodes.getNode(this.serial); + + if (this.serialConfig) { + var node = this; + node.tout = null; + var buf; + if (node.serialConfig.out != "count") { buf = new Buffer(bufMaxSize); } + else { buf = new Buffer(Number(node.serialConfig.newline)); } + var i = 0; + node.status({fill:"grey",shape:"dot",text:"unknown"}); + node.port = serialPool.get(this.serialConfig.serialport, + this.serialConfig.serialbaud, + this.serialConfig.databits, + this.serialConfig.parity, + this.serialConfig.stopbits, + this.serialConfig.newline + ); + + var splitc; + if (node.serialConfig.newline.substr(0,2) == "0x") { + splitc = new Buffer([parseInt(node.serialConfig.newline)]); + } else { + splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0")); + } + + this.port.on('data', function(msg) { + // single char buffer + if ((node.serialConfig.newline === 0)||(node.serialConfig.newline === "")) { + if (node.serialConfig.bin !== "bin") { node.send({"payload": String.fromCharCode(msg)}); } + else { node.send({"payload": new Buffer([msg])}); } + } + else { + // do the timer thing + if (node.serialConfig.out === "time") { + if (node.tout) { + i += 1; + buf[i] = msg; + } + else { + node.tout = setTimeout(function () { + node.tout = null; + var m = new Buffer(i+1); + buf.copy(m,0,0,i+1); + if (node.serialConfig.bin !== "bin") { m = m.toString(); } + node.send({"payload": m}); + m = null; + }, node.serialConfig.newline); + i = 0; + buf[0] = msg; + } + } + // count bytes into a buffer... + else if (node.serialConfig.out === "count") { + buf[i] = msg; + i += 1; + if ( i >= parseInt(node.serialConfig.newline)) { + var m = new Buffer(i); + buf.copy(m,0,0,i); + if (node.serialConfig.bin !== "bin") { m = m.toString(); } + node.send({"payload":m}); + m = null; + i = 0; + } + } + // look to match char... + else if (node.serialConfig.out === "char") { + buf[i] = msg; + i += 1; + if ((msg === splitc[0]) || (i === bufMaxSize)) { + var m = new Buffer(i); + buf.copy(m,0,0,i); + if (node.serialConfig.bin !== "bin") { m = m.toString(); } + node.send({"payload":m}); + m = null; + i = 0; + } + } + else { console.log("Should never get here"); } + } + }); + this.port.on('ready', function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + this.port.on('closed', function() { + node.status({fill:"red",shape:"ring",text:"not connected"}); + }); + } else { + this.error("missing serial config"); + } + + this.on("close", function(done) { + if (this.serialConfig) { + serialPool.close(this.serialConfig.serialport,done); + } else { + done(); + } + }); + } + RED.nodes.registerType("serial in",SerialInNode); + + + var serialPool = function() { + var connections = {}; + return { + get:function(port,baud,databits,parity,stopbits,newline,callback) { + var id = port; + if (!connections[id]) { + connections[id] = function() { + var obj = { + _emitter: new events.EventEmitter(), + serial: null, + _closing: false, + tout: null, + on: function(a,b) { this._emitter.on(a,b); }, + close: function(cb) { this.serial.close(cb); }, + write: function(m,cb) { this.serial.write(m,cb); }, + } + //newline = newline.replace("\\n","\n").replace("\\r","\r"); + var setupSerial = function() { + //if (newline == "") { + obj.serial = new serialp.SerialPort(port,{ + baudrate: baud, + databits: databits, + parity: parity, + stopbits: stopbits, + parser: serialp.parsers.raw + },true, function(err, results) { if (err) { obj.serial.emit('error',err); } }); + //} + //else { + // obj.serial = new serialp.SerialPort(port,{ + // baudrate: baud, + // databits: databits, + // parity: parity, + // stopbits: stopbits, + // parser: serialp.parsers.readline(newline) + // },true, function(err, results) { if (err) obj.serial.emit('error',err); }); + //} + obj.serial.on('error', function(err) { + util.log("[serial] serial port "+port+" error "+err); + obj._emitter.emit('closed'); + obj.tout = setTimeout(function() { + setupSerial(); + }, settings.serialReconnectTime); + }); + obj.serial.on('close', function() { + if (!obj._closing) { + util.log("[serial] serial port "+port+" closed unexpectedly"); + obj._emitter.emit('closed'); + obj.tout = setTimeout(function() { + setupSerial(); + }, settings.serialReconnectTime); + } + }); + obj.serial.on('open',function() { + util.log("[serial] serial port "+port+" opened at "+baud+" baud "+databits+""+parity.charAt(0).toUpperCase()+stopbits); + if (obj.tout) { clearTimeout(obj.tout); } + //obj.serial.flush(); + obj._emitter.emit('ready'); + }); + obj.serial.on('data',function(d) { + //console.log(Buffer.isBuffer(d),d.length,d); + //if (typeof d !== "string") { + // //d = d.toString(); + for (var z=0; z + + + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/io/31-tcpin.js b/dgbuilder/core_nodes/io/31-tcpin.js new file mode 100644 index 00000000..2e4e5e7b --- /dev/null +++ b/dgbuilder/core_nodes/io/31-tcpin.js @@ -0,0 +1,472 @@ +/** + * Copyright 2013,2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var reconnectTime = RED.settings.socketReconnectTime||10000; + var socketTimeout = RED.settings.socketTimeout||null; + var net = require('net'); + + var connectionPool = {}; + + function TcpIn(n) { + RED.nodes.createNode(this,n); + this.host = n.host; + this.port = n.port * 1; + this.topic = n.topic; + this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/ + this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */ + this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r"); + this.base64 = n.base64; + this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server"); + this.closing = false; + var node = this; + var count = 0; + + if (!node.server) { + var buffer = null; + var client; + var reconnectTimeout; + var end = false; + var setupTcpClient = function() { + node.log("connecting to "+node.host+":"+node.port); + node.status({fill:"grey",shape:"dot",text:"connecting"}); + var id = (1+Math.random()*4294967295).toString(16); + client = net.connect(node.port, node.host, function() { + buffer = (node.datatype == 'buffer')? new Buffer(0):""; + node.log("connected to "+node.host+":"+node.port); + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + connectionPool[id] = client; + + client.on('data', function (data) { + if (node.datatype != 'buffer') { + data = data.toString(node.datatype); + } + if (node.stream) { + if ((node.datatype) === "utf8" && node.newline != "") { + buffer = buffer+data; + var parts = buffer.split(node.newline); + for (var i = 0;i 0)) { + var msg = {topic:node.topic,payload:buffer}; + msg._session = {type:"tcp",id:id}; + if (buffer.length !== 0) { + end = true; // only ask for fast re-connect if we actually got something + node.send(msg); + } + buffer = null; + } + }); + client.on('close', function() { + delete connectionPool[id]; + node.status({fill:"red",shape:"ring",text:"disconnected"}); + if (!node.closing) { + if (end) { // if we were asked to close then try to reconnect once very quick. + end = false; + reconnectTimeout = setTimeout(setupTcpClient, 20); + } + else { + node.log("connection lost to "+node.host+":"+node.port); + reconnectTimeout = setTimeout(setupTcpClient, reconnectTime); + } + } + }); + client.on('error', function(err) { + node.log(err); + }); + } + setupTcpClient(); + + this.on('close', function() { + this.closing = true; + client.end(); + clearTimeout(reconnectTimeout); + }); + } else { + var server = net.createServer(function (socket) { + if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } + var id = (1+Math.random()*4294967295).toString(16); + connectionPool[id] = socket; + node.status({text:++count+" connections"}); + + var buffer = (node.datatype == 'buffer')? new Buffer(0):""; + socket.on('data', function (data) { + if (node.datatype != 'buffer') { + data = data.toString(node.datatype); + } + if (node.stream) { + if ((typeof data) === "string" && node.newline != "") { + buffer = buffer+data; + var parts = buffer.split(node.newline); + for (var i = 0;i 0) { + var msg = {topic:node.topic,payload:buffer}; + msg._session = {type:"tcp",id:id}; + node.send(msg); + } + buffer = null; + } + }); + socket.on('timeout', function() { + node.log('timeout closed socket port '+node.port); + socket.end(); + }); + socket.on('close', function() { + delete connectionPool[id]; + node.status({text:--count+" connections"}); + }); + socket.on('error',function(err) { + node.log(err); + }); + }); + server.on('error', function(err) { + if (err) { + node.error('unable to listen on port '+node.port+' : '+err); + } + }); + server.listen(node.port, function(err) { + if (err) { + node.error('unable to listen on port '+node.port+' : '+err); + } else { + node.log('listening on port '+node.port); + + node.on('close', function() { + node.closing = true; + server.close(); + node.log('stopped listening on port '+node.port); + }); + } + }); + } + + } + RED.nodes.registerType("tcp in",TcpIn); + + function TcpOut(n) { + RED.nodes.createNode(this,n); + this.host = n.host; + this.port = n.port * 1; + this.base64 = n.base64; + this.doend = n.end || false; + this.beserver = n.beserver; + this.name = n.name; + this.closing = false; + var node = this; + + if (!node.beserver||node.beserver=="client") { + var reconnectTimeout; + var client = null; + var connected = false; + var end = false; + + var setupTcpClient = function() { + node.log("connecting to "+node.host+":"+node.port); + node.status({fill:"grey",shape:"dot",text:"connecting"}); + client = net.connect(node.port, node.host, function() { + connected = true; + node.log("connected to "+node.host+":"+node.port); + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + client.on('error', function (err) { + node.log('error : '+err); + }); + client.on('end', function (err) { + }); + client.on('close', function() { + node.status({fill:"red",shape:"ring",text:"disconnected"}); + connected = false; + client.destroy(); + if (!node.closing) { + if (end) { + end = false; + reconnectTimeout = setTimeout(setupTcpClient,20); + } + else { + node.log("connection lost to "+node.host+":"+node.port); + reconnectTimeout = setTimeout(setupTcpClient,reconnectTime); + } + } + }); + } + setupTcpClient(); + + node.on("input", function(msg) { + if (connected && msg.payload != null) { + if (Buffer.isBuffer(msg.payload)) { + client.write(msg.payload); + } else if (typeof msg.payload === "string" && node.base64) { + client.write(new Buffer(msg.payload,'base64')); + } else { + client.write(new Buffer(""+msg.payload)); + } + if (node.doend === true) { + end = true; + client.end(); + } + } + }); + + node.on("close", function() { + this.closing = true; + client.end(); + clearTimeout(reconnectTimeout); + }); + + } else if (node.beserver == "reply") { + node.on("input",function(msg) { + if (msg._session && msg._session.type == "tcp") { + var client = connectionPool[msg._session.id]; + if (client) { + if (Buffer.isBuffer(msg.payload)) { + client.write(msg.payload); + } else if (typeof msg.payload === "string" && node.base64) { + client.write(new Buffer(msg.payload,'base64')); + } else { + client.write(new Buffer(""+msg.payload)); + } + } + } + }); + } else { + var connectedSockets = []; + node.status({text:"0 connections"}); + var server = net.createServer(function (socket) { + if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } + var remoteDetails = socket.remoteAddress+":"+socket.remotePort; + node.log("connection from "+remoteDetails); + connectedSockets.push(socket); + node.status({text:connectedSockets.length+" connections"}); + socket.on('timeout', function() { + node.log('timeout closed socket port '+node.port); + socket.end(); + }); + socket.on('close',function() { + node.log("connection closed from "+remoteDetails); + connectedSockets.splice(connectedSockets.indexOf(socket),1); + node.status({text:connectedSockets.length+" connections"}); + }); + socket.on('error',function() { + node.log("socket error from "+remoteDetails); + connectedSockets.splice(connectedSockets.indexOf(socket),1); + node.status({text:connectedSockets.length+" connections"}); + }); + }); + + node.on("input", function(msg) { + if (msg.payload != null) { + var buffer; + if (Buffer.isBuffer(msg.payload)) { + buffer = msg.payload; + } else if (typeof msg.payload === "string" && node.base64) { + buffer = new Buffer(msg.payload,'base64'); + } else { + buffer = new Buffer(""+msg.payload); + } + for (var i = 0; i= node.serialConfig.count) { + node.send({"payload": buf}); + client.end(); + i = 0; + } + } + // look for a char + else { + buf[i] = data[j]; + i += 1; + if (data[j] == node.splitc) { + var m = new Buffer(i); + buf.copy(m,0,0,i); + node.send({"payload": m}); + client.end(); + m = null; + i = 0; + } + } + } + } + }); + + client.on('end', function() { + //node.log('client disconnected'); + node.connected = false; + node.status({}); + client = null; + }); + + client.on('error', function() { + node.log('connect failed'); + node.status({fill:"red",shape:"ring",text:"error"}); + if (client) { client.end(); } + }); + + client.on('timeout',function() { + node.log('connect timeout'); + if (client) { + client.end(); + setTimeout(function() { + client.connect(node.port, node.server, function() { + //node.log('client connected'); + node.connected = true; + client.write(msg.payload); + }); + },reconnectTime); + } + }); + } + else { client.write(msg.payload); } + }); + + this.on("close", function() { + if (client) { buf = null; client.end(); } + }); + + } + RED.nodes.registerType("tcp request",TcpGet); +} diff --git a/dgbuilder/core_nodes/io/32-udp.html b/dgbuilder/core_nodes/io/32-udp.html new file mode 100644 index 00000000..1c2eed57 --- /dev/null +++ b/dgbuilder/core_nodes/io/32-udp.html @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/io/32-udp.js b/dgbuilder/core_nodes/io/32-udp.js new file mode 100644 index 00000000..a7968e3a --- /dev/null +++ b/dgbuilder/core_nodes/io/32-udp.js @@ -0,0 +1,171 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var dgram = require('dgram'); + + // The Input Node + function UDPin(n) { + RED.nodes.createNode(this,n); + this.group = n.group; + this.port = n.port; + this.datatype = n.datatype; + this.iface = n.iface || null; + this.multicast = n.multicast; + var node = this; + + var server = dgram.createSocket('udp4'); + + server.on("error", function (err) { + if ((err.code == "EACCES") && (node.port < 1024)) { + node.error("UDP access error, you may need root access for ports below 1024"); + } else { + node.error("UDP error : "+err.code); + } + server.close(); + }); + + server.on('message', function (message, remote) { + var msg; + if (node.datatype =="base64") { + msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port }; + } else if (node.datatype =="utf8") { + msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port }; + } else { + msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; + } + node.send(msg); + }); + + server.on('listening', function () { + var address = server.address(); + node.log('udp listener at ' + address.address + ":" + address.port); + if (node.multicast == "true") { + server.setBroadcast(true); + try { + server.setMulticastTTL(128); + server.addMembership(node.group,node.iface); + node.log("udp multicast group "+node.group); + } catch (e) { + if (e.errno == "EINVAL") { + node.error("Bad Multicast Address"); + } else if (e.errno == "ENODEV") { + node.error("Must be ip address of the required interface"); + } else { + node.error("Error :"+e.errno); + } + } + } + }); + + node.on("close", function() { + try { + server.close(); + node.log('udp listener stopped'); + } catch (err) { + node.error(err); + } + }); + + server.bind(node.port,node.iface); + } + RED.nodes.registerType("udp in",UDPin); + + + // The Output Node + function UDPout(n) { + RED.nodes.createNode(this,n); + //this.group = n.group; + this.port = n.port; + this.outport = n.outport||""; + this.base64 = n.base64; + this.addr = n.addr; + this.iface = n.iface || null; + this.multicast = n.multicast; + var node = this; + + var sock = dgram.createSocket('udp4'); // only use ipv4 for now + + if (node.multicast != "false") { + if (node.outport == "") { node.outport = node.port; } + sock.bind(node.outport, function() { // have to bind before you can enable broadcast... + sock.setBroadcast(true); // turn on broadcast + if (node.multicast == "multi") { + try { + sock.setMulticastTTL(128); + sock.addMembership(node.addr,node.iface); // Add to the multicast group + node.log('udp multicast ready : '+node.outport+' -> '+node.addr+":"+node.port); + } catch (e) { + if (e.errno == "EINVAL") { + node.error("Bad Multicast Address"); + } else if (e.errno == "ENODEV") { + node.error("Must be ip address of the required interface"); + } else { + node.error("Error :"+e.errno); + } + } + } else { + node.log('udp broadcast ready : '+node.outport+' -> '+node.addr+":"+node.port); + } + }); + } else if (node.outport != "") { + sock.bind(node.outport); + node.log('udp ready : '+node.outport+' -> '+node.addr+":"+node.port); + } else { + node.log('udp ready : '+node.addr+":"+node.port); + } + + node.on("input", function(msg) { + if (msg.payload != null) { + var add = node.addr || msg.ip || ""; + var por = node.port || msg.port || 0; + if (add == "") { + node.warn("udp: ip address not set"); + } else if (por == 0) { + node.warn("udp: port not set"); + } else if (isNaN(por) || (por < 1) || (por > 65535)) { + node.warn("udp: port number not valid"); + } else { + var message; + if (node.base64) { + message = new Buffer(msg.payload, 'base64'); + } else if (msg.payload instanceof Buffer) { + message = msg.payload; + } else { + message = new Buffer(""+msg.payload); + } + sock.send(message, 0, message.length, por, add, function(err, bytes) { + if (err) { + node.error("udp : "+err); + } + message = null; + }); + } + } + }); + + node.on("close", function() { + try { + sock.close(); + node.log('udp output stopped'); + } catch (err) { + node.error(err); + } + }); + } + RED.nodes.registerType("udp out",UDPout); +} diff --git a/dgbuilder/core_nodes/io/lib/mqtt.js b/dgbuilder/core_nodes/io/lib/mqtt.js new file mode 100644 index 00000000..141a8889 --- /dev/null +++ b/dgbuilder/core_nodes/io/lib/mqtt.js @@ -0,0 +1,254 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ +var util = require("util"); +var mqtt = require("mqtt"); +var events = require("events"); +//var inspect = require("sys").inspect; + +//var Client = module.exports.Client = function( + +var port = 1883; +var host = "localhost"; + +function MQTTClient(port,host) { + this.port = port||1883; + this.host = host||"localhost"; + this.messageId = 1; + this.pendingSubscriptions = {}; + this.inboundMessages = {}; + this.lastOutbound = (new Date()).getTime(); + this.lastInbound = (new Date()).getTime(); + this.connected = false; + + this._nextMessageId = function() { + this.messageId += 1; + if (this.messageId > 0xFFFF) { + this.messageId = 1; + } + return this.messageId; + } + events.EventEmitter.call(this); +} +util.inherits(MQTTClient, events.EventEmitter); + +MQTTClient.prototype.connect = function(options) { + if (!this.connected) { + var self = this; + options = options||{}; + self.options = options; + self.options.keepalive = options.keepalive||15; + self.options.clean = self.options.clean||true; + self.options.protocolId = 'MQIsdp'; + self.options.protocolVersion = 3; + + self.client = mqtt.createConnection(this.port,this.host,function(err,client) { + if (err) { + self.connected = false; + clearInterval(self.watchdog); + self.connectionError = true; + //util.log('[mqtt] ['+self.uid+'] connection error 1 : '+inspect(err)); + self.emit('connectionlost',err); + return; + } + client.on('close',function(e) { + //util.log('[mqtt] ['+self.uid+'] on close'); + clearInterval(self.watchdog); + if (!self.connectionError) { + if (self.connected) { + self.connected = false; + self.emit('connectionlost',e); + } else { + self.emit('disconnect'); + } + } + }); + client.on('error',function(e) { + //util.log('[mqtt] ['+self.uid+'] on error : '+inspect(e)); + clearInterval(self.watchdog); + if (self.connected) { + self.connected = false; + self.emit('connectionlost',e); + } + }); + client.on('connack',function(packet) { + if (packet.returnCode == 0) { + self.watchdog = setInterval(function(self) { + var now = (new Date()).getTime(); + + //util.log('[mqtt] ['+self.uid+'] watchdog '+inspect({connected:self.connected,connectionError:self.connectionError,pingOutstanding:self.pingOutstanding,now:now,lastOutbound:self.lastOutbound,lastInbound:self.lastInbound})); + + if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) { + if (self.pingOutstanding) { + //util.log('[mqtt] ['+self.uid+'] watchdog pingOustanding - disconnect'); + try { + self.client.disconnect(); + } catch (err) { + } + } else { + //util.log('[mqtt] ['+self.uid+'] watchdog pinging'); + self.lastOutbound = (new Date()).getTime(); + self.lastInbound = (new Date()).getTime(); + self.pingOutstanding = true; + self.client.pingreq(); + } + } + + },self.options.keepalive*500,self); + self.pingOutstanding = false; + self.lastInbound = (new Date()).getTime() + self.lastOutbound = (new Date()).getTime() + self.connected = true; + self.connectionError = false; + self.emit('connect'); + } else { + self.connected = false; + self.emit('connectionlost'); + } + }); + client.on('suback',function(packet) { + self.lastInbound = (new Date()).getTime() + var topic = self.pendingSubscriptions[packet.messageId]; + self.emit('subscribe',topic,packet.granted[0]); + delete self.pendingSubscriptions[packet.messageId]; + }); + client.on('unsuback',function(packet) { + self.lastInbound = (new Date()).getTime() + var topic = self.pendingSubscriptions[packet.messageId]; + self.emit('unsubscribe',topic,packet.granted[0]); + delete self.pendingSubscriptions[packet.messageId]; + }); + client.on('publish',function(packet) { + self.lastInbound = (new Date()).getTime() + if (packet.qos < 2) { + var p = packet; + self.emit('message',p.topic,p.payload,p.qos,p.retain); + } else { + self.inboundMessages[packet.messageId] = packet; + this.lastOutbound = (new Date()).getTime() + self.client.pubrec(packet); + } + if (packet.qos == 1) { + this.lastOutbound = (new Date()).getTime() + self.client.puback(packet); + } + }); + + client.on('pubrel',function(packet) { + self.lastInbound = (new Date()).getTime() + var p = self.inboundMessages[packet.messageId]; + if (p) { + self.emit('message',p.topic,p.payload,p.qos,p.retain); + delete self.inboundMessages[packet.messageId]; + } + self.lastOutbound = (new Date()).getTime() + self.client.pubcomp(packet); + }); + + client.on('puback',function(packet) { + self.lastInbound = (new Date()).getTime() + // outbound qos-1 complete + }); + + client.on('pubrec',function(packet) { + self.lastInbound = (new Date()).getTime() + self.lastOutbound = (new Date()).getTime() + self.client.pubrel(packet); + }); + client.on('pubcomp',function(packet) { + self.lastInbound = (new Date()).getTime() + // outbound qos-2 complete + }); + client.on('pingresp',function(packet) { + //util.log('[mqtt] ['+self.uid+'] received pingresp'); + self.lastInbound = (new Date()).getTime() + self.pingOutstanding = false; + }); + + this.lastOutbound = (new Date()).getTime() + this.connectionError = false; + client.connect(self.options); + }); + } +} + +MQTTClient.prototype.subscribe = function(topic,qos) { + var self = this; + if (self.connected) { + var options = { + subscriptions:[{topic:topic,qos:qos}], + messageId: self._nextMessageId() + }; + this.pendingSubscriptions[options.messageId] = topic; + this.lastOutbound = (new Date()).getTime() + self.client.subscribe(options); + } +} +MQTTClient.prototype.unsubscribe = function(topic) { + var self = this; + if (self.connected) { + var options = { + topic:topic, + messageId: self._nextMessageId() + }; + this.pendingSubscriptions[options.messageId] = topic; + this.lastOutbound = (new Date()).getTime() + self.client.unsubscribe(options); + } +} + +MQTTClient.prototype.publish = function(topic,payload,qos,retain) { + var self = this; + if (self.connected) { + + if (!Buffer.isBuffer(payload)) { + if (typeof payload === "object") { + payload = JSON.stringify(payload); + } else if (typeof payload !== "string") { + payload = ""+payload; + } + } + var options = { + topic: topic, + payload: payload, + qos: qos||0, + retain:retain||false + }; + if (options.qos != 0) { + options.messageId = self._nextMessageId(); + } + this.lastOutbound = (new Date()).getTime() + self.client.publish(options); + } +} + +MQTTClient.prototype.disconnect = function() { + var self = this; + if (this.connected) { + this.connected = false; + try { + this.client.disconnect(); + } catch(err) { + } + } +} +MQTTClient.prototype.isConnected = function() { + return this.connected; +} +module.exports.createClient = function(port,host) { + var mqtt_client = new MQTTClient(port,host); + return mqtt_client; +} + diff --git a/dgbuilder/core_nodes/io/lib/mqttConnectionPool.js b/dgbuilder/core_nodes/io/lib/mqttConnectionPool.js new file mode 100644 index 00000000..d15f0fc7 --- /dev/null +++ b/dgbuilder/core_nodes/io/lib/mqttConnectionPool.js @@ -0,0 +1,128 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ +var util = require("util"); +var mqtt = require("./mqtt"); +var settings = require(process.env.NODE_RED_HOME+"/red/red").settings; + +var connections = {}; + +function matchTopic(ts,t) { + if (ts == "#") { + return true; + } + var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); + return re.test(t); +} + +module.exports = { + get: function(broker,port,clientid,username,password,will) { + var id = "["+(username||"")+":"+(password||"")+"]["+(clientid||"")+"]@"+broker+":"+port; + if (!connections[id]) { + connections[id] = function() { + var uid = (1+Math.random()*4294967295).toString(16); + var client = mqtt.createClient(port,broker); + client.uid = uid; + client.setMaxListeners(0); + var options = {keepalive:15}; + options.clientId = clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16); + options.username = username; + options.password = password; + options.will = will; + var queue = []; + var subscriptions = []; + var connecting = false; + var obj = { + _instances: 0, + publish: function(msg) { + if (client.isConnected()) { + client.publish(msg.topic,msg.payload,msg.qos,msg.retain); + } else { + if (!connecting) { + connecting = true; + client.connect(options); + } + queue.push(msg); + } + }, + subscribe: function(topic,qos,callback) { + subscriptions.push({topic:topic,qos:qos,callback:callback}); + client.on('message',function(mtopic,mpayload,mqos,mretain) { + if (matchTopic(topic,mtopic)) { + callback(mtopic,mpayload,mqos,mretain); + } + }); + if (client.isConnected()) { + client.subscribe(topic,qos); + } + }, + on: function(a,b){ + client.on(a,b); + }, + once: function(a,b){ + client.once(a,b); + }, + connect: function() { + if (client && !client.isConnected() && !connecting) { + connecting = true; + client.connect(options); + } + }, + disconnect: function() { + this._instances -= 1; + if (this._instances == 0) { + client.disconnect(); + client = null; + delete connections[id]; + } + } + }; + client.on('connect',function() { + if (client) { + util.log('[mqtt] ['+uid+'] connected to broker tcp://'+broker+':'+port); + connecting = false; + for (var s in subscriptions) { + var topic = subscriptions[s].topic; + var qos = subscriptions[s].qos; + var callback = subscriptions[s].callback; + client.subscribe(topic,qos); + } + //console.log("connected - publishing",queue.length,"messages"); + while(queue.length) { + var msg = queue.shift(); + //console.log(msg); + client.publish(msg.topic,msg.payload,msg.qos,msg.retain); + } + } + }); + client.on('connectionlost', function(err) { + util.log('[mqtt] ['+uid+'] connection lost to broker tcp://'+broker+':'+port); + connecting = false; + setTimeout(function() { + obj.connect(); + }, settings.mqttReconnectTime||5000); + }); + client.on('disconnect', function() { + connecting = false; + util.log('[mqtt] ['+uid+'] disconnected from broker tcp://'+broker+':'+port); + }); + + return obj + }(); + } + connections[id]._instances += 1; + return connections[id]; + } +}; diff --git a/dgbuilder/core_nodes/logic/10-switch.html b/dgbuilder/core_nodes/logic/10-switch.html new file mode 100644 index 00000000..4e02f446 --- /dev/null +++ b/dgbuilder/core_nodes/logic/10-switch.html @@ -0,0 +1,198 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/logic/10-switch.js b/dgbuilder/core_nodes/logic/10-switch.js new file mode 100644 index 00000000..8bcb8571 --- /dev/null +++ b/dgbuilder/core_nodes/logic/10-switch.js @@ -0,0 +1,78 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var operators = { + 'eq': function(a, b) { return a == b; }, + 'neq': function(a, b) { return a != b; }, + 'lt': function(a, b) { return a < b; }, + 'lte': function(a, b) { return a <= b; }, + 'gt': function(a, b) { return a > b; }, + 'gte': function(a, b) { return a >= b; }, + 'btwn': function(a, b, c) { return a >= b && a <= c; }, + 'cont': function(a, b) { return (a + "").indexOf(b) != -1; }, + 'regex': function(a, b) { return (a + "").match(new RegExp(b)); }, + 'true': function(a) { return a === true; }, + 'false': function(a) { return a === false; }, + 'null': function(a) { return typeof a == "undefined"; }, + 'nnull': function(a) { return typeof a != "undefined"; }, + 'else': function(a) { return a === true; } + }; + + function SwitchNode(n) { + RED.nodes.createNode(this, n); + this.rules = n.rules; + this.property = n.property; + this.checkall = n.checkall || "true"; + var propertyParts = n.property.split("."); + var node = this; + + for (var i=0; i + + + + + + diff --git a/dgbuilder/core_nodes/logic/15-change.js b/dgbuilder/core_nodes/logic/15-change.js new file mode 100644 index 00000000..b7ef62e1 --- /dev/null +++ b/dgbuilder/core_nodes/logic/15-change.js @@ -0,0 +1,74 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + function ChangeNode(n) { + RED.nodes.createNode(this, n); + this.action = n.action; + this.property = n.property || ""; + this.from = n.from || " "; + this.to = n.to || " "; + this.reg = (n.reg === null || n.reg); + var node = this; + if (node.reg === false) { + this.from = this.from.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + } + var makeNew = function( stem, path, value ) { + var lastPart = (arguments.length === 3) ? path.pop() : false; + for (var i = 0; i < path.length; i++) { + stem = stem[path[i]] = stem[path[i]] || {}; + } + if (lastPart) { stem = stem[lastPart] = value; } + return stem; + }; + + this.on('input', function (msg) { + if (node.action == "change") { + try { + node.re = new RegExp(this.from, "g"); + } catch (e) { + node.error(e.message); + } + if (typeof msg[node.property] === "string") { + msg[node.property] = (msg[node.property]).replace(node.re, node.to); + } + } + //else if (node.action == "replace") { + //if (node.to.indexOf("msg.") == 0) { + //msg[node.property] = eval(node.to); + //} + //else { + //msg[node.property] = node.to; + //} + //} + else if (node.action == "replace") { + if (node.to.indexOf("msg.") === 0) { + makeNew( msg, node.property.split("."), eval(node.to) ); + } + else { + makeNew( msg, node.property.split("."), node.to ); + } + //makeNew( msg, node.property.split("."), node.to ); + } + else if (node.action == "delete") { + delete(msg[node.property]); + } + node.send(msg); + }); + } + RED.nodes.registerType("change", ChangeNode); +} diff --git a/dgbuilder/core_nodes/logic/16-range.html b/dgbuilder/core_nodes/logic/16-range.html new file mode 100644 index 00000000..5f87128f --- /dev/null +++ b/dgbuilder/core_nodes/logic/16-range.html @@ -0,0 +1,81 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/logic/16-range.js b/dgbuilder/core_nodes/logic/16-range.js new file mode 100644 index 00000000..ec39342a --- /dev/null +++ b/dgbuilder/core_nodes/logic/16-range.js @@ -0,0 +1,48 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + function RangeNode(n) { + RED.nodes.createNode(this, n); + this.action = n.action; + this.round = n.round || false; + this.minin = Number(n.minin); + this.maxin = Number(n.maxin); + this.minout = Number(n.minout); + this.maxout = Number(n.maxout); + var node = this; + + this.on('input', function (msg) { + var n = Number(msg.payload); + if (!isNaN(n)) { + if (node.action == "clamp") { + if (n < node.minin) { n = node.minin; } + if (n > node.maxin) { n = node.maxin; } + } + if (node.action == "roll") { + if (n >= node.maxin) { n = (n - node.minin) % (node.maxin - node.minin) + node.minin; } + if (n < node.minin) { n = (n - node.minin) % (node.maxin - node.minin) + node.maxin; } + } + msg.payload = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout; + if (node.round) { msg.payload = Math.round(msg.payload); } + node.send(msg); + } + else { node.log("Not a number: "+msg.payload); } + }); + } + RED.nodes.registerType("range", RangeNode); +} diff --git a/dgbuilder/core_nodes/parsers/70-CSV.html b/dgbuilder/core_nodes/parsers/70-CSV.html new file mode 100644 index 00000000..5632246f --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-CSV.html @@ -0,0 +1,123 @@ + + + + + + + + diff --git a/dgbuilder/core_nodes/parsers/70-CSV.js b/dgbuilder/core_nodes/parsers/70-CSV.js new file mode 100644 index 00000000..56aa7c72 --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-CSV.js @@ -0,0 +1,157 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + function CSVNode(n) { + RED.nodes.createNode(this,n); + this.template = n.temp.split(","); + this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r"); + this.quo = '"'; + this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r"); + this.winflag = (this.ret === "\r\n"); + this.lineend = "\n"; + this.multi = n.multi || "one"; + this.hdrin = n.hdrin || false; + this.hdrout = n.hdrout || false; + this.goodtmpl = true; + var node = this; + + // pass in an array of column names to be trimed, de-quoted and retrimed + var clean = function(col) { + for (var t = 0; t < col.length; t++) { + col[t] = col[t].trim(); // remove leading and trailing whitespace + if (col[t].charAt(0) === '"' && col[t].charAt(col[t].length -1) === '"') { + // remove leading and trailing quotes (if they exist) - and remove whitepace again. + col[t] = col[t].substr(1,col[t].length -2).trim(); + } + } + if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; } + else { node.goodtmpl = true; } + return col; + } + node.template = clean(node.template); + + this.on("input", function(msg) { + if (msg.hasOwnProperty("payload")) { + if (typeof msg.payload == "object") { // convert object to CSV string + try { + var ou = ""; + if (node.hdrout) { + ou += node.template.join(node.sep) + node.ret; + } + if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; } + for (var s = 0; s < msg.payload.length; s++) { + for (var t=0; t < node.template.length; t++) { + + // aaargh - resorting to eval here - but fairly contained front and back. + var p = RED.util.ensureString(eval("msg.payload[s]."+node.template[t])); + + if (p === "undefined") { p = ""; } + if (p.indexOf(node.sep) != -1) { // add quotes if any "commas" + ou += node.quo + p + node.quo + node.sep; + } + else if (p.indexOf(node.quo) != -1) { // add double quotes if any quotes + p = p.replace(/"/g, '""'); + ou += node.quo + p + node.quo + node.sep; + } + else { ou += p + node.sep; } // otherwise just add + } + ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline" + } + node.send({payload:ou}); + } + catch(e) { node.log(e); } + } + else if (typeof msg.payload == "string") { // convert CSV string to object + try { + var f = true; // flag to indicate if inside or outside a pair of quotes true = outside. + var j = 0; // pointer into array of template items + var k = [""]; // array of data for each of the template items + var o = {}; // output object to build up + var a = []; // output array is needed for multiline option + var first = true; // is this the first line + var tmp = ""; + + // For now we are just going to assume that any \r or \n means an end of line... + // got to be a weird csv that has singleton \r \n in it for another reason... + + // Now process the whole file/line + for (var i = 0; i < msg.payload.length; i++) { + if ((node.hdrin === true) && first) { // if the template is in the first line + if ((msg.payload[i] === "\n")||(msg.payload[i] === "\r")) { // look for first line break + node.template = clean(tmp.split(node.sep)); + first = false; + } + else { tmp += msg.payload[i]; } + } + else { + if (msg.payload[i] === node.quo) { // if it's a quote toggle inside or outside + f = !f; + if (msg.payload[i-1] === node.quo) { k[j] += '\"'; } // if it's a quotequote then it's actually a quote + } + else if ((msg.payload[i] === node.sep) && f) { // if we are outside of quote (ie valid separator + if (!node.goodtmpl) { node.template[j] = "col"+(j+1); } + if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) { + if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); } + o[node.template[j]] = k[j]; + } + j += 1; + k[j] = ""; + } + else if (f && ((msg.payload[i] === "\n") || (msg.payload[i] === "\r"))) { // handle multiple lines + //console.log(j,k,o,k[j]); + if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) { + if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); } + else { k[j].replace(/\r$/,''); } + o[node.template[j]] = k[j]; + } + if (JSON.stringify(o) !== "{}") { // don't send empty objects + if (node.multi === "one") { node.send({payload:o}); } // either send + else { a.push(o); } // or add to the array + } + j = 0; + k = [""]; + o = {}; + } + else { // just add to the part of the message + k[j] += msg.payload[i]; + } + } + } + // Finished so finalize and send anything left + //console.log(j,k,o,k[j]); + if (!node.goodtmpl) { node.template[j] = "col"+(j+1); } + if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) { + if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); } + else { k[j].replace(/\r$/,''); } + o[node.template[j]] = k[j]; + } + msg.payload = o; + if (JSON.stringify(o) !== "{}") { // don't send empty objects + if (node.multi === "one") { node.send({payload:o}); } // either send + else { a.push(o); } // or add to the aray + } + if (node.multi !== "one") { node.send({payload:a}); } // finally send the array + } + catch(e) { node.log(e); } + } + else { node.log("This node only handles csv strings or js objects."); } + } + }); + } + RED.nodes.registerType("csv",CSVNode); +} diff --git a/dgbuilder/core_nodes/parsers/70-HTML.html b/dgbuilder/core_nodes/parsers/70-HTML.html new file mode 100644 index 00000000..2b73b49c --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-HTML.html @@ -0,0 +1,73 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/parsers/70-HTML.js b/dgbuilder/core_nodes/parsers/70-HTML.js new file mode 100644 index 00000000..7a9450f3 --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-HTML.js @@ -0,0 +1,60 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var cheerio = require('cheerio'); + + function CheerioNode(n) { + RED.nodes.createNode(this,n); + this.tag = n.tag || "h1"; + this.ret = n.ret || "html"; + this.as = n.as || "single"; + var node = this; + this.on("input", function(msg) { + try { + var $ = cheerio.load(msg.payload); + var pay = []; + $(node.tag).each(function() { + if (node.as === "multi") { + var pay2 = null; + if (node.ret === "html") { pay2 = $(this).html(); } + if (node.ret === "text") { pay2 = $(this).text(); } + //if (node.ret === "attr") { pay2 = $(this)[0]["attribs"]; } + //if (node.ret === "val") { pay2 = $(this).val(); } + if (pay2) { + msg.payload = pay2; + node.send(msg); + } + } + if (node.as === "single") { + if (node.ret === "html") { pay.push( $(this).html() ); } + if (node.ret === "text") { pay.push( $(this).text() ); } + //if (node.ret === "attr") { pay.push( $(this)[0]["attribs"] ); } + //if (node.ret === "val") { pay.push( $(this).val() ); } + } + }); + if ((node.as === "single") && (pay.length !== 0)) { + msg.payload = pay; + node.send(msg); + } + } catch (error) { + node.log('Error: '+error.message); + } + }); + } + RED.nodes.registerType("html",CheerioNode); +} diff --git a/dgbuilder/core_nodes/parsers/70-JSON.html b/dgbuilder/core_nodes/parsers/70-JSON.html new file mode 100644 index 00000000..864974ab --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-JSON.html @@ -0,0 +1,47 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/parsers/70-JSON.js b/dgbuilder/core_nodes/parsers/70-JSON.js new file mode 100644 index 00000000..e216bd4f --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-JSON.js @@ -0,0 +1,46 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var util = require("util"); + + function JSONNode(n) { + RED.nodes.createNode(this,n); + var node = this; + this.on("input", function(msg) { + if (msg.hasOwnProperty("payload")) { + if (typeof msg.payload === "string") { + try { + msg.payload = JSON.parse(msg.payload); + node.send(msg); + } + catch(e) { node.log(e+ "\n"+msg.payload); } + } + else if (typeof msg.payload === "object") { + if (!Buffer.isBuffer(msg.payload) ) { + if (!util.isArray(msg.payload)) { + msg.payload = JSON.stringify(msg.payload); + node.send(msg); + } + } + } + else { node.log("dropped: "+msg.payload); } + } + }); + } + RED.nodes.registerType("json",JSONNode); +} diff --git a/dgbuilder/core_nodes/parsers/70-XML.html b/dgbuilder/core_nodes/parsers/70-XML.html new file mode 100644 index 00000000..8b0a2843 --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-XML.html @@ -0,0 +1,48 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/parsers/70-XML.js b/dgbuilder/core_nodes/parsers/70-XML.js new file mode 100644 index 00000000..931de7f5 --- /dev/null +++ b/dgbuilder/core_nodes/parsers/70-XML.js @@ -0,0 +1,46 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var xml2js = require('xml2js'); + var parseString = xml2js.parseString; + var builder = new xml2js.Builder({renderOpts:{pretty:false}}); + + function XMLNode(n) { + RED.nodes.createNode(this,n); + var node = this; + this.on("input", function(msg) { + if (msg.hasOwnProperty("payload")) { + if (typeof msg.payload == "object") { + msg.payload = builder.buildObject(msg.payload); + node.send(msg); + } + else if (typeof msg.payload == "string") { + parseString(msg.payload, {strict:true,async:true}, function (err, result) { + if (err) { node.error(err); } + else { + msg.payload = result; + node.send(msg); + } + }); + } + else { node.log("This node only handles xml strings or js objects."); } + } + }); + } + RED.nodes.registerType("xml",XMLNode); +} diff --git a/dgbuilder/core_nodes/social/27-twitter.html b/dgbuilder/core_nodes/social/27-twitter.html new file mode 100644 index 00000000..99d45213 --- /dev/null +++ b/dgbuilder/core_nodes/social/27-twitter.html @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/social/27-twitter.js b/dgbuilder/core_nodes/social/27-twitter.js new file mode 100644 index 00000000..5cacd9eb --- /dev/null +++ b/dgbuilder/core_nodes/social/27-twitter.js @@ -0,0 +1,347 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var ntwitter = require('twitter-ng'); + var OAuth= require('oauth').OAuth; + var request = require('request'); + + function TwitterNode(n) { + RED.nodes.createNode(this,n); + this.screen_name = n.screen_name; + } + RED.nodes.registerType("twitter-credentials",TwitterNode,{ + credentials: { + screen_name: {type:"text"}, + access_token: {type: "password"}, + access_token_secret: {type:"password"} + } + }); + + function TwitterInNode(n) { + RED.nodes.createNode(this,n); + this.active = true; + this.user = n.user; + //this.tags = n.tags.replace(/ /g,''); + this.tags = n.tags; + this.twitter = n.twitter; + this.topic = n.topic||"tweets"; + this.twitterConfig = RED.nodes.getNode(this.twitter); + var credentials = RED.nodes.getCredentials(this.twitter); + + if (credentials && credentials.screen_name == this.twitterConfig.screen_name) { + var twit = new ntwitter({ + consumer_key: "OKjYEd1ef2bfFolV25G5nQ", + consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g", + access_token_key: credentials.access_token, + access_token_secret: credentials.access_token_secret + }); + + + //setInterval(function() { + // twit.get("/application/rate_limit_status.json",null,function(err,cb) { + // console.log("direct_messages:",cb["resources"]["direct_messages"]); + // }); + // + //},10000); + + var node = this; + if (this.user === "user") { + node.poll_ids = []; + node.since_ids = {}; + var users = node.tags.split(","); + for (var i=0;i=0;t-=1) { + var tweet = cb[t]; + var where = tweet.user.location||""; + var la = tweet.lang || tweet.user.lang; + //console.log(tweet.user.location,"=>",tweet.user.screen_name,"=>",pay); + var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, location:where, lang:la, tweet:tweet }; + node.send(msg); + if (t == 0) { + node.since_ids[u] = tweet.id_str; + } + } + } + if (err) { + node.error(err); + } + }); + },60000)); + } + }()); + } + } else if (this.user === "dm") { + node.poll_ids = []; + twit.getDirectMessages({ + screen_name:node.twitterConfig.screen_name, + trim_user:0, + count:1 + },function(err,cb) { + if (err) { + node.error(err); + return; + } + if (cb[0]) { + node.since_id = cb[0].id_str; + } else { + node.since_id = '0'; + } + node.poll_ids.push(setInterval(function() { + twit.getDirectMessages({ + screen_name:node.twitterConfig.screen_name, + trim_user:0, + since_id:node.since_id + },function(err,cb) { + if (cb) { + for (var t=cb.length-1;t>=0;t-=1) { + var tweet = cb[t]; + var msg = { topic:node.topic+"/"+tweet.sender.screen_name, payload:tweet.text, tweet:tweet }; + node.send(msg); + if (t == 0) { + node.since_id = tweet.id_str; + } + } + } + if (err) { + node.error(err); + } + }); + },120000)); + }); + + } else if (this.tags !== "") { + try { + var thing = 'statuses/filter'; + if (this.user === "true") { thing = 'user'; } + var st = { track: [node.tags] }; + var bits = node.tags.split(","); + if ((bits.length > 0) && (bits.length % 4 == 0)) { + if ((Number(bits[0]) < Number(bits[2])) && (Number(bits[1]) < Number(bits[3]))) { + st = { locations: node.tags }; + } + else { + node.log("possible bad geo area format. Should be lower-left lon, lat, upper-right lon, lat"); + } + } + + var setupStream = function() { + if (node.active) { + twit.stream(thing, st, function(stream) { + //console.log(st); + //twit.stream('user', { track: [node.tags] }, function(stream) { + //twit.stream('site', { track: [node.tags] }, function(stream) { + //twit.stream('statuses/filter', { track: [node.tags] }, function(stream) { + node.stream = stream; + stream.on('data', function(tweet) { + //console.log(tweet.user); + if (tweet.user !== undefined) { + var where = tweet.user.location||""; + var la = tweet.lang || tweet.user.lang; + //console.log(tweet.user.location,"=>",tweet.user.screen_name,"=>",pay); + var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, location:where, lang:la, tweet:tweet }; + node.send(msg); + } + }); + stream.on('limit', function(tweet) { + node.log("tweet rate limit hit"); + }); + stream.on('error', function(tweet,rc) { + if (rc == 420) { + node.warn("Twitter rate limit hit"); + } else { + node.warn("Stream error:"+tweet.toString()+" ("+rc+")"); + } + setTimeout(setupStream,10000); + }); + stream.on('destroy', function (response) { + if (this.active) { + node.warn("twitter ended unexpectedly"); + setTimeout(setupStream,10000); + } + }); + }); + } + } + setupStream(); + } + catch (err) { + node.error(err); + } + } else { + this.error("Invalid tag property"); + } + } else { + this.error("missing twitter credentials"); + } + + this.on('close', function() { + if (this.stream) { + this.active = false; + this.stream.destroy(); + } + if (this.poll_ids) { + for (var i=0;i 140) { + msg.payload = msg.payload.slice(0,139); + node.warn("Tweet greater than 140 : truncated"); + } + + if (msg.media && Buffer.isBuffer(msg.media)) { + var apiUrl = "https://api.twitter.com/1.1/statuses/update_with_media.json"; + var signedUrl = oa.signUrl(apiUrl, + credentials.access_token, + credentials.access_token_secret, + "POST"); + + var r = request.post(signedUrl,function(err,httpResponse,body) { + if (err) { + node.error(err.toString()); + node.status({fill:"red",shape:"ring",text:"failed"}); + } else { + var response = JSON.parse(body); + if (body.errors) { + var errorList = body.errors.map(function(er) { return er.code+": "+er.message }).join(", "); + node.error("tweet failed: "+errorList); + node.status({fill:"red",shape:"ring",text:"failed"}); + } else { + node.status({}); + } + } + }); + var form = r.form(); + form.append("status",msg.payload); + form.append("media[]",msg.media,{filename:"image"}); + + } else { + twit.updateStatus(msg.payload, function (err, data) { + if (err) { + node.status({fill:"red",shape:"ring",text:"failed"}); + node.error(err); + } + node.status({}); + }); + } + }); + } + } + RED.nodes.registerType("twitter out",TwitterOutNode); + + var oa = new OAuth( + "https://api.twitter.com/oauth/request_token", + "https://api.twitter.com/oauth/access_token", + "OKjYEd1ef2bfFolV25G5nQ", + "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g", + "1.0", + null, + "HMAC-SHA1" + ); + + RED.httpAdmin.get('/twitter-credentials/:id/auth', function(req, res){ + var credentials = {}; + oa.getOAuthRequestToken({ + oauth_callback: req.query.callback + },function(error, oauth_token, oauth_token_secret, results){ + if (error) { + var resp = '

Oh no!

'+ + '

Something went wrong with the authentication process. The following error was returned:

'+ + '

'+error.statusCode+': '+error.data+'

'+ + '

One known cause of this type of failure is if the clock is wrong on system running Node-RED.'; + res.send(resp) + } else { + credentials.oauth_token = oauth_token; + credentials.oauth_token_secret = oauth_token_secret; + res.redirect('https://twitter.com/oauth/authorize?oauth_token='+oauth_token) + RED.nodes.addCredentials(req.params.id,credentials); + } + }); + }); + + RED.httpAdmin.get('/twitter-credentials/:id/auth/callback', function(req, res, next){ + var credentials = RED.nodes.getCredentials(req.params.id); + credentials.oauth_verifier = req.query.oauth_verifier; + + oa.getOAuthAccessToken( + credentials.oauth_token, + credentials.token_secret, + credentials.oauth_verifier, + function(error, oauth_access_token, oauth_access_token_secret, results){ + if (error){ + console.log(error); + res.send("yeah something broke."); + } else { + credentials = {}; + credentials.access_token = oauth_access_token; + credentials.access_token_secret = oauth_access_token_secret; + credentials.screen_name = "@"+results.screen_name; + RED.nodes.addCredentials(req.params.id,credentials); + res.send("Authorised - you can close this window and return to Node-RED"); + } + } + ); + }); +} diff --git a/dgbuilder/core_nodes/social/32-feedparse.html b/dgbuilder/core_nodes/social/32-feedparse.html new file mode 100644 index 00000000..a7954277 --- /dev/null +++ b/dgbuilder/core_nodes/social/32-feedparse.html @@ -0,0 +1,57 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/social/32-feedparse.js b/dgbuilder/core_nodes/social/32-feedparse.js new file mode 100644 index 00000000..97630e7d --- /dev/null +++ b/dgbuilder/core_nodes/social/32-feedparse.js @@ -0,0 +1,71 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var FeedParser = require("feedparser"); + var request = require("request"); + + function FeedParseNode(n) { + RED.nodes.createNode(this,n); + this.url = n.url; + this.interval = (parseInt(n.interval)||15)*60000; + var node = this; + this.interval_id = null; + this.seen = {}; + if (this.url !== "") { + var getFeed = function() { + request(node.url,function(err) { + if (err) node.error(err); + }) + .pipe(new FeedParser({feedurl:node.url})) + .on('error', function(error) { + node.error(error); + }) + .on('meta', function (meta) {}) + .on('readable', function () { + var stream = this, article; + while (article = stream.read()) { + if (!(article.guid in node.seen) || ( node.seen[article.guid] != 0 && node.seen[article.guid] != article.date.getTime())) { + node.seen[article.guid] = article.date?article.date.getTime():0; + var msg = { + topic:article.origlink||article.link, + payload: article.description, + article: article + }; + node.send(msg); + } + } + }) + .on('end', function () { + }); + }; + this.interval_id = setInterval(getFeed,node.interval); + getFeed(); + + } else { + this.error("Invalid url"); + } + } + + RED.nodes.registerType("feedparse",FeedParseNode); + + FeedParseNode.prototype.close = function() { + if (this.interval_id != null) { + clearInterval(this.interval_id); + } + } +} diff --git a/dgbuilder/core_nodes/social/61-email.html b/dgbuilder/core_nodes/social/61-email.html new file mode 100644 index 00000000..37397083 --- /dev/null +++ b/dgbuilder/core_nodes/social/61-email.html @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/social/61-email.js b/dgbuilder/core_nodes/social/61-email.js new file mode 100644 index 00000000..7d0f8cb9 --- /dev/null +++ b/dgbuilder/core_nodes/social/61-email.js @@ -0,0 +1,246 @@ +/** + * Copyright 2013,2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var nodemailer = require("nodemailer"); + var Imap = require('imap'); + + //console.log(nodemailer.Transport.transports.SMTP.wellKnownHosts); + + try { + var globalkeys = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js"); + } catch(err) { + } + + function EmailNode(n) { + RED.nodes.createNode(this,n); + this.topic = n.topic; + this.name = n.name; + this.outserver = n.server; + this.outport = n.port; + var flag = false; + if (this.credentials && this.credentials.hasOwnProperty("userid")) { + this.userid = this.credentials.userid; + } else { + if (globalkeys) { + this.userid = globalkeys.user; + flag = true; + } else { + this.error("No e-mail userid set"); + } + } + if (this.credentials && this.credentials.hasOwnProperty("password")) { + this.password = this.credentials.password; + } else { + if (globalkeys) { + this.password = globalkeys.pass; + flag = true; + } else { + this.error("No e-mail password set"); + } + } + if (flag) { + RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true}); + } + var node = this; + + var smtpTransport = nodemailer.createTransport({ + host: node.outserver, + port: node.outport, + secure: true, + auth: { + user: node.userid, + pass: node.password + } + }); + + this.on("input", function(msg) { + if (smtpTransport) { + node.status({fill:"blue",shape:"dot",text:"sending"}); + var payload = RED.util.ensureString(msg.payload); + smtpTransport.sendMail({ + from: node.userid, // sender address + to: msg.to || node.name, // comma separated list of addressees + subject: msg.topic, // subject line + text: payload // plaintext body + }, function(error, info) { + if (error) { + node.error(error); + node.status({fill:"red",shape:"ring",text:"send failed"}); + } else { + node.log("Message sent: " + info.response); + node.status({}); + } + }); + } + else { node.warn("No Email credentials found. See info panel."); } + }); + } + RED.nodes.registerType("e-mail",EmailNode,{ + credentials: { + userid: {type:"text"}, + password: {type: "password"}, + global: { type:"boolean"} + } + }); + + function EmailInNode(n) { + RED.nodes.createNode(this,n); + this.name = n.name; + this.repeat = n.repeat * 1000 || 300000; + this.inserver = n.server || globalkeys.server || "imap.gmail.com"; + this.inport = n.port || globalkeys.port || "993"; + var flag = false; + + if (this.credentials && this.credentials.hasOwnProperty("userid")) { + this.userid = this.credentials.userid; + } else { + if (globalkeys) { + this.userid = globalkeys.user; + flag = true; + } else { + this.error("No e-mail userid set"); + } + } + if (this.credentials && this.credentials.hasOwnProperty("password")) { + this.password = this.credentials.password; + } else { + if (globalkeys) { + this.password = globalkeys.pass; + flag = true; + } else { + this.error("No e-mail password set"); + } + } + if (flag) { + RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true}); + } + + var node = this; + this.interval_id = null; + var oldmail = {}; + + var imap = new Imap({ + user: node.userid, + password: node.password, + host: node.inserver, + port: node.inport, + tls: true, + tlsOptions: { rejectUnauthorized: false }, + connTimeout: node.repeat, + authTimeout: node.repeat + }); + + if (!isNaN(this.repeat) && this.repeat > 0) { + node.log("repeat = "+this.repeat); + this.interval_id = setInterval( function() { + node.emit("input",{}); + }, this.repeat ); + } + + this.on("input", function(msg) { + imap.once('ready', function() { + node.status({fill:"blue",shape:"dot",text:"fetching"}); + var pay = {}; + imap.openBox('INBOX', true, function(err, box) { + if (box.messages.total > 0) { + var f = imap.seq.fetch(box.messages.total + ':*', { bodies: ['HEADER.FIELDS (FROM SUBJECT DATE)','TEXT'] }); + f.on('message', function(msg, seqno) { + node.log('message: #'+ seqno); + var prefix = '(#' + seqno + ') '; + msg.on('body', function(stream, info) { + var buffer = ''; + stream.on('data', function(chunk) { + buffer += chunk.toString('utf8'); + }); + stream.on('end', function() { + if (info.which !== 'TEXT') { + pay.from = Imap.parseHeader(buffer).from[0]; + pay.topic = Imap.parseHeader(buffer).subject[0]; + pay.date = Imap.parseHeader(buffer).date[0]; + } else { + var parts = buffer.split("Content-Type"); + for (var p = 0; p < parts.length; p++) { + if (parts[p].indexOf("text/plain") >= 0) { + pay.payload = parts[p].split("\n").slice(1,-2).join("\n").trim(); + } + if (parts[p].indexOf("text/html") >= 0) { + pay.html = parts[p].split("\n").slice(1,-2).join("\n").trim(); + } + } + //pay.body = buffer; + } + }); + }); + msg.on('end', function() { + //node.log('Finished: '+prefix); + }); + }); + f.on('error', function(err) { + node.warn('fetch error: ' + err); + node.status({fill:"red",shape:"ring",text:"fetch error"}); + }); + f.on('end', function() { + if (JSON.stringify(pay) !== oldmail) { + node.send(pay); + oldmail = JSON.stringify(pay); + node.log('received new email: '+pay.topic); + } + else { node.log('duplicate not sent: '+pay.topic); } + //node.status({fill:"green",shape:"dot",text:"ok"}); + node.status({}); + imap.end(); + }); + } + else { + node.log("you have achieved inbox zero"); + //node.status({fill:"green",shape:"dot",text:"ok"}); + node.status({}); + imap.end(); + } + }); + }); + node.status({fill:"grey",shape:"dot",text:"connecting"}); + imap.connect(); + }); + + imap.on('error', function(err) { + node.log(err); + node.status({fill:"red",shape:"ring",text:"connect error"}); + }); + + this.on("error", function(err) { + node.log("error: ",err); + }); + + this.on("close", function() { + if (this.interval_id != null) { + clearInterval(this.interval_id); + } + if (imap) { imap.destroy(); } + }); + + node.emit("input",{}); + } + RED.nodes.registerType("e-mail in",EmailInNode,{ + credentials: { + userid: {type:"text"}, + password: {type: "password"}, + global: { type:"boolean"} + } + }); +} diff --git a/dgbuilder/core_nodes/social/91-irc.html b/dgbuilder/core_nodes/social/91-irc.html new file mode 100644 index 00000000..11112374 --- /dev/null +++ b/dgbuilder/core_nodes/social/91-irc.html @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/social/91-irc.js b/dgbuilder/core_nodes/social/91-irc.js new file mode 100644 index 00000000..c520e44f --- /dev/null +++ b/dgbuilder/core_nodes/social/91-irc.js @@ -0,0 +1,237 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var irc = require("irc"); + + // The Server Definition - this opens (and closes) the connection + function IRCServerNode(n) { + RED.nodes.createNode(this,n); + this.server = n.server; + this.channel = n.channel; + this.nickname = n.nickname; + this.lastseen = 0; + this.ircclient = null; + this.on("close", function() { + if (this.ircclient != null) { + this.ircclient.removeAllListeners(); + this.ircclient.disconnect(); + } + }); + } + RED.nodes.registerType("irc-server",IRCServerNode); + + + // The Input Node + function IrcInNode(n) { + RED.nodes.createNode(this,n); + this.ircserver = n.ircserver; + this.serverConfig = RED.nodes.getNode(this.ircserver); + this.channel = n.channel || this.serverConfig.channel; + var node = this; + if (node.serverConfig.ircclient === null) { + node.log("Connecting to "+node.serverConfig.server); + node.status({fill:"grey",shape:"dot",text:"connecting"}); + node.serverConfig.ircclient = new irc.Client(node.serverConfig.server, node.serverConfig.nickname,{autoConnect:false,retryDelay:20000}); + node.serverConfig.ircclient.setMaxListeners(0); + node.serverConfig.ircclient.addListener('error', function(message) { + node.log(JSON.stringify(message)); + }); + node.serverConfig.ircclient.addListener('netError', function(message) { + node.log(JSON.stringify("NET "+message)); + node.serverConfig.lastseen = Date.now(); + }); + node.serverConfig.ircclient.addListener('connect', function() { + node.serverConfig.lastseen = Date.now(); + }); + node.serverConfig.ircclient.addListener('ping', function(server) { + node.serverConfig.lastseen = Date.now(); + //node.log("PING "+JSON.stringify(server)); + }); + node.recon = setInterval( function() { + //console.log("CHK ",(Date.now()-node.serverConfig.lastseen)/1000); + if ((Date.now()-node.serverConfig.lastseen) > 300000) { // if more than 5 mins since last seen + node.ircclient.send.apply(node.ircclient,["TIME"]); // request time to check link + } + if ((Date.now()-node.serverConfig.lastseen) > 400000) { // If more than 6.5 mins + node.serverConfig.ircclient.disconnect(); + node.serverConfig.ircclient.connect(); + node.log("reconnect"); // then retry + } + node.ircclient.send.apply(node.ircclient,["TIME"]); // request time to check link + }, 60000); // check every 1 min + } + else { node.status({text:""}); } + node.ircclient = node.serverConfig.ircclient; + + node.ircclient.addListener('registered', function(message) { + //node.log(node.ircclient.nick+" ONLINE"); + node.status({fill:"yellow",shape:"dot",text:"connected"}); + node.ircclient.join( node.channel, function(data) { + // node.log(data+" JOINED "+node.channel); + node.status({fill:"green",shape:"dot",text:"joined"}); + }); + }); + node.ircclient.addListener('message', function (from, to, message) { + //node.log(from + ' => ' + to + ' : ' + message); + if (~node.channel.toLowerCase().indexOf(to.toLowerCase())) { + var msg = { "topic":from, "from":from, "to":to, "payload":message }; + node.send([msg,null]); + } + //else { console.log(node.channel,to); } + }); + node.ircclient.addListener('pm', function(from, message) { + //node.log("PM => "+from + ': ' + message); + var msg = { "topic":from, "from":from, "to":"PRIV", "payload":message }; + node.send([msg,null]); + }); + node.ircclient.addListener('join', function(channel, who) { + var msg = { "payload": { "type":"join", "who":who, "channel":channel } }; + node.send([null,msg]); + //node.log(who+' has joined '+channel); + }); + node.ircclient.addListener('invite', function(channel, from, message) { + var msg = { "payload": { "type":"invite", "who":from, "channel":channel, "message":message } }; + node.send([null,msg]); + //node.log(from+' sent invite to '+channel+': '+message); + }); + node.ircclient.addListener('part', function(channel, who, reason) { + var msg = { "payload": { "type":"part", "who":who, "channel":channel, "reason":reason } }; + node.send([null,msg]); + //node.log(who+' has left '+channel+': '+reason); + }); + node.ircclient.addListener('quit', function(nick, reason, channels, message) { + var msg = { "payload": { "type":"quit", "who":nick, "channel":channels, "reason":reason } }; + node.send([null,msg]); + //node.log(nick+' has quit '+channels+': '+reason); + }); + node.ircclient.addListener('kick', function(channel, who, by, reason) { + var msg = { "payload": { "type":"kick", "who":who, "channel":channel, "by":by, "reason":reason } }; + node.send([null,msg]); + //node.log(who+' was kicked from '+channel+' by '+by+': '+reason); + }); + node.ircclient.addListener('names', function (channel, nicks) { + var msg = { "payload": { "type": "names", "channel": channel, "names": nicks} }; + node.send([null, msg]); + }); + node.ircclient.addListener('raw', function (message) { // any message means we are alive + node.serverConfig.lastseen = Date.now(); + }); + node.on("close", function() { + node.ircclient.removeAllListeners(); + if (node.recon) { clearInterval(node.recon); } + }); + } + RED.nodes.registerType("irc in",IrcInNode); + + + // The Output Node + function IrcOutNode(n) { + RED.nodes.createNode(this,n); + this.sendFlag = n.sendObject; + this.ircserver = n.ircserver; + this.serverConfig = RED.nodes.getNode(this.ircserver); + this.channel = n.channel || this.serverConfig.channel; + var node = this; + if (node.serverConfig.ircclient === null) { + node.log("connecting to "+node.serverConfig.server); + node.status({fill:"grey",shape:"dot",text:"connecting"}); + node.serverConfig.ircclient = new irc.Client(node.serverConfig.server, node.serverConfig.nickname,{autoConnect:false,retryDelay:20000}); + node.serverConfig.ircclient.setMaxListeners(0); + node.serverConfig.ircclient.addListener('error', function(message) { + node.log(JSON.stringify(message)); + }); + node.serverConfig.ircclient.addListener('netError', function(message) { + node.log(JSON.stringify("NET "+message)); + node.serverConfig.lastseen = Date.now(); + }); + node.serverConfig.ircclient.addListener('connect', function() { + node.serverConfig.lastseen = Date.now(); + }); + node.serverConfig.ircclient.addListener('ping', function(server) { + node.serverConfig.lastseen = Date.now(); + //node.log("PING "+JSON.stringify(server)); + }); + node.serverConfig.ircclient.addListener('raw', function (message) { // any message received means we are alive + if (message.commandType === "reply") { node.serverConfig.lastseen = Date.now(); } + }); + node.recon = setInterval( function() { + //console.log("CHK ",(Date.now()-node.serverConfig.lastseen)/1000); + if ((Date.now()-node.serverConfig.lastseen) > 300000) { // if more than 5 mins since last seen + node.ircclient.send.apply(node.ircclient,["TIME"]); // request time to check link + } + if ((Date.now()-node.serverConfig.lastseen) > 400000) { // If more than 6.5 mins + node.serverConfig.ircclient.disconnect(); + node.serverConfig.ircclient.connect(); + node.log("reconnect"); // then retry + } + node.ircclient.send.apply(node.ircclient,["TIME"]); // request time to check link + }, 60000); // check every 1 min + node.serverConfig.ircclient.connect(); + } + else { node.status({text:""}); } + node.ircclient = node.serverConfig.ircclient; + + node.ircclient.addListener('registered', function(message) { + node.log(node.ircclient.nick+" ONLINE"); + node.status({fill:"yellow",shape:"dot",text:"connected"}); + node.ircclient.join( node.channel, function(data) { + //node.log(data+" JOINED "+node.channel); + node.status({fill:"green",shape:"dot",text:"joined"}); + }); + }); + + node.on("input", function(msg) { + if (Object.prototype.toString.call( msg.raw ) === '[object Array]') { + node.log("RAW command:"+msg.raw); + node.ircclient.send.apply(node.ircclient,msg.raw); + //var m = msg.raw; + //for (var i = 0; i < 10; i++) { + //if (typeof m[i] !== "string") { m[i] = ""; } + //m[i] = m[i].replace(/"/g, ""); + //} + //node.log("RAW command:"+m); + //node.ircclient.send(m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9]); + } + else { + if (msg._topic) { delete msg._topic; } + var ch = node.channel.split(","); // split on , so we can send to multiple + if (node.sendFlag == "true") { // override channels with msg.topic + if ((msg.hasOwnProperty('topic'))&&(typeof msg.topic === "string")) { + ch = msg.topic.split(","); // split on , so we can send to multiple + } + else { node.warn("msg.topic not set"); } + } + for (var c = 0; c < ch.length; c++) { + if (node.sendFlag == "false") { // send whole message object to each channel + node.ircclient.say(ch[c], JSON.stringify(msg)); + } + else { // send just the payload to each channel + if (typeof msg.payload === "object") { msg.payload = JSON.stringify(msg.payload); } + node.ircclient.say(ch[c], msg.payload); + } + } + } + }); + + node.on("close", function() { + node.ircclient.removeAllListeners(); + if (node.recon) { clearInterval(node.recon); } + }); + } + RED.nodes.registerType("irc out",IrcOutNode); +} diff --git a/dgbuilder/core_nodes/storage/28-tail.html b/dgbuilder/core_nodes/storage/28-tail.html new file mode 100644 index 00000000..c094d064 --- /dev/null +++ b/dgbuilder/core_nodes/storage/28-tail.html @@ -0,0 +1,58 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/storage/28-tail.js b/dgbuilder/core_nodes/storage/28-tail.js new file mode 100644 index 00000000..89c7a639 --- /dev/null +++ b/dgbuilder/core_nodes/storage/28-tail.js @@ -0,0 +1,69 @@ +/** + * Copyright 2013, 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var spawn = require('child_process').spawn; + var plat = require('os').platform(); + + if (plat.match(/^win/)) { + throw "Info : Currently not supported on Windows."; + } + + function TailNode(n) { + RED.nodes.createNode(this,n); + + this.filename = n.filename; + this.split = n.split; + var node = this; + + var err = ""; + // TODO: rewrite to use node-tail + var tail = spawn("tail", ["-F", "-n", "0", this.filename]); + tail.stdout.on("data", function (data) { + if (node.split) { + // TODO: allow customisation of the line break - as we do elsewhere + var strings = data.toString().split("\n"); + for (var s in strings) { + //TODO: should we really filter blanks? Is that expected? + if (strings[s] !== "") { + node.send({ + topic: node.filename, + payload: strings[s] + }); + } + } + } + else { + var msg = { + topic:node.filename, + payload: data.toString() + }; + node.send(msg); + } + }); + + tail.stderr.on("data", function(data) { + node.warn(data.toString()); + }); + + this.on("close", function() { + if (tail) { tail.kill(); } + }); + } + + RED.nodes.registerType("tail",TailNode); +} diff --git a/dgbuilder/core_nodes/storage/50-file.html b/dgbuilder/core_nodes/storage/50-file.html new file mode 100644 index 00000000..5113a17d --- /dev/null +++ b/dgbuilder/core_nodes/storage/50-file.html @@ -0,0 +1,110 @@ + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/storage/50-file.js b/dgbuilder/core_nodes/storage/50-file.js new file mode 100644 index 00000000..d6cc4410 --- /dev/null +++ b/dgbuilder/core_nodes/storage/50-file.js @@ -0,0 +1,93 @@ +/** + * Copyright 2013, 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var fs = require("fs"); + + function FileNode(n) { + RED.nodes.createNode(this,n); + this.filename = n.filename || ""; + this.appendNewline = n.appendNewline; + this.overwriteFile = n.overwriteFile; + var node = this; + this.on("input",function(msg) { + var filename = msg.filename || this.filename; + if (filename === "") { + node.warn('No filename specified'); + } else if (msg.hasOwnProperty('delete')) { + fs.unlink(filename, function (err) { + if (err) { node.warn('Failed to delete file : '+err); } + //console.log('Deleted file",filename); + }); + } else if (typeof msg.payload != "undefined") { + var data = msg.payload; + if (typeof data === "object") { + if (!Buffer.isBuffer(data)) { + data = JSON.stringify(data); + } + } + if (typeof data === "boolean") { data = data.toString(); } + if ((this.appendNewline)&&(!Buffer.isBuffer(data))) { data += "\n"; } + if (this.overwriteFile) { + // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while + fs.writeFile(filename, data, "binary", function (err) { + if (err) { node.warn('Failed to write to file : '+err); } + //console.log('Message written to file',filename); + }); + } + else { + // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while + fs.appendFile(filename, data, "binary", function (err) { + if (err) { node.warn('Failed to append to file : '+err); } + //console.log('Message appended to file',filename); + }); + } + } + }); + } + RED.nodes.registerType("file",FileNode); + + function FileInNode(n) { + RED.nodes.createNode(this,n); + + this.filename = n.filename || ""; + this.format = n.format; + var node = this; + var options = {}; + if (this.format) { + options['encoding'] = this.format; + } + this.on("input",function(msg) { + var filename = msg.filename || this.filename; + if (filename === "") { + node.warn('No filename specified'); + } else { + fs.readFile(filename,options,function(err,data) { + if (err) { + node.warn(err); + msg.error = err; + } else { + msg.filename = filename; + msg.payload = data; + } + node.send(msg); + }); + } + }); + } + RED.nodes.registerType("file in",FileInNode); +} diff --git a/dgbuilder/core_nodes/storage/65-redisout.html b/dgbuilder/core_nodes/storage/65-redisout.html new file mode 100644 index 00000000..9000dfd6 --- /dev/null +++ b/dgbuilder/core_nodes/storage/65-redisout.html @@ -0,0 +1,105 @@ + + + + + + + diff --git a/dgbuilder/core_nodes/storage/65-redisout.js b/dgbuilder/core_nodes/storage/65-redisout.js new file mode 100644 index 00000000..907e2a55 --- /dev/null +++ b/dgbuilder/core_nodes/storage/65-redisout.js @@ -0,0 +1,107 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var util = require("util"); + var redis = require("redis"); + + var hashFieldRE = /^([^=]+)=(.*)$/; + + var redisConnectionPool = function() { + var connections = {}; + var obj = { + get: function(host,port) { + var id = host+":"+port; + if (!connections[id]) { + connections[id] = redis.createClient(port,host); + connections[id].on("error",function(err) { + util.log("[redis] "+err); + }); + connections[id].on("connect",function() { + util.log("[redis] connected to "+host+":"+port); + }); + connections[id]._id = id; + connections[id]._nodeCount = 0; + } + connections[id]._nodeCount += 1; + return connections[id]; + }, + close: function(connection) { + connection._nodeCount -= 1; + if (connection._nodeCount === 0) { + if (connection) { + clearTimeout(connection.retry_timer); + connection.end(); + } + delete connections[connection._id]; + } + } + }; + return obj; + }(); + + + function RedisOutNode(n) { + RED.nodes.createNode(this,n); + this.port = n.port||"6379"; + this.hostname = n.hostname||"127.0.0.1"; + this.key = n.key; + this.structtype = n.structtype; + + this.client = redisConnectionPool.get(this.hostname,this.port); + + if (this.client.connected) { + this.status({fill:"green",shape:"dot",text:"connected"}); + } else { + this.status({fill:"red",shape:"ring",text:"disconnected"},true); + } + + var node = this; + this.client.on("end", function() { + node.status({fill:"red",shape:"ring",text:"disconnected"}); + }); + this.client.on("connect", function() { + node.status({fill:"green",shape:"dot",text:"connected"}); + }); + + this.on("input", function(msg) { + var k = this.key || msg.topic; + if (k) { + if (this.structtype == "string") { + this.client.set(k,RED.util.ensureString(msg.payload)); + } else if (this.structtype == "hash") { + var r = hashFieldRE.exec(msg.payload); + if (r) { + this.client.hset(k,r[1],r[2]); + } else { + this.warn("Invalid payload for redis hash"); + } + } else if (this.structtype == "set") { + this.client.sadd(k,msg.payload); + } else if (this.structtype == "list") { + this.client.rpush(k,msg.payload); + } + } else { + this.warn("No key or topic set"); + } + }); + this.on("close", function() { + redisConnectionPool.close(node.client); + }); + } + RED.nodes.registerType("redis out",RedisOutNode); +} diff --git a/dgbuilder/core_nodes/storage/66-mongodb.html b/dgbuilder/core_nodes/storage/66-mongodb.html new file mode 100644 index 00000000..81c56389 --- /dev/null +++ b/dgbuilder/core_nodes/storage/66-mongodb.html @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + diff --git a/dgbuilder/core_nodes/storage/66-mongodb.js b/dgbuilder/core_nodes/storage/66-mongodb.js new file mode 100644 index 00000000..3a71407c --- /dev/null +++ b/dgbuilder/core_nodes/storage/66-mongodb.js @@ -0,0 +1,233 @@ +/** + * Copyright 2013,2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + "use strict"; + var mongo = require('mongodb'); + var MongoClient = mongo.MongoClient; + + function MongoNode(n) { + RED.nodes.createNode(this,n); + this.hostname = n.hostname; + this.port = n.port; + this.db = n.db; + this.name = n.name; + + var url = "mongodb://"; + if (this.credentials && this.credentials.user && this.credentials.password) { + url += this.credentials.user+":"+this.credentials.password+"@"; + } + url += this.hostname+":"+this.port+"/"+this.db; + + this.url = url; + } + + RED.nodes.registerType("mongodb",MongoNode,{ + credentials: { + user: {type:"text"}, + password: {type: "password"} + } + }); + + function ensureValidSelectorObject(selector) { + if (selector != null && (typeof selector != 'object' || Buffer.isBuffer(selector))) { + return {}; + } + return selector; + } + + + function MongoOutNode(n) { + RED.nodes.createNode(this,n); + this.collection = n.collection; + this.mongodb = n.mongodb; + this.payonly = n.payonly || false; + this.upsert = n.upsert || false; + this.multi = n.multi || false; + this.operation = n.operation; + this.mongoConfig = RED.nodes.getNode(this.mongodb); + + if (this.mongoConfig) { + var node = this; + MongoClient.connect(this.mongoConfig.url, function(err, db) { + if (err) { + node.error(err); + } else { + node.clientDb = db; + var coll; + if (node.collection) { + coll = db.collection(node.collection); + } + node.on("input",function(msg) { + if (!coll) { + if (msg.collection) { + coll = db.collection(msg.collection); + } else { + node.error("No collection defined"); + return; + } + } + delete msg._topic; + delete msg.collection; + if (node.operation === "store") { + if (node.payonly) { + if (typeof msg.payload !== "object") { + msg.payload = {"payload": msg.payload}; + } + coll.save(msg.payload,function(err, item) { + if (err) { + node.error(err); + } + }); + } else { + coll.save(msg,function(err, item) { + if (err) { + node.error(err); + } + }); + } + } else if (node.operation === "insert") { + if (node.payonly) { + if (typeof msg.payload !== "object") { + msg.payload = {"payload": msg.payload}; + } + coll.insert(msg.payload, function(err, item) { + if (err) { + node.error(err); + } + }); + } else { + coll.insert(msg, function(err,item) { + if (err) { + node.error(err); + } + }); + } + } else if (node.operation === "update") { + if (typeof msg.payload !== "object") { + msg.payload = {"payload": msg.payload}; + } + var query = msg.query || {}; + var payload = msg.payload || {}; + var options = { + upsert: node.upsert, + multi: node.multi + }; + + coll.update(query, payload, options, function(err, item) { + if (err) { + node.error(err + " " + payload); + } + }); + } else if (node.operation === "delete") { + coll.remove(msg.payload, function(err, items) { + if (err) { + node.error(err); + } + }); + } + }); + } + }); + } else { + this.error("missing mongodb configuration"); + } + + this.on("close", function() { + if (this.clientDb) { + this.clientDb.close(); + } + }); + } + RED.nodes.registerType("mongodb out",MongoOutNode); + + function MongoInNode(n) { + RED.nodes.createNode(this,n); + this.collection = n.collection; + this.mongodb = n.mongodb; + this.operation = n.operation || "find"; + this.mongoConfig = RED.nodes.getNode(this.mongodb); + + if (this.mongoConfig) { + var node = this; + MongoClient.connect(this.mongoConfig.url, function(err,db) { + if (err) { + node.error(err); + } else { + node.clientDb = db; + var coll; + if (node.collection) { + coll = db.collection(node.collection); + } + node.on("input", function(msg) { + if (!coll) { + if (msg.collection) { + coll = db.collection(msg.collection); + } else { + node.error("No collection defined"); + return; + } + } + if (node.operation === "find") { + msg.projection = msg.projection || {}; + var selector = ensureValidSelectorObject(msg.payload); + coll.find(selector,msg.projection).sort(msg.sort).limit(msg.limit).toArray(function(err, items) { + if (err) { + node.error(err); + } else { + msg.payload = items; + delete msg.projection; + delete msg.sort; + delete msg.limit; + node.send(msg); + } + }); + } else if (node.operation === "count") { + var selector = ensureValidSelectorObject(msg.payload); + coll.count(selector, function(err, count) { + if (err) { + node.error(err); + } else { + msg.payload = count; + node.send(msg); + } + }); + } else if (node.operation === "aggregate") { + msg.payload = (msg.payload instanceof Array) ? msg.payload : []; + coll.aggregate(msg.payload, function(err, result) { + if (err) { + node.error(err); + } else { + msg.payload = result; + node.send(msg); + } + }); + } + }); + } + }); + } else { + this.error("missing mongodb configuration"); + } + + this.on("close", function() { + if (this.clientDb) { + this.clientDb.close(); + } + }); + } + RED.nodes.registerType("mongodb in",MongoInNode); +} -- cgit 1.2.3-korg