Move out several core node to node-red-nodes ready for 0.10.8

Update package.json to match.
Part of #668
This commit is contained in:
dceejay 2015-06-15 20:37:36 +01:00
parent 21d0adbdae
commit d8eb926e2c
17 changed files with 8 additions and 3361 deletions

View File

@ -1,171 +0,0 @@
<!--
Copyright 2013,2015 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="fa fa-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-pin"><i class="fa fa-circle"></i> Pin</label>
<input type="text" id="node-input-pin" placeholder="2">
</div>
<div class="form-row">
<label for="node-input-state"><i class="fa fa-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="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips"><b>Note:</b> You cannot use the same pin for both output and input.</div>
</script>
<script type="text/x-red" data-help-name="arduino in">
<p>Arduino input node. Connects to local Arduino and monitors the selected pin for changes. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>The Arduino must be loaded with the Standard Firmata sketch available in the Arduino examples.</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: 'Arduino',
color:"#3fadb5",
defaults: {
name: {value:""},
pin: {value:"",required:true},
state: {value:"INPUT",required:true},
arduino: {type:"arduino-board"}
},
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="fa fa-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-pin"><i class="fa fa-circle"></i> Pin</label>
<input type="text" id="node-input-pin" placeholder="13">
</div>
<div class="form-row">
<label for="node-input-state"><i class="fa fa-wrench"></i> Type</label>
<select type="text" id="node-input-state" style="width: 200px;">
<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="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips"><b>Note:</b> You cannot use the same pin for both output and input.</div>
</script>
<script type="text/x-red" data-help-name="arduino out">
<p>Arduino output node. Connects to local Arduino and writes to the selected digital pin. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>The Arduino must be loaded with the Standard Firmata sketch available in the Arduino examples.</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: 'Arduino',
color:"#3fadb5",
defaults: {
name: {value:""},
pin: {value:"",required:true},
state: {value:"",required:true},
arduino: {type:"arduino-board"}
},
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="fa fa-random"></i> Port</label>
<input type="text" id="node-config-input-device" style="width:60%;" placeholder="e.g. /dev/ttyUSB0 COM1"/>
<a id="node-config-lookup-serial" class="btn"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a>
</div>
<div class="form-tips"><b>Tip:</b> Use search to list serial ports, or leave blank to connect to first device found.</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('arduino-board',{
category: 'config',
defaults: {
device: {value:""}
},
label: function() {
return this.device||"arduino";
},
oneditprepare: function() {
try {
$("#node-config-input-device").autocomplete( "destroy" );
} catch(err) { }
$("#node-config-lookup-serial").click(function() {
$("#node-config-lookup-serial-icon").removeClass('fa-search');
$("#node-config-lookup-serial-icon").addClass('spinner');
$("#node-config-lookup-serial").addClass('disabled');
$.getJSON('arduinoports',function(data) {
$("#node-config-lookup-serial-icon").addClass('fa-search');
$("#node-config-lookup-serial-icon").removeClass('spinner');
$("#node-config-lookup-serial").removeClass('disabled');
var ports = [];
$.each(data, function(i, port){
ports.push(port);
});
$("#node-config-input-device").autocomplete({
source:ports,
minLength:0,
close: function( event, ui ) {
$("#node-config-input-device").autocomplete( "destroy" );
}
}).autocomplete("search","");
});
});
}
});
</script>

View File

@ -1,167 +0,0 @@
/**
* Copyright 2013,2015 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.
**/
module.exports = function(RED) {
"use strict";
var ArduinoFirmata = require('arduino-firmata');
// The Board Definition - this opens (and closes) the connection
function ArduinoNode(n) {
RED.nodes.createNode(this,n);
this.device = n.device || null;
this.repeat = n.repeat||25;
//node.log("opening connection "+this.device);
var node = this;
node.board = new ArduinoFirmata();
ArduinoFirmata.list(function (err, ports) {
if (!node.device) {
node.log("connecting to first board found.");
node.board.connect();
}
else {
if (ports.indexOf(node.device) === -1) {
node.warn(node.device + " not found. Trying to find board.");
node.board.connect();
}
else {
node.log("connecting to "+node.device);
node.board.connect(node.device);
}
}
node.board.on('boardReady', function() {
node.log("connected to "+node.board.serialport_name);
if (RED.settings.verbose) { node.log("version "+node.board.boardVersion); }
});
});
node.on('close', function(done) {
if (node.board) {
try {
node.board.close(function() {
done();
if (RED.settings.verbose) { node.log("port closed"); }
});
} catch(e) { done(); }
} else { done(); }
});
}
RED.nodes.registerType("arduino-board",ArduinoNode);
// The Input Node
function DuinoNodeIn(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = n.pin;
this.state = n.state;
this.arduino = n.arduino;
this.serverConfig = RED.nodes.getNode(this.arduino);
if (typeof this.serverConfig === "object") {
this.board = this.serverConfig.board;
var node = this;
node.status({fill:"red",shape:"ring",text:"connecting"});
node.board.on('connect', function() {
node.status({fill:"green",shape:"dot",text:"connected"});
//console.log("i",node.state,node.pin);
if (node.state == "ANALOG") {
node.board.on('analogChange', function(e) {
if (e.pin == node.pin) {
var msg = {payload:e.value, topic:"A"+e.pin};
node.send(msg);
}
});
}
if (node.state == "INPUT") {
node.board.pinMode(node.pin, ArduinoFirmata.INPUT);
node.board.on('digitalChange', function(e) {
if (e.pin == node.pin) {
var msg = {payload:e.value, topic:e.pin};
node.send(msg);
}
});
}
if (node.state == "SYSEX") {
node.board.on('sysex', function(e) {
var msg = {payload:e, topic:"sysex"};
node.send(msg);
});
}
});
}
else {
this.warn("port not configured");
}
}
RED.nodes.registerType("arduino in",DuinoNodeIn);
// The Output Node
function DuinoNodeOut(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = n.pin;
this.state = n.state;
this.arduino = n.arduino;
this.serverConfig = RED.nodes.getNode(this.arduino);
if (typeof this.serverConfig === "object") {
this.board = this.serverConfig.board;
var node = this;
node.status({fill:"red",shape:"ring",text:"connecting"});
node.board.on('connect', function() {
node.status({fill:"green",shape:"dot",text:"connected"});
//console.log("o",node.state,node.pin);
node.board.pinMode(node.pin, node.state);
node.on("input", function(msg) {
if (node.state === "OUTPUT") {
if ((msg.payload === true)||(msg.payload.toString() == "1")||(msg.payload.toString().toLowerCase() == "on")) {
node.board.digitalWrite(node.pin, true);
}
if ((msg.payload === false)||(msg.payload.toString() == "0")||(msg.payload.toString().toLowerCase() == "off")) {
node.board.digitalWrite(node.pin, false);
}
}
if (node.state === "PWM") {
msg.payload = msg.payload * 1;
if ((msg.payload >= 0) && (msg.payload <= 255)) {
node.board.analogWrite(node.pin, msg.payload);
}
}
if (node.state === "SERVO") {
msg.payload = msg.payload * 1;
if ((msg.payload >= 0) && (msg.payload <= 180)) {
node.board.servoWrite(node.pin, msg.payload);
}
}
if (node.state === "SYSEX") {
node.board.sysex(msg.payload);
}
});
});
}
else {
this.warn("port not configured");
}
}
RED.nodes.registerType("arduino out",DuinoNodeOut);
RED.httpAdmin.get("/arduinoports", RED.auth.needsPermission("arduino.read"), function(req,res) {
ArduinoFirmata.list(function (err, ports) {
res.json(ports);
});
});
}

View File

@ -1,266 +0,0 @@
<!--
Copyright 2013,2014 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="fa fa-random"></i> Serial Port</label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-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>Can either <ul><li>wait for a "split" character (default \n). Also accepts hex notation (0x0a).</li>
<li>Wait for a timeout in milliseconds for the first character received</li>
<li>Wait to fill a fixed sized buffer</li></ul></p>
<p>It then outputs <b>msg.payload</b> as either a UTF8 ascii string or a binary Buffer object.</p>
<p>If no split character is specified, or a timeout or buffer size of 0, then a stream of single characters is sent - again either as ascii chars or size 1 binary buffers.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial in',{
category: 'input',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:0,
outputs:1,
icon: "serial.png",
label: function() {
var serialNode = RED.nodes.node(this.serial);
return this.name||(serialNode?serialNode.label().split(":")[0]:"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="fa fa-random"></i> Serial Port</label>
<input type="text" id="node-input-serial">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-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>
<p>Optionally the new line character used to split the input can be appended to every message sent out to the serial port.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('serial out',{
category: 'output',
defaults: {
name: {name:""},
serial: {type:"serial-port",required:true}
},
color:"BurlyWood",
inputs:1,
outputs:0,
icon: "serial.png",
align: "right",
label: function() {
var serialNode = RED.nodes.node(this.serial);
return this.name||(serialNode?serialNode.label().split(":")[0]:"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="fa fa-random"></i> Serial Port</label>
<input type="text" id="node-config-input-serialport" style="width:60%;" placeholder="/dev/ttyUSB0"/>
<a id="node-config-lookup-serial" class="btn"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a>
</div>
<div class="form-row">
<table><tr>
<td width="102px"><i class="fa fa-wrench"></i> Settings</td>
<td width="100px">Baud Rate</td>
<td width="80px">Data Bits</td>
<td width="80px">Parity</td>
<td width="80px">Stop Bits</td>
</tr><tr><td>&nbsp;</td>
<td>
<select type="text" id="node-config-input-serialbaud" style="width: 100px;">
<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>
</td><td>
<select type="text" id="node-config-input-databits" style="width: 80px;">
<option value="8">8</option>
<option value="7">7</option>
<option value="6">6</option>
<option value="5">5</option>
</select>
</td><td>
<select type="text" id="node-config-input-parity" style="width: 80px;">
<option value="none">None</option>
<option value="even">Even</option>
<option value="mark">Mark</option>
<option value="odd">Odd</option>
<option value="space">Space</option>
</select>
</td><td>
<select type="text" id="node-config-input-stopbits" style="width: 80px;">
<option value="2">2</option>
<option value="1">1</option>
</select>
</td>
</tr></table><br/>
<div class="form-row">
<label><i class="fa fa-sign-in"></i> Input</label>
</div>
<div class="form-row" style="padding-left: 10px;">
Split input
<select type="text" id="node-config-input-out" style="margin-left: 5px; width:200px;">
<option value="char">on the character</option>
<option value="time">after a timeout of</option>
<option value="count">into fixed lengths of</option>
</select>
<input type="text" id="node-config-input-newline" style="width:50px;">
<span id="node-units"></span>
</div>
<div class="form-row" style="padding-left: 10px;">
and deliver
<select type="text" id="node-config-input-bin" style="margin-left: 5px; width: 150px;">
<option value="false">ascii strings</option>
<option value="bin">binary buffers</option>
</select>
</div>
<br/>
<div id="node-config-addchar">
<div class="form-row">
<label><i class="fa fa-sign-out"></i> Output</label>
</div>
<div class="form-row">
<input style="width: 30px;margin-left: 10px; vertical-align: top;" type="checkbox" id="node-config-input-addchar"><label style="width: auto;" for="node-config-input-addchar">add split character to output messages</label>
</div>
</div>
<div class="form-tips" id="tip-split">Tip: the "Split on" character is used to split the input into separate messages. It can also be added to every message sent out to the serial port.</div>
<div class="form-tips" id="tip-bin" hidden>Tip: In timeout mode timeout starts from arrival of first character.</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},
databits: {value:8,required:true},
parity: {value:"none",required:true},
stopbits: {value:1,required:true},
newline: {value:"\\n"},
bin: {value:"false"},
out: {value:"char"},
addchar: {value:false}
},
label: function() {
this.serialbaud = this.serialbaud || 57600;
this.databits = this.databits || 8;
this.parity = this.parity || 'none';
this.stopbits = this.stopbits || 1;
return this.serialport+":"+this.serialbaud+"-"+this.databits+this.parity.charAt(0).toUpperCase()+this.stopbits;
},
oneditprepare: function() {
var previous = null;
$("#node-config-input-out").on('focus', function () { previous = this.value; }).change(function() {
if (previous == null) { previous = $("#node-config-input-out").val(); }
if ($("#node-config-input-out").val() == "char") {
if (previous != "char") { $("#node-config-input-newline").val("\\n"); }
$("#node-units").text("");
$("#node-config-addchar").show();
$("#tip-split").show();
$("#tip-bin").hide();
}
else if ($("#node-config-input-out").val() == "time") {
if (previous != "time") { $("#node-config-input-newline").val("0"); }
$("#node-units").text("ms");
$("#node-config-addchar").hide();
$("#node-config-input-addchar").val("false");
$("#tip-split").hide();
$("#tip-bin").show();
}
else {
if (previous != "count") { $("#node-config-input-newline").val(""); }
$("#node-units").text("chars");
$("#node-config-addchar").hide();
$("#node-config-input-addchar").val("false");
$("#tip-split").hide();
$("#tip-bin").hide();
}
});
try {
$("#node-config-input-serialport").autocomplete( "destroy" );
} catch(err) {
}
$("#node-config-lookup-serial").click(function() {
//$("#node-config-lookup-serial-icon").removeClass('fa fa-search');
//$("#node-config-lookup-serial-icon").addClass('fa fa-spinner');
$("#node-config-lookup-serial").addClass('disabled');
$.getJSON('serialports',function(data) {
//$("#node-config-lookup-serial-icon").addClass('fa fa-search');
//$("#node-config-lookup-serial-icon").removeClass('fa fa-spinner');
$("#node-config-lookup-serial").removeClass('disabled');
var ports = [];
$.each(data, function(i, port){
ports.push(port.comName);
});
$("#node-config-input-serialport").autocomplete({
source:ports,
minLength:0,
close: function( event, ui ) {
$("#node-config-input-serialport").autocomplete( "destroy" );
}
}).autocomplete("search","");
});
});
}
});
</script>

View File

@ -1,309 +0,0 @@
/**
* Copyright 2013,2015 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.
**/
module.exports = function(RED) {
"use strict";
var settings = RED.settings;
var events = require("events");
var serialp = require("serialport");
var bufMaxSize = 32768; // Max serial buffer size, for inputs...
// TODO: 'serialPool' should be encapsulated in SerialPortNode
function SerialPortNode(n) {
RED.nodes.createNode(this,n);
this.serialport = n.serialport;
this.newline = n.newline;
this.addchar = n.addchar || "false";
this.serialbaud = parseInt(n.serialbaud) || 57600;
this.databits = parseInt(n.databits) || 8;
this.parity = n.parity || "none";
this.stopbits = parseInt(n.stopbits) || 1;
this.bin = n.bin || "false";
this.out = n.out || "char";
}
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;
node.port = serialPool.get(this.serialConfig.serialport,
this.serialConfig.serialbaud,
this.serialConfig.databits,
this.serialConfig.parity,
this.serialConfig.stopbits,
this.serialConfig.newline);
node.addCh = "";
if (node.serialConfig.addchar == "true" || node.serialConfig.addchar === true) {
node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0");
}
node.on("input",function(msg) {
if (msg.hasOwnProperty("payload")) {
var payload = msg.payload;
if (!Buffer.isBuffer(payload)) {
if (typeof payload === "object") {
payload = JSON.stringify(payload);
} else {
payload = payload.toString();
}
payload += node.addCh;
} else if (node.addCh !== "") {
payload = Buffer.concat([payload,new Buffer(node.addCh)]);
}
node.port.write(payload,function(err,res) {
if (err) {
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
node.error(errmsg,msg);
}
});
}
});
node.port.on('ready', function() {
node.status({fill:"green",shape:"dot",text:"connected"});
});
node.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"not connected"});
});
} else {
this.error("missing serial config");
}
this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
} else {
done();
}
});
}
RED.nodes.registerType("serial out",SerialOutNode);
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;
node.tout = null;
var buf;
if (node.serialConfig.out != "count") { buf = new Buffer(bufMaxSize); }
else { buf = new Buffer(Number(node.serialConfig.newline)); }
var i = 0;
node.status({fill:"grey",shape:"dot",text:"unknown"});
node.port = serialPool.get(this.serialConfig.serialport,
this.serialConfig.serialbaud,
this.serialConfig.databits,
this.serialConfig.parity,
this.serialConfig.stopbits,
this.serialConfig.newline
);
var splitc;
if (node.serialConfig.newline.substr(0,2) == "0x") {
splitc = new Buffer([parseInt(node.serialConfig.newline)]);
} else {
splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0"));
}
this.port.on('data', function(msg) {
// single char buffer
if ((node.serialConfig.newline === 0)||(node.serialConfig.newline === "")) {
if (node.serialConfig.bin !== "bin") { node.send({"payload": String.fromCharCode(msg)}); }
else { node.send({"payload": new Buffer([msg])}); }
}
else {
// do the timer thing
if (node.serialConfig.out === "time") {
if (node.tout) {
i += 1;
buf[i] = msg;
}
else {
node.tout = setTimeout(function () {
node.tout = null;
var m = new Buffer(i+1);
buf.copy(m,0,0,i+1);
if (node.serialConfig.bin !== "bin") { m = m.toString(); }
node.send({"payload": m});
m = null;
}, node.serialConfig.newline);
i = 0;
buf[0] = msg;
}
}
// count bytes into a buffer...
else if (node.serialConfig.out === "count") {
buf[i] = msg;
i += 1;
if ( i >= parseInt(node.serialConfig.newline)) {
var m = new Buffer(i);
buf.copy(m,0,0,i);
if (node.serialConfig.bin !== "bin") { m = m.toString(); }
node.send({"payload":m});
m = null;
i = 0;
}
}
// look to match char...
else if (node.serialConfig.out === "char") {
buf[i] = msg;
i += 1;
if ((msg === splitc[0]) || (i === bufMaxSize)) {
var m = new Buffer(i);
buf.copy(m,0,0,i);
if (node.serialConfig.bin !== "bin") { m = m.toString(); }
node.send({"payload":m});
m = null;
i = 0;
}
}
else { node.log("should never get here"); }
}
});
this.port.on('ready', function() {
node.status({fill:"green",shape:"dot",text:"connected"});
});
this.port.on('closed', function() {
node.status({fill:"red",shape:"ring",text:"not connected"});
});
} else {
this.error("missing serial config");
}
this.on("close", function(done) {
if (this.serialConfig) {
serialPool.close(this.serialConfig.serialport,done);
} else {
done();
}
});
}
RED.nodes.registerType("serial in",SerialInNode);
var serialPool = function() {
var connections = {};
return {
get:function(port,baud,databits,parity,stopbits,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() {
//if (newline == "") {
obj.serial = new serialp.SerialPort(port,{
baudrate: baud,
databits: databits,
parity: parity,
stopbits: stopbits,
parser: serialp.parsers.raw
},true, function(err, results) { if (err) { obj.serial.emit('error',err); } });
//}
//else {
// obj.serial = new serialp.SerialPort(port,{
// baudrate: baud,
// databits: databits,
// parity: parity,
// stopbits: stopbits,
// parser: serialp.parsers.readline(newline)
// },true, function(err, results) { if (err) obj.serial.emit('error',err); });
//}
obj.serial.on('error', function(err) {
RED.log.error("serial port "+port+" error "+err);
obj._emitter.emit('closed');
obj.tout = setTimeout(function() {
setupSerial();
}, settings.serialReconnectTime);
});
obj.serial.on('close', function() {
if (!obj._closing) {
RED.log.error("serial port "+port+" closed unexpectedly");
obj._emitter.emit('closed');
obj.tout = setTimeout(function() {
setupSerial();
}, settings.serialReconnectTime);
}
});
obj.serial.on('open',function() {
RED.log.info("serial port "+port+" opened at "+baud+" baud "+databits+""+parity.charAt(0).toUpperCase()+stopbits);
if (obj.tout) { clearTimeout(obj.tout); }
//obj.serial.flush();
obj._emitter.emit('ready');
});
obj.serial.on('data',function(d) {
//console.log(Buffer.isBuffer(d),d.length,d);
//if (typeof d !== "string") {
// //d = d.toString();
for (var z=0; z<d.length; z++) {
obj._emitter.emit('data',d[z]);
}
//}
//else {
// obj._emitter.emit('data',d);
//}
});
obj.serial.on("disconnect",function() {
RED.log.error("serial port "+port+" gone away");
});
}
setupSerial();
return obj;
}();
}
return connections[id];
},
close: function(port,done) {
if (connections[port]) {
if (connections[port].tout != null) {
clearTimeout(connections[port].tout);
}
connections[port]._closing = true;
try {
connections[port].close(function() {
RED.log.info("serial port closed");
done();
});
}
catch(err) { }
delete connections[port];
} else {
done();
}
}
}
}();
RED.httpAdmin.get("/serialports", RED.auth.needsPermission('serial.read'), function(req,res) {
serialp.list(function (err, ports) {
res.json(ports);
});
});
}

View File

@ -1,222 +0,0 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="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">
(function() {
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-credentials/"+twitterConfigNodeId+"/auth/callback");
$("#node-config-dialog-ok").button("disable");
$("#node-config-twitter-row").html('<div style="text-align: center; margin-top: 20px; "><a class="btn" id="node-config-twitter-start" href="twitter-credentials/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank">Click here to authenticate with Twitter.</a></div>');
$("#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="fa fa-user"></i> Twitter ID</label><span class="input-xlarge uneditable-input">'+sn+'</span>');
}
function pollTwitterCredentials(e) {
$.getJSON('credentials/twitter-credentials/'+twitterConfigNodeId,function(data) {
if (data.screen_name) {
updateTwitterScreenName(data.screen_name);
twitterConfigNodeIntervalId = null;
$("#node-config-dialog-ok").button("enable");
} else {
twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000);
}
})
}
RED.nodes.registerType('twitter-credentials',{
category: 'config',
defaults: {
screen_name: {value:""}
},
credentials: {
screen_name: {type:"text"},
access_token: {type: "password"},
access_token_secret: {type:"password"}
},
label: function() {
return this.screen_name;
},
exportable: false,
oneditprepare: function() {
twitterConfigNodeId = this.id;
if (!this.screen_name || this.screen_name == "") {
showTwitterAuthStart();
} else {
if (this.credentials.screen_name) {
updateTwitterScreenName(this.credentials.screen_name);
} else {
showTwitterAuthStart();
}
}
},
oneditsave: function() {
if (twitterConfigNodeIntervalId) {
window.clearTimeout(twitterConfigNodeIntervalId);
}
},
oneditcancel: function(adding) {
if (twitterConfigNodeIntervalId) {
window.clearTimeout(twitterConfigNodeIntervalId);
}
}
});
})();
</script>
<script type="text/x-red" data-template-name="twitter in">
<div class="form-row">
<label for="node-input-twitter"><i class="fa fa-user"></i> Log in as</label>
<input type="text" id="node-input-twitter">
</div>
<div class="form-row">
<label for="node-input-user"><i class="fa fa-search"></i> Search</label>
<select type="text" id="node-input-user" style="display: inline-block; vertical-align: middle; width:60%;">
<option value="false">all public tweets</option>
<option value="true">the tweets of who you follow</option>
<option value="user">the tweets of specific users</option>
<option value="dm">your direct messages</option>
</select>
</div>
<div class="form-row" id="node-input-tags-row">
<label for="node-input-tags"><i class="fa fa-tags"></i> <span id="node-input-tags-label">for</span></label>
<input type="text" id="node-input-tags" placeholder="comma-separated words, @ids, #tags">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">Tip: Use commas without spaces between multiple search terms. Comma = OR, Space = AND.
<br/>The Twitter API WILL NOT deliver 100% of all tweets.
<br/>Tweets of who you follow will include their retweets and favourites.</div>
</script>
<script type="text/x-red" data-help-name="twitter in">
<p>Twitter input node. Can be used to search either:
<ul><li>the public or a user's stream for tweets containing the configured search term</li>
<li>all tweets by specific users</li>
<li>direct messages received by the authenticated user</li>
</ul></p>
<p>Use space for <i>and</i> and comma , for <i>or</i> when searching for multiple terms.</p>
<p>Sets the <b>msg.topic</b> to <i>tweets/</i> and then appends the senders screen name.</p>
<p>Sets <b>msg.location</b> to the tweeters location if known.</p>
<p>Sets <b>msg.tweet</b> to the full tweet object as documented by <a href="https://dev.twitter.com/docs/platform-objects/tweets">Twitter</a>.
<p><b>Note:</b> when set to a specific user's tweets, or your direct messages, the node is subject to
Twitter's API rate limiting. If you deploy the flows multiple times within a 15 minute window, you may
exceed the limit and will see errors from the node. These errors will clear when the current 15 minute window
passes.</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:"",validate:function(v) { return this.user == "dm" || v.length > 0;}},
user: {value:"false",required:true},
name: {value:""},
topic: {value:"tweets"}
},
inputs:0,
outputs:1,
icon: "twitter.png",
label: function() {
if (this.name) {
return this.name;
}
if (this.user == "dm") {
var user = RED.nodes.node(this.twitter);
return (user?user.label()+" ":"")+"DMs";
} else if (this.user == "user") {
return this.tags+" tweets";
}
return this.tags;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
$("#node-input-user").change(function() {
var type = $("#node-input-user option:selected").val();
if (type == "user") {
$("#node-input-tags-row").show();
$("#node-input-tags-label").html("User");
$("#node-input-tags").attr("placeholder","comma-separated @twitter handles");
} else if (type == "dm") {
$("#node-input-tags-row").hide();
} else {
$("#node-input-tags-row").show();
$("#node-input-tags-label").html("for");
$("#node-input-tags").attr("placeholder","comma-separated words, @ids, #hashtags");
}
});
$("#node-input-user").change();
}
});
</script>
<script type="text/x-red" data-template-name="twitter out">
<div class="form-row">
<label for="node-input-twitter"><i class="fa fa-user"></i> Twitter</label>
<input type="text" id="node-input-twitter">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-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>
<p>To send a Direct Message (DM) - use a payload like "D {username} {message}"</p>
<p>If <b>msg.media</b> exists and is a Buffer object, this node will treat it
as an image and attach it to the tweet.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('twitter out',{
category: 'social-output',
color:"#C0DEED",
defaults: {
twitter: {type:"twitter-credentials",required:true},
name: {value:"Tweet"}
},
inputs:1,
outputs:0,
icon: "twitter.png",
align: "right",
label: function() {
return this.name;
}
});
</script>

View File

@ -1,386 +0,0 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
module.exports = function(RED) {
"use strict";
var ntwitter = require('twitter-ng');
var OAuth= require('oauth').OAuth;
var request = require('request');
function TwitterNode(n) {
RED.nodes.createNode(this,n);
this.screen_name = n.screen_name;
}
RED.nodes.registerType("twitter-credentials",TwitterNode,{
credentials: {
screen_name: {type:"text"},
access_token: {type: "password"},
access_token_secret: {type:"password"}
}
});
/**
* Populate msg.location based on data found in msg.tweet.
*/
function addLocationToTweet(msg) {
if(msg.tweet) {
if(msg.tweet.geo) { // if geo is set, always set location from geo
if(msg.tweet.geo.coordinates && msg.tweet.geo.coordinates.length === 2) {
if (!msg.location) { msg.location = {}; }
// coordinates[0] is lat, coordinates[1] is lon
msg.location.lat = msg.tweet.geo.coordinates[0];
msg.location.lon = msg.tweet.geo.coordinates[1];
msg.location.icon = "twitter";
}
} else if(msg.tweet.coordinates) { // otherwise attempt go get it from coordinates
if(msg.tweet.coordinates.coordinates && msg.tweet.coordinates.coordinates.length === 2) {
if (!msg.location) { msg.location = {}; }
// WARNING! coordinates[1] is lat, coordinates[0] is lon!!!
msg.location.lat = msg.tweet.coordinates.coordinates[1];
msg.location.lon = msg.tweet.coordinates.coordinates[0];
msg.location.icon = "twitter";
}
} // if none of these found then just do nothing
} // if no msg.tweet then just do nothing
}
function TwitterInNode(n) {
RED.nodes.createNode(this,n);
this.active = true;
this.user = n.user;
//this.tags = n.tags.replace(/ /g,'');
this.tags = n.tags;
this.twitter = n.twitter;
this.topic = n.topic||"tweets";
this.twitterConfig = RED.nodes.getNode(this.twitter);
var credentials = RED.nodes.getCredentials(this.twitter);
if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
var twit = new ntwitter({
consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
access_token_key: credentials.access_token,
access_token_secret: credentials.access_token_secret
});
//setInterval(function() {
// twit.get("/application/rate_limit_status.json",null,function(err,cb) {
// console.log("direct_messages:",cb["resources"]["direct_messages"]);
// });
//
//},10000);
var node = this;
if (this.user === "user") {
node.poll_ids = [];
node.since_ids = {};
var users = node.tags.split(",");
for (var i=0;i<users.length;i++) {
var user = users[i].replace(" ","");
twit.getUserTimeline({
screen_name:user,
trim_user:0,
count:1
},function() {
var u = user+"";
return function(err,cb) {
if (err) {
node.error(err);
return;
}
if (cb[0]) {
node.since_ids[u] = cb[0].id_str;
} else {
node.since_ids[u] = '0';
}
node.poll_ids.push(setInterval(function() {
twit.getUserTimeline({
screen_name:u,
trim_user:0,
since_id:node.since_ids[u]
},function(err,cb) {
if (cb) {
for (var t=cb.length-1;t>=0;t-=1) {
var tweet = cb[t];
var where = tweet.user.location;
var la = tweet.lang || tweet.user.lang;
var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, lang:la, tweet:tweet };
if (where) {
msg.location = {place:where};
addLocationToTweet(msg);
}
node.send(msg);
if (t == 0) {
node.since_ids[u] = tweet.id_str;
}
}
}
if (err) {
node.error(err);
}
});
},60000));
}
}());
}
} else if (this.user === "dm") {
node.poll_ids = [];
twit.getDirectMessages({
screen_name:node.twitterConfig.screen_name,
trim_user:0,
count:1
},function(err,cb) {
if (err) {
node.error(err);
return;
}
if (cb[0]) {
node.since_id = cb[0].id_str;
} else {
node.since_id = '0';
}
node.poll_ids.push(setInterval(function() {
twit.getDirectMessages({
screen_name:node.twitterConfig.screen_name,
trim_user:0,
since_id:node.since_id
},function(err,cb) {
if (cb) {
for (var t=cb.length-1;t>=0;t-=1) {
var tweet = cb[t];
var where = tweet.sender.location;
var la = tweet.lang || tweet.sender.lang;
var msg = { topic:node.topic+"/"+tweet.sender.screen_name, payload:tweet.text, lang:la, tweet:tweet };
if (where) {
msg.location = {place:where};
addLocationToTweet(msg);
}
node.send(msg);
if (t == 0) {
node.since_id = tweet.id_str;
}
}
}
if (err) {
node.error(err);
}
});
},120000));
});
} else if (this.tags !== "") {
try {
var thing = 'statuses/filter';
if (this.user === "true") { thing = 'user'; }
var st = { track: [node.tags] };
var bits = node.tags.split(",");
if (bits.length == 4) {
if ((Number(bits[0]) < Number(bits[2])) && (Number(bits[1]) < Number(bits[3]))) {
st = { locations: node.tags };
}
else {
node.log("possible bad geo area format. Should be lower-left lon, lat, upper-right lon, lat");
}
}
var setupStream = function() {
if (node.active) {
twit.stream(thing, st, function(stream) {
//console.log(st);
//twit.stream('user', { track: [node.tags] }, function(stream) {
//twit.stream('site', { track: [node.tags] }, function(stream) {
//twit.stream('statuses/filter', { track: [node.tags] }, function(stream) {
node.stream = stream;
stream.on('data', function(tweet) {
if (tweet.user !== undefined) {
var where = tweet.user.location;
var la = tweet.lang || tweet.user.lang;
var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, lang:la, tweet:tweet };
if (where) {
msg.location = {place:where};
addLocationToTweet(msg);
}
node.send(msg);
}
});
stream.on('limit', function(tweet) {
node.warn("tweet rate limit hit");
});
stream.on('error', function(tweet,rc) {
if (rc == 420) {
node.warn("Twitter rate limit hit");
} else {
node.warn("Stream error:"+tweet.toString()+" ("+rc+")");
}
setTimeout(setupStream,10000);
});
stream.on('destroy', function (response) {
if (this.active) {
node.warn("twitter ended unexpectedly");
setTimeout(setupStream,10000);
}
});
});
}
}
setupStream();
}
catch (err) {
node.error(err);
}
} else {
this.error("Invalid tag property");
}
} else {
this.error("missing twitter credentials");
}
this.on('close', function() {
if (this.stream) {
this.active = false;
this.stream.destroy();
}
if (this.poll_ids) {
for (var i=0;i<this.poll_ids.length;i++) {
clearInterval(this.poll_ids[i]);
}
}
});
}
RED.nodes.registerType("twitter in",TwitterInNode);
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
});
node.on("input", function(msg) {
if (msg.hasOwnProperty("payload")) {
node.status({fill:"blue",shape:"dot",text:"tweeting"});
if (msg.payload.length > 140) {
msg.payload = msg.payload.slice(0,139);
node.warn("Tweet greater than 140 : truncated");
}
if (msg.media && Buffer.isBuffer(msg.media)) {
var apiUrl = "https://api.twitter.com/1.1/statuses/update_with_media.json";
var signedUrl = oa.signUrl(apiUrl,
credentials.access_token,
credentials.access_token_secret,
"POST");
var r = request.post(signedUrl,function(err,httpResponse,body) {
if (err) {
node.error(err,msg);
node.status({fill:"red",shape:"ring",text:"failed"});
} else {
var response = JSON.parse(body);
if (response.errors) {
var errorList = response.errors.map(function(er) { return er.code+": "+er.message }).join(", ");
node.error("Send tweet failed: "+errorList,msg);
node.status({fill:"red",shape:"ring",text:"failed"});
} else {
node.status({});
}
}
});
var form = r.form();
form.append("status",msg.payload);
form.append("media[]",msg.media,{filename:"image"});
} else {
twit.updateStatus(msg.payload, function (err, data) {
if (err) {
node.status({fill:"red",shape:"ring",text:"failed"});
node.error(err,msg);
}
node.status({});
});
}
}
else { node.warn("No payload to tweet"); }
});
}
}
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"
);
RED.httpAdmin.get('/twitter-credentials/:id/auth', function(req, res){
var credentials = {};
oa.getOAuthRequestToken({
oauth_callback: req.query.callback
},function(error, oauth_token, oauth_token_secret, results){
if (error) {
var resp = '<h2>Oh no!</h2>'+
'<p>Something went wrong with the authentication process. The following error was returned:<p>'+
'<p><b>'+error.statusCode+'</b>: '+error.data+'</p>'+
'<p>One known cause of this type of failure is if the clock is wrong on system running Node-RED.';
res.send(resp)
} 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.httpAdmin.get('/twitter-credentials/: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){
RED.log.error(error);
res.send("something in twitter oauth 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

@ -1,56 +0,0 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="feedparse">
<div class="form-row">
<label for="node-input-url"><i class="fa fa-globe"></i> Feed url</label>
<input type="text" id="node-input-url">
</div>
<div class="form-row">
<label for="node-input-interval"><i class="fa fa-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="fa fa-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

@ -1,79 +0,0 @@
/**
* Copyright 2013,2014 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.
**/
module.exports = function(RED) {
"use strict";
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() {
var req = request(node.url, {timeout: 10000, pool: false});
//req.setMaxListeners(50);
//req.setHeader('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36');
req.setHeader('accept', 'text/html,application/xhtml+xml');
var feedparser = new FeedParser();
req.on('error', function(err) { node.error(err); });
req.on('response', function(res) {
if (res.statusCode != 200) { node.warn('error - Bad status code'); }
else { res.pipe(feedparser); }
});
feedparser.on('error', function(error) { node.error(error); });
feedparser.on('readable', function () {
var stream = this, article;
while (article = stream.read()) {
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: article
};
node.send(msg);
}
}
});
feedparser.on('meta', function (meta) {});
feedparser.on('end', function () {});
};
this.interval_id = setInterval(function() { getFeed(); }, node.interval);
getFeed();
} else {
this.error("Invalid url");
}
this.on("close", function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
}
});
}
RED.nodes.registerType("feedparse",FeedParseNode);
}

View File

@ -1,203 +0,0 @@
<!--
Copyright 2013,2014 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="e-mail">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-envelope"></i> To</label>
<input type="text" id="node-input-name" placeholder="email@address.com">
</div>
<!-- <div class="form-row">
<label for="node-input-pin"><i class="fa fa-asterisk"></i> Service</label>
<select type="text" id="node-input-pin" style="width: 150px;">
<option value="-" disabled> </option>
<option value="DynectEmail">DynectEmail</option>
<option value="Gmail">Gmail</option>
<option value="hot.ee">hot.ee</option>
<option value="Hotmail">Hotmail</option>
<option value="iCloud">iCloud</option>
<option value="mail.ee">mail.ee</option>
<option value="Mail.Ru">Mail.Ru</option>
<option value="Mailgun">Mailgun</option>
<option value="Mailjet">Mailjet</option>
<option value="Mandrill">Mandrill</option>
<option value="Postmark">Postmark</option>
<option value="QQ">QQ</option>
<option value="QQex">QQex</option>
<option value="SendGrid">SendGrid</option>
<option value="SendCloud">SendCloud</option>
<option value="SES">SES</option>
<option value="Yahoo">Yahoo</option>
<option value="yandex">yandex</option>
<option value="Zoho">Zoho</option>
</select>
</div> -->
<div class="form-row">
<label for="node-input-server"><i class="fa fa-globe"></i> Server</label>
<input type="text" id="node-input-server" placeholder="smtp.gmail.com">
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-random"></i> Port</label>
<input type="text" id="node-input-port" placeholder="465">
</div>
<div class="form-row">
<label for="node-input-userid"><i class="fa fa-user"></i> Userid</label>
<input type="text" id="node-input-userid">
</div>
<div class="form-row">
<label for="node-input-password"><i class="fa fa-lock"></i> Password</label>
<input type="password" id="node-input-password">
</div>
<br/>
<div class="form-row">
<label for="node-input-dname"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-dname" placeholder="Name">
</div>
<div class="form-tips" id="node-tip"><b>Note:</b> Copied credentials from global emailkeys.js file.</div>
</script>
<script type="text/x-red" data-help-name="e-mail">
<p>Sends the <b>msg.payload</b> as an email, with a subject of <b>msg.topic</b>.</p>
<p>The default message recipient can be configured in the node, if it is left
blank it should be set using the <b>msg.to</b> property of the incoming message.</p>
<p>The payload can be html format.</p>
<p>If the payload is a binary buffer then it will be converted to an attachment.
The filename should be set using <b>msg.filename</b>. Optionally <b>msg.description</b> can be added for the body text.</p>
<p>Alternatively you may provide <b>msg.attachments</b> which should contain an array of one or
more attachments in <a href="https://www.npmjs.com/package/nodemailer#attachments" target="_new">nodemailer</a> format.</p>
</script>
<script type="text/javascript">
(function() {
RED.nodes.registerType('e-mail',{
category: 'social-output',
color:"#c7e9c0",
defaults: {
server: {value:"smtp.gmail.com",required:true},
port: {value:"465",required:true},
name: {value:""},
dname: {value:""}
},
credentials: {
userid: {type:"text"},
password: {type: "password"},
global: { type:"boolean"}
},
inputs:1,
outputs:0,
icon: "envelope.png",
align: "right",
label: function() {
return this.dname||this.name||"email";
},
labelStyle: function() {
return (this.dname||!this.topic)?"node_label_italic":"";
},
oneditprepare: function() {
if (this.credentials.global) {
$('#node-tip').show();
} else {
$('#node-tip').hide();
};
}
});
})();
</script>
<script type="text/x-red" data-template-name="e-mail in">
<div class="form-row node-input-repeat">
<label for="node-input-repeat"><i class="fa fa-repeat"></i> Check Repeat (S)</label>
<input type="text" id="node-input-repeat" placeholder="300">
</div>
<div class="form-row">
<label for="node-input-server"><i class="fa fa-globe"></i> Server</label>
<input type="text" id="node-input-server" placeholder="imap.gmail.com">
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-random"></i> Port</label>
<input type="text" id="node-input-port" placeholder="993">
</div>
<div class="form-row">
<label for="node-input-userid"><i class="fa fa-user"></i> Userid</label>
<input type="text" id="node-input-userid">
</div>
<div class="form-row">
<label for="node-input-password"><i class="fa fa-lock"></i> Password</label>
<input type="password" id="node-input-password">
</div>
<div class="form-row">
<label for="node-input-box"><i class="fa fa-inbox"></i> Folder</label>
<input type="text" id="node-input-box">
</div>
<br/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips" id="node-tip"><b>Note:</b> Copied credentials from global emailkeys.js file.</div>
<div id="node-input-tip" class="form-tips">Tip: <b>ONLY</b> retrieves the single most recent email.</div>
</script>
<script type="text/x-red" data-help-name="e-mail in">
<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 plain text body.
If there is text/html then that is returned in <b>msg.html</b>. <b>msg.from</b> and <b>msg.date</b> are also set if you need them.</p>
<p>Additionally <b>msg.header</b> contains the complete header object including
<i>to</i>, <i>cc</i> and other potentially useful properties.</p>
<p>Uses the imap module.</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">
(function() {
RED.nodes.registerType('e-mail in',{
category: 'social-input',
color:"#c7e9c0",
defaults: {
name: {value:""},
server: {value:"imap.gmail.com",required:true},
port: {value:"993",required:true},
box: {value:"INBOX"},
repeat: {value:"300",required:true}
},
credentials: {
userid: {type:"text"},
password: {type: "password"},
global: { type:"boolean"}
},
inputs:0,
outputs:1,
icon: "envelope.png",
label: function() {
return this.name||"email";
},
labelStyle: function() {
return (this.name||!this.topic)?"node_label_italic":"";
},
oneditprepare: function() {
if (this.credentials.global) {
$('#node-tip').show();
} else {
$('#node-tip').hide();
};
if (typeof this.box === 'undefined') {
$("#node-input-box").val("INBOX");
this.box = "INBOX";
}
}
});
})();
</script>

View File

@ -1,283 +0,0 @@
/**
* Copyright 2013,2014 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.
**/
module.exports = function(RED) {
"use strict";
var nodemailer = require("nodemailer");
var Imap = require('imap');
//console.log(nodemailer.Transport.transports.SMTP.wellKnownHosts);
try {
var globalkeys = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js");
} catch(err) {
}
function EmailNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.name = n.name;
this.outserver = n.server;
this.outport = n.port;
var flag = false;
if (this.credentials && this.credentials.hasOwnProperty("userid")) {
this.userid = this.credentials.userid;
} else {
if (globalkeys) {
this.userid = globalkeys.user;
flag = true;
} else {
this.error("No e-mail userid set");
}
}
if (this.credentials && this.credentials.hasOwnProperty("password")) {
this.password = this.credentials.password;
} else {
if (globalkeys) {
this.password = globalkeys.pass;
flag = true;
} else {
this.error("No e-mail password set");
}
}
if (flag) {
RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true});
}
var node = this;
var smtpTransport = nodemailer.createTransport({
host: node.outserver,
port: node.outport,
secure: true,
auth: {
user: node.userid,
pass: node.password
}
});
this.on("input", function(msg) {
if (msg.hasOwnProperty("payload")) {
if (smtpTransport) {
node.status({fill:"blue",shape:"dot",text:"sending"});
if (msg.to && node.name && (msg.to !== node.name)) {
node.warn("Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props");
}
var sendopts = { from: node.userid }; // sender address
sendopts.to = node.name || msg.to; // comma separated list of addressees
sendopts.subject = msg.topic || msg.title || "Message from Node-RED"; // subject line
if (Buffer.isBuffer(msg.payload)) { // if it's a buffer in the payload then auto create an attachment instead
if (!msg.filename) {
var fe = "bin";
if ((msg.payload[0] === 0xFF)&&(msg.payload[1] === 0xD8)) { fe = "jpg"; }
if ((msg.payload[0] === 0x47)&&(msg.payload[1] === 0x49)) { fe = "gif"; } //46
if ((msg.payload[0] === 0x42)&&(msg.payload[1] === 0x4D)) { fe = "bmp"; }
if ((msg.payload[0] === 0x89)&&(msg.payload[1] === 0x50)) { fe = "png"; } //4E
msg.filename = "attachment."+fe;
}
sendopts.attachments = [ { content: msg.payload, filename:(msg.filename.replace(/^.*[\\\/]/, '') || "file.bin") } ];
if (msg.hasOwnProperty("headers") && msg.headers.hasOwnProperty("content-type")) {
sendopts.attachments[0].contentType = msg.headers["content-type"];
}
// Create some body text..
sendopts.text = "Your file from Node-RED is attached : "+(msg.filename.replace(/^.*[\\\/]/, '') || "file.bin")+ (msg.hasOwnProperty("description") ? "\n\n"+msg.description : "");
}
else {
var payload = RED.util.ensureString(msg.payload);
sendopts.text = payload; // plaintext body
if (/<[a-z][\s\S]*>/i.test(payload)) { sendopts.html = payload; } // html body
if (msg.attachments) { sendopts.attachments = msg.attachments; } // add attachments
}
smtpTransport.sendMail(sendopts, function(error, info) {
if (error) {
node.error(error,msg);
node.status({fill:"red",shape:"ring",text:"send failed"});
} else {
node.log("Message sent: " + info.response);
node.status({});
}
});
}
else { node.warn("No Email credentials found. See info panel."); }
}
else { node.warn("No payload to send"); }
});
}
RED.nodes.registerType("e-mail",EmailNode,{
credentials: {
userid: {type:"text"},
password: {type: "password"},
global: { type:"boolean"}
}
});
function EmailInNode(n) {
RED.nodes.createNode(this,n);
this.name = n.name;
this.repeat = n.repeat * 1000 || 300000;
this.inserver = n.server || globalkeys.server || "imap.gmail.com";
this.inport = n.port || globalkeys.port || "993";
this.box = n.box || "INBOX";
var flag = false;
if (this.credentials && this.credentials.hasOwnProperty("userid")) {
this.userid = this.credentials.userid;
} else {
if (globalkeys) {
this.userid = globalkeys.user;
flag = true;
} else {
this.error("No e-mail userid set");
}
}
if (this.credentials && this.credentials.hasOwnProperty("password")) {
this.password = this.credentials.password;
} else {
if (globalkeys) {
this.password = globalkeys.pass;
flag = true;
} else {
this.error("No e-mail password set");
}
}
if (flag) {
RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true});
}
var node = this;
this.interval_id = null;
var oldmail = {};
var imap = new Imap({
user: node.userid,
password: node.password,
host: node.inserver,
port: node.inport,
tls: true,
tlsOptions: { rejectUnauthorized: false },
connTimeout: node.repeat,
authTimeout: node.repeat
});
if (!isNaN(this.repeat) && this.repeat > 0) {
node.log("repeat = "+this.repeat);
this.interval_id = setInterval( function() {
node.emit("input",{});
}, this.repeat );
}
this.on("input", function(msg) {
imap.once('ready', function() {
node.status({fill:"blue",shape:"dot",text:"fetching"});
var pay = {};
imap.openBox(node.box, false, function(err, box) {
if (err) {
node.status({fill:"red",shape:"ring",text:"fetch folder error"});
node.error("Failed to fetch folder "+node.box,err);
}
else {
if (box.messages.total > 0) {
//var f = imap.seq.fetch(box.messages.total + ':*', { markSeen:true, bodies: ['HEADER.FIELDS (FROM SUBJECT DATE TO CC BCC)','TEXT'] });
var f = imap.seq.fetch(box.messages.total + ':*', { markSeen:true, bodies: ['HEADER','TEXT'] });
f.on('message', function(msg, seqno) {
node.log('message: #'+ seqno);
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
var buffer = '';
stream.on('data', function(chunk) {
buffer += chunk.toString('utf8');
});
stream.on('end', function() {
if (info.which !== 'TEXT') {
var head = Imap.parseHeader(buffer);
pay.from = head.from[0];
pay.topic = head.subject[0];
pay.date = head.date[0];
pay.header = head;
} else {
var parts = buffer.split("Content-Type");
for (var p = 0; p < parts.length; p++) {
if (parts[p].indexOf("text/plain") >= 0) {
pay.payload = parts[p].split("\n").slice(1,-2).join("\n").trim();
}
else if (parts[p].indexOf("text/html") >= 0) {
pay.html = parts[p].split("\n").slice(1,-2).join("\n").trim();
} else {
pay.payload = parts[0];
}
}
//pay.body = buffer;
}
});
});
msg.on('end', function() {
//node.log('Finished: '+prefix);
});
});
f.on('error', function(err) {
node.warn('fetch message error: ' + err);
node.status({fill:"red",shape:"ring",text:"fetch message error"});
});
f.on('end', function() {
delete(pay._msgid);
if (JSON.stringify(pay) !== oldmail) {
oldmail = JSON.stringify(pay);
node.send(pay);
node.log('received new email: '+pay.topic);
}
else { node.log('duplicate not sent: '+pay.topic); }
//node.status({fill:"green",shape:"dot",text:"ok"});
node.status({});
});
}
else {
node.log("you have achieved inbox zero");
//node.status({fill:"green",shape:"dot",text:"ok"});
node.status({});
}
}
imap.end();
});
});
node.status({fill:"grey",shape:"dot",text:"connecting"});
imap.connect();
});
imap.on('error', function(err) {
node.log(err);
node.status({fill:"red",shape:"ring",text:"connect error"});
});
this.on("error", function(err) {
node.log("error: ",err);
});
this.on("close", function() {
if (this.interval_id != null) {
clearInterval(this.interval_id);
}
if (imap) { imap.destroy(); }
});
node.emit("input",{});
}
RED.nodes.registerType("e-mail in",EmailInNode,{
credentials: {
userid: {type:"text"},
password: {type: "password"},
global: { type:"boolean"}
}
});
};

View File

@ -1,235 +0,0 @@
<!--
Copyright 2013,2014 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="fa fa-globe"></i> IRC Server</label>
<input type="text" id="node-input-ircserver">
</div>
<div class="form-row">
<label for="node-input-channel"><i class="fa fa-random"></i> Channel</label>
<input type="text" id="node-input-channel" placeholder="#nodered">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)<br/>
You may join multiple channels by comma separating a list - #chan1,#chan2,etc.</div>
</script>
<script type="text/x-red" data-help-name="irc in">
<p>Connects to a channel on an IRC server.</p>
<p>You may join multiple channels by comma separating a list - #chan1,#chan2,#etc.</p>
<p>Any messages on that channel will appear on the <code>msg.payload</code> at the output,
while <code>msg.topic</code> will contain who it is from.
<code>msg.to</code> contains either the name of the channel or PRIV in the case of a pm.</p>
<p>The second output provides a <code>msg.payload</code> that has any status messages such as joins, parts, kicks etc.</p>
<p>The type of the status message is set as <code>msg.payload.type</code>.</p>
<p>The possible status types are: <br />
<table border="1" cellpadding="1" cellspacing="1">
<thead>
<tr>
<th scope="col">Type</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>message</td>
<td>message is sent into the channel</td>
</tr>
<tr>
<td>pm</td>
<td>private message to the bot</td>
</tr>
<tr>
<td>join</td>
<td>a user joined the channel (also triggered when the bot joins a channel)</td>
</tr>
<tr>
<td>invite</td>
<td>the bot is being invited to a channel</td>
</tr>
<tr>
<td>part</td>
<td>a user leaves a channel</td>
</tr>
<tr>
<td>quit</td>
<td>a user quits a channel</td>
</tr>
<tr>
<td>kick</td>
<td>a user is kicked from a channel</td>
</tr>
<tr>
<td>names</td>
<td>retrieves the list of users when the bot joins a channel</td>
</tr>
</tbody>
</table>
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc in',{
category: 'social-input',
defaults: {
name: {value:""},
ircserver: {type:"irc-server", required:true},
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)}
},
color:"Silver",
inputs:0,
outputs:2,
icon: "hash.png",
label: function() {
var ircNode = RED.nodes.node(this.ircserver);
return this.name || (ircNode ? ircNode.label() : "irc");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if ((this.ircserver !== undefined) && (this.ircserver !== "")) {
this.channel = this.channel || RED.nodes.node(this.ircserver).channel;
$("#node-input-channel").val(this.channel);
}
else { this.channel = this.channel; }
$("#node-input-channel").val(this.channel);
}
});
</script>
<script type="text/x-red" data-template-name="irc out">
<div class="form-row">
<label for="node-input-ircserver"><i class="fa fa-globe"></i> IRC Server</label>
<input type="text" id="node-input-ircserver">
</div>
<div class="form-row">
<label for="node-input-channel"><i class="fa fa-random"></i> Channel</label>
<input type="text" id="node-input-channel" placeholder="#nodered">
</div>
<div class="form-row">
<label for="node-input-sendObject"><i class="fa fa-arrows"></i> Action</label>
<select type="text" id="node-input-sendObject" style="display: inline-block; vertical-align: middle; width:70%;">
<option value="pay">Send payload to channel(s)</option>
<option value="true">Use msg.topic to set nickname or channel(s)</option>
<option value="false">Send complete msg object to channel(s)</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips">The channel to join must start with a # (as per normal irc rules...)<br/>
Sending the complete object will stringify the whole msg object before sending.</div>
</script>
<script type="text/x-red" data-help-name="irc out">
<p>Sends messages to a channel on an IRC server</p>
<p>You can send just the <code>msg.payload</code>, or the complete <code>msg</code> object to the selected channel,
or you can select to use <code>msg.topic</code> to send the <code>msg.payload</code> to a specific user (private message) or channel.</p>
<p>If multiple output channels are listed (eg. #chan1,#chan2), then the message will be sent to all of them.</p>
<p><b>Note:</b> you can only send to channels you have previously joined so they MUST be specified in the node - even if you then decide to use a subset in msg.topic</p>
<p>You may send RAW commands using <code>msg.raw</code> - This must contain an array of parameters - eg. <pre>["privmsg","#nodered","Hello world"]</pre></p>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc out',{
category: 'social-output',
defaults: {
name: {value:""},
sendObject: {value:"pay", required:true},
ircserver: {type:"irc-server", required:true},
channel: {value:"",required:true,validate:RED.validators.regex(/^#/)}
},
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":"";
},
oneditprepare: function() {
if ((this.ircserver !== undefined) && (this.ircserver !== "")) {
this.channel = this.channel || RED.nodes.node(this.ircserver).channel;
$("#node-input-channel").val(this.channel);
}
else { this.channel = this.channel; }
}
});
</script>
<script type="text/x-red" data-template-name="irc-server">
<div class="form-row">
<label for="node-config-input-server"><i class="fa fa-globe"></i> IRC Server</label>
<input type="text" id="node-config-input-server" placeholder="irc.freenode.net" style="width: 45%;" >
<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>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-config-input-ssl" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-ssl" style="width: 70%;">Use Secure SSL connection ?</label>
</div>
<div class="form-row" id="certrow">
<label>&nbsp;</label>
<input type="checkbox" id="node-config-input-cert" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-cert" style="width: 70%;">Allow self-signed certificates ?</label>
</div>
<div class="form-row">
<label for="node-config-input-nickname"><i class="fa fa-user"></i> Nickname</label>
<input type="text" id="node-config-input-nickname" placeholder="joe123">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('irc-server',{
category: 'config',
defaults: {
server: {value:"",required:true},
port: {value:"6667"},
ssl: {value:false},
cert: {value:false},
nickname: {value:"",required:true}
},
label: function() {
return this.server;
},
oneditprepare: function() {
$("#node-config-input-ssl").change(function() {
if ($("#node-config-input-ssl").is(":checked")) {
$("#certrow").show();
}
else {
$("#certrow").hide();
}
});
},
oneditsave: function() {
this.ssl = $("#node-config-input-ssl").is(":checked");
this.cert = $("#node-config-input-cert").is(":checked");
}
});
</script>

View File

@ -1,282 +0,0 @@
/**
* Copyright 2013,2014 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.
**/
module.exports = function(RED) {
"use strict";
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.port = n.port || 6667;
this.ssl = n.ssl || false;
this.cert = n.cert || false;
this.channel = n.channel;
this.nickname = n.nickname;
this.lastseen = 0;
this.ircclient = null;
this.on("close", function() {
if (this.ircclient != null) {
this.ircclient.removeAllListeners();
this.ircclient.disconnect();
}
});
}
RED.nodes.registerType("irc-server",IRCServerNode);
// The Input Node
function IrcInNode(n) {
RED.nodes.createNode(this,n);
this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.ircserver);
this.channel = n.channel || this.serverConfig.channel;
var node = this;
if (node.serverConfig.ircclient === null) {
node.log("CONNECT: "+node.serverConfig.server);
node.status({fill:"grey",shape:"dot",text:"connecting"});
var options = {autoConnect:true,autoRejoin:false,floodProtection:true,secure:node.serverConfig.ssl,selfSigned:node.serverConfig.cert,port:node.serverConfig.port,retryDelay:20000};
node.serverConfig.ircclient = new irc.Client(node.serverConfig.server, node.serverConfig.nickname, options);
node.serverConfig.ircclient.setMaxListeners(0);
node.serverConfig.ircclient.addListener('error', function(message) {
if (RED.settings.verbose) { node.log("ERR: "+JSON.stringify(message)); }
});
node.serverConfig.ircclient.addListener('netError', function(message) {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("NET: "+JSON.stringify(message)); }
node.status({fill:"red",shape:"ring",text:"net error"});
});
node.serverConfig.ircclient.addListener('connect', function() {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("CONNECTED "); }
});
node.serverConfig.ircclient.addListener('registered', function(message) {
node.serverConfig.lastseen = Date.now();
node.log(node.serverConfig.ircclient.nick+" ONLINE: "+message.server);
node.status({fill:"yellow",shape:"dot",text:"connected"});
node.serverConfig.ircclient.join( node.channel, function(data) {
node.log(data+" JOINED: "+node.channel);
node.status({fill:"green",shape:"dot",text:"joined"});
});
});
node.serverConfig.ircclient.addListener('ping', function(server) {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("PING from "+JSON.stringify(server)); }
node.status({fill:"green",shape:"dot",text:"ok"});
});
node.serverConfig.ircclient.addListener('quit', function(nick, reason, channels, message) {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("QUIT: "+nick+" "+reason+" "+channels+" "+JSON.stringify(message)); }
node.status({fill:"grey",shape:"ring",text:"quit"});
//node.serverConfig.ircclient.disconnect( function() {
// node.serverConfig.ircclient.connect();
//});
//if (RED.settings.verbose) { node.log("restart"); } // then retry
});
node.serverConfig.ircclient.addListener('raw', function (message) { // any message received means we are alive
//console.log("RAW:"+JSON.stringify(message));
if (message.commandType === "reply") {
//console.log("RAW:"+JSON.stringify(message));
node.serverConfig.lastseen = Date.now();
}
});
node.recon = setInterval( function() {
//console.log("CHK ",(Date.now()-node.serverConfig.lastseen)/1000);
if ((Date.now()-node.serverConfig.lastseen) > 240000) { // if more than 4 mins since last seen
node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]); // request time to check link
}
if ((Date.now()-node.serverConfig.lastseen) > 300000) { // If more than 5 mins
//node.serverConfig.ircclient.disconnect();
//node.serverConfig.ircclient.connect();
node.status({fill:"grey",shape:"ring",text:"no connection"});
if (RED.settings.verbose) { node.log("CONNECTION LOST ?"); }
}
//node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]); // request time to check link
}, 60000); // check every 1 min
//node.serverConfig.ircclient.connect();
}
else { node.status({text:""}); }
node.ircclient = node.serverConfig.ircclient;
node.ircclient.addListener('registered', function(message) {
//node.log(node.ircclient.nick+" ONLINE");
node.status({fill:"yellow",shape:"dot",text:"connected"});
node.ircclient.join( node.channel, function(data) {
// node.log(data+" JOINED "+node.channel);
node.status({fill:"green",shape:"dot",text:"joined"});
});
});
node.ircclient.addListener('message', function (from, to, message) {
//node.log(from + ' => ' + to + ' : ' + message);
if (~node.channel.toLowerCase().indexOf(to.toLowerCase())) {
var msg = { "topic":from, "from":from, "to":to, "payload":message };
node.send([msg,null]);
}
//else { console.log(node.channel,to); }
});
node.ircclient.addListener('pm', function(from, message) {
//node.log("PM => "+from + ': ' + message);
var msg = { "topic":from, "from":from, "to":"PRIV", "payload":message };
node.send([msg,null]);
});
node.ircclient.addListener('join', function(channel, who) {
var msg = { "payload": { "type":"join", "who":who, "channel":channel } };
node.send([null,msg]);
//node.log(who+' has joined '+channel);
});
node.ircclient.addListener('invite', function(channel, from, message) {
var msg = { "payload": { "type":"invite", "who":from, "channel":channel, "message":message } };
node.send([null,msg]);
//node.log(from+' sent invite to '+channel+': '+message);
});
node.ircclient.addListener('part', function(channel, who, reason) {
var msg = { "payload": { "type":"part", "who":who, "channel":channel, "reason":reason } };
node.send([null,msg]);
//node.log(who+' has left '+channel+': '+reason);
});
node.ircclient.addListener('quit', function(nick, reason, channels, message) {
var msg = { "payload": { "type":"quit", "who":nick, "channel":channels, "reason":reason } };
node.send([null,msg]);
//node.log(nick+' has quit '+channels+': '+reason);
});
node.ircclient.addListener('kick', function(channel, who, by, reason) {
var msg = { "payload": { "type":"kick", "who":who, "channel":channel, "by":by, "reason":reason } };
node.send([null,msg]);
//node.log(who+' was kicked from '+channel+' by '+by+': '+reason);
});
node.ircclient.addListener('names', function (channel, nicks) {
var msg = { "payload": { "type": "names", "channel": channel, "names": nicks} };
node.send([null, msg]);
});
node.ircclient.addListener('raw', function (message) { // any message means we are alive
node.serverConfig.lastseen = Date.now();
});
node.on("close", function() {
node.ircclient.removeAllListeners();
if (node.recon) { clearInterval(node.recon); }
});
}
RED.nodes.registerType("irc in",IrcInNode);
// The Output Node
function IrcOutNode(n) {
RED.nodes.createNode(this,n);
this.sendFlag = n.sendObject;
this.ircserver = n.ircserver;
this.serverConfig = RED.nodes.getNode(this.ircserver);
this.channel = n.channel || this.serverConfig.channel;
var node = this;
if (node.serverConfig.ircclient === null) {
node.log("CONNECT: "+node.serverConfig.server);
node.status({fill:"grey",shape:"dot",text:"connecting"});
var options = {autoConnect:true,autoRejoin:false,floodProtection:true,secure:node.serverConfig.ssl,selfSigned:node.serverConfig.cert,port:node.serverConfig.port,retryDelay:20000};
node.serverConfig.ircclient = new irc.Client(node.serverConfig.server, node.serverConfig.nickname, options);
node.serverConfig.ircclient.setMaxListeners(0);
node.serverConfig.ircclient.addListener('error', function(message) {
if (RED.settings.verbose) { node.log("ERR: "+JSON.stringify(message)); }
});
node.serverConfig.ircclient.addListener('netError', function(message) {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("NET: "+JSON.stringify(message)); }
node.status({fill:"red",shape:"ring",text:"net error"});
});
node.serverConfig.ircclient.addListener('connect', function() {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("CONNECTED "); }
});
node.serverConfig.ircclient.addListener('registered', function(message) {
node.serverConfig.lastseen = Date.now();
node.log(node.serverConfig.ircclient.nick+" ONLINE: "+message.server);
node.status({fill:"yellow",shape:"dot",text:"connected"});
node.serverConfig.ircclient.join( node.channel, function(data) {
node.log(data+" JOINED: "+node.channel);
node.status({fill:"green",shape:"dot",text:"joined"});
});
});
node.serverConfig.ircclient.addListener('ping', function(server) {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("PING from "+JSON.stringify(server)); }
node.status({fill:"green",shape:"dot",text:"ok"});
});
node.serverConfig.ircclient.addListener('quit', function(nick, reason, channels, message) {
node.serverConfig.lastseen = Date.now();
if (RED.settings.verbose) { node.log("QUIT: "+nick+" "+reason+" "+channels+" "+JSON.stringify(message)); }
node.status({fill:"grey",shape:"ring",text:"quit"});
//node.serverConfig.ircclient.disconnect( function() {
// node.serverConfig.ircclient.connect();
//});
//if (RED.settings.verbose) { node.log("restart"); } // then retry
});
node.serverConfig.ircclient.addListener('raw', function (message) { // any message received means we are alive
//console.log("RAW:"+JSON.stringify(message));
if (message.commandType === "reply") {
//console.log("RAW:"+JSON.stringify(message));
node.serverConfig.lastseen = Date.now();
}
});
node.recon = setInterval( function() {
//console.log("CHK ",(Date.now()-node.serverConfig.lastseen)/1000);
if ((Date.now()-node.serverConfig.lastseen) > 240000) { // if more than 4 mins since last seen
node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]); // request time to check link
}
if ((Date.now()-node.serverConfig.lastseen) > 300000) { // If more than 5 mins
//node.serverConfig.ircclient.disconnect();
//node.serverConfig.ircclient.connect();
node.status({fill:"grey",shape:"ring",text:"no connection"});
if (RED.settings.verbose) { node.log("CONNECTION LOST ?"); }
}
//node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]); // request time to check link
}, 60000); // check every 1 min
//node.serverConfig.ircclient.connect();
}
else { node.status({text:""}); }
node.ircclient = node.serverConfig.ircclient;
node.on("input", function(msg) {
if (Object.prototype.toString.call( msg.raw ) === '[object Array]') {
if (RED.settings.verbose) { node.log("RAW command:"+msg.raw); }
node.ircclient.send.apply(node.ircclient,msg.raw);
}
else {
if (msg._topic) { delete msg._topic; }
var ch = node.channel.split(","); // split on , so we can send to multiple
if (node.sendFlag == "true") { // override channels with msg.topic
if ((msg.hasOwnProperty('topic'))&&(typeof msg.topic === "string")) {
ch = msg.topic.split(","); // split on , so we can send to multiple
}
else { node.warn("msg.topic not set"); }
}
for (var c = 0; c < ch.length; c++) {
if (node.sendFlag == "false") { // send whole message object to each channel
node.ircclient.say(ch[c], JSON.stringify(msg));
}
else { // send just the payload to each channel
if (typeof msg.payload === "object") { msg.payload = JSON.stringify(msg.payload); }
node.ircclient.say(ch[c], msg.payload);
}
}
}
});
node.on("close", function() {
node.ircclient.removeAllListeners();
if (node.recon) { clearInterval(node.recon); }
});
}
RED.nodes.registerType("irc out",IrcOutNode);
}

View File

@ -1,105 +0,0 @@
<!--
Copyright 2013 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/x-red" data-template-name="redis out">
<div class="form-row node-input-hostname">
<label for="node-input-hostname"><i class="fa fa-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="fa fa-key"></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="fa fa-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="fa fa-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 an object or field=value string.
</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

@ -1,110 +0,0 @@
/**
* Copyright 2013 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
module.exports = function(RED) {
"use strict";
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) {
RED.log.error(err);
});
connections[id].on("connect",function() {
if (RED.settings.verbose) { RED.log.info("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);
if (this.client.connected) {
this.status({fill:"green",shape:"dot",text:"connected"});
} else {
this.status({fill:"red",shape:"ring",text:"disconnected"},true);
}
var node = this;
this.client.on("end", function() {
node.status({fill:"red",shape:"ring",text:"disconnected"});
});
this.client.on("connect", function() {
node.status({fill:"green",shape:"dot",text:"connected"});
});
this.on("input", function(msg) {
var k = this.key || msg.topic;
if (k) {
if (this.structtype == "string") {
this.client.set(k,RED.util.ensureString(msg.payload));
} else if (this.structtype == "hash") {
if (typeof msg.payload == "object") {
this.client.hmset(k,msg.payload);
} else {
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");
}
});
this.on("close", function() {
redisConnectionPool.close(node.client);
});
}
RED.nodes.registerType("redis out",RedisOutNode);
}

View File

@ -1,231 +0,0 @@
<!--
Copyright 2013,2014 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="fa fa-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="fa fa-database"></i> Database</label>
<input type="text" id="node-config-input-db" placeholder="test">
</div>
<div class="form-row">
<label for="node-config-input-user"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-config-input-user">
</div>
<div class="form-row">
<label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
<input type="password" id="node-config-input-password">
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb', {
category: 'config',
color: "rgb(218, 196, 180)",
defaults: {
hostname: {value: "127.0.0.1", required: true},
port: {value: 27017, required: true},
db: {value: "", required: true},
name: {value: ""}
},
credentials: {
user: {type: "text"},
password: {type: "password"}
},
label: function() {
return this.name || this.hostname + ":" + this.port + "/" + this.db;
}
});
</script>
<script type="text/x-red" data-template-name="mongodb out">
<div class="form-row">
<label for="node-input-mongodb"><i class="fa fa-bookmark"></i> Server</label>
<input type="text" id="node-input-mongodb">
</div>
<div class="form-row">
<label for="node-input-collection"><i class="fa fa-briefcase"></i> Collection</label>
<input type="text" id="node-input-collection" placeholder="collection">
</div>
<div class="form-row">
<label for="node-input-operation"><i class="fa fa-wrench"></i> Operation</label>
<select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;">
<option value="store">save</option>
<option value="insert">insert</option>
<option value="update">update</option>
<option value="delete">remove</option>
</select>
</div>
<div class="form-row node-input-payonly">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-payonly" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-payonly" style="width: 70%;">Only store msg.payload object</label>
</div>
<div class="form-row node-input-upsert">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-upsert" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-upsert" style="width: 70%;">Create a new document if no match found</label>
</div>
<div class="form-row node-input-multi">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-multi" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;;">
<label for="node-input-multi" style="width: 70%;">Update all matching documents</label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips" id="node-warning" style="display: none"><b> Tip:</b> If no collection is set, ensure <b>msg.collection</b> will contain the collection name
</div>
</script>
<script type="text/x-red" data-help-name="mongodb out">
<p>A simple MongoDB output node. Can save, insert, update and remove objects from a chosen collection.</p>
<p>Save will update an existing object or insert a new object if one does not already exist.</p>
<p>Insert will insert a new object.</p>
<p>Save and insert either store <b>msg</b> or <b>msg.payload</b>.</p>
<p>Update will modify an existing object or objects. The query to select objects to update uses <b>msg.query</b> and the update to the element uses <b>msg.payload</b>.</p>
<p>Update can add a object if it does not exist or update multiple objects.</p>
<p>Remove will remove objects that match the query passed in on <b>msg.payload</b>. A blank query will delete <i>all of the objects</i> in the collection.</p>
<p>You can either set the collection method in the node config or on <b>msg.collection</b>. Setting it in the node will override <b>msg.collection</b>.</p>
<p>By default MongoDB creates an <i>_id</i> property as the primary key - so repeated injections of the same <b>msg</b> will result in many database entries.</p>
<p>If this is NOT the desired behaviour - ie. you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p>
<p>This could be a unique constant or you could create one based on some other msg property.</p>
<p>Currently we do not limit or cap the collection size at all... this may well change.</p>
</script>
<script type="text/javascript">
function oneditprepare() {
$("#node-input-operation").change(function () {
var id = $("#node-input-operation option:selected").val();
if (id === "update") {
$(".node-input-payonly").hide();
$(".node-input-upsert, .node-input-multi").show();
} else if (id === "delete") {
$(".node-input-payonly, .node-input-upsert, .node-input-multi").hide();
} else {
$(".node-input-payonly").show();
$(".node-input-upsert, .node-input-multi").hide();
}
});
$("#node-input-collection").change(function () {
if($("#node-input-collection").val() === "") {
$("#node-warning").show();
} else {
$("#node-warning").hide();
}
});
}
RED.nodes.registerType('mongodb out', {
category: 'storage-output',
color: "rgb(218, 196, 180)",
defaults: {
mongodb: {type: "mongodb", required: true},
name: {value: ""},
collection: {value: ""},
payonly: {value: false},
upsert: {value: false},
multi: {value: false},
operation: {value: "store"}
},
inputs: 1,
outputs: 0,
icon: "mongodb.png",
align: "right",
label: function() {
var mongoNode = RED.nodes.node(this.mongodb);
return this.name || (mongoNode ? mongoNode.label() + " " + this.collection: "mongodb");
},
labelStyle: function() {
return this.name ? "node_label_italic" : "";
},
oneditprepare: oneditprepare
});
</script>
<script type="text/x-red" data-template-name="mongodb in">
<div class="form-row">
<label for="node-input-mongodb"><i class="fa fa-bookmark"></i> Server</label>
<input type="text" id="node-input-mongodb">
</div>
<div class="form-row">
<label for="node-input-collection"><i class="fa fa-briefcase"></i> Collection</label>
<input type="text" id="node-input-collection" placeholder="collection">
</div>
<div class="form-row">
<label for="node-input-operation"><i class="fa fa-wrench"></i> Operation</label>
<select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;">
<option value="find">find</option>
<option value="count">count</option>
<option value="aggregate">aggregate</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-tips" id="node-warning" style="display: none"><b> Tip:</b> If no collection is set, ensure <b>msg.collection</b> will contain the collection name
</div>
</script>
<script type="text/x-red" data-help-name="mongodb in">
<p>Calls a MongoDB collection method based on the selected operator.</p>
<p>Find queries a collection using the <b>msg.payload</b> as the query statement as per the .find() function. Optionally, you may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object, a <b>msg.limit</b> number and a <b>msg.skip</b> number.</p>
<p>Count returns a count of the number of documents in a collection or matching a query using the <b>msg.payload</b> as the query statement.</p>
<p>Aggregate provides access to the aggregation pipeline using the <b>msg.payload</b> as the pipeline array.</p>
<p>You can either set the collection method in the node config or on <b>msg.collection</b>. Setting it in the node will override <b>msg.collection</b>.</p>
<p>See the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB collection methods docs</i></a> for examples.</p>
<p>The result is returned in <b>msg.payload</b>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mongodb in', {
category: 'storage-input',
color: "rgb(218, 196, 180)",
defaults: {
mongodb: {type: "mongodb", required: true},
name: {value: ""},
collection: {value: ""},
operation: {value: "find"}
},
inputs: 1,
outputs: 1,
icon: "mongodb.png",
label: function() {
var mongoNode = RED.nodes.node(this.mongodb);
return this.name || (mongoNode ? mongoNode.label() + " " + this.collection: "mongodb");
},
labelStyle: function() {
return this.name ? "node_label_italic" : "";
},
oneditprepare: oneditprepare
});
</script>

View File

@ -1,244 +0,0 @@
/**
* Copyright 2013,2014 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.
**/
module.exports = function(RED) {
"use strict";
var mongo = require('mongodb');
var MongoClient = mongo.MongoClient;
function MongoNode(n) {
RED.nodes.createNode(this,n);
this.hostname = n.hostname;
this.port = n.port;
this.db = n.db;
this.name = n.name;
var url = "mongodb://";
if (this.credentials && this.credentials.user && this.credentials.password) {
url += this.credentials.user+":"+this.credentials.password+"@";
}
url += this.hostname+":"+this.port+"/"+this.db;
this.url = url;
}
RED.nodes.registerType("mongodb",MongoNode,{
credentials: {
user: {type:"text"},
password: {type: "password"}
}
});
function ensureValidSelectorObject(selector) {
if (selector != null && (typeof selector != 'object' || Buffer.isBuffer(selector))) {
return {};
}
return selector;
}
function MongoOutNode(n) {
RED.nodes.createNode(this,n);
this.collection = n.collection;
this.mongodb = n.mongodb;
this.payonly = n.payonly || false;
this.upsert = n.upsert || false;
this.multi = n.multi || false;
this.operation = n.operation;
this.mongoConfig = RED.nodes.getNode(this.mongodb);
if (this.mongoConfig) {
var node = this;
MongoClient.connect(this.mongoConfig.url, function(err, db) {
if (err) {
node.error(err);
} else {
node.clientDb = db;
var coll;
if (node.collection) {
coll = db.collection(node.collection);
}
node.on("input",function(msg) {
if (!node.collection) {
if (msg.collection) {
coll = db.collection(msg.collection);
} else {
node.error("No collection defined",msg);
return;
}
}
delete msg._topic;
delete msg.collection;
if (node.operation === "store") {
if (node.payonly) {
if (typeof msg.payload !== "object") {
msg.payload = {"payload": msg.payload};
}
coll.save(msg.payload,function(err, item) {
if (err) {
node.error(err,msg);
}
});
} else {
coll.save(msg,function(err, item) {
if (err) {
node.error(err,msg);
}
});
}
} else if (node.operation === "insert") {
if (node.payonly) {
if (typeof msg.payload !== "object") {
msg.payload = {"payload": msg.payload};
}
coll.insert(msg.payload, function(err, item) {
if (err) {
node.error(err,msg);
}
});
} else {
coll.insert(msg, function(err,item) {
if (err) {
node.error(err,msg);
}
});
}
} else if (node.operation === "update") {
if (typeof msg.payload !== "object") {
msg.payload = {"payload": msg.payload};
}
var query = msg.query || {};
var payload = msg.payload || {};
var options = {
upsert: node.upsert,
multi: node.multi
};
coll.update(query, payload, options, function(err, item) {
if (err) {
node.error(err,msg);
}
});
} else if (node.operation === "delete") {
coll.remove(msg.payload, function(err, items) {
if (err) {
node.error(err,msg);
}
});
}
});
}
});
} else {
this.error("missing mongodb configuration");
}
this.on("close", function() {
if (this.clientDb) {
this.clientDb.close();
}
});
}
RED.nodes.registerType("mongodb out",MongoOutNode);
function MongoInNode(n) {
RED.nodes.createNode(this,n);
this.collection = n.collection;
this.mongodb = n.mongodb;
this.operation = n.operation || "find";
this.mongoConfig = RED.nodes.getNode(this.mongodb);
if (this.mongoConfig) {
var node = this;
var selector;
MongoClient.connect(this.mongoConfig.url, function(err,db) {
if (err) {
node.error(err);
} else {
node.clientDb = db;
var coll;
if (node.collection) {
coll = db.collection(node.collection);
}
node.on("input", function(msg) {
if (!node.collection) {
if (msg.collection) {
coll = db.collection(msg.collection);
} else {
node.error("No collection defined");
return;
}
}
if (node.operation === "find") {
msg.projection = msg.projection || {};
selector = ensureValidSelectorObject(msg.payload);
var limit = msg.limit;
if (typeof limit === "string" && !isNaN(limit)) {
limit = Number(limit);
}
var skip = msg.skip;
if (typeof skip === "string" && !isNaN(skip)) {
skip = Number(skip);
}
coll.find(selector,msg.projection).sort(msg.sort).limit(limit).skip(skip).toArray(function(err, items) {
if (err) {
node.error(err);
} else {
msg.payload = items;
delete msg.projection;
delete msg.sort;
delete msg.limit;
delete msg.skip;
node.send(msg);
}
});
} else if (node.operation === "count") {
selector = ensureValidSelectorObject(msg.payload);
coll.count(selector, function(err, count) {
if (err) {
node.error(err);
} else {
msg.payload = count;
node.send(msg);
}
});
} else if (node.operation === "aggregate") {
msg.payload = (Array.isArray(msg.payload)) ? msg.payload : [];
coll.aggregate(msg.payload, function(err, result) {
if (err) {
node.error(err);
} else {
msg.payload = result;
node.send(msg);
}
});
}
});
}
});
} else {
this.error("missing mongodb configuration");
}
this.on("close", function() {
if (this.clientDb) {
this.clientDb.close();
}
});
}
RED.nodes.registerType("mongodb in",MongoInNode);
}

View File

@ -3,7 +3,7 @@
"version" : "0.10.7",
"description" : "A visual tool for wiring the Internet of Things",
"homepage" : "http://nodered.org",
"license" : "Apache",
"license" : "Apache-2.0",
"repository" : {
"type":"git",
"url":"https://github.com/node-red/node-red.git"
@ -30,35 +30,31 @@
"bcryptjs": "2.1.0",
"nopt": "3.0.1",
"mqtt": "0.3.x",
"ws": "0.7.1",
"ws": "0.7.2",
"fs-extra": "0.16.3",
"clone": "0.2.0",
"mustache": "1.0.0",
"cron":"1.0.6",
"raw-body":"1.3.2",
"twitter-ng":"0.6.2",
"oauth":"0.9.12",
"xml2js":"0.4.4",
"sentiment":"0.2.3",
"follow-redirects":"0.0.3",
"cors":"2.5.3",
"cheerio":"0.18.0",
"cheerio":"0.19.0",
"uglify-js":"2.4.16",
"nodemailer":"1.3.0",
"imap":"0.8.14",
"request":"2.42.0",
"on-headers":"1.0.0",
"is-utf8":"0.2.0",
"feedparser":"0.19.2",
"fs.notify":"0.0.4",
"passport":"0.2.1",
"passport-http-bearer":"1.0.1",
"passport-oauth2-client-password":"0.1.2",
"oauth2orize":"1.0.1"
"oauth2orize":"1.0.1",
"node-red-node-feedparser":"0.0.2",
"node-red-node-email":"0.0.1",
"node-red-node-twitter":"0.0.4"
},
"optionalDependencies": {
"irc":"0.3.9",
"serialport":"1.4.10",
"node-red-node-serialport":"0.0.1",
"bcrypt":"0.8.1"
},
"devDependencies": {