mirror of
https://github.com/node-red/node-red-nodes.git
synced 2025-03-01 10:37:43 +00:00
Merge branch 'master' of github.com:node-red/node-red-nodes
This commit is contained in:
commit
d289272afe
@ -11,5 +11,5 @@
|
||||
"shadow": true,
|
||||
"sub": true,
|
||||
"proto": true,
|
||||
"esversion": 6
|
||||
"esversion": 8
|
||||
}
|
||||
|
15
.travis.yml
15
.travis.yml
@ -8,10 +8,10 @@ matrix:
|
||||
- node_js: 12
|
||||
- node_js: 10
|
||||
- node_js: 8
|
||||
- python: 2.7
|
||||
language: python
|
||||
before_script: pip install flake8
|
||||
script: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
# - python: 2.7
|
||||
# language: python
|
||||
# before_script: pip install flake8
|
||||
# script: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
- python: 3.7
|
||||
language: python
|
||||
dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
|
||||
@ -23,7 +23,10 @@ before_install:
|
||||
before_script:
|
||||
# Remove the './node_modules/.bin:' entry, see https://github.com/travis-ci/travis-ci/issues/8813
|
||||
- export PATH=`echo ${PATH} | sed -re 's,(^|:)(./)?node_modules/.bin($|:),\1,'`
|
||||
- npm install -g istanbul grunt-cli coveralls
|
||||
- npm install -g nyc grunt-cli coveralls
|
||||
- npm install node-red
|
||||
script:
|
||||
- istanbul cover grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
|
||||
# - istanbul cover grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
|
||||
# - nyc --check-coverage --reporter=text --reporter=lcovonly --reporter=html grunt
|
||||
# - nyc grunt && rm -rf coverage .nyc_output
|
||||
- nyc grunt && nyc report --reporter=text-lcov | coveralls
|
||||
|
2
coverall
2
coverall
@ -1,3 +1,3 @@
|
||||
# check coverage of tests... and browse report
|
||||
istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report html
|
||||
nyc --check-coverage --reporter=html grunt
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome coverage/index.html
|
||||
|
@ -70,8 +70,7 @@
|
||||
<script type="text/html" data-template-name="rpi-gpio in">
|
||||
|
||||
<div class="form-row" style="min-width: 540px">
|
||||
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
|
||||
<input type="text" id="node-input-pin" style="display:none;">
|
||||
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
|
||||
<div class="rpi-gpio-pinTable">
|
||||
<div class="pinTableBody" id="pinform">
|
||||
<div class="pinTableRow">
|
||||
@ -157,6 +156,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="text" id="node-input-pin" style="width: 352px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-intype"><i class="fa fa-level-up"></i> <span data-i18n="rpi-gpio.label.resistor"></span></label>
|
||||
<select type="text" id="node-input-intype" style="width:100px;">
|
||||
@ -187,12 +190,29 @@
|
||||
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
|
||||
};
|
||||
var pinsInUse = {};
|
||||
var validPinValues = Object.values(bcm2pin);
|
||||
var isEnvVar = function (value) {
|
||||
var re = /^\${([0-9a-zA-Z_]+)}$/;
|
||||
var match = value.match(re);
|
||||
return Boolean(match);
|
||||
};
|
||||
var isInt = function (value) {
|
||||
return parseInt(value).toString() === value.trim();
|
||||
};
|
||||
var uncheckAll = function() {
|
||||
for (var i=0; i< validPinValues.length; i++) {
|
||||
$("#pinform input[value="+validPinValues[i]+"]").prop('checked', false);
|
||||
}
|
||||
}
|
||||
var validatePin = function (value) {
|
||||
return isEnvVar(value) || (isInt(value) && validPinValues.includes(value));
|
||||
};
|
||||
RED.nodes.registerType('rpi-gpio in',{
|
||||
category: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
pin: { value:"tri",required:true,validate:RED.validators.number() },
|
||||
pin: { value:"tri",required:true,validate:validatePin },
|
||||
intype: { value:"tri" },
|
||||
debounce: { value:"25" },
|
||||
read: { value:false }
|
||||
@ -227,11 +247,20 @@
|
||||
pinsInUse = data || {};
|
||||
$('#pin-tip').html(pintip + Object.keys(data));
|
||||
});
|
||||
|
||||
for (var i=0; i< validPinValues.length; i++) {
|
||||
$("#pinform input[value="+validPinValues[i]+"]").on("change", function (evt) {
|
||||
$("#node-input-pin").val(evt.currentTarget.value);
|
||||
$("#node-input-pin").removeClass("input-error");
|
||||
});
|
||||
}
|
||||
$("#node-input-pin").on("change", function() {
|
||||
if ($("#node-input-pin").val()) {
|
||||
$("#pinform input[value="+$("#node-input-pin").val()+"]").prop('checked', true);
|
||||
}
|
||||
var pinnew = $("#node-input-pin").val();
|
||||
if (pinnew && isInt(pinnew) && validPinValues.includes(pinnew)) {
|
||||
$("#pinform input[value="+pinnew+"]").prop('checked', true);
|
||||
} else {
|
||||
uncheckAll();
|
||||
}
|
||||
if ((pinnew) && (pinnew !== pinnow)) {
|
||||
if (pinsInUse.hasOwnProperty(pinnew)) {
|
||||
RED.notify(pinname+" "+pinnew+" "+alreadyuse,"warn");
|
||||
@ -256,7 +285,6 @@
|
||||
<script type="text/html" data-template-name="rpi-gpio out">
|
||||
<div class="form-row" style="min-width: 540px">
|
||||
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
|
||||
<input type="text" id="node-input-pin" style="display:none;">
|
||||
<div class="rpi-gpio-pinTable">
|
||||
<div class="pinTableBody" id="pinform">
|
||||
<div class="pinTableRow">
|
||||
@ -342,6 +370,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="text" id="node-input-pin" style="width: 352px">
|
||||
</div>
|
||||
<div class="form-row" id="node-set-pwm">
|
||||
<label> <span data-i18n="rpi-gpio.label.type"></span></label>
|
||||
<select id="node-input-out" style="width: 250px;">
|
||||
@ -381,12 +413,29 @@
|
||||
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
|
||||
};
|
||||
var pinsInUse = {};
|
||||
var validPinValues = Object.values(bcm2pin);
|
||||
var isEnvVar = function (value) {
|
||||
var re = /^\${([0-9a-zA-Z_]+)}$/;
|
||||
var match = value.match(re);
|
||||
return Boolean(match);
|
||||
};
|
||||
var isInt = function (value) {
|
||||
return parseInt(value).toString() === value.trim();
|
||||
};
|
||||
var uncheckAll = function() {
|
||||
for (var i=0; i< validPinValues.length; i++) {
|
||||
$("#pinform input[value="+validPinValues[i]+"]").prop('checked', false);
|
||||
}
|
||||
}
|
||||
var validatePin = function (value) {
|
||||
return isEnvVar(value) || (isInt(value) && validPinValues.includes(value));
|
||||
};
|
||||
RED.nodes.registerType('rpi-gpio out',{
|
||||
category: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
pin: { value:"",required:true,validate:RED.validators.number() },
|
||||
pin: { value:"",required:true,validate:validatePin },
|
||||
set: { value:"" },
|
||||
level: { value:"0" },
|
||||
freq: {value:""},
|
||||
@ -428,11 +477,19 @@
|
||||
$('#pin-tip').html(pintip + Object.keys(data));
|
||||
});
|
||||
|
||||
for (var i=0; i< validPinValues.length; i++) {
|
||||
$("#pinform input[value="+validPinValues[i]+"]").on("change", function (evt) {
|
||||
$("#node-input-pin").val(evt.currentTarget.value);
|
||||
$("#node-input-pin").removeClass("input-error");
|
||||
});
|
||||
}
|
||||
$("#node-input-pin").on("change", function() {
|
||||
if ($("#node-input-pin").val()) {
|
||||
$("#pinform input[value="+$("#node-input-pin").val()+"]").prop('checked', true);
|
||||
}
|
||||
var pinnew = $("#node-input-pin").val();
|
||||
if (pinnew && isInt(pinnew) && validPinValues.includes(pinnew)) {
|
||||
$("#pinform input[value="+pinnew+"]").prop('checked', true);
|
||||
} else {
|
||||
uncheckAll();
|
||||
}
|
||||
if ((pinnew) && (pinnew !== pinnow)) {
|
||||
if (pinsInUse.hasOwnProperty(pinnew)) {
|
||||
RED.notify(pinname+" "+pinnew+" "+alreadyuse,"warn");
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red-node-pi-gpio",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"description": "The basic Node-RED node for Pi GPIO",
|
||||
"dependencies" : {
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red-node-pi-gpiod",
|
||||
"version": "0.0.13",
|
||||
"version": "0.1.0",
|
||||
"description": "A node-red node for PiGPIOd",
|
||||
"dependencies" : {
|
||||
"js-pigpio": "*"
|
||||
|
@ -46,13 +46,13 @@ module.exports = function(RED) {
|
||||
PiGPIO.set_glitch_filter(node.pin,node.debounce);
|
||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.ok"});
|
||||
node.cb = PiGPIO.callback(node.pin, PiGPIO.EITHER_EDGE, function(gpio, level, tick) {
|
||||
node.send({ topic:"pi/"+node.pio, payload:Number(level) });
|
||||
node.send({ topic:"pi/"+node.pio, payload:Number(level), host:node.host });
|
||||
node.status({fill:"green",shape:"dot",text:level});
|
||||
});
|
||||
if (node.read) {
|
||||
setTimeout(function() {
|
||||
PiGPIO.read(node.pin, function(err, level) {
|
||||
node.send({ topic:"pi/"+node.pio, payload:Number(level) });
|
||||
node.send({ topic:"pi/"+node.pio, payload:Number(level), host:node.host });
|
||||
node.status({fill:"green",shape:"dot",text:level});
|
||||
});
|
||||
}, 20);
|
||||
|
@ -336,10 +336,12 @@ WeMoNG.prototype.getSocketStatus = function getSocketStatus(socket) {
|
||||
|
||||
res.on('end', function(){
|
||||
xml2js.parseString(data, function(err, result){
|
||||
if (!err) {
|
||||
if (!err && result["s:Envelope"]) {
|
||||
var status = result["s:Envelope"]["s:Body"][0]["u:GetBinaryStateResponse"][0]["BinaryState"][0];
|
||||
status = parseInt(status);
|
||||
def.resolve(status);
|
||||
} else {
|
||||
def.reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -401,12 +403,14 @@ WeMoNG.prototype.getLightStatus = function getLightStatus(light) {
|
||||
};
|
||||
def.resolve(obj);
|
||||
} else {
|
||||
def.reject();
|
||||
console.log("err");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("err");
|
||||
def.reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -489,6 +493,8 @@ WeMoNG.prototype.parseEvent = function parseEvent(evt) {
|
||||
msg.capabilityName = capabilityMap[msg.capability];
|
||||
msg.value = res['StateEvent']['Value'][0];
|
||||
def.resolve(msg);
|
||||
} else {
|
||||
def.reject();
|
||||
}
|
||||
});
|
||||
} else if (prop.hasOwnProperty('BinaryState')) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red-node-wemo",
|
||||
"version": "0.1.17",
|
||||
"version": "0.1.18",
|
||||
"description": "Input and Output nodes for Belkin WeMo devices",
|
||||
"repository": "https://github.com/node-red/node-red-nodes/tree/master/hardware",
|
||||
"main": "WeMoNG.js",
|
||||
|
@ -57,6 +57,7 @@
|
||||
<script type="text/html" data-help-name="serial out">
|
||||
<p>Provides a connection to an outbound serial port.</p>
|
||||
<p>Only the <code>msg.payload</code> is sent.</p>
|
||||
<p>Optionally the baudrate can be changed using <code>msg.baudrate</code></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>
|
||||
<p>Binary payloads can be sent by using a Buffer object.</p>
|
||||
</script>
|
||||
@ -111,6 +112,7 @@
|
||||
</li>
|
||||
<li><code>msg.count</code> if set this will override the configured number of characters as long as it is less than the number configured.</li>
|
||||
<li><code>msg.waitfor</code> single character, escape code, or hex code. If set, the node will wait until it matches that character in the stream and then start the output.</li>
|
||||
<li>Optionally the baudrate can be changed using <code>msg.baudrate</code></li>
|
||||
</ul>
|
||||
<h3>Outputs</h3>
|
||||
<ul>
|
||||
|
@ -41,6 +41,19 @@ module.exports = function(RED) {
|
||||
node.port = serialPool.get(this.serialConfig);
|
||||
|
||||
node.on("input",function(msg) {
|
||||
if (msg.hasOwnProperty("baudrate")) {
|
||||
var baud = parseInt(msg.baudrate);
|
||||
if (isNaN(baud)) {
|
||||
node.error(RED._("serial.errors.badbaudrate"),msg);
|
||||
} else {
|
||||
node.port.update({baudRate: baud},function(err,res) {
|
||||
if (err) {
|
||||
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
|
||||
node.error(errmsg,msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!msg.hasOwnProperty("payload")) { return; } // do nothing unless we have a payload
|
||||
var payload = node.port.encodePayload(msg.payload);
|
||||
node.port.write(payload,function(err,res) {
|
||||
@ -121,6 +134,19 @@ module.exports = function(RED) {
|
||||
node.port = serialPool.get(this.serialConfig);
|
||||
// Serial Out
|
||||
node.on("input",function(msg) {
|
||||
if (msg.hasOwnProperty("baudrate")) {
|
||||
var baud = parseInt(msg.baudrate);
|
||||
if (isNaN(baud)) {
|
||||
node.error(RED._("serial.errors.badbaudrate"),msg);
|
||||
} else {
|
||||
node.port.update({baudRate: baud},function(err,res) {
|
||||
if (err) {
|
||||
var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path);
|
||||
node.error(errmsg,msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!msg.hasOwnProperty("payload")) { return; } // do nothing unless we have a payload
|
||||
if (msg.hasOwnProperty("count") && (typeof msg.count === "number") && (node.serialConfig.out === "count")) {
|
||||
node.serialConfig.newline = msg.count;
|
||||
@ -249,6 +275,7 @@ module.exports = function(RED) {
|
||||
return payload;
|
||||
},
|
||||
write: function(m,cb) { this.serial.write(m,cb); },
|
||||
update: function(m,cb) { this.serial.update(m,cb); },
|
||||
enqueue: function(msg,sender,cb) {
|
||||
var payload = this.encodePayload(msg.payload);
|
||||
var qobj = {
|
||||
|
@ -66,7 +66,8 @@
|
||||
"unexpected-close": "serial port __port__ closed unexpectedly",
|
||||
"disconnected": "serial port __port__ disconnected",
|
||||
"closed": "serial port __port__ closed",
|
||||
"list": "Failed to list ports. Please enter manually."
|
||||
"list": "Failed to list ports. Please enter manually.",
|
||||
"badbaudrate": "Baudrate is invalid"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,8 @@
|
||||
"unexpected-close": "serial port __port__ closed unexpectedly",
|
||||
"disconnected": "serial port __port__ disconnected",
|
||||
"closed": "serial port __port__ closed",
|
||||
"list": "ポートのリスト化に失敗しました。手動で入力してください。"
|
||||
"list": "ポートのリスト化に失敗しました。手動で入力してください。",
|
||||
"badbaudrate": "ボーレートが不正です"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name" : "node-red-node-serialport",
|
||||
"version" : "0.10.3",
|
||||
"version" : "0.11.0",
|
||||
"description" : "Node-RED nodes to talk to serial ports",
|
||||
"dependencies" : {
|
||||
"serialport" : "^8.0.8"
|
||||
"serialport" : "^9.0.1"
|
||||
},
|
||||
"repository" : {
|
||||
"type":"git",
|
||||
|
@ -107,7 +107,7 @@ Values depends on the oids being requested.
|
||||
|
||||
### snmp-subtree
|
||||
|
||||
Simple SNMP oid subtree fetcher. Triggered by any input.
|
||||
Simple SNMP oid subtree fetcher. Triggered by any input. Reads from OID specified and any below it.
|
||||
|
||||
`msg.host` may contain the host.
|
||||
|
||||
@ -127,7 +127,7 @@ Values depends on the oids being requested.
|
||||
|
||||
### snmp-walker
|
||||
|
||||
Simple SNMP oid walker fetcher. Triggered by any input.
|
||||
Simple SNMP oid walker fetcher. Triggered by any input. Reads from OID specified to the end of the table.
|
||||
|
||||
`msg.host` may contain the host.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name" : "node-red-node-snmp",
|
||||
"version" : "0.0.24",
|
||||
"version" : "0.0.25",
|
||||
"description" : "A Node-RED node that looks for SNMP oids.",
|
||||
"dependencies" : {
|
||||
"net-snmp" : "1.2.4"
|
||||
|
@ -224,7 +224,7 @@
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="snmp subtree">
|
||||
<p>Simple SNMP oid subtree fetcher. Triggered by any input.</p>
|
||||
<p>Simple SNMP oid subtree fetcher. Triggered by any input. Reads all OIDS at and below the current base OID.</p>
|
||||
<p><code>msg.host</code> may contain the host.</p>
|
||||
<p><code>msg.community</code> may contain the community.</p>
|
||||
<p><code>msg.oid</code> may contain the oid of a table to request.</p>
|
||||
@ -287,12 +287,15 @@
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="snmp walker">
|
||||
<p>Simple SNMP oid walker fetcher. Triggered by any input.</p>
|
||||
<p>Simple SNMP oid walker fetcher. Triggered by any input.
|
||||
Fetches all nodes from this OID to the end of the table.</p>
|
||||
<p><code>msg.host</code> may contain the host.</p>
|
||||
<p><code>msg.community</code> may contain the community.</p>
|
||||
<p><code>msg.oid</code> may contain the oid of a table to request.</p>
|
||||
<p>OID must be numeric. iso. is the same a 1. </p>
|
||||
<p>The node will output <code>msg.payload</code> and <code>msg.oid</code>.</p>
|
||||
<p><b>Note</b>: This node does indeed "walk" down the tree. This is different behaviour to
|
||||
the typical snmpwalk command line app.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
13
package.json
13
package.json
@ -33,32 +33,33 @@
|
||||
"devDependencies": {
|
||||
"exif": "^0.6.0",
|
||||
"feedparser": "^2.2.10",
|
||||
"grunt": "^1.1.0",
|
||||
"grunt": "^1.3.0",
|
||||
"grunt-cli": "^1.3.2",
|
||||
"grunt-contrib-jshint": "^2.1.0",
|
||||
"grunt-jscs": "^3.0.1",
|
||||
"grunt-lint-inline": "^1.0.0",
|
||||
"grunt-simple-mocha": "^0.4.1",
|
||||
"imap": "^0.8.19",
|
||||
"mailparser": "^2.7.7",
|
||||
"markdown-it": "^10.0.0",
|
||||
"mailparser": "^3.0.0",
|
||||
"markdown-it": "^11.0.0",
|
||||
"mocha": "~6.2.3",
|
||||
"msgpack-lite": "^0.1.26",
|
||||
"multilang-sentiment": "^1.2.0",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-red": "^1.0.6",
|
||||
"node-red": "^1.1.3",
|
||||
"node-red-node-test-helper": "~0.2.5",
|
||||
"nodemailer": "^6.4.6",
|
||||
"nodemailer": "^6.4.10",
|
||||
"poplib": "^0.1.7",
|
||||
"proxyquire": "^2.1.3",
|
||||
"pushbullet": "^2.4.0",
|
||||
"sentiment": "^2.1.0",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "~7.5.0",
|
||||
"smtp-server": "^3.7.0",
|
||||
"supertest": "^4.0.2",
|
||||
"when": "^3.7.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
"node": ">=8.17.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "node-red-node-markdown",
|
||||
"version": "0.1.4",
|
||||
"version": "0.2.0",
|
||||
"description": "A Node-RED node to convert a markdown string to html.",
|
||||
"dependencies": {
|
||||
"markdown-it": "^10.0.0"
|
||||
"markdown-it": "^11.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -86,7 +86,7 @@
|
||||
return this._("email.email");
|
||||
},
|
||||
label: function() {
|
||||
return this.dname||this.name||"email";
|
||||
return this.dname||this.name||this._("email.email");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return (this.dname)?"node_label_italic":"";
|
||||
@ -280,3 +280,37 @@
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/html" data-template-name="e-mail mta">
|
||||
<div class="form-row">
|
||||
<label for="node-input-port"><i class="fa fa-random"></i> <span data-i18n="email.label.port"></span></label>
|
||||
<input type="text" id="node-input-port" style="width:70%;"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
|
||||
</div>
|
||||
<div class="form-tips" id="node-tip"><span data-i18n="[html]email.tip.mta"></span></div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('e-mail mta',{
|
||||
category: 'social',
|
||||
color:"#c7e9c0",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
port: {value:"1025",required:true},
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "envelope.png",
|
||||
paletteLabel: function() { return this._("email.email") + " MTA" },
|
||||
label: function() {
|
||||
return this.name||this._("email.email") + " MTA";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable indent */
|
||||
|
||||
/**
|
||||
* POP3 protocol - RFC1939 - https://www.ietf.org/rfc/rfc1939.txt
|
||||
@ -11,11 +12,13 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var nodemailer = require("nodemailer");
|
||||
var util = require("util");
|
||||
var Imap = require('imap');
|
||||
var POP3Client = require("poplib");
|
||||
var SimpleParser = require("mailparser").simpleParser;
|
||||
var util = require("util");
|
||||
var nodemailer = require("nodemailer");
|
||||
var simpleParser = require("mailparser").simpleParser;
|
||||
var SMTPServer = require("smtp-server").SMTPServer;
|
||||
//var microMTA = require("micromta").microMTA;
|
||||
|
||||
if (parseInt(process.version.split("v")[1].split(".")[0]) < 8) {
|
||||
throw "Error : Requires nodejs version >= 8.";
|
||||
@ -291,8 +294,8 @@ module.exports = function(RED) {
|
||||
|
||||
// We have now received a new email message. Create an instance of a mail parser
|
||||
// and pass in the email message. The parser will signal when it has parsed the message.
|
||||
SimpleParser(data, {}, function(err, parsed) {
|
||||
//node.log(util.format("SimpleParser: on(end): %j", mailObject));
|
||||
simpleParser(data, {}, function(err, parsed) {
|
||||
//node.log(util.format("simpleParser: on(end): %j", mailObject));
|
||||
if (err) {
|
||||
node.status({fill:"red", shape:"ring", text:"email.status.parseerror"});
|
||||
node.error(RED._("email.errors.parsefail", {folder:node.box}), err);
|
||||
@ -341,17 +344,29 @@ module.exports = function(RED) {
|
||||
ss = true;
|
||||
node.status({fill:"blue", shape:"dot", text:"email.status.fetching"});
|
||||
//console.log("> ready");
|
||||
// Open the inbox folder
|
||||
// Open the folder
|
||||
imap.openBox(node.box, // Mailbox name
|
||||
false, // Open readonly?
|
||||
function(err, box) {
|
||||
//console.log("> Inbox err : %j", err);
|
||||
//console.log("> Inbox open: %j", box);
|
||||
if (err) {
|
||||
s = false;
|
||||
var boxs = [];
|
||||
imap.getBoxes(function(err,boxes) {
|
||||
if (err) { return; }
|
||||
for (var prop in boxes) {
|
||||
if (boxes.hasOwnProperty(prop)) {
|
||||
if (boxes[prop].children) {
|
||||
boxs.push(prop+"/{"+Object.keys(boxes[prop].children)+'}');
|
||||
}
|
||||
else { boxs.push(prop); }
|
||||
}
|
||||
}
|
||||
node.error(RED._("email.errors.fetchfail", {folder:node.box+". Folders - "+boxs.join(', ')}),err);
|
||||
});
|
||||
node.status({fill:"red", shape:"ring", text:"email.status.foldererror"});
|
||||
node.error(RED._("email.errors.fetchfail", {folder:node.box}),err);
|
||||
imap.end();
|
||||
s = false;
|
||||
setInputRepeatTimeout();
|
||||
return;
|
||||
}
|
||||
@ -394,7 +409,7 @@ module.exports = function(RED) {
|
||||
//console.log("> Fetch message - msg=%j, seqno=%d", imapMessage, seqno);
|
||||
imapMessage.on('body', function(stream, info) {
|
||||
//console.log("> message - body - stream=?, info=%j", info);
|
||||
SimpleParser(stream, {}, function(err, parsed) {
|
||||
simpleParser(stream, {}, function(err, parsed) {
|
||||
if (err) {
|
||||
node.status({fill:"red", shape:"ring", text:"email.status.parseerror"});
|
||||
node.error(RED._("email.errors.parsefail", {folder:node.box}),err);
|
||||
@ -412,19 +427,21 @@ module.exports = function(RED) {
|
||||
var cleanup = function() {
|
||||
imap.end();
|
||||
s = false;
|
||||
setInputRepeatTimeout();
|
||||
};
|
||||
if (this.disposition === "Delete") {
|
||||
if (node.disposition === "Delete") {
|
||||
imap.addFlags(results, "\Deleted", cleanup);
|
||||
} else if (this.disposition === "Read") {
|
||||
} else if (node.disposition === "Read") {
|
||||
imap.addFlags(results, "\Seen", cleanup);
|
||||
} else {
|
||||
cleanup();
|
||||
}
|
||||
setInputRepeatTimeout();
|
||||
});
|
||||
|
||||
fetch.once('error', function(err) {
|
||||
console.log('Fetch error: ' + err);
|
||||
imap.end();
|
||||
s = false;
|
||||
setInputRepeatTimeout();
|
||||
});
|
||||
}
|
||||
@ -442,7 +459,7 @@ module.exports = function(RED) {
|
||||
if (node.protocol === "POP3") {
|
||||
checkPOP3(msg);
|
||||
} else if (node.protocol === "IMAP") {
|
||||
if (s === false) { checkIMAP(msg); }
|
||||
if (s === false && ss == false) { checkIMAP(msg); }
|
||||
}
|
||||
} // End of checkEmail
|
||||
|
||||
@ -499,4 +516,61 @@ module.exports = function(RED) {
|
||||
global: { type:"boolean" }
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function EmailMtaNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.port = n.port;
|
||||
var node = this;
|
||||
|
||||
node.mta = new SMTPServer({
|
||||
secure: false,
|
||||
logger: false,
|
||||
disabledCommands: ['AUTH', 'STARTTLS'],
|
||||
|
||||
onData: function (stream, session, callback) {
|
||||
simpleParser(stream, { skipTextToHtml:true, skipTextLinks:true }, (err, parsed) => {
|
||||
if (err) { node.error(RED._("email.errors.parsefail"),err); }
|
||||
else {
|
||||
node.status({fill:"green", shape:"dot", text:""});
|
||||
var msg = {}
|
||||
msg.payload = parsed.text;
|
||||
msg.topic = parsed.subject;
|
||||
msg.date = parsed.date;
|
||||
msg.header = {};
|
||||
parsed.headers.forEach((v, k) => {msg.header[k] = v;});
|
||||
if (parsed.html) { msg.html = parsed.html; }
|
||||
if (parsed.to) {
|
||||
if (typeof(parsed.to) === "string" && parsed.to.length > 0) { msg.to = parsed.to; }
|
||||
else if (parsed.to.hasOwnProperty("text") && parsed.to.text.length > 0) { msg.to = parsed.to.text; }
|
||||
}
|
||||
if (parsed.cc) {
|
||||
if (typeof(parsed.cc) === "string" && parsed.cc.length > 0) { msg.cc = parsed.cc; }
|
||||
else if (parsed.cc.hasOwnProperty("text") && parsed.cc.text.length > 0) { msg.cc = parsed.cc.text; }
|
||||
}
|
||||
if (parsed.cc && parsed.cc.length > 0) { msg.cc = parsed.cc; }
|
||||
if (parsed.bcc && parsed.bcc.length > 0) { msg.bcc = parsed.bcc; }
|
||||
if (parsed.from && parsed.from.value && parsed.from.value.length > 0) { msg.from = parsed.from.value[0].address; }
|
||||
if (parsed.attachments) { msg.attachments = parsed.attachments; }
|
||||
else { msg.attachments = []; }
|
||||
node.send(msg); // Propagate the message down the flow
|
||||
setTimeout(function() { node.status({})}, 500);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
node.mta.listen(node.port);
|
||||
|
||||
node.mta.on("error", err => {
|
||||
node.error("Error: " + err.message, err);
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
node.mta.close();
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("e-mail mta",EmailMtaNode);
|
||||
|
||||
};
|
||||
|
@ -26,7 +26,7 @@
|
||||
<p>Additionally <code>msg.header</code> contains the complete header object including
|
||||
<i>to</i>, <i>cc</i> and other potentially useful properties.</p>
|
||||
<p>It can optionally mark the message as Read (default), Delete it, or leave unmarked (None).</p>
|
||||
<p>Uses the <a href="https://github.com/mscdex/node-imap/blob/master/README.md" target="_new">node-imap module</a> - see that page for
|
||||
<p>Uses the <a href="https://github.com/mscdex/node-imap/blob/master/README.md" target="_new">node-imap module</a> - see that page for
|
||||
information on the <code>msg.criteria</code> format if needed.</p>
|
||||
<p><b>Note</b>: uses IMAP with SSL to port 993.</p>
|
||||
<p>Any attachments supplied in the incoming email can be found in the <code>msg.attachments</code> property. This will be an array of objects where
|
||||
@ -51,3 +51,13 @@
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="e-mail mta">
|
||||
<p>Mail Transfer Agent - listens on a port for incoming SMTP mail.</p>
|
||||
<p><b>Note</b>: "NOT for production use" as there is no security built in.
|
||||
This is primarily for local testing of outbound mail sending, but could be used
|
||||
as a mail forwarder to a real email service if required.</p>
|
||||
<p>To use ports below 1024, for example 25 or 465, you may need to get privileged access.
|
||||
On linux systems this can be done by running
|
||||
<pre>sudo setcap 'cap_net_bind_service=+eip' $(which node)</pre>
|
||||
and restarting Node-RED. Be aware - this gives all node applications access to all ports.</p>
|
||||
</script>
|
@ -34,7 +34,8 @@
|
||||
"default-message": "__description__\n\nFile from Node-RED is attached: __filename__",
|
||||
"tip": {
|
||||
"cred": "<b>Note:</b> Copied credentials from global emailkeys.js file.",
|
||||
"recent": "Tip: Only retrieves the single most recent email."
|
||||
"recent": "Tip: Only retrieves the single most recent email.",
|
||||
"mta": "<b>Note:</b> To use ports below 1024 you may need elevated (root) privileges. See help sidebar."
|
||||
},
|
||||
"status": {
|
||||
"messagesent": "Message sent: __response__",
|
||||
@ -47,6 +48,7 @@
|
||||
"inboxzero": "you have achieved Inbox Zero",
|
||||
"sending": "sending",
|
||||
"sendfail": "send failed",
|
||||
"parseerror": "Failed to parse message",
|
||||
"connecterror": "connect error"
|
||||
},
|
||||
"errors": {
|
||||
@ -56,6 +58,7 @@
|
||||
"nosmtptransport": "No SMTP transport. See info panel.",
|
||||
"nopayload": "No payload to send",
|
||||
"fetchfail": "Failed to fetch folder: __folder__",
|
||||
"parsefail": "Failed to parse message",
|
||||
"messageerror": "Fetch message error: __error__",
|
||||
"refreshtoolarge": "Refresh interval too large. Limiting to 2147483 seconds"
|
||||
}
|
||||
|
@ -38,3 +38,13 @@
|
||||
<p>POP3プロトコルでのデフォルトポート番号は、素のTCPでは110番、SSLでは995番です。IMAPプロトコルでは、素のTCPでは143番、SSLでは993番です。</p>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="e-mail mta">
|
||||
<p>メール転送エージェント - 入ってくるSMTPメールのため、ポートでリッスンします。</p>
|
||||
<p><b>注釈</b>: セキュリティが組み込まれていないため、"プロダクション向けではありません"。
|
||||
これは主に、外部へのメール送信のローカルテスト向けですが、必要に応じて実際のメールサービスへのメール転送としても使用できます。</p>
|
||||
<p>1024未満のポート(例えば25や465など)を使用するには、特権的アクセスを得る必要がある場合があります。
|
||||
Linuxシステムでは以下のコマンドを実行し、Node-REDを再起動することで達成できます。
|
||||
<pre>sudo setcap ‘cap_net_bind_service=+eip’ $(which node)</pre>
|
||||
これによって、全てのnodeアプリケーションが全ポートにアクセスできる様になることに注意してください.</p>
|
||||
</script>
|
@ -21,7 +21,8 @@
|
||||
"default-message": "__description__\n\nNode-REDからファイルが添付されました: __filename__",
|
||||
"tip": {
|
||||
"cred": "<b>注釈:</b> emailkeys.jsファイルから認証情報をコピーしました。",
|
||||
"recent": "注釈: 最新のメールを1件のみ取得します。"
|
||||
"recent": "注釈: 最新のメールを1件のみ取得します。",
|
||||
"mta": "<b>注釈:</b> 1024未満のポートを使用するには、昇格された(root)特権が必要です。ヘルプサイドバーを参照してください。"
|
||||
},
|
||||
"status": {
|
||||
"messagesent": "メッセージを送信しました: __response__",
|
||||
@ -34,6 +35,7 @@
|
||||
"inboxzero": "受信トレイにメールがありません",
|
||||
"sending": "送信中",
|
||||
"sendfail": "送信が失敗しました",
|
||||
"parseerror": "メッセージのパースに失敗",
|
||||
"connecterror": "接続エラー"
|
||||
},
|
||||
"errors": {
|
||||
@ -43,6 +45,7 @@
|
||||
"nosmtptransport": "SMTP転送が設定されていません。「情報」タブを参照してください",
|
||||
"nopayload": "送信するペイロードがありません",
|
||||
"fetchfail": "フォルダの受信に失敗しました: __folder__",
|
||||
"parsefail": "メッセージのパースに失敗",
|
||||
"messageerror": "メッセージ受信エラー: __error__"
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "node-red-node-email",
|
||||
"version": "1.7.8",
|
||||
"description": "Node-RED nodes to send and receive simple emails",
|
||||
"version": "1.8.1",
|
||||
"description": "Node-RED nodes to send and receive simple emails.",
|
||||
"dependencies": {
|
||||
"imap": "^0.8.19",
|
||||
"mailparser": "^2.7.7",
|
||||
"nodemailer": "^6.4.6",
|
||||
"poplib": "^0.1.7"
|
||||
"poplib": "^0.1.7",
|
||||
"mailparser": "^3.0.0",
|
||||
"nodemailer": "~6.4.10",
|
||||
"smtp-server": "^3.7.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -18,7 +19,8 @@
|
||||
"email",
|
||||
"gmail",
|
||||
"imap",
|
||||
"pop"
|
||||
"pop",
|
||||
"mta"
|
||||
],
|
||||
"node-red": {
|
||||
"nodes": {
|
||||
|
@ -3,9 +3,7 @@ module.exports = function(RED) {
|
||||
"use strict";
|
||||
var PushBullet = require('pushbullet');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var when = require('when');
|
||||
var nodefn = require('when/node');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
function onError(err, node) {
|
||||
@ -127,17 +125,19 @@ module.exports = function(RED) {
|
||||
stream.on('close', function() {
|
||||
self.emitter.emit('stream_disconnected');
|
||||
if (!closing) {
|
||||
if (tout) { clearTimeout(tout); }
|
||||
tout = setTimeout(function() {
|
||||
stream.connect();
|
||||
},15000);
|
||||
}, 15000);
|
||||
}
|
||||
});
|
||||
stream.on('error', function(err) {
|
||||
self.emitter.emit('stream_error', err);
|
||||
if (!closing) {
|
||||
if (tout) { clearTimeout(tout); }
|
||||
tout = setTimeout(function() {
|
||||
stream.connect();
|
||||
},15000);
|
||||
}, 15000);
|
||||
}
|
||||
});
|
||||
stream.connect();
|
||||
@ -229,10 +229,14 @@ module.exports = function(RED) {
|
||||
msg.payload = incoming.body;
|
||||
}
|
||||
else if (incoming.type === 'dismissal') {
|
||||
msg.topic = "dismissal";
|
||||
msg.topic = "Push dismissed";
|
||||
msg.payload = incoming.iden;
|
||||
}
|
||||
else if (incoming.type === 'sms_changed') {
|
||||
msg.topic = "SMS: "+ incoming.notifications[0].title;
|
||||
msg.payload = incoming.notifications[0].body;
|
||||
msg.message = incoming;
|
||||
}
|
||||
else {
|
||||
this.error("unknown push type: " + incoming.type + " content: " + JSON.stringify(incoming));
|
||||
return;
|
||||
@ -257,8 +261,7 @@ module.exports = function(RED) {
|
||||
try {
|
||||
pushkeys = RED.settings.pushbullet || require(process.env.NODE_RED_HOME+"/../pushkey.js");
|
||||
}
|
||||
catch(err) {
|
||||
}
|
||||
catch(err) { }
|
||||
|
||||
var cred = RED.nodes.getCredentials(n.id);
|
||||
// get old apikey
|
||||
@ -327,7 +330,7 @@ module.exports = function(RED) {
|
||||
try {
|
||||
this.deviceid = this.credentials.deviceid;
|
||||
}
|
||||
catch(err) {}
|
||||
catch(err) { }
|
||||
}
|
||||
|
||||
if (configNode) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name" : "node-red-node-pushbullet",
|
||||
"version" : "0.0.14",
|
||||
"version" : "0.0.17",
|
||||
"description" : "A Node-RED node to send alerts via Pushbullet",
|
||||
"dependencies" : {
|
||||
"pushbullet": "^2.4.0",
|
||||
|
@ -103,8 +103,8 @@ module.exports = function(RED) {
|
||||
if (err) { node.error(err,msg); }
|
||||
else {
|
||||
try {
|
||||
response = JSON.parse(response);
|
||||
if (response.status !== 1) { node.error("[57-pushover.js] Error: "+response); }
|
||||
var responseObject = JSON.parse(response);
|
||||
if (responseObject.status !== 1) { node.error("[57-pushover.js] Error: "+response); }
|
||||
}
|
||||
catch(e) {
|
||||
node.error("[57-pushover.js] Error: "+response);
|
||||
|
@ -155,7 +155,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
else if(stanza.attrs.type === 'result'){
|
||||
// AM To-Do check for 'bind' result with our current jid
|
||||
// To-Do check for 'bind' result with our current jid
|
||||
var query = stanza.getChild('query');
|
||||
if (RED.settings.verbose || LOGITALL) {that.log("result!");}
|
||||
if (RED.settings.verbose || LOGITALL) {that.log(query);}
|
||||
@ -164,7 +164,6 @@ module.exports = function(RED) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// We shouldn't have any errors here that the input/output nodes can't handle
|
||||
// if you need to see everything though; uncomment this block
|
||||
// this.client.on('error', err => {
|
||||
@ -497,6 +496,10 @@ module.exports = function(RED) {
|
||||
node.error("Server doesn't exist "+xmpp.options.service,err);
|
||||
node.status({fill:"red",shape:"ring",text:"bad address"});
|
||||
}
|
||||
if (err.errno === "ENOTFOUND") {
|
||||
node.error("Server doesn't exist "+xmpp.options.service,err);
|
||||
node.status({fill:"red",shape:"ring",text:"bad address"});
|
||||
}
|
||||
else if (err === "XMPP authentication failure") {
|
||||
node.error(err,err);
|
||||
node.status({fill:"red",shape:"ring",text:"XMPP authentication failure"});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red-node-xmpp",
|
||||
"version": "0.2.4",
|
||||
"version": "0.3.0",
|
||||
"description": "A Node-RED node to talk to an XMPP server",
|
||||
"dependencies": {
|
||||
"@xmpp/client": "^0.11.1"
|
||||
|
@ -1,11 +1,29 @@
|
||||
|
||||
<script type="text/x-red" data-template-name="mongodb">
|
||||
<script type="text/html" data-template-name="mongodb">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-hostname"><i class="fa fa-bookmark"></i> <span data-i18n="mongodb.label.host"></span></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; "> <span data-i18n="mongodb.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" style="width:45px">
|
||||
<label for="node-config-input-hostname"><i class="fa fa-bookmark"></i><span data-i18n="mongodb.label.host"></span></label>
|
||||
<input class="input-append-left" type="text" id="node-config-input-hostname" placeholder="localhost">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-config-input-topology">
|
||||
<label for="node-config-input-topology"><span data-i18n="mongodb.label.topology"></span></label>
|
||||
<select id="node-config-input-topology">
|
||||
<option type="button" class="red-ui-button toggle topology-group" selected value="direct">Direct (mongodb://)</button>
|
||||
<option type="button" class="red-ui-button toggle topology-group" value="replicaset">RelicaSet/Cluster (mongodb://)</button>
|
||||
<option type="button" class="red-ui-button toggle topology-group" value="dnscluster">DNS Cluster (mongodb+srv://)</button>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row node-config-connectOptions">
|
||||
<label for="node-config-input-connectOptions"><i class="fa fa-wrench"></i><span data-i18n="mongodb.label.connectOptions"></span></label>
|
||||
<input type="text" id="node-config-input-connectOptions">
|
||||
</div>
|
||||
|
||||
<div class="form-row node-config-input-port">
|
||||
<label for="node-config-input-port"><i class="fa fa-plug"></i><span data-i18n="mongodb.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" style="width:55px;">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-db"><i class="fa fa-database"></i> <span data-i18n="mongodb.label.database"></span></label>
|
||||
<input type="text" id="node-config-input-db">
|
||||
@ -30,6 +48,8 @@
|
||||
color: "rgb(218, 196, 180)",
|
||||
defaults: {
|
||||
hostname: {value: "127.0.0.1", required: true},
|
||||
topology: {value: "direct", required: true},
|
||||
connectOptions: {value: "", required: false},
|
||||
port: {value: 27017, required: true},
|
||||
db: {value: "", required: true},
|
||||
name: {value: ""}
|
||||
@ -39,13 +59,60 @@
|
||||
password: {type: "password"}
|
||||
},
|
||||
label: function() {
|
||||
return this.name || this.hostname + ":" + this.port + "/" + this.db;
|
||||
}
|
||||
return this.name || this.db + "@" + this.hostname;
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$("#node-config-input-topology").on("change", function() {
|
||||
var topology = $("#node-config-input-topology option:selected").val();
|
||||
if (topology === "direct") {
|
||||
$(".node-config-input-port").show();
|
||||
} else {
|
||||
$(".node-config-input-port").hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="mongodb">
|
||||
<p>Define a connection method to your MongoDB server instance.</p>
|
||||
<p>There are 3 supported options:
|
||||
<details><summary>Standard/direct</summary>
|
||||
For databases that request connections in the form
|
||||
<code>
|
||||
mongodb://[username]:[password]@[hostname]:[port]/[dbname]
|
||||
</code>
|
||||
Most often used for local MongoDB instances (localhost:27017), and other stand-alone instances.
|
||||
</details>
|
||||
<details><summary>Standard/replicaset</summary>
|
||||
For databases that request connections in the form
|
||||
<code>
|
||||
mongodb://[username]:[password]@[hostnameA]:[port],[hostnameB]:[port]/[dbname]?replicaSet=[replsetname]
|
||||
</code>
|
||||
Often used with <q>database as a service</q> offerings,
|
||||
or on-premises instances that have been configured for availability and resilience
|
||||
</details>
|
||||
<details><summary>Clustered by DNS seedlist</summary>
|
||||
For databases that request connections in the form
|
||||
<code>
|
||||
mongodb+srv://[username]:[password]@[clustername]/[dbname]?retryWrites=true&w=majority
|
||||
</code>
|
||||
A configuration of MongoDB instances that provide availability and performance
|
||||
through replication and sharding, accessed through a cluster alias name, rather than
|
||||
by specific host:port connections. This is the default for MongoDB instances in the
|
||||
<a href="https://www.mongodb.com/cloud/atlas" target="_blank">Atlas cloud service</a>.
|
||||
</details>
|
||||
<p><strong>Connect options</strong> is where you add the optional parameters required by your MongoDB instance.
|
||||
This might include:
|
||||
<ul><li>w=majority</li><li>replicaSet=replset</li><li>authSource=admin</li></ul> and any other options appropriate -
|
||||
full set available at <a href="https://docs.mongodb.com/manual/reference/connection-string/" target="_blank">
|
||||
Connection String URI Format — MongoDB Manual</a>.
|
||||
<p>If you are connecting to <a href="https://cloud.ibm.com/catalog/services/databases-for-mongodb-group" target="_blank">
|
||||
IBM Databases for MongoDB</a>, as a replica-set, be sure to append <code>ssl=true&tlsAllowInvalidCertificates=true </code>
|
||||
to the <strong>Connect options</strong>.
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="mongodb out">
|
||||
<script type="text/html" data-template-name="mongodb out">
|
||||
<div class="form-row">
|
||||
<label for="node-input-mongodb"><i class="fa fa-bookmark"></i> <span data-i18n="mongodb.label.server"></span></label>
|
||||
<input type="text" id="node-input-mongodb">
|
||||
@ -85,29 +152,28 @@
|
||||
<div class="form-tips" id="node-warning" style="display: none"><span data-i18n="[html]mongodb.tip"></span></div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="mongodb out">
|
||||
<script type="text/html" 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 <code>msg</code> or <code>msg.payload</code>.</p>
|
||||
<p>Update will modify an existing object or objects. The query to select objects to update uses <code>msg.query</code>,
|
||||
<p>Update will modify an existing object or objects. The query to select objects to update uses <code>msg.query</code>
|
||||
and the update to the element uses <code>msg.payload</code>. If <code>msg.query._id</code> is
|
||||
a valid mongo ObjectId string it will be converted to an ObjectId type.</p>
|
||||
<p>Update can add an object if it does not exist or update multiple objects.</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 <code>msg.payload</code>. 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 <code>msg.collection</code>. Setting it in the
|
||||
node will override <code>msg.collection</code>.</p>
|
||||
<p>By default, MongoDB creates an <i>_id</i> property as the primary key, so repeated injections of the
|
||||
<p>By default MongoDB creates an <i>_id</i> property as the primary key - so repeated injections of the
|
||||
same <code>msg</code> will result in many database entries.</p>
|
||||
<p>If this is NOT the desired behaviour, i.e., you want repeated entries to overwrite, then you must set
|
||||
<p>If this is NOT the desired behaviour - ie. you want repeated entries to overwrite, then you must set
|
||||
the <code>msg._id</code> 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, however this may well change.</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();
|
||||
@ -150,7 +216,7 @@
|
||||
align: "right",
|
||||
label: function() {
|
||||
var mongoNode = RED.nodes.node(this.mongodb);
|
||||
return this.name || (mongoNode ? mongoNode.label() + " " + this.collection: "mongodb");
|
||||
return this.name || (mongoNode ? mongoNode.label() + " " + this.collection : "mongodb");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name ? "node_label_italic" : "";
|
||||
@ -160,7 +226,7 @@
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/x-red" data-template-name="mongodb in">
|
||||
<script type="text/html" data-template-name="mongodb in">
|
||||
<div class="form-row">
|
||||
<label for="node-input-mongodb"><i class="fa fa-bookmark"></i> <span data-i18n="mongodb.label.server"></span></label>
|
||||
<input type="text" id="node-input-mongodb">
|
||||
@ -184,12 +250,12 @@
|
||||
<div class="form-tips" id="node-warning" style="display: none"><span data-i18n="[html]mongodb.tip"></span></div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="mongodb in">
|
||||
<script type="text/html" data-help-name="mongodb in">
|
||||
<p>Calls a MongoDB collection method based on the selected operator.</p>
|
||||
<p>Find queries a collection using the <code>msg.payload</code> as the query statement as per the .find() function.
|
||||
Optionally, you may also set a <code>msg.projection</code> object (via a function) to constrain the returned
|
||||
fields. You can also set a <code>msg.sort</code> object, a <code>msg.limit</code> number and a <code>msg.skip</code> number.</p>
|
||||
<p>Count returns a count of the number of documents in a collection, or matches a query using the
|
||||
Optionally, you may also (via a function) set a <code>msg.projection</code> object to constrain the returned
|
||||
fields, a <code>msg.sort</code> object, a <code>msg.limit</code> number and a <code>msg.skip</code> number.</p>
|
||||
<p>Count returns a count of the number of documents in a collection or matching a query using the
|
||||
<code>msg.payload</code> as the query statement.</p>
|
||||
<p>Aggregate provides access to the aggregation pipeline using the <code>msg.payload</code> as the pipeline array.</p>
|
||||
<p>You can either set the collection method in the node config or on <code>msg.collection</code>. Setting it in
|
||||
@ -200,7 +266,6 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
RED.nodes.registerType('mongodb in', {
|
||||
category: 'storage-input',
|
||||
color: "rgb(218, 196, 180)",
|
||||
@ -215,7 +280,7 @@
|
||||
icon: "mongodb.png",
|
||||
label: function() {
|
||||
var mongoNode = RED.nodes.node(this.mongodb);
|
||||
return this.name || (mongoNode ? mongoNode.label() + " " + this.collection: "mongodb");
|
||||
return this.name || (mongoNode ? mongoNode.label() + " " + this.collection : "mongodb");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name ? "node_label_italic" : "";
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var mongo = require('mongodb');
|
||||
@ -11,13 +12,37 @@ module.exports = function(RED) {
|
||||
this.port = n.port;
|
||||
this.db = n.db;
|
||||
this.name = n.name;
|
||||
this.connectOptions= n.connectOptions;
|
||||
this.topology = n.topology;
|
||||
|
||||
//console.log(this);
|
||||
|
||||
var clustered = (this.topology !== "direct") || false;
|
||||
|
||||
var url = "mongodb://";
|
||||
if (this.credentials && this.credentials.user && this.credentials.password) {
|
||||
url += this.credentials.user+":"+this.credentials.password+"@";
|
||||
if (this.topology === "dnscluster") {
|
||||
url = "mongodb+srv://";
|
||||
}
|
||||
if (this.credentials && this.credentials.user && this.credentials.password) {
|
||||
this.user = this.credentials.user;
|
||||
this.password = this.credentials.password;
|
||||
} else {
|
||||
this.user = n.user;
|
||||
this.password = n.password;
|
||||
}
|
||||
if (this.user) {
|
||||
url += this.user+":"+this.password+"@";
|
||||
}
|
||||
if (clustered) {
|
||||
url += this.hostname + "/" + this.db
|
||||
} else {
|
||||
url += this.hostname + ":" + this.port + "/" + this.db;
|
||||
}
|
||||
if (this.connectOptions){
|
||||
url += "?" + this.connectOptions;
|
||||
}
|
||||
url += this.hostname+":"+this.port+"/"+this.db;
|
||||
|
||||
console.log("MongoDB URL: " + url);
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@ -49,7 +74,7 @@ module.exports = function(RED) {
|
||||
var noerror = true;
|
||||
|
||||
var connectToDB = function() {
|
||||
MongoClient.connect(node.mongoConfig.url, function(err, db) {
|
||||
MongoClient.connect(node.mongoConfig.url, function(err, client) {
|
||||
if (err) {
|
||||
node.status({fill:"red",shape:"ring",text:RED._("mongodb.status.error")});
|
||||
if (noerror) { node.error(err); }
|
||||
@ -58,9 +83,12 @@ module.exports = function(RED) {
|
||||
}
|
||||
else {
|
||||
node.status({fill:"green",shape:"dot",text:RED._("mongodb.status.connected")});
|
||||
node.clientDb = db;
|
||||
node.clientDb = client.db();
|
||||
var db = client.db();
|
||||
//console.log( db);
|
||||
noerror = true;
|
||||
var coll;
|
||||
|
||||
if (node.collection) {
|
||||
coll = db.collection(node.collection);
|
||||
}
|
||||
@ -174,7 +202,8 @@ module.exports = function(RED) {
|
||||
var noerror = true;
|
||||
|
||||
var connectToDB = function() {
|
||||
MongoClient.connect(node.mongoConfig.url, function(err,db) {
|
||||
console.log("connecting: " + node.mongoConfig.url);
|
||||
MongoClient.connect(node.mongoConfig.url, function(err,client) {
|
||||
if (err) {
|
||||
node.status({fill:"red",shape:"ring",text:RED._("mongodb.status.error")});
|
||||
if (noerror) { node.error(err); }
|
||||
@ -183,7 +212,8 @@ module.exports = function(RED) {
|
||||
}
|
||||
else {
|
||||
node.status({fill:"green",shape:"dot",text:RED._("mongodb.status.connected")});
|
||||
node.clientDb = db;
|
||||
node.clientDb = client.db();
|
||||
var db = client.db();
|
||||
noerror = true;
|
||||
var coll;
|
||||
node.on("input", function(msg) {
|
||||
@ -216,7 +246,7 @@ module.exports = function(RED) {
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
coll.find(selector,msg.projection).sort(msg.sort).limit(limit).skip(skip).toArray(function(err, items) {
|
||||
coll.find(selector).project(msg.projection).sort(msg.sort).limit(limit).skip(skip).toArray(function(err, items) {
|
||||
if (err) {
|
||||
node.error(err);
|
||||
}
|
||||
@ -249,8 +279,16 @@ module.exports = function(RED) {
|
||||
node.error(err);
|
||||
}
|
||||
else {
|
||||
msg.payload = result;
|
||||
node.send(msg);
|
||||
cursor.toArray(function(cursorError, cursorDocs) {
|
||||
console.log(cursorDocs);
|
||||
if (cursorError) {
|
||||
node.error(cursorError);
|
||||
}
|
||||
else {
|
||||
msg.payload = cursorDocs;
|
||||
node.send(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -16,13 +16,23 @@ Install
|
||||
-------
|
||||
|
||||
Run the following command in your Node-RED user directory - typically `~/.node-red`
|
||||
|
||||
```
|
||||
npm install node-red-node-mongodb
|
||||
```
|
||||
Note that this package requires a MongoDB client package at least version 3.6.1 - if you have an older (version 2) client,
|
||||
you may need to remove that before installing this
|
||||
```
|
||||
npm remove mongodb
|
||||
npm install node-red-node-mongodb
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Nodes to save and retrieve data in a local MongoDB instance.
|
||||
Nodes to save and retrieve data in a MongoDB instance - the database server can be local (mongodb//:localhost:27017), remote (mongodb://hostname.network:27017),
|
||||
replica-set or cluster (mongodb://hostnameA.network:27017,hostnameB.network:27017), and DNS seedlist cluster (mongodb+srv://clustername.network).
|
||||
|
||||
Reference [MongoDB docs](https://docs.mongodb.com/manual/reference/connection-string/) to see which connection method (host or clustered) to use for your MongoDB instance.
|
||||
|
||||
### Input
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
"mongodb": {
|
||||
"label": {
|
||||
"host": "Host",
|
||||
"topology":"Connection topology",
|
||||
"connectOptions":"Connect options",
|
||||
"port": "Port",
|
||||
"database": "Database",
|
||||
"username": "Username",
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name" : "node-red-node-mongodb",
|
||||
"version" : "0.0.14",
|
||||
"version" : "0.2.2",
|
||||
"description" : "Node-RED nodes to talk to an Mongo database",
|
||||
"dependencies" : {
|
||||
"mongodb" : "^2.2.34"
|
||||
"mongodb" : "^3.6.2"
|
||||
},
|
||||
"repository" : {
|
||||
"type":"git",
|
||||
@ -20,5 +20,11 @@
|
||||
"name": "Dave Conway-Jones",
|
||||
"email": "ceejay@vnet.ibm.com",
|
||||
"url": "http://nodered.org"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Ross Cruickshank",
|
||||
"email": "ross@vnet.ibm.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "node-red-node-sqlite",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"description": "A sqlite node for Node-RED",
|
||||
"dependencies": {
|
||||
"sqlite3": "~4.1.1"
|
||||
"sqlite3": "~4.2.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -43,6 +43,7 @@ describe('email Node', function () {
|
||||
id: "n1",
|
||||
type: "e-mail",
|
||||
name: "emailout",
|
||||
port: 1025,
|
||||
wires: [
|
||||
[]
|
||||
]
|
||||
@ -188,4 +189,64 @@ describe('email Node', function () {
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('email mta', function () {
|
||||
|
||||
it('should catch an email send to localhost 1025', function (done) {
|
||||
var flow = [{
|
||||
id: "n1",
|
||||
type: "e-mail mta",
|
||||
name: "emailmta",
|
||||
port: 1025,
|
||||
wires: [
|
||||
["n2"]
|
||||
]
|
||||
},
|
||||
{
|
||||
id:"n2",
|
||||
type:"helper"
|
||||
},
|
||||
{
|
||||
id: "n3",
|
||||
type: "e-mail",
|
||||
dname: "testout",
|
||||
server: "localhost",
|
||||
secure: false,
|
||||
port: 1025,
|
||||
wires: [
|
||||
[]
|
||||
]
|
||||
}];
|
||||
helper.load(emailNode, flow, function () {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n3 = helper.getNode("n3");
|
||||
n1.should.have.property('port', 1025);
|
||||
|
||||
n2.on("input", function(msg) {
|
||||
//console.log("GOT",msg);
|
||||
try {
|
||||
msg.should.have.a.property("payload",'Hello World\n');
|
||||
msg.should.have.a.property("topic","Test");
|
||||
msg.should.have.a.property("from",'foo@example.com');
|
||||
msg.should.have.a.property("to",'bar@example.com');
|
||||
msg.should.have.a.property("attachments");
|
||||
msg.should.have.a.property("header");
|
||||
done();
|
||||
}
|
||||
catch(e) {
|
||||
done(e)
|
||||
}
|
||||
});
|
||||
|
||||
n3.emit("input", {
|
||||
payload: "Hello World",
|
||||
topic: "Test",
|
||||
from: "foo@example.com",
|
||||
to: "bar@example.com"
|
||||
});
|
||||
//done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user