From 770ad94e95c8464b92d82a34c43f1daf19f35649 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 23 Oct 2020 16:12:49 +0100 Subject: [PATCH 01/43] Add Japanese translations for Sense HAT node (#704) --- hardware/sensehat/locales/en-US/sensehat.json | 12 +++++++++++ hardware/sensehat/locales/ja/sensehat.json | 12 +++++++++++ hardware/sensehat/sensehat.html | 20 +++++++++---------- 3 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 hardware/sensehat/locales/en-US/sensehat.json create mode 100644 hardware/sensehat/locales/ja/sensehat.json diff --git a/hardware/sensehat/locales/en-US/sensehat.json b/hardware/sensehat/locales/en-US/sensehat.json new file mode 100644 index 00000000..40656db3 --- /dev/null +++ b/hardware/sensehat/locales/en-US/sensehat.json @@ -0,0 +1,12 @@ +{ + "sensehat": { + "label": { + "outputs": "Outputs", + "motionEvents": "Motion events", + "motionEventsExamples": "accelerometer, gyroscope, magnetometer, compass", + "environmentEvents": "Environment events", + "environmentEventsExamples": "temperature, humidity, pressure", + "joystickEvents": "Joystick events" + } + } +} \ No newline at end of file diff --git a/hardware/sensehat/locales/ja/sensehat.json b/hardware/sensehat/locales/ja/sensehat.json new file mode 100644 index 00000000..8083b49e --- /dev/null +++ b/hardware/sensehat/locales/ja/sensehat.json @@ -0,0 +1,12 @@ +{ + "sensehat": { + "label": { + "outputs": "出力", + "motionEvents": "モーションイベント", + "motionEventsExamples": "加速度計、ジャイロスコープ、磁力計、方位計", + "environmentEvents": "環境イベント", + "environmentEventsExamples": "温度、湿度、気圧", + "joystickEvents": "ジョイスティックイベント" + } + } +} \ No newline at end of file diff --git a/hardware/sensehat/sensehat.html b/hardware/sensehat/sensehat.html index 25091b7a..41c42dd6 100644 --- a/hardware/sensehat/sensehat.html +++ b/hardware/sensehat/sensehat.html @@ -1,30 +1,30 @@ From 3b7fb0aa954afeaf551ab11047147d632dce6317 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 24 Oct 2020 15:02:45 +0100 Subject: [PATCH 02/43] tidy up xmpp node --- .gitignore | 2 + social/xmpp/92-xmpp.html | 2 +- social/xmpp/92-xmpp.js | 197 +++++++++++++++++++++++++++------------ social/xmpp/package.json | 2 +- 4 files changed, 139 insertions(+), 64 deletions(-) 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/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html index 3de2bb25..83d653e4 100644 --- a/social/xmpp/92-xmpp.html +++ b/social/xmpp/92-xmpp.html @@ -109,7 +109,7 @@ + + + + diff --git a/utility/annotate-image/annotate.js b/utility/annotate-image/annotate.js new file mode 100644 index 00000000..ea5edb75 --- /dev/null +++ b/utility/annotate-image/annotate.js @@ -0,0 +1,155 @@ +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; + 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"; + ctx.fillText(annotation.label, x+2,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..48b164f3 --- /dev/null +++ b/utility/annotate-image/package.json @@ -0,0 +1,26 @@ +{ + "name": "node-red-node-annotate-image", + "version": "0.1.0", + "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 From 3d3841e65189f3b5f0077ce89f041636c6194206 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 29 Oct 2020 22:47:30 +0000 Subject: [PATCH 08/43] Fix license --- utility/annotate-image/LICENSE | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utility/annotate-image/LICENSE b/utility/annotate-image/LICENSE index f5b60114..335c35ff 100644 --- a/utility/annotate-image/LICENSE +++ b/utility/annotate-image/LICENSE @@ -1,5 +1,4 @@ -Copyright 2016 JS Foundation and other contributors, https://js.foundation/ -Copyright 2013-2016 IBM Corp. +Copyright 2020 OpenJS Foundation and other contributors, https://openjsf.org/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 056836d1e5421335e4723ad53fddf04e892edd6e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 30 Oct 2020 09:51:03 +0000 Subject: [PATCH 09/43] Tidy up edit dialog --- utility/annotate-image/README.md | 4 ++-- utility/annotate-image/annotate.html | 35 ++++++++++++++-------------- utility/annotate-image/annotate.js | 5 ++++ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/utility/annotate-image/README.md b/utility/annotate-image/README.md index d541563a..de17bc47 100644 --- a/utility/annotate-image/README.md +++ b/utility/annotate-image/README.md @@ -31,9 +31,9 @@ Each annotation is an object with the following properties: - `r` (*number*) : the radius of a `circle` annotation - `bbox` (*array*) : this can be used instead of `x`, `y`, `w`, `h` and `r`. It should be an array of four values giving the bounding box of the annotation: - `[x, y, w, h]`. + `[x, y, w, h]`. If this property is set and `type` is not set, it will default to `rect`. - `label` (*string*) : an optional piece of text to label the annotation with - - `stroke` (*string*) : the color of the annotation. Default: `#ffC000` + - `stroke` (*string*) : the line color of the annotation. Default: `#ffC000` - `lineWidth` (*number*) : the stroke width used to draw the annotation. Default: `5` - `fontSize` (*number*) : the font size to use for the label. Default: `24` - `fontColor` (*string*) : the color of the font to use for the label. Default: `#ffC000` diff --git a/utility/annotate-image/annotate.html b/utility/annotate-image/annotate.html index efc18e25..f87a76b6 100644 --- a/utility/annotate-image/annotate.html +++ b/utility/annotate-image/annotate.html @@ -1,23 +1,23 @@ - - - - diff --git a/utility/exif/94-exif.js b/utility/exif/94-exif.js index 93ba0b3b..dbddba9f 100644 --- a/utility/exif/94-exif.js +++ b/utility/exif/94-exif.js @@ -11,6 +11,7 @@ module.exports = function(RED) { function ExifNode(n) { RED.nodes.createNode(this,n); + this.property = n.property || "payload"; var node = this; /*** @@ -20,7 +21,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 +32,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 +48,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,13 +60,32 @@ 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.popup = '' } this.on("input", function(msg) { try { - if (msg.payload) { - if (Buffer.isBuffer(msg.payload)) { - new ExifImage({ image : msg.payload }, function (error, exifData) { + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { + if (typeof value === "string") { // it must be a base64 encoded inline image type + if (value.indexOf('data:image') !== -1) { + value = new Buffer.from(value.replace(/^data:image\/[a-z]+;base64,/, ""), 'base64'); + } + } + if (Buffer.isBuffer(value)) { // or a proper jpg buffer + new ExifImage({ image:value }, function (error, exifData) { if (error) { node.log(error.toString()); } @@ -76,7 +93,7 @@ module.exports = function(RED) { if (exifData) { msg.exif = exifData; if ((exifData.hasOwnProperty("gps")) && (Object.keys(exifData.gps).length !== 0)) { - addMsgLocationDataFromExifGPSData(msg); + addMsgLocationDataFromExifGPSData(msg,value); } //else { node.log("The incoming image did not contain Exif GPS data."); } } @@ -93,7 +110,7 @@ module.exports = function(RED) { } } else { - node.error("No payload received, the Exif node cannot proceed, no messages sent.",msg); + node.warn("No input received, the Exif node cannot proceed, no messages sent.",msg); return; } } diff --git a/utility/exif/package.json b/utility/exif/package.json index fec3db6c..5eb5e30b 100644 --- a/utility/exif/package.json +++ b/utility/exif/package.json @@ -1,23 +1,30 @@ { - "name" : "node-red-node-exif", - "version" : "0.0.7", - "description" : "A Node-RED node that extracts Exif information from JPEG image buffers.", - "dependencies" : { - "exif": "0.4.0" + "name": "node-red-node-exif", + "version": "0.1.0", + "description": "A Node-RED node that extracts Exif information from JPEG image buffers.", + "dependencies": { + "exif": "^0.6.0" }, - "repository" : { - "type":"git", - "url":"https://github.com/node-red/node-red-nodes/tree/master/utility/exif" + "repository": { + "type": "git", + "url": "https://github.com/node-red/node-red-nodes/tree/master/utility/exif" }, "license": "Apache-2.0", - "keywords": [ "node-red", "exif"], - "node-red" : { - "nodes" : { + "keywords": [ + "node-red", + "exif" + ], + "node-red": { + "nodes": { "exif": "94-exif.js" } }, "contributors": [ - {"name": "Dave Conway-Jones"}, - {"name": "Zoltan Balogh"} + { + "name": "Dave Conway-Jones" + }, + { + "name": "Zoltan Balogh" + } ] } From c61e8b60b78c1159e1de209fea9bf0a1f1252984 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Mon, 7 Dec 2020 17:34:44 +0000 Subject: [PATCH 23/43] Add dedicated Worldmap mode better handling of other types --- utility/exif/94-exif.html | 17 +++++++++++++++++ utility/exif/94-exif.js | 18 ++++++++++++++++-- utility/exif/package.json | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/utility/exif/94-exif.html b/utility/exif/94-exif.html index 47c79101..3f7d99ac 100644 --- a/utility/exif/94-exif.html +++ b/utility/exif/94-exif.html @@ -1,6 +1,13 @@ diff --git a/utility/exif/94-exif.js b/utility/exif/94-exif.js index dbddba9f..0b1da20f 100644 --- a/utility/exif/94-exif.js +++ b/utility/exif/94-exif.js @@ -11,7 +11,9 @@ module.exports = function(RED) { function ExifNode(n) { RED.nodes.createNode(this,n); - this.property = n.property || "payload"; + this.mode = n.mode || "normal"; + if (this.mode === "worldmap") { this.property = "payload.content"; } + else { this.property = n.property || "payload"; } var node = this; /*** @@ -72,10 +74,12 @@ module.exports = function(RED) { 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 = '' } this.on("input", function(msg) { + if (node.mode === "worldmap" && (msg.payload.action !== "file" || msg.payload.type.indexOf("image") === -1)) { return; } // in case worldmap-in not filtered. try { var value = RED.util.getMessageProperty(msg,node.property); if (value !== undefined) { @@ -87,7 +91,13 @@ module.exports = function(RED) { if (Buffer.isBuffer(value)) { // or a proper jpg buffer new ExifImage({ image:value }, function (error, exifData) { if (error) { - node.log(error.toString()); + if (node.mode !== "worldmap") { + node.log(error.toString()); + } + else { + msg.location = {name:msg.payload.name, lat:msg.payload.lat, lon:msg.payload.lon, layer:"Images", icon:"fa-camera", draggable:true}; + msg.location.popup = '
'; + } } else { if (exifData) { @@ -101,6 +111,10 @@ module.exports = function(RED) { node.warn("The incoming image did not contain any Exif data, nothing to do."); } } + if (node.mode === "worldmap") { + msg.payload = msg.location; + delete msg.location; + } node.send(msg); }); } diff --git a/utility/exif/package.json b/utility/exif/package.json index 5eb5e30b..639bbcef 100644 --- a/utility/exif/package.json +++ b/utility/exif/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-exif", - "version": "0.1.0", + "version": "0.2.0", "description": "A Node-RED node that extracts Exif information from JPEG image buffers.", "dependencies": { "exif": "^0.6.0" From 8130f8c007a59216dc680416fed108c59b2bb9fb Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 12 Dec 2020 14:26:31 +0000 Subject: [PATCH 24/43] add extra gpoio support to neopixel node --- hardware/neopixel/neopix.py | 2 ++ hardware/neopixel/neopixel.html | 12 ++++++++++-- hardware/neopixel/neopixel.js | 5 ++++- hardware/neopixel/package.json | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/hardware/neopixel/neopix.py b/hardware/neopixel/neopix.py index 83756f0f..2cd53f19 100755 --- a/hardware/neopixel/neopix.py +++ b/hardware/neopixel/neopix.py @@ -47,6 +47,8 @@ MODE = sys.argv[3] LED_BRIGHTNESS = min(255,int(max(0,float(sys.argv[4])) * 255 / 100)) if (sys.argv[5].lower() != "true"): LED_GAMMA = range(256) +LED_CHANNEL = int(sys.argv[6]) +LED_PIN = int(sys.argv[7]) def getRGBfromI(RGBint): blue = RGBint & 255 diff --git a/hardware/neopixel/neopixel.html b/hardware/neopixel/neopixel.html index 794599df..ca28a4bd 100644 --- a/hardware/neopixel/neopixel.html +++ b/hardware/neopixel/neopixel.html @@ -1,8 +1,15 @@ - - diff --git a/hardware/neopixel/package.json b/hardware/neopixel/package.json index 05d0fbf8..6a1e65eb 100644 --- a/hardware/neopixel/package.json +++ b/hardware/neopixel/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-pi-neopixel", - "version" : "0.1.0", + "version" : "0.1.1", "description" : "A Node-RED node to output to a neopixel (ws2812) string of LEDS from a Raspberry Pi.", "dependencies" : { }, From 9eb031e912bad745b5d52d9802f0d53eff61a592 Mon Sep 17 00:00:00 2001 From: juggledad Date: Sun, 13 Dec 2020 07:58:13 -0500 Subject: [PATCH 26/43] Update node-red-random to add option to dynamically pass in 'To' and 'From' (#707) * Add option to dynamically pass in 'From' and 'To' - fixed bug when 'From' was greater than 'To' by flipping the two * Set initial values of 'From' and 'To to "" to allow dynamic overriding them --- function/random/README.md | 2 + function/random/locales/en-US/random.html | 9 +- function/random/random.html | 4 +- function/random/random.js | 74 +++++- test/function/random/random_spec.js | 273 ++++++++++++++++++---- 5 files changed, 303 insertions(+), 59 deletions(-) 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..0bc93b13 100644 --- a/function/random/locales/en-US/random.html +++ b/function/random/locales/en-US/random.html @@ -1,7 +1,14 @@ 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..2140717d 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); - } - else { - value = Math.random() * (node.high - node.low) + node.low; - } - RED.util.setMessageProperty(msg,node.property,value); - node.send(msg); + + 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 = 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 + } + } + + 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 = 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.nodes.registerType("random",RandomNode); diff --git a/test/function/random/random_spec.js b/test/function/random/random_spec.js index ffd119f2..d352c12a 100644 --- a/test/function/random/random_spec.js +++ b/test/function/random/random_spec.js @@ -15,74 +15,257 @@ describe('random node', function() { helper.stopServer(done); }); }); + +// ============================================================ - it("should be loaded with correct defaults", function(done) { - var flow = [{"id":"n1", "type":"random", "name":"random1", "wires":[[]]}]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - //console.log(n1); - n1.should.have.property("low", 1); - n1.should.have.property("high", 10); - n1.should.have.property("inte", false); - done(); - }); - }); - - it('should output an integer between -3 and 3', function(done) { - var flow = [{"id":"n1", "type":"random", low:-3, high:3, inte:true, wires:[["n2"]] }, + it ("Test i1 (integer) - DEFAULT no overrides defaults to 1 and 10", function(done) { + var flow = [{id:"n1", type:"random", low: "" , high:"" , inte:true, wires:[["n2"]] }, {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { + helper.load(testNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload"); - msg.payload.should.be.approximately(0,3); - msg.payload.toString().indexOf(".").should.equal(-1); - done(); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(1,10); + msg.payload.toString().indexOf(".").should.equal(-1); // see if it's really an integer and not a float... + done(); } + catch(err) { done(err); } }); - n1.emit("input", {payload:"a"}); + n1.emit("input", {"test":"Test i1"}); }); }); - it('should output an float between 20 and 30', function(done) { - var flow = [{"id":"n1", "type":"random", low:20, high:30, inte:false, wires:[["n2"]] }, + it ("Test f1 (float) - DEFAULT no overrides defaults to 1 and 10", function(done) { + var flow = [{id:"n1", type:"random", low:"" , high:"" , inte:false, wires:[["n2"]] }, {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { + helper.load(testNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload"); - msg.payload.should.be.approximately(25,5); - msg.payload.toString().indexOf(".").should.not.equal(-1); - done(); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(1.0,9.999); + //msg.payload.toString().indexOf(".").should.not.equal(-1); + done(); } + catch(err) { done(err); } }); - n1.emit("input", {payload:"a"}); + n1.emit("input", {"test":"Test f-1"}); }); }); - it('should output an integer between -3 and 3 on chosen property - foo', function(done) { - var flow = [{"id":"n1", "type":"random", property:"foo", low:-3, high:3, inte:true, wires:[["n2"]] }, +// ============================================================ + + it ("Test i2 (integer) - FLIP node From = 3 To = -3", function(done) { + var flow = [{id:"n1", type:"random", low: 3, high: -3, inte:true, wires:[["n2"]] }, {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { + helper.load(testNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("foo"); - msg.foo.should.be.approximately(0,3); - msg.foo.toString().indexOf(".").should.equal(-1); - done(); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(-3,3); + msg.payload.toString().indexOf(".").should.equal(-1); // slightly dumb test to see if it really is an integer and not a float... + done(); } + catch(err) { done(err); } }); - n1.emit("input", {payload:"a"}); + n1.emit("input", {"test":"Test i2"}); }); }); + it ("Test f2 (float) - FLIP node From = 3 To = -3", function(done) { + var flow = [{id:"n1", type:"random", low: 3, high: -3, inte:false, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(-3.0,3.0); + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test f2"}); + }); + }); + +// ============================================================ + + it ("Test i3 (integer) - values in msg From = 2 To = '5', node no entries", function(done) { + var flow = [{id:"n1", type:"random", low: "", high: "", inte:true, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(2,5); + msg.payload.toString().indexOf(".").should.equal(-1); // slightly dumb test to see if it really is an integer and not a float... + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test i3", "from":2, "to":'5'}); + }); + }); + + it ("Test f3 (float) - values in msg From = 2 To = '5', node no entries", function(done) { + var flow = [{id:"n1", type:"random", low: "", high: "", inte:false, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(2.0,5.0); + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test f3", "from":2, "to":'5'}); + }); + }); + + + // ============================================================ + + it ("Test i4 (integer) - value in msg From = 2, node From = 5 To = '' - node overides From = 5 To defaults to 10", function(done) { + var flow = [{id:"n1", type:"random", low: 5, high:"", inte:true, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(5,10); + msg.payload.toString().indexOf(".").should.equal(-1); + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test i4", "from": 2}); + }); + }); + + it ("Test f4 (float) - value in msg From = 2, node From = 5 To = '' - node wins 'To' defaults to 10", function(done) { + var flow = [{id:"n1", type:"random", low: 5, high:"", inte:false, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(5.0,10.0); + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test f4", "from": 2}); + }); + }); + +// ============================================================ + + it ("Test i5 (integer) - msg From = '6' To = '9' node no entries", function(done) { + var flow = [{id:"n1", type:"random", low: "", high: "", inte:true, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(6,9); + msg.payload.toString().indexOf(".").should.equal(-1); // slightly dumb test to see if it really is an integer and not a float... + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test i5", "from": '6', "to": '9'}); + }); + }); + + it ("Test f5 (float) - msg From = '6' To = '9' node no entries", function(done) { + var flow = [{id:"n1", type:"random", low: "", high: "", inte:false, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(6.0,9.0); + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test f5", "from": '6', "to": '9'}); + }); + }); + +// ============================================================ + + it ("Test i6 (integer) - msg From = 2.4 To = '7.3' node no entries", function(done) { + var flow = [{id:"n1", type:"random", low: "", high: "", inte:true, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(2,7); + msg.payload.toString().indexOf(".").should.equal(-1); // slightly dumb test to see if it really is an integer and not a float... + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test i6", "from": 2.4, "to": '7.3'}); + }); + }); + + it ("Test f6 (float) - msg From = 2.4 To = '7.3' node no entries", function(done) { + var flow = [{id:"n1", type:"random", low: "", high: "", inte:false, wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + try { + //console.log(msg); + msg.should.have.a.property("payload"); + msg.payload.should.be.within(2.4,7.3); + done(); + } + catch(err) { done(err); } + }); + n1.emit("input", {"test":"Test f6", "from": 2.4, "to": '7.3'}); + }); + }); + +// ============================================================ + + + }); From 354370e6e0f8f3b05b3af52dcbf7418ceaad057f Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sun, 13 Dec 2020 17:38:17 +0000 Subject: [PATCH 27/43] Tidy up random node info. --- function/random/locales/en-US/random.html | 20 +++++++++++--------- function/random/package.json | 7 +++++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/function/random/locales/en-US/random.html b/function/random/locales/en-US/random.html index 0bc93b13..9c18b0da 100644 --- a/function/random/locales/en-US/random.html +++ b/function/random/locales/en-US/random.html @@ -1,14 +1,16 @@ diff --git a/function/random/package.json b/function/random/package.json index 43965ece..03075efb 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.0", "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"} + ] } From cbcb3036be7988bab75a3371fd78c891352aecb3 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 14 Dec 2020 22:35:39 +0900 Subject: [PATCH 28/43] Add Japanese translations for random node (#724) --- function/random/locales/ja/random.html | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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 @@ From fa2ea96e6767b5d364e6c3e974a08e6d4e5f5127 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Thu, 17 Dec 2020 17:26:32 +0900 Subject: [PATCH 29/43] Add Japanese translations for sqlite node (#725) * Add Japanese translations for sqlite node * Improve Japanese translations for sqlite node * Restructure message catalog --- storage/sqlite/locales/en-US/sqlite.html | 28 +++++++++++ storage/sqlite/locales/en-US/sqlite.json | 20 ++++++++ storage/sqlite/locales/ja/sqlite.html | 26 ++++++++++ storage/sqlite/locales/ja/sqlite.json | 20 ++++++++ storage/sqlite/sqlite.html | 62 ++++++------------------ 5 files changed, 109 insertions(+), 47 deletions(-) create mode 100644 storage/sqlite/locales/en-US/sqlite.html create mode 100644 storage/sqlite/locales/en-US/sqlite.json create mode 100644 storage/sqlite/locales/ja/sqlite.html create mode 100644 storage/sqlite/locales/ja/sqlite.json diff --git a/storage/sqlite/locales/en-US/sqlite.html b/storage/sqlite/locales/en-US/sqlite.html new file mode 100644 index 00000000..7aedae62 --- /dev/null +++ b/storage/sqlite/locales/en-US/sqlite.html @@ -0,0 +1,28 @@ + + + diff --git a/storage/sqlite/locales/en-US/sqlite.json b/storage/sqlite/locales/en-US/sqlite.json new file mode 100644 index 00000000..059f1194 --- /dev/null +++ b/storage/sqlite/locales/en-US/sqlite.json @@ -0,0 +1,20 @@ +{ + "sqlite": { + "label": { + "database": "Database", + "sqlQuery": "SQL Query", + "viaMsgTopic": "Via msg.topic", + "fixedStatement": "Fixed Statement", + "preparedStatement": "Prepared Statement", + "batchWithoutResponse": "Batch without response", + "sqlStatement": "SQL Statement", + "mode": "Mode", + "readWriteCreate": "Read-Write-Create", + "readWrite": "Read-Write", + "readOnly": "Read-Only" + }, + "tips": { + "memoryDb": "Note: Setting the database name to :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/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", From ea92f293e2ba42d62c507a9fdc78be522a5f769f Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sun, 17 Jan 2021 17:29:57 +0000 Subject: [PATCH 39/43] Bump annotate image version from PR. --- utility/annotate-image/README.md | 4 ++-- utility/annotate-image/annotate.html | 11 ++++------- utility/annotate-image/package.json | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/utility/annotate-image/README.md b/utility/annotate-image/README.md index ba7bde25..2952ad15 100644 --- a/utility/annotate-image/README.md +++ b/utility/annotate-image/README.md @@ -11,7 +11,7 @@ detected in the image by a TensorFlow node. Install ------- -Run the following command in your Node-RED user directory - typically `~/.node-red` +Either use the Edit Menu - Manage Palette option to install, or run the following command in your Node-RED user directory - typically `~/.node-red` npm install node-red-node-annotate-image @@ -37,7 +37,7 @@ Each annotation is an object with the following properties: - `lineWidth` (*number*) : the stroke width used to draw the annotation. Default: `5` - `fontSize` (*number*) : the font size to use for the label. Default: `24` - `fontColor` (*string*) : the color of the font to use for the label. Default: `#ffC000` - - `labelLocation` (*string*) : The Location to place the label. `top` or `bottom`. + - `labelLocation` (*string*) : The location to place the label. `top` or `bottom`. If this propery is not set it will default to `automatic` and make the best guess based on location. diff --git a/utility/annotate-image/annotate.html b/utility/annotate-image/annotate.html index d0500625..b43eae69 100644 --- a/utility/annotate-image/annotate.html +++ b/utility/annotate-image/annotate.html @@ -1,5 +1,5 @@ - - -