diff --git a/.gitignore b/.gitignore
index e6623f64..729dabf3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@ puball.sh
setenv.sh
/.project
package-lock.json
+social/xmpp/92-xmpp.old
+*.tgz
diff --git a/function/random/README.md b/function/random/README.md
index 0713d08a..91710a84 100644
--- a/function/random/README.md
+++ b/function/random/README.md
@@ -19,4 +19,6 @@ If set to return an integer it can include both the low and high values.
If set to return a floating point value it will be from the low value, up to, but
**not** including the high value. `min <= n < max` - so selecting 1 to 6 will return values 1 <= n < 6 .
+You can dynamically pass in the 'From' and 'To' values to the node using msg.to and/or msg.from. **NOTE:** hard coded values in the node **always take precedence**.
+
**Note:** This returns numbers - objects of type **number**.
diff --git a/function/random/locales/en-US/random.html b/function/random/locales/en-US/random.html
index dbf239b4..9c18b0da 100644
--- a/function/random/locales/en-US/random.html
+++ b/function/random/locales/en-US/random.html
@@ -1,7 +1,16 @@
diff --git a/function/random/locales/ja/random.html b/function/random/locales/ja/random.html
index ffb9b2c0..5b09fea8 100644
--- a/function/random/locales/ja/random.html
+++ b/function/random/locales/ja/random.html
@@ -1,7 +1,16 @@
diff --git a/function/random/package.json b/function/random/package.json
index 43965ece..2f2f4dc0 100644
--- a/function/random/package.json
+++ b/function/random/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-red-node-random",
- "version" : "0.2.0",
+ "version" : "0.3.1",
"description" : "A Node-RED node that when triggered generates a random number between two values.",
"dependencies" : {
},
@@ -19,5 +19,8 @@
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
- }
+ },
+ "contributors": [
+ {"name": "@zenofmud"}
+ ]
}
diff --git a/function/random/random.html b/function/random/random.html
index bf1ae44a..7443615f 100644
--- a/function/random/random.html
+++ b/function/random/random.html
@@ -31,8 +31,8 @@
color:"#E2D96E",
defaults: {
name: {value:""},
- low: {value:"1"},
- high: {value:"10"},
+ low: {value: 1,validate:function(v) { return !isNaN(v) || v.length === 0;} },
+ high: {value: 10,validate:function(v) { return !isNaN(v) || v.length === 0;} },
inte: {value:"true"},
property: {value:"payload",required:true}
},
diff --git a/function/random/random.js b/function/random/random.js
index dac7b0b9..681cfefe 100644
--- a/function/random/random.js
+++ b/function/random/random.js
@@ -3,21 +3,73 @@ module.exports = function(RED) {
"use strict";
function RandomNode(n) {
RED.nodes.createNode(this,n);
- this.low = Number(n.low || 1);
- this.high = Number(n.high || 10);
+
+ this.low = n.low
+ this.high = n.high
this.inte = n.inte || false;
this.property = n.property||"payload";
var node = this;
+ var tmp = {};
+
this.on("input", function(msg) {
- var value;
- if (node.inte == "true" || node.inte === true) {
- value = Math.round(Math.random() * (node.high - node.low + 1) + node.low - 0.5);
+
+ tmp.low = 1 // set this as the default low value
+ tmp.low_e = ""
+ if (node.low) { // if the the node has a value use it
+ tmp.low = Number(node.low);
+ } else if ('from' in msg) { // else see if a 'from' is in the msg
+ if (Number(msg.from)) { // if it is, and is a number, use it
+ tmp.low = Number(msg.from);
+ } else { // otherwise setup NaN error
+ tmp.low = NaN;
+ tmp.low_e = " From: " + msg.from; // setup to show bad incoming msg.from
+ }
}
- else {
- value = Math.random() * (node.high - node.low) + node.low;
+
+ tmp.high = 10 // set this as the default high value
+ tmp.high_e = "";
+ if (node.high) { // if the the node has a value use it
+ tmp.high = Number(node.high);
+ } else if ('to' in msg) { // else see if a 'to' is in the msg
+ if (Number(msg.to)) { // if it is, and is a number, use it
+ tmp.high = Number(msg.to);
+ } else { // otherwise setup NaN error
+ tmp.high = NaN
+ tmp.high_e = " To: " + msg.to // setup to show bad incoming msg.to
+ }
+ }
+
+ // if tmp.low or high are not numbers, send an error msg with bad values
+ if ( (isNaN(tmp.low)) || (isNaN(tmp.high)) ) {
+ this.error("Random: one of the input values is not a number. " + tmp.low_e + tmp.high_e);
+ } else {
+ // at this point we have valid values so now to generate the random number!
+
+ // flip the values if low > high so random will work
+ var value = 0;
+ if (tmp.low > tmp.high) {
+ value = tmp.low
+ tmp.low = tmp.high
+ tmp.high = value
+ }
+
+ // if returning an integer, do a math.ceil() on the low value and a
+ // Math.floor()high value before generate the random number. This must be
+ // done to insure the rounding doesn't round up if using something like 4.7
+ // which would end up with 5
+ if ( (node.inte == "true") || (node.inte === true) ) {
+ tmp.low = Math.ceil(tmp.low);
+ tmp.high = Math.floor(tmp.high);
+ // use this to round integers
+ value = Math.round(Math.random() * (tmp.high - tmp.low + 1) + tmp.low - 0.5);
+ } else {
+ // use this to round floats
+ value = (Math.random() * (tmp.high - tmp.low)) + tmp.low;
+ }
+
+ RED.util.setMessageProperty(msg,node.property,value);
+ node.send(msg);
}
- RED.util.setMessageProperty(msg,node.property,value);
- node.send(msg);
});
}
RED.nodes.registerType("random",RandomNode);
diff --git a/hardware/Arduino/package.json b/hardware/Arduino/package.json
index b1c8e059..14a9d18f 100644
--- a/hardware/Arduino/package.json
+++ b/hardware/Arduino/package.json
@@ -3,7 +3,7 @@
"version" : "0.3.1",
"description" : "A Node-RED node to talk to an Arduino running firmata",
"dependencies" : {
- "firmata" : "^2.0.0"
+ "firmata" : "^2.3.0"
},
"repository" : {
"type":"git",
diff --git a/hardware/PiGpio/36-rpi-gpio.js b/hardware/PiGpio/36-rpi-gpio.js
index c5b11dfc..1c263f30 100644
--- a/hardware/PiGpio/36-rpi-gpio.js
+++ b/hardware/PiGpio/36-rpi-gpio.js
@@ -4,7 +4,6 @@ module.exports = function(RED) {
var execSync = require('child_process').execSync;
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
- var fs = require('fs');
var testCommand = __dirname+'/testgpio.py'
var gpioCommand = __dirname+'/nrgpio';
@@ -271,16 +270,21 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
var node = this;
- if (allOK === true) {
+ var doConnect = function() {
node.child = spawn(gpioCommand+".py", ["kbd","0"]);
node.status({fill:"green",shape:"dot",text:"rpi-gpio.status.ok"});
node.child.stdout.on('data', function (data) {
- var b = data.toString().trim().split(",");
- var act = "up";
- if (b[1] === "1") { act = "down"; }
- if (b[1] === "2") { act = "repeat"; }
- node.send({ topic:"pi/key", payload:Number(b[0]), action:act });
+ var d = data.toString().trim().split("\n");
+ for (var i = 0; i < d.length; i++) {
+ if (d[i] !== '') {
+ var b = d[i].trim().split(",");
+ var act = "up";
+ if (b[1] === "1") { act = "down"; }
+ if (b[1] === "2") { act = "repeat"; }
+ node.send({ topic:"pi/key", payload:Number(b[0]), action:act });
+ }
+ }
});
node.child.stderr.on('data', function (data) {
@@ -295,7 +299,10 @@ module.exports = function(RED) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.finished();
}
- else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
+ else {
+ node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"});
+ setTimeout(function() { doConnect(); },2000)
+ }
});
node.child.on('error', function (err) {
@@ -303,6 +310,10 @@ module.exports = function(RED) {
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
+ }
+
+ if (allOK === true) {
+ doConnect();
node.on("close", function(done) {
node.status({});
diff --git a/hardware/PiGpio/package.json b/hardware/PiGpio/package.json
index e948b4ba..4331e228 100644
--- a/hardware/PiGpio/package.json
+++ b/hardware/PiGpio/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red-node-pi-gpio",
- "version": "1.2.0",
+ "version": "1.2.3",
"description": "The basic Node-RED node for Pi GPIO",
"dependencies" : {
},
diff --git a/hardware/blink1/package.json b/hardware/blink1/package.json
index 32f29182..cbc9c384 100644
--- a/hardware/blink1/package.json
+++ b/hardware/blink1/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-blink1",
- "version" : "0.0.17",
+ "version" : "0.0.18",
"description" : "A Node-RED node to control a Thingm Blink(1)",
"dependencies" : {
- "node-blink1" : "0.2.2"
+ "node-blink1" : "0.5.1"
},
"repository" : {
"type":"git",
diff --git a/hardware/blinkstick/76-blinkstick.html b/hardware/blinkstick/76-blinkstick.html
index 0f9ce707..f72f3203 100644
--- a/hardware/blinkstick/76-blinkstick.html
+++ b/hardware/blinkstick/76-blinkstick.html
@@ -1,5 +1,5 @@
-
-
+
+
+
+
diff --git a/hardware/blinkstick/blinkstick.js b/hardware/blinkstick/blinkstick.js
new file mode 100644
index 00000000..389dd605
--- /dev/null
+++ b/hardware/blinkstick/blinkstick.js
@@ -0,0 +1,64 @@
+/**
+ * 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 blinkstick = require("blinkstick");
+
+ Object.size = function(obj) {
+ var size = 0;
+ for (var key in obj) { if (obj.hasOwnProperty(key)) { size++; } }
+ return size;
+ };
+
+ function BlinkStick(n) {
+ RED.nodes.createNode(this,n);
+ var p1 = /^\#[A-Fa-f0-9]{6}$/
+ var p2 = /[0-9]+,[0-9]+,[0-9]+/
+ this.led = blinkstick.findFirst(); // maybe try findAll() (one day)
+ var node = this;
+
+ this.on("input", function(msg) {
+ if (msg != null) {
+ if (Object.size(node.led) !== 0) {
+ try {
+ if (p2.test(msg.payload)) {
+ var rgb = msg.payload.split(",");
+ node.led.setColor(parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255);
+ }
+ else {
+ node.led.setColor(msg.payload.toLowerCase().replace(/\s+/g,''));
+ }
+ }
+ catch (err) {
+ node.warn("BlinkStick missing ?");
+ node.led = blinkstick.findFirst();
+ }
+ }
+ else {
+ //node.warn("No BlinkStick found");
+ node.led = blinkstick.findFirst();
+ }
+ }
+ });
+ if (Object.size(node.led) === 0) {
+ node.error("No BlinkStick found");
+ }
+
+ }
+
+ RED.nodes.registerType("blinkstick",BlinkStick);
+}
diff --git a/hardware/blinkstick/package.json b/hardware/blinkstick/package.json
index a7c2c344..6b43e404 100644
--- a/hardware/blinkstick/package.json
+++ b/hardware/blinkstick/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-blinkstick",
- "version" : "0.1.16",
+ "version" : "0.1.7",
"description" : "A Node-RED node to control a Blinkstick",
"dependencies" : {
- "blinkstick" : "1.1.3"
+ "blinkstick" : "1.2.0"
},
"repository" : {
"type":"git",
diff --git a/hardware/mcp3008/package.json b/hardware/mcp3008/package.json
index 218ce52d..8b221b0d 100644
--- a/hardware/mcp3008/package.json
+++ b/hardware/mcp3008/package.json
@@ -1,9 +1,9 @@
{
"name" : "node-red-node-pi-mcp3008",
- "version" : "0.2.1",
+ "version" : "0.3.0",
"description" : "A Node-RED node to read from the MCP3008 Analogue to Digital Converter",
"dependencies" : {
- "mcp-spi-adc": "^2.0.6"
+ "mcp-spi-adc": "^3.1.0"
},
"repository" : {
"type":"git",
diff --git a/hardware/mcp3008/pimcp3008.js b/hardware/mcp3008/pimcp3008.js
index e765836f..61c74485 100644
--- a/hardware/mcp3008/pimcp3008.js
+++ b/hardware/mcp3008/pimcp3008.js
@@ -2,16 +2,23 @@
module.exports = function(RED) {
"use strict";
var fs = require('fs');
+ var allOK = false;
+ var mcpadc;
// unlikely if not on a Pi
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
- if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
+ if (cpuinfo.indexOf(": BCM") === -1) {
+ RED.log.warn("Info : mcp3xxx : Not running on a Pi - Ignoring node");
+ }
+ else {
+ mcpadc = require('mcp-spi-adc');
+ allOK = true;
+ }
}
catch(err) {
- throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
+ RED.log.warn("Info : mcp3xxx : Not running on a Pi - Ignoring node");
}
- var mcpadc = require('mcp-spi-adc');
var mcp3xxx = [];
function PiMcpNode(n) {
@@ -26,49 +33,54 @@ module.exports = function(RED) {
var opt = { speedHz:20000, deviceNumber:node.dnum, busNumber:node.bus };
var chans = parseInt(this.dev.substr(3));
- try {
- fs.statSync("/dev/spidev"+node.bus+"."+node.dnum);
- if (mcp3xxx.length === 0) {
- for (var i=0; i Sends the The default message recipient can be configured in the node, if it is left
blank it should be set using the msg.payload
as an email, with a subject of msg.topic
.msg.to
property of the incoming message. If left blank
- you can also specify any or all of: msg.cc
, msg.bcc
, msg.replyTo
, msg.inReplyTo
, msg.references
properties.msg.cc
, msg.bcc
, msg.replyTo
,
+ msg.inReplyTo
, msg.references
, msg.headers
, or msg.priority
properties.
You may optionally set msg.from
in the payload which will override the userid
default value.
:memory:
will create a non-persistant in memory database."
+ }
+ }
+}
diff --git a/storage/sqlite/locales/ja/sqlite.html b/storage/sqlite/locales/ja/sqlite.html
new file mode 100644
index 00000000..5c6e8da1
--- /dev/null
+++ b/storage/sqlite/locales/ja/sqlite.html
@@ -0,0 +1,26 @@
+
+
+
diff --git a/storage/sqlite/locales/ja/sqlite.json b/storage/sqlite/locales/ja/sqlite.json
new file mode 100644
index 00000000..dda91077
--- /dev/null
+++ b/storage/sqlite/locales/ja/sqlite.json
@@ -0,0 +1,20 @@
+{
+ "sqlite": {
+ "label": {
+ "database": "データベース",
+ "sqlQuery": "SQLクエリ",
+ "viaMsgTopic": "msg.topic経由",
+ "fixedStatement": "固定文",
+ "preparedStatement": "事前定義文",
+ "batchWithoutResponse": "一括(応答なし)",
+ "sqlStatement": "SQL文",
+ "mode": "モード",
+ "readWriteCreate": "読み取り-書き込み-作成",
+ "readWrite": "読み取り-書き込み",
+ "readOnly": "読み取りのみ"
+ },
+ "tips": {
+ "memoryDb": "注釈: データベース名に :memory:
を設定すると、非永続的なメモリデータベースを作成します。"
+ }
+ }
+}
diff --git a/storage/sqlite/sqlite.html b/storage/sqlite/sqlite.html
index ceeca265..faca8ab7 100644
--- a/storage/sqlite/sqlite.html
+++ b/storage/sqlite/sqlite.html
@@ -1,19 +1,17 @@
-
-
-
-
-
-
-
+
+
+
+
diff --git a/utility/annotate-image/annotate.js b/utility/annotate-image/annotate.js
new file mode 100644
index 00000000..bf12fc72
--- /dev/null
+++ b/utility/annotate-image/annotate.js
@@ -0,0 +1,192 @@
+module.exports = function(RED) {
+ "use strict";
+ const pureimage = require("pureimage");
+ const Readable = require("stream").Readable;
+ const Writable = require("stream").Writable;
+ const path = require("path");
+
+ let fontLoaded = false;
+ function loadFont() {
+ if (!fontLoaded) {
+ const fnt = pureimage.registerFont(path.join(__dirname,'./SourceSansPro-Regular.ttf'),'Source Sans Pro');
+ fnt.load();
+ fontLoaded = true;
+ }
+ }
+
+ function AnnotateNode(n) {
+ RED.nodes.createNode(this,n);
+ var node = this;
+ const defaultFill = n.fill || "";
+ const defaultStroke = n.stroke || "#ffC000";
+ const defaultLineWidth = parseInt(n.lineWidth) || 5;
+ const defaultFontSize = n.fontSize || 24;
+ const defaultFontColor = n.fontColor || "#ffC000";
+ loadFont();
+
+ this.on("input", function(msg) {
+ if (Buffer.isBuffer(msg.payload)) {
+ if (msg.payload[0] !== 0xFF || msg.payload[1] !== 0xD8) {
+ node.error("Not a JPEG image",msg);
+ } else if (Array.isArray(msg.annotations) && msg.annotations.length > 0) {
+ const stream = new Readable();
+ stream.push(msg.payload);
+ stream.push(null);
+ pureimage.decodeJPEGFromStream(stream).then(img => {
+ const c = pureimage.make(img.width, img.height);
+ const ctx = c.getContext('2d');
+ ctx.drawImage(img,0,0,img.width,img.height);
+
+ ctx.lineJoin = 'bevel';
+
+ msg.annotations.forEach(function(annotation) {
+ ctx.fillStyle = annotation.fill || defaultFill;
+ ctx.strokeStyle = annotation.stroke || defaultStroke;
+ ctx.lineWidth = annotation.lineWidth || defaultLineWidth;
+ ctx.lineJoin = 'bevel';
+ let x,y,r,w,h;
+
+ if (!annotation.type && annotation.bbox) {
+ annotation.type = 'rect';
+ }
+
+ switch(annotation.type) {
+ case 'rect':
+ if (annotation.bbox) {
+ x = annotation.bbox[0]
+ y = annotation.bbox[1]
+ w = annotation.bbox[2]
+ h = annotation.bbox[3]
+ } else {
+ x = annotation.x
+ y = annotation.y
+ w = annotation.w
+ h = annotation.h
+ }
+
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+ ctx.beginPath();
+ ctx.lineWidth = annotation.lineWidth || defaultLineWidth;
+ ctx.rect(x,y,w,h);
+ ctx.closePath();
+ ctx.stroke();
+
+ if (annotation.label) {
+ ctx.font = `${annotation.fontSize || defaultFontSize}pt 'Source Sans Pro'`;
+ ctx.fillStyle = annotation.fontColor || defaultFontColor;
+ ctx.textBaseline = "top";
+ ctx.textAlign = "left";
+ //set offset value so txt is above or below image
+ if (annotation.labelLocation) {
+ if (annotation.labelLocation === "top") {
+ y = y - (20+((defaultLineWidth*0.5)+(Number(defaultFontSize))));
+ if (y < 0)
+ {
+ y = 0;
+ }
+ }
+ else if (annotation.labelLocation === "bottom") {
+ y = y + (10+h+(((defaultLineWidth*0.5)+(Number(defaultFontSize)))));
+ ctx.textBaseline = "bottom";
+
+ }
+ }
+ //if not user defined make best guess for top or bottom based on location
+ else {
+ //not enought room above imagebox, put label on the bottom
+ if (y < 0 + (20+((defaultLineWidth*0.5)+(Number(defaultFontSize))))) {
+ y = y + (10+h+(((defaultLineWidth*0.5)+(Number(defaultFontSize)))));
+ ctx.textBaseline = "bottom";
+ }
+ //else put the label on the top
+ else {
+ y = y - (20+((defaultLineWidth*0.5)+(Number(defaultFontSize))));
+ if (y < 0) {
+ y = 0;
+ }
+ }
+ }
+
+
+ ctx.fillText(annotation.label, x,y);
+ }
+ break;
+ case 'circle':
+ if (annotation.bbox) {
+ x = annotation.bbox[0] + annotation.bbox[2]/2
+ y = annotation.bbox[1] + annotation.bbox[3]/2
+ r = Math.min(annotation.bbox[2],annotation.bbox[3])/2;
+ } else {
+ x = annotation.x
+ y = annotation.y
+ r = annotation.r;
+ }
+ ctx.beginPath();
+ ctx.lineWidth = annotation.lineWidth || defaultLineWidth;
+ ctx.arc(x,y,r,0,Math.PI*2);
+ ctx.closePath();
+ ctx.stroke();
+ if (annotation.label) {
+ ctx.font = `${annotation.fontSize || defaultFontSize}pt 'Source Sans Pro'`;
+ ctx.fillStyle = annotation.fontColor || defaultFontColor;
+ ctx.textBaseline = "middle";
+ ctx.textAlign = "center";
+ ctx.fillText(annotation.label, x+2,y)
+ }
+ break;
+ }
+ });
+ const bufferOutput = getWritableBuffer();
+ pureimage.encodeJPEGToStream(c,bufferOutput.stream,90).then(() => {
+ msg.payload = bufferOutput.getBuffer();
+ node.send(msg);
+ })
+ }).catch(err => {
+ node.error(err,msg);
+ })
+ } else {
+ // No annotations to make - send the message on
+ node.send(msg);
+ }
+ } else {
+ node.error("Payload not a Buffer",msg)
+ }
+ return msg;
+ });
+ }
+ RED.nodes.registerType("annotate-image",AnnotateNode);
+
+
+
+ function getWritableBuffer() {
+ var currentSize = 0;
+ var buffer = null;
+ const stream = new Writable({
+ write(chunk, encoding, callback) {
+ if (!buffer) {
+ buffer = Buffer.from(chunk);
+ } else {
+ var newBuffer = Buffer.allocUnsafe(currentSize + chunk.length);
+ buffer.copy(newBuffer);
+ chunk.copy(newBuffer,currentSize);
+ buffer = newBuffer;
+ }
+ currentSize += chunk.length
+ callback();
+ }
+ });
+ return {
+ stream: stream,
+ getBuffer: function() {
+ return buffer;
+ }
+ }
+ }
+}
diff --git a/utility/annotate-image/package.json b/utility/annotate-image/package.json
new file mode 100644
index 00000000..8b3564e4
--- /dev/null
+++ b/utility/annotate-image/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "node-red-node-annotate-image",
+ "version": "0.1.1",
+ "description": "A Node-RED node that can annotate an image",
+ "dependencies": {
+ "pureimage": "^0.2.5"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/node-red/node-red-nodes/tree/master/utility/iamge-annotate"
+ },
+ "license": "Apache-2.0",
+ "keywords": [
+ "node-red"
+ ],
+ "node-red": {
+ "nodes": {
+ "annotate": "annotate.js"
+ }
+ },
+ "contributors": [
+ {
+ "name": "Nick O'Leary"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/utility/exif/94-exif.html b/utility/exif/94-exif.html
index 3b019849..3f7d99ac 100644
--- a/utility/exif/94-exif.html
+++ b/utility/exif/94-exif.html
@@ -1,17 +1,31 @@
-
-
diff --git a/utility/exif/94-exif.js b/utility/exif/94-exif.js
index 93ba0b3b..ff7e07e1 100644
--- a/utility/exif/94-exif.js
+++ b/utility/exif/94-exif.js
@@ -1,7 +1,6 @@
module.exports = function(RED) {
"use strict";
- var ExifImage = require('exif').ExifImage;
function convertDegreesMinutesSecondsToDecimals(degrees, minutes, seconds) {
var result;
@@ -11,7 +10,11 @@ module.exports = function(RED) {
function ExifNode(n) {
RED.nodes.createNode(this,n);
+ this.mode = n.mode || "normal";
+ if (this.mode === "worldmap") { this.property = "payload.content"; }
+ else { this.property = n.property || "payload"; }
var node = this;
+ var ExifImage = require('exif').ExifImage;
/***
* Extracts GPS location information from Exif data. If enough information is
@@ -20,7 +23,7 @@ module.exports = function(RED) {
* Assumes that the msg object will always have exifData available as msg.exif.
* Assume that the GPS data saved into Exif provides a valid value
*/
- function addMsgLocationDataFromExifGPSData(msg) {
+ function addMsgLocationDataFromExifGPSData(msg,val) {
var gpsData = msg.exif.gps; // declaring variable purely to make checks more readable
if (gpsData.GPSAltitude) {
/* istanbul ignore else */
@@ -31,14 +34,12 @@ module.exports = function(RED) {
// The data provided in Exif is in degrees, minutes, seconds, this is to be converted into a single floating point degree
if (gpsData.GPSLatitude.length === 3) { // OK to convert latitude
if (gpsData.GPSLongitude.length === 3) { // OK to convert longitude
-
var latitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLatitude[0], gpsData.GPSLatitude[1], gpsData.GPSLatitude[2]);
latitude = Math.round(latitude * 100000)/100000; // 5dp is approx 1m resolution...
// (N)orth means positive latitude, (S)outh means negative latitude
if (gpsData.GPSLatitudeRef.toString() === 'S' || gpsData.GPSLatitudeRef.toString() === 's') {
latitude = latitude * -1;
}
-
var longitude = convertDegreesMinutesSecondsToDecimals(gpsData.GPSLongitude[0], gpsData.GPSLongitude[1], gpsData.GPSLongitude[2]);
longitude = Math.round(longitude * 100000)/100000; // 5dp is approx 1m resolution...
// (E)ast means positive longitude, (W)est means negative longitude
@@ -49,7 +50,6 @@ module.exports = function(RED) {
if (!msg.location) { msg.location = {}; }
msg.location.lat = latitude;
msg.location.lon = longitude;
- return;
}
else {
node.log("Invalid longitude data, no location information has been added to the message.");
@@ -62,21 +62,49 @@ module.exports = function(RED) {
else {
node.log("The location of this image cannot be determined safely so no location information has been added to the message.");
}
+ msg.location.arc = {
+ ranges: [500,1000,2000],
+ pan: gpsData.GPSImgDirection,
+ fov: (2 * Math.atan(36 / (2 * msg.exif.exif.FocalLengthIn35mmFormat)) * 180 / Math.PI),
+ color: '#910000'
+ }
+ msg.location.icon = "fa-camera";
+ var na;
+ if (val.hasOwnProperty("name")) { na = val.name; }
+ else if (msg.hasOwnProperty("filename")) { na = msg.filename.split('/').pop(); }
+ else { na = msg.exif.image.Make+"_"+msg.exif.image.ModifyDate; }
+ msg.location.name = na;
+ msg.location.layer = "Images";
+ msg.location.popup = '