mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
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:
parent
21d0adbdae
commit
d8eb926e2c
@ -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>
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -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> </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>
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -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>
|
|
@ -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>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
@ -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>
|
|
@ -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);
|
|
||||||
}
|
|
@ -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>
|
|
@ -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"}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -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> </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> </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>
|
|
@ -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);
|
|
||||||
}
|
|
@ -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>
|
|
@ -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);
|
|
||||||
}
|
|
@ -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> </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> </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> </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>
|
|
@ -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);
|
|
||||||
}
|
|
20
package.json
20
package.json
@ -3,7 +3,7 @@
|
|||||||
"version" : "0.10.7",
|
"version" : "0.10.7",
|
||||||
"description" : "A visual tool for wiring the Internet of Things",
|
"description" : "A visual tool for wiring the Internet of Things",
|
||||||
"homepage" : "http://nodered.org",
|
"homepage" : "http://nodered.org",
|
||||||
"license" : "Apache",
|
"license" : "Apache-2.0",
|
||||||
"repository" : {
|
"repository" : {
|
||||||
"type":"git",
|
"type":"git",
|
||||||
"url":"https://github.com/node-red/node-red.git"
|
"url":"https://github.com/node-red/node-red.git"
|
||||||
@ -30,35 +30,31 @@
|
|||||||
"bcryptjs": "2.1.0",
|
"bcryptjs": "2.1.0",
|
||||||
"nopt": "3.0.1",
|
"nopt": "3.0.1",
|
||||||
"mqtt": "0.3.x",
|
"mqtt": "0.3.x",
|
||||||
"ws": "0.7.1",
|
"ws": "0.7.2",
|
||||||
"fs-extra": "0.16.3",
|
"fs-extra": "0.16.3",
|
||||||
"clone": "0.2.0",
|
"clone": "0.2.0",
|
||||||
"mustache": "1.0.0",
|
"mustache": "1.0.0",
|
||||||
"cron":"1.0.6",
|
"cron":"1.0.6",
|
||||||
"raw-body":"1.3.2",
|
"raw-body":"1.3.2",
|
||||||
"twitter-ng":"0.6.2",
|
|
||||||
"oauth":"0.9.12",
|
|
||||||
"xml2js":"0.4.4",
|
"xml2js":"0.4.4",
|
||||||
"sentiment":"0.2.3",
|
"sentiment":"0.2.3",
|
||||||
"follow-redirects":"0.0.3",
|
"follow-redirects":"0.0.3",
|
||||||
"cors":"2.5.3",
|
"cors":"2.5.3",
|
||||||
"cheerio":"0.18.0",
|
"cheerio":"0.19.0",
|
||||||
"uglify-js":"2.4.16",
|
"uglify-js":"2.4.16",
|
||||||
"nodemailer":"1.3.0",
|
|
||||||
"imap":"0.8.14",
|
|
||||||
"request":"2.42.0",
|
|
||||||
"on-headers":"1.0.0",
|
"on-headers":"1.0.0",
|
||||||
"is-utf8":"0.2.0",
|
"is-utf8":"0.2.0",
|
||||||
"feedparser":"0.19.2",
|
|
||||||
"fs.notify":"0.0.4",
|
"fs.notify":"0.0.4",
|
||||||
"passport":"0.2.1",
|
"passport":"0.2.1",
|
||||||
"passport-http-bearer":"1.0.1",
|
"passport-http-bearer":"1.0.1",
|
||||||
"passport-oauth2-client-password":"0.1.2",
|
"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": {
|
"optionalDependencies": {
|
||||||
"irc":"0.3.9",
|
"node-red-node-serialport":"0.0.1",
|
||||||
"serialport":"1.4.10",
|
|
||||||
"bcrypt":"0.8.1"
|
"bcrypt":"0.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
Loading…
Reference in New Issue
Block a user