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

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];
}
};