mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Move out several core node to node-red-nodes ready for 0.10.8
Update package.json to match. Part of #668
This commit is contained in:
		| @@ -1,171 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013,2015 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
| <script type="text/x-red" data-template-name="arduino in"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-arduino"><i class="fa fa-tasks"></i> Arduino</label> |  | ||||||
|         <input type="text" id="node-input-arduino"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-pin"><i class="fa fa-circle"></i> Pin</label> |  | ||||||
|         <input type="text" id="node-input-pin" placeholder="2"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-state"><i class="fa fa-wrench"></i> Type</label> |  | ||||||
|         <select type="text" id="node-input-state" style="width: 150px;"> |  | ||||||
|             <option value="INPUT">Digital pin</option> |  | ||||||
|             <option value="ANALOG">Analogue pin</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips"><b>Note:</b> You cannot use the same pin for both output and input.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="arduino in"> |  | ||||||
|     <p>Arduino input node. Connects to local Arduino and monitors the selected pin for changes. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p> |  | ||||||
|     <p>The Arduino must be loaded with the Standard Firmata sketch available in the Arduino examples.</p> |  | ||||||
|     <p>You can select either Digital or Analogue input. Outputs the value read as <b>msg.payload</b> and the pin number as <b>msg.topic</b>.</p> |  | ||||||
|     <p>It only outputs on a change of value - fine for digital inputs, but you can get a lot of data from analogue pins which you must then handle.</p> |  | ||||||
|     <p>You can set the sample rate in ms from 20 to 65535.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('arduino in',{ |  | ||||||
|         category: 'Arduino', |  | ||||||
|         color:"#3fadb5", |  | ||||||
|         defaults: { |  | ||||||
|             name: {value:""}, |  | ||||||
|             pin: {value:"",required:true}, |  | ||||||
|             state: {value:"INPUT",required:true}, |  | ||||||
|             arduino: {type:"arduino-board"} |  | ||||||
|         }, |  | ||||||
|         inputs:0, |  | ||||||
|         outputs:1, |  | ||||||
|         icon: "arduino.png", |  | ||||||
|         label: function() { |  | ||||||
|             var a = ""; |  | ||||||
|             if (this.state === "ANALOG") { a = "A"; } |  | ||||||
|             return this.name||"Pin: "+a+this.pin; |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="arduino out"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-arduino"><i class="fa fa-tasks"></i> Arduino</label> |  | ||||||
|         <input type="text" id="node-input-arduino"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-pin"><i class="fa fa-circle"></i> Pin</label> |  | ||||||
|         <input type="text" id="node-input-pin" placeholder="13"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-state"><i class="fa fa-wrench"></i> Type</label> |  | ||||||
|         <select type="text" id="node-input-state" style="width: 200px;"> |  | ||||||
|             <option value="OUTPUT">Digital (0/1)</option> |  | ||||||
|             <option value="PWM">Analogue (0-255)</option> |  | ||||||
|             <option value="SERVO">Servo (0-180)</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips"><b>Note:</b> You cannot use the same pin for both output and input.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="arduino out"> |  | ||||||
|     <p>Arduino output node. Connects to local Arduino and writes to the selected digital pin. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p> |  | ||||||
|     <p>The Arduino must be loaded with the Standard Firmata sketch available in the Arduino examples.</p> |  | ||||||
|     <p>You can select Digital, Analogue (PWM) or Servo type outputs. Expects a numeric value in <b>msg.payload</b>. The pin number is set in the properties panel.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('arduino out',{ |  | ||||||
|         category: 'Arduino', |  | ||||||
|         color:"#3fadb5", |  | ||||||
|         defaults: { |  | ||||||
|             name: {value:""}, |  | ||||||
|             pin: {value:"",required:true}, |  | ||||||
|             state: {value:"",required:true}, |  | ||||||
|             arduino: {type:"arduino-board"} |  | ||||||
|         }, |  | ||||||
|         inputs:1, |  | ||||||
|         outputs:0, |  | ||||||
|         icon: "arduino.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             return this.name||"Pin: "+this.pin; |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="arduino-board"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-device"><i class="fa fa-random"></i> Port</label> |  | ||||||
|         <input type="text" id="node-config-input-device" style="width:60%;" placeholder="e.g. /dev/ttyUSB0  COM1"/> |  | ||||||
|         <a id="node-config-lookup-serial" class="btn"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips"><b>Tip:</b> Use search to list serial ports, or leave blank to connect to first device found.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('arduino-board',{ |  | ||||||
|         category: 'config', |  | ||||||
|         defaults: { |  | ||||||
|             device: {value:""} |  | ||||||
|         }, |  | ||||||
|         label: function() { |  | ||||||
|             return this.device||"arduino"; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             try { |  | ||||||
|                 $("#node-config-input-device").autocomplete( "destroy" ); |  | ||||||
|             } catch(err) { } |  | ||||||
|             $("#node-config-lookup-serial").click(function() { |  | ||||||
|                 $("#node-config-lookup-serial-icon").removeClass('fa-search'); |  | ||||||
|                 $("#node-config-lookup-serial-icon").addClass('spinner'); |  | ||||||
|                 $("#node-config-lookup-serial").addClass('disabled'); |  | ||||||
|  |  | ||||||
|                 $.getJSON('arduinoports',function(data) { |  | ||||||
|                     $("#node-config-lookup-serial-icon").addClass('fa-search'); |  | ||||||
|                     $("#node-config-lookup-serial-icon").removeClass('spinner'); |  | ||||||
|                     $("#node-config-lookup-serial").removeClass('disabled'); |  | ||||||
|                     var ports = []; |  | ||||||
|                     $.each(data, function(i, port){ |  | ||||||
|                         ports.push(port); |  | ||||||
|                     }); |  | ||||||
|                     $("#node-config-input-device").autocomplete({ |  | ||||||
|                         source:ports, |  | ||||||
|                         minLength:0, |  | ||||||
|                         close: function( event, ui ) { |  | ||||||
|                             $("#node-config-input-device").autocomplete( "destroy" ); |  | ||||||
|                         } |  | ||||||
|                     }).autocomplete("search",""); |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,167 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013,2015 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var ArduinoFirmata = require('arduino-firmata'); |  | ||||||
|  |  | ||||||
|     // The Board Definition - this opens (and closes) the connection |  | ||||||
|     function ArduinoNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.device = n.device || null; |  | ||||||
|         this.repeat = n.repeat||25; |  | ||||||
|         //node.log("opening connection "+this.device); |  | ||||||
|         var node = this; |  | ||||||
|         node.board = new ArduinoFirmata(); |  | ||||||
|         ArduinoFirmata.list(function (err, ports) { |  | ||||||
|             if (!node.device) { |  | ||||||
|                 node.log("connecting to first board found."); |  | ||||||
|                 node.board.connect(); |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 if (ports.indexOf(node.device) === -1) { |  | ||||||
|                     node.warn(node.device + " not found. Trying to find board."); |  | ||||||
|                     node.board.connect(); |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     node.log("connecting to "+node.device); |  | ||||||
|                     node.board.connect(node.device); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             node.board.on('boardReady', function() { |  | ||||||
|                 node.log("connected to "+node.board.serialport_name); |  | ||||||
|                 if (RED.settings.verbose) { node.log("version "+node.board.boardVersion); } |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         node.on('close', function(done) { |  | ||||||
|             if (node.board) { |  | ||||||
|                 try { |  | ||||||
|                     node.board.close(function() { |  | ||||||
|                         done(); |  | ||||||
|                         if (RED.settings.verbose) { node.log("port closed"); } |  | ||||||
|                     }); |  | ||||||
|                 } catch(e) { done(); } |  | ||||||
|             } else { done(); } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("arduino-board",ArduinoNode); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // The Input Node |  | ||||||
|     function DuinoNodeIn(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.buttonState = -1; |  | ||||||
|         this.pin = n.pin; |  | ||||||
|         this.state = n.state; |  | ||||||
|         this.arduino = n.arduino; |  | ||||||
|         this.serverConfig = RED.nodes.getNode(this.arduino); |  | ||||||
|         if (typeof this.serverConfig === "object") { |  | ||||||
|             this.board = this.serverConfig.board; |  | ||||||
|             var node = this; |  | ||||||
|             node.status({fill:"red",shape:"ring",text:"connecting"}); |  | ||||||
|             node.board.on('connect', function() { |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"connected"}); |  | ||||||
|                 //console.log("i",node.state,node.pin); |  | ||||||
|                 if (node.state == "ANALOG") { |  | ||||||
|                     node.board.on('analogChange', function(e) { |  | ||||||
|                         if (e.pin == node.pin) { |  | ||||||
|                             var msg = {payload:e.value, topic:"A"+e.pin}; |  | ||||||
|                             node.send(msg); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|                 if (node.state == "INPUT") { |  | ||||||
|                     node.board.pinMode(node.pin, ArduinoFirmata.INPUT); |  | ||||||
|                     node.board.on('digitalChange', function(e) { |  | ||||||
|                         if (e.pin == node.pin) { |  | ||||||
|                             var msg = {payload:e.value, topic:e.pin}; |  | ||||||
|                             node.send(msg); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|                 if (node.state == "SYSEX") { |  | ||||||
|                     node.board.on('sysex', function(e) { |  | ||||||
|                         var msg = {payload:e, topic:"sysex"}; |  | ||||||
|                         node.send(msg); |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             this.warn("port not configured"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("arduino in",DuinoNodeIn); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // The Output Node |  | ||||||
|     function DuinoNodeOut(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.buttonState = -1; |  | ||||||
|         this.pin = n.pin; |  | ||||||
|         this.state = n.state; |  | ||||||
|         this.arduino = n.arduino; |  | ||||||
|         this.serverConfig = RED.nodes.getNode(this.arduino); |  | ||||||
|         if (typeof this.serverConfig === "object") { |  | ||||||
|             this.board = this.serverConfig.board; |  | ||||||
|             var node = this; |  | ||||||
|             node.status({fill:"red",shape:"ring",text:"connecting"}); |  | ||||||
|  |  | ||||||
|             node.board.on('connect', function() { |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"connected"}); |  | ||||||
|                 //console.log("o",node.state,node.pin); |  | ||||||
|                 node.board.pinMode(node.pin, node.state); |  | ||||||
|                 node.on("input", function(msg) { |  | ||||||
|                     if (node.state === "OUTPUT") { |  | ||||||
|                         if ((msg.payload === true)||(msg.payload.toString() == "1")||(msg.payload.toString().toLowerCase() == "on")) { |  | ||||||
|                             node.board.digitalWrite(node.pin, true); |  | ||||||
|                         } |  | ||||||
|                         if ((msg.payload === false)||(msg.payload.toString() == "0")||(msg.payload.toString().toLowerCase() == "off")) { |  | ||||||
|                             node.board.digitalWrite(node.pin, false); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     if (node.state === "PWM") { |  | ||||||
|                         msg.payload = msg.payload * 1; |  | ||||||
|                         if ((msg.payload >= 0) && (msg.payload <= 255)) { |  | ||||||
|                             node.board.analogWrite(node.pin, msg.payload); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     if (node.state === "SERVO") { |  | ||||||
|                         msg.payload = msg.payload * 1; |  | ||||||
|                         if ((msg.payload >= 0) && (msg.payload <= 180)) { |  | ||||||
|                             node.board.servoWrite(node.pin, msg.payload); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     if (node.state === "SYSEX") { |  | ||||||
|                         node.board.sysex(msg.payload); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             this.warn("port not configured"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("arduino out",DuinoNodeOut); |  | ||||||
|  |  | ||||||
|     RED.httpAdmin.get("/arduinoports", RED.auth.needsPermission("arduino.read"), function(req,res) { |  | ||||||
|         ArduinoFirmata.list(function (err, ports) { |  | ||||||
|             res.json(ports); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @@ -1,266 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013,2014 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="serial in"> |  | ||||||
|     <div class="form-row node-input-serial"> |  | ||||||
|         <label for="node-input-serial"><i class="fa fa-random"></i> Serial Port</label> |  | ||||||
|         <input type="text" id="node-input-serial"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="serial in"> |  | ||||||
|     <p>Reads data from a local serial port.</p> |  | ||||||
|     <p>Can either <ul><li>wait for a "split" character (default \n). Also accepts hex notation (0x0a).</li> |  | ||||||
|     <li>Wait for a timeout in milliseconds for the first character received</li> |  | ||||||
|     <li>Wait to fill a fixed sized buffer</li></ul></p> |  | ||||||
|     <p>It then outputs <b>msg.payload</b> as either a UTF8 ascii string or a binary Buffer object.</p> |  | ||||||
|     <p>If no split character is specified, or a timeout or buffer size of 0, then a stream of single characters is sent - again either as ascii chars or size 1 binary buffers.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('serial in',{ |  | ||||||
|         category: 'input', |  | ||||||
|         defaults: { |  | ||||||
|             name: {name:""}, |  | ||||||
|             serial: {type:"serial-port",required:true} |  | ||||||
|         }, |  | ||||||
|         color:"BurlyWood", |  | ||||||
|         inputs:0, |  | ||||||
|         outputs:1, |  | ||||||
|         icon: "serial.png", |  | ||||||
|         label: function() { |  | ||||||
|             var serialNode = RED.nodes.node(this.serial); |  | ||||||
|             return this.name||(serialNode?serialNode.label().split(":")[0]:"serial"); |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="serial out"> |  | ||||||
|     <div class="form-row node-input-serial"> |  | ||||||
|         <label for="node-input-serial"><i class="fa fa-random"></i> Serial Port</label> |  | ||||||
|         <input type="text" id="node-input-serial"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="serial out"> |  | ||||||
|     <p>Provides a connection to an outbound serial port.</p> |  | ||||||
|     <p>Only the <b>msg.payload</b> is sent.</p> |  | ||||||
|     <p>Optionally the new line character used to split the input can be appended to every message sent out to the serial port.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('serial out',{ |  | ||||||
|         category: 'output', |  | ||||||
|         defaults: { |  | ||||||
|             name: {name:""}, |  | ||||||
|             serial: {type:"serial-port",required:true} |  | ||||||
|         }, |  | ||||||
|         color:"BurlyWood", |  | ||||||
|         inputs:1, |  | ||||||
|         outputs:0, |  | ||||||
|         icon: "serial.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             var serialNode = RED.nodes.node(this.serial); |  | ||||||
|             return this.name||(serialNode?serialNode.label().split(":")[0]:"serial"); |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="serial-port"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-serialport"><i class="fa fa-random"></i> Serial Port</label> |  | ||||||
|         <input type="text" id="node-config-input-serialport" style="width:60%;" placeholder="/dev/ttyUSB0"/> |  | ||||||
|         <a id="node-config-lookup-serial" class="btn"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <table><tr> |  | ||||||
|             <td width="102px"><i class="fa fa-wrench"></i> Settings</td> |  | ||||||
|             <td width="100px">Baud Rate</td> |  | ||||||
|             <td width="80px">Data Bits</td> |  | ||||||
|             <td width="80px">Parity</td> |  | ||||||
|             <td width="80px">Stop Bits</td> |  | ||||||
|         </tr><tr><td> </td> |  | ||||||
|         <td> |  | ||||||
|         <select type="text" id="node-config-input-serialbaud" style="width: 100px;"> |  | ||||||
|             <option value="115200">115200</option> |  | ||||||
|             <option value="57600">57600</option> |  | ||||||
|             <option value="38400">38400</option> |  | ||||||
|             <option value="19200">19200</option> |  | ||||||
|             <option value="9600">9600</option> |  | ||||||
|             <option value="4800">4800</option> |  | ||||||
|             <option value="2400">2400</option> |  | ||||||
|             <option value="1800">1800</option> |  | ||||||
|             <option value="1200">1200</option> |  | ||||||
|             <option value="600">600</option> |  | ||||||
|             <option value="300">300</option> |  | ||||||
|             <option value="200">200</option> |  | ||||||
|             <option value="150">150</option> |  | ||||||
|             <option value="134">134</option> |  | ||||||
|             <option value="110">110</option> |  | ||||||
|             <option value="75">75</option> |  | ||||||
|             <option value="50">50</option> |  | ||||||
|         </select> |  | ||||||
|         </td><td> |  | ||||||
|         <select type="text" id="node-config-input-databits" style="width: 80px;"> |  | ||||||
|             <option value="8">8</option> |  | ||||||
|             <option value="7">7</option> |  | ||||||
|             <option value="6">6</option> |  | ||||||
|             <option value="5">5</option> |  | ||||||
|         </select> |  | ||||||
|         </td><td> |  | ||||||
|         <select type="text" id="node-config-input-parity" style="width: 80px;"> |  | ||||||
|             <option value="none">None</option> |  | ||||||
|             <option value="even">Even</option> |  | ||||||
|             <option value="mark">Mark</option> |  | ||||||
|             <option value="odd">Odd</option> |  | ||||||
|             <option value="space">Space</option> |  | ||||||
|         </select> |  | ||||||
|         </td><td> |  | ||||||
|         <select type="text" id="node-config-input-stopbits" style="width: 80px;"> |  | ||||||
|             <option value="2">2</option> |  | ||||||
|             <option value="1">1</option> |  | ||||||
|         </select> |  | ||||||
|     </td> |  | ||||||
|     </tr></table><br/> |  | ||||||
|  |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label><i class="fa fa-sign-in"></i> Input</label> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row" style="padding-left: 10px;"> |  | ||||||
|         Split input |  | ||||||
|         <select type="text" id="node-config-input-out" style="margin-left: 5px; width:200px;"> |  | ||||||
|             <option value="char">on the character</option> |  | ||||||
|             <option value="time">after a timeout of</option> |  | ||||||
|             <option value="count">into fixed lengths of</option> |  | ||||||
|         </select> |  | ||||||
|         <input type="text" id="node-config-input-newline"  style="width:50px;"> |  | ||||||
|         <span id="node-units"></span> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row" style="padding-left: 10px;"> |  | ||||||
|         and deliver |  | ||||||
|         <select type="text" id="node-config-input-bin" style="margin-left: 5px; width: 150px;"> |  | ||||||
|             <option value="false">ascii strings</option> |  | ||||||
|             <option value="bin">binary buffers</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <br/> |  | ||||||
|     <div id="node-config-addchar"> |  | ||||||
|         <div class="form-row"> |  | ||||||
|             <label><i class="fa fa-sign-out"></i> Output</label> |  | ||||||
|         </div> |  | ||||||
|         <div class="form-row"> |  | ||||||
|             <input style="width: 30px;margin-left: 10px; vertical-align: top;" type="checkbox" id="node-config-input-addchar"><label style="width: auto;" for="node-config-input-addchar">add split character to output messages</label> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips" id="tip-split">Tip: the "Split on" character is used to split the input into separate messages. It can also be added to every message sent out to the serial port.</div> |  | ||||||
|     <div class="form-tips" id="tip-bin" hidden>Tip: In timeout mode timeout starts from arrival of first character.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('serial-port',{ |  | ||||||
|         category: 'config', |  | ||||||
|         defaults: { |  | ||||||
|             //name: {value:""}, |  | ||||||
|             serialport: {value:"",required:true}, |  | ||||||
|             serialbaud: {value:57600,required:true}, |  | ||||||
|             databits: {value:8,required:true}, |  | ||||||
|             parity: {value:"none",required:true}, |  | ||||||
|             stopbits: {value:1,required:true}, |  | ||||||
|             newline: {value:"\\n"}, |  | ||||||
|             bin: {value:"false"}, |  | ||||||
|             out: {value:"char"}, |  | ||||||
|             addchar: {value:false} |  | ||||||
|         }, |  | ||||||
|         label: function() { |  | ||||||
|             this.serialbaud = this.serialbaud || 57600; |  | ||||||
|             this.databits = this.databits || 8; |  | ||||||
|             this.parity = this.parity || 'none'; |  | ||||||
|             this.stopbits = this.stopbits || 1; |  | ||||||
|             return this.serialport+":"+this.serialbaud+"-"+this.databits+this.parity.charAt(0).toUpperCase()+this.stopbits; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             var previous = null; |  | ||||||
|             $("#node-config-input-out").on('focus', function () { previous = this.value; }).change(function() { |  | ||||||
|                 if (previous == null) { previous = $("#node-config-input-out").val(); } |  | ||||||
|                 if ($("#node-config-input-out").val() == "char") { |  | ||||||
|                     if (previous != "char") { $("#node-config-input-newline").val("\\n"); } |  | ||||||
|                     $("#node-units").text(""); |  | ||||||
|                     $("#node-config-addchar").show(); |  | ||||||
|                     $("#tip-split").show(); |  | ||||||
|                     $("#tip-bin").hide(); |  | ||||||
|                 } |  | ||||||
|                 else if ($("#node-config-input-out").val() == "time") { |  | ||||||
|                     if (previous != "time") { $("#node-config-input-newline").val("0"); } |  | ||||||
|                     $("#node-units").text("ms"); |  | ||||||
|                     $("#node-config-addchar").hide(); |  | ||||||
|                     $("#node-config-input-addchar").val("false"); |  | ||||||
|                     $("#tip-split").hide(); |  | ||||||
|                     $("#tip-bin").show(); |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     if (previous != "count") { $("#node-config-input-newline").val(""); } |  | ||||||
|                     $("#node-units").text("chars"); |  | ||||||
|                     $("#node-config-addchar").hide(); |  | ||||||
|                     $("#node-config-input-addchar").val("false"); |  | ||||||
|                     $("#tip-split").hide(); |  | ||||||
|                     $("#tip-bin").hide(); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             try { |  | ||||||
|                 $("#node-config-input-serialport").autocomplete( "destroy" ); |  | ||||||
|             } catch(err) { |  | ||||||
|             } |  | ||||||
|             $("#node-config-lookup-serial").click(function() { |  | ||||||
|                 //$("#node-config-lookup-serial-icon").removeClass('fa fa-search'); |  | ||||||
|                 //$("#node-config-lookup-serial-icon").addClass('fa fa-spinner'); |  | ||||||
|                 $("#node-config-lookup-serial").addClass('disabled'); |  | ||||||
|                 $.getJSON('serialports',function(data) { |  | ||||||
|                     //$("#node-config-lookup-serial-icon").addClass('fa fa-search'); |  | ||||||
|                     //$("#node-config-lookup-serial-icon").removeClass('fa fa-spinner'); |  | ||||||
|                     $("#node-config-lookup-serial").removeClass('disabled'); |  | ||||||
|                     var ports = []; |  | ||||||
|                     $.each(data, function(i, port){ |  | ||||||
|                         ports.push(port.comName); |  | ||||||
|                     }); |  | ||||||
|                     $("#node-config-input-serialport").autocomplete({ |  | ||||||
|                         source:ports, |  | ||||||
|                         minLength:0, |  | ||||||
|                         close: function( event, ui ) { |  | ||||||
|                             $("#node-config-input-serialport").autocomplete( "destroy" ); |  | ||||||
|                         } |  | ||||||
|                     }).autocomplete("search",""); |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,309 +0,0 @@ | |||||||
| /** |  | ||||||
| * Copyright 2013,2015 IBM Corp. |  | ||||||
| * |  | ||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
| * you may not use this file except in compliance with the License. |  | ||||||
| * You may obtain a copy of the License at |  | ||||||
| * |  | ||||||
| * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| * |  | ||||||
| * Unless required by applicable law or agreed to in writing, software |  | ||||||
| * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
| * See the License for the specific language governing permissions and |  | ||||||
| * limitations under the License. |  | ||||||
| **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var settings = RED.settings; |  | ||||||
|     var events = require("events"); |  | ||||||
|     var serialp = require("serialport"); |  | ||||||
|     var bufMaxSize = 32768;  // Max serial buffer size, for inputs... |  | ||||||
|  |  | ||||||
|     // TODO: 'serialPool' should be encapsulated in SerialPortNode |  | ||||||
|  |  | ||||||
|     function SerialPortNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.serialport = n.serialport; |  | ||||||
|         this.newline = n.newline; |  | ||||||
|         this.addchar = n.addchar || "false"; |  | ||||||
|         this.serialbaud = parseInt(n.serialbaud) || 57600; |  | ||||||
|         this.databits = parseInt(n.databits) || 8; |  | ||||||
|         this.parity = n.parity || "none"; |  | ||||||
|         this.stopbits = parseInt(n.stopbits) || 1; |  | ||||||
|         this.bin = n.bin || "false"; |  | ||||||
|         this.out = n.out || "char"; |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("serial-port",SerialPortNode); |  | ||||||
|  |  | ||||||
|     function SerialOutNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.serial = n.serial; |  | ||||||
|         this.serialConfig = RED.nodes.getNode(this.serial); |  | ||||||
|  |  | ||||||
|         if (this.serialConfig) { |  | ||||||
|             var node = this; |  | ||||||
|             node.port = serialPool.get(this.serialConfig.serialport, |  | ||||||
|                 this.serialConfig.serialbaud, |  | ||||||
|                 this.serialConfig.databits, |  | ||||||
|                 this.serialConfig.parity, |  | ||||||
|                 this.serialConfig.stopbits, |  | ||||||
|                 this.serialConfig.newline); |  | ||||||
|             node.addCh = ""; |  | ||||||
|             if (node.serialConfig.addchar == "true" || node.serialConfig.addchar === true) { |  | ||||||
|                 node.addCh = this.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0"); |  | ||||||
|             } |  | ||||||
|             node.on("input",function(msg) { |  | ||||||
|                 if (msg.hasOwnProperty("payload")) { |  | ||||||
|                     var payload = msg.payload; |  | ||||||
|                     if (!Buffer.isBuffer(payload)) { |  | ||||||
|                         if (typeof payload === "object") { |  | ||||||
|                             payload = JSON.stringify(payload); |  | ||||||
|                         } else { |  | ||||||
|                             payload = payload.toString(); |  | ||||||
|                         } |  | ||||||
|                         payload += node.addCh; |  | ||||||
|                     } else if (node.addCh !== "") { |  | ||||||
|                         payload = Buffer.concat([payload,new Buffer(node.addCh)]); |  | ||||||
|                     } |  | ||||||
|                     node.port.write(payload,function(err,res) { |  | ||||||
|                         if (err) { |  | ||||||
|                             var errmsg = err.toString().replace("Serialport","Serialport "+node.port.serial.path); |  | ||||||
|                             node.error(errmsg,msg); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             node.port.on('ready', function() { |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"connected"}); |  | ||||||
|             }); |  | ||||||
|             node.port.on('closed', function() { |  | ||||||
|                 node.status({fill:"red",shape:"ring",text:"not connected"}); |  | ||||||
|             }); |  | ||||||
|         } else { |  | ||||||
|             this.error("missing serial config"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on("close", function(done) { |  | ||||||
|             if (this.serialConfig) { |  | ||||||
|                 serialPool.close(this.serialConfig.serialport,done); |  | ||||||
|             } else { |  | ||||||
|                 done(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("serial out",SerialOutNode); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     function SerialInNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.serial = n.serial; |  | ||||||
|         this.serialConfig = RED.nodes.getNode(this.serial); |  | ||||||
|  |  | ||||||
|         if (this.serialConfig) { |  | ||||||
|             var node = this; |  | ||||||
|             node.tout = null; |  | ||||||
|             var buf; |  | ||||||
|             if (node.serialConfig.out != "count") { buf = new Buffer(bufMaxSize); } |  | ||||||
|             else { buf = new Buffer(Number(node.serialConfig.newline)); } |  | ||||||
|             var i = 0; |  | ||||||
|             node.status({fill:"grey",shape:"dot",text:"unknown"}); |  | ||||||
|             node.port = serialPool.get(this.serialConfig.serialport, |  | ||||||
|                 this.serialConfig.serialbaud, |  | ||||||
|                 this.serialConfig.databits, |  | ||||||
|                 this.serialConfig.parity, |  | ||||||
|                 this.serialConfig.stopbits, |  | ||||||
|                 this.serialConfig.newline |  | ||||||
|             ); |  | ||||||
|  |  | ||||||
|             var splitc; |  | ||||||
|             if (node.serialConfig.newline.substr(0,2) == "0x") { |  | ||||||
|                 splitc = new Buffer([parseInt(node.serialConfig.newline)]); |  | ||||||
|             } else { |  | ||||||
|                 splitc = new Buffer(node.serialConfig.newline.replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\e","\e").replace("\\f","\f").replace("\\0","\0")); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             this.port.on('data', function(msg) { |  | ||||||
|                 // single char buffer |  | ||||||
|                 if ((node.serialConfig.newline === 0)||(node.serialConfig.newline === "")) { |  | ||||||
|                     if (node.serialConfig.bin !== "bin") { node.send({"payload": String.fromCharCode(msg)}); } |  | ||||||
|                     else { node.send({"payload": new Buffer([msg])}); } |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     // do the timer thing |  | ||||||
|                     if (node.serialConfig.out === "time")  { |  | ||||||
|                         if (node.tout) { |  | ||||||
|                             i += 1; |  | ||||||
|                             buf[i] = msg; |  | ||||||
|                         } |  | ||||||
|                         else { |  | ||||||
|                             node.tout = setTimeout(function () { |  | ||||||
|                                 node.tout = null; |  | ||||||
|                                 var m = new Buffer(i+1); |  | ||||||
|                                 buf.copy(m,0,0,i+1); |  | ||||||
|                                 if (node.serialConfig.bin !== "bin") { m = m.toString(); } |  | ||||||
|                                 node.send({"payload": m}); |  | ||||||
|                                 m = null; |  | ||||||
|                             }, node.serialConfig.newline); |  | ||||||
|                             i = 0; |  | ||||||
|                             buf[0] = msg; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     // count bytes into a buffer... |  | ||||||
|                     else if (node.serialConfig.out === "count") { |  | ||||||
|                         buf[i] = msg; |  | ||||||
|                         i += 1; |  | ||||||
|                         if ( i >= parseInt(node.serialConfig.newline)) { |  | ||||||
|                             var m = new Buffer(i); |  | ||||||
|                             buf.copy(m,0,0,i); |  | ||||||
|                             if (node.serialConfig.bin !== "bin") { m = m.toString(); } |  | ||||||
|                             node.send({"payload":m}); |  | ||||||
|                             m = null; |  | ||||||
|                             i = 0; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     // look to match char... |  | ||||||
|                     else if (node.serialConfig.out === "char") { |  | ||||||
|                         buf[i] = msg; |  | ||||||
|                         i += 1; |  | ||||||
|                         if ((msg === splitc[0]) || (i === bufMaxSize)) { |  | ||||||
|                             var m = new Buffer(i); |  | ||||||
|                             buf.copy(m,0,0,i); |  | ||||||
|                             if (node.serialConfig.bin !== "bin") { m = m.toString(); } |  | ||||||
|                             node.send({"payload":m}); |  | ||||||
|                             m = null; |  | ||||||
|                             i = 0; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else { node.log("should never get here"); } |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             this.port.on('ready', function() { |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"connected"}); |  | ||||||
|             }); |  | ||||||
|             this.port.on('closed', function() { |  | ||||||
|                 node.status({fill:"red",shape:"ring",text:"not connected"}); |  | ||||||
|             }); |  | ||||||
|         } else { |  | ||||||
|             this.error("missing serial config"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on("close", function(done) { |  | ||||||
|             if (this.serialConfig) { |  | ||||||
|                 serialPool.close(this.serialConfig.serialport,done); |  | ||||||
|             } else { |  | ||||||
|                 done(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("serial in",SerialInNode); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     var serialPool = function() { |  | ||||||
|         var connections = {}; |  | ||||||
|         return { |  | ||||||
|             get:function(port,baud,databits,parity,stopbits,newline,callback) { |  | ||||||
|                 var id = port; |  | ||||||
|                 if (!connections[id]) { |  | ||||||
|                     connections[id] = function() { |  | ||||||
|                         var obj = { |  | ||||||
|                             _emitter: new events.EventEmitter(), |  | ||||||
|                             serial: null, |  | ||||||
|                             _closing: false, |  | ||||||
|                             tout: null, |  | ||||||
|                             on: function(a,b) { this._emitter.on(a,b); }, |  | ||||||
|                             close: function(cb) { this.serial.close(cb); }, |  | ||||||
|                             write: function(m,cb) { this.serial.write(m,cb); }, |  | ||||||
|                         } |  | ||||||
|                         //newline = newline.replace("\\n","\n").replace("\\r","\r"); |  | ||||||
|                         var setupSerial = function() { |  | ||||||
|                             //if (newline == "") { |  | ||||||
|                                 obj.serial = new serialp.SerialPort(port,{ |  | ||||||
|                                     baudrate: baud, |  | ||||||
|                                     databits: databits, |  | ||||||
|                                     parity: parity, |  | ||||||
|                                     stopbits: stopbits, |  | ||||||
|                                     parser: serialp.parsers.raw |  | ||||||
|                                 },true, function(err, results) { if (err) { obj.serial.emit('error',err); } }); |  | ||||||
|                             //} |  | ||||||
|                             //else { |  | ||||||
|                             //    obj.serial = new serialp.SerialPort(port,{ |  | ||||||
|                             //        baudrate: baud, |  | ||||||
|                             //        databits: databits, |  | ||||||
|                             //        parity: parity, |  | ||||||
|                             //        stopbits: stopbits, |  | ||||||
|                             //        parser: serialp.parsers.readline(newline) |  | ||||||
|                             //    },true, function(err, results) { if (err) obj.serial.emit('error',err); }); |  | ||||||
|                             //} |  | ||||||
|                             obj.serial.on('error', function(err) { |  | ||||||
|                                 RED.log.error("serial port "+port+" error "+err); |  | ||||||
|                                 obj._emitter.emit('closed'); |  | ||||||
|                                 obj.tout = setTimeout(function() { |  | ||||||
|                                     setupSerial(); |  | ||||||
|                                 }, settings.serialReconnectTime); |  | ||||||
|                             }); |  | ||||||
|                             obj.serial.on('close', function() { |  | ||||||
|                                 if (!obj._closing) { |  | ||||||
|                                     RED.log.error("serial port "+port+" closed unexpectedly"); |  | ||||||
|                                     obj._emitter.emit('closed'); |  | ||||||
|                                     obj.tout = setTimeout(function() { |  | ||||||
|                                         setupSerial(); |  | ||||||
|                                     }, settings.serialReconnectTime); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                             obj.serial.on('open',function() { |  | ||||||
|                                 RED.log.info("serial port "+port+" opened at "+baud+" baud "+databits+""+parity.charAt(0).toUpperCase()+stopbits); |  | ||||||
|                                 if (obj.tout) { clearTimeout(obj.tout); } |  | ||||||
|                                 //obj.serial.flush(); |  | ||||||
|                                 obj._emitter.emit('ready'); |  | ||||||
|                             }); |  | ||||||
|                             obj.serial.on('data',function(d) { |  | ||||||
|                                 //console.log(Buffer.isBuffer(d),d.length,d); |  | ||||||
|                                 //if (typeof d !== "string") { |  | ||||||
|                                 //    //d = d.toString(); |  | ||||||
|                                     for (var z=0; z<d.length; z++) { |  | ||||||
|                                         obj._emitter.emit('data',d[z]); |  | ||||||
|                                     } |  | ||||||
|                                 //} |  | ||||||
|                                 //else { |  | ||||||
|                                 //    obj._emitter.emit('data',d); |  | ||||||
|                                 //} |  | ||||||
|                             }); |  | ||||||
|                             obj.serial.on("disconnect",function() { |  | ||||||
|                                 RED.log.error("serial port "+port+" gone away"); |  | ||||||
|                             }); |  | ||||||
|                         } |  | ||||||
|                         setupSerial(); |  | ||||||
|                         return obj; |  | ||||||
|                     }(); |  | ||||||
|                 } |  | ||||||
|                 return connections[id]; |  | ||||||
|             }, |  | ||||||
|             close: function(port,done) { |  | ||||||
|                 if (connections[port]) { |  | ||||||
|                     if (connections[port].tout != null) { |  | ||||||
|                         clearTimeout(connections[port].tout); |  | ||||||
|                     } |  | ||||||
|                     connections[port]._closing = true; |  | ||||||
|                     try { |  | ||||||
|                         connections[port].close(function() { |  | ||||||
|                             RED.log.info("serial port closed"); |  | ||||||
|                             done(); |  | ||||||
|                         }); |  | ||||||
|                     } |  | ||||||
|                     catch(err) { } |  | ||||||
|                     delete connections[port]; |  | ||||||
|                 } else { |  | ||||||
|                     done(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }(); |  | ||||||
|  |  | ||||||
|     RED.httpAdmin.get("/serialports", RED.auth.needsPermission('serial.read'), function(req,res) { |  | ||||||
|         serialp.list(function (err, ports) { |  | ||||||
|             res.json(ports); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @@ -1,222 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="twitter-credentials"> |  | ||||||
|     <div class="form-row" id="node-config-twitter-row"></div> |  | ||||||
|     <input type="hidden" id="node-config-input-screen_name"> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
| (function() { |  | ||||||
|     var twitterConfigNodeId = null; |  | ||||||
|     var twitterConfigNodeIntervalId = null; |  | ||||||
|  |  | ||||||
|     function showTwitterAuthStart() { |  | ||||||
|         var pathname = document.location.pathname; |  | ||||||
|         if (pathname.slice(-1) != "/") { |  | ||||||
|             pathname += "/"; |  | ||||||
|         } |  | ||||||
|         var callback = encodeURIComponent(location.protocol+"//"+location.hostname+":"+location.port+pathname+"twitter-credentials/"+twitterConfigNodeId+"/auth/callback"); |  | ||||||
|         $("#node-config-dialog-ok").button("disable"); |  | ||||||
|         $("#node-config-twitter-row").html('<div style="text-align: center; margin-top: 20px; "><a class="btn" id="node-config-twitter-start" href="twitter-credentials/'+twitterConfigNodeId+'/auth?callback='+callback+'" target="_blank">Click here to authenticate with Twitter.</a></div>'); |  | ||||||
|         $("#node-config-twitter-start").click(function() { |  | ||||||
|             twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     function updateTwitterScreenName(sn) { |  | ||||||
|         $("#node-config-input-screen_name").val(sn); |  | ||||||
|         $("#node-config-twitter-row").html('<label><i class="fa fa-user"></i> Twitter ID</label><span class="input-xlarge uneditable-input">'+sn+'</span>'); |  | ||||||
|     } |  | ||||||
|     function pollTwitterCredentials(e) { |  | ||||||
|         $.getJSON('credentials/twitter-credentials/'+twitterConfigNodeId,function(data) { |  | ||||||
|             if (data.screen_name) { |  | ||||||
|                 updateTwitterScreenName(data.screen_name); |  | ||||||
|                 twitterConfigNodeIntervalId = null; |  | ||||||
|                 $("#node-config-dialog-ok").button("enable"); |  | ||||||
|             } else { |  | ||||||
|                 twitterConfigNodeIntervalId = window.setTimeout(pollTwitterCredentials,2000); |  | ||||||
|             } |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType('twitter-credentials',{ |  | ||||||
|         category: 'config', |  | ||||||
|         defaults: { |  | ||||||
|             screen_name: {value:""} |  | ||||||
|         }, |  | ||||||
|         credentials: { |  | ||||||
|             screen_name: {type:"text"}, |  | ||||||
|             access_token: {type: "password"}, |  | ||||||
|             access_token_secret: {type:"password"} |  | ||||||
|         }, |  | ||||||
|         label: function() { |  | ||||||
|             return this.screen_name; |  | ||||||
|         }, |  | ||||||
|         exportable: false, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             twitterConfigNodeId = this.id; |  | ||||||
|             if (!this.screen_name || this.screen_name == "") { |  | ||||||
|                 showTwitterAuthStart(); |  | ||||||
|             } else { |  | ||||||
|                 if (this.credentials.screen_name) { |  | ||||||
|                     updateTwitterScreenName(this.credentials.screen_name); |  | ||||||
|                 } else { |  | ||||||
|                     showTwitterAuthStart(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         oneditsave: function() { |  | ||||||
|             if (twitterConfigNodeIntervalId) { |  | ||||||
|                 window.clearTimeout(twitterConfigNodeIntervalId); |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         oneditcancel: function(adding) { |  | ||||||
|             if (twitterConfigNodeIntervalId) { |  | ||||||
|                 window.clearTimeout(twitterConfigNodeIntervalId); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| })(); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="twitter in"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-twitter"><i class="fa fa-user"></i> Log in as</label> |  | ||||||
|         <input type="text" id="node-input-twitter"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-user"><i class="fa fa-search"></i> Search</label> |  | ||||||
|         <select type="text" id="node-input-user" style="display: inline-block; vertical-align: middle; width:60%;"> |  | ||||||
|             <option value="false">all public tweets</option> |  | ||||||
|             <option value="true">the tweets of who you follow</option> |  | ||||||
|             <option value="user">the tweets of specific users</option> |  | ||||||
|             <option value="dm">your direct messages</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row" id="node-input-tags-row"> |  | ||||||
|         <label for="node-input-tags"><i class="fa fa-tags"></i> <span id="node-input-tags-label">for</span></label> |  | ||||||
|         <input type="text" id="node-input-tags" placeholder="comma-separated words, @ids, #tags"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips">Tip: Use commas without spaces between multiple search terms. Comma = OR, Space = AND. |  | ||||||
|     <br/>The Twitter API WILL NOT deliver 100% of all tweets. |  | ||||||
|     <br/>Tweets of who you follow will include their retweets and favourites.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="twitter in"> |  | ||||||
|     <p>Twitter input node. Can be used to search either: |  | ||||||
|     <ul><li>the public or a user's stream for tweets containing the configured search term</li> |  | ||||||
|         <li>all tweets by specific users</li> |  | ||||||
|         <li>direct messages received by the authenticated user</li> |  | ||||||
|     </ul></p> |  | ||||||
|     <p>Use space for <i>and</i> and comma , for <i>or</i> when searching for multiple terms.</p> |  | ||||||
|     <p>Sets the <b>msg.topic</b> to <i>tweets/</i> and then appends the senders screen name.</p> |  | ||||||
|     <p>Sets <b>msg.location</b> to the tweeters location if known.</p> |  | ||||||
|     <p>Sets <b>msg.tweet</b> to the full tweet object as documented by <a href="https://dev.twitter.com/docs/platform-objects/tweets">Twitter</a>. |  | ||||||
|     <p><b>Note:</b> when set to a specific user's tweets, or your direct messages, the node is subject to |  | ||||||
|       Twitter's API rate limiting. If you deploy the flows multiple times within a 15 minute window, you may |  | ||||||
|       exceed the limit and will see errors from the node. These errors will clear when the current 15 minute window |  | ||||||
|       passes.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('twitter in',{ |  | ||||||
|         category: 'social-input', |  | ||||||
|         color:"#C0DEED", |  | ||||||
|         defaults: { |  | ||||||
|             twitter: {type:"twitter-credentials",required:true}, |  | ||||||
|             tags: {value:"",validate:function(v) { return this.user == "dm" || v.length > 0;}}, |  | ||||||
|             user: {value:"false",required:true}, |  | ||||||
|             name: {value:""}, |  | ||||||
|             topic: {value:"tweets"} |  | ||||||
|         }, |  | ||||||
|         inputs:0, |  | ||||||
|         outputs:1, |  | ||||||
|         icon: "twitter.png", |  | ||||||
|         label: function() { |  | ||||||
|             if (this.name) { |  | ||||||
|                 return this.name; |  | ||||||
|             } |  | ||||||
|             if (this.user == "dm") { |  | ||||||
|                 var user = RED.nodes.node(this.twitter); |  | ||||||
|                 return (user?user.label()+" ":"")+"DMs"; |  | ||||||
|             } else if (this.user == "user") { |  | ||||||
|                 return this.tags+" tweets"; |  | ||||||
|             } |  | ||||||
|             return this.tags; |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             $("#node-input-user").change(function() { |  | ||||||
|                 var type = $("#node-input-user option:selected").val(); |  | ||||||
|                 if (type == "user") { |  | ||||||
|                     $("#node-input-tags-row").show(); |  | ||||||
|                     $("#node-input-tags-label").html("User"); |  | ||||||
|                     $("#node-input-tags").attr("placeholder","comma-separated @twitter handles"); |  | ||||||
|                 } else if (type == "dm") { |  | ||||||
|                     $("#node-input-tags-row").hide(); |  | ||||||
|                 } else { |  | ||||||
|                     $("#node-input-tags-row").show(); |  | ||||||
|                     $("#node-input-tags-label").html("for"); |  | ||||||
|                     $("#node-input-tags").attr("placeholder","comma-separated words, @ids, #hashtags"); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|             }); |  | ||||||
|             $("#node-input-user").change(); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="twitter out"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-twitter"><i class="fa fa-user"></i> Twitter</label> |  | ||||||
|         <input type="text" id="node-input-twitter"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="twitter out"> |  | ||||||
|     <p>Twitter out node. Tweets the <b>msg.payload</b>.</p> |  | ||||||
|     <p>To send a Direct Message (DM) - use a payload like "D {username} {message}"</p> |  | ||||||
|     <p>If <b>msg.media</b> exists and is a Buffer object, this node will treat it |  | ||||||
|        as an image and attach it to the tweet.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('twitter out',{ |  | ||||||
|         category: 'social-output', |  | ||||||
|         color:"#C0DEED", |  | ||||||
|         defaults: { |  | ||||||
|             twitter: {type:"twitter-credentials",required:true}, |  | ||||||
|             name: {value:"Tweet"} |  | ||||||
|         }, |  | ||||||
|         inputs:1, |  | ||||||
|         outputs:0, |  | ||||||
|         icon: "twitter.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             return this.name; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,386 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var ntwitter = require('twitter-ng'); |  | ||||||
|     var OAuth= require('oauth').OAuth; |  | ||||||
|     var request = require('request'); |  | ||||||
|  |  | ||||||
|     function TwitterNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.screen_name = n.screen_name; |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("twitter-credentials",TwitterNode,{ |  | ||||||
|         credentials: { |  | ||||||
|             screen_name: {type:"text"}, |  | ||||||
|             access_token: {type: "password"}, |  | ||||||
|             access_token_secret: {type:"password"} |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Populate msg.location based on data found in msg.tweet. |  | ||||||
|      */ |  | ||||||
|     function addLocationToTweet(msg) { |  | ||||||
|         if(msg.tweet) { |  | ||||||
|             if(msg.tweet.geo) { // if geo is set, always set location from geo |  | ||||||
|                 if(msg.tweet.geo.coordinates && msg.tweet.geo.coordinates.length === 2) { |  | ||||||
|                     if (!msg.location) { msg.location = {}; } |  | ||||||
|                     // coordinates[0] is lat, coordinates[1] is lon |  | ||||||
|                     msg.location.lat = msg.tweet.geo.coordinates[0]; |  | ||||||
|                     msg.location.lon = msg.tweet.geo.coordinates[1]; |  | ||||||
|                     msg.location.icon = "twitter"; |  | ||||||
|                 } |  | ||||||
|             } else if(msg.tweet.coordinates) { // otherwise attempt go get it from coordinates |  | ||||||
|                 if(msg.tweet.coordinates.coordinates && msg.tweet.coordinates.coordinates.length === 2) { |  | ||||||
|                     if (!msg.location) { msg.location = {}; } |  | ||||||
|                     // WARNING! coordinates[1] is lat, coordinates[0] is lon!!! |  | ||||||
|                     msg.location.lat = msg.tweet.coordinates.coordinates[1]; |  | ||||||
|                     msg.location.lon = msg.tweet.coordinates.coordinates[0]; |  | ||||||
|                     msg.location.icon = "twitter"; |  | ||||||
|                 } |  | ||||||
|             } // if none of these found then just do nothing |  | ||||||
|         } // if no msg.tweet then just do nothing |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function TwitterInNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.active = true; |  | ||||||
|         this.user = n.user; |  | ||||||
|         //this.tags = n.tags.replace(/ /g,''); |  | ||||||
|         this.tags = n.tags; |  | ||||||
|         this.twitter = n.twitter; |  | ||||||
|         this.topic = n.topic||"tweets"; |  | ||||||
|         this.twitterConfig = RED.nodes.getNode(this.twitter); |  | ||||||
|         var credentials = RED.nodes.getCredentials(this.twitter); |  | ||||||
|  |  | ||||||
|         if (credentials && credentials.screen_name == this.twitterConfig.screen_name) { |  | ||||||
|             var twit = new ntwitter({ |  | ||||||
|                 consumer_key: "OKjYEd1ef2bfFolV25G5nQ", |  | ||||||
|                 consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g", |  | ||||||
|                 access_token_key: credentials.access_token, |  | ||||||
|                 access_token_secret: credentials.access_token_secret |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             //setInterval(function() { |  | ||||||
|             //        twit.get("/application/rate_limit_status.json",null,function(err,cb) { |  | ||||||
|             //                console.log("direct_messages:",cb["resources"]["direct_messages"]); |  | ||||||
|             //        }); |  | ||||||
|             // |  | ||||||
|             //},10000); |  | ||||||
|  |  | ||||||
|             var node = this; |  | ||||||
|             if (this.user === "user") { |  | ||||||
|                 node.poll_ids = []; |  | ||||||
|                 node.since_ids = {}; |  | ||||||
|                 var users = node.tags.split(","); |  | ||||||
|                 for (var i=0;i<users.length;i++) { |  | ||||||
|                     var user = users[i].replace(" ",""); |  | ||||||
|                     twit.getUserTimeline({ |  | ||||||
|                             screen_name:user, |  | ||||||
|                             trim_user:0, |  | ||||||
|                             count:1 |  | ||||||
|                     },function() { |  | ||||||
|                         var u = user+""; |  | ||||||
|                         return function(err,cb) { |  | ||||||
|                             if (err) { |  | ||||||
|                                 node.error(err); |  | ||||||
|                                 return; |  | ||||||
|                             } |  | ||||||
|                             if (cb[0]) { |  | ||||||
|                                 node.since_ids[u] = cb[0].id_str; |  | ||||||
|                             } else { |  | ||||||
|                                 node.since_ids[u] = '0'; |  | ||||||
|                             } |  | ||||||
|                             node.poll_ids.push(setInterval(function() { |  | ||||||
|                                 twit.getUserTimeline({ |  | ||||||
|                                         screen_name:u, |  | ||||||
|                                         trim_user:0, |  | ||||||
|                                         since_id:node.since_ids[u] |  | ||||||
|                                 },function(err,cb) { |  | ||||||
|                                     if (cb) { |  | ||||||
|                                         for (var t=cb.length-1;t>=0;t-=1) { |  | ||||||
|                                             var tweet = cb[t]; |  | ||||||
|                                             var where = tweet.user.location; |  | ||||||
|                                             var la = tweet.lang || tweet.user.lang; |  | ||||||
|                                             var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, lang:la, tweet:tweet }; |  | ||||||
|                                             if (where) { |  | ||||||
|                                                 msg.location = {place:where}; |  | ||||||
|                                                 addLocationToTweet(msg); |  | ||||||
|                                             } |  | ||||||
|                                             node.send(msg); |  | ||||||
|                                             if (t == 0) { |  | ||||||
|                                                 node.since_ids[u] = tweet.id_str; |  | ||||||
|                                             } |  | ||||||
|                                         } |  | ||||||
|                                     } |  | ||||||
|                                     if (err) { |  | ||||||
|                                         node.error(err); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                             },60000)); |  | ||||||
|                         } |  | ||||||
|                     }()); |  | ||||||
|                 } |  | ||||||
|             } else if (this.user === "dm") { |  | ||||||
|                 node.poll_ids = []; |  | ||||||
|                 twit.getDirectMessages({ |  | ||||||
|                         screen_name:node.twitterConfig.screen_name, |  | ||||||
|                         trim_user:0, |  | ||||||
|                         count:1 |  | ||||||
|                 },function(err,cb) { |  | ||||||
|                     if (err) { |  | ||||||
|                         node.error(err); |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
|                     if (cb[0]) { |  | ||||||
|                         node.since_id = cb[0].id_str; |  | ||||||
|                     } else { |  | ||||||
|                         node.since_id = '0'; |  | ||||||
|                     } |  | ||||||
|                     node.poll_ids.push(setInterval(function() { |  | ||||||
|                             twit.getDirectMessages({ |  | ||||||
|                                     screen_name:node.twitterConfig.screen_name, |  | ||||||
|                                     trim_user:0, |  | ||||||
|                                     since_id:node.since_id |  | ||||||
|                             },function(err,cb) { |  | ||||||
|                                 if (cb) { |  | ||||||
|                                     for (var t=cb.length-1;t>=0;t-=1) { |  | ||||||
|                                         var tweet = cb[t]; |  | ||||||
|                                         var where = tweet.sender.location; |  | ||||||
|                                         var la = tweet.lang || tweet.sender.lang; |  | ||||||
|                                         var msg = { topic:node.topic+"/"+tweet.sender.screen_name, payload:tweet.text, lang:la, tweet:tweet }; |  | ||||||
|                                         if (where) { |  | ||||||
|                                             msg.location = {place:where}; |  | ||||||
|                                             addLocationToTweet(msg); |  | ||||||
|                                         } |  | ||||||
|                                         node.send(msg); |  | ||||||
|                                         if (t == 0) { |  | ||||||
|                                             node.since_id = tweet.id_str; |  | ||||||
|                                         } |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                                 if (err) { |  | ||||||
|                                     node.error(err); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                     },120000)); |  | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|             } else if (this.tags !== "") { |  | ||||||
|                 try { |  | ||||||
|                     var thing = 'statuses/filter'; |  | ||||||
|                     if (this.user === "true") { thing = 'user'; } |  | ||||||
|                     var st = { track: [node.tags] }; |  | ||||||
|                     var bits = node.tags.split(","); |  | ||||||
|                     if (bits.length == 4) { |  | ||||||
|                         if ((Number(bits[0]) < Number(bits[2])) && (Number(bits[1]) < Number(bits[3]))) { |  | ||||||
|                             st = { locations: node.tags }; |  | ||||||
|                         } |  | ||||||
|                         else { |  | ||||||
|                             node.log("possible bad geo area format. Should be lower-left lon, lat, upper-right lon, lat"); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     var setupStream = function() { |  | ||||||
|                         if (node.active) { |  | ||||||
|                             twit.stream(thing, st, function(stream) { |  | ||||||
|                                 //console.log(st); |  | ||||||
|                                 //twit.stream('user', { track: [node.tags] }, function(stream) { |  | ||||||
|                                 //twit.stream('site', { track: [node.tags] }, function(stream) { |  | ||||||
|                                 //twit.stream('statuses/filter', { track: [node.tags] }, function(stream) { |  | ||||||
|                                 node.stream = stream; |  | ||||||
|                                 stream.on('data', function(tweet) { |  | ||||||
|                                     if (tweet.user !== undefined) { |  | ||||||
|                                         var where = tweet.user.location; |  | ||||||
|                                         var la = tweet.lang || tweet.user.lang; |  | ||||||
|                                         var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, lang:la, tweet:tweet }; |  | ||||||
|                                         if (where) { |  | ||||||
|                                             msg.location = {place:where}; |  | ||||||
|                                             addLocationToTweet(msg); |  | ||||||
|                                         } |  | ||||||
|                                         node.send(msg); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                                 stream.on('limit', function(tweet) { |  | ||||||
|                                     node.warn("tweet rate limit hit"); |  | ||||||
|                                 }); |  | ||||||
|                                 stream.on('error', function(tweet,rc) { |  | ||||||
|                                     if (rc == 420) { |  | ||||||
|                                         node.warn("Twitter rate limit hit"); |  | ||||||
|                                     } else { |  | ||||||
|                                         node.warn("Stream error:"+tweet.toString()+" ("+rc+")"); |  | ||||||
|                                     } |  | ||||||
|                                     setTimeout(setupStream,10000); |  | ||||||
|                                 }); |  | ||||||
|                                 stream.on('destroy', function (response) { |  | ||||||
|                                     if (this.active) { |  | ||||||
|                                         node.warn("twitter ended unexpectedly"); |  | ||||||
|                                         setTimeout(setupStream,10000); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                             }); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     setupStream(); |  | ||||||
|                 } |  | ||||||
|                 catch (err) { |  | ||||||
|                     node.error(err); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 this.error("Invalid tag property"); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             this.error("missing twitter credentials"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on('close', function() { |  | ||||||
|             if (this.stream) { |  | ||||||
|                 this.active = false; |  | ||||||
|                 this.stream.destroy(); |  | ||||||
|             } |  | ||||||
|             if (this.poll_ids) { |  | ||||||
|                 for (var i=0;i<this.poll_ids.length;i++) { |  | ||||||
|                     clearInterval(this.poll_ids[i]); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("twitter in",TwitterInNode); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     function TwitterOutNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.topic = n.topic; |  | ||||||
|         this.twitter = n.twitter; |  | ||||||
|         this.twitterConfig = RED.nodes.getNode(this.twitter); |  | ||||||
|         var credentials = RED.nodes.getCredentials(this.twitter); |  | ||||||
|         var node = this; |  | ||||||
|  |  | ||||||
|         if (credentials && credentials.screen_name == this.twitterConfig.screen_name) { |  | ||||||
|             var twit = new ntwitter({ |  | ||||||
|                 consumer_key: "OKjYEd1ef2bfFolV25G5nQ", |  | ||||||
|                 consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g", |  | ||||||
|                 access_token_key: credentials.access_token, |  | ||||||
|                 access_token_secret: credentials.access_token_secret |  | ||||||
|             }); |  | ||||||
|             node.on("input", function(msg) { |  | ||||||
|                 if (msg.hasOwnProperty("payload")) { |  | ||||||
|                     node.status({fill:"blue",shape:"dot",text:"tweeting"}); |  | ||||||
|  |  | ||||||
|                     if (msg.payload.length > 140) { |  | ||||||
|                         msg.payload = msg.payload.slice(0,139); |  | ||||||
|                         node.warn("Tweet greater than 140 : truncated"); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     if (msg.media && Buffer.isBuffer(msg.media)) { |  | ||||||
|                         var apiUrl = "https://api.twitter.com/1.1/statuses/update_with_media.json"; |  | ||||||
|                         var signedUrl = oa.signUrl(apiUrl, |  | ||||||
|                             credentials.access_token, |  | ||||||
|                             credentials.access_token_secret, |  | ||||||
|                             "POST"); |  | ||||||
|  |  | ||||||
|                         var r = request.post(signedUrl,function(err,httpResponse,body) { |  | ||||||
|                             if (err) { |  | ||||||
|                                 node.error(err,msg); |  | ||||||
|                                 node.status({fill:"red",shape:"ring",text:"failed"}); |  | ||||||
|                             } else { |  | ||||||
|                                 var response = JSON.parse(body); |  | ||||||
|                                 if (response.errors) { |  | ||||||
|                                     var errorList = response.errors.map(function(er) { return er.code+": "+er.message }).join(", "); |  | ||||||
|                                     node.error("Send tweet failed: "+errorList,msg); |  | ||||||
|                                     node.status({fill:"red",shape:"ring",text:"failed"}); |  | ||||||
|                                 } else { |  | ||||||
|                                     node.status({}); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         }); |  | ||||||
|                         var form = r.form(); |  | ||||||
|                         form.append("status",msg.payload); |  | ||||||
|                         form.append("media[]",msg.media,{filename:"image"}); |  | ||||||
|  |  | ||||||
|                     } else { |  | ||||||
|                         twit.updateStatus(msg.payload, function (err, data) { |  | ||||||
|                             if (err) { |  | ||||||
|                                 node.status({fill:"red",shape:"ring",text:"failed"}); |  | ||||||
|                                 node.error(err,msg); |  | ||||||
|                             } |  | ||||||
|                             node.status({}); |  | ||||||
|                         }); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 else { node.warn("No payload to tweet"); } |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("twitter out",TwitterOutNode); |  | ||||||
|  |  | ||||||
|     var oa = new OAuth( |  | ||||||
|         "https://api.twitter.com/oauth/request_token", |  | ||||||
|         "https://api.twitter.com/oauth/access_token", |  | ||||||
|         "OKjYEd1ef2bfFolV25G5nQ", |  | ||||||
|         "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g", |  | ||||||
|         "1.0", |  | ||||||
|         null, |  | ||||||
|         "HMAC-SHA1" |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     RED.httpAdmin.get('/twitter-credentials/:id/auth', function(req, res){ |  | ||||||
|         var credentials = {}; |  | ||||||
|         oa.getOAuthRequestToken({ |  | ||||||
|                 oauth_callback: req.query.callback |  | ||||||
|         },function(error, oauth_token, oauth_token_secret, results){ |  | ||||||
|             if (error) { |  | ||||||
|                 var resp = '<h2>Oh no!</h2>'+ |  | ||||||
|                 '<p>Something went wrong with the authentication process. The following error was returned:<p>'+ |  | ||||||
|                 '<p><b>'+error.statusCode+'</b>: '+error.data+'</p>'+ |  | ||||||
|                 '<p>One known cause of this type of failure is if the clock is wrong on system running Node-RED.'; |  | ||||||
|                 res.send(resp) |  | ||||||
|             } else { |  | ||||||
|                 credentials.oauth_token = oauth_token; |  | ||||||
|                 credentials.oauth_token_secret = oauth_token_secret; |  | ||||||
|                 res.redirect('https://twitter.com/oauth/authorize?oauth_token='+oauth_token) |  | ||||||
|                 RED.nodes.addCredentials(req.params.id,credentials); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     RED.httpAdmin.get('/twitter-credentials/:id/auth/callback', function(req, res, next){ |  | ||||||
|         var credentials = RED.nodes.getCredentials(req.params.id); |  | ||||||
|         credentials.oauth_verifier = req.query.oauth_verifier; |  | ||||||
|  |  | ||||||
|         oa.getOAuthAccessToken( |  | ||||||
|             credentials.oauth_token, |  | ||||||
|             credentials.token_secret, |  | ||||||
|             credentials.oauth_verifier, |  | ||||||
|             function(error, oauth_access_token, oauth_access_token_secret, results){ |  | ||||||
|                 if (error){ |  | ||||||
|                     RED.log.error(error); |  | ||||||
|                     res.send("something in twitter oauth broke."); |  | ||||||
|                 } else { |  | ||||||
|                     credentials = {}; |  | ||||||
|                     credentials.access_token = oauth_access_token; |  | ||||||
|                     credentials.access_token_secret = oauth_access_token_secret; |  | ||||||
|                     credentials.screen_name = "@"+results.screen_name; |  | ||||||
|                     RED.nodes.addCredentials(req.params.id,credentials); |  | ||||||
|                     res.send("<html><head></head><body>Authorised - you can close this window and return to Node-RED</body></html>"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @@ -1,56 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="feedparse"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-url"><i class="fa fa-globe"></i> Feed url</label> |  | ||||||
|         <input type="text" id="node-input-url"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-interval"><i class="fa fa-repeat"></i> Repeat <span style="font-size: 0.9em;">(M)</span></label> |  | ||||||
|         <input type="text" id="node-input-interval" placeholder="minutes"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <!-- <div class="form-tips"></div> --> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="feedparse"> |  | ||||||
|     <p>Monitors an RSS/atom feed for new entries.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('feedparse',{ |  | ||||||
|         category: 'advanced-input', |  | ||||||
|         color:"#C0DEED", |  | ||||||
|         defaults: { |  | ||||||
|             name: {value:""}, |  | ||||||
|             url: {value:"", required:true}, |  | ||||||
|             interval: { value:15, required: true,validate:RED.validators.number()} |  | ||||||
|         }, |  | ||||||
|         inputs:0, |  | ||||||
|         outputs:1, |  | ||||||
|         icon: "feed.png", |  | ||||||
|         label: function() { |  | ||||||
|             return this.name||this.url; |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,79 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013,2014 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var FeedParser = require("feedparser"); |  | ||||||
|     var request = require("request"); |  | ||||||
|  |  | ||||||
|     function FeedParseNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.url = n.url; |  | ||||||
|         this.interval = (parseInt(n.interval)||15) * 60000; |  | ||||||
|         var node = this; |  | ||||||
|         this.interval_id = null; |  | ||||||
|         this.seen = {}; |  | ||||||
|         if (this.url !== "") { |  | ||||||
|             var getFeed = function() { |  | ||||||
|                 var req = request(node.url, {timeout: 10000, pool: false}); |  | ||||||
|                 //req.setMaxListeners(50); |  | ||||||
|                 //req.setHeader('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36'); |  | ||||||
|                 req.setHeader('accept', 'text/html,application/xhtml+xml'); |  | ||||||
|  |  | ||||||
|                 var feedparser = new FeedParser(); |  | ||||||
|  |  | ||||||
|                 req.on('error', function(err) { node.error(err); }); |  | ||||||
|  |  | ||||||
|                 req.on('response', function(res) { |  | ||||||
|                     if (res.statusCode != 200) { node.warn('error - Bad status code'); } |  | ||||||
|                     else { res.pipe(feedparser); } |  | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|                 feedparser.on('error', function(error) { node.error(error); }); |  | ||||||
|  |  | ||||||
|                 feedparser.on('readable', function () { |  | ||||||
|                     var stream = this, article; |  | ||||||
|                     while (article = stream.read()) { |  | ||||||
|                         if (!(article.guid in node.seen) || ( node.seen[article.guid] !== 0 && node.seen[article.guid] != article.date.getTime())) { |  | ||||||
|                             node.seen[article.guid] = article.date?article.date.getTime():0; |  | ||||||
|                             var msg = { |  | ||||||
|                                 topic: article.origlink || article.link, |  | ||||||
|                                 payload: article.description, |  | ||||||
|                                 article: article |  | ||||||
|                             }; |  | ||||||
|                             node.send(msg); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|  |  | ||||||
|                 feedparser.on('meta', function (meta) {}); |  | ||||||
|                 feedparser.on('end', function () {}); |  | ||||||
|             }; |  | ||||||
|             this.interval_id = setInterval(function() { getFeed(); }, node.interval); |  | ||||||
|             getFeed(); |  | ||||||
|         } else { |  | ||||||
|             this.error("Invalid url"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on("close", function() { |  | ||||||
|             if (this.interval_id != null) { |  | ||||||
|                 clearInterval(this.interval_id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     RED.nodes.registerType("feedparse",FeedParseNode); |  | ||||||
| } |  | ||||||
| @@ -1,203 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013,2014 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="e-mail"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-envelope"></i> To</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="email@address.com"> |  | ||||||
|     </div> |  | ||||||
|     <!-- <div class="form-row"> |  | ||||||
|         <label for="node-input-pin"><i class="fa fa-asterisk"></i> Service</label> |  | ||||||
|         <select type="text" id="node-input-pin" style="width: 150px;"> |  | ||||||
|             <option value="-" disabled> </option> |  | ||||||
|             <option value="DynectEmail">DynectEmail</option> |  | ||||||
|             <option value="Gmail">Gmail</option> |  | ||||||
|             <option value="hot.ee">hot.ee</option> |  | ||||||
|             <option value="Hotmail">Hotmail</option> |  | ||||||
|             <option value="iCloud">iCloud</option> |  | ||||||
|             <option value="mail.ee">mail.ee</option> |  | ||||||
|             <option value="Mail.Ru">Mail.Ru</option> |  | ||||||
|             <option value="Mailgun">Mailgun</option> |  | ||||||
|             <option value="Mailjet">Mailjet</option> |  | ||||||
|             <option value="Mandrill">Mandrill</option> |  | ||||||
|             <option value="Postmark">Postmark</option> |  | ||||||
|             <option value="QQ">QQ</option> |  | ||||||
|             <option value="QQex">QQex</option> |  | ||||||
|             <option value="SendGrid">SendGrid</option> |  | ||||||
|             <option value="SendCloud">SendCloud</option> |  | ||||||
|             <option value="SES">SES</option> |  | ||||||
|             <option value="Yahoo">Yahoo</option> |  | ||||||
|             <option value="yandex">yandex</option> |  | ||||||
|             <option value="Zoho">Zoho</option> |  | ||||||
|          </select> |  | ||||||
|     </div> --> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-server"><i class="fa fa-globe"></i> Server</label> |  | ||||||
|         <input type="text" id="node-input-server" placeholder="smtp.gmail.com"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-port"><i class="fa fa-random"></i> Port</label> |  | ||||||
|         <input type="text" id="node-input-port" placeholder="465"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-userid"><i class="fa fa-user"></i> Userid</label> |  | ||||||
|         <input type="text" id="node-input-userid"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-password"><i class="fa fa-lock"></i> Password</label> |  | ||||||
|         <input type="password" id="node-input-password"> |  | ||||||
|     </div> |  | ||||||
|     <br/> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-dname"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-dname" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips" id="node-tip"><b>Note:</b> Copied credentials from global emailkeys.js file.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="e-mail"> |  | ||||||
|     <p>Sends the <b>msg.payload</b> as an email, with a subject of <b>msg.topic</b>.</p> |  | ||||||
|     <p>The default message recipient can be configured in the node, if it is left |  | ||||||
|     blank it should be set using the <b>msg.to</b> property of the incoming message.</p> |  | ||||||
|     <p>The payload can be html format.</p> |  | ||||||
|     <p>If the payload is a binary buffer then it will be converted to an attachment. |  | ||||||
|     The filename should be set using <b>msg.filename</b>. Optionally <b>msg.description</b> can be added for the body text.</p> |  | ||||||
|     <p>Alternatively you may provide <b>msg.attachments</b> which should contain an array of one or |  | ||||||
|     more attachments in <a href="https://www.npmjs.com/package/nodemailer#attachments" target="_new">nodemailer</a> format.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
| (function() { |  | ||||||
|     RED.nodes.registerType('e-mail',{ |  | ||||||
|         category: 'social-output', |  | ||||||
|         color:"#c7e9c0", |  | ||||||
|         defaults: { |  | ||||||
|             server: {value:"smtp.gmail.com",required:true}, |  | ||||||
|             port: {value:"465",required:true}, |  | ||||||
|             name: {value:""}, |  | ||||||
|             dname: {value:""} |  | ||||||
|         }, |  | ||||||
|         credentials: { |  | ||||||
|             userid: {type:"text"}, |  | ||||||
|             password: {type: "password"}, |  | ||||||
|             global: { type:"boolean"} |  | ||||||
|         }, |  | ||||||
|         inputs:1, |  | ||||||
|         outputs:0, |  | ||||||
|         icon: "envelope.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             return this.dname||this.name||"email"; |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return (this.dname||!this.topic)?"node_label_italic":""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             if (this.credentials.global) { |  | ||||||
|                 $('#node-tip').show(); |  | ||||||
|             } else { |  | ||||||
|                 $('#node-tip').hide(); |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| })(); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="e-mail in"> |  | ||||||
|     <div class="form-row node-input-repeat"> |  | ||||||
|         <label for="node-input-repeat"><i class="fa fa-repeat"></i> Check Repeat (S)</label> |  | ||||||
|         <input type="text" id="node-input-repeat" placeholder="300"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-server"><i class="fa fa-globe"></i> Server</label> |  | ||||||
|         <input type="text" id="node-input-server" placeholder="imap.gmail.com"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-port"><i class="fa fa-random"></i> Port</label> |  | ||||||
|         <input type="text" id="node-input-port" placeholder="993"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-userid"><i class="fa fa-user"></i> Userid</label> |  | ||||||
|         <input type="text" id="node-input-userid"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-password"><i class="fa fa-lock"></i> Password</label> |  | ||||||
|         <input type="password" id="node-input-password"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-box"><i class="fa fa-inbox"></i> Folder</label> |  | ||||||
|         <input type="text" id="node-input-box"> |  | ||||||
|     </div> |  | ||||||
|     <br/> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips" id="node-tip"><b>Note:</b> Copied credentials from global emailkeys.js file.</div> |  | ||||||
|     <div id="node-input-tip" class="form-tips">Tip: <b>ONLY</b> retrieves the single most recent email.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="e-mail in"> |  | ||||||
|     <p>Repeatedly gets a <b>single email</b> from an IMAP server and forwards on as a msg if not already seen.</p> |  | ||||||
|     <p>The subject is loaded into <b>msg.topic</b> and <b>msg.payload</b> is the plain text body. |  | ||||||
|        If there is text/html then that is returned in <b>msg.html</b>. <b>msg.from</b> and <b>msg.date</b> are also set if you need them.</p> |  | ||||||
|     <p>Additionally <b>msg.header</b> contains the complete header object including |  | ||||||
|     <i>to</i>, <i>cc</i> and other potentially useful properties.</p> |  | ||||||
|     <p>Uses the imap module.</p> |  | ||||||
|     <p><b>Note:</b> this node <i>only</i> gets the most recent single email from the inbox, so set the repeat (polling) time appropriately.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
| (function() { |  | ||||||
|     RED.nodes.registerType('e-mail in',{ |  | ||||||
|         category: 'social-input', |  | ||||||
|         color:"#c7e9c0", |  | ||||||
|         defaults: { |  | ||||||
|             name: {value:""}, |  | ||||||
|             server: {value:"imap.gmail.com",required:true}, |  | ||||||
|             port: {value:"993",required:true}, |  | ||||||
|             box: {value:"INBOX"}, |  | ||||||
|             repeat: {value:"300",required:true} |  | ||||||
|         }, |  | ||||||
|         credentials: { |  | ||||||
|             userid: {type:"text"}, |  | ||||||
|             password: {type: "password"}, |  | ||||||
|             global: { type:"boolean"} |  | ||||||
|         }, |  | ||||||
|         inputs:0, |  | ||||||
|         outputs:1, |  | ||||||
|         icon: "envelope.png", |  | ||||||
|         label: function() { |  | ||||||
|             return this.name||"email"; |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return (this.name||!this.topic)?"node_label_italic":""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             if (this.credentials.global) { |  | ||||||
|                 $('#node-tip').show(); |  | ||||||
|             } else { |  | ||||||
|                 $('#node-tip').hide(); |  | ||||||
|             }; |  | ||||||
|             if (typeof this.box === 'undefined') { |  | ||||||
|                 $("#node-input-box").val("INBOX"); |  | ||||||
|                 this.box = "INBOX"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| })(); |  | ||||||
| </script> |  | ||||||
| @@ -1,283 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013,2014 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var nodemailer = require("nodemailer"); |  | ||||||
|     var Imap = require('imap'); |  | ||||||
|  |  | ||||||
|     //console.log(nodemailer.Transport.transports.SMTP.wellKnownHosts); |  | ||||||
|  |  | ||||||
|     try { |  | ||||||
|         var globalkeys = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js"); |  | ||||||
|     } catch(err) { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function EmailNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.topic = n.topic; |  | ||||||
|         this.name = n.name; |  | ||||||
|         this.outserver = n.server; |  | ||||||
|         this.outport = n.port; |  | ||||||
|         var flag = false; |  | ||||||
|         if (this.credentials && this.credentials.hasOwnProperty("userid")) { |  | ||||||
|             this.userid = this.credentials.userid; |  | ||||||
|         } else { |  | ||||||
|             if (globalkeys) { |  | ||||||
|                 this.userid = globalkeys.user; |  | ||||||
|                 flag = true; |  | ||||||
|             } else { |  | ||||||
|                 this.error("No e-mail userid set"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (this.credentials && this.credentials.hasOwnProperty("password")) { |  | ||||||
|             this.password = this.credentials.password; |  | ||||||
|         } else { |  | ||||||
|             if (globalkeys) { |  | ||||||
|                 this.password = globalkeys.pass; |  | ||||||
|                 flag = true; |  | ||||||
|             } else { |  | ||||||
|                 this.error("No e-mail password set"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (flag) { |  | ||||||
|             RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true}); |  | ||||||
|         } |  | ||||||
|         var node = this; |  | ||||||
|  |  | ||||||
|         var smtpTransport = nodemailer.createTransport({ |  | ||||||
|             host: node.outserver, |  | ||||||
|             port: node.outport, |  | ||||||
|             secure: true, |  | ||||||
|             auth: { |  | ||||||
|                 user: node.userid, |  | ||||||
|                 pass: node.password |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.on("input", function(msg) { |  | ||||||
|             if (msg.hasOwnProperty("payload")) { |  | ||||||
|                 if (smtpTransport) { |  | ||||||
|                     node.status({fill:"blue",shape:"dot",text:"sending"}); |  | ||||||
|                     if (msg.to && node.name && (msg.to !== node.name)) { |  | ||||||
|                         node.warn("Warning: msg properties can no longer override set node properties. See bit.ly/nr-override-msg-props"); |  | ||||||
|                     } |  | ||||||
|                     var sendopts = { from: node.userid };   // sender address |  | ||||||
|                     sendopts.to = node.name || msg.to; // comma separated list of addressees |  | ||||||
|                     sendopts.subject = msg.topic || msg.title || "Message from Node-RED"; // subject line |  | ||||||
|                     if (Buffer.isBuffer(msg.payload)) { // if it's a buffer in the payload then auto create an attachment instead |  | ||||||
|                         if (!msg.filename) { |  | ||||||
|                             var fe = "bin"; |  | ||||||
|                             if ((msg.payload[0] === 0xFF)&&(msg.payload[1] === 0xD8)) { fe = "jpg"; } |  | ||||||
|                             if ((msg.payload[0] === 0x47)&&(msg.payload[1] === 0x49)) { fe = "gif"; } //46 |  | ||||||
|                             if ((msg.payload[0] === 0x42)&&(msg.payload[1] === 0x4D)) { fe = "bmp"; } |  | ||||||
|                             if ((msg.payload[0] === 0x89)&&(msg.payload[1] === 0x50)) { fe = "png"; } //4E |  | ||||||
|                             msg.filename = "attachment."+fe; |  | ||||||
|                         } |  | ||||||
|                         sendopts.attachments = [ { content: msg.payload, filename:(msg.filename.replace(/^.*[\\\/]/, '') || "file.bin") } ]; |  | ||||||
|                         if (msg.hasOwnProperty("headers") && msg.headers.hasOwnProperty("content-type")) { |  | ||||||
|                             sendopts.attachments[0].contentType = msg.headers["content-type"]; |  | ||||||
|                         } |  | ||||||
|                         // Create some body text.. |  | ||||||
|                         sendopts.text = "Your file from Node-RED is attached : "+(msg.filename.replace(/^.*[\\\/]/, '') || "file.bin")+ (msg.hasOwnProperty("description") ? "\n\n"+msg.description : ""); |  | ||||||
|                     } |  | ||||||
|                     else { |  | ||||||
|                         var payload = RED.util.ensureString(msg.payload); |  | ||||||
|                         sendopts.text =  payload; // plaintext body |  | ||||||
|                         if (/<[a-z][\s\S]*>/i.test(payload)) { sendopts.html = payload; } // html body |  | ||||||
|                         if (msg.attachments) { sendopts.attachments = msg.attachments; } // add attachments |  | ||||||
|                     } |  | ||||||
|                     smtpTransport.sendMail(sendopts, function(error, info) { |  | ||||||
|                         if (error) { |  | ||||||
|                             node.error(error,msg); |  | ||||||
|                             node.status({fill:"red",shape:"ring",text:"send failed"}); |  | ||||||
|                         } else { |  | ||||||
|                             node.log("Message sent: " + info.response); |  | ||||||
|                             node.status({}); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|                 else { node.warn("No Email credentials found. See info panel."); } |  | ||||||
|             } |  | ||||||
|             else { node.warn("No payload to send"); } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("e-mail",EmailNode,{ |  | ||||||
|         credentials: { |  | ||||||
|             userid: {type:"text"}, |  | ||||||
|             password: {type: "password"}, |  | ||||||
|             global: { type:"boolean"} |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     function EmailInNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.name = n.name; |  | ||||||
|         this.repeat = n.repeat * 1000 || 300000; |  | ||||||
|         this.inserver = n.server || globalkeys.server || "imap.gmail.com"; |  | ||||||
|         this.inport = n.port || globalkeys.port || "993"; |  | ||||||
|         this.box = n.box || "INBOX"; |  | ||||||
|         var flag = false; |  | ||||||
|  |  | ||||||
|         if (this.credentials && this.credentials.hasOwnProperty("userid")) { |  | ||||||
|             this.userid = this.credentials.userid; |  | ||||||
|         } else { |  | ||||||
|             if (globalkeys) { |  | ||||||
|                 this.userid = globalkeys.user; |  | ||||||
|                 flag = true; |  | ||||||
|             } else { |  | ||||||
|                 this.error("No e-mail userid set"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (this.credentials && this.credentials.hasOwnProperty("password")) { |  | ||||||
|             this.password = this.credentials.password; |  | ||||||
|         } else { |  | ||||||
|             if (globalkeys) { |  | ||||||
|                 this.password = globalkeys.pass; |  | ||||||
|                 flag = true; |  | ||||||
|             } else { |  | ||||||
|                 this.error("No e-mail password set"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (flag) { |  | ||||||
|             RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true}); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         var node = this; |  | ||||||
|         this.interval_id = null; |  | ||||||
|         var oldmail = {}; |  | ||||||
|  |  | ||||||
|         var imap = new Imap({ |  | ||||||
|             user: node.userid, |  | ||||||
|             password: node.password, |  | ||||||
|             host: node.inserver, |  | ||||||
|             port: node.inport, |  | ||||||
|             tls: true, |  | ||||||
|             tlsOptions: { rejectUnauthorized: false }, |  | ||||||
|             connTimeout: node.repeat, |  | ||||||
|             authTimeout: node.repeat |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         if (!isNaN(this.repeat) && this.repeat > 0) { |  | ||||||
|             node.log("repeat = "+this.repeat); |  | ||||||
|             this.interval_id = setInterval( function() { |  | ||||||
|                 node.emit("input",{}); |  | ||||||
|             }, this.repeat ); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on("input", function(msg) { |  | ||||||
|             imap.once('ready', function() { |  | ||||||
|                 node.status({fill:"blue",shape:"dot",text:"fetching"}); |  | ||||||
|                 var pay = {}; |  | ||||||
|                 imap.openBox(node.box, false, function(err, box) { |  | ||||||
|                     if (err) { |  | ||||||
|                         node.status({fill:"red",shape:"ring",text:"fetch folder error"}); |  | ||||||
|                         node.error("Failed to fetch folder "+node.box,err); |  | ||||||
|                     } |  | ||||||
|                     else { |  | ||||||
|                         if (box.messages.total > 0) { |  | ||||||
|                             //var f = imap.seq.fetch(box.messages.total + ':*', { markSeen:true, bodies: ['HEADER.FIELDS (FROM SUBJECT DATE TO CC BCC)','TEXT'] }); |  | ||||||
|                             var f = imap.seq.fetch(box.messages.total + ':*', { markSeen:true, bodies: ['HEADER','TEXT'] }); |  | ||||||
|                             f.on('message', function(msg, seqno) { |  | ||||||
|                                 node.log('message: #'+ seqno); |  | ||||||
|                                 var prefix = '(#' + seqno + ') '; |  | ||||||
|                                 msg.on('body', function(stream, info) { |  | ||||||
|                                     var buffer = ''; |  | ||||||
|                                     stream.on('data', function(chunk) { |  | ||||||
|                                         buffer += chunk.toString('utf8'); |  | ||||||
|                                     }); |  | ||||||
|                                     stream.on('end', function() { |  | ||||||
|                                         if (info.which !== 'TEXT') { |  | ||||||
|                                             var head = Imap.parseHeader(buffer); |  | ||||||
|                                             pay.from = head.from[0]; |  | ||||||
|                                             pay.topic = head.subject[0]; |  | ||||||
|                                             pay.date = head.date[0]; |  | ||||||
|                                             pay.header = head; |  | ||||||
|                                         } else { |  | ||||||
|                                             var parts = buffer.split("Content-Type"); |  | ||||||
|                                             for (var p = 0; p < parts.length; p++) { |  | ||||||
|                                                 if (parts[p].indexOf("text/plain") >= 0) { |  | ||||||
|                                                     pay.payload = parts[p].split("\n").slice(1,-2).join("\n").trim(); |  | ||||||
|                                                 } |  | ||||||
|                                                 else if (parts[p].indexOf("text/html") >= 0) { |  | ||||||
|                                                     pay.html = parts[p].split("\n").slice(1,-2).join("\n").trim(); |  | ||||||
|                                                 } else { |  | ||||||
|                                                     pay.payload = parts[0]; |  | ||||||
|                                                 } |  | ||||||
|                                             } |  | ||||||
|                                             //pay.body = buffer; |  | ||||||
|                                         } |  | ||||||
|                                     }); |  | ||||||
|                                 }); |  | ||||||
|                                 msg.on('end', function() { |  | ||||||
|                                     //node.log('Finished: '+prefix); |  | ||||||
|                                 }); |  | ||||||
|                             }); |  | ||||||
|                             f.on('error', function(err) { |  | ||||||
|                                 node.warn('fetch message error: ' + err); |  | ||||||
|                                 node.status({fill:"red",shape:"ring",text:"fetch message error"}); |  | ||||||
|                             }); |  | ||||||
|                             f.on('end', function() { |  | ||||||
|                                 delete(pay._msgid); |  | ||||||
|                                 if (JSON.stringify(pay) !== oldmail) { |  | ||||||
|                                     oldmail = JSON.stringify(pay); |  | ||||||
|                                     node.send(pay); |  | ||||||
|                                     node.log('received new email: '+pay.topic); |  | ||||||
|                                 } |  | ||||||
|                                 else { node.log('duplicate not sent: '+pay.topic); } |  | ||||||
|                                 //node.status({fill:"green",shape:"dot",text:"ok"}); |  | ||||||
|                                 node.status({}); |  | ||||||
|                             }); |  | ||||||
|                         } |  | ||||||
|                         else { |  | ||||||
|                             node.log("you have achieved inbox zero"); |  | ||||||
|                             //node.status({fill:"green",shape:"dot",text:"ok"}); |  | ||||||
|                             node.status({}); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     imap.end(); |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|             node.status({fill:"grey",shape:"dot",text:"connecting"}); |  | ||||||
|             imap.connect(); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         imap.on('error', function(err) { |  | ||||||
|             node.log(err); |  | ||||||
|             node.status({fill:"red",shape:"ring",text:"connect error"}); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.on("error", function(err) { |  | ||||||
|             node.log("error: ",err); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.on("close", function() { |  | ||||||
|             if (this.interval_id != null) { |  | ||||||
|                 clearInterval(this.interval_id); |  | ||||||
|             } |  | ||||||
|             if (imap) { imap.destroy(); } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         node.emit("input",{}); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("e-mail in",EmailInNode,{ |  | ||||||
|         credentials: { |  | ||||||
|             userid: {type:"text"}, |  | ||||||
|             password: {type: "password"}, |  | ||||||
|             global: { type:"boolean"} |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| }; |  | ||||||
| @@ -1,235 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013,2014 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="irc in"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-ircserver"><i class="fa fa-globe"></i> IRC Server</label> |  | ||||||
|         <input type="text" id="node-input-ircserver"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-channel"><i class="fa fa-random"></i> Channel</label> |  | ||||||
|         <input type="text" id="node-input-channel" placeholder="#nodered"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips">The channel to join must start with a # (as per normal irc rules...)<br/> |  | ||||||
|     You may join multiple channels by comma separating a list - #chan1,#chan2,etc.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="irc in"> |  | ||||||
|     <p>Connects to a channel on an IRC server.</p> |  | ||||||
|     <p>You may join multiple channels by comma separating a list - #chan1,#chan2,#etc.</p> |  | ||||||
|     <p>Any messages on that channel will appear on the <code>msg.payload</code> at the output, |  | ||||||
|     while <code>msg.topic</code> will contain who it is from. |  | ||||||
|     <code>msg.to</code> contains either the name of the channel or PRIV in the case of a pm.</p> |  | ||||||
|     <p>The second output provides a <code>msg.payload</code> that has any status messages such as joins, parts, kicks etc.</p> |  | ||||||
|     <p>The type of the status message is set as <code>msg.payload.type</code>.</p> |  | ||||||
|     <p>The possible status types are: <br /> |  | ||||||
|     <table border="1" cellpadding="1" cellspacing="1"> |  | ||||||
|     <thead> |  | ||||||
|         <tr> |  | ||||||
|             <th scope="col">Type</th> |  | ||||||
|             <th scope="col">Description</th> |  | ||||||
|         </tr> |  | ||||||
|     </thead> |  | ||||||
|     <tbody> |  | ||||||
|         <tr> |  | ||||||
|             <td>message</td> |  | ||||||
|             <td>message is sent into the channel</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>pm</td> |  | ||||||
|             <td>private message to the bot</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>join</td> |  | ||||||
|             <td>a user joined the channel (also triggered when the bot joins a channel)</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>invite</td> |  | ||||||
|             <td>the bot is being invited to a channel</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>part</td> |  | ||||||
|             <td>a user leaves a channel</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>quit</td> |  | ||||||
|             <td>a user quits a channel</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>kick</td> |  | ||||||
|             <td>a user is kicked from a channel</td> |  | ||||||
|         </tr> |  | ||||||
|         <tr> |  | ||||||
|             <td>names</td> |  | ||||||
|             <td>retrieves the list of users when the bot joins a channel</td> |  | ||||||
|         </tr> |  | ||||||
|     </tbody> |  | ||||||
| </table> |  | ||||||
| </p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('irc in',{ |  | ||||||
|         category: 'social-input', |  | ||||||
|         defaults: { |  | ||||||
|             name: {value:""}, |  | ||||||
|             ircserver: {type:"irc-server", required:true}, |  | ||||||
|             channel: {value:"",required:true,validate:RED.validators.regex(/^#/)} |  | ||||||
|         }, |  | ||||||
|         color:"Silver", |  | ||||||
|         inputs:0, |  | ||||||
|         outputs:2, |  | ||||||
|         icon: "hash.png", |  | ||||||
|         label: function() { |  | ||||||
|             var ircNode = RED.nodes.node(this.ircserver); |  | ||||||
|             return this.name || (ircNode ? ircNode.label() : "irc"); |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             if ((this.ircserver !== undefined) && (this.ircserver !== "")) { |  | ||||||
|                 this.channel = this.channel || RED.nodes.node(this.ircserver).channel; |  | ||||||
|                 $("#node-input-channel").val(this.channel); |  | ||||||
|             } |  | ||||||
|             else { this.channel = this.channel; } |  | ||||||
|             $("#node-input-channel").val(this.channel); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="irc out"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-ircserver"><i class="fa fa-globe"></i> IRC Server</label> |  | ||||||
|         <input type="text" id="node-input-ircserver"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-channel"><i class="fa fa-random"></i> Channel</label> |  | ||||||
|         <input type="text" id="node-input-channel" placeholder="#nodered"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-sendObject"><i class="fa fa-arrows"></i> Action</label> |  | ||||||
|         <select type="text" id="node-input-sendObject" style="display: inline-block; vertical-align: middle; width:70%;"> |  | ||||||
|             <option value="pay">Send payload to channel(s)</option> |  | ||||||
|             <option value="true">Use msg.topic to set nickname or channel(s)</option> |  | ||||||
|             <option value="false">Send complete msg object to channel(s)</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips">The channel to join must start with a # (as per normal irc rules...)<br/> |  | ||||||
|     Sending the complete object will stringify the whole msg object before sending.</div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="irc out"> |  | ||||||
|     <p>Sends messages to a channel on an IRC server</p> |  | ||||||
|     <p>You can send just the <code>msg.payload</code>, or the complete <code>msg</code> object to the selected channel, |  | ||||||
|     or you can select to use <code>msg.topic</code> to send the <code>msg.payload</code> to a specific user (private message) or channel.</p> |  | ||||||
|     <p>If multiple output channels are listed (eg. #chan1,#chan2), then the message will be sent to all of them.</p> |  | ||||||
|     <p><b>Note:</b> you can only send to channels you have previously joined so they MUST be specified in the node - even if you then decide to use a subset in msg.topic</p> |  | ||||||
|     <p>You may send RAW commands using <code>msg.raw</code> - This must contain an array of parameters - eg. <pre>["privmsg","#nodered","Hello world"]</pre></p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('irc out',{ |  | ||||||
|         category: 'social-output', |  | ||||||
|         defaults: { |  | ||||||
|             name: {value:""}, |  | ||||||
|             sendObject: {value:"pay", required:true}, |  | ||||||
|             ircserver: {type:"irc-server", required:true}, |  | ||||||
|             channel: {value:"",required:true,validate:RED.validators.regex(/^#/)} |  | ||||||
|         }, |  | ||||||
|         color:"Silver", |  | ||||||
|         inputs:1, |  | ||||||
|         outputs:0, |  | ||||||
|         icon: "hash.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             return this.name || (this.ircserver ? RED.nodes.node(this.ircserver).label() : "irc"); |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name?"node_label_italic":""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             if ((this.ircserver !== undefined) && (this.ircserver !== "")) { |  | ||||||
|                 this.channel = this.channel || RED.nodes.node(this.ircserver).channel; |  | ||||||
|                 $("#node-input-channel").val(this.channel); |  | ||||||
|             } |  | ||||||
|             else { this.channel = this.channel; } |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="irc-server"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-server"><i class="fa fa-globe"></i> IRC Server</label> |  | ||||||
|         <input type="text" id="node-config-input-server" placeholder="irc.freenode.net" style="width: 45%;" > |  | ||||||
|         <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label> |  | ||||||
|         <input type="text" id="node-config-input-port" placeholder="Port" style="width:45px"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label> </label> |  | ||||||
|         <input type="checkbox" id="node-config-input-ssl" style="display: inline-block; width: auto; vertical-align: top;"> |  | ||||||
|         <label for="node-config-input-ssl" style="width: 70%;">Use Secure SSL connection ?</label> |  | ||||||
|     </div> |  | ||||||
|         <div class="form-row" id="certrow"> |  | ||||||
|         <label> </label> |  | ||||||
|         <input type="checkbox" id="node-config-input-cert" style="display: inline-block; width: auto; vertical-align: top;"> |  | ||||||
|         <label for="node-config-input-cert" style="width: 70%;">Allow self-signed certificates ?</label> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-nickname"><i class="fa fa-user"></i> Nickname</label> |  | ||||||
|         <input type="text" id="node-config-input-nickname" placeholder="joe123"> |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('irc-server',{ |  | ||||||
|         category: 'config', |  | ||||||
|         defaults: { |  | ||||||
|             server: {value:"",required:true}, |  | ||||||
|             port: {value:"6667"}, |  | ||||||
|             ssl: {value:false}, |  | ||||||
|             cert: {value:false}, |  | ||||||
|             nickname: {value:"",required:true} |  | ||||||
|         }, |  | ||||||
|         label: function() { |  | ||||||
|             return this.server; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             $("#node-config-input-ssl").change(function() { |  | ||||||
|                 if ($("#node-config-input-ssl").is(":checked")) { |  | ||||||
|                     $("#certrow").show(); |  | ||||||
|                 } |  | ||||||
|                 else { |  | ||||||
|                     $("#certrow").hide(); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         }, |  | ||||||
|         oneditsave: function() { |  | ||||||
|             this.ssl = $("#node-config-input-ssl").is(":checked"); |  | ||||||
|             this.cert = $("#node-config-input-cert").is(":checked"); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,282 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013,2014 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var irc = require("irc"); |  | ||||||
|  |  | ||||||
|     // The Server Definition - this opens (and closes) the connection |  | ||||||
|     function IRCServerNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.server = n.server; |  | ||||||
|         this.port = n.port || 6667; |  | ||||||
|         this.ssl = n.ssl || false; |  | ||||||
|         this.cert = n.cert || false; |  | ||||||
|         this.channel = n.channel; |  | ||||||
|         this.nickname = n.nickname; |  | ||||||
|         this.lastseen = 0; |  | ||||||
|         this.ircclient = null; |  | ||||||
|         this.on("close", function() { |  | ||||||
|             if (this.ircclient != null) { |  | ||||||
|                 this.ircclient.removeAllListeners(); |  | ||||||
|                 this.ircclient.disconnect(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("irc-server",IRCServerNode); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // The Input Node |  | ||||||
|     function IrcInNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.ircserver = n.ircserver; |  | ||||||
|         this.serverConfig = RED.nodes.getNode(this.ircserver); |  | ||||||
|         this.channel = n.channel || this.serverConfig.channel; |  | ||||||
|         var node = this; |  | ||||||
|         if (node.serverConfig.ircclient === null) { |  | ||||||
|             node.log("CONNECT: "+node.serverConfig.server); |  | ||||||
|             node.status({fill:"grey",shape:"dot",text:"connecting"}); |  | ||||||
|             var options = {autoConnect:true,autoRejoin:false,floodProtection:true,secure:node.serverConfig.ssl,selfSigned:node.serverConfig.cert,port:node.serverConfig.port,retryDelay:20000}; |  | ||||||
|             node.serverConfig.ircclient = new irc.Client(node.serverConfig.server, node.serverConfig.nickname, options); |  | ||||||
|             node.serverConfig.ircclient.setMaxListeners(0); |  | ||||||
|             node.serverConfig.ircclient.addListener('error', function(message) { |  | ||||||
|                 if (RED.settings.verbose) { node.log("ERR: "+JSON.stringify(message)); } |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('netError', function(message) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("NET: "+JSON.stringify(message)); } |  | ||||||
|                 node.status({fill:"red",shape:"ring",text:"net error"}); |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('connect', function() { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("CONNECTED "); } |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('registered', function(message) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 node.log(node.serverConfig.ircclient.nick+" ONLINE: "+message.server); |  | ||||||
|                 node.status({fill:"yellow",shape:"dot",text:"connected"}); |  | ||||||
|                 node.serverConfig.ircclient.join( node.channel, function(data) { |  | ||||||
|                     node.log(data+" JOINED: "+node.channel); |  | ||||||
|                     node.status({fill:"green",shape:"dot",text:"joined"}); |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('ping', function(server) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("PING from "+JSON.stringify(server)); } |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"ok"}); |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('quit', function(nick, reason, channels, message) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("QUIT: "+nick+" "+reason+" "+channels+" "+JSON.stringify(message)); } |  | ||||||
|                 node.status({fill:"grey",shape:"ring",text:"quit"}); |  | ||||||
|                 //node.serverConfig.ircclient.disconnect( function() { |  | ||||||
|                 //    node.serverConfig.ircclient.connect(); |  | ||||||
|                 //}); |  | ||||||
|                 //if (RED.settings.verbose) { node.log("restart"); }          // then retry |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('raw', function (message) { // any message received means we are alive |  | ||||||
|                 //console.log("RAW:"+JSON.stringify(message)); |  | ||||||
|                 if (message.commandType === "reply") { |  | ||||||
|                     //console.log("RAW:"+JSON.stringify(message)); |  | ||||||
|                     node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             node.recon = setInterval( function() { |  | ||||||
|                 //console.log("CHK ",(Date.now()-node.serverConfig.lastseen)/1000); |  | ||||||
|                 if ((Date.now()-node.serverConfig.lastseen) > 240000) {     // if more than 4 mins since last seen |  | ||||||
|                     node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]);     // request time to check link |  | ||||||
|                 } |  | ||||||
|                 if ((Date.now()-node.serverConfig.lastseen) > 300000) {     // If more than 5 mins |  | ||||||
|                     //node.serverConfig.ircclient.disconnect(); |  | ||||||
|                     //node.serverConfig.ircclient.connect(); |  | ||||||
|                     node.status({fill:"grey",shape:"ring",text:"no connection"}); |  | ||||||
|                     if (RED.settings.verbose) { node.log("CONNECTION LOST ?"); } |  | ||||||
|                 } |  | ||||||
|                 //node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]); // request time to check link |  | ||||||
|             }, 60000); // check every 1 min |  | ||||||
|             //node.serverConfig.ircclient.connect(); |  | ||||||
|         } |  | ||||||
|         else { node.status({text:""}); } |  | ||||||
|         node.ircclient = node.serverConfig.ircclient; |  | ||||||
|  |  | ||||||
|         node.ircclient.addListener('registered', function(message) { |  | ||||||
|             //node.log(node.ircclient.nick+" ONLINE"); |  | ||||||
|             node.status({fill:"yellow",shape:"dot",text:"connected"}); |  | ||||||
|             node.ircclient.join( node.channel, function(data) { |  | ||||||
|                 // node.log(data+" JOINED "+node.channel); |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"joined"}); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('message', function (from, to, message) { |  | ||||||
|             //node.log(from + ' => ' + to + ' : ' + message); |  | ||||||
|             if (~node.channel.toLowerCase().indexOf(to.toLowerCase())) { |  | ||||||
|                 var msg = { "topic":from, "from":from, "to":to, "payload":message }; |  | ||||||
|                 node.send([msg,null]); |  | ||||||
|             } |  | ||||||
|             //else { console.log(node.channel,to); } |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('pm', function(from, message) { |  | ||||||
|             //node.log("PM => "+from + ': ' + message); |  | ||||||
|             var msg = { "topic":from, "from":from, "to":"PRIV", "payload":message }; |  | ||||||
|             node.send([msg,null]); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('join', function(channel, who) { |  | ||||||
|             var msg = { "payload": { "type":"join", "who":who, "channel":channel } }; |  | ||||||
|             node.send([null,msg]); |  | ||||||
|             //node.log(who+' has joined '+channel); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('invite', function(channel, from, message) { |  | ||||||
|             var msg = { "payload": { "type":"invite", "who":from, "channel":channel, "message":message } }; |  | ||||||
|             node.send([null,msg]); |  | ||||||
|             //node.log(from+' sent invite to '+channel+': '+message); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('part', function(channel, who, reason) { |  | ||||||
|             var msg = { "payload": { "type":"part", "who":who, "channel":channel, "reason":reason } }; |  | ||||||
|             node.send([null,msg]); |  | ||||||
|             //node.log(who+' has left '+channel+': '+reason); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('quit', function(nick, reason, channels, message) { |  | ||||||
|             var msg = { "payload": { "type":"quit", "who":nick, "channel":channels, "reason":reason } }; |  | ||||||
|             node.send([null,msg]); |  | ||||||
|             //node.log(nick+' has quit '+channels+': '+reason); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('kick', function(channel, who, by, reason) { |  | ||||||
|             var msg = { "payload": { "type":"kick", "who":who, "channel":channel, "by":by, "reason":reason } }; |  | ||||||
|             node.send([null,msg]); |  | ||||||
|             //node.log(who+' was kicked from '+channel+' by '+by+': '+reason); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('names', function (channel, nicks) { |  | ||||||
|             var msg = { "payload": { "type": "names", "channel": channel, "names": nicks} }; |  | ||||||
|             node.send([null, msg]); |  | ||||||
|         }); |  | ||||||
|         node.ircclient.addListener('raw', function (message) { // any message means we are alive |  | ||||||
|             node.serverConfig.lastseen = Date.now(); |  | ||||||
|         }); |  | ||||||
|         node.on("close", function() { |  | ||||||
|             node.ircclient.removeAllListeners(); |  | ||||||
|             if (node.recon) { clearInterval(node.recon); } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("irc in",IrcInNode); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // The Output Node |  | ||||||
|     function IrcOutNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.sendFlag = n.sendObject; |  | ||||||
|         this.ircserver = n.ircserver; |  | ||||||
|         this.serverConfig = RED.nodes.getNode(this.ircserver); |  | ||||||
|         this.channel = n.channel || this.serverConfig.channel; |  | ||||||
|         var node = this; |  | ||||||
|         if (node.serverConfig.ircclient === null) { |  | ||||||
|             node.log("CONNECT: "+node.serverConfig.server); |  | ||||||
|             node.status({fill:"grey",shape:"dot",text:"connecting"}); |  | ||||||
|             var options = {autoConnect:true,autoRejoin:false,floodProtection:true,secure:node.serverConfig.ssl,selfSigned:node.serverConfig.cert,port:node.serverConfig.port,retryDelay:20000}; |  | ||||||
|             node.serverConfig.ircclient = new irc.Client(node.serverConfig.server, node.serverConfig.nickname, options); |  | ||||||
|             node.serverConfig.ircclient.setMaxListeners(0); |  | ||||||
|             node.serverConfig.ircclient.addListener('error', function(message) { |  | ||||||
|                 if (RED.settings.verbose) { node.log("ERR: "+JSON.stringify(message)); } |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('netError', function(message) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("NET: "+JSON.stringify(message)); } |  | ||||||
|                 node.status({fill:"red",shape:"ring",text:"net error"}); |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('connect', function() { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("CONNECTED "); } |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('registered', function(message) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 node.log(node.serverConfig.ircclient.nick+" ONLINE: "+message.server); |  | ||||||
|                 node.status({fill:"yellow",shape:"dot",text:"connected"}); |  | ||||||
|                 node.serverConfig.ircclient.join( node.channel, function(data) { |  | ||||||
|                     node.log(data+" JOINED: "+node.channel); |  | ||||||
|                     node.status({fill:"green",shape:"dot",text:"joined"}); |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('ping', function(server) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("PING from "+JSON.stringify(server)); } |  | ||||||
|                 node.status({fill:"green",shape:"dot",text:"ok"}); |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('quit', function(nick, reason, channels, message) { |  | ||||||
|                 node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 if (RED.settings.verbose) { node.log("QUIT: "+nick+" "+reason+" "+channels+" "+JSON.stringify(message)); } |  | ||||||
|                 node.status({fill:"grey",shape:"ring",text:"quit"}); |  | ||||||
|                 //node.serverConfig.ircclient.disconnect( function() { |  | ||||||
|                 //    node.serverConfig.ircclient.connect(); |  | ||||||
|                 //}); |  | ||||||
|                 //if (RED.settings.verbose) { node.log("restart"); }          // then retry |  | ||||||
|             }); |  | ||||||
|             node.serverConfig.ircclient.addListener('raw', function (message) { // any message received means we are alive |  | ||||||
|                 //console.log("RAW:"+JSON.stringify(message)); |  | ||||||
|                 if (message.commandType === "reply") { |  | ||||||
|                     //console.log("RAW:"+JSON.stringify(message)); |  | ||||||
|                     node.serverConfig.lastseen = Date.now(); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             node.recon = setInterval( function() { |  | ||||||
|                 //console.log("CHK ",(Date.now()-node.serverConfig.lastseen)/1000); |  | ||||||
|                 if ((Date.now()-node.serverConfig.lastseen) > 240000) {     // if more than 4 mins since last seen |  | ||||||
|                     node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]);     // request time to check link |  | ||||||
|                 } |  | ||||||
|                 if ((Date.now()-node.serverConfig.lastseen) > 300000) {     // If more than 5 mins |  | ||||||
|                     //node.serverConfig.ircclient.disconnect(); |  | ||||||
|                     //node.serverConfig.ircclient.connect(); |  | ||||||
|                     node.status({fill:"grey",shape:"ring",text:"no connection"}); |  | ||||||
|                     if (RED.settings.verbose) { node.log("CONNECTION LOST ?"); } |  | ||||||
|                 } |  | ||||||
|                 //node.serverConfig.ircclient.send.apply(node.serverConfig.ircclient,["TIME"]); // request time to check link |  | ||||||
|             }, 60000); // check every 1 min |  | ||||||
|             //node.serverConfig.ircclient.connect(); |  | ||||||
|         } |  | ||||||
|         else { node.status({text:""}); } |  | ||||||
|         node.ircclient = node.serverConfig.ircclient; |  | ||||||
|  |  | ||||||
|         node.on("input", function(msg) { |  | ||||||
|             if (Object.prototype.toString.call( msg.raw ) === '[object Array]') { |  | ||||||
|                 if (RED.settings.verbose) { node.log("RAW command:"+msg.raw); } |  | ||||||
|                 node.ircclient.send.apply(node.ircclient,msg.raw); |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 if (msg._topic) { delete msg._topic; } |  | ||||||
|                 var ch = node.channel.split(","); // split on , so we can send to multiple |  | ||||||
|                 if (node.sendFlag == "true") { // override channels with msg.topic |  | ||||||
|                     if ((msg.hasOwnProperty('topic'))&&(typeof msg.topic === "string")) { |  | ||||||
|                         ch = msg.topic.split(","); // split on , so we can send to multiple |  | ||||||
|                     } |  | ||||||
|                     else { node.warn("msg.topic not set"); } |  | ||||||
|                 } |  | ||||||
|                 for (var c = 0; c < ch.length; c++) { |  | ||||||
|                     if (node.sendFlag == "false") { // send whole message object to each channel |  | ||||||
|                         node.ircclient.say(ch[c], JSON.stringify(msg)); |  | ||||||
|                     } |  | ||||||
|                     else { // send just the payload to each channel |  | ||||||
|                         if (typeof msg.payload === "object") { msg.payload = JSON.stringify(msg.payload); } |  | ||||||
|                         node.ircclient.say(ch[c], msg.payload); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         node.on("close", function() { |  | ||||||
|             node.ircclient.removeAllListeners(); |  | ||||||
|             if (node.recon) { clearInterval(node.recon); } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("irc out",IrcOutNode); |  | ||||||
| } |  | ||||||
| @@ -1,105 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="redis out"> |  | ||||||
|     <div class="form-row node-input-hostname"> |  | ||||||
|         <label for="node-input-hostname"><i class="fa fa-bookmark"></i> Host</label> |  | ||||||
|         <input class="input-append-left" type="text" id="node-input-hostname" placeholder="127.0.0.1" style="width: 40%;" ><button id="node-input-hostname-lookup" class="btn input-append-right"><span class="caret"></span></button> |  | ||||||
|         <label for="node-input-port" style="margin-left: 10px; width: 35px; "> Port</label> |  | ||||||
|         <input type="text" id="node-input-port" placeholder="6379" style="width:45px"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-key"><i class="fa fa-key"></i> Key</label> |  | ||||||
|         <input type="text" id="node-input-key" placeholder="Redis Key"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-type"><i class="fa fa-th"></i> Type</label> |  | ||||||
|         <select type="text" id="node-input-structtype" style="width: 150px;"> |  | ||||||
|         <option value="string">String</option> |  | ||||||
|         <option value="hash">Hash</option> |  | ||||||
|         <option value="set">Set</option> |  | ||||||
|         <option value="list">List</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips"> |  | ||||||
|         If key is blank, the topic will be used as the key.<br> |  | ||||||
|         If type is hash, payload should be an object or field=value string. |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="redis out"> |  | ||||||
|     <p>A Redis output node. Options include Hash, Set, List and String.</p> |  | ||||||
|     <p>To run this you need a local Redis server running. For details see <a href="http://redis.io/" target="_new">the Redis site</a>.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('redis out',{ |  | ||||||
|         category: 'storage-output', |  | ||||||
|         color:"#ffaaaa", |  | ||||||
|         defaults: { |  | ||||||
|             hostname: { value:"127.0.0.1",required:true}, |  | ||||||
|             port: { value: 6379,required:true}, |  | ||||||
|             name: {value:""}, |  | ||||||
|             key: {value:""}, |  | ||||||
|             structtype: {value:"",required:true} |  | ||||||
|         }, |  | ||||||
|         inputs:1, |  | ||||||
|         outputs:0, |  | ||||||
|         icon: "redis.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             return this.name||this.key+" ("+this.structtype+")"; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: function() { |  | ||||||
|             var availableServers = []; |  | ||||||
|             var matchedServers = {}; |  | ||||||
|             RED.nodes.eachNode(function(node) { |  | ||||||
|                 if (node.type == "redis out" && node.hostname && node.port && !matchedServers[node.hostname+":"+node.port]) { |  | ||||||
|                     var label = node.hostname+":"+node.port; |  | ||||||
|                     matchedServers[label] = true; |  | ||||||
|                     availableServers.push({ |  | ||||||
|                         label:label, |  | ||||||
|                         value:node.hostname, |  | ||||||
|                         port:node.port |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             $( "#node-input-hostname" ).autocomplete({ |  | ||||||
|                 minLength: 0, |  | ||||||
|                 source: availableServers, |  | ||||||
|                 select: function( event, ui ) { |  | ||||||
|                     $("#node-input-port").val(ui.item.port); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|             var tt = this; |  | ||||||
|             tt._acOpen = false; |  | ||||||
|             $( "#node-input-hostname" ).on( "autocompleteclose", function( event, ui ) { tt._acOpen = false;} ); |  | ||||||
|             $( "#node-input-hostname-lookup" ).click(function(e) { |  | ||||||
|                 if (tt._acOpen) { |  | ||||||
|                     $( "#node-input-hostname" ).autocomplete( "close"); |  | ||||||
|                 } else { |  | ||||||
|                     $( "#node-input-hostname" ).autocomplete( "search", "" ); |  | ||||||
|                 } |  | ||||||
|                 tt._acOpen = !tt._acOpen; |  | ||||||
|                 e.preventDefault(); |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,110 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var redis = require("redis"); |  | ||||||
|  |  | ||||||
|     var hashFieldRE = /^([^=]+)=(.*)$/; |  | ||||||
|  |  | ||||||
|     var redisConnectionPool = function() { |  | ||||||
|         var connections = {}; |  | ||||||
|         var obj = { |  | ||||||
|             get: function(host,port) { |  | ||||||
|                 var id = host+":"+port; |  | ||||||
|                 if (!connections[id]) { |  | ||||||
|                     connections[id] = redis.createClient(port,host); |  | ||||||
|                     connections[id].on("error",function(err) { |  | ||||||
|                         RED.log.error(err); |  | ||||||
|                     }); |  | ||||||
|                     connections[id].on("connect",function() { |  | ||||||
|                         if (RED.settings.verbose) { RED.log.info("connected to "+host+":"+port); } |  | ||||||
|                     }); |  | ||||||
|                     connections[id]._id = id; |  | ||||||
|                     connections[id]._nodeCount = 0; |  | ||||||
|                 } |  | ||||||
|                 connections[id]._nodeCount += 1; |  | ||||||
|                 return connections[id]; |  | ||||||
|             }, |  | ||||||
|             close: function(connection) { |  | ||||||
|                 connection._nodeCount -= 1; |  | ||||||
|                 if (connection._nodeCount === 0) { |  | ||||||
|                     if (connection) { |  | ||||||
|                         clearTimeout(connection.retry_timer); |  | ||||||
|                         connection.end(); |  | ||||||
|                     } |  | ||||||
|                     delete connections[connection._id]; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
|         return obj; |  | ||||||
|     }(); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     function RedisOutNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.port = n.port||"6379"; |  | ||||||
|         this.hostname = n.hostname||"127.0.0.1"; |  | ||||||
|         this.key = n.key; |  | ||||||
|         this.structtype = n.structtype; |  | ||||||
|  |  | ||||||
|         this.client = redisConnectionPool.get(this.hostname,this.port); |  | ||||||
|  |  | ||||||
|         if (this.client.connected) { |  | ||||||
|             this.status({fill:"green",shape:"dot",text:"connected"}); |  | ||||||
|         } else { |  | ||||||
|             this.status({fill:"red",shape:"ring",text:"disconnected"},true); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         var node = this; |  | ||||||
|         this.client.on("end", function() { |  | ||||||
|             node.status({fill:"red",shape:"ring",text:"disconnected"}); |  | ||||||
|         }); |  | ||||||
|         this.client.on("connect", function() { |  | ||||||
|             node.status({fill:"green",shape:"dot",text:"connected"}); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.on("input", function(msg) { |  | ||||||
|             var k = this.key || msg.topic; |  | ||||||
|             if (k) { |  | ||||||
|                 if (this.structtype == "string") { |  | ||||||
|                     this.client.set(k,RED.util.ensureString(msg.payload)); |  | ||||||
|                 } else if (this.structtype == "hash") { |  | ||||||
|                     if (typeof msg.payload == "object") { |  | ||||||
|                         this.client.hmset(k,msg.payload); |  | ||||||
|                     } else { |  | ||||||
|                         var r = hashFieldRE.exec(msg.payload); |  | ||||||
|                         if (r) { |  | ||||||
|                             this.client.hset(k,r[1],r[2]); |  | ||||||
|                         } else { |  | ||||||
|                             this.warn("Invalid payload for redis hash"); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } else if (this.structtype == "set") { |  | ||||||
|                     this.client.sadd(k,msg.payload); |  | ||||||
|                 } else if (this.structtype == "list") { |  | ||||||
|                     this.client.rpush(k,msg.payload); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 this.warn("No key or topic set"); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         this.on("close", function() { |  | ||||||
|             redisConnectionPool.close(node.client); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("redis out",RedisOutNode); |  | ||||||
| } |  | ||||||
| @@ -1,231 +0,0 @@ | |||||||
| <!-- |  | ||||||
|   Copyright 2013,2014 IBM Corp. |  | ||||||
|  |  | ||||||
|   Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|   you may not use this file except in compliance with the License. |  | ||||||
|   You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|   http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|   Unless required by applicable law or agreed to in writing, software |  | ||||||
|   distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|   See the License for the specific language governing permissions and |  | ||||||
|   limitations under the License. |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="mongodb"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-hostname"><i class="fa fa-bookmark"></i> Host</label> |  | ||||||
|         <input class="input-append-left" type="text" id="node-config-input-hostname" placeholder="localhost" style="width: 40%;" > |  | ||||||
|         <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label> |  | ||||||
|         <input type="text" id="node-config-input-port" placeholder="27017" style="width:45px"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-db"><i class="fa fa-database"></i> Database</label> |  | ||||||
|         <input type="text" id="node-config-input-db" placeholder="test"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-user"><i class="fa fa-user"></i> Username</label> |  | ||||||
|         <input type="text" id="node-config-input-user"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label> |  | ||||||
|         <input type="password" id="node-config-input-password"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-config-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     RED.nodes.registerType('mongodb', { |  | ||||||
|         category: 'config', |  | ||||||
|         color: "rgb(218, 196, 180)", |  | ||||||
|         defaults: { |  | ||||||
|             hostname: {value: "127.0.0.1", required: true}, |  | ||||||
|             port: {value: 27017, required: true}, |  | ||||||
|             db: {value: "", required: true}, |  | ||||||
|             name: {value: ""} |  | ||||||
|         }, |  | ||||||
|         credentials: { |  | ||||||
|             user: {type: "text"}, |  | ||||||
|             password: {type: "password"} |  | ||||||
|         }, |  | ||||||
|         label: function() { |  | ||||||
|             return this.name || this.hostname + ":" + this.port + "/" + this.db; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="mongodb out"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-mongodb"><i class="fa fa-bookmark"></i> Server</label> |  | ||||||
|         <input type="text" id="node-input-mongodb"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-collection"><i class="fa fa-briefcase"></i> Collection</label> |  | ||||||
|         <input type="text" id="node-input-collection" placeholder="collection"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-operation"><i class="fa fa-wrench"></i> Operation</label> |  | ||||||
|         <select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;"> |  | ||||||
|             <option value="store">save</option> |  | ||||||
|             <option value="insert">insert</option> |  | ||||||
|             <option value="update">update</option> |  | ||||||
|             <option value="delete">remove</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row node-input-payonly"> |  | ||||||
|         <label> </label> |  | ||||||
|         <input type="checkbox" id="node-input-payonly" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;"> |  | ||||||
|         <label for="node-input-payonly" style="width: 70%;">Only store msg.payload object</label> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row node-input-upsert"> |  | ||||||
|         <label> </label> |  | ||||||
|         <input type="checkbox" id="node-input-upsert" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;"> |  | ||||||
|         <label for="node-input-upsert" style="width: 70%;">Create a new document if no match found</label> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row node-input-multi"> |  | ||||||
|         <label> </label> |  | ||||||
|         <input type="checkbox" id="node-input-multi" placeholder="Only" style="display: inline-block; width: auto; vertical-align: top;;"> |  | ||||||
|         <label for="node-input-multi" style="width: 70%;">Update all matching documents</label> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips" id="node-warning" style="display: none"><b> Tip:</b> If no collection is set, ensure <b>msg.collection</b> will contain the collection name |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="mongodb out"> |  | ||||||
|     <p>A simple MongoDB output node. Can save, insert, update and remove objects from a chosen collection.</p> |  | ||||||
|     <p>Save will update an existing object or insert a new object if one does not already exist.</p> |  | ||||||
|     <p>Insert will insert a new object.</p> |  | ||||||
|     <p>Save and insert either store <b>msg</b> or <b>msg.payload</b>.</p> |  | ||||||
|     <p>Update will modify an existing object or objects. The query to select objects to update uses <b>msg.query</b> and the update to the element uses <b>msg.payload</b>.</p> |  | ||||||
|     <p>Update can add a object if it does not exist or update multiple objects.</p> |  | ||||||
|     <p>Remove will remove objects that match the query passed in on <b>msg.payload</b>. A blank query will delete <i>all of the objects</i> in the collection.</p> |  | ||||||
|     <p>You can either set the collection method in the node config or on <b>msg.collection</b>. Setting it in the node will override <b>msg.collection</b>.</p> |  | ||||||
|     <p>By default MongoDB creates an <i>_id</i> property as the primary key - so repeated injections of the same <b>msg</b> will result in many database entries.</p> |  | ||||||
|     <p>If this is NOT the desired behaviour - ie. you want repeated entries to overwrite, then you must set the <b>msg._id</b> property to be a constant by the use of a previous function node.</p> |  | ||||||
|     <p>This could be a unique constant or you could create one based on some other msg property.</p> |  | ||||||
|     <p>Currently we do not limit or cap the collection size at all... this may well change.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|  |  | ||||||
|     function oneditprepare() { |  | ||||||
|         $("#node-input-operation").change(function () { |  | ||||||
|             var id = $("#node-input-operation option:selected").val(); |  | ||||||
|  |  | ||||||
|             if (id === "update") { |  | ||||||
|                 $(".node-input-payonly").hide(); |  | ||||||
|                 $(".node-input-upsert, .node-input-multi").show(); |  | ||||||
|             } else if (id === "delete") { |  | ||||||
|                 $(".node-input-payonly, .node-input-upsert, .node-input-multi").hide(); |  | ||||||
|             } else { |  | ||||||
|                 $(".node-input-payonly").show(); |  | ||||||
|                 $(".node-input-upsert, .node-input-multi").hide(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         $("#node-input-collection").change(function () { |  | ||||||
|             if($("#node-input-collection").val() === "") { |  | ||||||
|                 $("#node-warning").show(); |  | ||||||
|             } else { |  | ||||||
|                 $("#node-warning").hide(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     RED.nodes.registerType('mongodb out', { |  | ||||||
|         category: 'storage-output', |  | ||||||
|         color: "rgb(218, 196, 180)", |  | ||||||
|         defaults: { |  | ||||||
|             mongodb: {type: "mongodb", required: true}, |  | ||||||
|             name: {value: ""}, |  | ||||||
|             collection: {value: ""}, |  | ||||||
|             payonly: {value: false}, |  | ||||||
|             upsert: {value: false}, |  | ||||||
|             multi: {value: false}, |  | ||||||
|             operation: {value: "store"} |  | ||||||
|         }, |  | ||||||
|         inputs: 1, |  | ||||||
|         outputs: 0, |  | ||||||
|         icon: "mongodb.png", |  | ||||||
|         align: "right", |  | ||||||
|         label: function() { |  | ||||||
|             var mongoNode = RED.nodes.node(this.mongodb); |  | ||||||
|             return this.name || (mongoNode ? mongoNode.label() + " " + this.collection: "mongodb"); |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name ? "node_label_italic" : ""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: oneditprepare |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-template-name="mongodb in"> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-mongodb"><i class="fa fa-bookmark"></i> Server</label> |  | ||||||
|         <input type="text" id="node-input-mongodb"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-collection"><i class="fa fa-briefcase"></i> Collection</label> |  | ||||||
|         <input type="text" id="node-input-collection" placeholder="collection"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-operation"><i class="fa fa-wrench"></i> Operation</label> |  | ||||||
|         <select type="text" id="node-input-operation" style="display: inline-block; vertical-align: top;"> |  | ||||||
|             <option value="find">find</option> |  | ||||||
|             <option value="count">count</option> |  | ||||||
|             <option value="aggregate">aggregate</option> |  | ||||||
|         </select> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-row"> |  | ||||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> |  | ||||||
|         <input type="text" id="node-input-name" placeholder="Name"> |  | ||||||
|     </div> |  | ||||||
|     <div class="form-tips" id="node-warning" style="display: none"><b> Tip:</b> If no collection is set, ensure <b>msg.collection</b> will contain the collection name |  | ||||||
|     </div> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/x-red" data-help-name="mongodb in"> |  | ||||||
|     <p>Calls a MongoDB collection method based on the selected operator.</p> |  | ||||||
|         <p>Find queries a collection using the <b>msg.payload</b> as the query statement as per the .find() function. Optionally, you may also (via a function) set a <b>msg.projection</b> object to constrain the returned fields, a <b>msg.sort</b> object, a <b>msg.limit</b> number and a <b>msg.skip</b> number.</p> |  | ||||||
|     <p>Count returns a count of the number of documents in a collection or matching a query using the <b>msg.payload</b> as the query statement.</p> |  | ||||||
|     <p>Aggregate provides access to the aggregation pipeline using the <b>msg.payload</b> as the pipeline array.</p> |  | ||||||
|     <p>You can either set the collection method in the node config or on <b>msg.collection</b>. Setting it in the node will override <b>msg.collection</b>.</p> |  | ||||||
|     <p>See the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.find/" target="new"><i>MongoDB collection methods docs</i></a> for examples.</p> |  | ||||||
|     <p>The result is returned in <b>msg.payload</b>.</p> |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script type="text/javascript"> |  | ||||||
|  |  | ||||||
|     RED.nodes.registerType('mongodb in', { |  | ||||||
|         category: 'storage-input', |  | ||||||
|         color: "rgb(218, 196, 180)", |  | ||||||
|         defaults: { |  | ||||||
|             mongodb: {type: "mongodb", required: true}, |  | ||||||
|             name: {value: ""}, |  | ||||||
|             collection: {value: ""}, |  | ||||||
|             operation: {value: "find"} |  | ||||||
|         }, |  | ||||||
|         inputs: 1, |  | ||||||
|         outputs: 1, |  | ||||||
|         icon: "mongodb.png", |  | ||||||
|         label: function() { |  | ||||||
|             var mongoNode = RED.nodes.node(this.mongodb); |  | ||||||
|             return this.name || (mongoNode ? mongoNode.label() + " " + this.collection: "mongodb"); |  | ||||||
|         }, |  | ||||||
|         labelStyle: function() { |  | ||||||
|             return this.name ? "node_label_italic" : ""; |  | ||||||
|         }, |  | ||||||
|         oneditprepare: oneditprepare |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| @@ -1,244 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright 2013,2014 IBM Corp. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  * http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  **/ |  | ||||||
|  |  | ||||||
| module.exports = function(RED) { |  | ||||||
|     "use strict"; |  | ||||||
|     var mongo = require('mongodb'); |  | ||||||
|     var MongoClient = mongo.MongoClient; |  | ||||||
|  |  | ||||||
|     function MongoNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.hostname = n.hostname; |  | ||||||
|         this.port = n.port; |  | ||||||
|         this.db = n.db; |  | ||||||
|         this.name = n.name; |  | ||||||
|  |  | ||||||
|         var url = "mongodb://"; |  | ||||||
|         if (this.credentials && this.credentials.user && this.credentials.password) { |  | ||||||
|             url += this.credentials.user+":"+this.credentials.password+"@"; |  | ||||||
|         } |  | ||||||
|         url += this.hostname+":"+this.port+"/"+this.db; |  | ||||||
|  |  | ||||||
|         this.url = url; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     RED.nodes.registerType("mongodb",MongoNode,{ |  | ||||||
|         credentials: { |  | ||||||
|             user: {type:"text"}, |  | ||||||
|             password: {type: "password"} |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     function ensureValidSelectorObject(selector) { |  | ||||||
|         if (selector != null && (typeof selector != 'object' || Buffer.isBuffer(selector))) { |  | ||||||
|             return {}; |  | ||||||
|         } |  | ||||||
|         return selector; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     function MongoOutNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.collection = n.collection; |  | ||||||
|         this.mongodb = n.mongodb; |  | ||||||
|         this.payonly = n.payonly || false; |  | ||||||
|         this.upsert = n.upsert || false; |  | ||||||
|         this.multi = n.multi || false; |  | ||||||
|         this.operation = n.operation; |  | ||||||
|         this.mongoConfig = RED.nodes.getNode(this.mongodb); |  | ||||||
|  |  | ||||||
|         if (this.mongoConfig) { |  | ||||||
|             var node = this; |  | ||||||
|             MongoClient.connect(this.mongoConfig.url, function(err, db) { |  | ||||||
|                 if (err) { |  | ||||||
|                     node.error(err); |  | ||||||
|                 } else { |  | ||||||
|                     node.clientDb = db; |  | ||||||
|                     var coll; |  | ||||||
|                     if (node.collection) { |  | ||||||
|                         coll = db.collection(node.collection); |  | ||||||
|                     } |  | ||||||
|                     node.on("input",function(msg) { |  | ||||||
|                         if (!node.collection) { |  | ||||||
|                             if (msg.collection) { |  | ||||||
|                                 coll = db.collection(msg.collection); |  | ||||||
|                             } else { |  | ||||||
|                                 node.error("No collection defined",msg); |  | ||||||
|                                 return; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         delete msg._topic; |  | ||||||
|                         delete msg.collection; |  | ||||||
|                         if (node.operation === "store") { |  | ||||||
|                             if (node.payonly) { |  | ||||||
|                                 if (typeof msg.payload !== "object") { |  | ||||||
|                                     msg.payload = {"payload": msg.payload}; |  | ||||||
|                                 } |  | ||||||
|                                 coll.save(msg.payload,function(err, item) { |  | ||||||
|                                     if (err) { |  | ||||||
|                                         node.error(err,msg); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                             } else { |  | ||||||
|                                 coll.save(msg,function(err, item) { |  | ||||||
|                                     if (err) { |  | ||||||
|                                         node.error(err,msg); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                             } |  | ||||||
|                         } else if (node.operation === "insert") { |  | ||||||
|                             if (node.payonly) { |  | ||||||
|                                 if (typeof msg.payload !== "object") { |  | ||||||
|                                     msg.payload = {"payload": msg.payload}; |  | ||||||
|                                 } |  | ||||||
|                                 coll.insert(msg.payload, function(err, item) { |  | ||||||
|                                     if (err) { |  | ||||||
|                                         node.error(err,msg); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                             } else { |  | ||||||
|                                 coll.insert(msg, function(err,item) { |  | ||||||
|                                     if (err) { |  | ||||||
|                                         node.error(err,msg); |  | ||||||
|                                     } |  | ||||||
|                                 }); |  | ||||||
|                             } |  | ||||||
|                         } else if (node.operation === "update") { |  | ||||||
|                             if (typeof msg.payload !== "object") { |  | ||||||
|                                 msg.payload = {"payload": msg.payload}; |  | ||||||
|                             } |  | ||||||
|                             var query = msg.query || {}; |  | ||||||
|                             var payload = msg.payload || {}; |  | ||||||
|                             var options = { |  | ||||||
|                                 upsert: node.upsert, |  | ||||||
|                                 multi: node.multi |  | ||||||
|                             }; |  | ||||||
|  |  | ||||||
|                             coll.update(query, payload, options, function(err, item) { |  | ||||||
|                                 if (err) { |  | ||||||
|                                     node.error(err,msg); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                         } else if (node.operation === "delete") { |  | ||||||
|                             coll.remove(msg.payload, function(err, items) { |  | ||||||
|                                 if (err) { |  | ||||||
|                                     node.error(err,msg); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } else { |  | ||||||
|             this.error("missing mongodb configuration"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on("close", function() { |  | ||||||
|             if (this.clientDb) { |  | ||||||
|                 this.clientDb.close(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("mongodb out",MongoOutNode); |  | ||||||
|  |  | ||||||
|     function MongoInNode(n) { |  | ||||||
|         RED.nodes.createNode(this,n); |  | ||||||
|         this.collection = n.collection; |  | ||||||
|         this.mongodb = n.mongodb; |  | ||||||
|         this.operation = n.operation || "find"; |  | ||||||
|         this.mongoConfig = RED.nodes.getNode(this.mongodb); |  | ||||||
|  |  | ||||||
|         if (this.mongoConfig) { |  | ||||||
|             var node = this; |  | ||||||
|             var selector; |  | ||||||
|             MongoClient.connect(this.mongoConfig.url, function(err,db) { |  | ||||||
|                 if (err) { |  | ||||||
|                     node.error(err); |  | ||||||
|                 } else { |  | ||||||
|                     node.clientDb = db; |  | ||||||
|                     var coll; |  | ||||||
|                     if (node.collection) { |  | ||||||
|                         coll = db.collection(node.collection); |  | ||||||
|                     } |  | ||||||
|                     node.on("input", function(msg) { |  | ||||||
|                         if (!node.collection) { |  | ||||||
|                             if (msg.collection) { |  | ||||||
|                                 coll = db.collection(msg.collection); |  | ||||||
|                             } else { |  | ||||||
|                                 node.error("No collection defined"); |  | ||||||
|                                 return; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         if (node.operation === "find") { |  | ||||||
|                             msg.projection = msg.projection || {}; |  | ||||||
|                             selector = ensureValidSelectorObject(msg.payload); |  | ||||||
|                             var limit = msg.limit; |  | ||||||
|                             if (typeof limit === "string" && !isNaN(limit)) { |  | ||||||
|                                 limit = Number(limit); |  | ||||||
|                             } |  | ||||||
|                             var skip = msg.skip; |  | ||||||
|                             if (typeof skip === "string" && !isNaN(skip)) { |  | ||||||
|                                 skip = Number(skip); |  | ||||||
|                             } |  | ||||||
|  |  | ||||||
|                             coll.find(selector,msg.projection).sort(msg.sort).limit(limit).skip(skip).toArray(function(err, items) { |  | ||||||
|                                 if (err) { |  | ||||||
|                                     node.error(err); |  | ||||||
|                                 } else { |  | ||||||
|                                     msg.payload = items; |  | ||||||
|                                     delete msg.projection; |  | ||||||
|                                     delete msg.sort; |  | ||||||
|                                     delete msg.limit; |  | ||||||
|                                     delete msg.skip; |  | ||||||
|                                     node.send(msg); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                         } else if (node.operation === "count") { |  | ||||||
|                             selector = ensureValidSelectorObject(msg.payload); |  | ||||||
|                             coll.count(selector, function(err, count) { |  | ||||||
|                                 if (err) { |  | ||||||
|                                     node.error(err); |  | ||||||
|                                 } else { |  | ||||||
|                                     msg.payload = count; |  | ||||||
|                                     node.send(msg); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                         } else if (node.operation === "aggregate") { |  | ||||||
|                             msg.payload = (Array.isArray(msg.payload)) ? msg.payload : []; |  | ||||||
|                             coll.aggregate(msg.payload, function(err, result) { |  | ||||||
|                                 if (err) { |  | ||||||
|                                     node.error(err); |  | ||||||
|                                 } else { |  | ||||||
|                                     msg.payload = result; |  | ||||||
|                                     node.send(msg); |  | ||||||
|                                 } |  | ||||||
|                             }); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } else { |  | ||||||
|             this.error("missing mongodb configuration"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.on("close", function() { |  | ||||||
|             if (this.clientDb) { |  | ||||||
|                 this.clientDb.close(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     RED.nodes.registerType("mongodb in",MongoInNode); |  | ||||||
| } |  | ||||||
							
								
								
									
										20
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								package.json
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | |||||||
|     "version"      : "0.10.7", |     "version"      : "0.10.7", | ||||||
|     "description"  : "A visual tool for wiring the Internet of Things", |     "description"  : "A visual tool for wiring the Internet of Things", | ||||||
|     "homepage"     : "http://nodered.org", |     "homepage"     : "http://nodered.org", | ||||||
|     "license"      : "Apache", |     "license"      : "Apache-2.0", | ||||||
|     "repository"   : { |     "repository"   : { | ||||||
|         "type":"git", |         "type":"git", | ||||||
|         "url":"https://github.com/node-red/node-red.git" |         "url":"https://github.com/node-red/node-red.git" | ||||||
| @@ -30,35 +30,31 @@ | |||||||
|         "bcryptjs": "2.1.0", |         "bcryptjs": "2.1.0", | ||||||
|         "nopt": "3.0.1", |         "nopt": "3.0.1", | ||||||
|         "mqtt": "0.3.x", |         "mqtt": "0.3.x", | ||||||
|         "ws": "0.7.1", |         "ws": "0.7.2", | ||||||
|         "fs-extra": "0.16.3", |         "fs-extra": "0.16.3", | ||||||
|         "clone": "0.2.0", |         "clone": "0.2.0", | ||||||
|         "mustache": "1.0.0", |         "mustache": "1.0.0", | ||||||
|         "cron":"1.0.6", |         "cron":"1.0.6", | ||||||
|         "raw-body":"1.3.2", |         "raw-body":"1.3.2", | ||||||
|         "twitter-ng":"0.6.2", |  | ||||||
|         "oauth":"0.9.12", |  | ||||||
|         "xml2js":"0.4.4", |         "xml2js":"0.4.4", | ||||||
|         "sentiment":"0.2.3", |         "sentiment":"0.2.3", | ||||||
|         "follow-redirects":"0.0.3", |         "follow-redirects":"0.0.3", | ||||||
|         "cors":"2.5.3", |         "cors":"2.5.3", | ||||||
|         "cheerio":"0.18.0", |         "cheerio":"0.19.0", | ||||||
|         "uglify-js":"2.4.16", |         "uglify-js":"2.4.16", | ||||||
|         "nodemailer":"1.3.0", |  | ||||||
|         "imap":"0.8.14", |  | ||||||
|         "request":"2.42.0", |  | ||||||
|         "on-headers":"1.0.0", |         "on-headers":"1.0.0", | ||||||
|         "is-utf8":"0.2.0", |         "is-utf8":"0.2.0", | ||||||
|         "feedparser":"0.19.2", |  | ||||||
|         "fs.notify":"0.0.4", |         "fs.notify":"0.0.4", | ||||||
|         "passport":"0.2.1", |         "passport":"0.2.1", | ||||||
|         "passport-http-bearer":"1.0.1", |         "passport-http-bearer":"1.0.1", | ||||||
|         "passport-oauth2-client-password":"0.1.2", |         "passport-oauth2-client-password":"0.1.2", | ||||||
|         "oauth2orize":"1.0.1" |         "oauth2orize":"1.0.1", | ||||||
|  |         "node-red-node-feedparser":"0.0.2", | ||||||
|  |         "node-red-node-email":"0.0.1", | ||||||
|  |         "node-red-node-twitter":"0.0.4" | ||||||
|     }, |     }, | ||||||
|     "optionalDependencies": { |     "optionalDependencies": { | ||||||
|         "irc":"0.3.9", |         "node-red-node-serialport":"0.0.1", | ||||||
|         "serialport":"1.4.10", |  | ||||||
|         "bcrypt":"0.8.1" |         "bcrypt":"0.8.1" | ||||||
|     }, |     }, | ||||||
|     "devDependencies": { |     "devDependencies": { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user