1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Ben Hardill 2013-10-13 17:33:19 +01:00
commit 74d8958526
30 changed files with 560 additions and 193 deletions

View File

@ -2,6 +2,8 @@
A visual tool for wiring the Internet of Things. A visual tool for wiring the Internet of Things.
![Screenshot](http://nodered.org/images/node-red-screenshot.png "Node-RED: A visual tool for wiring the Internet of Things")
## Quick Start ## Quick Start
Check out [INSTALL](INSTALL.md) for full instructions on getting started. Check out [INSTALL](INSTALL.md) for full instructions on getting started.
@ -16,6 +18,8 @@ Check out [INSTALL](INSTALL.md) for full instructions on getting started.
More documentation can be found [here](http://nodered.org/docs). More documentation can be found [here](http://nodered.org/docs).
For further help, or general discussion, there is also a [mailing list](https://groups.google.com/forum/#!forum/node-red).
## Browser Support ## Browser Support
The Node-RED editor runs in the browser. We routinely develop and test using The Node-RED editor runs in the browser. We routinely develop and test using
@ -28,9 +32,11 @@ list.
### Reporting issues ### Reporting issues
Please raise any bug reports or feature requests on the project's issue Please raise any bug reports on the project's [issue tracker](https://github.com/node-red/node-red/issues?state=open).
tracker. Be sure to search the list to see if your issue has already Be sure to search the list to see if your issue has already been raised.
been raised.
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red)
first.
### Creating new nodes ### Creating new nodes

View File

@ -17,6 +17,8 @@
<!-- Sample html file that corresponds to the 99-sample.js file --> <!-- Sample html file that corresponds to the 99-sample.js file -->
<!-- This creates and configures the onscreen elements of the node --> <!-- This creates and configures the onscreen elements of the node -->
<!-- If you use this as a template, replace IBM Corp. with your own name. -->
<!-- First, the content of the edit dialog is defined. --> <!-- First, the content of the edit dialog is defined. -->
<script type="text/x-red" data-template-name="sample"> <script type="text/x-red" data-template-name="sample">
@ -38,7 +40,6 @@
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
</script> </script>

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
**/ **/
// If you use this as a template, replace IBM Corp. with your own name.
// Sample Node-RED node file // Sample Node-RED node file
// Require main module // Require main module
@ -32,20 +34,19 @@ function SampleNode(n) {
// this message once at startup... // this message once at startup...
// Look at other real nodes for some better ideas of what to do.... // Look at other real nodes for some better ideas of what to do....
var msg = {}; var msg = {};
msg.topic = node.topic; msg.topic = this.topic;
msg.payload = "Hello world !" msg.payload = "Hello world !"
// send out the message to the rest of the workspace. // send out the message to the rest of the workspace.
this.send(msg); this.send(msg);
this.on("close", function() {
// Called when the node is shutdown - eg on redeploy.
// Allows ports to be closed, connections dropped etc.
// eg: this.client.disconnect();
});
} }
// Register the node by name. This must be called before overriding any of the // Register the node by name. This must be called before overriding any of the
// Node functions. // Node functions.
RED.nodes.registerType("sample",SampleNode); RED.nodes.registerType("sample",SampleNode);
SampleNode.prototype.close = function() {
// Called when the node is shutdown - eg on redeploy.
// Allows ports to be closed, connections dropped etc.
// eg: this.client.disconnect();
}

View File

@ -37,7 +37,7 @@
category: 'advanced-function', category: 'advanced-function',
color:"#E6E0F8", color:"#E6E0F8",
defaults: { defaults: {
useEyes: {value:"false"}, useEyes: {value:false},
name: {value:""}, name: {value:""},
}, },
inputs:1, inputs:1,

View File

@ -202,7 +202,7 @@
}, },
oneditprepare: function() { oneditprepare: function() {
var repeattype = "none"; var repeattype = "none";
if (Number(this.repeat) != 0) { if (this.repeat != "") {
repeattype = "interval"; repeattype = "interval";
$("#inject-time-interval-units option").filter(function() {return $(this).val() == "s";}).attr('selected',true); $("#inject-time-interval-units option").filter(function() {return $(this).val() == "s";}).attr('selected',true);
$("#inject-time-interval-count").val(this.repeat); $("#inject-time-interval-count").val(this.repeat);
@ -210,7 +210,7 @@
} else if (this.crontab) { } else if (this.crontab) {
var cronparts = this.crontab.split(" "); var cronparts = this.crontab.split(" ");
var days = cronparts[4]; var days = cronparts[4];
if (Number(cronparts[0]) && Number(cronparts[1])) { if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) {
repeattype = "time"; repeattype = "time";
// Fixed time // Fixed time
var time = cronparts[1]+":"+cronparts[0]; var time = cronparts[1]+":"+cronparts[0];

View File

@ -21,7 +21,7 @@
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-func"><i class="icon-wrench"></i> Function</label> <label for="node-input-func"><i class="icon-wrench"></i> Function</label>
<input type="hidden" id="node-input-func"> <input type="hidden" id="node-input-func" autofocus="autofocus">
<div style="height: 250px;" class="node-text-editor" id="node-input-func-editor" ></div> <div style="height: 250px;" class="node-text-editor" id="node-input-func-editor" ></div>
</div> </div>
<div class="form-row"> <div class="form-row">
@ -85,6 +85,7 @@
editor:that.editor, // the field name the main text body goes to editor:that.editor, // the field name the main text body goes to
fields:['name','outputs'] fields:['name','outputs']
}); });
$("#node-input-name").focus();
}); });
}, },

View File

@ -22,7 +22,7 @@
<div class="form-row"> <div class="form-row">
<label for="node-input-template"><i class="icon-wrench"></i> Template</label> <label for="node-input-template"><i class="icon-wrench"></i> Template</label>
<input type="hidden" id="node-input-template"> <input type="hidden" id="node-input-template" autofocus="autofocus">
<div style="height: 250px;" class="node-text-editor" id="node-input-template-editor" ></div> <div style="height: 250px;" class="node-text-editor" id="node-input-template-editor" ></div>
</div> </div>
@ -79,7 +79,7 @@
editor:that.editor, // the field name the main text body goes to editor:that.editor, // the field name the main text body goes to
fields:['name'] fields:['name']
}); });
$("#node-input-name").focus();
}); });
}, },
oneditsave: function() { oneditsave: function() {

View File

@ -21,7 +21,7 @@
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-info" style="width: 100% !important;"><i class="icon-file"></i> More</label> <label for="node-input-info" style="width: 100% !important;"><i class="icon-file"></i> More</label>
<input type="hidden" id="node-input-info"> <input type="hidden" id="node-input-info" autofocus="autofocus">
<div style="height: 250px;" class="node-text-editor" id="node-input-info-editor" ></div> <div style="height: 250px;" class="node-text-editor" id="node-input-info-editor" ></div>
</div> </div>
<div class="form-tips">Tip: this isn't meant for War and Peace - but useful notes can be kept here.</div> <div class="form-tips">Tip: this isn't meant for War and Peace - but useful notes can be kept here.</div>
@ -75,6 +75,7 @@
showFoldingRuler:false, showFoldingRuler:false,
contents: $("#node-input-info").val() contents: $("#node-input-info").val()
}); });
$("#node-input-name").focus();
}); });
}, },
oneditsave: function() { oneditsave: function() {

View File

@ -138,7 +138,7 @@
category: 'config', category: 'config',
defaults: { defaults: {
//baud: {baud:"57600",required:true}, //baud: {baud:"57600",required:true},
repeat: {value:"25",required:true,validate:RED.validators.number()}, repeat: {value:"50",required:true,validate:RED.validators.number()},
device: {value:"",required:true} device: {value:"",required:true}
}, },
label: function() { label: function() {

View File

@ -25,46 +25,46 @@ function ArduinoNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.device = n.device; this.device = n.device;
this.repeat = n.repeat||25; this.repeat = n.repeat||25;
util.log("[firmata] Opening"+this.device); util.log("[firmata] Opening "+this.device);
var node = this;
// var tou = setInterval(function() { node.toun = setInterval(function() {
// if (!arduinoReady) { if (!arduinoReady) {
// clearInterval(tou);
arduinoReady = false;
if (thisboard == null) { if (thisboard == null) {
this.board = new firmata.Board(this.device, function(err) { node.board = new firmata.Board(node.device, function(err) {
if (err) { if (err) {
util.log("[firmata] "+err); console.log("[firmata] error: ",err);
return; return;
} }
arduinoReady = true; arduinoReady = true;
thisboard = node.board;
clearInterval(node.toun);
util.log('[firmata] Arduino connected'); util.log('[firmata] Arduino connected');
}); });
thisboard = this.board;
} }
else { else {
util.log("[firmata] Arduino already connected"); node.board = thisboard;
this.board = thisboard; node.board.removeAllListeners();
console.log(this.board._events);
this.board.removeAllListeners();
arduinoReady = true; 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
// } else { util.log("[firmata] Waiting for Firmata"); } this.on('close', function() {
// }, 1000); // wait for firmata to disconnect from arduino
this._close = function() {
//this.board.sp.close(function() { console.log("[firmata] Serial port closed"); arduinoReady = false; }); //this.board.sp.close(function() { console.log("[firmata] Serial port closed"); arduinoReady = false; });
util.log("[firmata] Stopped"); 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); RED.nodes.registerType("arduino-board",ArduinoNode);
ArduinoNode.prototype.close = function() {
this._close();
}
// The Input Node // The Input Node
function DuinoNodeIn(n) { function DuinoNodeIn(n) {
@ -79,10 +79,12 @@ function DuinoNodeIn(n) {
this.repeat = this.serverConfig.repeat; this.repeat = this.serverConfig.repeat;
var node = this; var node = this;
var tout = setInterval(function() { node.toui = setInterval(function() {
if (arduinoReady) { if (thisboard != null) {
clearInterval(tout); node.board = thisboard;
console.log(node.state,node.pin,node.board.MODES[node.state]); clearInterval(node.toui);
node.toui = false;
//console.log(node.state,node.pin,node.board.MODES[node.state]);
node.board.pinMode(node.pin, node.board.MODES[node.state]); node.board.pinMode(node.pin, node.board.MODES[node.state]);
node.board.setSamplingInterval(node.repeat); node.board.setSamplingInterval(node.repeat);
var oldrdg = ""; var oldrdg = "";
@ -103,23 +105,21 @@ function DuinoNodeIn(n) {
} }
} }
else { node.log("Waiting for Arduino"); } else { node.log("Waiting for Arduino"); }
}, 2000); // loop to wait for firmata to connect to arduino }, 5000); // loop to wait for firmata to connect to arduino
this._close = function() { this.on('close', function() {
clearInterval(this._interval); if (node.toui) {
util.log("[arduino] input eventlistener stopped"); clearInterval(node.toui);
util.log("[firmata] input wait loop stopped");
} }
});
} }
else { else {
util.log("[arduino] Serial Port not Configured"); util.log("[firmata] Serial Port not Configured");
} }
} }
RED.nodes.registerType("arduino in",DuinoNodeIn); RED.nodes.registerType("arduino in",DuinoNodeIn);
DuinoNodeIn.prototype.close = function() {
this._close();
}
// The Output Node // The Output Node
function DuinoNodeOut(n) { function DuinoNodeOut(n) {
@ -135,7 +135,7 @@ function DuinoNodeOut(n) {
this.on("input", function(msg) { this.on("input", function(msg) {
//console.log(msg); //console.log(msg);
if (arduinoReady) { if (thisboard != null) {
if (node.state == "OUTPUT") { if (node.state == "OUTPUT") {
if ((msg.payload == true)||(msg.payload == 1)||(msg.payload.toString().toLowerCase() == "on")) { if ((msg.payload == true)||(msg.payload == 1)||(msg.payload.toString().toLowerCase() == "on")) {
node.board.digitalWrite(node.pin, node.board.HIGH); node.board.digitalWrite(node.pin, node.board.HIGH);
@ -162,16 +162,25 @@ function DuinoNodeOut(n) {
//else { console.log("Arduino not ready"); } //else { console.log("Arduino not ready"); }
}); });
var touo = setInterval(function() { node.touo = setInterval(function() {
if (arduinoReady) { if (thisboard != null) {
clearInterval(touo); clearInterval(node.touo);
//console.log(node.state,node.pin,node.board.MODES[node.state]); node.touo = false;
node.board = thisboard;
node.board.pinMode(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 }, 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 { else {
util.log("[arduino] Serial Port not Configured"); util.log("[firmata] Serial Port not Configured");
} }
} }
RED.nodes.registerType("arduino out",DuinoNodeOut); RED.nodes.registerType("arduino out",DuinoNodeOut);

View File

@ -57,7 +57,7 @@ RED.nodes.registerType("http in",HTTPIn);
function HTTPOut(n) { function HTTPOut(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
var node = this;
this.on("input",function(msg) { this.on("input",function(msg) {
if (msg.res) { if (msg.res) {
if (msg.headers) { if (msg.headers) {
@ -65,6 +65,8 @@ function HTTPOut(n) {
} }
var statusCode = msg.statusCode || 200; var statusCode = msg.statusCode || 200;
msg.res.send(statusCode,msg.payload); msg.res.send(statusCode,msg.payload);
} else {
node.warn("No response object");
} }
}); });
} }
@ -80,9 +82,9 @@ function HTTPRequest(n) {
this.on("input",function(msg) { this.on("input",function(msg) {
var opts = urllib.parse(msg.url||url); var opts = urllib.parse(msg.url||url);
opts.method = msg.method||method; opts.method = (msg.method||method).toUpperCase();
if (msg.headers) { if (msg.headers) {
opts.header = headers; opts.header = msg.headers;
} }
var req = httplib.request(opts,function(res) { var req = httplib.request(opts,function(res) {
res.setEncoding('utf8'); res.setEncoding('utf8');
@ -103,7 +105,7 @@ function HTTPRequest(n) {
msg.statusCode = err.code; msg.statusCode = err.code;
node.send(msg); node.send(msg);
}); });
if (msg.payload) { if (msg.payload && (method == "PUSH" || method == "PUT") ) {
if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) { if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
req.write(msg.payload); req.write(msg.payload);
} else if (typeof msg.payload == "number") { } else if (typeof msg.payload == "number") {

View File

@ -47,7 +47,7 @@ function TcpIn(n) {
data = data.toString(node.datatype); data = data.toString(node.datatype);
} }
if (node.stream) { if (node.stream) {
if ((typeof data) === "string" && node.newline != "") { if ((node.datatype) === "utf8" && node.newline != "") {
buffer = buffer+data; buffer = buffer+data;
var parts = buffer.split(node.newline); var parts = buffer.split(node.newline);
for (var i = 0;i<parts.length-1;i+=1) { for (var i = 0;i<parts.length-1;i+=1) {
@ -150,4 +150,3 @@ RED.nodes.registerType("tcp in",TcpIn);
TcpIn.prototype.close = function() { TcpIn.prototype.close = function() {
this._close(); this._close();
} }

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

@ -0,0 +1,176 @@
<!--
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.fromip</b> in the form ipaddress:port .</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>
<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>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 broadcast.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('udp out',{
category: 'output',
color:"Silver",
defaults: {
name: {value:""},
addr: {value:"",required:true},
//group: {value:""},
iface: {value:""},
port: {value:"",required:true,validate:RED.validators.number()},
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>

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

@ -0,0 +1,115 @@
/**
* 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("../../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);
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 }; }
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) {
//console.log("UDP:",msg.payload);
var message;
if (node.base64) { message = new Buffer(b64string, 'base64'); }
else { message = new Buffer(""+msg.payload); }
console.log("UDP send :",node.addr,node.port);
sock.send(message, 0, message.length, node.port, node.addr, 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

@ -30,7 +30,7 @@
} }
var callback = encodeURIComponent(location.protocol+"//"+location.hostname+":"+location.port+pathname+"twitter/"+twitterConfigNodeId+"/auth/callback"); 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">here</a> to authenticate with Twitter.'); $("#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() { $("#node-config-twitter-start").click(function() {
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000); twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
}); });
@ -103,33 +103,32 @@
<script type="text/x-red" data-template-name="twitter in"> <script type="text/x-red" data-template-name="twitter in">
<div class="form-row"> <div class="form-row">
<label for="node-input-twitter"><i class="icon-user"></i> Twitter</label> <label for="node-input-twitter"><i class="icon-user"></i> Log in as</label>
<input type="text" id="node-input-twitter"> <input type="text" id="node-input-twitter">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-tags"><i class="icon-tag"></i> Tags</label> <label for="node-input-user"><i class="icon-search"></i> Search</label>
<input type="text" id="node-input-tags" placeholder="comma-separated tags"> <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>
<div class="form-row"> <div class="form-row">
<label>&nbsp;</label> <label for="node-input-tags"><i class="icon-tags"></i> for</label>
<input type="checkbox" id="node-input-user" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;"> <input type="text" id="node-input-tags" placeholder="comma-separated words, @ids, #tags">
<label for="node-input-user" style="width: 70%;">Tick to use user stream<br/>(rather than status/filter)</label>
</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>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
<div class="form-tips">Tip: the Senders name gets appended to the topic heirarchy <div class="form-tips">Tip: Use commas without spaces between multiple search terms. Comma = OR, Space = AND.
</div> <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>
<script type="text/x-red" data-help-name="twitter in"> <script type="text/x-red" data-help-name="twitter in">
<p>Twitter input node. Watches the public stream for tweets containing the configured search term.</p> <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 the configured topic and then appends the senders screen name.</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.location</b> to the tweeters location if known.</p>
</script> </script>
@ -140,7 +139,7 @@
defaults: { defaults: {
twitter: {type:"twitter-credentials",required:true}, twitter: {type:"twitter-credentials",required:true},
tags: {value:"",required:true}, tags: {value:"",required:true},
user: {value:false}, user: {value:"false",required:true},
name: {value:""}, name: {value:""},
topic: {value:"tweets"} topic: {value:"tweets"}
}, },

View File

@ -24,14 +24,13 @@ function TwitterNode(n) {
} }
RED.nodes.registerType("twitter-credentials",TwitterNode); RED.nodes.registerType("twitter-credentials",TwitterNode);
function TwitterInNode(n) { function TwitterInNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.active = true; this.active = true;
this.user = n.user; this.user = n.user;
this.tags = n.tags.replace(/ /g,''); this.tags = n.tags.replace(/ /g,'');
this.twitter = n.twitter; this.twitter = n.twitter;
this.topic = n.topic; this.topic = n.topic||"tweets";
this.twitterConfig = RED.nodes.getNode(this.twitter); this.twitterConfig = RED.nodes.getNode(this.twitter);
var credentials = RED.nodes.getCredentials(this.twitter); var credentials = RED.nodes.getCredentials(this.twitter);
@ -47,11 +46,12 @@ function TwitterInNode(n) {
if (this.tags !== "") { if (this.tags !== "") {
try { try {
var thing = 'statuses/filter'; var thing = 'statuses/filter';
if (this.user) { thing = 'user'; } if (this.user == "true") { thing = 'user'; }
function setupStream() { function setupStream() {
if (node.active) { if (node.active) {
twit.stream(thing, { track: [node.tags] }, function(stream) { twit.stream(thing, { track: [node.tags] }, function(stream) {
//twit.stream('user', { 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) { //twit.stream('statuses/filter', { track: [node.tags] }, function(stream) {
node.stream = stream; node.stream = stream;
stream.on('data', function(tweet) { stream.on('data', function(tweet) {
@ -101,8 +101,6 @@ TwitterInNode.prototype.close = function() {
} }
} }
function TwitterOutNode(n) { function TwitterOutNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.topic = n.topic; this.topic = n.topic;

View File

@ -16,7 +16,7 @@
<script type="text/x-red" data-template-name="notify"> <script type="text/x-red" data-template-name="notify">
<div class="form-row"> <div class="form-row">
<label for="node-input-title"><i class="icon-tag"></i> Title</label> <label for="node-input-title"><i class="icon-flag"></i> Title</label>
<input type="text" id="node-input-title" placeholder="Node-RED"> <input type="text" id="node-input-title" placeholder="Node-RED">
</div> </div>
<div class="form-row"> <div class="form-row">

View File

@ -37,7 +37,7 @@ if (pushkey) {
function ProwlNode(n) { function ProwlNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.title = n.title; this.title = n.title;
this.priority = n.priority * 1; this.priority = parseInt(n.priority);
if (this.priority > 2) this.priority = 2; if (this.priority > 2) this.priority = 2;
if (this.priority < -2) this.priority = -2; if (this.priority < -2) this.priority = -2;
var node = this; var node = this;

View File

@ -65,12 +65,12 @@
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"> <input type="text" id="node-input-name" placeholder="Name">
</div> </div>
<div class="form-tips">Sending complete object will stringify the whole msg object before sending.</div> <div class="form-tips">Sending the complete object will stringify the whole msg object before sending.</div>
</script> </script>
<script type="text/x-red" data-help-name="irc out"> <script type="text/x-red" data-help-name="irc out">
<p>Connects to a channel on an IRC server</p> <p>Sends messages to a channel on an IRC server</p>
<p>If you send something with NO msg.topic it will go to the channel - otherwise it will go to the id in the <b>msg.topic</b> field.</p> <p>If you send something with NO <b>msg.topic</b> it will go to the configured channel - otherwise it will go to the id in the <b>msg.topic</b> field.</p>
<p>You can either just send the <b>msg.payload</b>, or you can send the complete <b>msg</b> object.</p> <p>You can either just send the <b>msg.payload</b>, or you can send the complete <b>msg</b> object.</p>
</script> </script>
@ -103,7 +103,7 @@
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-channel"><i class="icon-tasks"></i> Channel</label> <label for="node-config-input-channel"><i class="icon-tasks"></i> Channel</label>
<input type="text" id="node-config-input-channel" placeholder="#testing1234"> <input type="text" id="node-config-input-channel" placeholder="#node-red">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-nickname"><i class="icon-tasks"></i> Nickname</label> <label for="node-config-input-nickname"><i class="icon-tasks"></i> Nickname</label>

View File

@ -16,6 +16,7 @@
var RED = require("../../red/red"); var RED = require("../../red/red");
var irc = require("irc"); var irc = require("irc");
var util = require("util");
// The Server Definition - this opens (and closes) the connection // The Server Definition - this opens (and closes) the connection
function IRCServerNode(n) { function IRCServerNode(n) {
@ -23,40 +24,38 @@ function IRCServerNode(n) {
this.server = n.server; this.server = n.server;
this.channel = n.channel; this.channel = n.channel;
this.nickname = n.nickname; this.nickname = n.nickname;
this.ircclient = new irc.Client(this.server, this.nickname, { this.ircclient = null;
channels: [this.channel] this.on("close", function() {
}); if (this.ircclient != null) {
this._close = function() {
this.ircclient.disconnect(); this.ircclient.disconnect();
} }
});
} }
RED.nodes.registerType("irc-server",IRCServerNode); RED.nodes.registerType("irc-server",IRCServerNode);
IRCServerNode.prototype.close = function() {
this._close();
}
// The Input Node // The Input Node
function IrcInNode(n) { function IrcInNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.ircserver = n.ircserver; this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.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; this.ircclient = this.serverConfig.ircclient;
var node = this; var node = this;
this.ircclient.addListener('message', function (from, to, message) { this.ircclient.addListener('message', function (from, to, message) {
console.log(from + ' => ' + to + ': ' + message); console.log(from + ' => ' + to + ': ' + message);
var msg = { "topic":from, "to":to, "payload":message }; var msg = { "topic":from, "to":to, "payload":message };
node.send(msg); node.send(msg);
}); });
this.ircclient.addListener('error', function(message) {
node.error(JSON.stringify(message));
});
} }
RED.nodes.registerType("irc in",IrcInNode); RED.nodes.registerType("irc in",IrcInNode);
@ -66,12 +65,20 @@ function IrcOutNode(n) {
this.sendAll = n.sendObject; this.sendAll = n.sendObject;
this.ircserver = n.ircserver; this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.ircserver); this.serverConfig = RED.nodes.getNode(this.ircserver);
this.ircclient = this.serverConfig.ircclient;
this.channel = this.serverConfig.channel; 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; var node = this;
this.on("input", function(msg) { this.on("input", function(msg) {
console.log(msg); //console.log(msg,node.channel);
if (node.sendAll) { if (node.sendAll) {
node.ircclient.say(node.channel, JSON.stringify(msg)); node.ircclient.say(node.channel, JSON.stringify(msg));
} }

View File

@ -14,8 +14,17 @@
* limitations under the License. * limitations under the License.
**/ **/
var orig=console.warn;
console.warn=(function() { // suppress warning from stringprep when not needed)
var orig=console.warn;
return function() {
//orig.apply(console, arguments);
};
})();
var RED = require("../../red/red"); var RED = require("../../red/red");
var xmpp = require('simple-xmpp'); var xmpp = require('simple-xmpp');
console.warn = orig;
try { try {
var xmppkey = require("../../settings").xmpp || require("../../../xmppkeys.js"); var xmppkey = require("../../settings").xmpp || require("../../../xmppkeys.js");
@ -97,17 +106,13 @@ function XmppNode(n) {
} }
}); });
this._close = function() { this.on("close", function() {
xmpp.setPresence('offline'); xmpp.setPresence('offline');
//xmpp.conn.end(); //xmpp.conn.end();
// TODO - DCJ NOTE... this is not good. It leaves the connection up over a restart - which will end up with bad things happening... // TODO - DCJ NOTE... this is not good. It leaves the connection up over a restart - which will end up with bad things happening...
// (but requires the underlying xmpp lib to be fixed (which does have an open bug request on fixing the close method)). // (but requires the underlying xmpp lib to be fixed (which does have an open bug request on fixing the close method)).
this.warn("Due to an underlying bug in the xmpp library this does not disconnect old sessions. This is bad... A restart would be better."); this.warn("Due to an underlying bug in the xmpp library this does not disconnect old sessions. This is bad... A restart would be better.");
} });
} }
RED.nodes.registerType("xmpp",XmppNode); RED.nodes.registerType("xmpp",XmppNode);
XmppNode.prototype.close = function() {
this._close();
}

BIN
public/icons/bluetooth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -143,8 +143,34 @@ RED.editor = function() {
var changes = {}; var changes = {};
var changed = false; var changed = false;
var wasDirty = RED.view.dirty(); var wasDirty = RED.view.dirty();
if (editing_node._def.oneditsave) { if (editing_node._def.oneditsave) {
var oldValues = {};
for (var d in editing_node._def.defaults) {
if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") {
oldValues[d] = editing_node[d];
} else {
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
}
}
editing_node._def.oneditsave.call(editing_node); editing_node._def.oneditsave.call(editing_node);
for (var d in editing_node._def.defaults) {
if (oldValues[d] === null || typeof oldValues[d] === "string" || typeof oldValues[d] === "number") {
if (oldValues[d] !== editing_node[d]) {
changes[d] = oldValues[d];
changed = true;
}
} else {
if (JSON.stringify(oldValues[d]) !== JSON.stringify(editing_node[d])) {
changes[d] = oldValues[d];
changed = true;
}
}
}
} }
if (editing_node._def.defaults) { if (editing_node._def.defaults) {
@ -177,7 +203,6 @@ RED.editor = function() {
changes[d] = editing_node[d]; changes[d] = editing_node[d];
editing_node[d] = newValue; editing_node[d] = newValue;
changed = true; changed = true;
RED.view.dirty(true);
} }
} }
} }
@ -185,6 +210,7 @@ RED.editor = function() {
var removedLinks = updateNodeProperties(editing_node); var removedLinks = updateNodeProperties(editing_node);
if (changed) { if (changed) {
RED.view.dirty(true);
RED.history.push({t:'edit',node:editing_node,changes:changes,links:removedLinks,dirty:wasDirty}); RED.history.push({t:'edit',node:editing_node,changes:changes,links:removedLinks,dirty:wasDirty});
} }

View File

@ -63,7 +63,10 @@ RED.palette = function() {
container:'body', container:'body',
content: $(($("script[data-help-name|='"+nt+"']").html()||"<p>no information available</p>").trim())[0] content: $(($("script[data-help-name|='"+nt+"']").html()||"<p>no information available</p>").trim())[0]
}); });
$(d).click(function() {
var help = '<div class="node-help">'+($("script[data-help-name|='"+d.type+"']").html()||"")+"</div>";
$("#tab-info").html(help);
});
$(d).draggable({ $(d).draggable({
helper: 'clone', helper: 'clone',
appendTo: 'body', appendTo: 'body',

View File

@ -649,7 +649,13 @@ RED.view = function() {
//mainRect.on("touchend",nodeMouseUp); //mainRect.on("touchend",nodeMouseUp);
if (d._def.icon) { if (d._def.icon) {
var icon = node.append("image").attr("xlink:href","icons/"+d._def.icon).attr("class","node_icon").attr("x",0).attr("y",0).attr("width","15").attr("height",function(d){return Math.min(50,d.h);}); var icon = node.append("image")
.attr("xlink:href","icons/"+d._def.icon)
.attr("class","node_icon")
.attr("x",0).attr("y",function(d){return (d.h-Math.min(50,d.h))/2;})
.attr("width","15")
.attr("height", function(d){return Math.min(50,d.h);});
if (d._def.align) { if (d._def.align) {
icon.attr('class','node_icon node_icon_'+d._def.align); icon.attr('class','node_icon node_icon_'+d._def.align);
} }
@ -747,7 +753,7 @@ RED.view = function() {
var port = d3.select(this); var port = d3.select(this);
port.attr("y",function(d){return (d.h/2)-5;}) port.attr("y",function(d){return (d.h/2)-5;})
}); });
thisNode.selectAll(".node_icon").attr("height",function(d){return Math.min(50,d.h);}); thisNode.selectAll(".node_icon").attr("height",function(d){return Math.min(50,d.h);}).attr("y",function(d){return (d.h-Math.min(50,d.h))/2;});
thisNode.selectAll('.node_right_button_group').attr("transform",function(d){return "translate("+(d.w-100)+","+0+")";}); thisNode.selectAll('.node_right_button_group').attr("transform",function(d){return "translate("+(d.w-100)+","+0+")";});
thisNode.selectAll('.node_right_button').attr("transform",function(d){return "translate("+(d.w-100)+","+0+")";}).attr("fill",function(d) { thisNode.selectAll('.node_right_button').attr("transform",function(d){return "translate("+(d.w-100)+","+0+")";}).attr("fill",function(d) {

26
red.js
View File

@ -21,7 +21,6 @@ var crypto = require("crypto");
var settings = require("./settings"); var settings = require("./settings");
var RED = require("./red/red.js"); var RED = require("./red/red.js");
var server; var server;
var app = express(); var app = express();
@ -49,11 +48,30 @@ if (settings.httpAuth) {
); );
} }
settings.flowFile = process.argv[2] || settings.flowFile;
var red = RED.init(server,settings); var red = RED.init(server,settings);
app.use(settings.httpRoot,red); app.use(settings.httpRoot,red);
server.listen(settings.uiPort);
RED.start(); RED.start();
util.log('[red] Server now running at http'+(settings.https?'s':'')+'://127.0.0.1:'+settings.uiPort+settings.httpRoot);
server.listen(settings.uiPort,function() {
util.log('[red] Server now running at http'+(settings.https?'s':'')+'://127.0.0.1:'+settings.uiPort+settings.httpRoot);
});
process.on('uncaughtException',function(err) {
if (err.errno === "EADDRINUSE") {
util.log('[red] Unable to listen on http'+(settings.https?'s':'')+'://127.0.0.1:'+settings.uiPort+settings.httpRoot);
util.log('[red] Error: port in use');
} else {
util.log('[red] Uncaught Exception:');
util.log(err.stack);
}
process.exit(1);
});
process.on('SIGINT', function () {
RED.stop();
util.log('[red] Exiting Node-RED. Thank you.');
process.exit();
});

View File

@ -42,7 +42,6 @@ function getCallerFilename(type) {
return stack[0].getFileName(); return stack[0].getFileName();
} }
var registry = (function() { var registry = (function() {
var nodes = {}; var nodes = {};
var logHandlers = []; var logHandlers = [];
@ -91,7 +90,6 @@ var node_type_registry = (function() {
var obj = { var obj = {
register: function(type,node) { register: function(type,node) {
util.inherits(node, Node); util.inherits(node, Node);
var callerFilename = getCallerFilename(type); var callerFilename = getCallerFilename(type);
if (callerFilename == null) { if (callerFilename == null) {
util.log("["+type+"] unable to determine filename"); util.log("["+type+"] unable to determine filename");
@ -117,7 +115,6 @@ var node_type_registry = (function() {
result += node_configs[nt]; result += node_configs[nt];
} }
return result; return result;
} }
} }
return obj; return obj;
@ -176,7 +173,6 @@ Node.prototype.send = function(msg) {
} }
module.exports.Node = Node; module.exports.Node = Node;
Node.prototype.receive = function(msg) { Node.prototype.receive = function(msg) {
this.emit("input",msg); this.emit("input",msg);
} }
@ -197,9 +193,6 @@ Node.prototype.error = function(msg) {
this.emit("log",o); this.emit("log",o);
} }
var credentials = {}; var credentials = {};
var credentialsFile = "credentials.json"; var credentialsFile = "credentials.json";
if (fs.existsSync(credentialsFile)) { if (fs.existsSync(credentialsFile)) {
@ -225,8 +218,6 @@ module.exports.deleteCredentials = function(id) {
delete credentials[id]; delete credentials[id];
saveCredentialsFile(); saveCredentialsFile();
} }
module.exports.createNode = function(node,def) { module.exports.createNode = function(node,def) {
Node.call(node,def); Node.call(node,def);
} }
@ -257,12 +248,9 @@ module.exports.load = function() {
}); });
} }
loadNodes(__dirname+"/../nodes"); loadNodes(__dirname+"/../nodes");
//events.emit("nodes-loaded"); //events.emit("nodes-loaded");
} }
var activeConfig = null; var activeConfig = null;
var missingTypes = []; var missingTypes = [];
@ -279,10 +267,15 @@ events.on('type-registered',function(type) {
} }
}); });
module.exports.getNode = function(nid) { module.exports.getNode = function(nid) {
return registry.get(nid); return registry.get(nid);
} }
module.exports.closedown = function() {
util.log("[red] Closing Down Nodes");
registry.clear();
}
module.exports.setConfig = function(conf) { module.exports.setConfig = function(conf) {
if (activeConfig&&activeConfig.length > 0) { if (activeConfig&&activeConfig.length > 0) {
util.log("[red] Stopping flows"); util.log("[red] Stopping flows");
@ -293,7 +286,6 @@ module.exports.setConfig = function(conf) {
} }
var parseConfig = function() { var parseConfig = function() {
missingTypes = []; missingTypes = [];
for (var i in activeConfig) { for (var i in activeConfig) {
var type = activeConfig[i].type; var type = activeConfig[i].type;
@ -307,7 +299,6 @@ var parseConfig = function() {
for (var i in missingTypes) { for (var i in missingTypes) {
util.log("[red] - "+missingTypes[i]); util.log("[red] - "+missingTypes[i]);
} }
return; return;
} }
@ -329,7 +320,6 @@ var parseConfig = function() {
util.log("[red] unknown type: "+activeConfig[i].type); util.log("[red] unknown type: "+activeConfig[i].type);
} }
} }
// Clean up any orphaned credentials // Clean up any orphaned credentials
var deletedCredentials = false; var deletedCredentials = false;
for (var c in credentials) { for (var c in credentials) {
@ -343,5 +333,4 @@ var parseConfig = function() {
saveCredentialsFile(); saveCredentialsFile();
} }
events.emit("nodes-started"); events.emit("nodes-started");
} }

View File

@ -33,10 +33,10 @@ var RED = {
}, },
start: server.start, start: server.start,
nodes: nodes, nodes: nodes,
library: library, library: library,
events: events events: events,
stop: nodes.closedown,
}; };
RED.__defineGetter__("app", function() { return server.app }); RED.__defineGetter__("app", function() { return server.app });

View File

@ -18,9 +18,9 @@ var fs = require('fs');
var util = require('util'); var util = require('util');
var createUI = require("./ui"); var createUI = require("./ui");
var redNodes = require("./nodes"); var redNodes = require("./nodes");
var host = require('os').hostname();
//TODO: relocated user dir //TODO: relocated user dir
var rulesfile = process.argv[2] || 'flows_'+host+'.json';
var flowfile = '';
var app = null; var app = null;
var server = null; var server = null;
@ -29,6 +29,8 @@ function createServer(_server,settings) {
server = _server; server = _server;
app = createUI(settings); app = createUI(settings);
flowfile = settings.flowFile || 'flows_'+require('os').hostname()+'.json';
//TODO: relocated user dir //TODO: relocated user dir
fs.exists("lib/",function(exists) { fs.exists("lib/",function(exists) {
if (!exists) { if (!exists) {
@ -43,9 +45,9 @@ function createServer(_server,settings) {
}); });
app.get("/flows",function(req,res) { app.get("/flows",function(req,res) {
fs.exists(rulesfile, function (exists) { fs.exists(flowfile, function (exists) {
if (exists) { if (exists) {
res.sendfile(rulesfile); res.sendfile(flowfile);
} else { } else {
res.writeHead(200, {'Content-Type': 'text/plain'}); res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("[]"); res.write("[]");
@ -62,7 +64,7 @@ function createServer(_server,settings) {
req.on('end', function() { req.on('end', function() {
res.writeHead(204, {'Content-Type': 'text/plain'}); res.writeHead(204, {'Content-Type': 'text/plain'});
res.end(); res.end();
fs.writeFile(rulesfile, fullBody, function(err) { fs.writeFile(flowfile, fullBody, function(err) {
if(err) { if(err) {
util.log(err); util.log(err);
} else { } else {
@ -87,14 +89,14 @@ function start() {
util.log("------------------------------------------"); util.log("------------------------------------------");
fs.exists(rulesfile, function (exists) { fs.exists(flowfile, function (exists) {
if (exists) { if (exists) {
util.log("[red] Loading flows : "+rulesfile); util.log("[red] Loading flows : "+flowfile);
fs.readFile(rulesfile,'utf8',function(err,data) { fs.readFile(flowfile,'utf8',function(err,data) {
redNodes.setConfig(JSON.parse(data)); redNodes.setConfig(JSON.parse(data));
}); });
} else { } else {
util.log("[red] Flows file not found : "+rulesfile); util.log("[red] Flows file not found : "+flowfile);
} }
}); });
} }

View File

@ -19,6 +19,9 @@ module.exports = {
serialReconnectTime: 15000, serialReconnectTime: 15000,
debugMaxLength: 1000, debugMaxLength: 1000,
// The file containing the flows. If not set, it defaults to flows_<hostname>.json
//flowFile: 'flows.json',
// You can protect the user interface with a userid and password by using the following property // You can protect the user interface with a userid and password by using the following property
// the password must be an md5 hash eg.. 5f4dcc3b5aa765d61d8327deb882cf99 ('password') // the password must be an md5 hash eg.. 5f4dcc3b5aa765d61d8327deb882cf99 ('password')
//httpAuth: {user:"user",pass:"5f4dcc3b5aa765d61d8327deb882cf99"}, //httpAuth: {user:"user",pass:"5f4dcc3b5aa765d61d8327deb882cf99"},