Move all nodes into core subdirectory

This makes it easier to distinguish core nodes from those added later
This commit is contained in:
Nicholas O'Leary
2013-11-14 15:52:19 +00:00
parent affcc8ae65
commit 48dabffefc
70 changed files with 0 additions and 0 deletions

View File

@@ -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.
-->
<script type="text/x-red" data-template-name="sentiment">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Adds <b>msg.sentiment.score</b> as the anaylsis result.
</div>
</script>
<script type="text/x-red" data-help-name="sentiment">
<p>Analyses the <b>msg.payload</b> and adds a <b>msg.sentiment</b> object that contains the resulting AFINN-111 sentiment score as <b>msg.sentiment.score</b>.</p>
<p>A score greater than zero is positive and less than zero is negative.</p>
<p>Score can range from -5 to +5.</p>
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_new">the Sentiment docs here</a>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('sentiment',{
category: 'analysis-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"sentiment";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,32 @@
/**
* 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 sentiment = require('sentiment');
function SentimentNode(n) {
RED.nodes.createNode(this,n);
this.on("input", function(msg) {
var node = this;
sentiment(msg.payload, function (err, result) {
msg.sentiment = result;
node.send(msg);
});
});
}
RED.nodes.registerType("sentiment",SentimentNode);

View File

@@ -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.
-->
<script type="text/x-red" data-template-name="xml2js">
<div class="form-row">
<label>Use Console</label>
<input type="checkbox" id="node-input-useEyes" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-useEyes" style="width: 70%;">Debug output in console ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Uses xml2js to process xml into javascript object.</div>
</script>
<script type="text/x-red" data-help-name="xml2js">
<p>A function that parses the <b>msg.payload</b> using the xml2js library. Places the result in the payload.</p>
<p>See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a> for more information.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('xml2js',{
category: 'advanced-function',
color:"#E6E0F8",
defaults: {
useEyes: {value:false},
name: {value:""},
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"xml2json";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,49 @@
/**
* 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 util = require("util");
var parseString = require('xml2js').parseString;
var gotEyes = false;
try {
var eyes = require("eyes");
gotEyes = true;
} catch(e) {
util.log("[73-parsexml.js] Note: Module 'eyes' not installed. (not needed, but useful)");
}
function Xml2jsNode(n) {
RED.nodes.createNode(this,n);
this.useEyes = n.useEyes;
var node = this;
this.on("input", function(msg) {
try {
parseString(msg.payload, function (err, result) {
if (err) { node.error(err); }
else {
msg.payload = result;
node.send(msg);
if (node.useEyes == true) {
if (gotEyes == true) { eyes.inspect(msg); }
else { node.log(JSON.stringify(msg)); }
}
}
});
}
catch(e) { util.log("[73-parsexml.js] "+e); }
});
}
RED.nodes.registerType("xml2js",Xml2jsNode);

View File

@@ -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.
-->
<script type="text/x-red" data-template-name="json2xml">
<div class="form-row">
<label for="node-input-name"><i class="icon-list"></i> XML Root</label>
<input type="text" id="node-input-root" placeholder="object"></input>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"></input>
</div>
</script>
<script type="text/x-red" data-help-name="json2xml">
<p>A function that parses the <b>msg.payload</b> using the js2xmlparser library. Places the result back in <b>msg.payload</b>.</p>
<p>See the <a href="https://github.com/michaelkourlas/node-js2xmlparser" target="_new">js2xmlparser docs</a> for more information.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('json2xml',{
category: 'advanced-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
root: {value:"object"},
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"json2xml";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,36 @@
/**
* 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 js2xmlparser = require("js2xmlparser");
function Js2XmlNode(n) {
RED.nodes.createNode(this,n);
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);

View File

@@ -0,0 +1,69 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="socket in">
<div class="form-row">
<label for="node-input-transport"><i class="icon-tasks"></i> Type</label>
<select type="text" id="node-input-transport" style="width: 150px;">
<option value="http">http listen</option>
<option value="tcp">tcp server</option>
<!-- <option value="tcpc">tcp client</option> -->
<option value="udp">udp socket</option>
</select>
</div>
<div class="form-row">
<label for="node-input-port"><i class="icon-random"></i> Port</label>
<input type="text" id="node-input-port" placeholder="Port">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: sends the received data as a Buffer object.</div>
</script>
<script type="text/x-red" data-help-name="socket in">
<p>Provides a input node for http, tcp or udp sockets. Topic is optional. These are server like sockets.</p>
<p>The TCP and UDP sockets produce a <i>BUFFER</i> object msg.payload and NOT a String. If you need a String then use .toString() on msg.payload in your next function block.</p>
<p>TCP and UDP sockets also provide <b>msg.fromip</b> of the form ipaddress:port</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('socket in',{
category: 'deprecated',
color:"Silver",
defaults: {
name: {value:""},
topic: {value:""},
port: {value:"",required:true},
transport: {value:"tcp",required:true}
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
return this.name||this.topic||("socket "+this.transport+":"+this.port);
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,152 @@
/**
* 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 SocketIn(n) {
RED.nodes.createNode(this,n);
this.warn("node type deprecated - will be removed in a future release");
this.port = n.port;
this.topic = n.topic;
this.trans = (n.transport||n.trans||"").toLowerCase();
var node = this;
if (this.trans == "http") {
var http = require('http');
var server = http.createServer(function (req, res) {
//node.log("http "+req.url);
var msg = {topic:node.topic,payload:req.url.slice(1)};
node.send(msg);
res.writeHead(304, {'Content-Type': 'text/plain'});
res.end('\n');
}).listen(node.port);
server.on('error', function (e) {
if (e.code == 'EADDRINUSE') {
setTimeout(node.error('TCP port is already in use - please reconfigure socket.'),250);
}
else { console.log(e); }
server = null;
});
node.log('http listener at http://127.0.0.1:'+node.port+'/');
this._close = function() {
if (server) server.close();
node.log('http listener stopped');
}
}
if (this.trans == "tcp") {
var net = require('net');
var server = net.createServer(function (socket) {
var buffer = null;
socket.on('data', function (chunk) {
if (buffer == null) {
buffer = chunk;
} else {
buffer = Buffer.concat([buffer,chunk]);
}
});
socket.on('end', function() {
var msg = {topic:node.topic, payload:buffer, fromip:socket.remoteAddress+':'+socket.remotePort};
node.send(msg);
});
});
server.on('error', function (e) {
if (e.code == 'EADDRINUSE') {
setTimeout(node.error('TCP port is already in use - please reconfigure socket.'),250);
}
else { console.log(e); }
server = null;
});
server.listen(node.port);
node.log('tcp listener on port :'+node.port);
this._close = function() {
if (server) server.close();
node.log('tcp listener stopped');
}
}
if (this.trans == "tcpc") {
var net = require('net');
var client;
var to;
function setupTcpClient() {
node.log('tcpc connecting to port :'+node.port);
client = net.connect({port: node.port}, function() {
node.log("tcpc connected");
});
client.on('data', function (data) {
var msg = {topic:node.topic, payload:data};
node.send(msg);
});
client.on('end', function() {
node.log("tcpc socket ended");
});
client.on('close', function() {
node.log('tcpc socket closed');
to = setTimeout(setupTcpClient, 10000); //Try to reconnect
});
client.on('error', function() {
node.log('tcpc socket error');
client = null;
to = setTimeout(setupTcpClient, 10000); //Try to reconnect
});
}
setupTcpClient();
this._close = function() {
if (client) client.end();
//client.destroy();
clearTimeout(to);
node.log('tcpc stopped client');
}
setupTcpClient();
}
if (this.trans == "udp") {
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
server.on('listening', function () {
var address = server.address();
node.log('udp listener at ' + address.address + ":" + address.port);
});
server.on('message', function (message, remote) {
var msg = {topic:node.topic,payload:message,fromip:remote.address+':'+remote.port};
node.send(msg);
});
server.on('error', function (e) {
console.log(e);
server = null;
});
server.bind(node.port);
this._close = function() {
if (server) server.close();
node.log('udp listener stopped');
}
}
}
RED.nodes.registerType("socket in",SocketIn);
SocketIn.prototype.close = function() {
this._close();
}

View File

@@ -0,0 +1,66 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="socket out">
<div class="form-row">
<label for="node-input-host"><i class="icon-bookmark"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;" >
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label for="node-input-transport"><i class="icon-tasks"></i> Type</label>
<select type="text" id="node-input-transport" style="width: 150px;">
<option value="http">http</option>
<option value="tcp">tcp</option>
<option value="udp">udp</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="socket out">
<p>Provides a choice of http, tcp or udp output connections. All connect, send their <b>msg.payload</b> and disconnect.</p>
<p>To use broadcast select udp and set the host to be either the subnet broadcast address required or 255.255.255.255</p>
<p>If you need a response from an http request use the httpget node instead.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('socket out',{
category: 'deprecated',
color:"Silver",
defaults: {
host: {value:"127.0.0.1",required:true},
port: {value:"",required:true},
name: {value:""},
transport: {value:"tcp",required:true}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
return this.name||this.topic||("socket "+this.transport+":"+this.port);
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,65 @@
/**
* 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 SocketOut(n) {
RED.nodes.createNode(this,n);
this.warn("node type deprecated - will be removed in a future release");
this.host = n.host;
this.port = n.port * 1;
this.name = n.name;
this.trans = n.transport||n.trans||"";
var node = this;
this.on("input", function(msg) {
if (msg != null) {
if (this.trans == "http") {
var http = require("http");
http.get(msg.payload, function(res) {
node.log("http : response : " + res.statusCode);
}).on('error', function(e) {
node.error("http : error : " + e.message);
});
}
if (this.trans == "tcp") {
var net = require('net');
var client = new net.Socket();
client.on('error', function (err) {
node.error('tcp : '+err);
});
client.connect(this.port, this.host, function() {
try { client.end(msg.payload); }
catch (e) { node.error(e); }
});
}
if (this.trans == "udp") {
var dgram = require('dgram');
var sock = dgram.createSocket('udp4'); // only use ipv4 for now
sock.bind(this.port); // have to bind before you can enable broadcast...
sock.setBroadcast(true); // turn on broadcast
var buf = new Buffer(msg.payload);
sock.send(buf, 0, buf.length, this.port, this.host, function(err, bytes) {
if (err) node.error("udp : "+err);
//util.log('[socket out] udp :' +bytes);
sock.close();
});
}
}
});
var node = this;
}
RED.nodes.registerType("socket out",SocketOut);

View File

@@ -0,0 +1,131 @@
<!--
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.
-->
<!-- The Input Node -->
<script type="text/x-red" data-template-name="multicast in">
<div class="form-row">
<label for="node-input-group"><i class="icon-tasks"></i> Group</label>
<input type="text" id="node-input-group" placeholder="225.0.18.83" style="width: 40%;">
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label for="node-input-iface"><i class="icon-globe"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="eth0">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Base64 encode payload ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: sends the received data as a Buffer object (not a String).<br/>Make sure your firewall will allow the data in.</div>
</script>
<script type="text/x-red" data-help-name="multicast in">
<p>A multicast udp input node, that produces a <b>msg.payload</b> containing a <i>BUFFER</i> object and NOT a String.</p>
<p>If you need a String then use <i>.toString()</i> on <b>msg.payload</b> in your next function block.</p>
<p>It also provides <b>msg.fromip</b> of the form ipaddress:port .</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('multicast in',{
category: 'deprecated',
color:"Silver",
defaults: {
name: {value:""},
group: {value:"",required:true},
host: {value:""},
iface: {value:""},
port: {value:"",required:true,validate:RED.validators.number()},
base64: {value:false,required:true},
multicast: {value:"true"}
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
if ((this.group!="") & (this.port!="")) {
return this.name||(this.group+":"+this.port);
}
else { return "multicast in"; }
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<!-- The Output Node -->
<script type="text/x-red" data-template-name="multicast out">
<div class="form-row">
<label for="node-input-group"><i class="icon-tasks"></i> Group</label>
<input type="text" id="node-input-group" placeholder="225.0.18.83" style="width: 40%;">
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label for="node-input-iface"><i class="icon-globe"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="eth0">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 encoded payload ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="multicast out">
<p>This node sends <b>msg.payload</b> to the designated multicast group and port.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('multicast out',{
category: 'deprecated',
color:"Silver",
defaults: {
name: {value:""},
group: {value:"",required:true},
host: {value:""},
iface: {value:""},
port: {value:"",required:true,validate:RED.validators.number()},
base64: {value:false,required:true},
multicast: {value:"true"}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
if ((this.group!="") & (this.port!="")) {
return this.name||(this.group+":"+this.port);
}
else { return "multicast out"; }
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,119 @@
/**
* 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 dgram = require('dgram');
// The Input Node
function MCastIn(n) {
RED.nodes.createNode(this,n);
this.warn("node type deprecated - will be removed in a future release");
this.group = n.group;
this.port = n.port;
this.host = n.host || null;
this.base64 = n.base64;
this.iface = n.iface || null;
this.multicast = n.multicast;
var node = this;
var server = dgram.createSocket('udp4');
server.on("error", function (err) {
console.log("udp listener error:\n" + err.stack);
server.close();
});
server.on('message', function (message, remote) {
var msg;
if (node.base64) { msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port }; }
else { msg = { payload:message, fromip:remote.address+':'+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) {
server.setBroadcast(true)
server.setMulticastTTL(128);
server.addMembership(node.group,node.iface);
node.log("udp multicast group "+node.group);
}
});
//server.bind(node.port,node.host);
server.bind(node.port,node.host);
this._close = function() {
server.close();
node.log('udp listener stopped');
}
}
MCastIn.prototype.close = function() {
this._close();
}
RED.nodes.registerType("multicast in",MCastIn);
// The Output Node
function MCastOut(n) {
RED.nodes.createNode(this,n);
this.warn("node type deprecated");
this.group = n.group;
this.port = n.port;
this.host = n.host || null;
this.base64 = n.base64;
this.iface = n.iface || null;
this.multicast = n.multicast;
var node = this;
var sock = dgram.createSocket('udp4'); // only use ipv4 for now
sock.bind(node.port); // have to bind before you can enable broadcast...
sock.setBroadcast(true); // turn on broadcast
sock.setMulticastTTL(128);
sock.addMembership(node.group,node.iface); // Add to the multicast group
node.log('udp multicaster ready on '+node.group+":"+node.port);
node.on("input", function(msg) {
if (msg.payload != null) {
console.log("MCast:",msg.payload);
var message;
if (node.base64) {
message = new Buffer(msg.payload,'base64');
}
else {
message = new Buffer(msg.payload);
}
sock.send(message, 0, message.length, node.port, node.group, function(err, bytes) {
if (err) node.error("udp : "+err);
//util.log('[socket out] udp :' +bytes);
});
}
});
this._close = function() {
sock.close();
node.log('udp multicaster stopped');
}
}
RED.nodes.registerType("multicast out",MCastOut);
MCastOut.prototype.close = function() {
this._close();
}

View File

@@ -0,0 +1,69 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="rpi-gpio in">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="7">7</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="18">18</option>
<option value="22">22</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-resistor"><i class=" icon-resize-full"></i> Resistor?</label>
<select type="text" id="node-input-resistor" style="width: 150px;">
<option value="no">no</option>
<option value="pullup">pullup</option>
<option value="pulldown">pulldown</option>
</select>
</div>
<div class="form-tips">Tip: if pull up/down resistor is selected, the <code>gpio-admin</code> command <em>must</em> be used
to do the actual enabling. If 'no' resistor is selected, nothing further needs to be done
to use this node. See <code>man gpio-admin</code> for more details.</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-gpio in',{
category: 'deprecated',
color:"#c6dbef",
defaults: {
name: { value:""},
resistor: { value: "no"},
pin: {value:"",required:true},
},
inputs:0,
outputs:1,
icon: "rpi.png",
label: function() {
return this.name||"Pin: "+this.pin;
//+(this.resistor == "no"?"":" ("+(this.resistor=="pullup"?"":"&darr;")+")");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,70 @@
/**
* 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 gpio = require("pi-gpio");
function GPIOInNode(n) {
RED.nodes.createNode(this,n);
this.warn("node type deprecated - will be removed in a future release");
this.buttonState = -1;
this.pin = n.pin;
this.resistor = n.resistor;
var node = this;
if (this.pin) {
var setupPin = function(err) {
if (err) {
node.error(err);
} else {
node._interval = setInterval(function(){
gpio.read(node.pin, function(err, value) {
if(err){
node.error(err);
} else{
if(node.buttonState !== value){
var previousState = node.buttonState;
node.buttonState = value;
if (previousState !== -1) {
var msg = {payload:node.buttonState};
node.send(msg);
}
}
}
});
}, 50);
}
};
if (this.resistor == "no") {
gpio.open(this.pin,"input",setupPin());
} else {
// Assume enabled externally via gpio-admin
setupPin();
}
} else {
this.error("Invalid GPIO pin: "+this.pin);
}
}
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
GPIOInNode.prototype.close = function() {
clearInterval(this._interval);
if (this.resistor == "no") {
gpio.close(this.pin);
}
}

View File

@@ -0,0 +1,59 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="rpi-gpio out">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="7">7</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="18">18</option>
<option value="22">22</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-gpio out',{
category: 'deprecated',
color:"#c6dbef",
defaults: {
name: { value:""},
resistor: { value: "no"},
pin: {value:"",required:true},
},
inputs:1,
outputs:0,
icon: "rpi.png",
align: "right",
label: function() {
return this.name||"Pin: "+this.pin;
//+(this.resistor == "no"?"":" ("+(this.resistor=="pullup"?"":"&darr;")+")");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -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.
**/
var RED = require(process.env.NODE_RED_HOME+"/red/red");
var gpio = require("pi-gpio");
function GPIOOutNode(n) {
RED.nodes.createNode(this,n);
this.warn("node type deprecated - will be removed in a future release");
this.pin = n.pin;
var node = this;
if (this.pin) {
gpio.open(this.pin,"output",function(err) {
if (err) {
node.error(err);
} else {
node.on("input",function(msg) {
gpio.write(node.pin,msg.payload,function(err) {
if (err) node.error(err);
});
});
}
});
} else {
this.error("Invalid GPIO pin: "+this.pin);
}
}
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
GPIOOutNode.prototype.close = function() {
gpio.close(this.pin);
}

View File

@@ -0,0 +1,150 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="arduino in">
<div class="form-row">
<label for="node-input-arduino"><i class="icon-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<input type="text" id="node-input-pin" placeholder="2">
</div>
<div class="form-row">
<label for="node-input-state"><i class="icon-wrench"></i> Type</label>
<select type="text" id="node-input-state" style="width: 150px;">
<option value="INPUT">Digital pin</option>
<option value="ANALOG">Analogue pin</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips"><b>Note:</b> You cannot use the same pin for both output and input.</div>
</script>
<script type="text/x-red" data-help-name="arduino in">
<p>Arduino input node. Connects to local Arduino and monitors the selected pin for changes. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>You can select either Digital or Analogue input. Outputs the value read as <b>msg.payload</b> and the pin number as <b>msg.topic</b>.</p>
<p>It only outputs on a change of value - fine for digital inputs, but you can get a lot of data from analogue pins which you must then handle.</p>
<p>You can set the sample rate in ms from 20 to 65535.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino in',{
category: 'advanced-input',
color:"#3fadb5",
defaults: {
name: {value:""},
pin: {value:"",required:true},
state: {value:"INPUT",required:true},
arduino: {type:"arduino-board",required:true}
},
inputs:0,
outputs:1,
icon: "arduino.png",
label: function() {
var a = "";
if (this.state == "ANALOG") a = "A";
return this.name||"Pin: "+a+this.pin;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="arduino out">
<div class="form-row">
<label for="node-input-arduino"><i class="icon-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<input type="text" id="node-input-pin" placeholder="13">
</div>
<div class="form-row">
<label for="node-input-state"><i class="icon-wrench"></i> Type</label>
<select type="text" id="node-input-state" style="width: 150px;">
<option value="OUTPUT">Digital (0/1)</option>
<option value="PWM">Analogue (0-255)</option>
<option value="SERVO">Servo (0-180)</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips"><b>Note:</b> You cannot use the same pin for both output and input.</div>
</script>
<script type="text/x-red" data-help-name="arduino out">
<p>Arduino output node. Connects to local Arduino and writes to the selected digital pin. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>You can select Digital, Analogue (PWM) or Servo type outputs. Expects a numeric value in <b>msg.payload</b>. The pin number is set in the properties panel.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino out',{
category: 'advanced-output',
color:"#3fadb5",
defaults: {
name: {value:""},
pin: {value:""},
state: {value:"",required:true},
arduino: {type:"arduino-board",required:true}
},
inputs:1,
outputs:0,
icon: "arduino.png",
align: "right",
label: function() {
return this.name||"Pin: "+this.pin;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="arduino-board">
<div class="form-row">
<label for="node-config-input-device"><i class="icon-bullhorn"></i> Arduino Port</label>
<input type="text" id="node-config-input-device" placeholder="/dev/ttyUSB0" style="width:50%;">
</div>
<div class="form-row">
<label for="node-config-input-repeat"><i class="icon-repeat"></i> Sample (ms)</label>
<input type="text" id="node-config-input-repeat" placeholder="25">
</div>
<!-- <div class="form-row">
<label for="node-config-input-baud"><i class="icon-bullhorn"></i> Baudrate</label>
<input type="text" id="node-config-input-baud" placeholder="115200" style="width:50%;">
</div> -->
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino-board',{
category: 'config',
defaults: {
//baud: {baud:"57600",required:true},
repeat: {value:"50",required:true,validate:RED.validators.number()},
device: {value:"",required:true}
},
label: function() {
return this.device||"arduino";
}
});
</script>

View File

@@ -0,0 +1,187 @@
/**
* 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 util = require("util");
var firmata = require("firmata");
var arduinoReady = false;
var thisboard = null;
// The Board Definition - this opens (and closes) the connection
function ArduinoNode(n) {
RED.nodes.createNode(this,n);
this.device = n.device;
this.repeat = n.repeat||25;
util.log("[firmata] Opening "+this.device);
var node = this;
node.toun = setInterval(function() {
if (!arduinoReady) {
if (thisboard == null) {
node.board = new firmata.Board(node.device, function(err) {
if (err) {
util.log("[firmata] error: "+err);
return;
}
arduinoReady = true;
thisboard = node.board;
clearInterval(node.toun);
util.log('[firmata] Arduino connected');
});
}
else {
node.board = thisboard;
node.board.removeAllListeners();
arduinoReady = true;
clearInterval(node.toun);
node.toun = false;
util.log("[firmata] Arduino already connected");
}
} else { util.log("[firmata] Waiting for Firmata"); }
}, 10000); // wait for firmata to connect to arduino
this.on('close', function() {
//this.board.sp.close(function() { console.log("[firmata] Serial port closed"); arduinoReady = false; });
arduinoReady = false;
if (node.toun) {
clearInterval(node.toun);
util.log("[firmata] arduino wait loop stopped");
}
util.log("[firmata] Stopped");
});
}
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.toui = setInterval(function() {
if (thisboard != null) {
node.board = thisboard;
clearInterval(node.toui);
node.toui = false;
//console.log("i",node.state,node.pin,node.board.MODES[node.state]);
node.board.pinMode(node.pin, node.board.MODES[node.state]);
node.board.setSamplingInterval(node.repeat);
var oldrdg = "";
if (node.state == "ANALOG") {
node.board.analogRead(node.pin, function(data) {
var msg = {payload:data, topic:"A"+node.pin};
if (data != oldrdg) {
node.send(msg);
oldrdg = data;
}
});
}
else {
node.board.digitalRead(node.pin, function(data) {
var msg = {payload:data, topic:node.pin};
node.send(msg);
});
}
}
else { node.log("Waiting for Arduino"); }
}, 5000); // loop to wait for firmata to connect to arduino
this.on('close', function() {
if (node.toui) {
clearInterval(node.toui);
util.log("[firmata] input wait loop stopped");
}
});
}
else {
util.log("[firmata] Serial 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;
this.on("input", function(msg) {
//console.log(msg);
if (node.board != null) {
if (node.state == "OUTPUT") {
if ((msg.payload == true)||(msg.payload == 1)||(msg.payload.toString().toLowerCase() == "on")) {
node.board.digitalWrite(node.pin, node.board.HIGH);
}
if ((msg.payload == false)||(msg.payload == 0)||(msg.payload.toString().toLowerCase() == "off")) {
node.board.digitalWrite(node.pin, node.board.LOW);
}
}
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 { console.log("Arduino not ready"); }
});
node.touo = setInterval(function() {
if (thisboard != null) {
clearInterval(node.touo);
node.touo = false;
node.board = thisboard;
//console.log("o",node.state,node.pin,node.board.MODES[node.state]);
node.board.pinMode(node.pin, node.board.MODES[node.state]);
}
else { util.log("[firmata] waiting for arduino to connect"); }
}, 5000); // loop to wait for firmata to connect to arduino
this.on('close', function() {
if (node.touo) {
clearInterval(node.touo);
util.log("[firmata] output wait loop stopped");
}
});
}
else {
util.log("[firmata] Serial Port not Configured");
}
}
RED.nodes.registerType("arduino out",DuinoNodeOut);

View File

@@ -0,0 +1,126 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="rpi-gpio in">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> GPIO Pin</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="-">select pin</option>
<option value="7">7</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="18">18</option>
<option value="22">22</option>
</select>
</div>
<div class="form-row">
<label for="node-input-intype"><i class=" icon-resize-full"></i> Resistor?</label>
<select type="text" id="node-input-intype" style="width: 150px;">
<option value="tri">none</option>
<option value="up">pullup</option>
<option value="down">pulldown</option>
<!--<option value="tri">tristate</option>-->
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: Only Digital I/O is supported - input must be 0 or 1.</div>
</script>
<script type="text/x-red" data-help-name="rpi-gpio in">
<p>Raspberry Pi input node. Generates a <b>msg.payload</b> with either a 0 or 1 depending on the state of the input pin. Requires the gpio command to work.</p>
<p>You may also enable the input pullup resitor or the pulldown resistor.</p>
<p>The <b>msg.topic</b> is set to <i>pi/{the pin number}</i></p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
<p><b>Note:</b> This node currently polls the pin every 250mS. This is not ideal as it loads the cpu, and will be rewritten shortly to try to use interrupts.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-gpio in',{
category: 'advanced-input',
color:"#c6dbef",
defaults: {
name: { value:"" },
intype: { value: "in" },
pin: { value:"",required:true,validate:RED.validators.number() },
},
inputs:0,
outputs:1,
icon: "rpi.png",
label: function() {
return this.name||"Pin: "+this.pin ;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="rpi-gpio out">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> GPIO Pin</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="-">select pin</option>
<option value="7">7</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="18">18</option>
<option value="22">22</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: Only Digital I/O is supported - input must be 0 or 1.</div>
</script>
<script type="text/x-red" data-help-name="rpi-gpio out">
<p>Raspberry Pi output node. Expects a <b>msg.payload</b> with either a 0 or 1 (or true or false). Requires the gpio command to work.</p>
<p>Will set the selected physical pin high or low depending on the value passed in.</p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-gpio out',{
category: 'advanced-output',
color:"#c6dbef",
defaults: {
name: { value:"" },
pin: { value:"",required:true,validate:RED.validators.number() },
},
inputs:1,
outputs:0,
icon: "rpi.png",
align: "right",
label: function() {
return this.name||"Pin: "+this.pin;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,143 @@
/**
* 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 util = require("util");
var exec = require('child_process').exec;
var fs = require('fs');
if (!fs.existsSync("/usr/local/bin/gpio")) {
exec("cat /proc/cpuinfo | grep BCM27",function(err,stdout,stderr) {
if (stdout.indexOf('BCM27') > -1) {
util.log('[36-rpi-gpio.js] Error: Cannot find Wiring-Pi "gpio" command. http://wiringpi.com/download-and-install/');
}
// else not on a Pi so don't worry anyone with needless messages.
});
return;
}
// Map physical P1 pins to Gordon's Wiring-Pi Pins (as they should be V1/V2 tolerant)
var pintable = {
// Physical : WiringPi
"7":"7",
"11":"0",
"12":"1",
"13":"2",
"15":"3",
"16":"4",
"18":"5",
"22":"6"
}
var tablepin = {
// WiringPi : Physical
"7":"7",
"0":"11",
"1":"12",
"2":"13",
"3":"15",
"4":"16",
"5":"18",
"6":"22"
}
function GPIOInNode(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = pintable[n.pin];
this.intype = n.intype;
var node = this;
if (this.pin) {
exec("gpio mode "+node.pin+" "+node.intype, function(err,stdout,stderr) {
if (err) node.error(err);
else {
node._interval = setInterval( function() {
exec("gpio 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 {
this.error("Invalid GPIO pin: "+this.pin);
}
}
function GPIOOutNode(n) {
RED.nodes.createNode(this,n);
this.pin = pintable[n.pin];
var node = this;
if (this.pin) {
process.nextTick(function() {
exec("gpio 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("gpio write "+node.pin+" "+out, function(err,stdout,stderr) {
if (err) node.error(err);
});
}
else node.warn("Invalid input - not 0 or 1");
});
}
});
});
}
else {
this.error("Invalid GPIO pin: "+this.pin);
}
}
exec("gpio mode 0 in",function(err,stdout,stderr) {
if (err) {
util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.');
}
exec("gpio mode 1 in");
exec("gpio mode 2 in");
exec("gpio mode 3 in");
exec("gpio mode 4 in");
exec("gpio mode 5 in");
exec("gpio mode 6 in");
exec("gpio mode 7 in",function(err,stdout,stderr) {
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
GPIOInNode.prototype.close = function() {
clearInterval(this._interval);
}
GPIOOutNode.prototype.close = function() {
exec("gpio mode "+this.pin+" in");
}
});
});

121
nodes/core/io/10-mqtt.html Normal file
View File

@@ -0,0 +1,121 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="mqtt in">
<div class="form-row">
<label for="node-input-broker"><i class="icon-tag"></i> Broker</label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mqtt in">
<p>MQTT input node. Connects to the specified broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p>
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>. <b>msg.payload</b> is a String.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt in',{
category: 'input',
defaults: {
name: {value:""},
topic: {value:"",required:true},
broker: {type:"mqtt-broker", required:true}
},
color:"#c6dbef",
inputs:0,
outputs:1,
icon: "bridge.png",
label: function() {
return this.name||this.topic||"mqtt";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="mqtt out">
<div class="form-row">
<label for="node-input-broker"><i class="icon-tag"></i> Broker</label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mqtt out">
<p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> OR to the topic specified in the edit window. The value in the edit window has precedence.</p>
<p><b>msg.qos</b> and <b>msg.retain</b> may also optionally have been set. If not set they are set to 0 and false respectively.</p>
<p>If <b>msg.payload</b> contains a buffer or an object it will be stringified before being sent.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt out',{
category: 'output',
defaults: {
name: {value:""},
topic: {value:""},
broker: {type:"mqtt-broker", required:true}
},
color:"#c6dbef",
inputs:1,
outputs:0,
icon: "bridge.png",
align: "right",
label: function() {
return this.name||this.topic||"mqtt";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="mqtt-broker">
<div class="form-row node-input-broker">
<label for="node-config-input-broker"><i class="icon-bookmark"></i> Broker</label>
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="Broker" style="width: 40%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="Port" style="width:45px">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt-broker',{
category: 'config',
defaults: {
broker: {value:"localhost",required:true},
port: {value:1883,required:true,validate:RED.validators.number()}
},
label: function() {
return this.broker+":"+this.port;
}
});
</script>

90
nodes/core/io/10-mqtt.js Normal file
View File

@@ -0,0 +1,90 @@
/**
* 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 connectionPool = require("./lib/mqttConnectionPool");
var util = require("util");
function MQTTBrokerNode(n) {
RED.nodes.createNode(this,n);
this.broker = n.broker;
this.port = n.port;
}
RED.nodes.registerType("mqtt-broker",MQTTBrokerNode);
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.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port);
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.connect();
} else {
this.error("missing broker configuration");
}
}
RED.nodes.registerType("mqtt in",MQTTInNode);
MQTTInNode.prototype.close = function() {
if (this.client) {
this.client.disconnect();
}
}
function MQTTOutNode(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.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port);
this.on("input",function(msg) {
if (msg != null) {
if (this.topic) {
msg.topic = this.topic;
}
this.client.publish(msg);
}
});
this.client.connect();
} else {
this.error("missing broker configuration");
}
}
RED.nodes.registerType("mqtt out",MQTTOutNode);
MQTTOutNode.prototype.close = function() {
if (this.client) {
this.client.disconnect();
}
}

View File

@@ -0,0 +1,195 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="http in">
<div class="form-row">
<label for="node-input-method"><i class="icon-tasks"></i> Method</label>
<select type="text" id="node-input-method" style="width: 150px;">
<option value="get">GET</option>
<option value="post">POST</option>
<option value="put">PUT</option>
<option value="delete">DELETE</option>
</select>
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-globe"></i> url</label>
<input type="text" id="node-input-url" placeholder="/url">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="http in">
<p>Provides an input node for http requests, allowing the creation of simple web services.</p>
<p>The resulting message has the following properties:
<ul>
<li>msg.req : <a href="http://expressjs.com/api.html#req">http request</a></li>
<li>msg.res : <a href="http://expressjs.com/api.html#res">http response</a></li>
</ul>
</p>
<p>For POST/PUT requests, the body is available under <code>msg.req.body</code>. This
uses the <a href="http://expressjs.com/api.html#bodyParser">Express bodyParser middleware</a> to parse the content to a JSON object.
</p>
<p>
By default, this expects the body of the request to be url encoded:
<pre>foo=bar&amp;this=that</pre>
</p>
<p>
To send JSON encoded data to the node, the content-type header of the request must be set to
<code>application/json</code>.
</p>
<p>
<b>Note: </b>This node does not send any response to the http request. This should be done with
a subsequent HTTP Response node, or Function node.
In the case of a Function node, the <a href="http://expressjs.com/api.html#res">Express response documentation</a>
describes how this should be done. For example:
<pre>msg.res.send(200, 'Thanks for the request ');<br/>return msg;</pre>
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('http in',{
category: 'input',
color:"rgb(231, 231, 174)",
defaults: {
name: {value:""},
url: {value:"",required:true},
method: {value:"get",required:true}
},
inputs:0,
outputs:1,
icon: "white-globe.png",
label: function() {
return this.name||(this.url?("["+this.method+"] "+this.url):"http");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="http response">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The messages sent to this node <b>must</b> originate from an <i>http input</i> node</div>
</script>
<script type="text/x-red" data-help-name="http response">
<p>Sends responses back to http requests received from an HTTP Input node.</p>
<p>The response can be customised using the following message properties:</p>
<ul>
<li><code>payload</code> is sent as the body of the reponse</li>
<li><code>statusCode</code>, if set, is used as the response status code (default: 200)</li>
<li><code>headers</code>, if set, should be an object containing field/value
pairs to be added as response headers.</li>
</ul>
</script>
<script type="text/javascript">
RED.nodes.registerType('http response',{
category: 'output',
color:"rgb(231, 231, 174)",
defaults: {
name: {value:""}
},
inputs:1,
outputs:0,
align: "right",
icon: "white-globe.png",
label: function() {
return this.name||"http";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="http request">
<div class="form-row">
<label for="node-input-method"><i class="icon-tasks"></i> Method</label>
<select type="text" id="node-input-method" style="width: 150px;">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="form-row">
<label for="node-input-url"><i class="icon-tasks"></i> URL</label>
<input type="text" id="node-input-url" placeholder="http://">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="http request">
<p>Provides a node for making http requests.</p>
<p>The URL and HTTP method can be configured in the node, but also
overridden by the incoming message:
<ul>
<li><code>url</code>, if set, is used as the url of the request</li>
<li><code>method</code>, if set, is used as the HTTP method of the request. Must be one of <code>GET</code>, <code>PUT</code>, <code>POST</code> or <code>DELETE</code> (default: GET)</li>
<li><code>headers</code>, if set, should be an object containing field/value
pairs to be added as request headers</li>
<li><code>payload</code> is sent as the body of the request</li>
</ul>
The output message contains the following properties:
<ul>
<li><code>payload</code> is the body of the response</li>
<li><code>statusCode</code> is the status code of the response, or the error code if the request could not be completed</li>
<li><code>headers</code> is an object containing the response headers</li>
</ul>
</script>
<script type="text/javascript">
RED.nodes.registerType('http request',{
category: 'function',
color:"rgb(231, 231, 174)",
defaults: {
name: {value:""},
method:{value:"GET"},
url:{value:""}
},
inputs:1,
outputs:1,
align: "right",
icon: "white-globe.png",
label: function() {
return this.name||"http request";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

124
nodes/core/io/21-httpin.js Normal file
View File

@@ -0,0 +1,124 @@
/**
* 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 util = require("util");
var http = require("http");
var https = require("https");
var urllib = require("url");
var express = require("express");
var jsonParser = express.json();
var urlencParser = express.urlencoded();
function HTTPIn(n) {
RED.nodes.createNode(this,n);
this.url = n.url;
this.method = n.method;
var node = this;
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});
}
if (this.method == "get") {
RED.app.get(this.url,this.callback);
} else if (this.method == "post") {
RED.app.post(this.url,jsonParser,urlencParser,this.callback);
} else if (this.method == "put") {
RED.app.put(this.url,jsonParser,urlencParser,this.callback);
} else if (this.method == "delete") {
RED.app.delete(this.url,this.callback);
}
this.on("close",function() {
var routes = RED.app.routes[this.method];
for (var i in routes) {
if (routes[i].path == this.url) {
routes.splice(i,1);
//break;
}
}
});
}
RED.nodes.registerType("http in",HTTPIn);
function HTTPOut(n) {
RED.nodes.createNode(this,n);
var node = this;
this.on("input",function(msg) {
if (msg.res) {
if (msg.headers) {
msg.res.set(msg.headers);
}
var statusCode = msg.statusCode || 200;
msg.res.send(statusCode,msg.payload);
} else {
node.warn("No response object");
}
});
}
RED.nodes.registerType("http response",HTTPOut);
function HTTPRequest(n) {
RED.nodes.createNode(this,n);
var url = n.url;
var method = n.method || "GET";
var httplib = (/^https/.test(url))?https:http;
var node = this;
this.on("input",function(msg) {
var opts = urllib.parse(msg.url||url);
opts.method = (msg.method||method).toUpperCase();
if (msg.headers) {
opts.headers = msg.headers;
}
var req = httplib.request(opts,function(res) {
res.setEncoding('utf8');
msg.statusCode = res.statusCode;
msg.headers = res.headers;
msg.payload = "";
res.on('data',function(chunk) {
msg.payload += chunk;
});
res.on('end',function() {
node.send(msg);
});
});
req.on('error',function(err) {
msg.payload = err.toString();
msg.statusCode = err.code;
node.send(msg);
});
if (msg.payload && (method == "POST" || method == "PUT") ) {
if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
req.write(msg.payload);
} else if (typeof msg.payload == "number") {
req.write(msg.payload+"");
} else {
req.write(JSON.stringify(msg.payload));
}
}
req.end();
});
}
RED.nodes.registerType("http request",HTTPRequest);

View File

@@ -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.
-->
<script type="text/x-red" data-template-name="watch">
<div class="form-row node-input-filename">
<label for="node-input-files"><i class="icon-file"></i> File(s)</label>
<input type="text" id="node-input-files" placeholder="File(s) or Directory">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="watch">
<p>Watches a file or directory for any changes.</p>
<p>You can enter a list of comma separated files, or directories if you like. You will need to put " around any that have spaces in.</p>
<p>The filename of the file that actually changed is put into <b>msg.payload</b>, while a stringified version of the watched criteria is returned in <b>msg.topic</b>.</p>
<p>Of course in Linux, <i>everything</i> could be a file and thus watched...</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('watch',{
category: 'advanced-input',
defaults: {
name: {value:""},
files: {value:"",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "watch.png",
label: function() {
return this.name||this.files;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

45
nodes/core/io/23-watch.js Normal file
View File

@@ -0,0 +1,45 @@
/**
* 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 notify = require("fs.notify");
function WatchNode(n) {
RED.nodes.createNode(this,n);
this.files = n.files.split(",");
for (var f in this.files) {
this.files[f] = this.files[f].trim();
}
var node = this;
var notifications = new notify(this.files);
notifications.on('change', function (file) {
node.log('file changed '+file);
var msg = { payload: file, topic: JSON.stringify(node.files) };
node.send(msg);
});
this._close = function() {
notifications.close();
}
}
RED.nodes.registerType("watch",WatchNode);
WatchNode.prototype.close = function() {
this._close();
}

View File

@@ -0,0 +1,148 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="serial in">
<div class="form-row node-input-serial">
<label for="node-input-serial"><i class="icon-bullhorn"></i> Serial Port</label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="serial in">
<p>Reads data from a local serial port.</p>
<p>Keeps reading from the serial port until it sees \n (default) or the character(s) requested. Only sets <b>msg.payload</b>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial in',{
category: 'input',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "serial.png",
label: function() {
var serialNode = RED.nodes.node(this.serial);
return this.name||(serialNode?serialNode.label():"serial");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="serial out">
<div class="form-row node-input-serial">
<label for="node-input-serial"><i class="icon-bullhorn"></i> Serial Port</label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="serial out">
<p>Provides a connection to an outbound serial port.</p>
<p>Only the <b>msg.payload</b> is sent.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial out',{
category: 'output',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:1,
outputs:0,
icon: "serial.png",
align: "right",
label: function() {
var serialNode = RED.nodes.node(this.serial);
return this.name||(serialNode?serialNode.label():"serial");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="serial-port">
<div class="form-row">
<label for="node-config-input-serialport"><i class="icon-bullhorn"></i> Serial Port</label>
<input type="text" id="node-config-input-serialport" placeholder="/dev/ttyUSB0" style="width:50%;">
</div>
<div class="form-row">
<label for="node-config-input-serialbaud"><i class="icon-wrench"></i> Baud Rate</label>
<select type="text" id="node-config-input-serialbaud" style="width: 150px;">
<option value="115200">115200</option>
<option value="57600">57600</option>
<option value="38400">38400</option>
<option value="19200">19200</option>
<option value="9600">9600</option>
<option value="4800">4800</option>
<option value="2400">2400</option>
<option value="1800">1800</option>
<option value="1200">1200</option>
<option value="600">600</option>
<option value="300">300</option>
<option value="200">200</option>
<option value="150">150</option>
<option value="134">134</option>
<option value="110">110</option>
<option value="75">75</option>
<option value="50">50</option>
</select>
</div>
<div class="form-row">
<label for="node-config-input-newline"><i class="icon-text-width"></i> New line</label>
<input type="text" id="node-config-input-newline">
</div>
<!--
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name">
</div>
-->
</script>
<script type="text/javascript">
RED.nodes.registerType('serial-port',{
category: 'config',
defaults: {
//name: {value:""},
serialport: {value:"",required:true},
serialbaud: {value:57600,required:true},
newline: {value:"\\n"}
},
label: function() {
//return this.name||this.serialport;
return this.serialport+":"+this.serialbaud;
}
});
</script>

195
nodes/core/io/25-serial.js Normal file
View File

@@ -0,0 +1,195 @@
/**
* 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 settings = RED.settings;
var events = require("events");
var util = require("util");
var serialp = require("serialport");
// TODO: 'serialPool' should be encapsulated in SerialPortNode
function SerialPortNode(n) {
RED.nodes.createNode(this,n);
this.serialport = n.serialport;
this.serialbaud = n.serialbaud * 1;
this.newline = n.newline;
}
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;
try {
node.port = serialPool.get(this.serialConfig.serialport,this.serialConfig.serialbaud,this.serialConfig.newline);
} catch(err) {
this.error(err);
return;
}
node.on("input",function(msg) {
//console.log("{",msg,"}");
node.port.write(msg.payload,function(err,res) {
if (err) {
node.error(err);
}
});
});
} else {
this.error("missing serial config");
}
}
RED.nodes.registerType("serial out",SerialOutNode);
SerialOutNode.prototype.close = function() {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport);
}
}
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;
try {
this.port = serialPool.get(this.serialConfig.serialport,this.serialConfig.serialbaud,this.serialConfig.newline);
} catch(err) {
this.error(err);
return;
}
this.port.on('data', function(msg) {
// console.log("{",msg,"}");
var m = { "payload": msg };
node.send(m);
});
} else {
this.error("missing serial config");
}
}
RED.nodes.registerType("serial in",SerialInNode);
SerialInNode.prototype.close = function() {
if (this.serialConfig) {
try {
serialPool.close(this.serialConfig.serialport);
} catch(err) {
}
this.warn("Deploying with serial-port nodes is known to occasionally cause Node-RED to hang. This is due to an open issue with the underlying module.");
}
}
var serialPool = function() {
var connections = {};
return {
get:function(port,baud,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() {
try {
if (newline == "") {
obj.serial = new serialp.SerialPort(port,{
baudrate: baud,
parser: serialp.parsers.raw
});
}
else {
obj.serial = new serialp.SerialPort(port,{
baudrate: baud,
parser: serialp.parsers.readline(newline)
});
}
obj.serial.on('error', function(err) {
util.log("[serial] serial port "+port+" error "+err);
obj.tout = setTimeout(function() {
setupSerial();
},settings.serialReconnectTime);
});
obj.serial.on('close', function() {
if (!obj._closing) {
util.log("[serial] serial port "+port+" closed unexpectedly");
obj.tout = setTimeout(function() {
setupSerial();
},settings.serialReconnectTime);
}
});
obj.serial.on('open',function() {
util.log("[serial] serial port "+port+" opened at "+baud+" baud");
obj.serial.flush();
obj._emitter.emit('ready');
});
obj.serial.on('data',function(d) {
if (typeof d !== "string") {
d = d.toString();
for (i=0; i<d.length; i++) {
obj._emitter.emit('data',d.charAt(i));
}
}
else {
obj._emitter.emit('data',d);
}
});
} catch(err) { console.log("Booo!",err,"Booo!"); }
}
setupSerial();
return obj;
}();
}
return connections[id];
},
close: function(port) {
if (connections[port]) {
if (connections[port].tout != null) clearTimeout(connections[port].tout);
connections[port]._closing = true;
try {
connections[port].close(function() {
util.log("[serial] serial port closed");
});
} catch(err) { };
}
delete connections[port];
}
}
}();
RED.app.get("/serialports",function(req,res) {
serialp.list(function (err, ports) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(JSON.stringify(ports));
res.end();
});
});

187
nodes/core/io/31-tcpin.html Normal file
View File

@@ -0,0 +1,187 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="tcp in">
<div class="form-row">
<label for="node-input-server"><i class="icon-resize-small"></i> Type</label>
<select id="node-input-server" style="width:120px; margin-right:5px;">
<option value="server">Listen on</option>
<option value="client">Connect to</option>
</select>
port <input type="text" id="node-input-port" style="width: 50px">
</div>
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
at host <input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;">
</div>
<div class="form-row">
<label><i class="icon-th"></i> Output</label>
a
<select id="node-input-datamode" style="width:110px;">
<option value="stream">stream of</option>
<option value="single">single</option>
</select>
<select id="node-input-datatype" style="width:140px;">
<option value="buffer">Buffer</option>
<option value="utf8">String</option>
<option value="base64">Base64 String</option>
</select>
payload<span id="node-input-datamode-plural">s</span>
</div>
<div id="node-row-newline" class="form-row hidden" style="padding-left: 110px;">
delimited by <input type="text" id="node-input-newline" style="width: 110px;">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="tcp in">
<p>Provides a choice of tcp inputs. Can either connect to a remote tcp port,
or accept incoming connections.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tcp in',{
category: 'input',
color:"Silver",
defaults: {
server: {value:"server",required:true},
host: {value:"",validate:function(v) { return (this.server == "server")||v.length > 0;} },
port: {value:"",required:true,validate:RED.validators.number()},
datamode:{value:"stream"},
datatype:{value:"buffer"},
newline:{value:""},
topic: {value:""},
name: {value:""},
base64: {/*deprecated*/ value:false,required:true}
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var updateOptions = function() {
var sockettype = $("#node-input-server option:selected").val();
if (sockettype == "client") {
$("#node-input-host-row").show();
} else {
$("#node-input-host-row").hide();
}
var datamode = $("#node-input-datamode option:selected").val();
var datatype = $("#node-input-datatype option:selected").val();
if (datamode == "stream") {
$("#node-input-datamode-plural").show();
if (datatype == "utf8") {
$("#node-row-newline").show();
} else {
$("#node-row-newline").hide();
}
} else {
$("#node-input-datamode-plural").hide();
$("#node-row-newline").hide();
}
};
updateOptions();
$("#node-input-server").change(updateOptions);
$("#node-input-datatype").change(updateOptions);
$("#node-input-datamode").change(updateOptions);
}
});
</script>
<script type="text/x-red" data-template-name="tcp out">
<div class="form-row">
<label for="node-input-beserver"><i class="icon-resize-small"></i> Type</label>
<select id="node-input-beserver" style="width:120px; margin-right:5px;">
<option value="server">Listen on</option>
<option value="client">Connect to</option>
</select>
port <input type="text" id="node-input-port" style="width: 50px">
</div>
<div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;">
at host <input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 message ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="tcp out">
<p>Provides a choice of tcp outputs. Can either connect to a remote tcp port,
or accept incoming connections.</p>
<p>Only <b>msg.payload</b> is sent.</p>
<p>If <b>msg.payload</b> is a string containing a base64 encoding of binary
data, the Base64 decoding option will cause it to be converted back to binary
before being sent.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tcp out',{
category: 'output',
color:"Silver",
defaults: {
host: {value:"",validate:function(v) { return (this.beserver == "server")||v.length > 0;} },
port: {value:"",required:true},
beserver: {value:"client",required:true},
base64: {value:false,required:true},
name: {value:""}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
return this.name || "tcp:"+(this.host?this.host+":":"")+this.port;
},
labelStyle: function() {
return (this.name)?"node_label_italic":"";
},
oneditprepare: function() {
var updateOptions = function() {
var sockettype = $("#node-input-beserver option:selected").val();
if (sockettype == "client") {
$("#node-input-host-row").show();
} else {
$("#node-input-host-row").hide();
}
};
updateOptions();
$("#node-input-beserver").change(updateOptions);
}
});
</script>

253
nodes/core/io/31-tcpin.js Normal file
View File

@@ -0,0 +1,253 @@
/**
* 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 reconnectTime = RED.settings.socketReconnectTime||10000;
var net = require('net');
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;
if (!node.server) {
var buffer = null;
var client;
var reconnectTimeout;
function setupTcpClient() {
node.log("connecting to "+node.host+":"+node.port);
client = net.connect(node.port, node.host, function() {
buffer = (node.datatype == 'buffer')? new Buffer(0):"";
node.log("connected to "+node.host+":"+node.port);
});
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<parts.length-1;i+=1) {
var msg = {topic:node.topic, payload:parts[i]};
node.send(msg);
}
buffer = parts[parts.length-1];
} else {
var msg = {topic:node.topic, payload:data};
node.send(msg);
}
} else {
if ((typeof data) === "string") {
buffer = buffer+data;
} else {
buffer = Buffer.concat([buffer,data],buffer.length+data.length);
}
}
});
client.on('end', function() {
if (!node.stream || (node.datatype == "utf8" && node.newline != "" && buffer.length > 0)) {
var msg = {topic:node.topic,payload:buffer};
node.send(msg);
buffer = null;
}
});
client.on('close', function() {
node.log("connection lost to "+node.host+":"+node.port);
if (!node.closing) {
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) {
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<parts.length-1;i+=1) {
var msg = {topic:node.topic, payload:parts[i],ip:socket.remoteAddress,port:socket.remotePort};
node.send(msg);
}
buffer = parts[parts.length-1];
} else {
var msg = {topic:node.topic, payload:data};
node.send(msg);
}
} else {
if ((typeof data) === "string") {
buffer = buffer+data;
} else {
buffer = Buffer.concat([buffer,data],buffer.length+data.length);
}
}
});
socket.on('end', function() {
if (!node.stream || (node.datatype == "utf8" && node.newline != "" && buffer.length > 0)) {
var msg = {topic:node.topic,payload:buffer};
node.send(msg);
buffer = null;
}
});
socket.on('error',function(err) {
node.log(err);
});
});
server.listen(node.port);
node.log('listening on port '+node.port);
this.on('close', function() {
this.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.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;
function setupTcpClient() {
node.log("connecting to "+node.host+":"+node.port);
client = net.connect(node.port, node.host, function() {
connected = true;
node.log("connected to "+node.host+":"+node.port);
});
client.on('error', function (err) {
node.log('error : '+err);
});
client.on('end', function (err) {
});
client.on('close', function() {
node.log("connection lost to "+node.host+":"+node.port);
connected = false;
client.destroy();
if (!node.closing) {
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));
}
}
});
node.on("close", function() {
this.closing = true;
client.end();
clearTimeout(reconnectTimeout);
});
} else {
var connectedSockets = [];
var server = net.createServer(function (socket) {
var remoteDetails = socket.remoteAddress+":"+socket.remotePort;
node.log("connection from "+remoteDetails);
connectedSockets.push(socket);
socket.on('close',function() {
node.log("connection closed from "+remoteDetails);
connectedSockets.splice(connectedSockets.indexOf(socket),1);
});
socket.on('error',function() {
node.log("socket error from "+remoteDetails);
connectedSockets.splice(connectedSockets.indexOf(socket),1);
});
});
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<connectedSockets.length;i+=1) {
connectedSockets[i].write(buffer);
}
}
});
server.listen(node.port);
node.log('listening on port '+node.port);
node.on('close', function() {
server.close();
node.log('stopped listening on port '+node.port);
});
}
}
RED.nodes.registerType("tcp out",TcpOut);

178
nodes/core/io/32-udp.html Normal file
View File

@@ -0,0 +1,178 @@
<!--
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.
-->
<!-- The Input Node -->
<script type="text/x-red" data-template-name="udp in">
<div class="form-row">
<label for="node-input-port"><i class="icon-inbox"></i> Listen</label>
on port <input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
for <select id="node-input-multicast" style='width:40%'>
<option value="false">udp messages</option>
<option value="true">multicast messages</option>
</select>
</div>
<div class="form-row node-input-group">
<label for="node-input-group"><i class="icon-list"></i> Group</label>
<input type="text" id="node-input-group" placeholder="225.0.18.83">
</div>
<div class="form-row node-input-iface">
<label for="node-input-iface"><i class="icon-random"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="eth0">
</div>
<div class="form-row">
<label for="node-input-datatype"><i class="icon-file"></i> Output</label>
<select id="node-input-datatype" style="width: 70%;">
<option value="buffer">a Buffer</option>
<option value="utf8">a String</option>
<option value="base64">a Base64 encoded string</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: Make sure your firewall will allow the data in.</div>
<script>
$("#node-input-multicast").change(function() {
var id = $("#node-input-multicast option:selected").val();
if (id == "false") {
$(".node-input-group").hide();
$(".node-input-iface").hide();
}
else {
$(".node-input-group").show();
$(".node-input-iface").show();
}
});
</script>
</script>
<script type="text/x-red" data-help-name="udp in">
<p>A udp input node, that produces a <b>msg.payload</b> containing a <i>BUFFER</i>, string, or base64 encoded string. Supports multicast.</p>
<p>It also provides <b>msg.ip</b> and <b>msg.port</b> to the ip address and port from which the message was received.</b>
<p>On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('udp in',{
category: 'input',
color:"Silver",
defaults: {
name: {value:""},
host: {value:""},
iface: {value:""},
port: {value:"",required:true,validate:RED.validators.number()},
datatype: {value:"buffer",required:true},
multicast: {value:"false"},
group: {value:"",validate:function(v) { return (this.multicast !== "true")||v.length > 0;} }
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
if (this.multicast=="false") {
return this.name||"udp "+this.port;
}
else return this.name||"udp "+(this.group+":"+this.port);
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<!-- The Output Node -->
<script type="text/x-red" data-template-name="udp out">
<div class="form-row">
<label for="node-input-port"><i class="icon-envelope"></i> Send a</label>
<select id="node-input-multicast" style='width:40%'>
<option value="false">udp message</option>
<option value="broad">broadcast message</option>
<option value="multi">multicast message</option>
</select>
to port <input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row node-input-addr">
<label for="node-input-addr" id="node-input-addr-label"><i class="icon-list"></i> Address</label>
<input type="text" id="node-input-addr" placeholder="destination ip" style="width: 70%;">
</div>
<div class="form-row node-input-iface">
<label for="node-input-iface"><i class="icon-random"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="eth0">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 encoded payload ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: leave address and port blank if you want to set using <b>msg.ip</b> and <b>msg.port</b>.</div>
<script>
$("#node-input-multicast").change(function() {
var id = $("#node-input-multicast option:selected").val();
console.log(id,$("#node-input-addr")[0].placeholder);
if (id !== "multi") {
$(".node-input-iface").hide();
$("#node-input-addr-label").html('<i class="icon-list"></i> Address');
$("#node-input-addr")[0].placeholder = 'destination ip';
}
else {
$(".node-input-iface").show();
$("#node-input-addr-label").html('<i class="icon-list"></i> Group');
$("#node-input-addr")[0].placeholder = '225.0.18.83';
}
if (id === "broad") {
$("#node-input-addr")[0].placeholder = '255.255.255.255';
}
});
</script>
</script>
<script type="text/x-red" data-help-name="udp out">
<p>This node sends <b>msg.payload</b> to the designated udp host and port. Supports multicast.</p>
<p>You may also use <b>msg.ip</b> and <b>msg.port</b> to set the destination values.<br/><b>Note</b>: the statically configured values have precedence.</p>
<p>If you select broadcast either set the address to the local broadcast ip address, or maybe try 255.255.255.255, which is the global broadcast address.</p>
<p>On some systems you may need to be root to use ports below 1024 and/or broadcast.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('udp out',{
category: 'output',
color:"Silver",
defaults: {
name: {value:""},
addr: {value:""},
iface: {value:""},
port: {value:""},
base64: {value:false,required:true},
multicast: {value:"false"}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
return this.name||"udp "+(this.addr+":"+this.port);
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

124
nodes/core/io/32-udp.js Normal file
View File

@@ -0,0 +1,124 @@
/**
* 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 dgram = require('dgram');
// The Input Node
function UDPin(n) {
RED.nodes.createNode(this,n);
this.group = n.group;
this.port = n.port;
this.host = n.host || null;
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) {
//console.log("udp listener error:\n" + err.stack);
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)
server.setMulticastTTL(128);
server.addMembership(node.group,node.iface);
node.log("udp multicast group "+node.group);
}
});
node.on("close", function() {
try {
server.close();
node.log('udp listener stopped');
}
catch (err) { console.log(err); }
});
server.bind(node.port,node.host);
}
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.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
sock.bind(node.port); // have to bind before you can enable broadcast...
if (this.multicast != "false") {
sock.setBroadcast(true); // turn on broadcast
if (this.multicast == "multi") {
sock.setMulticastTTL(128);
sock.addMembership(node.addr,node.iface); // Add to the multicast group
node.log('udp multicast ready : '+node.addr+":"+node.port);
}
else node.log('udp broadcast ready : '+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(b64string, 'base64'); }
else if (msg.payload instanceof Buffer) { message = msg.payload; }
else { message = new Buffer(""+msg.payload); }
console.log("UDP send :",add,por,msg.payload.toString());
sock.send(message, 0, message.length, por, add, function(err, bytes) {
if (err) node.error("udp : "+err);
});
}
}
});
node.on("close", function() {
try {
sock.close();
node.log('udp output stopped');
}
catch (err) { console.log(err); }
});
}
RED.nodes.registerType("udp out",UDPout);

View File

@@ -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.
-->
<script type="text/x-red" data-template-name="httpget">
<div class="form-tips"><b>Deprecated</b>: please use the <i>http request</i> node.</div>
<br>
<div class="form-row">
<label for="node-input-baseurl"><i class="icon-tasks"></i> Base URL</label>
<input type="text" id="node-input-baseurl" placeholder="http(s)://url">
</div>
<div class="form-row">
<label for="node-input-append"><i class="icon-tasks"></i> Append</label>
<input type="text" id="node-input-append" placeholder="">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The <b>Base URL</b> gets prepended to whatever payload is passed in. Leave blank if you pass in a full url.<br/>The append gets added to the end after any payload.<br/>The output Topic is the same as the input Topic.</div>
</script>
<script type="text/x-red" data-help-name="httpget">
<p>Performs an HTTP or HTTPS GET and returns the fetched page.</p>
<p>The return code is placed in <b>msg.rc</b>, and the full text of the result is in <b>msg.payload</b>.</p>
<p>The <b>msg.payload</b> is added to the base url, and then the optional append is added after.</p>
<p>This is mostly suitable for small pages as large results will need a lot of parsing....</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('httpget',{
category: 'advanced-function',
color:"rgb(231, 231, 174)",
defaults: {
name: {value:""},
baseurl: {value:""},
append: {value:""}
},
inputs:1,
outputs:1,
icon: "white-globe.png",
label: function() {
return this.name||this.baseurl;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,52 @@
/**
* 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.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);

225
nodes/core/io/lib/mqtt.js Normal file
View File

@@ -0,0 +1,225 @@
/**
* 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 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) {
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.emit('connectionlost',err);
return;
}
client.on('close',function(e) {
clearInterval(self.watchdog);
if (self.connected) {
self.connected = false;
self.emit('connectionlost',e);
} else {
self.emit('disconnect');
}
});
client.on('error',function(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();
if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) {
if (self.pingOutstanding) {
// DO DISCONNECT
} else {
self.lastOutbound = (new Date()).getTime();
self.lastInbound = (new Date()).getTime();
self.pingOutstanding = true;
self.client.pingreq();
}
}
},self.options.keepalive*500,self);
self.lastInbound = (new Date()).getTime()
self.connected = true;
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];
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) {
self.lastInbound = (new Date()).getTime()
self.pingOutstanding = false;
});
this.lastOutbound = (new Date()).getTime()
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)) {
payload = payload.toString();
} else 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;
this.client.disconnect();
}
}
MQTTClient.prototype.isConnected = function() {
return this.connected;
}
module.exports.createClient = function(port,host) {
var mqtt_client = new MQTTClient(port,host);
return mqtt_client;
}

View File

@@ -0,0 +1,120 @@
/**
* 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) {
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/#$/,".*"));
return re.test(t);
}
module.exports = {
get: function(broker,port) {
var id = broker+":"+port;
if (!connections[id]) {
connections[id] = function() {
var client = mqtt.createClient(port,broker);
client.setMaxListeners(0);
var options = {keepalive:15,clientId:'mqtt_' + (1+Math.random()*4294967295).toString(16)};
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.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() {
util.log('[mqtt] 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] connection lost to broker tcp://'+broker+':'+port);
setTimeout(function() {
if (client) {
client.connect(options);
}
}, settings.mqttReconnectTime||5000);
});
client.on('disconnect', function() {
util.log('[mqtt] disconnected from broker tcp://'+broker+':'+port);
});
return obj
}();
}
connections[id]._instances += 1;
return connections[id];
}
};

View File

@@ -0,0 +1,199 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="switch">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row" style="padding-top:10px;">
If msg.<input type="text" id="node-input-property" style="width: 200px;"/>
</div>
<div class="form-row">
<div id="node-input-rule-container-div" style="border-radius: 5px; height: 310px; padding: 5px; border: 1px solid #ccc; overflow-y:scroll;">
<ol id="node-input-rule-container" style=" list-style-type:none; margin: 0;">
</ol>
</div>
<a href="#" class="btn btn-mini" id="node-input-add-rule" style="margin-top: 4px;"><i class="icon-plus"></i> Add</a>
</div>
</script>
<script type="text/x-red" data-help-name="switch">
<p>A simple function node to route messages based on its properties.</p>
<p>When a message arrives, the selected property is evaluated against each
of the defined rules. The message is then sent to the output of <i>all</i>
rules that pass.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('switch',{
color:"#E2D96E",
category: 'function',
defaults: {
name: {value:""},
property: {value: "payload",required:true},
rules:{value:[{t:"eq",v:""}]},
outputs:{value:1}
},
inputs:1,
outputs:1,
icon: "switch.png",
label: function() {
return this.name;
},
oneditprepare: function() {
var operators = [
{v:"eq",t:"=="},
{v:"neq",t:"!="},
{v:"lt",t:"<"},
{v:"lte",t:"<="},
{v:"gt",t:">"},
{v:"gte",t:">="},
{v:"btwn",t:"is between"},
{v:"cont",t:"contains"},
{v:"regex",t:"matches regex"},
{v:"true",t:"is true"},
{v:"false",t:"is false"},
{v:"null",t:"is null"},
{v:"nnull",t:"is not null"}
];
function generateRule(i,rule) {
var container = $('<li/>',{style:"margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"});
var row = $('<div/>').appendTo(container);
var row2 = $('<div/>',{style:"padding-top: 5px; text-align: right;"}).appendTo(container);
var selectField = $('<select/>',{style:"width:120px; margin-left: 5px; text-align: center;"}).appendTo(row);
for (var d in operators) {
selectField.append($("<option></option>").val(operators[d].v).text(operators[d].t));
}
var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px; width: 145px;"}).appendTo(row);
var btwnField = $('<span/>').appendTo(row);
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px; width: 50px;"}).appendTo(btwnField);
btwnField.append(" and ");
var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 50px;margin-left:2px;"}).appendTo(btwnField);
var finalspan = $('<span/>',{style:"float: right; margin-top: 3px;margin-right: 10px;"}).appendTo(row);
finalspan.append(' send to <span class="node-input-rule-index">'+i+'</span> ');
selectField.change(function() {
var type = selectField.children("option:selected").val();
if (type.length < 4) {
selectField.css({"width":"60px"});
} else if (type === "regex") {
selectField.css({"width":"147px"});
} else {
selectField.css({"width":"120px"});
}
if (type === "btwn") {
valueField.hide();
btwnField.show();
} else {
btwnField.hide();
if (type === "true" || type === "false" || type === "null" || type === "nnull") {
valueField.hide();
} else {
valueField.show();
}
}
});
var deleteButton = $('<a/>',{href:"#",class:"btn btn-mini", style:"margin-left: 5px;"}).appendTo(finalspan);
$('<i/>',{class:"icon-remove"}).appendTo(deleteButton);
deleteButton.click(function() {
container.css({"background":"#fee"});
container.fadeOut(300, function() {
$(this).remove();
$("#node-input-rule-container").children().each(function(i) {
$(this).find(".node-input-rule-index").html(i+1);
});
});
});
$("#node-input-rule-container").append(container);
selectField.find("option").filter(function() {return $(this).val() == rule.t;}).attr('selected',true);
if (rule.t == "btwn") {
btwnValueField.val(rule.v);
btwnValue2Field.val(rule.v2);
} else if (rule.v) {
valueField.val(rule.v);
}
selectField.change();
}
$("#node-input-add-rule").click(function() {
generateRule($("#node-input-rule-container").children().length+1,{t:"",v:"",v2:""});
$("#node-input-rule-container-div").scrollTop($("#node-input-rule-container-div").get(0).scrollHeight);
});
for (var i=0;i<this.rules.length;i++) {
var rule = this.rules[i];
generateRule(i+1,rule);
}
function switchDialogResize(ev,ui) {
$("#node-input-rule-container-div").css("height",(ui.size.height-260)+"px");
};
$( "#dialog" ).on("dialogresize", switchDialogResize);
$( "#dialog" ).one("dialogopen", function(ev) {
var size = $( "#dialog" ).dialog('option','sizeCache-switch');
if (size) {
switchDialogResize(null,{size:size});
}
});
$( "#dialog" ).one("dialogclose", function(ev,ui) {
$( "#dialog" ).off("dialogresize",switchDialogResize);
});
},
oneditsave: function() {
var rules = $("#node-input-rule-container").children();
var ruleset;
var node = this;
node.rules= [];
rules.each(function(i) {
var rule = $(this);
var type = rule.find("select option:selected").val();
var r = {t:type};
if (!(type === "true" || type === "false" || type === "null" || type === "nnull")) {
if (type === "btwn") {
r.v = rule.find(".node-input-rule-btwn-value").val();
r.v2 = rule.find(".node-input-rule-btwn-value2").val();
} else {
r.v = rule.find(".node-input-rule-value").val();
}
}
node.rules.push(r);
});
node.outputs = node.rules.length;
}
});
</script>

View File

@@ -0,0 +1,64 @@
/**
* 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 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 a === null; },
'nnull': function(a) { return a !== null; }
};
function SwitchNode(n) {
RED.nodes.createNode(this,n);
this.rules = n.rules;
this.property = n.property;
var propertyParts = n.property.split(".");
var node = this;
this.on('input',function(msg) {
var onward = [];
var prop = propertyParts.reduce(function(obj,i) {
return obj[i]
},msg);
for (var i=0;i<node.rules.length;i+=1) {
var rule = node.rules[i];
if (operators[rule.t](prop,rule.v,rule.v2)) {
onward.push(msg);
} else {
onward.push(null);
}
}
this.send(onward);
});
}
RED.nodes.registerType("switch",SwitchNode);

View File

@@ -0,0 +1,192 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="twitter-credentials">
<div class="form-row" id="node-config-twitter-row"></div>
<input type="hidden" id="node-config-input-screen_name">
</script>
<script type="text/javascript">
var twitterConfigNodeId = null;
var twitterConfigNodeIntervalId = null;
function showTwitterAuthStart() {
var pathname = document.location.pathname;
if (pathname.slice(-1) != "/") {
pathname += "/";
}
var callback = encodeURIComponent(location.protocol+"//"+location.hostname+":"+location.port+pathname+"twitter/"+twitterConfigNodeId+"/auth/callback");
$("#node-config-twitter-row").html('Click <a id="node-config-twitter-start" href="/twitter/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank"><b>here</b></a> to authenticate with Twitter.');
$("#node-config-twitter-start").click(function() {
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
});
}
function updateTwitterScreenName(sn) {
$("#node-config-input-screen_name").val(sn);
$("#node-config-twitter-row").html('<label><i class="icon-user"></i> Twitter ID</label><span class="input-xlarge uneditable-input">'+sn+'</span>');
}
function pollTwitterCredentials(e) {
$.getJSON('twitter/'+twitterConfigNodeId,function(data) {
if (data.sn) {
updateTwitterScreenName(data.sn);
twitterConfigNodeIntervalId = null;
} else {
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
}
})
}
RED.nodes.registerType('twitter-credentials',{
category: 'config',
defaults: {
screen_name: {value:""},
access_token: {value: ""},
access_token_secret: {value:""}
},
label: function() {
return this.screen_name;
},
exportable: false,
oneditprepare: function() {
twitterConfigNodeId = this.id;
if (!this.screen_name || this.screen_name == "") {
showTwitterAuthStart();
} else {
$.getJSON('twitter/'+twitterConfigNodeId,function(data) {
if (data.sn) {
updateTwitterScreenName(data.sn);
} else {
showTwitterAuthStart();
}
});
}
},
oneditsave: function() {
if (twitterConfigNodeIntervalId) {
window.clearTimeout(twitterConfigNodeIntervalId);
}
},
oneditcancel: function(adding) {
if (twitterConfigNodeIntervalId) {
window.clearTimeout(twitterConfigNodeIntervalId);
}
if (adding) {
$.ajax({
url: 'twitter/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
},
ondelete: function() {
$.ajax({
url: 'twitter/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
});
</script>
<script type="text/x-red" data-template-name="twitter in">
<div class="form-row">
<label for="node-input-twitter"><i class="icon-user"></i> Log in as</label>
<input type="text" id="node-input-twitter">
</div>
<div class="form-row">
<label for="node-input-user"><i class="icon-search"></i> Search</label>
<select type="text" id="node-input-user" style="display: inline-block; vertical-align: middle; width:60%;">
<option value="false">all public tweets</option>
<option value="true">the tweets of who you follow</option>
</select>
</div>
<div class="form-row">
<label for="node-input-tags"><i class="icon-tags"></i> for</label>
<input type="text" id="node-input-tags" placeholder="comma-separated words, @ids, #tags">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: Use commas without spaces between multiple search terms. Comma = OR, Space = AND.
<br/>The Twitter API WILL NOT deliver 100% of all tweets.
<br/>Tweets of who you follow will include their retweets and favourites.</div>
</script>
<script type="text/x-red" data-help-name="twitter in">
<p>Twitter input node. Watches either the public or the user's stream for tweets containing the configured search term.</p>
<p>Sets the <b>msg.topic</b> to <i>tweets/</i> and then appends the senders screen name.</p>
<p>Sets <b>msg.location</b> to the tweeters location if known.</p>
<p>Sets <b>msg.tweet</b> to the full tweet object as documented by <a href="https://dev.twitter.com/docs/platform-objects/tweets">Twitter</a>.
</script>
<script type="text/javascript">
RED.nodes.registerType('twitter in',{
category: 'social-input',
color:"#C0DEED",
defaults: {
twitter: {type:"twitter-credentials",required:true},
tags: {value:"",required:true},
user: {value:"false",required:true},
name: {value:""},
topic: {value:"tweets"}
},
inputs:0,
outputs:1,
icon: "twitter.png",
label: function() {
return this.name||this.tags;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="twitter out">
<div class="form-row">
<label for="node-input-twitter"><i class="icon-user"></i> Twitter</label>
<input type="text" id="node-input-twitter">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="twitter out">
<p>Twitter out node. Tweets the <b>msg.payload</b>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('twitter out',{
category: 'social-output',
color:"#C0DEED",
defaults: {
twitter: {type:"twitter-credentials",required:true},
name: {value:"Tweet"}
},
inputs:1,
outputs:0,
icon: "twitter.png",
align: "right",
label: function() {
return this.name;
}
});
</script>

View File

@@ -0,0 +1,209 @@
/**
* 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 ntwitter = require('ntwitter');
var OAuth= require('oauth').OAuth;
function TwitterNode(n) {
RED.nodes.createNode(this,n);
this.screen_name = n.screen_name;
}
RED.nodes.registerType("twitter-credentials",TwitterNode);
function TwitterInNode(n) {
RED.nodes.createNode(this,n);
this.active = true;
this.user = n.user;
this.tags = n.tags.replace(/ /g,'');
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
});
var node = this;
if (this.tags !== "") {
try {
var thing = 'statuses/filter';
if (this.user == "true") { thing = 'user'; }
function setupStream() {
if (node.active) {
twit.stream(thing, { track: [node.tags] }, function(stream) {
//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('error', function(tweet) {
node.warn(tweet);
setTimeout(setupStream,5000);
});
stream.on('destroy', function (response) {
if (this.active) {
node.warn("twitter ended unexpectedly");
setTimeout(setupStream,5000);
}
});
});
}
}
setupStream();
}
catch (err) {
node.error(err);
}
} else {
this.error("Invalid tag property");
}
} else {
this.error("missing twitter credentials");
}
}
RED.nodes.registerType("twitter in",TwitterInNode);
TwitterInNode.prototype.close = function() {
if (this.stream) {
this.active = false;
this.stream.destroy();
}
}
function TwitterOutNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.twitter = n.twitter;
this.twitterConfig = RED.nodes.getNode(this.twitter);
var credentials = RED.nodes.getCredentials(this.twitter);
var node = this;
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
}).verifyCredentials(function (err, data) {
if (err) {
node.error("Error verifying credentials: " + err);
} else {
node.on("input", function(msg) {
if (msg != null) {
if (msg.payload.length > 140) {
msg.payload = msg.payload.slice(0,139);
node.warn("Tweet greater than 140 : truncated");
}
twit.updateStatus(msg.payload, function (err, data) {
if (err) node.error(err);
});
}
});
}
});
}
}
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"
);
var credentials = {};
RED.app.get('/twitter/:id', function(req,res) {
var credentials = RED.nodes.getCredentials(req.params.id);
if (credentials) {
res.send(JSON.stringify({sn:credentials.screen_name}));
} else {
res.send(JSON.stringify({}));
}
});
RED.app.delete('/twitter/:id', function(req,res) {
RED.nodes.deleteCredentials(req.params.id);
res.send(200);
});
RED.app.get('/twitter/:id/auth', function(req, res){
var credentials = {};
oa.getOAuthRequestToken({
oauth_callback: req.query.callback
},function(error, oauth_token, oauth_token_secret, results){
if (error) {
console.log(error);
res.send("yeah no. didn't work.")
}
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.app.get('/twitter/: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("<html><head></head><body>Authorised - you can close this window and return to Node-RED</body></html>");
}
}
);
});

View File

@@ -0,0 +1,57 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="feedparse">
<div class="form-row">
<label for="node-input-url"><i class="icon-globe"></i> Feed url</label>
<input type="text" id="node-input-url">
</div>
<div class="form-row">
<label for="node-input-interval"><i class="icon-repeat"></i> Repeat <span style="font-size: 0.9em;">(M)</span></label>
<input type="text" id="node-input-interval" placeholder="minutes">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<!-- <div class="form-tips"></div> -->
</script>
<script type="text/x-red" data-help-name="feedparse">
<p>Monitors an RSS/atom feed for new entries.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('feedparse',{
category: 'advanced-input',
color:"#C0DEED",
defaults: {
name: {value:""},
url: {value:"", required:true},
interval: { value:15, required: true,validate:RED.validators.number()}
},
inputs:0,
outputs:1,
icon: "feed.png",
label: function() {
return this.name||this.url;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,75 @@
/**
* 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 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('article', function (article) {
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: {
summary:article.summary,
link:article.link,
date: article.date,
pubdate: article.pubdate,
author: article.author,
guid: article.guid,
}
};
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);
}
}

View File

@@ -0,0 +1,50 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="email">
<div class="form-row">
<label for="node-input-name"><i class="icon-envelope"></i> To</label>
<input type="text" id="node-input-name" placeholder="email@address.com">
</div>
</script>
<script type="text/x-red" data-help-name="email">
<p>Sends the <b>msg.payload</b> as an email, with a subject of <b>msg.topic</b>.</p>
<p>It sends the message to the configured recipient <i>only</i>.</p>
<p><b>msg.topic</b> is used to set the subject of the email, and <b>msg.payload</b> is the body text.</p>
<p>Uses the nodemailer module - you also need to pre-configure your email SMTP settings in ../../emailkeys.js - see INSTALL file for details.</p>
<p><pre>module.exports = { service: "Gmail", user: "blahblah@gmail.com", pass: "password", server: "imap.gmail.com", port: "993" }</pre></p>
</script>
<script type="text/javascript">
RED.nodes.registerType('email',{
category: 'social-output',
color:"#c7e9c0",
defaults: {
name: {value:"",required:true}
},
inputs:1,
outputs:0,
icon: "envelope.png",
align: "right",
label: function() {
return this.name;
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,55 @@
/**
* 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 nodemailer = require("nodemailer");
var emailkey = require(process.env.NODE_RED_HOME+"/../emailkeys.js");
var smtpTransport = nodemailer.createTransport("SMTP",{
service: emailkey.service,
auth: {
user: emailkey.user,
pass: emailkey.pass
}
});
function EmailNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.name = n.name;
var node = this;
this.on("input", function(msg) {
//node.log("email :",this.id,this.topic," received",msg.payload);
if (msg != null) {
smtpTransport.sendMail({
from: emailkey.user, // sender address
to: node.name, // comma separated list of receivers
subject: msg.topic, // Subject line
text: msg.payload // plaintext body
}, function(error, response) {
if (error) {
node.error(error);
} else {
node.log("Message sent: " + response.message);
}
});
}
});
}
RED.nodes.registerType("email",EmailNode);

View File

@@ -0,0 +1,54 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="imap">
<div class="form-row node-input-repeat">
<label for="node-input-repeat"><i class="icon-repeat"></i>Repeat (S)</label>
<input type="text" id="node-input-repeat" placeholder="300">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="imap">
<p>Repeatedly gets a <b>single email</b> from an IMAP server and forwards on as a msg if not already seen.</p>
<p>The subject is loaded into <b>msg.topic</b> and <b>msg.payload</b> is the body text. <b>msg.from</b> is also set if you need it.</p>
<p>Uses the imap module - you also need to pre-configure your email settings in ../../emailkeys.js - see INSTALL file for details.</p>
<p><pre>module.exports = { service: "Gmail", user: "blahblah@gmail.com", pass: "password", server: "imap.gmail.com", port: "993" }</pre></p>
<p><b>Note:</b> this node <i>only</i> gets the most recent single email from the inbox, so set the repeat (polling) time appropriately.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('imap',{
category: 'social-input',
color:"#c7e9c0",
defaults: {
repeat: {value:"300",required:true},
name: {value:""}
},
inputs:0,
outputs:1,
icon: "envelope.png",
label: function() {
return this.name||"IMAP";
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,108 @@
/**
* 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');
var oldmail = {};
try {
var emailkey = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js");
} catch(err) {
throw new Error("Failed to load Email credentials");
}
var imap = new Imap({
user: emailkey.user,
password: emailkey.pass,
host: emailkey.server||"imap.gmail.com",
port: emailkey.port||"993",
secure: true
});
function fail(err) {
util.log('[imap] : ' + err);
}
function openInbox(cb) {
imap.connect(function(err) {
if (err) fail(err);
imap.openBox('INBOX', true, cb);
});
}
function ImapNode(n) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.repeat = n.repeat * 1000;
var node = this;
this.interval_id = null;
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.log("repeat = "+this.repeat);
this.interval_id = setInterval( function() {
node.emit("input",{});
}, this.repeat );
}
this.on("input", function(msg) {
openInbox(function(err, mailbox) {
if (err) fail(err);
imap.seq.fetch(mailbox.messages.total + ':*', { struct: false },
{ headers: ['from', 'subject'],
body: true,
cb: function(fetch) {
fetch.on('message', function(msg) {
//node.log('Saw message no. ' + msg.seqno);
var pay = {};
var body = '';
msg.on('headers', function(hdrs) {
pay.from = hdrs.from[0];
pay.topic = hdrs.subject[0];
});
msg.on('data', function(chunk) {
body += chunk.toString('utf8');
});
msg.on('end', function() {
pay.payload = body;
if ((pay.topic !== oldmail.topic)|(pay.payload !== oldmail.payload)) {
oldmail = pay;
//node.log("From: "+pay.from);
node.log("Subj: "+pay.topic);
//node.log("Body: "+pay.payload);
node.send(pay);
}
});
});
}
}, function(err) {
if (err) node.log("Err : "+err);
//node.log("Done fetching messages.");
imap.logout();
}
);
});
});
this.on("close", function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
}
});
}
RED.nodes.registerType("imap",ImapNode);

View File

@@ -0,0 +1,130 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="irc in">
<div class="form-row">
<label for="node-input-ircserver"><i class="icon-tasks"></i> IRC Server</label>
<input type="text" id="node-input-ircserver">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="irc in">
<p>Connects to a channel on an IRC server</p>
<p>Any messages on that channel will appear on the <b>msg.payload</b> at the output, while <b>msg.topic</b> will contain who it is from.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc in',{
category: 'social-input',
defaults: {
name: {value:""},
ircserver: {type:"irc-server", required:true}
},
color:"Silver",
inputs:0,
outputs:1,
icon: "hash.png",
label: function() {
var ircNode = RED.nodes.node(this.ircserver);
return this.name||(ircNode?ircNode.label():"irc");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="irc out">
<div class="form-row">
<label for="node-input-ircserver"><i class="icon-tasks"></i> IRC Server</label>
<input type="text" id="node-input-ircserver">
</div>
<div class="form-row">
<label for="node-input-sendObject"><i class="icon-check"></i> Action</label>
<select type="text" id="node-input-sendObject" style="display: inline-block; vertical-align: middle; width:70%;">
<option value="pay">Send msg.payload to channel</option>
<option value="true">Send msg.payload to id in msg.topic</option>
<option value="false">Send complete msg object to channel</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Sending the complete object will stringify the whole msg object before sending.</div>
</script>
<script type="text/x-red" data-help-name="irc out">
<p>Sends messages to a channel on an IRC server</p>
<p>You can send just the <b>msg.payload</b>, or the complete <b>msg</b> object to the selected channel,
or you can select to use <b>msg.topic</b> to send the <b>msg.payload</b> to a specific user in the channel (private conversation).</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc out',{
category: 'social-output',
defaults: {
name: {value:""},
sendObject: {value:"pay", required:true},
ircserver: {type:"irc-server", required:true}
},
color:"Silver",
inputs:1,
outputs:0,
icon: "hash.png",
align: "right",
label: function() {
return this.name || (this.ircserver)?RED.nodes.node(this.ircserver).label():"irc";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="irc-server">
<div class="form-row">
<label for="node-config-input-server"><i class="icon-tasks"></i> IRC Server</label>
<input type="text" id="node-config-input-server" placeholder="irc.UK-IRC.net">
</div>
<div class="form-row">
<label for="node-config-input-channel"><i class="icon-tasks"></i> Channel</label>
<input type="text" id="node-config-input-channel" placeholder="#node-red">
</div>
<div class="form-row">
<label for="node-config-input-nickname"><i class="icon-tasks"></i> Nickname</label>
<input type="text" id="node-config-input-nickname" placeholder="joe123">
</div>
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc-server',{
category: 'config',
defaults: {
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)},
server: {value:"",required:true},
nickname: {value:"",required:true}
},
label: function() {
return this.server+":"+this.channel;
}
});
</script>

View File

@@ -0,0 +1,98 @@
/**
* 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 irc = require("irc");
var util = require("util");
// 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.ircclient = null;
this.on("close", function() {
if (this.ircclient != null) {
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);
if (this.serverConfig.ircclient == null) {
this.serverConfig.ircclient = new irc.Client(this.serverConfig.server, this.serverConfig.nickname, {
channels: [this.serverConfig.channel]
});
this.serverConfig.ircclient.addListener('error', function(message) {
util.log('[irc] '+ JSON.stringify(message));
});
}
this.ircclient = this.serverConfig.ircclient;
var node = this;
this.ircclient.addListener('message', function (from, to, message) {
//util.log(from + ' => ' + to + ': ' + message);
var msg = { "topic":from, "to":to, "payload":message };
node.send(msg);
});
}
RED.nodes.registerType("irc in",IrcInNode);
// The Output Node
function IrcOutNode(n) {
RED.nodes.createNode(this,n);
this.sendAll = n.sendObject;
this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.ircserver);
this.channel = this.serverConfig.channel;
if (this.serverConfig.ircclient == null) {
this.serverConfig.ircclient = new irc.Client(this.serverConfig.server, this.serverConfig.nickname, {
channels: [this.serverConfig.channel]
});
this.serverConfig.ircclient.addListener('error', function(message) {
util.log('[irc] '+ JSON.stringify(message));
});
}
this.ircclient = this.serverConfig.ircclient;
var node = this;
this.on("input", function(msg) {
//console.log(msg,node.channel);
if (msg._topic) { delete msg._topic; }
if (node.sendAll == "false") {
node.ircclient.say(node.channel, JSON.stringify(msg));
}
else {
if (typeof msg.payload === "object") { msg.payload = JSON.stringify(msg.payload); }
if (node.sendAll == "pay") {
node.ircclient.say(node.channel, msg.payload);
}
else {
var to = msg.topic || node.channel;
node.ircclient.say(to, msg.payload);
}
}
});
}
RED.nodes.registerType("irc out",IrcOutNode);

View File

@@ -0,0 +1,58 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="tail">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="icon-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-split" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-split" style="width: 70%;">Split lines if we see \n ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">WON'T work on Windows.</div>
</script>
<script type="text/x-red" data-help-name="tail">
<p>Tails (watches for things to be added) to the configured file. (Linux/Mac ONLY)</p>
<p>This won't work on Windows filesystems (as it relies on the tail -f command) so we will probably have to hide it in future.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tail',{
category: 'storage-input',
defaults: {
name: {value:""},
split: {value:false},
filename: {value:"",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "file.png",
label: function() {
return this.name||this.filename;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,56 @@
/**
* 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 fs = require("fs");
var spawn = require('child_process').spawn;
function TailNode(n) {
RED.nodes.createNode(this,n);
this.filename = n.filename;
this.split = n.split;
var node = this;
var err = "";
var tail = spawn("tail", ["-f", this.filename]);
tail.stdout.on("data", function (data) {
var msg = {topic:node.filename};
if (node.split) {
var strings = data.toString().split("\n");
for (s in strings) {
if (strings[s] != "") {
msg.payload = strings[s];
node.send(msg);
}
}
}
else {
msg.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);

View File

@@ -0,0 +1,66 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="file">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="icon-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-appendNewline" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-appendNewline" style="width: 70%;">Append newline ?</label>
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-overwriteFile" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-overwriteFile" style="width: 70%;">Overwrite complete file ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="file">
<p>Writes the <b>msg.payload</b> to the file specified, e.g. to create a log.</p>
<p>A newline is added to every message. But this can be turned off if required, for example, to allow binary files to be written.</p>
<p>The default behaviour is to append to the file. This can be changed to overwrite the file each time, for example if you want to output a "static" web page or report.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('file',{
category: 'storage-output',
defaults: {
name: {value:""},
filename: {value:"",required:true},
appendNewline: {value:true},
overwriteFile: {value:false}
},
color:"BurlyWood",
inputs:1,
outputs:0,
icon: "file.png",
align: "right",
label: function() {
return this.name||this.filename;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -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.
**/
var RED = require(process.env.NODE_RED_HOME+"/red/red");
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 data = msg.payload;
if (this.appendNewline) {
data += "\n";
}
if (this.overwriteFile) {
fs.writeFile(this.filename, data, function (err) {
if (err) node.warn('Failed to write to file : '+err);
//console.log('Message written to file',this.filename);
});
}
else {
fs.appendFile(this.filename, data, function (err) {
if (err) node.warn('Failed to append to file : '+err);
//console.log('Message appended to file',this.filename);
});
}
});
}
RED.nodes.registerType("file",FileNode);

View File

@@ -0,0 +1,105 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="redis out">
<div class="form-row node-input-hostname">
<label for="node-input-hostname"><i class="icon-bookmark"></i> Host</label>
<input class="input-append-left" type="text" id="node-input-hostname" placeholder="127.0.0.1" style="width: 40%;" ><button id="node-input-hostname-lookup" class="btn input-append-right"><span class="caret"></span></button>
<label for="node-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-input-port" placeholder="6379" style="width:45px">
</div>
<div class="form-row">
<label for="node-input-key"><i class="icon-briefcase"></i> Key</label>
<input type="text" id="node-input-key" placeholder="Redis Key">
</div>
<div class="form-row">
<label for="node-input-type"><i class="icon-th"></i> Type</label>
<select type="text" id="node-input-structtype" style="width: 150px;">
<option value="string">String</option>
<option value="hash">Hash</option>
<option value="set">Set</option>
<option value="list">List</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">
If key is blank, the topic will be used as the key.<br>
If type is hash, payload should be field=value.
</div>
</script>
<script type="text/x-red" data-help-name="redis out">
<p>A Redis output node. Options include Hash, Set, List and String.</p>
<p>To run this you need a local Redis server running. For details see <a href="http://redis.io/" target="_new">the Redis site</a>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('redis out',{
category: 'storage-output',
color:"#ffaaaa",
defaults: {
hostname: { value:"127.0.0.1",required:true},
port: { value: 6379,required:true},
name: {value:""},
key: {value:""},
structtype: {value:"",required:true}
},
inputs:1,
outputs:0,
icon: "redis.png",
align: "right",
label: function() {
return this.name||this.key+" ("+this.structtype+")";
},
oneditprepare: function() {
var availableServers = [];
var matchedServers = {};
RED.nodes.eachNode(function(node) {
if (node.type == "redis out" && node.hostname && node.port && !matchedServers[node.hostname+":"+node.port]) {
var label = node.hostname+":"+node.port;
matchedServers[label] = true;
availableServers.push({
label:label,
value:node.hostname,
port:node.port
});
}
});
$( "#node-input-hostname" ).autocomplete({
minLength: 0,
source: availableServers,
select: function( event, ui ) {
$("#node-input-port").val(ui.item.port);
}
});
var tt = this;
tt._acOpen = false;
$( "#node-input-hostname" ).on( "autocompleteclose", function( event, ui ) { tt._acOpen = false;} );
$( "#node-input-hostname-lookup" ).click(function(e) {
if (tt._acOpen) {
$( "#node-input-hostname" ).autocomplete( "close");
} else {
$( "#node-input-hostname" ).autocomplete( "search", "" );
}
tt._acOpen = !tt._acOpen;
e.preventDefault();
});
}
});
</script>

View File

@@ -0,0 +1,96 @@
/**
* 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 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);
this.on("input", function(msg) {
if (msg != null) {
var k = this.key || msg.topic;
if (k) {
if (this.structtype == "string") {
this.client.set(k,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");
}
}
});
}
RED.nodes.registerType("redis out",RedisOutNode);
RedisOutNode.prototype.close = function() {
redisConnectionPool.close(this.client);
}

View File

@@ -0,0 +1,163 @@
<!--
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.
-->
<script type="text/x-red" data-template-name="mongodb">
<div class="form-row">
<label for="node-config-input-hostname"><i class="icon-bookmark"></i> Host</label>
<input class="input-append-left" type="text" id="node-config-input-hostname" placeholder="localhost" style="width: 40%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="27017" style="width:45px">
</div>
<div class="form-row">
<label for="node-config-input-db"><i class="icon-briefcase"></i> Database</label>
<input type="text" id="node-config-input-db" placeholder="test">
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb',{
category: 'config',
color:"rgb(218, 196, 180)",
defaults: {
hostname: { value:"127.0.0.1",required:true},
port: { value: 27017,required:true},
db: { value:"",required:true},
name: { value:"" }
},
label: function() {
return this.name||this.hostname+":"+this.port+"//"+this.db;
}
});
</script>
<script type="text/x-red" data-template-name="mongodb out">
<div class="form-row">
<label for="node-input-mongodb"><i class="icon-tag"></i> Server</label>
<input type="text" id="node-input-mongodb">
</div>
<div class="form-row">
<label for="node-input-collection"><i class="icon-briefcase"></i> Collection</label>
<input type="text" id="node-input-collection" placeholder="collection">
</div>
<div class="form-row">
<label for="node-input-operation"><i class="icon-wrench"></i> Operation</label>
<select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;">
<option value=store>save</option>
<option value=insert>insert</option>
<option value=delete>remove</option>
</select>
</div>
<div class="form-row node-input-payonly">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-payonly" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-payonly" style="width: 70%;">Only store msg.payload object ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<script>
$("#node-input-operation").change(function() {
var id = $("#node-input-operation option:selected").val();
if (id == "delete") $(".node-input-payonly").hide();
else $(".node-input-payonly").show();
});
</script>
</script>
<script type="text/x-red" data-help-name="mongodb out">
<p>A simple MongoDB output node. Stores the <b>msg</b> object in a chosen collection.</p>
<p>By default MongoDB creates an <i>_id</i> property as the primary key - so repeated injections of the same <b>msg</b> will result in many database entries.</p>
<p>If this is NOT the desired behaviour - ie you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p>
<p>This could be a unique constant or you could create one based on some other msg property.</p>
<p>Currently we do not limit or cap the collection size at all... this may well change.</p>
<p>You can also choose to <b>remove</b> items. To do so the <b>msg.payload</b> <i>MUST</i> contain an object that will select the items(s) to remove.
A blank object will delete <i>all of the objects</i> in the collection. You have been warned...</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb out',{
category: 'storage-output',
color:"rgb(218, 196, 180)",
defaults: {
mongodb: { type:"mongodb",required:true},
name: {value:""},
collection: {value:"",required:true},
payonly: {value:false},
operation: {value:"store"}
},
inputs:1,
outputs:0,
icon: "mongodb.png",
align: "right",
label: function() {
var mongoNode = RED.nodes.node(this.mongodb);
return this.name||(mongoNode?mongoNode.label()+"//"+this.collection:"mongodb");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="mongodb in">
<div class="form-row">
<label for="node-input-mongodb"><i class="icon-tag"></i> Server</label>
<input type="text" id="node-input-mongodb">
</div>
<div class="form-row">
<label for="node-input-collection"><i class="icon-briefcase"></i> Collection</label>
<input type="text" id="node-input-collection" placeholder="collection">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mongodb in">
<p>Queries a MongoDB collection by using the <b>msg.payload</b> to be a MongoDB query statement as per the .find() function.</p>
<p>You may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object and a <b>msg.limit</b> object.</p>
<p>All are optional - see the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB find docs</i></a> for examples.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb in',{
category: 'storage-input',
color:"rgb(218, 196, 180)",
defaults: {
mongodb: { type:"mongodb",required:true},
name: {value:""},
collection: {value:"",required:true}
},
inputs:1,
outputs:1,
icon: "mongodb.png",
label: function() {
var mongoNode = RED.nodes.node(this.mongodb);
return this.name||(mongoNode?mongoNode.label()+"//"+this.collection:"mongodb");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,125 @@
/**
* 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 mongo = require('mongodb');
function MongoNode(n) {
RED.nodes.createNode(this,n);
this.hostname = n.hostname;
this.port = n.port;
this.db = n.db;
this.name = n.name;
}
RED.nodes.registerType("mongodb",MongoNode);
function MongoOutNode(n) {
RED.nodes.createNode(this,n);
this.collection = n.collection;
this.mongodb = n.mongodb;
this.payonly = n.payonly || false;
this.operation = n.operation;
this.mongoConfig = RED.nodes.getNode(this.mongodb);
if (this.mongoConfig) {
var node = this;
this.clientDb = new mongo.Db(node.mongoConfig.db, new mongo.Server(node.mongoConfig.hostname, node.mongoConfig.port, {}), {w: 1});
this.clientDb.open(function(err,cli) {
if (err) { node.error(err); }
else {
node.clientDb.collection(node.collection,function(err,coll) {
if (err) { node.error(err); }
else {
node.on("input",function(msg) {
if (node.operation == "store") {
delete msg._topic;
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") {
delete msg._topic;
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);}});
}
if (node.operation == "delete") {
coll.remove(msg.payload, {w:1}, 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.mongoConfig = RED.nodes.getNode(this.mongodb);
if (this.mongoConfig) {
var node = this;
this.clientDb = new mongo.Db(node.mongoConfig.db, new mongo.Server(node.mongoConfig.hostname, node.mongoConfig.port, {}), {w: 1});
this.clientDb.open(function(err,cli) {
if (err) { node.error(err); }
else {
node.clientDb.collection(node.collection,function(err,coll) {
if (err) { node.error(err); }
else {
node.on("input",function(msg) {
msg.projection = msg.projection || {};
coll.find(msg.payload,msg.projection).sort(msg.sort).limit(msg.limit).toArray(function(err, items) {
if (err) { node.error(err); }
msg.payload = items;
delete msg.projection;
delete msg.sort;
delete msg.limit;
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);