Got to start somewhere

This commit is contained in:
Nicholas O'Leary
2013-09-05 15:02:48 +01:00
commit 32796dd74c
155 changed files with 21836 additions and 0 deletions

76
nodes/99-sample.html.demo Normal file
View File

@@ -0,0 +1,76 @@
<!--
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.
-->
<!-- Sample html file that corresponds to the 99-sample.js file -->
<!-- This creates and configures the onscreen elements of the node -->
<!-- First, the content of the edit dialog is defined. -->
<script type="text/x-red" data-template-name="sample">
<!-- data-template-name identifies the node type this is for -->
<!-- Each of the following divs creates a field in the edit dialog. -->
<!-- Generally, there should be an input for each property of the node. -->
<!-- The for and id attributes identify the corresponding property -->
<!-- (with the 'node-input-' prefix). -->
<!-- The available icon classes are defined in Twitter Bootstrap -->
<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>
<!-- By convention, most nodes have a 'name' property. The following div -->
<!-- provides the necessary field. -->
<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>
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="sample">
<!-- data-help-name identifies the node type this help is for -->
<!-- This content appears in the Info sidebar when a node is selected -->
<!-- The first <p> is used as the pop-up tool tip when hovering over a -->
<!-- node in the palette. -->
<p>Simple sample input node. Just sends a single message when it starts up.
This is not very useful.</p>
<p>Outputs an object called <b>msg</b> containing <b>msg.topic</b> and
<b>msg.payload</b>. msg.payload is a String.</p>
</script>
<!-- Finally, the node type is registered along with all of its properties -->
<!-- The example below shows a small subset of the properties that can be set-->
<script type="text/javascript">
RED.nodes.registerType('sample',{
category: 'input', // the palette category
defaults: { // defines the editable properties of the node
name: {value:""}, // along with default values.
topic: {value:"", required:true}
},
inputs:0, // set the number of inputs - only 0 or 1
outputs:1, // set the number of outputs - 0 to n
icon: "arrow-in.png", // set the icon (held in public/icons)
label: function() { // sets the default label contents
return this.name||this.topic||"sample";
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
}
});
</script>

51
nodes/99-sample.js.demo Normal file
View File

@@ -0,0 +1,51 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
// Sample Node-RED node file
// Require main module
var RED = require("../../red/red");
// The main node definition - most things happen in here
function SampleNode(n) {
// Create a RED node
RED.nodes.createNode(this,n);
// Store local copies of the node configuration (as defined in the .html)
this.topic = n.topic;
// Do whatever you need to do in here - declare callbacks etc
// Note: this sample doesn't do anything much - it will only send
// this message once at startup...
// Look at other real nodes for some better ideas of what to do....
var msg = {};
msg.topic = node.topic;
msg.payload = "Hello world !"
// send out the message to the rest of the workspace.
this.send(msg);
}
// Register the node by name. This must be called before overriding any of the
// Node functions.
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

@@ -0,0 +1,51 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="sentiment">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Adds <b>msg.sentiment.score</b> as the anaylsis result.
</div>
</script>
<script type="text/x-red" data-help-name="sentiment">
<p>Analyses the <b>msg.payload</b> and adds a <b>msg.sentiment</b> object that contains the resulting AFINN-111 sentiment score as <b>msg.sentiment.score</b>.</p>
<p>A score greater than zero is positive and less than zero is negative.</p>
<p>Score can range from -5 to +5.</p>
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_new">the Sentiment docs here</a>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('sentiment',{
category: 'analysis-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"sentiment";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var sentiment = require('sentiment');
function SentimentNode(n) {
RED.nodes.createNode(this,n);
this.on("input", function(msg) {
var node = this;
sentiment(msg.payload, function (err, result) {
msg.sentiment = result;
node.send(msg);
});
});
}
RED.nodes.registerType("sentiment",SentimentNode);

View File

@@ -0,0 +1,57 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="wordpos">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Adds <b>msg.pos</b> as the anaylsis result.
</div>
</script>
<script type="text/x-red" data-help-name="wordpos">
<p>Analyses <b>msg.payload</b> and classifies the part-of-speech of each word.</p>
<p>The resulting message has <b>msg.pos</b> added with the results:</p>
<pre>{
nouns:[],
verbs:[],
adjectives:[],
adverbs:[],
rest:[]
}</pre>
<p>Note: a word may appear in multiple POS (eg, 'great' is both a noun and an adjective)</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('wordpos',{
category: 'analysis-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"wordpos";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,34 @@
/**
* 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 util = require("util");
var WordPos = require('wordpos');
var wordpos = new WordPos();
function WordPOSNode(n) {
RED.nodes.createNode(this,n);
this.on("input", function(msg) {
var node = this;
wordpos.getPOS(msg.payload, function (result) {
msg.pos = result;
node.send(msg);
});
});
}
RED.nodes.registerType("wordpos",WordPOSNode);

View File

@@ -0,0 +1,53 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="xml2js">
<div class="form-row">
<label>Use Console</label>
<input type="checkbox" id="node-input-useEyes" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-useEyes" style="width: 70%;">Debug output in console ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Uses xml2js to process xml into javascript object.</div>
</script>
<script type="text/x-red" data-help-name="xml2js">
<p>A function that parses the <b>msg.payload</b> using the xml2js library. Places the result in the payload.</p>
<p>See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a> for more information.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('xml2js',{
category: 'advanced-function',
color:"#E6E0F8",
defaults: {
useEyes: {value:"false"},
name: {value:""},
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"xml2js";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,45 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var util = require("util");
var parseString = require('xml2js').parseString;
var gotEyes = false;
try {
var eyes = require("eyes");
gotEyes = true;
} catch(e) {
util.log("[73-parsexml.js] Warning: Module 'eyes' not installed");
}
function Xml2jsNode(n) {
RED.nodes.createNode(this,n);
this.useEyes = n.useEyes;
var node = this;
this.on("input", function(msg) {
parseString(msg.payload, function (err, result) {
msg.payload = result;
node.send(msg);
if (node.useEyes == true) {
if (gotEyes == true) { eyes.inspect(msg); }
else { node.log(JSON.stringify(msg)); }
}
});
});
}
RED.nodes.registerType("xml2js",Xml2jsNode);

94
nodes/core/20-inject.html Normal file
View File

@@ -0,0 +1,94 @@
<!--
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="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">
<label for="node-input-payload"><i class="icon-envelope"></i> Payload</label>
<input type="text" id="node-input-payload" placeholder="Payload">
</div>
<div class="form-row node-input-repeat">
<label for="node-input-repeat"><i class="icon-repeat"></i> Repeat (S)</label>
<input type="text" id="node-input-repeat" placeholder="0">
</div>
<div class="form-row">
<label>&nbsp;</label>
<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>
</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: Injects Date.now() if no payload set.<br/>Repeat interval blank or 0 means no repeat.</div>
</script>
<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>If no payload is specified the payload is set to the current time in millisecs since 1970. This allows subsequent functions to perform time based actions.</p>
<p>The repeat function does what it says on the tin and continuously sends the payload every x seconds.</p>
<p>The Fire once at start option actually waits 50mS before firing to give other nodes a chance to instantiate properly.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('inject',{
category: 'input',
color:"#a6bbcf",
defaults: {
name: {value:""},
topic: {value:""},
payload: {value:""},
repeat: {value:""},
once: {value:false}
},
inputs:0,
outputs:1,
icon: "inject.png",
label: function() {
return this.name||this.topic||this.payload;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
button: {
onclick: function() {
var label = this.name||this.payload;
d3.xhr("inject/"+this.id).post(function(err,resp) {
if (err) {
if (err.status == 404) {
RED.notify("<strong>Error</strong>: inject node not deployed","error");
} else {
RED.notify("<strong>Error</strong>: "+err.response,"error");
}
} else if (resp.status == 200) {
RED.notify("Successfully injected: "+label,"success");
} else {
if (resp) {
RED.notify("<strong>Error</strong>: "+resp,"error");
} else {
RED.notify("<strong>Error</strong>: no response from server","error");
}
console.log(err,resp);
}
});
}
}
});
</script>

65
nodes/core/20-inject.js Normal file
View File

@@ -0,0 +1,65 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
function InjectNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.payload = n.payload;
this.repeat = n.repeat;
this.once = n.once;
var node = this;
this.interval_id = null;
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.repeat = this.repeat * 1000;
this.log("repeat = "+this.repeat);
this.interval_id = setInterval( function() {
node.emit("input",{});
}, this.repeat );
}
if (this.once) {
setTimeout( function(){ node.emit("input",{}); }, 50);
}
this.on("input",function(msg) {
var msg = {topic:this.topic,payload:this.payload};
if (msg.payload == "") { msg.payload = Date.now(); }
this.send(msg);
msg = null;
});
}
RED.nodes.registerType("inject",InjectNode);
InjectNode.prototype.close = function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
this.log("inject: repeat stopped");
}
}
RED.app.post("/inject/:id", function(req,res) {
var node = RED.nodes.getNode(req.params.id);
if (node != null) {
node.receive();
res.send(200);
} else {
res.send(404);
}
});

238
nodes/core/58-debug.html Normal file
View File

@@ -0,0 +1,238 @@
<!--
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="debug">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-complete" placeholder="Complete" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-complete" style="width: 70%;">Show complete msg object ?</label>
</div>
</script>
<script type="text/x-red" data-help-name="debug">
<p>The Debug node can be connected to the output of any node. It will display the timestamp, <b>msg.topic</b> and <b>msg.payload</b> fields of any messages it receives under the Debug tab at the top of the sidebar.</p>
<p>The button to the right of the node will toggle it's output on and off so you can de-clutter the debug window.</p>
<p>If the payload is an object it will be stringified first for display and indicate that by saying "(Object) ".</p>
<p>If the payload is a buffer it will be stringified first for display and indicate that by saying "(Buffer) ".</p>
<p>Selecting any particular message will highlight (in red) the debug node that reported it. This is useful if you wire up multiple debug nodes.</p>
<p>Optionally can show the complete msg object - but the screen can get messy.</p>
<p>In addition any calls to node.warn or node.error will appear here.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('debug',{
category: 'output',
defaults: {
name: {value:""},
active: {value:true},
complete: {value:false}
},
label: function() {
return this.name||"debug";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
color:"#87a980",
inputs:1,
outputs:0,
icon: "debug.png",
align: "right",
button: {
color: function() {
return (typeof this.active === 'undefined') ? ("#87a980" ) : (this.active ? "#87a980" : "#b9b9b9");
},
onclick: function() {
var label = this.name||"debug";
var node = this;
d3.xhr("debug/"+this.id).post(function(err,resp) {
if (err) {
if (err.status == 404) {
RED.notify("<strong>Error</strong>: debug node not deployed","error");
} else {
RED.notify("<strong>Error</strong>: "+err.response,"error");
}
} else if (resp.status == 200) {
RED.notify("Successfully activated: "+label,"success");
node.active = true;
node.dirty = true;
RED.view.redraw();
} else if (resp.status == 201) {
RED.notify("Successfully deactivated: "+label,"success");
node.active = false;
node.dirty = true;
RED.view.redraw();
} else {
if (resp) {
RED.notify("<strong>Error</strong>: "+resp,"error");
} else {
RED.notify("<strong>Error</strong>: no response from server","error");
}
console.log(err,resp);
}
});
}
}
});
var a = function() {
var content = document.createElement("div");
content.id = "tab-debug";
var toolbar = document.createElement("div");
toolbar.id = "debug-toolbar";
content.appendChild(toolbar);
toolbar.innerHTML = '<div class="btn-group pull-right"><a id="debug-tab-clear" title="clear log" class="btn btn-mini" href="#"><i class="icon-trash"></i></a></div> ';
var messages = document.createElement("div");
messages.id = "debug-content";
content.appendChild(messages);
RED.sidebar.addTab("debug",content);
function getTimestamp() {
var d = new Date();
return d.toUTCString().substring(5,25)+"."+d.getMilliseconds();
}
var sbc = document.getElementById("debug-content");
function debugConnect() {
//console.log("debug ws connecting");
var ws = new WebSocket("ws://"+location.hostname+":"+location.port+document.location.pathname+"/debug");
ws.onopen = function() {
//console.log("debug ws connected");
}
ws.onmessage = function(event) {
var o = JSON.parse(event.data);
//console.log(msg);
var msg = document.createElement("div");
msg.onmouseover = function() {
msg.style.borderRightColor = "#999";
RED.nodes.eachNode(function(node) {
if( node.id == o.id) {
node.highlighted = true;
node.dirty = true;
}
});
RED.view.redraw();
};
msg.onmouseout = function() {
msg.style.borderRightColor = "";
RED.nodes.eachNode(function(node) {
if( node.id == o.id) {
node.highlighted = false;
node.dirty = true;
}
});
RED.view.redraw();
};
var name = (o.name?o.name:o.id).toString().replace(/</g,"&lt;").replace(/>/g,"&gt;");
var topic = (o.topic||"").toString().replace(/</g,"&lt;").replace(/>/g,"&gt;");
var payload = (o.msg||"").toString().replace(/</g,"&lt;").replace(/>/g,"&gt;");
msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'')
msg.innerHTML = '<span class="debug-message-date">'+getTimestamp()+'</span>'+
'<span class="debug-message-name">['+name+']</span>'+
(o.topic?'<span class="debug-message-topic">'+topic+'</span>':'')+
'<span class="debug-message-payload">'+payload+'</span>';
var atBottom = (sbc.scrollHeight-messages.offsetHeight-sbc.scrollTop) < 5;
$(messages).append(msg);
if (atBottom) {
$(sbc).scrollTop(sbc.scrollHeight);
}
};
ws.onclose = function() {
//console.log("debug ws closed");
setTimeout(debugConnect,1000);
}
}
debugConnect();
$("#debug-tab-clear").click(function() {
$(".debug-message").remove();
RED.nodes.eachNode(function(node) {
node.highlighted = false;
node.dirty = true;
});
RED.view.redraw();
});
}();
</script>
<style>
#debug-content {
position: absolute;
top: 30px;
bottom: 0px;
left:0px;
right: 0px;
overflow-y: scroll;
}
#debug-toolbar {
padding: 3px 10px;
height: 24px;
background: #f3f3f3;
}
.debug-message {
cursor: pointer;
border-bottom: 1px solid #eee;
border-left: 8px solid #eee;
border-right: 8px solid #eee;
padding: 2px;
}
.debug-message-date {
background: #fff;
font-size: 9px;
color: #aaa;
padding: 1px 5px 1px 1px;
}
.debug-message-topic {
display: block;
background: #fff;
padding: 1px 5px;
font-size: 9px;
color: #caa;
}
.debug-message-name {
background: #fff;
padding: 1px 5px;
font-size: 9px;
color: #aac;
}
.debug-message-payload {
display: block;
padding: 2px;
background: #fff;
}
.debug-message-level-log {
border-left-color: #eee;
border-right-color: #eee;
}
.debug-message-level-warn {
border-left-color: #ffdf9d;
border-right-color: #ffdf9d;
}
.debug-message-level-error {
border-left-color: #f99;
border-right-color: #f99;
}
</style>

97
nodes/core/58-debug.js Normal file
View File

@@ -0,0 +1,97 @@
/**
* 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 util = require("util");
var ws = require('ws');
var events = require("events");
function DebugNode(n) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.complete = n.complete;
this.active = (n.active == null)||n.active;
this.on("input",function(msg) {
if (this.active) {
if (msg.payload instanceof Buffer) {
msg.payload = "(Buffer) "+msg.payload.toString();
}
if (this.complete) {
DebugNode.send({id:this.id,name:this.name,topic:msg.topic,msg:msg,_path:msg._path});
} else {
DebugNode.send({id:this.id,name:this.name,topic:msg.topic,msg:msg.payload,_path:msg._path});
}
}
});
}
RED.nodes.registerType("debug",DebugNode);
DebugNode.send = function(msg) {
if (msg.msg instanceof Error) {
msg.msg = msg.msg.toString();
} else if (typeof msg.msg === 'object') {
msg.msg = "(Object) "+JSON.stringify(msg.msg,null,1);
} else if (msg.msg == 0) msg.msg = "0";
for (var i in DebugNode.activeConnections) {
var ws = DebugNode.activeConnections[i];
try {
var p = JSON.stringify(msg);
ws.send(p);
} catch(err) {
util.log("[debug] ws error : "+err);
}
}
}
DebugNode.activeConnections = [];
DebugNode.wsServer = new ws.Server({server:RED.server});
DebugNode.wsServer.on('connection',function(ws) {
DebugNode.activeConnections.push(ws);
ws.on('close',function() {
for (var i in DebugNode.activeConnections) {
if (DebugNode.activeConnections[i] === ws) {
DebugNode.activeConnections.splice(i,1);
break;
}
}
});
});
DebugNode.logHandler = new events.EventEmitter();
DebugNode.logHandler.on("log",function(msg) {
if (msg.level == "warn" || msg.level == "error") {
DebugNode.send(msg);
}
});
RED.nodes.addLogHandler(DebugNode.logHandler);
RED.app.post("/debug/:id", function(req,res) {
var node = RED.nodes.getNode(req.params.id);
if (node != null) {
if (node.active) {
node.active = false;
res.send(201);
} else {
node.active = true;
res.send(200);
}
} else {
res.send(404);
}
});

63
nodes/core/75-exec.html Normal file
View File

@@ -0,0 +1,63 @@
<!--
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="exec">
<div class="form-row">
<label for="node-input-command"><i class="icon-label"></i> Command</label>
<input type="text" id="node-input-command" placeholder="command">
</div>
<div class="form-row">
<label for="node-input-append"><i class="icon-label"></i> Append</label>
<input type="text" id="node-input-append" placeholder="extra input">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-useSpawn" placeholder="spawn" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-useSpawn" style="width: 70%;">Use spawn() instead of exec() ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="exec">
<p>Call to a system command.<br/>This can be very dangerous...</p>
<p>Provides 3 outputs... stdout, stderr, and return code.</p>
<p>By default uses exec() which calls the command, waits for completion and then returns the complete result in one go. Along with any errors.</p>
<p>Optionally can use spawn() instead, which returns output from stdout and stderr as the command runs (ie one line at a time). On completion then returns a return code (on the 3rd output).</p>
<p>The optional append gets added to the command after the <b>msg.payload</b> (so you can do things like pipe etc.)</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('exec',{
category: 'advanced-function',
color:"darksalmon",
defaults: {
command: {value:"",required:true},
append: {value:""},
useSpawn: {value:""},
name: {value:""}
},
inputs:1,
outputs:3,
icon: "arrow-in.png",
align: "right",
label: function() {
return this.name||this.command;
}
});
</script>

76
nodes/core/75-exec.js Normal file
View File

@@ -0,0 +1,76 @@
/**
* 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 spawn = require('child_process').spawn;
var exec = require('child_process').exec;
function ExecNode(n) {
RED.nodes.createNode(this,n);
this.cmd = n.command;
this.append = n.append || "";
this.useSpawn = n.useSpawn;
var node = this;
this.on("input", function(msg) {
if (msg != null) {
if (this.useSpawn == true) {
// make the extra args into an array
// then prepend with the msg.payload
var arg = node.append.split(",");
if (msg.payload != " ") { arg.unshift(msg.payload); }
console.log(arg);
var ex = spawn(node.cmd,arg);
ex.stdout.on('data', function (data) {
//console.log('[exec] stdout: ' + data);
msg.payload = data;
node.send([msg,null,null]);
});
ex.stderr.on('data', function (data) {
//console.log('[exec] stderr: ' + data);
msg.payload = data;
node.send([null,msg,null]);
});
ex.on('close', function (code) {
//console.log('[exec] result: ' + code);
msg.payload = code;
node.send([null,null,msg]);
});
}
else {
var cl = node.cmd+" "+msg.payload+" "+node.append;
node.log(cl);
var child = exec(cl, function (error, stdout, stderr) {
msg.payload = stdout;
var msg2 = {payload:stderr};
//console.log('[exec] stdout: ' + stdout);
//console.log('[exec] stderr: ' + stderr);
if (error !== null) {
var msg3 = {payload:error};
//console.log('[exec] error: ' + error);
}
node.send([msg,msg2,msg3]);
});
}
}
});
}
RED.nodes.registerType("exec",ExecNode);

View File

@@ -0,0 +1,96 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="function">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-func"><i class="icon-wrench"></i> Function</label>
<input type="hidden" id="node-input-func">
<div style="height: 250px;" class="node-text-editor" id="node-input-func-editor" ></div>
</div>
<div class="form-row">
<label for="node-input-outputs"><i class="icon-random"></i> Outputs</label>
<input id="node-input-outputs" style="width: 60px; height: 1.7em;" value="1">
</div>
</script>
<script type="text/x-red" data-help-name="function">
<p>The generic function block where you can write code to do more interesting things.</p>
<p>You can have multiple outputs - in which case the function expects you to return an array of messages... <pre>return [msg,msg2,...];</pre></p>
<p>You may return null for any or all outputs if you want to.</p>
<p>You can save your functions to a library (button to right of name field) - and likewise pick from that library.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('function',{
color:"#fdd0a2",
category: 'function',
defaults: {
name: {value:""},
func: {value:"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n// console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n// context = {};\n\n\nreturn msg;"},
outputs: {value:1}
},
inputs:1,
outputs:1,
icon: "function.png",
label: function() {
return this.name;
},
oneditprepare: function() {
$( "#node-input-outputs" ).spinner({
min:1
});
function functionDialogResize(ev,ui) {
$("#node-input-func-editor").css("height",(ui.size.height-235)+"px");
};
$( "#dialog" ).on("dialogresize", functionDialogResize);
$( "#dialog" ).one("dialogopen", function(ev) {
var size = $( "#dialog" ).dialog('option','sizeCache-function');
if (size) {
functionDialogResize(null,{size:size});
}
});
$( "#dialog" ).one("dialogclose", function(ev,ui) {
var height = $( "#dialog" ).dialog('option','height');
$( "#dialog" ).off("dialogresize",functionDialogResize);
});
var that = this;
require(["orion/editor/edit"], function(edit) {
that.editor = edit({
parent:document.getElementById('node-input-func-editor'),
lang:"js",
contents: $("#node-input-func").val()
});
RED.library.create({
url:"functions", // where to get the data from
type:"function", // the type of object the library is for
editor:that.editor, // the field name the main text body goes to
fields:['name','outputs']
});
});
},
oneditsave: function() {
$("#node-input-func").val(this.editor.getText())
delete this.editor;
}
});
</script>

71
nodes/core/80-function.js Normal file
View File

@@ -0,0 +1,71 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var util = require("util");
var vm = require("vm");
var fs = require('fs');
var fspath = require('path');
function FunctionNode(n) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.func = n.func;
var functionText = "var results = (function(msg){"+this.func+"})(msg);";
this.topic = n.topic;
this.context = {global:RED.settings.functionGlobalContext || {}};
try {
this.script = vm.createScript(functionText);
this.on("input", function(msg) {
if (msg != null) {
var sandbox = {msg:msg,console:console,util:util,Buffer:Buffer,context:this.context};
try {
this.script.runInNewContext(sandbox);
var results = sandbox.results;
if (results == null) {
results = [];
} else if (results.length == null) {
results = [results];
}
if (msg._topic) {
for (var m in results) {
if (results[m]) {
if (util.isArray(results[m])) {
for (var n in results[m]) {
results[m][n]._topic = msg._topic;
}
} else {
results[m]._topic = msg._topic;
}
}
}
}
this.send(results);
} catch(err) {
this.error(err.message);
}
}
});
} catch(err) {
this.error(err.message);
}
}
RED.nodes.registerType("function",FunctionNode);
RED.library.register("functions");

View File

@@ -0,0 +1,91 @@
<!--
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="template">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-template"><i class="icon-wrench"></i> Template</label>
<input type="hidden" id="node-input-template">
<div style="height: 250px;" class="node-text-editor" id="node-input-template-editor" ></div>
</div>
</script>
<script type="text/x-red" data-help-name="template">
<p>Creates new messages based on a template.</p>
<p>Very useful for creating boilerplate web pages, emails, tweets and so on.</p>
<p>Uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_new">Mustache</a></i> format.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('template',{
color:"rgb(243, 181, 103)",
category: 'function',
defaults: {
name: {value:""},
template: {value:"This is the payload: {{payload}}!"},
},
inputs:1,
outputs:1,
icon: "template.png",
label: function() {
return this.name;
},
oneditprepare: function() {
function templateDialogResize(ev,ui) {
$("#node-input-template-editor").css("height",(ui.size.height-200)+"px");
};
$( "#dialog" ).on("dialogresize", templateDialogResize);
$( "#dialog" ).one("dialogopen", function(ev) {
var size = $( "#dialog" ).dialog('option','sizeCache-template');
if (size) {
templateDialogResize(null,{size:size});
}
});
$( "#dialog" ).one("dialogclose", function(ev,ui) {
var height = $( "#dialog" ).dialog('option','height');
$( "#dialog" ).off("dialogresize",templateDialogResize);
});
var that = this;
require(["orion/editor/edit"], function(edit) {
that.editor = edit({
parent:document.getElementById('node-input-template-editor'),
lang:"html",
contents: $("#node-input-template").val()
});
RED.library.create({
url:"templates", // where to get the data from
type:"template", // the type of object the library is for
editor:that.editor, // the field name the main text body goes to
fields:['name']
});
});
},
oneditsave: function() {
$("#node-input-template").val(this.editor.getText())
delete this.editor;
}
});
</script>

41
nodes/core/80-template.js Normal file
View File

@@ -0,0 +1,41 @@
/**
* 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 mustache = require("mustache");
var util = require("util");
var fs = require('fs');
function TemplateNode(n) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.template = n.template;
this.on("input", function(msg) {
if (msg != null) {
try {
msg.payload = mustache.render(this.template,msg)
this.send(msg);
} catch(err) {
this.error(err.message);
}
}
});
}
RED.nodes.registerType("template",TemplateNode);
RED.library.register("templates");

View File

@@ -0,0 +1,45 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="comment">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Comment</label>
<input type="text" id="node-input-name" placeholder="Comment">
</div>
</script>
<script type="text/x-red" data-help-name="comment">
<p>Simple comment block. More of a label really...</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('comment',{
category: 'function',
color:"#ffffff",
defaults: {
name: {value:""}
},
inputs:0,
outputs:0,
icon: "file.png",
label: function() {
return this.name||"comment";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

23
nodes/core/90-comment.js Normal file
View File

@@ -0,0 +1,23 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
function CommentNode(n) {
RED.nodes.createNode(this,n);
}
RED.nodes.registerType("comment",CommentNode);

View File

@@ -0,0 +1,148 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="arduino in">
<div class="form-row">
<label for="node-input-arduino"><i class="icon-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<input type="text" id="node-input-pin" placeholder="2">
</div>
<div class="form-row">
<label for="node-input-state"><i class="icon-wrench"></i> Type</label>
<select type="text" id="node-input-state" style="width: 150px;">
<option value="INPUT">Digital pin</option>
<option value="ANALOG">Analogue pin</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="arduino in">
<p>Arduino input node. Connects to local Arduino and monitors the selected pin for changes. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>You can select either Digital or Analogue input. Outputs the value read as <b>msg.payload</b> and the pin number as <b>msg.topic</b>.</p>
<p>It only outputs on a change of value - fine for digital inputs, but you can get a lot of data from analogue pins which you must then handle.</p>
<p>You can set the sample rate in ms from 20 to 65535.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino in',{
category: 'advanced-input',
color:"#c6dbef",
defaults: {
name: {value:""},
pin: {value:"",required:true},
state: {value:"INPUT",required:true},
arduino: {type:"arduino-board",required:true}
},
inputs:0,
outputs:1,
icon: "arduino.png",
label: function() {
var a = "";
if (this.state == "ANALOG") a = "A";
return this.name||"Pin: "+a+this.pin;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="arduino out">
<div class="form-row">
<label for="node-input-arduino"><i class="icon-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<input type="text" id="node-input-pin" placeholder="13">
</div>
<div class="form-row">
<label for="node-input-state"><i class="icon-wrench"></i> Type</label>
<select type="text" id="node-input-state" style="width: 150px;">
<option value="OUTPUT">Digital (0/1)</option>
<option value="PWM">Analogue (0-255)</option>
<option value="SERVO">Servo (0-180)</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="arduino out">
<p>Arduino output node. Connects to local Arduino and writes to the selected digital pin. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>You can select Digital, Analogue (PWM) or Servo type outputs. Expects a numeric value in <b>msg.payload</b>. The pin number is set in the properties panel.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino out',{
category: 'advanced-output',
color:"#c6dbef",
defaults: {
name: {value:""},
pin: {value:""},
state: {value:"",required:true},
arduino: {type:"arduino-board",required:true}
},
inputs:1,
outputs:0,
icon: "arduino.png",
align: "right",
label: function() {
return this.name||"Pin: "+this.pin;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="arduino-board">
<div class="form-row">
<label for="node-config-input-device"><i class="icon-bullhorn"></i> Arduino Port</label>
<input type="text" id="node-config-input-device" placeholder="ttyU" style="width:50%;">
</div>
<div class="form-row">
<label for="node-config-input-repeat"><i class="icon-repeat"></i> Sample (ms)</label>
<input type="text" id="node-config-input-repeat" placeholder="25">
</div>
<!-- <div class="form-row">
<label for="node-config-input-baud"><i class="icon-bullhorn"></i> Baudrate</label>
<input type="text" id="node-config-input-baud" placeholder="115200" style="width:50%;">
</div> -->
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino-board',{
category: 'config',
defaults: {
//baud: {baud:"57600",required:true},
repeat: {value:"25",required:true,validate:RED.validators.number()},
device: {value:"",required:true}
},
label: function() {
return this.device||"arduino";
}
});
</script>

View File

@@ -0,0 +1,167 @@
/**
* 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 util = require("util");
var firmata = require("firmata");
var arduinoReady = false;
var thisboard = null;
// The Board Definition - this opens (and closes) the connection
function ArduinoNode(n) {
RED.nodes.createNode(this,n);
this.device = n.device;
this.repeat = n.repeat||25;
util.log("[firmata] Opening"+this.device);
// var tou = setInterval(function() {
// if (!arduinoReady) {
// clearInterval(tou);
arduinoReady = false;
if (thisboard == null) {
this.board = new firmata.Board(this.device, function(err) {
if (err) {
util.log("[firmata] "+err);
return;
}
arduinoReady = true;
util.log('[firmata] Arduino connected');
});
thisboard = this.board;
}
else {
util.log("[firmata] Arduino already connected");
this.board = thisboard;
console.log(this.board._events);
this.board.removeAllListeners();
arduinoReady = true;
}
// } else { util.log("[firmata] Waiting for Firmata"); }
// }, 1000); // wait for firmata to disconnect from arduino
this._close = function() {
//this.board.sp.close(function() { console.log("[firmata] Serial port closed"); arduinoReady = false; });
util.log("[firmata] Stopped");
}
}
RED.nodes.registerType("arduino-board",ArduinoNode);
ArduinoNode.prototype.close = function() {
this._close();
}
// The Input Node
function DuinoNodeIn(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = n.pin;
this.state = n.state;
this.arduino = n.arduino;
this.serverConfig = RED.nodes.getNode(this.arduino);
this.board = this.serverConfig.board;
this.repeat = this.serverConfig.repeat;
var node = this;
var tout = setInterval(function() {
if (arduinoReady) {
clearInterval(tout);
console.log(node.state,node.pin,node.board.MODES[node.state]);
node.board.pinMode(node.pin, node.board.MODES[node.state]);
node.board.setSamplingInterval(node.repeat);
var oldrdg = "";
if (node.state == "ANALOG") {
node.board.analogRead(node.pin, function(data) {
var msg = {payload:data, topic:"A"+node.pin};
if (data != oldrdg) {
node.send(msg);
oldrdg = data;
}
});
}
else {
node.board.digitalRead(node.pin, function(data) {
var msg = {payload:data, topic:node.pin};
node.send(msg);
});
}
}
else { node.log("Waiting for Arduino"); }
}, 2000); // loop to wait for firmata to connect to arduino
this._close = function() {
clearInterval(this._interval);
util.log("[firmata] input eventlistener stopped");
}
}
RED.nodes.registerType("arduino in",DuinoNodeIn);
DuinoNodeIn.prototype.close = function() {
this._close();
}
// The Output Node
function DuinoNodeOut(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = n.pin;
this.state = n.state;
this.arduino = n.arduino;
this.serverConfig = RED.nodes.getNode(this.arduino);
this.board = this.serverConfig.board;
var node = this;
this.on("input", function(msg) {
//console.log(msg);
if (arduinoReady) {
if (node.state == "OUTPUT") {
if ((msg.payload == true)||(msg.payload == 1)||(msg.payload.toString().toLowerCase() == "on")) {
node.board.digitalWrite(node.pin, node.board.HIGH);
}
if ((msg.payload == false)||(msg.payload == 0)||(msg.payload.toString().toLowerCase() == "off")) {
node.board.digitalWrite(node.pin, node.board.LOW);
}
}
if (node.state == "PWM") {
msg.payload = msg.payload * 1;
if ((msg.payload >= 0) && (msg.payload <= 255)) {
//console.log(msg.payload, node.pin);
node.board.servoWrite(node.pin, msg.payload);
}
}
if (node.state == "SERVO") {
msg.payload = msg.payload * 1;
if ((msg.payload >= 0) && (msg.payload <= 180)) {
//console.log(msg.payload, node.pin);
node.board.servoWrite(node.pin, msg.payload);
}
}
}
//else { console.log("Arduino not ready"); }
});
var touo = setInterval(function() {
if (arduinoReady) {
clearInterval(touo);
//console.log(node.state,node.pin,node.board.MODES[node.state]);
node.board.pinMode(node.pin, node.board.MODES[node.state]);
}
}, 5000); // loop to wait for firmata to connect to arduino
}
RED.nodes.registerType("arduino out",DuinoNodeOut);

View File

@@ -0,0 +1,69 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="rpi-gpio in">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="7">7</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="18">18</option>
<option value="22">22</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-resistor"><i class=" icon-resize-full"></i> Resistor?</label>
<select type="text" id="node-input-resistor" style="width: 150px;">
<option value="no">no</option>
<option value="pullup">pullup</option>
<option value="pulldown">pulldown</option>
</select>
</div>
<div class="form-tips">Tip: if pull up/down resistor is selected, the <code>gpio-admin</code> command <em>must</em> be used
to do the actual enabling. If 'no' resistor is selected, nothing further needs to be done
to use this node. See <code>man gpio-admin</code> for more details.</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-gpio in',{
category: 'advanced-input',
color:"#c6dbef",
defaults: {
name: { value:""},
resistor: { value: "no"},
pin: {value:"",required:true},
},
inputs:0,
outputs:1,
icon: "rpi.png",
label: function() {
return this.name||"Pin: "+this.pin;
//+(this.resistor == "no"?"":" ("+(this.resistor=="pullup"?"":"&darr;")+")");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,71 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var gpio = require("pi-gpio");
function GPIOInNode(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = n.pin;
this.resistor = n.resistor;
var node = this;
if (this.pin) {
var setupPin = function(err) {
if (err) {
node.error(err);
} else {
node._interval = setInterval(function(){
gpio.read(node.pin, function(err, value) {
if(err){
node.error(err);
} else{
if(node.buttonState !== value){
var previousState = node.buttonState;
node.buttonState = value;
if (previousState !== -1) {
var msg = {payload:node.buttonState};
node.send(msg);
}
}
}
});
}, 50);
}
};
if (this.resistor == "no") {
gpio.open(this.pin,"input",setupPin());
} else {
// Assume enabled externally via gpio-admin
setupPin();
}
} else {
this.error("Invalid GPIO pin: "+this.pin);
}
}
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
GPIOInNode.prototype.close = function() {
clearInterval(this._interval);
if (this.resistor == "no") {
gpio.close(this.pin);
}
}

View File

@@ -0,0 +1,59 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="rpi-gpio out">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i> Pin</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="7">7</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="18">18</option>
<option value="22">22</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-gpio out',{
category: 'advanced-output',
color:"#c6dbef",
defaults: {
name: { value:""},
resistor: { value: "no"},
pin: {value:"",required:true},
},
inputs:1,
outputs:0,
icon: "rpi.png",
align: "right",
label: function() {
return this.name||"Pin: "+this.pin;
//+(this.resistor == "no"?"":" ("+(this.resistor=="pullup"?"":"&darr;")+")");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,51 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var gpio = require("pi-gpio");
function GPIOOutNode(n) {
RED.nodes.createNode(this,n);
this.pin = n.pin;
var node = this;
if (this.pin) {
gpio.open(this.pin,"output",function(err) {
if (err) {
node.error(err);
} else {
node.on("input",function(msg) {
gpio.write(node.pin,msg.payload,function(err) {
if (err) node.error(err);
});
});
}
});
} else {
this.error("Invalid GPIO pin: "+this.pin);
}
}
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
GPIOOutNode.prototype.close = function() {
gpio.close(this.pin);
}

View File

@@ -0,0 +1,47 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="blink">
<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">Expects a msg.payload with three part csv string of r,g,b.</div>
</script>
<script type="text/x-red" data-help-name="blink">
<p>Thingm Blink1 output node. Expects a msg.payload with a three part csv string of r,g,b.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('blink',{
category: 'output',
color:"cornsilk",
defaults: {
name: {value:""}
},
inputs:1,
outputs:0,
icon: "arrow-in.png",
align: "right",
label: function() {
return this.name||"blink1";
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,46 @@
/**
* 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 Blink1 = require("node-blink1");
function Blink1Node(n) {
RED.nodes.createNode(this,n);
var node = this;
try {
var blink1 = new Blink1.Blink1();
node.log("started");
this.on("input", function(msg) {
if (msg != null) {
var rgb = msg.payload.split(',');
// only do it if three parameters...
if (rgb.length == 3) {
blink1.setRGB( (rgb[0]*1)&255, (rgb[1]*1)&255, (rgb[2]*1)&255 );
}
else {
//let Andy do fancy colours by name here if he wants...
node.log("received "+msg.payload);
}
}
});
}
catch(e) {
node.error(e);
}
}
RED.nodes.registerType("blink",Blink1Node);

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

@@ -0,0 +1,121 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="mqtt in">
<div class="form-row">
<label for="node-input-broker"><i class="icon-tag"></i> Broker</label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mqtt in">
<p>MQTT input node. Connects to the specified broker and subscribes to the specified topic. The topic may contain MQTT wildcards.</p>
<p>Outputs an object called <b>msg</b> containing <b>msg.topic, msg.payload, msg.qos</b> and <b>msg.retain</b>. <b>msg.payload</b> is a String.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt in',{
category: 'input',
defaults: {
name: {value:""},
topic: {value:"",required:true},
broker: {type:"mqtt-broker", required:true}
},
color:"#c6dbef",
inputs:0,
outputs:1,
icon: "bridge.png",
label: function() {
return this.name||this.topic;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="mqtt out">
<div class="form-row">
<label for="node-input-broker"><i class="icon-tag"></i> Broker</label>
<input type="text" id="node-input-broker">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mqtt out">
<p>Connects to a MQTT broker and publishes <b>msg.payload</b> either to the <b>msg.topic</b> OR to the topic specified in the edit window. The value in the edit window has precedence.</p>
<p><b>msg.qos</b> and <b>msg.retain</b> may also optionally have been set. If not set they are set to 0 and false respectively.</p>
<p>If <b>msg.payload</b> contains a buffer or an object it will be stringified before being sent.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt out',{
category: 'output',
defaults: {
name: {value:""},
topic: {value:""},
broker: {type:"mqtt-broker", required:true}
},
color:"#c6dbef",
inputs:1,
outputs:0,
icon: "bridge.png",
align: "right",
label: function() {
return this.name||this.topic;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="mqtt-broker">
<div class="form-row node-input-broker">
<label for="node-config-input-broker"><i class="icon-bookmark"></i> Broker</label>
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="Broker" style="width: 40%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="Port" style="width:45px">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('mqtt-broker',{
category: 'config',
defaults: {
broker: {value:"localhost",required:true},
port: {value:1883,required:true,validate:RED.validators.number()}
},
label: function() {
return this.broker+":"+this.port;
}
});
</script>

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

@@ -0,0 +1,90 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var connectionPool = require("./lib/mqttConnectionPool");
var util = require("util");
function MQTTBrokerNode(n) {
RED.nodes.createNode(this,n);
this.broker = n.broker;
this.port = n.port;
}
RED.nodes.registerType("mqtt-broker",MQTTBrokerNode);
function MQTTInNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.broker = n.broker;
this.brokerConfig = RED.nodes.getNode(this.broker);
if (this.brokerConfig) {
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port);
var node = this;
this.client.subscribe(this.topic,2,function(topic,payload,qos,retain) {
var msg = {topic:topic,payload:payload,qos:qos,retain:retain};
if ((node.brokerConfig.broker == "localhost")||(node.brokerConfig.broker == "127.0.0.1")) {
msg._topic = topic;
}
node.send(msg);
});
this.client.connect();
} else {
this.error("missing broker configuration");
}
}
RED.nodes.registerType("mqtt in",MQTTInNode);
MQTTInNode.prototype.close = function() {
if (this.client) {
this.client.disconnect();
}
}
function MQTTOutNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.broker = n.broker;
this.brokerConfig = RED.nodes.getNode(this.broker);
if (this.brokerConfig) {
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port);
this.on("input",function(msg) {
if (msg != null) {
if (this.topic) {
msg.topic = this.topic;
}
this.client.publish(msg);
}
});
this.client.connect();
} else {
this.error("missing broker configuration");
}
}
RED.nodes.registerType("mqtt out",MQTTOutNode);
MQTTOutNode.prototype.close = function() {
if (this.client) {
this.client.disconnect();
}
}

85
nodes/io/21-httpin.html Normal file
View File

@@ -0,0 +1,85 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="http in">
<div class="form-row">
<label for="node-input-method"><i class="icon-tasks"></i> Method</label>
<select type="text" id="node-input-method" style="width: 150px;">
<option value="get">GET</option>
<option value="post">POST</option>
<option value="put">PUT</option>
<option value="delete">DELETE</option>
</select>
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-globe"></i> url</label>
<input type="text" id="node-input-url" placeholder="/url">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="http in">
<p>Provides an input node for http requests, allowing the creation of simple web services.</p>
<p>The resulting message has the following properties:
<ul>
<li>msg.req : <a href="http://expressjs.com/api.html#req">http request</a></li>
<li>msg.res : <a href="http://expressjs.com/api.html#res">http response</a></li>
</ul>
</p>
<p>For POST/PUT requests, the body is available under <code>msg.req.body</code>. This
uses the <a href="http://expressjs.com/api.html#bodyParser">Express bodyParser middleware</a> to parse the content to a JSON object.
</p>
<p>
By default, this expects the body of the request to be url encoded:
<pre>foo=bar&amp;this=that</pre>
</p>
<p>
To send JSON encoded data, the content-type header of the request must be set to
<code>application/json</code>.
</p>
<p>
<b>Note: </b>This node does not send any response to the http request. This must be done within
a subsequent Function node. The <a href="http://expressjs.com/api.html#res">Express response documentation</a>
describes how this should be done.
<pre>msg.res.send(200, 'Thanks for the request ');<br/>return msg;</pre>
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('http in',{
category: 'input',
color:"rgb(231, 231, 174)",
defaults: {
name: {value:""},
url: {value:"",required:true},
method: {value:"get",required:true}
},
inputs:0,
outputs:1,
icon: "white-globe.png",
label: function() {
return this.name||"["+this.method+"] "+this.url;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

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

@@ -0,0 +1,51 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var util = require("util");
function HTTPIn(n) {
RED.nodes.createNode(this,n);
this.url = n.url;
this.method = n.method;
var node = this;
this.callback = function(req,res) {
node.send({req:req,res:res});
}
if (this.method == "get") {
RED.app.get(this.url,this.callback);
} else if (this.method == "post") {
RED.app.post(this.url,this.callback);
} else if (this.method == "put") {
RED.app.put(this.url,this.callback);
} else if (this.method == "delete") {
RED.app.delete(this.url,this.callback);
}
}
HTTPIn.prototype.close = function() {
var routes = redUI.app.routes[this.method];
for (var i in routes) {
if (routes[i].path == this.url) {
routes.splice(i,1);
break;
}
}
}
RED.nodes.registerType("http in",HTTPIn);

53
nodes/io/23-watch.html Normal file
View File

@@ -0,0 +1,53 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="watch">
<div class="form-row node-input-filename">
<label for="node-input-files"><i class="icon-file"></i> File(s)</label>
<input type="text" id="node-input-files" placeholder="File(s) or Directory">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="watch">
<p>Watches a file or directory for any changes.</p>
<p>You can enter a list of comma separated files, or directories if you like. You will need to put " around any that have spaces in.</p>
<p>The filename of the file that actually changed is put into <b>msg.payload</b>, while a stringified version of the watched criteria is returned in <b>msg.topic</b>.</p>
<p>Of course in Linux, <i>everything</i> could be a file and thus watched...</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('watch',{
category: 'advanced-input',
defaults: {
name: {value:""},
files: {value:"",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "watch.png",
label: function() {
return this.name||this.files;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

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

@@ -0,0 +1,45 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var notify = require("fs.notify");
function WatchNode(n) {
RED.nodes.createNode(this,n);
this.files = n.files.split(",");
for (var f in this.files) {
this.files[f] = this.files[f].trim();
}
var node = this;
var notifications = new notify(this.files);
notifications.on('change', function (file) {
node.log('file changed '+file);
var msg = { payload: file, topic: JSON.stringify(node.files) };
node.send(msg);
});
this._close = function() {
notifications.close();
}
}
RED.nodes.registerType("watch",WatchNode);
WatchNode.prototype.close = function() {
this._close();
}

146
nodes/io/25-serial.html Normal file
View File

@@ -0,0 +1,146 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="serial in">
<div class="form-row node-input-serial">
<label for="node-input-serial"><i class="icon-bullhorn"></i> Serial Port</label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="serial in">
<p>Reads data from a local serial port.</p>
<p>Keeps reading from the serial port until it sees \n (default) or the character(s) requested. Only sets <b>msg.payload</b>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial in',{
category: 'input',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "serial.png",
label: function() {
return this.name||(this.serial)?RED.nodes.node(this.serial).label():"serial";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="serial out">
<div class="form-row node-input-serial">
<label for="node-input-serial"><i class="icon-bullhorn"></i> Serial Port</label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="serial out">
<p>Provides a connection to an outbound serial port.</p>
<p>Only the <b>msg.payload</b> is sent.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial out',{
category: 'output',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:1,
outputs:0,
icon: "serial.png",
align: "right",
label: function() {
return this.name||((this.serial)?RED.nodes.node(this.serial).label():"serial");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="serial-port">
<div class="form-row">
<label for="node-config-input-serialport"><i class="icon-bullhorn"></i> Serial Port</label>
<input type="text" id="node-config-input-serialport" placeholder="/dev/ttyUSB0" style="width:50%;">
</div>
<div class="form-row">
<label for="node-config-input-serialbaud"><i class="icon-wrench"></i> Baud Rate</label>
<select type="text" id="node-config-input-serialbaud" style="width: 150px;">
<option value="115200">115200</option>
<option value="57600">57600</option>
<option value="38400">38400</option>
<option value="19200">19200</option>
<option value="9600">9600</option>
<option value="4800">4800</option>
<option value="2400">2400</option>
<option value="1800">1800</option>
<option value="1200">1200</option>
<option value="600">600</option>
<option value="300">300</option>
<option value="200">200</option>
<option value="150">150</option>
<option value="134">134</option>
<option value="110">110</option>
<option value="75">75</option>
<option value="50">50</option>
</select>
</div>
<div class="form-row">
<label for="node-config-input-newline"><i class="icon-text-width"></i> New line</label>
<input type="text" id="node-config-input-newline">
</div>
<!--
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name">
</div>
-->
</script>
<script type="text/javascript">
RED.nodes.registerType('serial-port',{
category: 'config',
defaults: {
//name: {value:""},
serialport: {value:"",required:true},
serialbaud: {value:57600,required:true},
newline: {value:"\\n"}
},
label: function() {
//return this.name||this.serialport;
return this.serialport+":"+this.serialbaud;
}
});
</script>

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

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

69
nodes/io/30-socketin.html Normal file
View File

@@ -0,0 +1,69 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="socket in">
<div class="form-row">
<label for="node-input-transport"><i class="icon-tasks"></i> Type</label>
<select type="text" id="node-input-transport" style="width: 150px;">
<option value="http">http listen</option>
<option value="tcp">tcp server</option>
<!-- <option value="tcpc">tcp client</option> -->
<option value="udp">udp socket</option>
</select>
</div>
<div class="form-row">
<label for="node-input-port"><i class="icon-random"></i> Port</label>
<input type="text" id="node-input-port" placeholder="Port">
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: sends the received data as a Buffer object.</div>
</script>
<script type="text/x-red" data-help-name="socket in">
<p>Provides a input node for http, tcp or udp sockets. Topic is optional. These are server like sockets.</p>
<p>The TCP and UDP sockets produce a <i>BUFFER</i> object msg.payload and NOT a String. If you need a String then use .toString() on msg.payload in your next function block.</p>
<p>TCP and UDP sockets also provide <b>msg.fromip</b> of the form ipaddress:port</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('socket in',{
category: 'input',
color:"Silver",
defaults: {
name: {value:""},
topic: {value:""},
port: {value:"",required:true},
transport: {value:"tcp",required:true}
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
return this.name||this.topic||(this.transport+":"+this.port);
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

134
nodes/io/30-socketin.js Normal file
View File

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

View File

@@ -0,0 +1,66 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="socket out">
<div class="form-row">
<label for="node-input-host"><i class="icon-bookmark"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;" >
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label for="node-input-transport"><i class="icon-tasks"></i> Type</label>
<select type="text" id="node-input-transport" style="width: 150px;">
<option value="http">http</option>
<option value="tcp">tcp</option>
<option value="udp">udp</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="socket out">
<p>Provides a choice of http, tcp or udp output connections. All connect, send their <b>msg.payload</b> and disconnect.</p>
<p>To use broadcast select udp and set the host to be either the subnet broadcast address required or 255.255.255.255</p>
<p>If you need a response from an http request use the httpget node instead.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('socket out',{
category: 'output',
color:"Silver",
defaults: {
host: {value:"127.0.0.1",required:true},
port: {value:"",required:true},
name: {value:""},
transport: {value:"tcp",required:true}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
return this.name||this.topic||(this.transport+":"+this.port);
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

63
nodes/io/30-socketout.js Normal file
View File

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

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

@@ -0,0 +1,79 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="tcp in">
<div class="form-row">
<label for="node-input-host"><i class="icon-bookmark"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;" >
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-server" placeholder="server" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-server" style="width: 70%;">Be a server socket ?</label>
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Base64 encode payload ?</label>
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: sends the received data as a Buffer object (not a String).<br/>If you select server socket the host defaults to localhost.</div>
</script>
<script type="text/x-red" data-help-name="tcp in">
<p>Provides a choice of tcp input connections. Can either be a client - or provide a listening socket.</p>
<p>The TCP node produces a <i>BUFFER</i> object <b></b>msg.payload</b> and NOT a String. If you need a String then use <i>.toString()</i> on <b>msg.payload</b> in your next function block.</p>
<p>It also provides <b>msg.fromip</b> of the form ipaddress:port .</p>
<p>You can select Base64 encoding if you want to make it easy to preserve the complete message as a string.</p>
<p>In case of disconnection the client trys to reconnect every 10 secs. Topic is optional.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tcp in',{
category: 'input',
color:"Silver",
defaults: {
host: {value:"127.0.0.1",required:true},
port: {value:"",required:true,validate:RED.validators.number()},
base64: {value:false,required:true},
server: {value:false,required:true},
topic: {value:""},
name: {value:""}
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
if ((this.host!="") & (this.port!="")) {
return this.name||(this.host+":"+this.port);
}
else { return "tcp in"; }
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

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

@@ -0,0 +1,103 @@
/**
* 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 reConnect = RED.settings.socketReconnectTime||10000;
var net = require('net');
function TcpIn(n) {
RED.nodes.createNode(this,n);
this.host = n.host;
this.port = n.port * 1;
this.topic = n.topic;
this.base64 = n.base64;
this.server = n.server;
var node = this;
if (!node.server) {
var client;
var to;
function setupTcpClient() {
node.log('connecting to port '+node.port);
client = net.connect(node.port, node.host, function() {
node.log("input connected to "+node.host+":"+node.port);
});
client.on('data', function (data) {
var msg;
if (node.base64) { msg = { topic:node.topic, payload:new Buffer(data).toString('base64') }; }
else { msg = {topic:node.topic, payload:data}; }
node.send(msg);
});
client.on('end', function() {
node.log("ended");
});
client.on('close', function() {
client.destroy();
node.log('closed');
to = setTimeout(setupTcpClient, reConnect);
});
client.on('error', function(err) {
node.log('error : '+err);
//to = setTimeout(setupTcpClient, reConnect);
});
}
setupTcpClient();
this._close = function() {
client.end();
clearTimeout(to);
node.log('input stopped');
}
}
else {
var server = net.createServer(function (socket) {
var buffer = null;
socket.on('data', function (chunk) {
//if (buffer == null) {
// buffer = chunk;
//} else {
//buffer = Buffer.concat([buffer,chunk]);
var msg = {topic:node.topic, payload:chunk, fromip:socket.remoteAddress+':'+socket.remotePort};
node.send(msg);
//}
});
socket.on('end', function() {
var msg = {topic:node.topic, payload:buffer, fromip:socket.remoteAddress+':'+socket.remotePort};
node.send(msg);
});
});
server.listen(node.port);
node.log('socket input on port '+node.port);
this._close = function() {
server.close();
node.log('socket input stopped');
}
}
}
RED.nodes.registerType("tcp in",TcpIn);
TcpIn.prototype.close = function() {
this._close();
}

73
nodes/io/31-tcpout.html Normal file
View File

@@ -0,0 +1,73 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="tcp out">
<div class="form-row">
<label for="node-input-host"><i class="icon-bookmark"></i> Host</label>
<input type="text" id="node-input-host" placeholder="localhost" style="width: 40%;" >
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-beserver" placeholder="server" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-beserver" style="width: 70%;">Be a server socket ?</label>
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 message ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: If you select server socket the host defaults to localhost.</div>
</script>
<script type="text/x-red" data-help-name="tcp out">
<p>Provides a choice of tcp output connections. Can either connect out - or provide a socket connection.</p>
<p>Only <b>msg.payload</b> is sent.</p>
<p>You can select Base64 decoding if you want to decode a message encoded by the input socket.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tcp out',{
category: 'output',
color:"Silver",
defaults: {
host: {value:"127.0.0.1",required:true},
port: {value:"",required:true},
beserver: {value:false,required:true},
base64: {value:false,required:true},
name: {value:""}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
var lab = this.host+":"+this.port;
if (this.server) lab = "tcp out:"+this.port;
return this.name||lab;
},
labelStyle: function() {
return (this.name)?"node_label_italic":"";
}
});
</script>

98
nodes/io/31-tcpout.js Normal file
View File

@@ -0,0 +1,98 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var reConnect = RED.settings.socketReconnectTime||10000;
var net = require('net');
function TcpOut(n) {
RED.nodes.createNode(this,n);
this.host = n.host;
this.port = n.port * 1;
this.base64 = n.base64;
this.beserver = n.beserver;
this.name = n.name;
var node = this;
if (!node.beserver) {
var client = new net.Socket();
var to;
function setupTcpClient() {
client.connect(node.port, node.host, function() {
node.log("output connected to "+node.host+":"+node.port);
});
client.on('error', function (err) {
node.error('error : '+err);
to = setTimeout(setupTcpClient, reConnect);
});
client.on('end', function (err) {
node.log("output disconnected");
to = setTimeout(setupTcpClient, reConnect);
});
client.on('close', function() {
client.destroy();
node.log('closed');
to = setTimeout(setupTcpClient, reConnect);
});
node.on("input", function(msg) {
if (msg.payload != null) {
if (node.base64) { client.write(new Buffer(msg.payload,'base64')); }
else { client.write(msg.payload);}
}
});
}
setupTcpClient();
this._close = function() {
client.end();
clearTimeout(to);
node.log('output stopped');
}
}
else {
var server = net.createServer(function (socket) {
socket.on("connect",function() {
node.log("Connection from "+socket.remoteAddress);
});
node.on("input", function(msg) {
if (msg.payload != null) {
if (node.base64) { socket.write(new Buffer(msg.payload,'base64')); }
else { socket.write(msg.payload);}
}
});
});
server.listen(node.port);
node.log('socket output on port '+node.port);
this._close = function() {
server.close();
node.log('output stopped');
}
}
}
RED.nodes.registerType("tcp out",TcpOut);
TcpOut.prototype.close = function() {
this._close();
}

131
nodes/io/32-multicast.html Normal file
View File

@@ -0,0 +1,131 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The Input Node -->
<script type="text/x-red" data-template-name="multicast in">
<div class="form-row">
<label for="node-input-group"><i class="icon-tasks"></i> Group</label>
<input type="text" id="node-input-group" placeholder="225.0.18.83" style="width: 40%;">
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label for="node-input-iface"><i class="icon-globe"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="eth0">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Base64 encode payload ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: sends the received data as a Buffer object (not a String).<br/>Make sure your firewall will allow the data in.</div>
</script>
<script type="text/x-red" data-help-name="multicast in">
<p>A multicast udp input node, that produces a <b>msg.payload</b> containing a <i>BUFFER</i> object and NOT a String.</p>
<p>If you need a String then use <i>.toString()</i> on <b>msg.payload</b> in your next function block.</p>
<p>It also provides <b>msg.fromip</b> of the form ipaddress:port .</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('multicast in',{
category: 'input',
color:"Silver",
defaults: {
name: {value:""},
group: {value:"",required:true},
host: {value:""},
iface: {value:""},
port: {value:"",required:true,validate:RED.validators.number()},
base64: {value:false,required:true},
multicast: {value:"true"}
},
inputs:0,
outputs:1,
icon: "bridge-dash.png",
label: function() {
if ((this.group!="") & (this.port!="")) {
return this.name||(this.group+":"+this.port);
}
else { return "multicast in"; }
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<!-- The Output Node -->
<script type="text/x-red" data-template-name="multicast out">
<div class="form-row">
<label for="node-input-group"><i class="icon-tasks"></i> Group</label>
<input type="text" id="node-input-group" placeholder="225.0.18.83" style="width: 40%;">
<label for="node-input-port" style="margin-left: 10px; width: 35px;"> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width: 45px">
</div>
<div class="form-row">
<label for="node-input-iface"><i class="icon-globe"></i> Interface</label>
<input type="text" id="node-input-iface" placeholder="eth0">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-base64" placeholder="base64" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-base64" style="width: 70%;">Decode Base64 encoded payload ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="multicast out">
<p>This node sends <b>msg.payload</b> to the designated multicast group and port.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('multicast out',{
category: 'output',
color:"Silver",
defaults: {
name: {value:""},
group: {value:"",required:true},
host: {value:""},
iface: {value:""},
port: {value:"",required:true,validate:RED.validators.number()},
base64: {value:false,required:true},
multicast: {value:"true"}
},
inputs:1,
outputs:0,
icon: "bridge-dash.png",
align: "right",
label: function() {
if ((this.group!="") & (this.port!="")) {
return this.name||(this.group+":"+this.port);
}
else { return "multicast out"; }
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

118
nodes/io/32-multicast.js Normal file
View File

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

59
nodes/io/90-httpget.html Normal file
View File

@@ -0,0 +1,59 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="httpget">
<div class="form-row">
<label for="node-input-baseurl"><i class="icon-tasks"></i> Base URL</label>
<input type="text" id="node-input-baseurl" placeholder="http(s)://url">
</div>
<div class="form-row">
<label for="node-input-append"><i class="icon-tasks"></i> Append</label>
<input type="text" id="node-input-append" placeholder="">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The <b>Base URL</b> gets prepended to whatever payload is passed in. Leave blank if you pass in a full url.<br/>The append gets added to the end after any payload.<br/>The output Topic is the same as the input Topic.</div>
</script>
<script type="text/x-red" data-help-name="httpget">
<p>Performs an HTTP or HTTPS GET and returns the fetched page.</p>
<p>The return code is placed in <b>msg.rc</b>, and the full text of the result is in <b>msg.payload</b>.</p>
<p>The <b>msg.payload</b> is added to the base url, and then the optional append is added after.</p>
<p>This is mostly suitable for small pages as large results will need a lot of parsing....</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('httpget',{
category: 'advanced-function',
color:"rgb(231, 231, 174)",
defaults: {
name: {value:""},
baseurl: {value:""},
append: {value:""}
},
inputs:1,
outputs:1,
icon: "white-globe.png",
label: function() {
return this.name||this.baseurl||"http(s) get";
},
labelStyle: function() {
return (this.name||!this.baseurl)?"node_label_italic":"";
}
});
</script>

52
nodes/io/90-httpget.js Normal file
View File

@@ -0,0 +1,52 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
function HttpGet(n) {
RED.nodes.createNode(this,n);
this.baseurl = n.baseurl || "";
this.append = n.append || "";
var node = this;
if (this.baseurl.substring(0,5) === "https") { var http = require("https"); }
else { var http = require("http"); }
this.on("input", function(msg) {
msg._payload = msg.payload;
//util.log("[httpget] "+this.baseurl+msg.payload+this.append);
http.get(this.baseurl+msg.payload+this.append, function(res) {
node.log("Http response: " + res.statusCode);
msg.rc = res.statusCode;
msg.payload = "";
if ((msg.rc != 200) && (msg.rc != 404)) {
node.send(msg);
}
res.setEncoding('utf8');
res.on('data', function(chunk) {
msg.payload += chunk;
});
res.on('end', function() {
node.send(msg);
});
}).on('error', function(e) {
//node.error(e);
msg.rc = 503;
msg.payload = e;
node.send(msg);
});
});
}
RED.nodes.registerType("httpget",HttpGet);

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

@@ -0,0 +1,225 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require("util");
var mqtt = require("mqtt");
var events = require("events");
//var Client = module.exports.Client = function(
var port = 1883;
var host = "localhost";
function MQTTClient(port,host) {
this.port = port||1883;
this.host = host||"localhost";
this.messageId = 1;
this.pendingSubscriptions = {};
this.inboundMessages = {};
this.lastOutbound = (new Date()).getTime();
this.lastInbound = (new Date()).getTime();
this.connected = false;
this._nextMessageId = function() {
this.messageId += 1;
if (this.messageId > 0xFFFF) {
this.messageId = 1;
}
return this.messageId;
}
events.EventEmitter.call(this);
}
util.inherits(MQTTClient, events.EventEmitter);
MQTTClient.prototype.connect = function(options) {
var self = this;
options = options||{};
self.options = options;
self.options.keepalive = options.keepalive||15;
self.options.clean = self.options.clean||true;
self.options.protocolId = 'MQIsdp';
self.options.protocolVersion = 3;
self.client = mqtt.createConnection(this.port,this.host,function(err,client) {
if (err) {
self.emit('connectionlost',err);
return;
}
client.on('close',function(e) {
clearInterval(self.watchdog);
if (self.connected) {
self.connected = false;
self.emit('connectionlost',e);
} else {
self.emit('disconnect');
}
});
client.on('error',function(e) {
clearInterval(self.watchdog);
if (self.connected) {
self.connected = false;
self.emit('connectionlost',e);
}
});
client.on('connack',function(packet) {
if (packet.returnCode == 0) {
self.watchdog = setInterval(function(self) {
var now = (new Date()).getTime();
if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) {
if (self.pingOutstanding) {
// DO DISCONNECT
} else {
self.lastOutbound = (new Date()).getTime();
self.lastInbound = (new Date()).getTime();
self.pingOutstanding = true;
self.client.pingreq();
}
}
},self.options.keepalive*500,self);
self.lastInbound = (new Date()).getTime()
self.connected = true;
self.emit('connect');
} else {
self.connected = false;
self.emit('connectionlost');
}
});
client.on('suback',function(packet) {
self.lastInbound = (new Date()).getTime()
var topic = self.pendingSubscriptions[packet.messageId];
self.emit('subscribe',topic,packet.granted[0]);
delete self.pendingSubscriptions[packet.messageId];
});
client.on('unsuback',function(packet) {
self.lastInbound = (new Date()).getTime()
var topic = self.pendingSubscriptions[packet.messageId];
self.emit('unsubscribe',topic,packet.granted[0]);
delete self.pendingSubscriptions[packet.messageId];
});
client.on('publish',function(packet) {
self.lastInbound = (new Date()).getTime()
if (packet.qos < 2) {
var p = packet;
self.emit('message',p.topic,p.payload,p.qos,p.retain);
} else {
self.inboundMessages[packet.messageId] = packet;
this.lastOutbound = (new Date()).getTime()
self.client.pubrec(packet);
}
if (packet.qos == 1) {
this.lastOutbound = (new Date()).getTime()
self.client.puback(packet);
}
});
client.on('pubrel',function(packet) {
self.lastInbound = (new Date()).getTime()
var p = self.inboundMessages[packet.messageId];
self.emit('message',p.topic,p.payload,p.qos,p.retain);
delete self.inboundMessages[packet.messageId];
self.lastOutbound = (new Date()).getTime()
self.client.pubcomp(packet);
});
client.on('puback',function(packet) {
self.lastInbound = (new Date()).getTime()
// outbound qos-1 complete
});
client.on('pubrec',function(packet) {
self.lastInbound = (new Date()).getTime()
self.lastOutbound = (new Date()).getTime()
self.client.pubrel(packet);
});
client.on('pubcomp',function(packet) {
self.lastInbound = (new Date()).getTime()
// outbound qos-2 complete
});
client.on('pingresp',function(packet) {
self.lastInbound = (new Date()).getTime()
self.pingOutstanding = false;
});
this.lastOutbound = (new Date()).getTime()
client.connect(self.options);
});
}
MQTTClient.prototype.subscribe = function(topic,qos) {
var self = this;
if (self.connected) {
var options = {
subscriptions:[{topic:topic,qos:qos}],
messageId: self._nextMessageId()
};
this.pendingSubscriptions[options.messageId] = topic;
this.lastOutbound = (new Date()).getTime()
self.client.subscribe(options);
}
}
MQTTClient.prototype.unsubscribe = function(topic) {
var self = this;
if (self.connected) {
var options = {
topic:topic,
messageId: self._nextMessageId()
};
this.pendingSubscriptions[options.messageId] = topic;
this.lastOutbound = (new Date()).getTime()
self.client.unsubscribe(options);
}
}
MQTTClient.prototype.publish = function(topic,payload,qos,retain) {
var self = this;
if (self.connected) {
if (Buffer.isBuffer(payload)) {
payload = payload.toString();
} else if (typeof payload === "object") {
payload = JSON.stringify(payload);
} else if (typeof payload !== "string") {
payload = ""+payload;
}
var options = {
topic: topic,
payload: payload,
qos: qos||0,
retain:retain||false
};
if (options.qos != 0) {
options.messageId = self._nextMessageId();
}
this.lastOutbound = (new Date()).getTime()
self.client.publish(options);
}
}
MQTTClient.prototype.disconnect = function() {
var self = this;
if (this.connected) {
this.connected = false;
this.client.disconnect();
}
}
MQTTClient.prototype.isConnected = function() {
return this.connected;
}
module.exports.createClient = function(port,host) {
var mqtt_client = new MQTTClient(port,host);
return mqtt_client;
}

View File

@@ -0,0 +1,119 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require("util");
var mqtt = require("./mqtt");
var settings = require("../../../red/red").settings;
var connections = {};
function matchTopic(ts,t) {
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/#$/,".*"));
return re.test(t);
}
module.exports = {
get: function(broker,port) {
var id = broker+":"+port;
if (!connections[id]) {
connections[id] = function() {
var client = mqtt.createClient(port,broker);
var options = {keepalive:15,clientId:'mqtt_' + (1+Math.random()*4294967295).toString(16)};
var queue = [];
var subscriptions = [];
var connecting = false;
var obj = {
_instances: 0,
publish: function(msg) {
if (client.isConnected()) {
client.publish(msg.topic,msg.payload,msg.qos,msg.retain);
} else {
if (!connecting) {
connecting = true;
client.connect(options);
}
queue.push(msg);
}
},
subscribe: function(topic,qos,callback) {
subscriptions.push({topic:topic,qos:qos,callback:callback});
client.on('message',function(mtopic,mpayload,mqos,mretain) {
if (matchTopic(topic,mtopic)) {
callback(mtopic,mpayload,mqos,mretain);
}
});
if (client.isConnected()) {
client.subscribe(topic,qos);
}
},
on: function(a,b){
client.on(a,b);
},
once: function(a,b){
client.once(a,b);
},
connect: function() {
if (!client.isConnected() && !connecting) {
connecting = true;
client.connect(options);
}
},
disconnect: function() {
this._instances -= 1;
if (this._instances == 0) {
client.disconnect();
client = null;
delete connections[id];
}
}
};
client.on('connect',function() {
util.log('[mqtt] connected to broker tcp://'+broker+':'+port);
connecting = false;
for (var s in subscriptions) {
var topic = subscriptions[s].topic;
var qos = subscriptions[s].qos;
var callback = subscriptions[s].callback;
client.subscribe(topic,qos);
}
//console.log("connected - publishing",queue.length,"messages");
while(queue.length) {
var msg = queue.shift();
//console.log(msg);
client.publish(msg.topic,msg.payload,msg.qos,msg.retain);
}
});
client.on('connectionlost', function(err) {
util.log('[mqtt] connection lost to broker tcp://'+broker+':'+port);
setTimeout(function() {
if (client) {
client.connect(options);
}
}, settings.mqttReconnectTime||5000);
});
client.on('disconnect', function() {
util.log('[mqtt] disconnected from broker tcp://'+broker+':'+port);
});
return obj
}();
}
connections[id]._instances += 1;
return connections[id];
}
};

View File

@@ -0,0 +1,192 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="twitter-credentials">
<div class="form-row" id="node-config-twitter-row"></div>
<input type="hidden" id="node-config-input-screen_name">
</script>
<script type="text/javascript">
var twitterConfigNodeId = null;
var twitterConfigNodeIntervalId = null;
function showTwitterAuthStart() {
var pathname = document.location.pathname;
if (pathname.slice(-1) != "/") {
pathname += "/";
}
var callback = encodeURIComponent(location.protocol+"//"+location.hostname+":"+location.port+pathname+"twitter/"+twitterConfigNodeId+"/auth/callback");
$("#node-config-twitter-row").html('Click <a id="node-config-twitter-start" href="/twitter/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank">here</a> to authenticate with Twitter.');
$("#node-config-twitter-start").click(function() {
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
});
}
function updateTwitterScreenName(sn) {
$("#node-config-input-screen_name").val(sn);
$("#node-config-twitter-row").html('<label><i class="icon-user"></i> Twitter ID</label><span class="input-xlarge uneditable-input">'+sn+'</span>');
}
function pollTwitterCredentials(e) {
$.getJSON('twitter/'+twitterConfigNodeId,function(data) {
if (data.sn) {
updateTwitterScreenName(data.sn);
twitterConfigNodeIntervalId = null;
} else {
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
}
})
}
RED.nodes.registerType('twitter-credentials',{
category: 'config',
defaults: {
screen_name: {value:""},
access_token: {value: ""},
access_token_secret: {value:""}
},
label: function() {
return this.screen_name;
},
exportable: false,
oneditprepare: function() {
twitterConfigNodeId = this.id;
if (!this.screen_name || this.screen_name == "") {
showTwitterAuthStart();
} else {
$.getJSON('twitter/'+twitterConfigNodeId,function(data) {
if (data.sn) {
updateTwitterScreenName(data.sn);
} else {
showTwitterAuthStart();
}
});
}
},
oneditsave: function() {
if (twitterConfigNodeIntervalId) {
window.clearTimeout(twitterConfigNodeIntervalId);
}
},
oneditcancel: function(adding) {
if (twitterConfigNodeIntervalId) {
window.clearTimeout(twitterConfigNodeIntervalId);
}
if (adding) {
$.ajax({
url: 'twitter/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
},
ondelete: function() {
$.ajax({
url: 'twitter/'+this.id,
type: 'DELETE',
success: function(result) {}
});
}
});
</script>
<script type="text/x-red" data-template-name="twitter in">
<div class="form-row">
<label for="node-input-twitter"><i class="icon-user"></i> Twitter</label>
<input type="text" id="node-input-twitter">
</div>
<div class="form-row">
<label for="node-input-tags"><i class="icon-tag"></i> Tags</label>
<input type="text" id="node-input-tags" placeholder="comma-separated tags">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-user" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<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 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: the Senders name gets appended to the topic heirarchy
</div>
</script>
<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>Sets the <b>msg.topic</b> to the configured topic and then appends the senders screen name.</p>
<p>Sets <b>msg.location</b> to the tweeters location if known.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('twitter in',{
category: 'social-input',
color:"#C0DEED",
defaults: {
twitter: {type:"twitter-credentials",required:true},
tags: {value:"",required:true},
user: {value:false},
name: {value:""},
topic: {value:"tweets"}
},
inputs:0,
outputs:1,
icon: "twitter.png",
label: function() {
return this.name||this.topic;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="twitter out">
<div class="form-row">
<label for="node-input-twitter"><i class="icon-user"></i> Twitter</label>
<input type="text" id="node-input-twitter">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="twitter out">
<p>Twitter out node. Tweets the <b>msg.payload</b>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('twitter out',{
category: 'social-output',
color:"#C0DEED",
defaults: {
twitter: {type:"twitter-credentials",required:true},
name: {value:"Tweet"}
},
inputs:1,
outputs:0,
icon: "twitter.png",
align: "right",
label: function() {
return this.name;
}
});
</script>

211
nodes/social/27-twitter.js Normal file
View File

@@ -0,0 +1,211 @@
/**
* 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 ntwitter = require('ntwitter');
var OAuth= require('oauth').OAuth;
function TwitterNode(n) {
RED.nodes.createNode(this,n);
this.screen_name = n.screen_name;
}
RED.nodes.registerType("twitter-credentials",TwitterNode);
function TwitterInNode(n) {
RED.nodes.createNode(this,n);
this.active = true;
this.user = n.user;
this.tags = n.tags.replace(/ /g,'');
this.twitter = n.twitter;
this.topic = n.topic;
this.twitterConfig = RED.nodes.getNode(this.twitter);
var credentials = RED.nodes.getCredentials(this.twitter);
if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
var twit = new ntwitter({
consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
access_token_key: credentials.access_token,
access_token_secret: credentials.access_token_secret
});
var node = this;
if (this.tags !== "") {
try {
var thing = 'statuses/filter';
if (this.user) { thing = 'user'; }
function setupStream() {
if (node.active) {
twit.stream(thing, { track: [node.tags] }, function(stream) {
//twit.stream('user', { track: [node.tags] }, function(stream) {
//twit.stream('statuses/filter', { track: [node.tags] }, function(stream) {
node.stream = stream;
stream.on('data', function(tweet) {
//console.log(tweet.user);
if (tweet.user !== undefined) {
var where = tweet.user.location||"";
var la = tweet.lang || tweet.user.lang;
//console.log(tweet.user.location,"=>",tweet.user.screen_name,"=>",pay);
var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, location:where, lang:la, tweet:tweet };
node.send(msg);
}
});
stream.on('error', function(tweet) {
node.warn(tweet);
setTimeout(setupStream,5000);
});
stream.on('destroy', function (response) {
if (this.active) {
node.warn("twitter ended unexpectedly");
setTimeout(setupStream,5000);
}
});
});
}
}
setupStream();
}
catch (err) {
node.error(err);
}
} else {
this.error("Invalid tag property");
}
} else {
this.error("missing twitter credentials");
}
}
RED.nodes.registerType("twitter in",TwitterInNode);
TwitterInNode.prototype.close = function() {
if (this.stream) {
this.active = false;
this.stream.destroy();
}
}
function TwitterOutNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.twitter = n.twitter;
this.twitterConfig = RED.nodes.getNode(this.twitter);
var credentials = RED.nodes.getCredentials(this.twitter);
var node = this;
if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
var twit = new ntwitter({
consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
access_token_key: credentials.access_token,
access_token_secret: credentials.access_token_secret
}).verifyCredentials(function (err, data) {
if (err) {
node.error("Error verifying credentials: " + err);
} else {
node.on("input", function(msg) {
if (msg != null) {
if (msg.payload.length > 140) {
msg.payload = msg.payload.slice(0,139);
node.warn("Tweet greater than 140 : truncated");
}
twit.updateStatus(msg.payload, function (err, data) {
if (err) node.error(err);
});
}
});
}
});
}
}
RED.nodes.registerType("twitter out",TwitterOutNode);
var oa = new OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
"OKjYEd1ef2bfFolV25G5nQ",
"meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
"1.0",
null,
"HMAC-SHA1"
);
var credentials = {};
RED.app.get('/twitter/:id', function(req,res) {
var credentials = RED.nodes.getCredentials(req.params.id);
if (credentials) {
res.send(JSON.stringify({sn:credentials.screen_name}));
} else {
res.send(JSON.stringify({}));
}
});
RED.app.delete('/twitter/:id', function(req,res) {
RED.nodes.deleteCredentials(req.params.id);
res.send(200);
});
RED.app.get('/twitter/:id/auth', function(req, res){
var credentials = {};
oa.getOAuthRequestToken({
oauth_callback: req.query.callback
},function(error, oauth_token, oauth_token_secret, results){
if (error) {
console.log(error);
res.send("yeah no. didn't work.")
}
else {
credentials.oauth_token = oauth_token;
credentials.oauth_token_secret = oauth_token_secret;
res.redirect('https://twitter.com/oauth/authorize?oauth_token='+oauth_token)
RED.nodes.addCredentials(req.params.id,credentials);
}
});
});
RED.app.get('/twitter/:id/auth/callback', function(req, res, next){
var credentials = RED.nodes.getCredentials(req.params.id);
credentials.oauth_verifier = req.query.oauth_verifier;
oa.getOAuthAccessToken(
credentials.oauth_token,
credentials.token_secret,
credentials.oauth_verifier,
function(error, oauth_access_token, oauth_access_token_secret, results){
if (error){
console.log(error);
res.send("yeah something broke.");
} else {
credentials = {};
credentials.access_token = oauth_access_token;
credentials.access_token_secret = oauth_access_token_secret;
credentials.screen_name = "@"+results.screen_name;
RED.nodes.addCredentials(req.params.id,credentials);
res.send("<html><head></head><body>Authorised - you can close this window and return to Node-RED</body></html>");
}
}
);
});

View File

@@ -0,0 +1,57 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="feedparse">
<div class="form-row">
<label for="node-input-url"><i class="icon-globe"></i> Feed url</label>
<input type="text" id="node-input-url">
</div>
<div class="form-row">
<label for="node-input-interval"><i class="icon-repeat"></i> Repeat <span style="font-size: 0.9em;">(M)</span></label>
<input type="text" id="node-input-interval" placeholder="minutes">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<!-- <div class="form-tips"></div> -->
</script>
<script type="text/x-red" data-help-name="feedparse">
<p>Monitors an RSS/atom feed for new entries.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('feedparse',{
category: 'advanced-input',
color:"#C0DEED",
defaults: {
name: {value:""},
url: {value:"", required:true},
interval: { value:15, required: true,validate:RED.validators.number()}
},
inputs:0,
outputs:1,
icon: "feed.png",
label: function() {
return this.name||this.url;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,75 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var FeedParser = require("feedparser");
var request = require("request");
function FeedParseNode(n) {
RED.nodes.createNode(this,n);
this.url = n.url;
this.interval = (parseInt(n.interval)||15)*60000;
var node = this;
this.interval_id = null;
this.seen = {};
if (this.url !== "") {
var getFeed = function() {
request(node.url,function(err) {
if (err) node.error(err);
})
.pipe(new FeedParser({feedurl:node.url}))
.on('error', function(error) {
node.error(error);
})
.on('meta', function (meta) {})
.on('article', function (article) {
if (!(article.guid in node.seen) || ( node.seen[article.guid] != 0 && node.seen[article.guid] != article.date.getTime())) {
node.seen[article.guid] = article.date?article.date.getTime():0;
var msg = {
topic:article.origlink||article.link,
payload: article.description,
article: {
summary:article.summary,
link:article.link,
date: article.date,
pubdate: article.pubdate,
author: article.author,
guid: article.guid,
}
};
node.send(msg);
}
})
.on('end', function () {
});
};
this.interval_id = setInterval(getFeed,node.interval);
getFeed();
} else {
this.error("Invalid url");
}
}
RED.nodes.registerType("feedparse",FeedParseNode);
FeedParseNode.prototype.close = function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
}
}

View File

@@ -0,0 +1,55 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="notify">
<div class="form-row">
<label for="node-input-title"><i class="icon-tag"></i> Title</label>
<input type="text" id="node-input-title" placeholder="Node-RED">
</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="notify">
<p>Uses Growl to provide a desktop popup containing the <b>msg.payload</b>. Only useful on the local machine.</p>
<p>Optionally uses <b>msg.topic</b> as the title.</p>
<p>Uses Growl so should work cross platform but will need pre-reqs installed... see <i><a href="https://npmjs.org/package/growl" target="_new">this link.</a></i></p>
<p>If installing on Windows you MUST read the install instructions ... especially the bit about adding growlnotify to your path... or it WON'T work.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('notify',{
category: 'output',
defaults: {
title: {value:""},
name: {value:""}
},
color:"#a7c9a0",
inputs:1,
outputs:0,
icon: "alert.png",
align: "right",
label: function() {
return this.name||this.title||"notify";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

39
nodes/social/57-notify.js Normal file
View File

@@ -0,0 +1,39 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var growl = require('growl');
var imagefile = __dirname+"/../../public/mqtt-node-red.png";
function NotifyNode(n) {
RED.nodes.createNode(this,n);
this.title = n.title;
var node = this;
this.on("input",function(msg) {
var titl = this.title||msg.topic;
if (typeof(msg.payload) == 'object') {
msg.payload = JSON.stringify(msg.payload);
}
if (typeof(titl) != 'undefined') {
growl(msg.payload, { title: titl, image: imagefile });
}
else {
growl(msg.payload, { image: imagefile });
}
});
}
RED.nodes.registerType("notify",NotifyNode);

View File

@@ -0,0 +1,61 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="prowl">
<div class="form-row">
<label for="node-input-title"><i class="icon-tag"></i> Title</label>
<input type="text" id="node-input-title" placeholder="Node-RED">
</div>
<div class="form-row">
<label for="node-input-priority"><i class="icon-tag"></i> Priority</label>
<input type="text" id="node-input-priority" placeholder="0">
</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="prowl">
<p>Uses Prowl to push the <b>msg.payload</b> to an Apple device that has the prowl app installed.</p>
<p>Optionally uses <b>msg.topic</b> to set the title. You can also set <b>msg.priority</b> to confgure the urgency from -2 (low), through 0 (normal) to 2 (urgent).</p>
<p>You MUST configure your prowl API key into the pushkey.js file in the directory above node-red.</p>
<p><pre>module.exports = { prowl:'My-API-KEY' }</pre></p>
<p>Uses Prowl so see <i><a href="https://www.prowlapp.com" target="_new">this link</a></i> for more details.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('prowl',{
category: 'output',
defaults: {
title: {value:""},
priority: {value:0,required:true,validate:RED.validators.number()},
name: {value:""}
},
color:"#a7c9a0",
inputs:1,
outputs:0,
icon: "prowl.png",
align: "right",
label: function() {
return this.name||this.title||"prowl";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

56
nodes/social/57-prowl.js Normal file
View File

@@ -0,0 +1,56 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var Prowl = require('node-prowl');
// pushkey.js just needs to be like (with the quotes)
// module.exports = {prowl:'My-API-KEY'}
try {
var pushkey = require("../../settings").prowl || require("../../../pushkey.js");
} catch(err) {
throw new Error("Failed to load Prowl credentials");
}
var prowl = new Prowl(pushkey.prowl);
function ProwlNode(n) {
RED.nodes.createNode(this,n);
this.title = n.title;
this.priority = n.priority * 1;
if (this.priority > 2) this.priority = 2;
if (this.priority < -2) this.priority = -2;
var node = this;
this.on("input",function(msg) {
var titl = this.title||msg.topic||"Node-RED";
var pri = msg.priority||this.priority;
if (typeof(msg.payload) == 'object') {
msg.payload = JSON.stringify(msg.payload);
}
try {
prowl.push(msg.payload, titl, { priority: pri }, function( err, remaining ){
if ( err ) node.error(err);
node.log( remaining + ' calls to Prowl api during current hour.' );
});
}
catch (err) {
node.error(err);
}
});
}
RED.nodes.registerType("prowl",ProwlNode);

View File

@@ -0,0 +1,60 @@
<!--
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="pushbullet">
<div class="form-row">
<label for="node-input-title"><i class="icon-tag"></i> Title</label>
<input type="text" id="node-input-title" placeholder="Node-RED">
</div>
<div class="form-row">
<label for="node-input-priority"><i class="icon-tag"></i> Priority</label>
<input type="text" id="node-input-priority" placeholder="0">
</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="pushbullet">
<p>Uses PushBullet to push the <b>msg.payload</b> to an Android device that has PushBullet app installed.</p>
<p>Optionally uses <b>msg.topic</b> to set the title.</p>
<p>You MUST configure both your API key and the target device ID into the pushkey.js file in the directory above node-red.<p>
<p><pre>>module.exports = { pushbullet:'My-API-KEY', deviceid:'12345' }</pre</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('pushbullet',{
category: 'output',
defaults: {
title: {value:""},
priority: {value:0,required:true,validate:RED.validators.number()},
name: {value:""}
},
color:"#a7c9a0",
inputs:1,
outputs:0,
icon: "bullet.png",
align: "right",
label: function() {
return this.name||this.title||"pushbullet";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@@ -0,0 +1,54 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var PushBullet = require('pushbullet');
// pushkey.js just needs to be like (with the quotes)
// module.exports = {pushbullet:'My-API-KEY', deviceid:'12345'}
try {
var pushkey = require("../../settings").pushbullet || require("../../../pushkey.js");
} catch(err) {
throw new Error("Failed to load PushBullet credentials");
}
var pusher = new PushBullet(pushkey.pushbullet);
var deviceId = pushkey.deviceid;
function PushbulletNode(n) {
RED.nodes.createNode(this,n);
this.title = n.title;
this.device
var node = this;
this.on("input",function(msg) {
var titl = this.title||msg.topic||"Node-RED";
if (typeof(msg.payload) == 'object') {
msg.payload = JSON.stringify(msg.payload);
}
try {
pusher.note(deviceId, titl, msg.payload, function(err, response) {
if ( err ) node.error(err);
node.log( JSON.stringify(response) );
});
}
catch (err) {
node.error(err);
}
});
}
RED.nodes.registerType("pushbullet",PushbulletNode);

View File

@@ -0,0 +1,50 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="email">
<div class="form-row">
<label for="node-input-name"><i class="icon-envelope"></i> To</label>
<input type="text" id="node-input-name" placeholder="email@address.com">
</div>
</script>
<script type="text/x-red" data-help-name="email">
<p>Sends the <b>msg.payload</b> as an email, with a subject of <b>msg.topic</b>.</p>
<p>It sends the message to the configured recipient <i>only</i>.</p>
<p><b>msg.topic</b> is used to set the subject of the email, and <b>msg.payload</b> is the body text.</p>
<p>Uses the nodemailer module - you also need to pre-configure your email SMTP settings in ../../emailkeys.js - see INSTALL file for details.</p>
<p><pre>module.exports = { service: "Gmail", user: "blahblah@gmail.com", pass: "password", server: "imap.gmail.com", port: "993" }</pre></p>
</script>
<script type="text/javascript">
RED.nodes.registerType('email',{
category: 'social-output',
color:"#c7e9c0",
defaults: {
name: {value:"",required:true}
},
inputs:1,
outputs:0,
icon: "envelope.png",
align: "right",
label: function() {
return this.name;
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

55
nodes/social/61-email.js Normal file
View File

@@ -0,0 +1,55 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var nodemailer = require("nodemailer");
var emailkey = require("../../../emailkeys.js");
var smtpTransport = nodemailer.createTransport("SMTP",{
service: emailkey.service,
auth: {
user: emailkey.user,
pass: emailkey.pass
}
});
function EmailNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.name = n.name;
var node = this;
this.on("input", function(msg) {
//node.log("email :",this.id,this.topic," received",msg.payload);
if (msg != null) {
smtpTransport.sendMail({
from: emailkey.user, // sender address
to: node.name, // comma separated list of receivers
subject: msg.topic, // Subject line
text: msg.payload // plaintext body
}, function(error, response) {
if (error) {
node.error(error);
} else {
node.log("Message sent: " + response.message);
}
});
}
});
}
RED.nodes.registerType("email",EmailNode);

54
nodes/social/61-imap.html Normal file
View File

@@ -0,0 +1,54 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="imap">
<div class="form-row node-input-repeat">
<label for="node-input-repeat"><i class="icon-repeat"></i>Repeat (S)</label>
<input type="text" id="node-input-repeat" placeholder="300">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="imap">
<p>Repeatedly gets a <b>single email</b> from an IMAP server and forwards on as a msg if not already seen.</p>
<p>The subject is loaded into <b>msg.topic</b> and <b>msg.payload</b> is the body text. <b>msg.from</b> is also set if you need it.</p>
<p>Uses the imap module - you also need to pre-configure your email settings in ../../emailkeys.js - see INSTALL file for details.</p>
<p><pre>module.exports = { service: "Gmail", user: "blahblah@gmail.com", pass: "password", server: "imap.gmail.com", port: "993" }</pre></p>
<p><b>Note:</b> this node <i>only</i> gets the most recent single email from the inbox, so set the repeat (polling) time appropriately.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('imap',{
category: 'social-input',
color:"#c7e9c0",
defaults: {
repeat: {value:"300",required:true},
name: {value:""}
},
inputs:0,
outputs:1,
icon: "envelope.png",
label: function() {
return this.name||"IMAP";
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
}
});
</script>

114
nodes/social/61-imap.js Normal file
View File

@@ -0,0 +1,114 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var Imap = require('imap');
var inspect = require('util').inspect;
var oldmail = {};
try {
var emailkey = require("../../settings").email || require("../../../emailkeys.js");
} catch(err) {
throw new Error("Failed to load Email credentials");
}
var imap = new Imap({
user: emailkey.user,
password: emailkey.pass,
host: emailkey.server||"imap.gmail.com",
port: emailkey.port||"993",
secure: true
});
function show(obj) {
return inspect(obj, false, Infinity);
}
function fail(err) {
console.log('[imap] : ' + err);
}
function openInbox(cb) {
imap.connect(function(err) {
if (err) fail(err);
imap.openBox('INBOX', true, cb);
});
}
function ImapNode(n) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.repeat = n.repeat * 1000;
var node = this;
this.interval_id = null;
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.log("repeat = "+this.repeat);
this.interval_id = setInterval( function() {
node.emit("input",{});
}, this.repeat );
}
this.on("input", function(msg) {
openInbox(function(err, mailbox) {
if (err) fail(err);
imap.seq.fetch(mailbox.messages.total + ':*', { struct: false },
{ headers: ['from', 'subject'],
body: true,
cb: function(fetch) {
fetch.on('message', function(msg) {
//node.log('Saw message no. ' + msg.seqno);
var pay = {};
var body = '';
msg.on('headers', function(hdrs) {
pay.from = hdrs.from[0];
pay.topic = hdrs.subject[0];
});
msg.on('data', function(chunk) {
body += chunk.toString('utf8');
});
msg.on('end', function() {
pay.payload = body;
if ((pay.topic !== oldmail.topic)|(pay.payload !== oldmail.payload)) {
oldmail = pay;
//node.log("From: "+pay.from);
node.log("Subj: "+pay.topic);
//node.log("Body: "+pay.payload);
node.send(pay);
}
});
});
}
}, function(err) {
if (err) node.log("Err : "+err);
//node.log("Done fetching messages.");
imap.logout();
}
);
});
});
}
RED.nodes.registerType("imap",ImapNode);
ImapNode.prototype.close = function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
this.log("inject: repeat stopped");
}
}

126
nodes/social/91-irc.html Normal file
View File

@@ -0,0 +1,126 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="irc in">
<div class="form-row">
<label for="node-input-ircserver"><i class="icon-tasks"></i> IRC Server</label>
<input type="text" id="node-input-ircserver">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="irc in">
<p>Connects to a channel on an IRC server</p>
<p>Any messages on that channel will appear on the <b>msg.payload</b> at the output, while <b>msg.topic</b> will contain who it is from.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc in',{
category: 'social-input',
defaults: {
name: {value:""},
ircserver: {type:"irc-server", required:true}
},
color:"Silver",
inputs:0,
outputs:1,
icon: "hash.png",
label: function() {
return this.name||(this.ircserver)?RED.nodes.node(this.ircserver).label():"irc";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="irc out">
<div class="form-row">
<label for="node-input-ircserver"><i class="icon-tasks"></i> IRC Server</label>
<input type="text" id="node-input-ircserver">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-sendObject" placeholder="" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-sendObject" style="width: 70%;">Send complete msg object ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Sending complete object will stringify the whole msg object before sending.</div>
</script>
<script type="text/x-red" data-help-name="irc out">
<p>Connects 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>You can either just send the <b>msg.payload</b>, or you can send the complete <b>msg</b> object.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc out',{
category: 'social-output',
defaults: {
name: {value:""},
sendObject: {value:false},
ircserver: {type:"irc-server", required:true}
},
color:"Silver",
inputs:1,
outputs:0,
icon: "hash.png",
align: "right",
label: function() {
return this.name || (this.ircserver)?RED.nodes.node(this.ircserver).label():"irc";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="irc-server">
<div class="form-row">
<label for="node-config-input-server"><i class="icon-tasks"></i> IRC Server</label>
<input type="text" id="node-config-input-server" placeholder="irc.UK-IRC.net">
</div>
<div class="form-row">
<label for="node-config-input-channel"><i class="icon-tasks"></i> Channel</label>
<input type="text" id="node-config-input-channel" placeholder="#testing1234">
</div>
<div class="form-row">
<label for="node-config-input-nickname"><i class="icon-tasks"></i> Nickname</label>
<input type="text" id="node-config-input-nickname" placeholder="joe123">
</div>
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc-server',{
category: 'config',
defaults: {
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)},
server: {value:"",required:true},
nickname: {value:"",required:true}
},
label: function() {
return this.server+":"+this.channel;
}
});
</script>

84
nodes/social/91-irc.js Normal file
View File

@@ -0,0 +1,84 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var irc = require("irc");
// The Server Definition - this opens (and closes) the connection
function IRCServerNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
this.channel = n.channel;
this.nickname = n.nickname;
this.ircclient = new irc.Client(this.server, this.nickname, {
channels: [this.channel]
});
this._close = function() {
this.ircclient.disconnect();
}
}
RED.nodes.registerType("irc-server",IRCServerNode);
IRCServerNode.prototype.close = function() {
this._close();
}
// The Input Node
function IrcInNode(n) {
RED.nodes.createNode(this,n);
this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.ircserver);
this.ircclient = this.serverConfig.ircclient;
var node = this;
this.ircclient.addListener('message', function (from, to, message) {
console.log(from + ' => ' + to + ': ' + message);
var msg = { "topic":from, "to":to, "payload":message };
node.send(msg);
});
this.ircclient.addListener('error', function(message) {
node.error(JSON.stringify(message));
});
}
RED.nodes.registerType("irc in",IrcInNode);
// The Output Node
function IrcOutNode(n) {
RED.nodes.createNode(this,n);
this.sendAll = n.sendObject;
this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.ircserver);
this.ircclient = this.serverConfig.ircclient;
this.channel = this.serverConfig.channel;
var node = this;
this.on("input", function(msg) {
console.log(msg);
if (node.sendAll) {
node.ircclient.say(node.channel, JSON.stringify(msg));
}
else {
var to = msg.topic || node.channel;
node.ircclient.say(to, msg.payload);
}
});
}
RED.nodes.registerType("irc out",IrcOutNode);

80
nodes/social/92-xmpp.html Normal file
View File

@@ -0,0 +1,80 @@
<!--
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="xmpp">
<div class="form-row">
<label for="node-input-server"><i class="icon-bookmark"></i> Server</label>
<input type="text" id="node-input-server" placeholder="talk.google.com" style="width: 40%;" >
<label for="node-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-input-port" placeholder="Port" style="width:45px">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-sendObject" placeholder="" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-sendObject" style="width: 70%;">Send complete msg object ?</label>
</div>
<div class="form-row">
<label for="node-input-to"><i class="icon-envelope"></i> To</label>
<input type="text" id="node-input-to" placeholder="joe@gmail.com">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-join" placeholder="" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-join" style="width: 70%;">Is a Chat Room ?</label>
</div>
<div class="form-row">
<label for="node-input-nick"><i class="icon-user"></i> Nickname</label>
<input type="text" id="node-input-nick" placeholder="Joe">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The <b>To</b> field is optional. If not set uses the <b>msg.topic</b> property of the message.</div>
</script>
<script type="text/x-red" data-help-name="xmpp">
<p>Connects to an XMPP server to send and receive messages.</p>
<p>Incoming messages will appear as <b>msg.payload</b> on the first output, while <b>msg.topic</b> will contain who it is from.</p>
<p>The second output will user presence and status in <b>msg.payload</b>.</p>
<p>The <b>To</b> field is optional. If not set uses the <b>msg.topic</b> property of the message.</p>
<p>If you are joining a room then the <b>To</b> field must be filled in.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('xmpp',{
category: 'advanced-function',
color:"Silver",
defaults: {
name: {value:""},
server: { value:"",required:true},
port: {value:5222,required:true},
to: {value:""},
join: {value:"false"},
sendObject: { value:false },
nick: {value:""}
},
inputs:1,
outputs:2,
icon: "xmpp.png",
label: function() {
return this.name||this.server||"xmpp";
},
labelStyle: function() {
return (this.name||!this.server)?"node_label_italic":"";
}
});
</script>

113
nodes/social/92-xmpp.js Normal file
View File

@@ -0,0 +1,113 @@
/**
* 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 xmpp = require('simple-xmpp');
try {
var xmppkey = require("../../settings").xmpp || require("../../../xmppkeys.js");
} catch(err) {
throw new Error("Failed to load XMPP credentials");
}
function XmppNode(n) {
RED.nodes.createNode(this,n);
this.server = n.server;
this.port = n.port;
this.join = n.join || false;
this.nick = n.nick || "Node-RED";
this.sendAll = n.sendObject;
this.to = n.to || "";
var node = this;
setTimeout(function() {
xmpp.connect({
jid : xmppkey.jid,
password : xmppkey.password,
host : this.server,
port : this.port,
skipPresence : true,
reconnect : false
});
}, 5000);
xmpp.on('online', function() {
node.log('connected to '+node.server);
xmpp.setPresence('online', node.nick+' online');
if (node.join) {
xmpp.join(node.to+'/'+node.nick);
}
});
xmpp.on('chat', function(from, message) {
var msg = { topic:from, payload:message };
node.send([msg,null]);
});
xmpp.on('groupchat', function(conference, from, message, stamp) {
var msg = { topic:from, payload:message, room:conference };
if (from != node.nick) { node.send([msg,null]); }
});
//xmpp.on('chatstate', function(from, state) {
//console.log('%s is currently %s', from, state);
//var msg = { topic:from, payload:state };
//node.send([null,msg]);
//});
xmpp.on('buddy', function(jid, state, statusText) {
node.log(jid+" is "+state+" : "+statusText);
var msg = { topic:jid, payload: { presence:state, status:statusText} };
node.send([null,msg]);
});
xmpp.on('error', function(err) {
console.error(err);
});
xmpp.on('close', function(err) {
node.log('connection closed');
});
xmpp.on('subscribe', function(from) {
xmpp.acceptSubscription(from);
});
this.on("input", function(msg) {
var to = msg.topic;
if (node.to != "") { to = node.to; }
if (node.sendAll) {
xmpp.send(to, JSON.stringify(msg), node.join);
}
else {
xmpp.send(to, msg.payload, node.join);
}
});
this._close = function() {
xmpp.setPresence('offline');
//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...
// (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.");
}
}
RED.nodes.registerType("xmpp",XmppNode);
XmppNode.prototype.close = function() {
this._close();
}

View File

@@ -0,0 +1,58 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="tail">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="icon-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-split" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-split" style="width: 70%;">Split lines if we see \n ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">WON'T work on Windows.</div>
</script>
<script type="text/x-red" data-help-name="tail">
<p>Tails (watches for things to be added) to the configured file. (Linux/Mac ONLY)</p>
<p>This won't work on Windows filesystems (as it relies on the tail -f command) so we will probably have to hide it in future.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tail',{
category: 'storage-input',
defaults: {
name: {value:""},
split: {value:false},
filename: {value:"",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "file.png",
label: function() {
return this.name||this.filename;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

47
nodes/storage/28-tail.js Normal file
View File

@@ -0,0 +1,47 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var fs = require("fs");
var spawn = require('child_process').spawn;
function TailNode(n) {
RED.nodes.createNode(this,n);
this.filename = n.filename;
this.split = n.split;
var node = this;
var tail = spawn("tail", ["-f", this.filename]);
tail.stdout.on("data", function (data) {
var msg = {topic:node.filename};
if (node.split) {
var strings = data.toString().split("\n");
for (s in strings) {
if (strings[s] != "") {
msg.payload = strings[s];
node.send(msg);
}
}
}
else {
msg.payload = data.toString();
node.send(msg);
}
});
}
RED.nodes.registerType("tail",TailNode);

View File

@@ -0,0 +1,66 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="file">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="icon-file"></i> Filename</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-appendNewline" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-appendNewline" style="width: 70%;">Append newline ?</label>
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-overwriteFile" placeholder="Name" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-overwriteFile" style="width: 70%;">Overwrite complete file ?</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="file">
<p>Writes the <b>msg.payload</b> to the file specified, e.g. to create a log.</p>
<p>A newline is added to every message. But this can be turned off if required, for example, to allow binary files to be written.</p>
<p>The default behaviour is to append to the file. This can be changed to overwrite the file each time, for example if you want to output a "static" web page or report.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('file',{
category: 'storage-output',
defaults: {
name: {value:""},
filename: {value:"",required:true},
appendNewline: {value:true},
overwriteFile: {value:false}
},
color:"BurlyWood",
inputs:1,
outputs:0,
icon: "file.png",
align: "right",
label: function() {
return this.name||this.filename;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

47
nodes/storage/50-file.js Normal file
View File

@@ -0,0 +1,47 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var fs = require("fs");
function FileNode(n) {
RED.nodes.createNode(this,n);
this.filename = n.filename;
this.appendNewline = n.appendNewline;
this.overwriteFile = n.overwriteFile;
var node = this;
this.on("input",function(msg) {
var data = msg.payload;
if (this.appendNewline) {
data += "\n";
}
if (this.overwriteFile) {
fs.writeFile(this.filename, data, function (err) {
if (err) node.warn('Failed to write to file : '+err);
//console.log('Message written to file',this.filename);
});
}
else {
fs.appendFile(this.filename, data, function (err) {
if (err) node.warn('Failed to append to file : '+err);
//console.log('Message appended to file',this.filename);
});
}
});
}
RED.nodes.registerType("file",FileNode);

View File

@@ -0,0 +1,105 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="redis out">
<div class="form-row node-input-hostname">
<label for="node-input-hostname"><i class="icon-bookmark"></i> Host</label>
<input class="input-append-left" type="text" id="node-input-hostname" placeholder="127.0.0.1" style="width: 40%;" ><button id="node-input-hostname-lookup" class="btn input-append-right"><span class="caret"></span></button>
<label for="node-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-input-port" placeholder="6379" style="width:45px">
</div>
<div class="form-row">
<label for="node-input-key"><i class="icon-briefcase"></i> Key</label>
<input type="text" id="node-input-key" placeholder="Redis Key">
</div>
<div class="form-row">
<label for="node-input-type"><i class="icon-th"></i> Type</label>
<select type="text" id="node-input-structtype" style="width: 150px;">
<option value="string">String</option>
<option value="hash">Hash</option>
<option value="set">Set</option>
<option value="list">List</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">
If key is blank, the topic will be used as the key.<br>
If type is hash, payload should be field=value.
</div>
</script>
<script type="text/x-red" data-help-name="redis out">
<p>A Redis output node. Options include Hash, Set, List and String.</p>
<p>To run this you need a local Redis server running. For details see <a href="http://redis.io/" target="_new">the Redis site</a>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('redis out',{
category: 'storage-output',
color:"#ffaaaa",
defaults: {
hostname: { value:"127.0.0.1",required:true},
port: { value: 6379,required:true},
name: {value:""},
key: {value:""},
structtype: {value:"",required:true}
},
inputs:1,
outputs:0,
icon: "redis.png",
align: "right",
label: function() {
return this.name||this.key+" ("+this.structtype+")";
},
oneditprepare: function() {
var availableServers = [];
var matchedServers = {};
RED.nodes.eachNode(function(node) {
if (node.type == "redis out" && node.hostname && node.port && !matchedServers[node.hostname+":"+node.port]) {
var label = node.hostname+":"+node.port;
matchedServers[label] = true;
availableServers.push({
label:label,
value:node.hostname,
port:node.port
});
}
});
$( "#node-input-hostname" ).autocomplete({
minLength: 0,
source: availableServers,
select: function( event, ui ) {
$("#node-input-port").val(ui.item.port);
}
});
var tt = this;
tt._acOpen = false;
$( "#node-input-hostname" ).on( "autocompleteclose", function( event, ui ) { tt._acOpen = false;} );
$( "#node-input-hostname-lookup" ).click(function(e) {
if (tt._acOpen) {
$( "#node-input-hostname" ).autocomplete( "close");
} else {
$( "#node-input-hostname" ).autocomplete( "search", "" );
}
tt._acOpen = !tt._acOpen;
e.preventDefault();
});
}
});
</script>

View File

@@ -0,0 +1,96 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var util = require("util");
var redis = require("redis");
var hashFieldRE = /^([^=]+)=(.*)$/;
var redisConnectionPool = function() {
var connections = {};
var obj = {
get: function(host,port) {
var id = host+":"+port;
if (!connections[id]) {
connections[id] = redis.createClient(port,host);
connections[id].on("error",function(err) {
util.log("[redis] "+err);
});
connections[id].on("connect",function() {
util.log("[redis] connected to "+host+":"+port);
});
connections[id]._id = id;
connections[id]._nodeCount = 0;
}
connections[id]._nodeCount += 1;
return connections[id];
},
close: function(connection) {
connection._nodeCount -= 1;
if (connection._nodeCount == 0) {
if (connection) {
clearTimeout(connection.retry_timer);
connection.end();
}
delete connections[connection._id];
}
}
};
return obj;
}();
function RedisOutNode(n) {
RED.nodes.createNode(this,n);
this.port = n.port||"6379";
this.hostname = n.hostname||"127.0.0.1";
this.key = n.key;
this.structtype = n.structtype;
this.client = redisConnectionPool.get(this.hostname,this.port);
this.on("input", function(msg) {
if (msg != null) {
var k = this.key || msg.topic;
if (k) {
if (this.structtype == "string") {
this.client.set(k,msg.payload);
} else if (this.structtype == "hash") {
var r = hashFieldRE.exec(msg.payload);
if (r) {
this.client.hset(k,r[1],r[2]);
} else {
this.warn("Invalid payload for redis hash");
}
} else if (this.structtype == "set") {
this.client.sadd(k,msg.payload);
} else if (this.structtype == "list") {
this.client.rpush(k,msg.payload);
}
} else {
this.warn("No key or topic set");
}
}
});
}
RED.nodes.registerType("redis out",RedisOutNode);
RedisOutNode.prototype.close = function() {
redisConnectionPool.close(this.client);
}

View File

@@ -0,0 +1,91 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="mongodb">
<div class="form-row">
<label for="node-config-input-hostname"><i class="icon-bookmark"></i> Host</label>
<input class="input-append-left" type="text" id="node-config-input-hostname" placeholder="localhost" style="width: 40%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="27017" style="width:45px">
</div>
<div class="form-row">
<label for="node-config-input-db"><i class="icon-briefcase"></i> Database</label>
<input type="text" id="node-config-input-db" placeholder="test">
</div>
</script>
<script type="text/x-red" data-help-name="mongodb out">
<p>A MongoDB output node.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb',{
category: 'config',
color:"rgb(218, 196, 180)",
defaults: {
hostname: { value:"127.0.0.1",required:true},
port: { value: 27017,required:true},
db: { value:"",required:true},
},
label: function() {
return this.hostname+":"+this.port+"//"+this.db;
}
});
</script>
<script type="text/x-red" data-template-name="mongodb out">
<div class="form-row">
<label for="node-input-mongodb"><i class="icon-tag"></i> Server</label>
<input type="text" id="node-input-mongodb">
</div>
<div class="form-row">
<label for="node-input-collection"><i class="icon-briefcase"></i> Collection</label>
<input type="text" id="node-input-collection" placeholder="collection">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="mongodb out">
<p>A MongoDB output node.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb out',{
category: 'storage-output',
color:"rgb(218, 196, 180)",
defaults: {
mongodb: { type:"mongodb",required:true},
name: {value:""},
collection: {value:"",required:true},
},
inputs:1,
outputs:0,
icon: "mongodb.png",
align: "right",
label: function() {
return this.name||this.collection||((this.mongodb)?RED.nodes.node(this.mongodb).label():"mongodb");
}
});
</script>

View File

@@ -0,0 +1,66 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = require("../../red/red");
var mongo = require('mongodb');
function MongoNode(n) {
RED.nodes.createNode(this,n);
this.hostname = n.hostname;
this.port = n.port;
this.db = n.db;
}
RED.nodes.registerType("mongodb",MongoNode);
function MongoOutNode(n) {
RED.nodes.createNode(this,n);
this.collection = n.collection;
this.mongodb = n.mongodb;
this.mongoConfig = RED.nodes.getNode(this.mongodb);
if (this.mongoConfig) {
var node = this;
this.clientDb = new mongo.Db(node.mongoConfig.db, new mongo.Server(node.mongoConfig.hostname, node.mongoConfig.port, {}), {w: 1});
this.clientDb.open(function(err,cli) {
if (err) { node.error(err); }
else {
node.clientDb.collection(node.collection,function(err,coll) {
if (err) { node.error(err); }
else {
node.on("input",function(msg) {
delete msg._topic;
coll.save(msg,function(err,item){if (err){node.error(err);}});
});
}
});
}
});
} else {
this.error("missing mongodb configuration");
}
}
RED.nodes.registerType("mongodb out",MongoOutNode);
MongoOutNode.prototype.close = function() {
if (this.clientDb) {
this.clientDb.close();
}
}