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:
commit
a4fb33ee73
@ -30,7 +30,7 @@ From the top-level directory, run:
|
|||||||
|
|
||||||
You can then access Node-RED at <http://localhost:1880>.
|
You can then access Node-RED at <http://localhost:1880>.
|
||||||
|
|
||||||
Online documentation is available at <http://node-red.github.io/docs>.
|
Online documentation is available at <http://nodered.org/docs>.
|
||||||
|
|
||||||
## Installing individual node dependencies
|
## Installing individual node dependencies
|
||||||
|
|
||||||
|
27
README.md
27
README.md
@ -12,6 +12,10 @@ Check out [INSTALL](INSTALL.md) for full instructions on getting started.
|
|||||||
4. node red.js
|
4. node red.js
|
||||||
5. Open <http://localhost:1880>
|
5. Open <http://localhost:1880>
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
More documentation can be found [here](http://nodered.org/docs).
|
||||||
|
|
||||||
## 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
|
||||||
@ -31,8 +35,21 @@ been raised.
|
|||||||
### Creating new nodes
|
### Creating new nodes
|
||||||
|
|
||||||
The plugin nature of Node-RED means anyone can create a new node to extend
|
The plugin nature of Node-RED means anyone can create a new node to extend
|
||||||
its capabilities. Eventually, the nodes will be npm-installable, but we're not
|
its capabilities.
|
||||||
there yet.
|
|
||||||
|
We want to avoid duplication as that can lead to confusion. Many of our existing
|
||||||
|
nodes offer a starting point of functionality. If they are missing features,
|
||||||
|
we would rather extend them than add separate 'advanced' versions. But the key
|
||||||
|
to that approach is getting the UX right to not lose the simplicity.
|
||||||
|
|
||||||
|
We are also going to be quite selective over what nodes are included in the main
|
||||||
|
repository - enough to be useful, but not so many that new user is overwhelmed.
|
||||||
|
|
||||||
|
To contribute a new node, please raise a pull-request against the
|
||||||
|
`node-red-nodes` repository.
|
||||||
|
|
||||||
|
Eventually, the nodes will be npm-installable, but we're not there yet. We'll
|
||||||
|
also have some sort of registry of nodes to help with discoverability.
|
||||||
|
|
||||||
### Pull-Requests
|
### Pull-Requests
|
||||||
|
|
||||||
@ -42,7 +59,11 @@ property license granted with any contribution. It is for your protection as a
|
|||||||
Contributor as well as the protection of IBM and its customers; it does not
|
Contributor as well as the protection of IBM and its customers; it does not
|
||||||
change your rights to use your own Contributions for any other purpose.
|
change your rights to use your own Contributions for any other purpose.
|
||||||
|
|
||||||
We'll add some information on how to do this in the next few days.
|
Once you have created a pull-request, we'll provide a link to the appropriate
|
||||||
|
CLA document.
|
||||||
|
|
||||||
|
If you are an IBMer, please contact us directly as the contribution process is
|
||||||
|
slightly different.
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
|
@ -15,61 +15,62 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script type="text/x-red" data-template-name="inject">
|
<script type="text/x-red" data-template-name="inject">
|
||||||
<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 node-input-payload">
|
<div class="form-row node-input-payload">
|
||||||
<label for="node-input-payload"><i class="icon-envelope"></i> Payload</label>
|
<label for="node-input-payload"><i class="icon-envelope"></i> Payload</label>
|
||||||
<input type="text" id="node-input-payload" placeholder="Payload">
|
<input type="text" id="node-input-payload" placeholder="Payload">
|
||||||
</div>
|
</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">
|
<div class="form-row">
|
||||||
<label> </label>
|
<label> </label>
|
||||||
<input type="checkbox" id="node-input-once" placeholder="once" style="display: inline-block; width: auto; vertical-align: top;">
|
<input type="checkbox" id="node-input-once" placeholder="once" style="display: inline-block; width: auto; vertical-align: top;">
|
||||||
<label for="node-input-once" style="width: 70%;">Fire once at start ?</label>
|
<label for="node-input-once" style="width: 70%;">Fire once at start ?</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for=""><i class="icon-repeat"></i> Repeat</label>
|
<label for=""><i class="icon-repeat"></i> Repeat</label>
|
||||||
<select id="inject-time-type-select"><option value="none">None</option><option value="interval">interval</option><option value="interval-time">interval between times</option><option value="time">at a specific time</option></select>
|
<select id="inject-time-type-select"><option value="none">None</option><option value="interval">interval</option><option value="interval-time">interval between times</option><option value="time">at a specific time</option></select>
|
||||||
<input type="hidden" id="node-input-repeat" placeholder="Payload">
|
<input type="hidden" id="node-input-repeat" placeholder="Payload">
|
||||||
<input type="hidden" id="node-input-crontab" placeholder="Payload">
|
<input type="hidden" id="node-input-crontab" placeholder="Payload">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row inject-time-row hidden" id="inject-time-row-interval">
|
<div class="form-row inject-time-row hidden" id="inject-time-row-interval">
|
||||||
every <input id="inject-time-interval-count" class="inject-time-count" value="1"></input>
|
every <input id="inject-time-interval-count" class="inject-time-count" value="1"></input>
|
||||||
<select style="width: 100px" id="inject-time-interval-units"><option value="s">seconds</option><option value="m">minutes</option><option value="h">hours</option></select><br/>
|
<select style="width: 100px" id="inject-time-interval-units"><option value="s">seconds</option><option value="m">minutes</option><option value="h">hours</option></select><br/>
|
||||||
on <select disabled id="inject-time-interval-days" class="inject-time-days"></select>
|
on <select disabled id="inject-time-interval-days" class="inject-time-days"></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row inject-time-row hidden" id="inject-time-row-interval-time">
|
<div class="form-row inject-time-row hidden" id="inject-time-row-interval-time">
|
||||||
every <input id="inject-time-interval-time-units" class="inject-time-count" value="1"></input> minutes<br/>
|
every <input id="inject-time-interval-time-units" class="inject-time-count" value="1"></input> minutes<br/>
|
||||||
between <select id="inject-time-interval-time-start" class="inject-time-times"></select>
|
between <select id="inject-time-interval-time-start" class="inject-time-times"></select>
|
||||||
and <select id="inject-time-interval-time-end" class="inject-time-times"></select><br/>
|
and <select id="inject-time-interval-time-end" class="inject-time-times"></select><br/>
|
||||||
on <select id="inject-time-interval-time-days" class="inject-time-days"></select>
|
on <select id="inject-time-interval-time-days" class="inject-time-days"></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row inject-time-row hidden" id="inject-time-row-time">
|
<div class="form-row inject-time-row hidden" id="inject-time-row-time">
|
||||||
at <input id="inject-time-time" value="12:00"></input><br/>
|
at <input id="inject-time-time" value="12:00"></input><br/>
|
||||||
on <select id="inject-time-time-days" class="inject-time-days"></select>
|
on <select id="inject-time-time-days" class="inject-time-days"></select>
|
||||||
</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: Injects Date.now() if no payload set</div>
|
<div class="form-tips">Tip: Injects Date.now() if no payload set</div>
|
||||||
<script>
|
<script>
|
||||||
{
|
{
|
||||||
|
|
||||||
$("#inject-time-type-select").change(function() {
|
$("#inject-time-type-select").change(function() {
|
||||||
var id = $("#inject-time-type-select option:selected").val();
|
var id = $("#inject-time-type-select option:selected").val();
|
||||||
$(".inject-time-row").hide();
|
$(".inject-time-row").hide();
|
||||||
$("#inject-time-row-"+id).show();
|
$("#inject-time-row-"+id).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
var days = [
|
var days = [
|
||||||
{v:"*",t:"every day"},
|
{v:"*",t:"every day"},
|
||||||
{v:"1-5",t:"Mondays to Fridays"},
|
{v:"1-5",t:"Mondays to Fridays"},
|
||||||
@ -87,7 +88,7 @@
|
|||||||
$(this).append($("<option></option>").val(days[d].v).text(days[d].t));
|
$(this).append($("<option></option>").val(days[d].v).text(days[d].t));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".inject-time-times").each(function() {
|
$(".inject-time-times").each(function() {
|
||||||
for (var i=0;i<24;i++) {
|
for (var i=0;i<24;i++) {
|
||||||
var l = (i<10?"0":"")+i+":00";
|
var l = (i<10?"0":"")+i+":00";
|
||||||
@ -98,14 +99,14 @@
|
|||||||
min:1,
|
min:1,
|
||||||
max:60
|
max:60
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#inject-time-interval-units").change(function() {
|
$("#inject-time-interval-units").change(function() {
|
||||||
var units = $("#inject-time-interval-units option:selected").val();
|
var units = $("#inject-time-interval-units option:selected").val();
|
||||||
$("#inject-time-interval-days").prop("disabled",(units == "s")?"disabled":false);
|
$("#inject-time-interval-days").prop("disabled",(units == "s")?"disabled":false);
|
||||||
$(".inject-time-count").spinner("option","max",(units == "h")?24:60);
|
$(".inject-time-count").spinner("option","max",(units == "h")?24:60);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$.widget( "ui.injecttimespinner", $.ui.spinner, {
|
$.widget( "ui.injecttimespinner", $.ui.spinner, {
|
||||||
options: {
|
options: {
|
||||||
@ -131,16 +132,16 @@
|
|||||||
var d = new Date(value);
|
var d = new Date(value);
|
||||||
var h = d.getHours();
|
var h = d.getHours();
|
||||||
var m = d.getMinutes();
|
var m = d.getMinutes();
|
||||||
|
|
||||||
return ((h<10)?"0":"")+h+":"+((m<10)?"0":"")+m;
|
return ((h<10)?"0":"")+h+":"+((m<10)?"0":"")+m;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$("#inject-time-time").injecttimespinner();
|
$("#inject-time-time").injecttimespinner();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
@ -169,7 +170,7 @@
|
|||||||
width: 30px !important;
|
width: 30px !important;
|
||||||
}
|
}
|
||||||
.
|
.
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script type="text/x-red" data-help-name="inject">
|
<script type="text/x-red" data-help-name="inject">
|
||||||
<p>Pressing the button on the left side of the node allows a message on a topic to be injected into the flow. This is mainly for test purposes.</p>
|
<p>Pressing the button on the left side of the node allows a message on a topic to be injected into the flow. This is mainly for test purposes.</p>
|
||||||
@ -194,7 +195,7 @@
|
|||||||
outputs:1,
|
outputs:1,
|
||||||
icon: "inject.png",
|
icon: "inject.png",
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||this.topic||this.payload;
|
return this.name||this.topic||this.payload||"inject";
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
@ -216,7 +217,7 @@
|
|||||||
$("#inject-time-time").val(time);
|
$("#inject-time-time").val(time);
|
||||||
$("#inject-time-type-select option").filter(function() {return $(this).val() == "s";}).attr('selected',true);
|
$("#inject-time-type-select option").filter(function() {return $(this).val() == "s";}).attr('selected',true);
|
||||||
$("#inject-time-time-days option").filter(function() {return $(this).val() == days;}).attr('selected',true);
|
$("#inject-time-time-days option").filter(function() {return $(this).val() == days;}).attr('selected',true);
|
||||||
|
|
||||||
} else if (cronparts[0] == "0") {
|
} else if (cronparts[0] == "0") {
|
||||||
// interval - hours
|
// interval - hours
|
||||||
var hours = cronparts[1].slice(2);
|
var hours = cronparts[1].slice(2);
|
||||||
@ -257,7 +258,7 @@
|
|||||||
// 23,0 or 17-23,0-10 or 23,0-2 or 17-23,0
|
// 23,0 or 17-23,0-10 or 23,0-2 or 17-23,0
|
||||||
var startparts = timeparts[0].split("-");
|
var startparts = timeparts[0].split("-");
|
||||||
start = startparts[0];
|
start = startparts[0];
|
||||||
|
|
||||||
var endparts = timeparts[1].split("-");
|
var endparts = timeparts[1].split("-");
|
||||||
if (endparts.length == 1) {
|
if (endparts.length == 1) {
|
||||||
end = Number(endparts[0])+1;
|
end = Number(endparts[0])+1;
|
||||||
@ -272,12 +273,12 @@
|
|||||||
} else {
|
} else {
|
||||||
$("#inject-time-type-select option").filter(function() {return $(this).val() == "none";}).attr('selected',true);
|
$("#inject-time-type-select option").filter(function() {return $(this).val() == "none";}).attr('selected',true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".inject-time-row").hide();
|
$(".inject-time-row").hide();
|
||||||
$("#inject-time-type-select option").filter(function() {return $(this).val() == repeattype;}).attr('selected',true);
|
$("#inject-time-type-select option").filter(function() {return $(this).val() == repeattype;}).attr('selected',true);
|
||||||
$("#inject-time-row-"+repeattype).show();
|
$("#inject-time-row-"+repeattype).show();
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
oneditsave: function() {
|
oneditsave: function() {
|
||||||
var repeat = "";
|
var repeat = "";
|
||||||
@ -297,7 +298,7 @@
|
|||||||
} else if (units == "h") {
|
} else if (units == "h") {
|
||||||
crontab = "0 */"+count+" * * "+days;
|
crontab = "0 */"+count+" * * "+days;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type == "interval-time") {
|
} else if (type == "interval-time") {
|
||||||
var count = $("#inject-time-interval-time-units").val();
|
var count = $("#inject-time-interval-time-units").val();
|
||||||
var startTime = Number($("#inject-time-interval-time-start option:selected").val());
|
var startTime = Number($("#inject-time-interval-time-start option:selected").val());
|
||||||
@ -338,10 +339,10 @@
|
|||||||
repeat = 0;
|
repeat = 0;
|
||||||
crontab = parts[1]+" "+parts[0]+" * * "+days;
|
crontab = parts[1]+" "+parts[0]+" * * "+days;
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#node-input-repeat").val(repeat);
|
$("#node-input-repeat").val(repeat);
|
||||||
$("#node-input-crontab").val(crontab);
|
$("#node-input-crontab").val(crontab);
|
||||||
|
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
onclick: function() {
|
onclick: function() {
|
||||||
|
@ -117,6 +117,9 @@
|
|||||||
|
|
||||||
var errornotification = null;
|
var errornotification = null;
|
||||||
|
|
||||||
|
var messageCount = 0;
|
||||||
|
|
||||||
|
|
||||||
function debugConnect() {
|
function debugConnect() {
|
||||||
//console.log("debug ws connecting");
|
//console.log("debug ws connecting");
|
||||||
var ws = new WebSocket("ws://"+location.hostname+":"+location.port+document.location.pathname+"/debug");
|
var ws = new WebSocket("ws://"+location.hostname+":"+location.port+document.location.pathname+"/debug");
|
||||||
@ -160,7 +163,13 @@
|
|||||||
(o.topic?'<span class="debug-message-topic">'+topic+'</span>':'')+
|
(o.topic?'<span class="debug-message-topic">'+topic+'</span>':'')+
|
||||||
'<span class="debug-message-payload">'+payload+'</span>';
|
'<span class="debug-message-payload">'+payload+'</span>';
|
||||||
var atBottom = (sbc.scrollHeight-messages.offsetHeight-sbc.scrollTop) < 5;
|
var atBottom = (sbc.scrollHeight-messages.offsetHeight-sbc.scrollTop) < 5;
|
||||||
|
messageCount++;
|
||||||
$(messages).append(msg);
|
$(messages).append(msg);
|
||||||
|
|
||||||
|
if (messageCount > 200) {
|
||||||
|
$("#debug-content .debug-message:first").remove();
|
||||||
|
messageCount--;
|
||||||
|
}
|
||||||
if (atBottom) {
|
if (atBottom) {
|
||||||
$(sbc).scrollTop(sbc.scrollHeight);
|
$(sbc).scrollTop(sbc.scrollHeight);
|
||||||
}
|
}
|
||||||
@ -176,6 +185,7 @@
|
|||||||
|
|
||||||
$("#debug-tab-clear").click(function() {
|
$("#debug-tab-clear").click(function() {
|
||||||
$(".debug-message").remove();
|
$(".debug-message").remove();
|
||||||
|
messageCount = 0;
|
||||||
RED.nodes.eachNode(function(node) {
|
RED.nodes.eachNode(function(node) {
|
||||||
node.highlighted = false;
|
node.highlighted = false;
|
||||||
node.dirty = true;
|
node.dirty = true;
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
outputs:0,
|
outputs:0,
|
||||||
icon: "file.png",
|
icon: "file.png",
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||"comment";
|
return this.name||"";
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
|
@ -117,15 +117,17 @@ function GPIOOutNode(n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exec("gpio reset",function(err,stdout,stderr) {
|
exec("gpio mode 0 in",function(err,stdout,stderr) {
|
||||||
if (err) {
|
if (err) {
|
||||||
util.log('[36-rpi-gpio.js] Error: "gpio reset" command failed for some reason.');
|
util.log('[36-rpi-gpio.js] Error: "gpio" command failed for some reason.');
|
||||||
}
|
}
|
||||||
exec("gpio load spi",function(err,stdout,stderr) {
|
exec("gpio mode 1 in");
|
||||||
if (err) {
|
exec("gpio mode 2 in");
|
||||||
util.log('[36-rpi-gpio.js] Error: "gpio load spi" command failed for some reason.');
|
exec("gpio mode 3 in");
|
||||||
}
|
exec("gpio mode 4 in");
|
||||||
|
exec("gpio mode 5 in");
|
||||||
|
exec("gpio mode 6 in");
|
||||||
|
exec("gpio mode 7 in",function(err,stdout,stderr) {
|
||||||
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
|
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
|
||||||
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
|
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ function BlinkStick(n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.warn("No BlinkStick found");
|
//node.warn("No BlinkStick found");
|
||||||
node.led = blinkstick.findFirst();
|
node.led = blinkstick.findFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
outputs:1,
|
outputs:1,
|
||||||
icon: "bridge.png",
|
icon: "bridge.png",
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||this.topic;
|
return this.name||this.topic||"mqtt";
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
@ -90,7 +90,7 @@
|
|||||||
icon: "bridge.png",
|
icon: "bridge.png",
|
||||||
align: "right",
|
align: "right",
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||this.topic;
|
return this.name||this.topic||"mqtt";
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
|
@ -40,7 +40,6 @@ function HTTPIn(n) {
|
|||||||
RED.nodes.registerType("http in",HTTPIn);
|
RED.nodes.registerType("http in",HTTPIn);
|
||||||
|
|
||||||
HTTPIn.prototype.close = function() {
|
HTTPIn.prototype.close = function() {
|
||||||
console.log(RED.app.routes[this.method]);
|
|
||||||
var routes = RED.app.routes[this.method];
|
var routes = RED.app.routes[this.method];
|
||||||
for (var i in routes) {
|
for (var i in routes) {
|
||||||
if (routes[i].path == this.url) {
|
if (routes[i].path == this.url) {
|
||||||
@ -48,6 +47,5 @@ HTTPIn.prototype.close = function() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(RED.app.routes[this.method]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,15 +46,13 @@ function SerialOutNode(n) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.port.on("ready",function() {
|
node.on("input",function(msg) {
|
||||||
node.on("input",function(msg) {
|
|
||||||
//console.log("{",msg,"}");
|
//console.log("{",msg,"}");
|
||||||
node.port.write(msg.payload,function(err,res) {
|
node.port.write(msg.payload,function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
node.error(err);
|
node.error(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.error("missing serial config");
|
this.error("missing serial config");
|
||||||
@ -123,10 +121,18 @@ var serialPool = function() {
|
|||||||
}
|
}
|
||||||
newline = newline.replace("\\n","\n").replace("\\r","\r");
|
newline = newline.replace("\\n","\n").replace("\\r","\r");
|
||||||
var setupSerial = function() {
|
var setupSerial = function() {
|
||||||
obj.serial = new serialp.SerialPort(port,{
|
if (newline == "") {
|
||||||
baudrate: baud,
|
obj.serial = new serialp.SerialPort(port,{
|
||||||
parser: serialp.parsers.readline(newline)
|
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) {
|
obj.serial.on('error', function(err) {
|
||||||
util.log("[serial] serial port "+port+" error "+err);
|
util.log("[serial] serial port "+port+" error "+err);
|
||||||
obj.tout = setTimeout(function() {
|
obj.tout = setTimeout(function() {
|
||||||
@ -147,7 +153,15 @@ var serialPool = function() {
|
|||||||
obj._emitter.emit('ready');
|
obj._emitter.emit('ready');
|
||||||
});
|
});
|
||||||
obj.serial.on('data',function(d) {
|
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);
|
obj._emitter.emit('data',d);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setupSerial();
|
setupSerial();
|
||||||
|
@ -50,10 +50,10 @@
|
|||||||
outputs:1,
|
outputs:1,
|
||||||
icon: "white-globe.png",
|
icon: "white-globe.png",
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||this.baseurl||"http(s) get";
|
return this.name||this.baseurl;
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return (this.name||!this.baseurl)?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -25,6 +25,10 @@
|
|||||||
<label for="node-config-input-db"><i class="icon-briefcase"></i> Database</label>
|
<label for="node-config-input-db"><i class="icon-briefcase"></i> Database</label>
|
||||||
<input type="text" id="node-config-input-db" placeholder="test">
|
<input type="text" id="node-config-input-db" placeholder="test">
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -35,9 +39,10 @@
|
|||||||
hostname: { value:"127.0.0.1",required:true},
|
hostname: { value:"127.0.0.1",required:true},
|
||||||
port: { value: 27017,required:true},
|
port: { value: 27017,required:true},
|
||||||
db: { value:"",required:true},
|
db: { value:"",required:true},
|
||||||
|
name: { value:"" }
|
||||||
},
|
},
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.hostname+":"+this.port+"//"+this.db;
|
return this.name||this.hostname+":"+this.port+"//"+this.db;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -52,7 +57,14 @@
|
|||||||
<label for="node-input-collection"><i class="icon-briefcase"></i> Collection</label>
|
<label for="node-input-collection"><i class="icon-briefcase"></i> Collection</label>
|
||||||
<input type="text" id="node-input-collection" placeholder="collection">
|
<input type="text" id="node-input-collection" placeholder="collection">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
|
<label for="node-input-operation"><i class="icon-wrench"></i> Operation</label>
|
||||||
|
<select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;">
|
||||||
|
<option value=store>Store</option>
|
||||||
|
<option value=delete>Delete</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-row node-input-payonly">
|
||||||
<label> </label>
|
<label> </label>
|
||||||
<input type="checkbox" id="node-input-payonly" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;">
|
<input type="checkbox" id="node-input-payonly" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;">
|
||||||
<label for="node-input-payonly" style="width: 70%;">Only store msg.payload object ?</label>
|
<label for="node-input-payonly" style="width: 70%;">Only store msg.payload object ?</label>
|
||||||
@ -61,6 +73,13 @@
|
|||||||
<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>
|
||||||
|
$("#node-input-operation").change(function() {
|
||||||
|
var id = $("#node-input-operation option:selected").val();
|
||||||
|
if (id == "delete") $(".node-input-payonly").hide();
|
||||||
|
else $(".node-input-payonly").show();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/x-red" data-help-name="mongodb out">
|
<script type="text/x-red" data-help-name="mongodb out">
|
||||||
@ -69,6 +88,8 @@
|
|||||||
<p>If this is NOT the desired behaviour - ie you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p>
|
<p>If this is NOT the desired behaviour - ie you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p>
|
||||||
<p>This could be a unique constant or you could create one based on some other msg property.</p>
|
<p>This could be a unique constant or you could create one based on some other msg property.</p>
|
||||||
<p>Currently we do not limit or cap the collection size at all... this may well change.</p>
|
<p>Currently we do not limit or cap the collection size at all... this may well change.</p>
|
||||||
|
<p>You can also choose to <b>remove</b> items. To do so the <b>msg.payload</b> <i>MUST</i> contain an object that will select the items(s) to remove.
|
||||||
|
A blank object will delete <i>all of the objects</i> in the collection. You have been warned...</p>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -79,7 +100,8 @@
|
|||||||
mongodb: { type:"mongodb",required:true},
|
mongodb: { type:"mongodb",required:true},
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
collection: {value:"",required:true},
|
collection: {value:"",required:true},
|
||||||
payonly: {value:false}
|
payonly: {value:false},
|
||||||
|
operation: {value:"store"}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:0,
|
outputs:0,
|
||||||
@ -87,7 +109,7 @@
|
|||||||
align: "right",
|
align: "right",
|
||||||
label: function() {
|
label: function() {
|
||||||
var mongoNode = RED.nodes.node(this.mongodb);
|
var mongoNode = RED.nodes.node(this.mongodb);
|
||||||
return this.name||this.collection||(mongoNode?mongoNode.label():"mongodb");
|
return this.name||(mongoNode?mongoNode.label()+"//"+this.collection:"mongodb");
|
||||||
},
|
},
|
||||||
labelStyle: function() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
@ -114,7 +136,7 @@
|
|||||||
<script type="text/x-red" data-help-name="mongodb in">
|
<script type="text/x-red" data-help-name="mongodb in">
|
||||||
<p>Queries a MongoDB collection by using the <b>msg.payload</b> to be a MongoDB query statement as per the .find() function.</p>
|
<p>Queries a MongoDB collection by using the <b>msg.payload</b> to be a MongoDB query statement as per the .find() function.</p>
|
||||||
<p>You may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object and a <b>msg.limit</b> object.</p>
|
<p>You may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object and a <b>msg.limit</b> object.</p>
|
||||||
<p>All are optional - see the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB find docs</i></a> for examples.
|
<p>All are optional - see the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB find docs</i></a> for examples.</p>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -124,7 +146,7 @@
|
|||||||
defaults: {
|
defaults: {
|
||||||
mongodb: { type:"mongodb",required:true},
|
mongodb: { type:"mongodb",required:true},
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
collection: {value:"",required:true},
|
collection: {value:"",required:true}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:1,
|
outputs:1,
|
||||||
|
@ -22,6 +22,7 @@ function MongoNode(n) {
|
|||||||
this.hostname = n.hostname;
|
this.hostname = n.hostname;
|
||||||
this.port = n.port;
|
this.port = n.port;
|
||||||
this.db = n.db;
|
this.db = n.db;
|
||||||
|
this.name = n.name;
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("mongodb",MongoNode);
|
RED.nodes.registerType("mongodb",MongoNode);
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ function MongoOutNode(n) {
|
|||||||
this.collection = n.collection;
|
this.collection = n.collection;
|
||||||
this.mongodb = n.mongodb;
|
this.mongodb = n.mongodb;
|
||||||
this.payonly = n.payonly || false;
|
this.payonly = n.payonly || false;
|
||||||
|
this.operation = n.operation;
|
||||||
this.mongoConfig = RED.nodes.getNode(this.mongodb);
|
this.mongoConfig = RED.nodes.getNode(this.mongodb);
|
||||||
|
|
||||||
if (this.mongoConfig) {
|
if (this.mongoConfig) {
|
||||||
@ -43,9 +45,15 @@ function MongoOutNode(n) {
|
|||||||
if (err) { node.error(err); }
|
if (err) { node.error(err); }
|
||||||
else {
|
else {
|
||||||
node.on("input",function(msg) {
|
node.on("input",function(msg) {
|
||||||
|
if (node.operation == "store") {
|
||||||
delete msg._topic;
|
delete msg._topic;
|
||||||
if (node.payonly) coll.save(msg.payload,function(err,item){if (err){node.error(err);}});
|
if (node.payonly) coll.save(msg.payload,function(err,item){ if (err){node.error(err);} });
|
||||||
else coll.save(msg,function(err,item){if (err){node.error(err);}});
|
else coll.save(msg,function(err,item){if (err){node.error(err);}});
|
||||||
|
}
|
||||||
|
if (node.operation == "delete") {
|
||||||
|
console.log(msg.payload);
|
||||||
|
coll.remove(msg.payload, {w:1}, function(err, items){ if (err) node.error(err); });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
<!--
|
|
||||||
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="leveldbase">
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="node-config-input-db"><i class="icon-briefcase"></i> Database</label>
|
|
||||||
<input type="text" id="node-config-input-db" placeholder="database path/name">
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('leveldbase',{
|
|
||||||
category: 'config',
|
|
||||||
defaults: {
|
|
||||||
db: {value:"",required:true}
|
|
||||||
},
|
|
||||||
label: function() {
|
|
||||||
return this.db;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/x-red" data-template-name="leveldb in">
|
|
||||||
<div class="form-row node-input-level">
|
|
||||||
<label for="node-input-level"><i class="icon-briefcase"></i> Database</label>
|
|
||||||
<input type="text" id="node-input-level">
|
|
||||||
</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="leveldb in">
|
|
||||||
<p>Uses <a href="https://code.google.com/p/leveldb/" target="_new"><i>LevelDB</i></a> for a simple key value pair database.</p>
|
|
||||||
<p>Use this node to <b>get</b>, or retrieve the data already saved in the database.</p>
|
|
||||||
<p><b>msg.topic</b> must hold the <i>key</i> for the database, and the result is returned in <b>msg.payload</b>.</p>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('leveldb in',{
|
|
||||||
category: 'storage-input',
|
|
||||||
color:"#dbb84d",
|
|
||||||
defaults: {
|
|
||||||
level: {type:"leveldbase",required:true},
|
|
||||||
name: {value:""}
|
|
||||||
},
|
|
||||||
inputs:1,
|
|
||||||
outputs:1,
|
|
||||||
icon: "leveldb.png",
|
|
||||||
label: function() {
|
|
||||||
var levelNode = RED.nodes.node(this.level);
|
|
||||||
return this.name||(levelNode?levelNode.label():"leveldb");
|
|
||||||
},
|
|
||||||
labelStyle: function() {
|
|
||||||
return this.name?"node_label_italic":"";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/x-red" data-template-name="leveldb out">
|
|
||||||
<div class="form-row node-input-level">
|
|
||||||
<label for="node-input-level"><i class="icon-briefcase"></i> Database</label>
|
|
||||||
<input type="text" id="node-input-level">
|
|
||||||
</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="leveldb out">
|
|
||||||
<p>Uses <a href="https://code.google.com/p/leveldb/" target="_new"><i>LevelDB</i></a> for a simple key value pair database.</p>
|
|
||||||
<p>Use this node to <b>put</b> (save) the <b>msg.payload</b> to the named database file, using <b>msg.topic</b> as the key.</p>
|
|
||||||
<p>To <b>delete</b> information do a <b>put</b> to the required <b>msg.topic</b> (key) with a <b>msg.payload</b> of <b><i>null</i></b>.</p>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('leveldb out',{
|
|
||||||
category: 'storage-output',
|
|
||||||
color:"#dbb84d",
|
|
||||||
defaults: {
|
|
||||||
level: {type:"leveldbase",required:true},
|
|
||||||
op: {value:"put",required:true},
|
|
||||||
name: {value:""}
|
|
||||||
},
|
|
||||||
inputs:1,
|
|
||||||
outputs:0,
|
|
||||||
icon: "leveldb.png",
|
|
||||||
align: "right",
|
|
||||||
label: function() {
|
|
||||||
var levelNode = RED.nodes.node(this.level);
|
|
||||||
return this.name||(levelNode?levelNode.label():"leveldb");
|
|
||||||
},
|
|
||||||
labelStyle: function() {
|
|
||||||
return this.name?"node_label_italic":"";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,90 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 lvldb = require('leveldb');
|
|
||||||
|
|
||||||
function LevelNode(n) {
|
|
||||||
RED.nodes.createNode(this,n);
|
|
||||||
this.dbname = n.db;
|
|
||||||
lvldb.open(this.dbname, { create_if_missing: true }, onOpen);
|
|
||||||
var node = this;
|
|
||||||
function onOpen(err, db) {
|
|
||||||
if (err) node.error(err);
|
|
||||||
node.db = db;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RED.nodes.registerType("leveldbase",LevelNode);
|
|
||||||
|
|
||||||
|
|
||||||
function LevelDBNodeIn(n) {
|
|
||||||
RED.nodes.createNode(this,n);
|
|
||||||
this.level = n.level;
|
|
||||||
this.op = n.op;
|
|
||||||
this.levelConfig = RED.nodes.getNode(this.level);
|
|
||||||
|
|
||||||
if (this.levelConfig) {
|
|
||||||
var node = this;
|
|
||||||
node.on("input", function(msg) {
|
|
||||||
if (typeof msg.topic === 'string') {
|
|
||||||
node.levelConfig.db.get(msg.topic, function(err, value) {
|
|
||||||
if (err) node.error(err);
|
|
||||||
msg.payload = JSON.parse(value);
|
|
||||||
delete msg.cmd;
|
|
||||||
node.send(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (typeof msg.topic !== 'string') node.error("msg.topic (the key is not defined");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.error("LevelDB database name not configured");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RED.nodes.registerType("leveldb in",LevelDBNodeIn);
|
|
||||||
|
|
||||||
|
|
||||||
function LevelDBNodeOut(n) {
|
|
||||||
RED.nodes.createNode(this,n);
|
|
||||||
this.level = n.level;
|
|
||||||
this.levelConfig = RED.nodes.getNode(this.level);
|
|
||||||
|
|
||||||
if (this.levelConfig) {
|
|
||||||
var node = this;
|
|
||||||
node.on("input", function(msg) {
|
|
||||||
if (typeof msg.topic === 'string') {
|
|
||||||
console.log(msg);
|
|
||||||
if (msg.payload === null) {
|
|
||||||
node.levelConfig.db.del(msg.topic);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.levelConfig.db.put(msg.topic, JSON.stringify(msg.payload), function(err) {
|
|
||||||
if (err) node.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (typeof msg.topic !== 'string') node.error("msg.topic (the key is not defined");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.error("LevelDB database name not configured");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RED.nodes.registerType("leveldb out",LevelDBNodeOut);
|
|
10
package.json
10
package.json
@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description" : "A visual tool for wiring the Internet of Things",
|
"description": "A visual tool for wiring the Internet of Things",
|
||||||
|
"homepage": "http://nodered.org",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node red.js"
|
"start": "node red.js"
|
||||||
},
|
},
|
||||||
"main": "lib/server.js",
|
"main": "red/red.js",
|
||||||
"author": "Nicholas O'Leary",
|
"author": "Nick O'Leary",
|
||||||
"contributors": [ {"name": "Dave Conway-Jones"} ],
|
"contributors": [ {"name": "Dave Conway-Jones"} ],
|
||||||
"keywords": ["editor", "messaging", "iot", "m2m", "pi", "arduino", "beaglebone", "ibm"],
|
"keywords": ["editor", "messaging", "iot", "m2m", "pi", "arduino", "beaglebone", "ibm"],
|
||||||
"license": "Apache",
|
"license": "Apache",
|
||||||
@ -17,5 +18,6 @@
|
|||||||
"mustache": "*",
|
"mustache": "*",
|
||||||
"cron":"*"
|
"cron":"*"
|
||||||
},
|
},
|
||||||
"engines": { "node": ">=0.8" }
|
"engines": { "node": ">=0.8" },
|
||||||
|
"repository": {"type":"git","url":"https://github.com/node-red/node-red.git"}
|
||||||
}
|
}
|
||||||
|
BIN
public/icons/db.png
Normal file
BIN
public/icons/db.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 558 B |
@ -857,7 +857,11 @@ RED.view = function() {
|
|||||||
var root_node = new_ms[0].n;
|
var root_node = new_ms[0].n;
|
||||||
var dx = root_node.x;
|
var dx = root_node.x;
|
||||||
var dy = root_node.y;
|
var dy = root_node.y;
|
||||||
|
|
||||||
|
if (mouse_position == null) {
|
||||||
|
mouse_position = [0,0];
|
||||||
|
}
|
||||||
|
|
||||||
for (var i in new_ms) {
|
for (var i in new_ms) {
|
||||||
new_ms[i].n.selected = true;
|
new_ms[i].n.selected = true;
|
||||||
new_ms[i].n.x -= dx - mouse_position[0];
|
new_ms[i].n.x -= dx - mouse_position[0];
|
||||||
|
10
red.js
10
red.js
@ -19,20 +19,18 @@ var util = require("util");
|
|||||||
var express = require("express");
|
var express = require("express");
|
||||||
var crypto = require("crypto");
|
var crypto = require("crypto");
|
||||||
var settings = require("./settings");
|
var settings = require("./settings");
|
||||||
|
var RED = require("./red/red.js");
|
||||||
|
|
||||||
|
|
||||||
var server;
|
var server;
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
var redApp = null;
|
|
||||||
|
|
||||||
if (settings.https) {
|
if (settings.https) {
|
||||||
server = https.createServer(settings.https,function(req,res){app(req,res);});
|
server = https.createServer(settings.https,function(req,res){app(req,res);});
|
||||||
} else {
|
} else {
|
||||||
server = http.createServer(function(req,res){app(req,res);});
|
server = http.createServer(function(req,res){app(req,res);});
|
||||||
}
|
}
|
||||||
|
|
||||||
redApp = require('./red/server.js').init(server,settings);
|
|
||||||
|
|
||||||
settings.httpRoot = settings.httpRoot||"/";
|
settings.httpRoot = settings.httpRoot||"/";
|
||||||
|
|
||||||
if (settings.httpRoot[0] != "/") {
|
if (settings.httpRoot[0] != "/") {
|
||||||
@ -51,9 +49,11 @@ if (settings.httpAuth) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(settings.httpRoot,redApp);
|
var red = RED.init(server,settings);
|
||||||
|
app.use(settings.httpRoot,red);
|
||||||
|
|
||||||
|
|
||||||
server.listen(settings.uiPort);
|
server.listen(settings.uiPort);
|
||||||
|
RED.start();
|
||||||
util.log('[red] Server now running at http'+(settings.https?'s':'')+'://127.0.0.1:'+settings.uiPort+settings.httpRoot);
|
util.log('[red] Server now running at http'+(settings.https?'s':'')+'://127.0.0.1:'+settings.uiPort+settings.httpRoot);
|
||||||
|
|
||||||
|
141
red/library.js
141
red/library.js
@ -16,78 +16,80 @@
|
|||||||
|
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var fspath = require("path");
|
var fspath = require("path");
|
||||||
var redUI = require("./server");
|
var redApp = null;
|
||||||
|
|
||||||
// -------- Flow Library --------
|
function init() {
|
||||||
redUI.app.post(new RegExp("/library/flows\/(.*)"), function(req,res) {
|
redApp = require("./server").app;
|
||||||
var fullBody = '';
|
// -------- Flow Library --------
|
||||||
req.on('data', function(chunk) {
|
redApp.post(new RegExp("/library/flows\/(.*)"), function(req,res) {
|
||||||
fullBody += chunk.toString();
|
var fullBody = '';
|
||||||
});
|
req.on('data', function(chunk) {
|
||||||
req.on('end', function() {
|
fullBody += chunk.toString();
|
||||||
var fn = "lib/flows/"+req.params[0]+".json";
|
});
|
||||||
var parts = fn.split("/");
|
req.on('end', function() {
|
||||||
for (var i = 3;i<parts.length;i+=1) {
|
var fn = "lib/flows/"+req.params[0]+".json";
|
||||||
var dirname = parts.slice(0,i).join("/");
|
var parts = fn.split("/");
|
||||||
if (!fs.existsSync(dirname)) {
|
for (var i = 3;i<parts.length;i+=1) {
|
||||||
fs.mkdirSync(dirname);
|
var dirname = parts.slice(0,i).join("/");
|
||||||
|
if (!fs.existsSync(dirname)) {
|
||||||
|
fs.mkdirSync(dirname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fs.writeFile(fn,fullBody,function(err) {
|
||||||
|
res.writeHead(204, {'Content-Type': 'text/plain'});
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function listFiles(dir) {
|
||||||
|
var dirs = {};
|
||||||
|
var files = [];
|
||||||
|
var dirCount = 0;
|
||||||
|
fs.readdirSync(dir).sort().filter(function(fn) {
|
||||||
|
var stats = fs.lstatSync(dir+"/"+fn);
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
dirCount += 1;
|
||||||
|
dirs[fn] = listFiles(dir+"/"+fn);
|
||||||
|
} else {
|
||||||
|
files.push(fn.split(".")[0]);
|
||||||
}
|
}
|
||||||
fs.writeFile(fn,fullBody,function(err) {
|
});
|
||||||
res.writeHead(204, {'Content-Type': 'text/plain'});
|
var result = {};
|
||||||
|
if (dirCount > 0) { result.d = dirs; }
|
||||||
|
if (files.length > 0) { result.f = files; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
redApp.get("/library/flows",function(req,res) {
|
||||||
|
var flows = {};
|
||||||
|
if (fs.existsSync("lib/flows")) {
|
||||||
|
flows = listFiles("lib/flows");
|
||||||
|
} else {
|
||||||
|
fs.mkdirSync("lib/flows");
|
||||||
|
}
|
||||||
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
|
res.write(JSON.stringify(flows));
|
||||||
|
res.end();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
redApp.get(new RegExp("/library/flows\/(.*)"), function(req,res) {
|
||||||
|
var fn = "lib/flows/"+req.params[0]+".json";
|
||||||
|
if (fs.existsSync(fn)) {
|
||||||
|
fs.readFile(fn,function(err,data) {
|
||||||
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
|
res.write(data);
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function listFiles(dir) {
|
|
||||||
var dirs = {};
|
|
||||||
var files = [];
|
|
||||||
var dirCount = 0;
|
|
||||||
fs.readdirSync(dir).sort().filter(function(fn) {
|
|
||||||
var stats = fs.lstatSync(dir+"/"+fn);
|
|
||||||
if (stats.isDirectory()) {
|
|
||||||
dirCount += 1;
|
|
||||||
dirs[fn] = listFiles(dir+"/"+fn);
|
|
||||||
} else {
|
} else {
|
||||||
files.push(fn.split(".")[0]);
|
res.send(404);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var result = {};
|
|
||||||
if (dirCount > 0) { result.d = dirs; }
|
// ------------------------------
|
||||||
if (files.length > 0) { result.f = files; }
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
redUI.app.get("/library/flows",function(req,res) {
|
|
||||||
var flows = {};
|
|
||||||
if (fs.existsSync("lib/flows")) {
|
|
||||||
flows = listFiles("lib/flows");
|
|
||||||
} else {
|
|
||||||
fs.mkdirSync("lib/flows");
|
|
||||||
}
|
|
||||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
|
||||||
res.write(JSON.stringify(flows));
|
|
||||||
res.end();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
redUI.app.get(new RegExp("/library/flows\/(.*)"), function(req,res) {
|
|
||||||
var fn = "lib/flows/"+req.params[0]+".json";
|
|
||||||
if (fs.existsSync(fn)) {
|
|
||||||
fs.readFile(fn,function(err,data) {
|
|
||||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
|
||||||
res.write(data);
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.send(404);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
function createLibrary(type) {
|
function createLibrary(type) {
|
||||||
|
|
||||||
@ -96,11 +98,11 @@ function createLibrary(type) {
|
|||||||
var root = fspath.join("lib",type)+"/";
|
var root = fspath.join("lib",type)+"/";
|
||||||
|
|
||||||
fs.exists(root,function(exists) {
|
fs.exists(root,function(exists) {
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
fs.mkdir(root);
|
fs.mkdir(root);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
redUI.app.get(new RegExp("/library/"+type+"($|\/(.*))"),function(req,res) {
|
redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),function(req,res) {
|
||||||
var path = req.params[1]||"";
|
var path = req.params[1]||"";
|
||||||
var rootPath = fspath.join(root,path);
|
var rootPath = fspath.join(root,path);
|
||||||
|
|
||||||
@ -141,7 +143,7 @@ function createLibrary(type) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
redUI.app.post(new RegExp("/library/"+type+"\/(.*)"),function(req,res) {
|
redApp.post(new RegExp("/library/"+type+"\/(.*)"),function(req,res) {
|
||||||
var path = req.params[0];
|
var path = req.params[0];
|
||||||
var fullBody = '';
|
var fullBody = '';
|
||||||
req.on('data', function(chunk) {
|
req.on('data', function(chunk) {
|
||||||
@ -246,4 +248,5 @@ function getFileBody(root,path,res) {
|
|||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.init = init;
|
||||||
module.exports.register = createLibrary;
|
module.exports.register = createLibrary;
|
||||||
|
14
red/nodes.js
14
red/nodes.js
@ -66,7 +66,11 @@ var registry = (function() {
|
|||||||
events.emit("nodes-stopped");
|
events.emit("nodes-stopped");
|
||||||
nodes = {};
|
nodes = {};
|
||||||
},
|
},
|
||||||
|
each: function(cb) {
|
||||||
|
for (var n in nodes) {
|
||||||
|
cb(nodes[n]);
|
||||||
|
}
|
||||||
|
},
|
||||||
addLogHandler: function(handler) {
|
addLogHandler: function(handler) {
|
||||||
logHandlers.push(handler);
|
logHandlers.push(handler);
|
||||||
}
|
}
|
||||||
@ -132,6 +136,7 @@ util.inherits(Node,EventEmitter);
|
|||||||
|
|
||||||
Node.prototype.close = function() {
|
Node.prototype.close = function() {
|
||||||
// called when a node is removed
|
// called when a node is removed
|
||||||
|
this.emit("close");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -237,7 +242,7 @@ module.exports.load = function() {
|
|||||||
if (stats.isFile()) {
|
if (stats.isFile()) {
|
||||||
if (/\.js$/.test(fn)) {
|
if (/\.js$/.test(fn)) {
|
||||||
try {
|
try {
|
||||||
require("../"+dir+"/"+fn);
|
require(dir+"/"+fn);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
util.log("["+fn+"] "+err);
|
util.log("["+fn+"] "+err);
|
||||||
//console.log(err.stack);
|
//console.log(err.stack);
|
||||||
@ -251,8 +256,7 @@ module.exports.load = function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
loadNodes(__dirname+"/../nodes");
|
||||||
loadNodes("nodes");
|
|
||||||
|
|
||||||
//events.emit("nodes-loaded");
|
//events.emit("nodes-loaded");
|
||||||
}
|
}
|
||||||
@ -325,7 +329,7 @@ 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) {
|
||||||
|
19
red/red.js
19
red/red.js
@ -18,18 +18,29 @@ var events = require("./events");
|
|||||||
var server = require("./server");
|
var server = require("./server");
|
||||||
var nodes = require("./nodes");
|
var nodes = require("./nodes");
|
||||||
var library = require("./library");
|
var library = require("./library");
|
||||||
var settings = require("../settings");
|
var settings = null;
|
||||||
|
|
||||||
|
|
||||||
var events = require("events");
|
var events = require("events");
|
||||||
|
|
||||||
var RED = {
|
var RED = {
|
||||||
|
|
||||||
|
init: function(httpServer,userSettings) {
|
||||||
|
settings = userSettings;
|
||||||
|
server.init(httpServer,settings);
|
||||||
|
library.init();
|
||||||
|
return server.app;
|
||||||
|
},
|
||||||
|
|
||||||
|
start: server.start,
|
||||||
|
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
app: server.app,
|
|
||||||
server: server.server,
|
|
||||||
settings: settings,
|
|
||||||
library: library,
|
library: library,
|
||||||
events: events
|
events: events
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RED.__defineGetter__("app", function() { return server.app });
|
||||||
|
RED.__defineGetter__("server", function() { return server.server });
|
||||||
|
RED.__defineGetter__("settings", function() { return settings });
|
||||||
|
|
||||||
module.exports = RED;
|
module.exports = RED;
|
||||||
|
@ -19,6 +19,8 @@ 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();
|
var host = require('os').hostname();
|
||||||
|
//TODO: relocated user dir
|
||||||
|
var rulesfile = process.argv[2] || 'flows_'+host+'.json';
|
||||||
|
|
||||||
var app = null;
|
var app = null;
|
||||||
var server = null;
|
var server = null;
|
||||||
@ -28,8 +30,12 @@ function createServer(_server,settings) {
|
|||||||
app = createUI(settings);
|
app = createUI(settings);
|
||||||
|
|
||||||
//TODO: relocated user dir
|
//TODO: relocated user dir
|
||||||
var rulesfile = process.argv[2] || 'flows_'+host+'.json';
|
fs.exists("lib/",function(exists) {
|
||||||
|
if (!exists) {
|
||||||
|
fs.mkdir("lib");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/nodes",function(req,res) {
|
app.get("/nodes",function(req,res) {
|
||||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
res.write(redNodes.getNodeConfigs());
|
res.write(redNodes.getNodeConfigs());
|
||||||
@ -65,7 +71,8 @@ function createServer(_server,settings) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
function start() {
|
||||||
console.log("\nWelcome to Node-RED\n===================\n");
|
console.log("\nWelcome to Node-RED\n===================\n");
|
||||||
util.log("[red] Loading palette nodes");
|
util.log("[red] Loading palette nodes");
|
||||||
util.log("------------------------------------------");
|
util.log("------------------------------------------");
|
||||||
@ -78,20 +85,23 @@ function createServer(_server,settings) {
|
|||||||
util.log(' npm install {the module name}');
|
util.log(' npm install {the module name}');
|
||||||
util.log('or any other errors are resolved');
|
util.log('or any other errors are resolved');
|
||||||
util.log("------------------------------------------");
|
util.log("------------------------------------------");
|
||||||
|
|
||||||
|
|
||||||
fs.exists(rulesfile, function (exists) {
|
fs.exists(rulesfile, function (exists) {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
util.log("[red] Loading workspace flow : "+rulesfile);
|
util.log("[red] Loading flows : "+rulesfile);
|
||||||
fs.readFile(rulesfile,'utf8',function(err,data) {
|
fs.readFile(rulesfile,'utf8',function(err,data) {
|
||||||
redNodes.setConfig(JSON.parse(data));
|
redNodes.setConfig(JSON.parse(data));
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
util.log("[red] Flows file not found : "+rulesfile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return app;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: createServer
|
init: createServer,
|
||||||
|
start: start
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.__defineGetter__("app", function() { return app });
|
module.exports.__defineGetter__("app", function() { return app });
|
||||||
|
Loading…
Reference in New Issue
Block a user