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:
		| @@ -4,7 +4,6 @@ matrix: | ||||
|   include: | ||||
|     - node_js: 8 | ||||
|     - node_js: 10 | ||||
|     - node_js: 6 | ||||
|     - python: 2.7 | ||||
|       language: python | ||||
|       before_script: pip install flake8 | ||||
|   | ||||
| @@ -9,15 +9,17 @@ Install | ||||
| Run the following command in your Node-RED user directory - typically `~/.node-red` | ||||
|  | ||||
|     npm install node-red-node-random | ||||
|  | ||||
|      | ||||
|  | ||||
| Usage | ||||
| ----- | ||||
|  | ||||
| A simple node to generate a random number when triggered. | ||||
|  | ||||
| If integer mode is selected (default) it will return an integer **between and including** the two values given - so selecting 1 to 6 will return values 1,2,3,4,5 or 6. | ||||
| If you return an integer it can include both the low and high values. | ||||
| `min <= n <= max` - so selecting 1 to 6 will return values 1,2,3,4,5 or 6. | ||||
|  | ||||
| If floating point mode is selected then it will return a number **between** the two values given - so selecting 1 to 6 will return values 1 < x < 6 . | ||||
| If you return a floating point value it will be from the low value, up to, but | ||||
| not including the high value. `min <= n < max` - so selecting 1 to 6 will return values 1 <= n < 6 . | ||||
|  | ||||
| **Note:** This generates **numbers**. | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-random", | ||||
|     "version"       : "0.1.0", | ||||
|     "version"       : "0.1.2", | ||||
|     "description"   : "A Node-RED node that when triggered generates a random number between two values.", | ||||
|     "dependencies"  : { | ||||
|     }, | ||||
|   | ||||
| @@ -28,8 +28,10 @@ | ||||
| 70% | ||||
| <script type="text/x-red" data-help-name="random"> | ||||
|     <p>Generates a random number between a low and high value.</p> | ||||
|     <p>If you return an integer it can <i>include</i> both the low and high values.</p> | ||||
|     <p>If you return a floating point value it will be <i>between</i> the low and high values.</p> | ||||
|     <p>If you return an integer it can <i>include</i> both the low and high values. | ||||
|     <code>min <= n <= max</code></p> | ||||
|     <p>If you return a floating point value it will be from the low value, up to, but | ||||
|     not including the high value. <code>min <= n < max</code></p> | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|   | ||||
| @@ -11,10 +11,10 @@ module.exports = function(RED) { | ||||
|         this.on("input", function(msg) { | ||||
|             var value; | ||||
|             if (node.inte == "true" || node.inte === true) { | ||||
|                 value = Math.round(Number(Math.random()) * (node.high - node.low + 1) + node.low - 0.5); | ||||
|                 value = Math.round(Math.random() * (node.high - node.low + 1) + node.low - 0.5); | ||||
|             } | ||||
|             else { | ||||
|                 value = Number(Math.random()) * (node.high - node.low) + node.low; | ||||
|                 value = Math.random() * (node.high - node.low) + node.low; | ||||
|             } | ||||
|             RED.util.setMessageProperty(msg,node.property,value); | ||||
|             node.send(msg); | ||||
|   | ||||
| @@ -37,6 +37,8 @@ select the channel dynamically. If so then the payload must be a value from 0 to | ||||
|  | ||||
| You can also select device id 0 or 1 (CE0 or CE1) depending on how you have wired up your device. Defaults to CE0. | ||||
|  | ||||
| And you can also select the SPI bus number 0 or 1 depending on how you have wired up your device. Defaults to 0 for spidev0. | ||||
|  | ||||
| Outputs a numeric `msg.payload` with a range of 0 to 1023, where 0 = 0V and 1023 = 3.3V (assuming you use the default 3.3V voltage reference). | ||||
|  | ||||
| **Hint**: use a `range` node to adjust the values to the range you want. | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-pi-mcp3008", | ||||
|     "version"       : "0.1.1", | ||||
|     "version"       : "0.2.0", | ||||
|     "description"   : "A Node-RED node to read from the MCP3008 Analogue to Digital Converter", | ||||
|     "dependencies"  : { | ||||
|         "mcp-spi-adc": "^1.0.0" | ||||
|         "mcp-spi-adc": "^2.0.3" | ||||
|     }, | ||||
|     "repository" : { | ||||
|         "type":"git", | ||||
|   | ||||
| @@ -33,6 +33,13 @@ | ||||
|           <option value=1>CE1</option> | ||||
|         </select> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-bus"><i class="fa fa-toggle-on"></i> SPI bus</label> | ||||
|         <select type="text" id="node-input-bus" style="width:150px;"> | ||||
|           <option value=0>0</option> | ||||
|           <option value=1>1</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"/> | ||||
| @@ -61,7 +68,8 @@ | ||||
|             name: {value:""}, | ||||
|             dev: {value:"3008"}, | ||||
|             pin: {value:0, required:true}, | ||||
|             dnum: {value:0} | ||||
|             dnum: {value:0}, | ||||
|             bus: {value:0} | ||||
|         }, | ||||
|         inputs: 1, | ||||
|         outputs: 1, | ||||
|   | ||||
| @@ -19,14 +19,15 @@ module.exports = function(RED) { | ||||
|         this.pin = n.pin || 0; | ||||
|         this.interval = n.interval || 1000; | ||||
|         this.dnum = parseInt(n.dnum || 0); | ||||
|         this.bus = parseInt(n.bus || 0); | ||||
|         this.dev = n.dev || "3008"; | ||||
|         var node = this; | ||||
|         var cb = function (err) { if (err) { node.error("Error: "+err); } }; | ||||
|         var opt = { speedHz:20000, deviceNumber:node.dnum }; | ||||
|         var opt = { speedHz:20000, deviceNumber:node.dnum, busNumber:node.bus }; | ||||
|         var chans = parseInt(this.dev.substr(3)); | ||||
|  | ||||
|         try { | ||||
|             fs.statSync("/dev/spidev0."+node.dnum); | ||||
|             fs.statSync("/dev/spidev"+node.bus+"."+node.dnum); | ||||
|             if (mcp3xxx.length === 0) { | ||||
|                 for (var i=0; i<chans; i++) { | ||||
|                     if (node.dev === "3002") { mcp3xxx.push(mcpadc.openMcp3002(i, opt, cb)); } | ||||
|   | ||||
							
								
								
									
										0
									
								
								hardware/neopixel/neopixel.html
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								hardware/neopixel/neopixel.html
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										2
									
								
								hardware/neopixel/neopixel.js
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								hardware/neopixel/neopixel.js
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -14,7 +14,7 @@ module.exports = function(RED) { | ||||
|             RED.log.warn("rpi-neopixels : "+RED._("node-red:rpi-gpio.errors.ignorenode")); | ||||
|             allOK = false; | ||||
|         } | ||||
|         else if (execSync('python -c "import neopixel"').toString() !== "") { | ||||
|         else if (execSync('python -c "import rpi_ws281x"').toString() !== "") { | ||||
|             RED.log.warn("rpi-neopixels : Can't find neopixel python library"); | ||||
|             allOK = false; | ||||
|         } | ||||
|   | ||||
							
								
								
									
										2
									
								
								hardware/neopixel/package.json
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								hardware/neopixel/package.json
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-pi-neopixel", | ||||
|     "version"       : "0.0.21", | ||||
|     "version"       : "0.0.22", | ||||
|     "description"   : "A Node-RED node to output to a neopixel (ws2812) string of LEDS from a Raspberry Pi.", | ||||
|     "dependencies"  : { | ||||
|     }, | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var wol = require('wake_on_lan'); | ||||
|     var chk = /^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/; | ||||
|     var chk = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; | ||||
|  | ||||
|     function WOLnode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
| @@ -17,9 +17,13 @@ module.exports = function(RED) { | ||||
|                 if (chk.test(mac)) { | ||||
|                     try { | ||||
|                         wol.wake(mac, {address: host}, function(error) { | ||||
|                             if (error) { node.warn(error); } | ||||
|                             if (error) { | ||||
|                                 node.warn(error); | ||||
|                                 node.status({fill:"red",shape:"ring",text:" "}); | ||||
|                             } | ||||
|                             else if (RED.settings.verbose) { | ||||
|                                 node.log("sent WOL magic packet"); | ||||
|                                 node.status({fill:"green",shape:"dot",text:" "}); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
| @@ -31,6 +35,10 @@ module.exports = function(RED) { | ||||
|             } | ||||
|             else { node.warn("WOL: no mac address specified"); } | ||||
|         }); | ||||
|          | ||||
|         this.on("close", function () { | ||||
|             node.status({}); | ||||
|         }) | ||||
|     } | ||||
|     RED.nodes.registerType("wake on lan",WOLnode); | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-wol", | ||||
|     "version"       : "0.0.8", | ||||
|     "version"       : "0.0.9", | ||||
|     "description"   : "A Node-RED node to send Wake-On-LAN (WOL) magic packets", | ||||
|     "dependencies"  : { | ||||
|         "wake_on_lan"   : "0.0.4" | ||||
|         "wake_on_lan"   : "1.0.0" | ||||
|     }, | ||||
|     "repository" : { | ||||
|         "type":"git", | ||||
|   | ||||
							
								
								
									
										2857
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2857
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										24
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								package.json
									
									
									
									
									
								
							| @@ -32,30 +32,30 @@ | ||||
|     ], | ||||
|     "devDependencies": { | ||||
|         "exif": "^0.6.0", | ||||
|         "grunt": "~1.0.3", | ||||
|         "grunt-cli": "^1.2.0", | ||||
|         "grunt-contrib-jshint": "^1.1.0", | ||||
|         "grunt": "^1.0.3", | ||||
|         "grunt-cli": "^1.3.1", | ||||
|         "grunt-contrib-jshint": "^2.0.0", | ||||
|         "grunt-jscs": "^3.0.1", | ||||
|         "grunt-lint-inline": "^1.0.0", | ||||
|         "grunt-simple-mocha": "^0.4.1", | ||||
|         "imap": "^0.8.19", | ||||
|         "mailparser": "^0.6.2", | ||||
|         "mocha": "~5.1.1", | ||||
|         "mailparser-mit": "^0.6.2", | ||||
|         "mocha": "^5.2.0", | ||||
|         "msgpack-lite": "^0.1.26", | ||||
|         "multilang-sentiment": "^1.1.6", | ||||
|         "ngeohash": "^0.6.0", | ||||
|         "node-red": "*", | ||||
|         "node-red-node-test-helper": "*", | ||||
|         "nodemailer": "~4.6.7", | ||||
|         "nodemailer": "^4.6.8", | ||||
|         "poplib": "^0.1.7", | ||||
|         "proxyquire": "^2.0.1", | ||||
|         "pushbullet": "~2.2.0", | ||||
|         "should": "~13.2.3", | ||||
|         "sinon": "~5.0.10", | ||||
|         "supertest": "~3.1.0", | ||||
|         "proxyquire": "^2.1.0", | ||||
|         "pushbullet": "^2.3.0", | ||||
|         "should": "^13.2.3", | ||||
|         "sinon": "^6.3.4", | ||||
|         "supertest": "^3.3.0", | ||||
|         "when": "^3.7.8" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=6.0.0" | ||||
|         "node": ">=8.0.0" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -49,6 +49,10 @@ | ||||
|         <input type="password" id="node-input-password"> | ||||
|     </div> | ||||
|     <br/> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-useTLS"><i class="fa fa-lock"></i> <span data-i18n="email.label.useTLS"></label> | ||||
|         <input type="checkbox" id="node-input-tls" style="display:inline-block; width:20px; vertical-align:baseline;"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-dname"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-dname" data-i18n="[placeholder]node-red:common.label.name"> | ||||
| @@ -69,6 +73,7 @@ | ||||
|     <p>Alternatively you may provide <code>msg.attachments</code> 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> | ||||
|     <p>If required by your recipient you may also pass in a <code>msg.envelope</code> object, typically containing extra from and to properties.</p> | ||||
|     <p>If you have own signed certificates, Nodemailer can complain about that and refuse sending the message. In this case you can try switching off TLS.</p> | ||||
|     <p>Note: uses SMTP with SSL to port 465.</p> | ||||
| </script> | ||||
|  | ||||
| @@ -81,6 +86,7 @@ | ||||
|             server: {value:"smtp.gmail.com",required:true}, | ||||
|             port: {value:"465",required:true}, | ||||
|             secure: {value: true}, | ||||
|             tls: {value: true}, | ||||
|             name: {value:""}, | ||||
|             dname: {value:""} | ||||
|         }, | ||||
|   | ||||
| @@ -14,9 +14,13 @@ module.exports = function(RED) { | ||||
|     var nodemailer = require("nodemailer"); | ||||
|     var Imap = require('imap'); | ||||
|     var POP3Client = require("poplib"); | ||||
|     var MailParser = require("mailparser").MailParser; | ||||
|     var MailParser = require("mailparser-mit").MailParser; | ||||
|     var util = require("util"); | ||||
|  | ||||
|     if (parseInt(process.version.split("v")[1].split(".")[0]) < 8) { | ||||
|         throw "Error : Requires nodejs version >= 8."; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|         var globalkeys = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js"); | ||||
|     } | ||||
| @@ -30,6 +34,7 @@ module.exports = function(RED) { | ||||
|         this.outserver = n.server; | ||||
|         this.outport = n.port; | ||||
|         this.secure = n.secure; | ||||
|         this.tls = true; | ||||
|         var flag = false; | ||||
|         if (this.credentials && this.credentials.hasOwnProperty("userid")) { | ||||
|             this.userid = this.credentials.userid; | ||||
| @@ -50,12 +55,16 @@ module.exports = function(RED) { | ||||
|         if (flag) { | ||||
|             RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true}); | ||||
|         } | ||||
|         if (n.tls === false){ | ||||
|             this.tls = false; | ||||
|         } | ||||
|         var node = this; | ||||
|  | ||||
|         var smtpOptions = { | ||||
|             host: node.outserver, | ||||
|             port: node.outport, | ||||
|             secure: node.secure | ||||
|             secure: node.secure, | ||||
|             tls: {rejectUnauthorized: node.tls} | ||||
|         } | ||||
|  | ||||
|         if (this.userid && this.password) { | ||||
| @@ -185,7 +194,7 @@ module.exports = function(RED) { | ||||
|         // will be used to populate the email. | ||||
|         // DCJ NOTE: - heirachical multipart mime parsers seem to not exist - this one is barely functional. | ||||
|         function processNewMessage(msg, mailMessage) { | ||||
|             msg = JSON.parse(JSON.stringify(msg)); // Clone the message | ||||
|             msg = RED.util.cloneMessage(msg); // Clone the message | ||||
|             // Populate the msg fields from the content of the email message | ||||
|             // that we have just parsed. | ||||
|             msg.payload = mailMessage.text; | ||||
| @@ -193,9 +202,9 @@ module.exports = function(RED) { | ||||
|             msg.date = mailMessage.date; | ||||
|             msg.header = mailMessage.headers; | ||||
|             if (mailMessage.html) { msg.html = mailMessage.html; } | ||||
|             if (mailMessage.to && mailMessage.from.to > 0) { msg.to = mailMessage.to; } | ||||
|             if (mailMessage.cc && mailMessage.from.cc > 0) { msg.cc = mailMessage.cc; } | ||||
|             if (mailMessage.bcc && mailMessage.from.bcc > 0) { msg.bcc = mailMessage.bcc; } | ||||
|             if (mailMessage.to && mailMessage.to.length > 0) { msg.to = mailMessage.to; } | ||||
|             if (mailMessage.cc && mailMessage.cc.length > 0) { msg.cc = mailMessage.cc; } | ||||
|             if (mailMessage.bcc && mailMessage.bcc.length > 0) { msg.bcc = mailMessage.bcc; } | ||||
|             if (mailMessage.from && mailMessage.from.length > 0) { msg.from = mailMessage.from[0].address; } | ||||
|             if (mailMessage.attachments) { msg.attachments = mailMessage.attachments; } | ||||
|             else { msg.attachments = []; } | ||||
| @@ -221,6 +230,7 @@ module.exports = function(RED) { | ||||
|             function nextMessage() { | ||||
|                 if (currentMessage > maxMessage) { | ||||
|                     pop3Client.quit(); | ||||
|                     setInputRepeatTimeout(); | ||||
|                     return; | ||||
|                 } | ||||
|                 pop3Client.retr(currentMessage); | ||||
| @@ -243,6 +253,7 @@ module.exports = function(RED) { | ||||
|             }); | ||||
|  | ||||
|             pop3Client.on("error", function(err) { | ||||
|                 setInputRepeatTimeout(); | ||||
|                 node.log("error: " + JSON.stringify(err)); | ||||
|             }); | ||||
|  | ||||
| @@ -258,6 +269,7 @@ module.exports = function(RED) { | ||||
|                 } else { | ||||
|                     node.log(util.format("login error: %s %j", status, rawData)); | ||||
|                     pop3Client.quit(); | ||||
|                     setInputRepeatTimeout(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
| @@ -279,6 +291,7 @@ module.exports = function(RED) { | ||||
|                 else { | ||||
|                     node.log(util.format("retr error: %s %j", status, rawData)); | ||||
|                     pop3Client.quit(); | ||||
|                     setInputRepeatTimeout(); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
| @@ -318,6 +331,7 @@ module.exports = function(RED) { | ||||
|                             node.status({fill:"red", shape:"ring", text:"email.status.foldererror"}); | ||||
|                             node.error(RED._("email.errors.fetchfail", {folder:node.box}),err); | ||||
|                             imap.end(); | ||||
|                             setInputRepeatTimeout(); | ||||
|                             return; | ||||
|                         } | ||||
|                         //console.log("> search - err=%j, results=%j", err, results); | ||||
| @@ -325,6 +339,7 @@ module.exports = function(RED) { | ||||
|                             //console.log(" [X] - Nothing to fetch"); | ||||
|                             node.status({}); | ||||
|                             imap.end(); | ||||
|                             setInputRepeatTimeout(); | ||||
|                             return; | ||||
|                         } | ||||
|  | ||||
| @@ -372,10 +387,12 @@ module.exports = function(RED) { | ||||
|                             } else { | ||||
|                                 cleanup(); | ||||
|                             } | ||||
|                             setInputRepeatTimeout(); | ||||
|                         }); | ||||
|  | ||||
|                         fetch.once('error', function(err) { | ||||
|                             console.log('Fetch error: ' + err); | ||||
|                             setInputRepeatTimeout(); | ||||
|                         }); | ||||
|                     }); // End of imap->search | ||||
|                 }); // End of imap->openInbox | ||||
| @@ -419,16 +436,19 @@ module.exports = function(RED) { | ||||
|  | ||||
|         this.on("close", function() { | ||||
|             if (this.interval_id != null) { | ||||
|                 clearInterval(this.interval_id); | ||||
|                 clearTimeout(this.interval_id); | ||||
|             } | ||||
|             if (imap) { imap.destroy(); } | ||||
|         }); | ||||
|  | ||||
|         // Set the repetition timer as needed | ||||
|         if (!isNaN(this.repeat) && this.repeat > 0) { | ||||
|             this.interval_id = setInterval( function() { | ||||
|                 node.emit("input",{}); | ||||
|             }, this.repeat ); | ||||
|         function setInputRepeatTimeout() | ||||
|         { | ||||
|             // Set the repetition timer as needed | ||||
|             if (!isNaN(node.repeat) && node.repeat > 0) { | ||||
|                 node.interval_id = setTimeout( function() { | ||||
|                     node.emit("input",{}); | ||||
|                 }, node.repeat ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         node.emit("input",{}); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| node-red-node-email | ||||
| =================== | ||||
|  | ||||
| <a href="http://nodered.org" target="_new">Node-RED</a> nodes to send and receive simple emails. | ||||
| <a href="http://nodered.org" target="info">Node-RED</a> nodes to send and receive simple emails. | ||||
|  | ||||
|  | ||||
| Pre-requisite | ||||
| @@ -9,7 +9,7 @@ Pre-requisite | ||||
|  | ||||
| You will need valid email credentials for your email server. | ||||
|  | ||||
| **Note :** Version 1.x of this node requires Node.js v6 or newer. | ||||
| **Note :** Version 1.x of this node requires **Node.js v8** or newer. | ||||
|  | ||||
|  | ||||
| Install | ||||
| @@ -18,10 +18,12 @@ Install | ||||
| Version 0.x of this node is usually installed by default by Node-RED. | ||||
| To install version 1.x you need to uninstall the existing version. | ||||
|  | ||||
|         sudo npm uninstall -g node-red-node-email | ||||
|         cd /usr/lib/node_modules/node-red | ||||
|         sudo npm uninstall --unsafe-perm node-red-node-email | ||||
|  | ||||
| Then run the following command in your Node-RED user directory - typically `~/.node-red` | ||||
|  | ||||
|         cd ~/.node-red | ||||
|         npm i node-red-node-email | ||||
|  | ||||
| **Note :** this installs the new version locally rather than globally. This can then be managed by the palette manager. | ||||
|   | ||||
| @@ -13,6 +13,7 @@ | ||||
|             "folder": "Folder", | ||||
|             "protocol": "Protocol", | ||||
|             "useSSL": "Use SSL?", | ||||
|             "useTLS": "Use TLS?", | ||||
|             "disposition": "Disposition", | ||||
|             "none": "None", | ||||
|             "read": "Mark Read", | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| { | ||||
|   "name": "node-red-node-email", | ||||
|   "version": "1.0.0", | ||||
|   "version": "1.0.4", | ||||
|   "description": "Node-RED nodes to send and receive simple emails", | ||||
|   "dependencies": { | ||||
|     "imap": "^0.8.19", | ||||
|     "mailparser": "^0.6.2", | ||||
|     "nodemailer": "^4.6.4", | ||||
|     "mailparser-mit": "^0.6.2", | ||||
|     "nodemailer": "^4.6.8", | ||||
|     "poplib": "^0.1.7" | ||||
|   }, | ||||
|   "repository": { | ||||
| @@ -30,6 +30,6 @@ | ||||
|     "url": "http://nodered.org" | ||||
|   }, | ||||
|   "engines": { | ||||
|     "node": ">=6.0.0" | ||||
|     "node": ">=8.0.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,7 @@ module.exports = function(RED) { | ||||
|                 var req = request(node.url, {timeout:10000, pool:false}); | ||||
|                 //req.setMaxListeners(50); | ||||
|                 req.setHeader('user-agent', 'Mozilla/5.0 (Node-RED)'); | ||||
|                 req.setHeader('accept', 'text/html,application/xhtml+xml'); | ||||
|                 req.setHeader('accept', 'application/rss+xml,text/html,application/xhtml+xml'); | ||||
|  | ||||
|                 var feedparser = new FeedParser(); | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| { | ||||
|   "name": "node-red-node-feedparser", | ||||
|   "version": "0.1.13", | ||||
|   "version": "0.1.14", | ||||
|   "description": "A Node-RED node to get RSS Atom feeds.", | ||||
|   "dependencies": { | ||||
|     "feedparser": "^2.2.9", | ||||
|     "request": "^2.83.0" | ||||
|     "request": "^2.88.0" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="notify"> | ||||
| <script type="text/x-red" data-template-name="nnotify"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-title"><i class="fa fa-flag"></i> Title</label> | ||||
|         <input type="text" id="node-input-title" placeholder="Node-RED"> | ||||
| @@ -10,15 +10,15 @@ | ||||
|     </div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-help-name="notify"> | ||||
|     <p>Uses Growl to provide a desktop popup containing the <code>msg.payload</code>. Only useful on the local machine.</p> | ||||
|     <p>Optionally uses <code>msg.topic</code> as the title.</p> | ||||
|     <p>Uses Growl so should work cross platform but will need pre-reqs installed... see <i><a href="https://npmjs.org/package/growl" target="_new">this link.</a></i></p> | ||||
|     <p>If installing on Windows you MUST read the install instructions ... especially the bit about adding growlnotify to your path... or it WILL NOT work.</p> | ||||
| <script type="text/x-red" data-help-name="nnotify"> | ||||
|     <p>Uses node-notifier to provide a desktop popup containing the <code>msg.payload</code>. Only useful on the local machine.</p> | ||||
|     <p>Optionally uses <code>msg.topic</code> as the title, and <code>msg.icon</code> as the full path to an icon file to display.</p> | ||||
|     <p>Uses node-notifier so should work cross platform but may need to intall pre-reqs... see <i><a href="https://www.npmjs.com/package/node-notifier" target="_new">this link.</a></i></p> | ||||
|     <p>If installing on Windows you MUST read the install instructions...  or it WILL NOT work.</p> | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|     RED.nodes.registerType('notify',{ | ||||
|     RED.nodes.registerType('nnotify',{ | ||||
|         category: 'output', | ||||
|         defaults: { | ||||
|             title: {value:""}, | ||||
| @@ -29,6 +29,7 @@ | ||||
|         outputs:0, | ||||
|         icon: "alert.png", | ||||
|         align: "right", | ||||
|         paletteLabel: "notify", | ||||
|         label: function() { | ||||
|             return this.name||this.title||"notify"; | ||||
|         }, | ||||
|   | ||||
| @@ -1,26 +1,35 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var growl = require('growl'); | ||||
|     var imagefile = process.env.NODE_RED_HOME+"/public/node-red.png"; | ||||
|     var notifier = require('node-notifier'); | ||||
|     var path = require('path'); | ||||
|     var fs = require('fs'); | ||||
|     var image = path.join(__dirname, "/node-red.png"); | ||||
|  | ||||
|     function NotifyNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         this.title = n.title; | ||||
|         var node = this; | ||||
|          | ||||
|         node.on("input",function(msg) { | ||||
|             var titl = node.title || msg.topic; | ||||
|             if (typeof(msg.payload) == 'object') { | ||||
|             var title = node.title || msg.topic; | ||||
|             if (typeof msg.payload === 'object') { | ||||
|                 msg.payload = JSON.stringify(msg.payload); | ||||
|             } | ||||
|             if (typeof(titl) != 'undefined') { | ||||
|                 growl(msg.payload, { title: titl, image: imagefile }); | ||||
|             var icon = image; | ||||
|             if (msg.icon) { | ||||
|                 if (fs.existsSync(msg.icon)) { icon = msg.icon; } | ||||
|                 else { node.error("Bad Icon file: "+msg.icon,msg); } | ||||
|             } | ||||
|             var icon = msg.icon || image; | ||||
|             if (typeof(title) !== 'undefined') { | ||||
|                 notifier.notify({ message:msg.payload, title:title, icon:icon }); | ||||
|             } | ||||
|             else { | ||||
|                 growl(msg.payload, { image: imagefile }); | ||||
|                 notifier.notify({ message:msg.payload, icon:imagefile }); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     RED.nodes.registerType("notify",NotifyNode); | ||||
|     RED.nodes.registerType("nnotify",NotifyNode); | ||||
| } | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								social/notify/node-red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								social/notify/node-red.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1019 B | 
| @@ -1,17 +1,17 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-notify", | ||||
|     "version"       : "0.0.5", | ||||
|     "version"       : "0.1.1", | ||||
|     "description"   : "A Node-RED node to send local popup Notify alerts", | ||||
|     "dependencies"  : { | ||||
|         "growl"   : "1.8.1" | ||||
|         "node-notifier" : "5.2.1" | ||||
|     }, | ||||
|     "repository" : { | ||||
|         "type":"git", | ||||
|         "url":"https://github.com/node-red/node-red-nodes.git", | ||||
|         "path":"/tree/master/social/growl" | ||||
|         "path":"/tree/master/social/notify" | ||||
|     }, | ||||
|     "license": "Apache-2.0", | ||||
|     "keywords": [ "node-red", "growl", "notify"], | ||||
|     "keywords": [ "node-red", "notify", "growl"], | ||||
|     "node-red"      : { | ||||
|         "nodes"     : { | ||||
|             "notify": "57-notify.js" | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-pushbullet", | ||||
|     "version"       : "0.0.13", | ||||
|     "version"       : "0.0.14", | ||||
|     "description"   : "A Node-RED node to send alerts via Pushbullet", | ||||
|     "dependencies"  : { | ||||
|         "pushbullet": "~2.2.0", | ||||
|         "pushbullet": "^2.3.0", | ||||
|         "when": "^3.7.8" | ||||
|     }, | ||||
|     "repository" : { | ||||
|   | ||||
| @@ -73,10 +73,15 @@ | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-help-name="pushover"> | ||||
|     <p>Uses Pushover to push the <code>msg.payload</code> to a device that has the Pushover app installed.</p> | ||||
|     <p>Optionally uses <code>msg.topic</code> to set the title, <code>msg.device</code> to set the device, | ||||
|     <code>msg.priority</code> to set the priority, <code>msg.url</code> to add a web address and <code>msg.url_title</code> | ||||
|     to add a url title if not already set in the properties.</p> | ||||
|     <p>Uses Pushover to push the msg.payload to a device that has the Pushover app installed.</p> | ||||
| 	<br> | ||||
|     <p>Optionally uses msg.topic to set the configuration:</p> | ||||
|     <p><code>msg.topic</code>: set the title</p> | ||||
|     <p><code>msg.device</code>: set the device</p> | ||||
|     <p><code>msg.priority</code>: set the priority</p> | ||||
|     <p><code>msg.url</code>: to add a web address</p> | ||||
| 	<p><code>msg.url_title</code>: to add a url title if not already set in the properties</p> | ||||
| 	<p><code>msg.sound</code>: set the notification sound, <i><a href="https://pushover.net/api#sounds" target="_new">see the available options</a></i></p> | ||||
|     <p>Uses Pushover. See <i><a href="https://pushover.net" target="_new">this link</a></i> for more details.</p> | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -16,12 +16,14 @@ Usage | ||||
|  | ||||
| Uses Pushover to push the `msg.payload` to a device that has the Pushover app installed. | ||||
|  | ||||
| Optionally uses `msg.topic` to set the title, `msg.device` to set the device | ||||
| and `msg.priority` to set the priority, if not already set in the properties. | ||||
|  | ||||
| Optionally uses `msg.topic` to set the title, `msg.device` to set the device, | ||||
| `msg.priority` to set the priority, `msg.url` to add a web address and `msg.url_title` | ||||
| to add a url title - if not already set in the properties. | ||||
| Optionally uses `msg.topic` to set the configuration, if not already set in the properties: | ||||
|  - `msg.device`: to set the device | ||||
|  - `msg.priority`: to set the priority | ||||
|  - `msg.topic`: to set the title | ||||
|  - `msg.url`: to add a web address | ||||
|  - `msg.url_title`: to add a url title | ||||
|  - `msg.sound`: to set the alert sound, see the [available options](https://pushover.net/api#sounds) | ||||
|  | ||||
| The User-key and API-token are stored in a separate credentials file. | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-pushover", | ||||
|     "version"       : "0.0.12", | ||||
|     "version"       : "0.0.13", | ||||
|     "description"   : "A Node-RED node to send alerts via Pushover", | ||||
|     "dependencies"  : { | ||||
|         "pushover-notifications"   : "~0.2.4" | ||||
|   | ||||
| @@ -25,6 +25,8 @@ By it's very nature it is SQL injection... so *be careful* out there... | ||||
|  | ||||
| Typically the returned payload will be an array of the result rows, (or an error). | ||||
|  | ||||
| You can load sqlite extensions by inputting a <code>msg.extension</code> property containing the full path and filename. | ||||
|  | ||||
| The reconnect timeout in milliseconds can be changed by adding a line to **settings.js** | ||||
|  | ||||
|     sqliteReconnectTime: 20000, | ||||
|   | ||||
							
								
								
									
										30
									
								
								storage/sqlite/ext/half.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								storage/sqlite/ext/half.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| /* Add your header comment here */ | ||||
|  | ||||
| #include <sqlite3ext.h> | ||||
| SQLITE_EXTENSION_INIT1 | ||||
|  | ||||
| /* | ||||
| ** The half() SQL function returns half of its input value. | ||||
| */ | ||||
| static void halfFunc( | ||||
|   sqlite3_context *context, | ||||
|   int argc, | ||||
|   sqlite3_value **argv | ||||
| ){ | ||||
|   sqlite3_result_double(context, 0.5*sqlite3_value_double(argv[0])); | ||||
| } | ||||
|  | ||||
| /* SQLite invokes this routine once when it loads the extension. | ||||
| ** Create new functions, collating sequences, and virtual table | ||||
| ** modules here.  This is usually the only exported symbol in | ||||
| ** the shared library. | ||||
| */ | ||||
| int sqlite3_extension_init( | ||||
|   sqlite3 *db, | ||||
|   char **pzErrMsg, | ||||
|   const sqlite3_api_routines *pApi | ||||
| ){ | ||||
|   SQLITE_EXTENSION_INIT2(pApi) | ||||
|   sqlite3_create_function(db, "half", 1, SQLITE_ANY, 0, halfFunc, 0, 0); | ||||
|   return 0; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								storage/sqlite/ext/half.dylib
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								storage/sqlite/ext/half.dylib
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "node-red-node-sqlite", | ||||
|     "version": "0.3.2", | ||||
|     "version": "0.3.5", | ||||
|     "description": "A sqlite node for Node-RED", | ||||
|     "dependencies": { | ||||
|         "sqlite3": "^4.0.2" | ||||
|   | ||||
| @@ -77,6 +77,8 @@ | ||||
|     be sure to include $ on the parameter object key.</p> | ||||
|     <p>Using any SQL Query, the result is returned in <code>msg.payload</code></p> | ||||
|     <p>Typically the returned payload will be an array of the result rows, (or an error).</p> | ||||
|     <p>You can load sqlite extensions by inputting a <code>msg.extension</code> property containing the full | ||||
|     path and filename.</p> | ||||
|     <p>The reconnect timeout in milliseconds can be changed by adding a line to <b>settings.js</b> | ||||
|     <pre>sqliteReconnectTime: 20000,</pre></p> | ||||
| </script> | ||||
|   | ||||
| @@ -43,89 +43,107 @@ module.exports = function(RED) { | ||||
|         var node = this; | ||||
|         node.status({}); | ||||
|  | ||||
|         if (this.mydbConfig) { | ||||
|             this.mydbConfig.doConnect(); | ||||
|         if (node.mydbConfig) { | ||||
|             node.mydbConfig.doConnect(); | ||||
|             node.status({fill:"green",shape:"dot",text:this.mydbConfig.mod}); | ||||
|             var bind = []; | ||||
|             node.on("input", function(msg) { | ||||
|                 if (this.sqlquery == "msg.topic"){ | ||||
|  | ||||
|             var doQuery = function(msg) { | ||||
|                 if (node.sqlquery == "msg.topic"){ | ||||
|                     if (typeof msg.topic === 'string') { | ||||
|                         bind = Array.isArray(msg.payload) ? msg.payload : []; | ||||
|                         node.mydbConfig.db.all(msg.topic, bind, function(err, row) { | ||||
|                             if (err) { node.error(err,msg); } | ||||
|                             else { | ||||
|                                 msg.payload = row; | ||||
|                                 node.send(msg); | ||||
|                             } | ||||
|                         }); | ||||
|                         if (msg.topic.length > 0) { | ||||
|                             bind = Array.isArray(msg.payload) ? msg.payload : []; | ||||
|                             node.mydbConfig.db.all(msg.topic, bind, function(err, row) { | ||||
|                                 if (err) { node.error(err,msg); } | ||||
|                                 else { | ||||
|                                     msg.payload = row; | ||||
|                                     node.send(msg); | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     else { | ||||
|                         node.error("msg.topic : the query is not defined as a string",msg); | ||||
|                         node.status({fill:"red",shape:"dot",text:"msg.topic error"}); | ||||
|                     } | ||||
|                 } | ||||
|                 if (this.sqlquery == "batch") { | ||||
|                 if (node.sqlquery == "batch") { | ||||
|                     if (typeof msg.topic === 'string') { | ||||
|                         node.mydbConfig.db.exec(msg.topic, function(err) { | ||||
|                             if (err) { node.error(err,msg);} | ||||
|                             else { | ||||
|                                 msg.payload = []; | ||||
|                                 node.send(msg); | ||||
|                             } | ||||
|                         }); | ||||
|                         if (msg.topic.length > 0) { | ||||
|                             node.mydbConfig.db.exec(msg.topic, function(err) { | ||||
|                                 if (err) { node.error(err,msg);} | ||||
|                                 else { | ||||
|                                     msg.payload = []; | ||||
|                                     node.send(msg); | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     else { | ||||
|                         node.error("msg.topic : the query is not defined as string", msg); | ||||
|                         node.status({fill:"red", shape:"dot",text:"msg.topic error"}); | ||||
|                     } | ||||
|                 } | ||||
|                 if (this.sqlquery == "fixed"){ | ||||
|                     if (typeof this.sql === 'string'){ | ||||
|                         bind = Array.isArray(msg.payload) ? msg.payload : []; | ||||
|                         node.mydbConfig.db.all(this.sql, bind, function(err, row) { | ||||
|                             if (err) { node.error(err,msg); } | ||||
|                             else { | ||||
|                                 msg.payload = row; | ||||
|                                 node.send(msg); | ||||
|                             } | ||||
|                         }); | ||||
|                 if (node.sqlquery == "fixed"){ | ||||
|                     if (typeof node.sql === 'string') { | ||||
|                         if (node.sql.length > 0) { | ||||
|                             node.mydbConfig.db.all(node.sql, bind, function(err, row) { | ||||
|                                 if (err) { node.error(err,msg); } | ||||
|                                 else { | ||||
|                                     msg.payload = row; | ||||
|                                     node.send(msg); | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     else{ | ||||
|                         if (this.sql === null || this.sql == ""){ | ||||
|                         if (node.sql === null || node.sql == "") { | ||||
|                             node.error("SQL statement config not set up",msg); | ||||
|                             node.status({fill:"red",shape:"dot",text:"SQL config not set up"}); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (this.sqlquery == "prepared"){ | ||||
|                     if (typeof this.sql === 'string' && typeof msg.params !== "undefined" && typeof msg.params === "object"){ | ||||
|                         node.mydbConfig.db.all(this.sql, msg.params, function(err, row) { | ||||
|                             if (err) { node.error(err,msg); } | ||||
|                             else { | ||||
|                                 msg.payload = row; | ||||
|                                 node.send(msg); | ||||
|                             } | ||||
|                         }); | ||||
|                 if (node.sqlquery == "prepared"){ | ||||
|                     if (typeof node.sql === 'string' && typeof msg.params !== "undefined" && typeof msg.params === "object") { | ||||
|                         if (node.sql.length > 0) { | ||||
|                                 node.mydbConfig.db.all(node.sql, msg.params, function(err, row) { | ||||
|                                 if (err) { node.error(err,msg); } | ||||
|                                 else { | ||||
|                                     msg.payload = row; | ||||
|                                     node.send(msg); | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     else{ | ||||
|                         if (this.sql === null || this.sql == ""){ | ||||
|                     else { | ||||
|                         if (node.sql === null || node.sql == "") { | ||||
|                             node.error("Prepared statement config not set up",msg); | ||||
|                             node.status({fill:"red",shape:"dot",text:"Prepared statement not set up"}); | ||||
|                         } | ||||
|                         if (typeof msg.params == "undefined"){ | ||||
|                         if (typeof msg.params == "undefined") { | ||||
|                             node.error("msg.params not passed"); | ||||
|                             node.status({fill:"red",shape:"dot",text:"msg.params not defined"}); | ||||
|                         } | ||||
|                         else if (typeof msg.params != "object"){ | ||||
|                         else if (typeof msg.params != "object") { | ||||
|                             node.error("msg.params not an object"); | ||||
|                             node.status({fill:"red",shape:"dot",text:"msg.params not an object"}); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             node.on("input", function(msg) { | ||||
|                 if (msg.hasOwnProperty("extension")) { | ||||
|                     node.mydbConfig.db.loadExtension(msg.extension, function(err) { | ||||
|                         if (err) { node.error(err,msg); } | ||||
|                         else { doQuery(msg); } | ||||
|                     }); | ||||
|                 } | ||||
|                 else { doQuery(msg); } | ||||
|             }); | ||||
|         } | ||||
|         else { | ||||
|             this.error("Sqlite database not configured"); | ||||
|             node.error("Sqlite database not configured"); | ||||
|         } | ||||
|     } | ||||
|     RED.nodes.registerType("sqlite",SqliteNodeIn); | ||||
|   | ||||
| @@ -11,12 +11,17 @@ | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-cr" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <input type="checkbox" id="node-input-autorun" style="display:inline-block; width: auto; vertical-align:baseline;"> | ||||
|         <label for="node-input-autorun" style="width: 70%;">Auto-start daemon on deploy ?</label> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-cr" style="display:inline-block; width:auto; vertical-align:baseline;"> | ||||
|         <label for="node-input-cr" style="width: 70%;">Add [enter] to every message sent ?</label> | ||||
|     </div> | ||||
|         <div class="form-row"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-redo" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <input type="checkbox" id="node-input-redo" style="display:inline-block; width:auto; vertical-align:baseline;"> | ||||
|         <label for="node-input-redo" style="width: 70%;">Relaunch command on exit or error ?</label> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
| @@ -62,6 +67,7 @@ | ||||
|             name: {value:""}, | ||||
|             command: {value:"",required:true}, | ||||
|             args: {value:""}, | ||||
|             autorun: {value:true}, | ||||
|             cr: {value:false}, | ||||
|             redo: {value:true}, | ||||
|             op: {value:"string"}, | ||||
| @@ -77,6 +83,9 @@ | ||||
|         }, | ||||
|         labelStyle: function() { | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             if (this.autorun === undefined) { $("#node-input-autorun").prop('checked', true); } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -12,6 +12,8 @@ module.exports = function(RED) { | ||||
|         this.redo = n.redo; | ||||
|         this.running = false; | ||||
|         this.closer = n.closer || "SIGKILL"; | ||||
|         this.autorun = true; | ||||
|         if (n.autorun === false) { this.autorun = false; } | ||||
|         var node = this; | ||||
|  | ||||
|         function inputlistener(msg) { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name"          : "node-red-node-daemon", | ||||
|     "version"       : "0.0.21", | ||||
|     "version"       : "0.0.22", | ||||
|     "description"   : "A Node-RED node that runs and monitors a long running system command.", | ||||
|     "dependencies"  : { | ||||
|     }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user