From 7447cdc56a1b945c073f08dc74d7f9b7bba531f0 Mon Sep 17 00:00:00 2001 From: lucarin91 Date: Wed, 4 Dec 2024 10:39:51 +0100 Subject: [PATCH 1/4] chore: add prettier for code formatting --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7127cfc..3bc06d2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,9 @@ "version": "1.1.0", "description": "Node-RED nodes to talk to Arduino IoT Cloud", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "fmt": "prettier --write .", + "fmt-check": "prettier --check ." }, "keywords": [ "node-red", @@ -26,6 +28,9 @@ "mqtt": "^3.0.0", "superagent": "^6.1.0" }, + "devDependencies": { + "prettier": "^3.4.2" + }, "repository": { "type": "git", "url": "git+https://github.com/arduino/node-red-contrib-arduino-iot-cloud.git" From f922030ed5d3493f5841502aed0d3619b911d0bc Mon Sep 17 00:00:00 2001 From: lucarin91 Date: Wed, 4 Dec 2024 10:40:27 +0100 Subject: [PATCH 2/4] run format --- README.md | 48 +- arduino-iot-client-mqtt/ArduinoCloudError.js | 36 +- .../arduino-iot-client-mqtt.js | 384 ++++---- arduino-iot-cloud.html | 830 ++++++++++-------- arduino-iot-cloud.js | 696 ++++++++++----- locales/en-US/arduino-iot-cloud.html | 5 +- locales/en-US/arduino-iot-cloud.json | 96 +- utils/arduino-connection-manager.js | 221 +++-- utils/arduino-iot-cloud-api-wrapper.js | 69 +- 9 files changed, 1431 insertions(+), 954 deletions(-) diff --git a/README.md b/README.md index 9532c1c..951466a 100644 --- a/README.md +++ b/README.md @@ -4,63 +4,77 @@ This module implements Node-RED nodes for interacting with [Arduino IoT Cloud](h Multi Arduino Accounts are allowed. ## Docker and Node-RED installation + The easiest way to install Docker on a linux system is to use [the convenience script](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script): + ```shell curl -sSL https://get.docker.com/ | sh ``` + To install Node-RED you can use the [official docker container](https://hub.docker.com/r/nodered/node-red): + ```shell docker run -it -p 1880:1880 -v myNodeREDdata:/data --name mynodered nodered/node-red ``` - ## Installation -Just search *node-red-contrib-arduino-iot-cloud* in the Node-RED palette manager and click on install + +Just search _node-red-contrib-arduino-iot-cloud_ in the Node-RED palette manager and click on install Or you can install the plugin by using `npm` in your `$HOME/.node-red` directory: `npm install @arduino/node-red-contrib-arduino-iot-cloud` ### Update -If you're using a service hosting your node-red instance and you want to be sure that you're using the latest version of *node-red-contrib-arduino-iot-cloud* published on npm, you might need to force require an update. + +If you're using a service hosting your node-red instance and you want to be sure that you're using the latest version of _node-red-contrib-arduino-iot-cloud_ published on npm, you might need to force require an update. You can use either one of these methods: - - Go to [https://flows.nodered.org/add/node](https://flows.nodered.org/add/node) and write `node-red-contrib-arduino-iot-cloud` in the input field - - If you are logged in, you should see a `check for update` on the right side of the [module's page](https://flows.nodered.org/node/@arduino/node-red-contrib-arduino-iot-cloud) + +- Go to [https://flows.nodered.org/add/node](https://flows.nodered.org/add/node) and write `node-red-contrib-arduino-iot-cloud` in the input field +- If you are logged in, you should see a `check for update` on the right side of the [module's page](https://flows.nodered.org/node/@arduino/node-red-contrib-arduino-iot-cloud) ## Configuration -1. Obtain Client ID and Client Secret from the [integrations webpage](https://create.arduino.cc/iot/integrations) by clicking on *Create API key* + +1. Obtain Client ID and Client Secret from the [integrations webpage](https://create.arduino.cc/iot/integrations) by clicking on _Create API key_ 2. Go to Node-RED web page 3. Select one Arduino nodes from the pallete and drag to a flow 4. Double click on the node - * set a new connection - + select 'Add new arduino-connection...' in the field 'Connection' - + Click edit (Pencil button) - + Insert a connection name, Client ID and Client Secret (collected at point 1) - + Click Add - * Select a thing - * Select a Property - * Set a name + - set a new connection + - select 'Add new arduino-connection...' in the field 'Connection' + - Click edit (Pencil button) + - Insert a connection name, Client ID and Client Secret (collected at point 1) + - Click Add + - Select a thing + - Select a Property + - Set a name 5. Connect Arduino property input node to other nodes to consume data coming from a thing property. 6. Send a payload to the Arduino property output node to change the value of a thing property. ## Nodes ### property (In) + This node injects in the flow the changed value of a specific Arduino IoT Cloud property. ### property (Out) + This node update a specific Arduino IoT Cloud property with the value received in input ### historic + This node injects in the flow a set of values of an Arduino IoT Cloud Property based on the node configuration. Node parameter: -+ Time Filter: defines time range for historical values + +- Time Filter: defines time range for historical values ### periodic + This node injects in the flow the value of an Arduino IoT Cloud Property with a periodicity based on the node configuration. Node parameter: -+ Poll Every: defines polling time interval (seconds, minutes, hours, days, weeks) + +- Poll Every: defines polling time interval (seconds, minutes, hours, days, weeks) ### inject -This node injects in the flow the value of an Arduino IoT Cloud Property after receiving an input event. \ No newline at end of file + +This node injects in the flow the value of an Arduino IoT Cloud Property after receiving an input event. diff --git a/arduino-iot-client-mqtt/ArduinoCloudError.js b/arduino-iot-client-mqtt/ArduinoCloudError.js index 596faba..a6d3d31 100644 --- a/arduino-iot-client-mqtt/ArduinoCloudError.js +++ b/arduino-iot-client-mqtt/ArduinoCloudError.js @@ -1,21 +1,21 @@ /* -* Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -* This file is part of node-red-contrib-arduino-iot-cloud. -* Copyright (c) 2019 -* -* This software is released under: -* The GNU General Public License, which covers the main part of -* node-red-contrib-arduino-iot-cloud -* The terms of this license can be found at: -* https://www.gnu.org/licenses/gpl-3.0.en.html -* -* You can be released from the requirements of the above licenses by purchasing -* a commercial license. Buying such a license is mandatory if you want to modify or -* otherwise use the software for commercial activities involving the Arduino -* software without disclosing the source code of your own applications. To purchase -* a commercial license, send an email to license@arduino.cc. -* -*/ + * Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + * This file is part of node-red-contrib-arduino-iot-cloud. + * Copyright (c) 2019 + * + * This software is released under: + * The GNU General Public License, which covers the main part of + * node-red-contrib-arduino-iot-cloud + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ module.exports = class ArduinoCloudError extends Error { constructor(code, message) { super(message); @@ -31,4 +31,4 @@ module.exports = class ArduinoCloudError extends Error { this.code = code; } -} +}; diff --git a/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js b/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js index edd9759..cd585d5 100644 --- a/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js +++ b/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js @@ -1,21 +1,21 @@ /* -* Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -* This file is part of node-red-contrib-arduino-iot-cloud. -* Copyright (c) 2019 -* -* This software is released under: -* The GNU General Public License, which covers the main part of -* node-red-contrib-arduino-iot-cloud -* The terms of this license can be found at: -* https://www.gnu.org/licenses/gpl-3.0.en.html -* -* You can be released from the requirements of the above licenses by purchasing -* a commercial license. Buying such a license is mandatory if you want to modify or -* otherwise use the software for commercial activities involving the Arduino -* software without disclosing the source code of your own applications. To purchase -* a commercial license, send an email to license@arduino.cc. -* -*/ + * Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + * This file is part of node-red-contrib-arduino-iot-cloud. + * Copyright (c) 2019 + * + * This software is released under: + * The GNU General Public License, which covers the main part of + * node-red-contrib-arduino-iot-cloud + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ /* SenML labels @@ -42,13 +42,13 @@ +---------------+-------+------------+------------+------------+ */ -const mqtt = require('mqtt'); -const CBOR = require('@arduino/cbor-js'); -const jws = require('jws'); -const ArduinoCloudError = require('./ArduinoCloudError'); +const mqtt = require("mqtt"); +const CBOR = require("@arduino/cbor-js"); +const jws = require("jws"); +const ArduinoCloudError = require("./ArduinoCloudError"); const arduinoCloudPort = 8443; -const arduinoCloudHost = 'wss.iot.arduino.cc'; +const arduinoCloudHost = "wss.iot.arduino.cc"; class ArduinoClientMqtt { constructor() { @@ -79,15 +79,21 @@ class ArduinoClientMqtt { }; if (this.connection) { - return reject(new Error('connection failed: connection already open')); + return reject(new Error("connection failed: connection already open")); } if (!this.opts.host) { - return reject(new Error('connection failed: you need to provide a valid host (broker)')); + return reject( + new Error( + "connection failed: you need to provide a valid host (broker)", + ), + ); } if (!this.opts.token) { - return reject(new Error('connection failed: you need to provide a valid token')); + return reject( + new Error("connection failed: you need to provide a valid token"), + ); } const userid = jws.decode(options.token).payload["http://arduino.cc/id"]; @@ -100,16 +106,19 @@ class ArduinoClientMqtt { protocolVersion: 4, connectTimeout: 30000, keepalive: 30, - clean: true + clean: true, }; - const client = mqtt.connect('wss://' + this.opts.host + ':' + this.opts.port + '/mqtt', connectionOpts); + const client = mqtt.connect( + "wss://" + this.opts.host + ":" + this.opts.port + "/mqtt", + connectionOpts, + ); this.connection = client; client.topics = {}; client.on("connect", () => { - if (typeof this.opts.onConnected === 'function') { + if (typeof this.opts.onConnected === "function") { this.opts.onConnected(); } @@ -117,13 +126,11 @@ class ArduinoClientMqtt { }); client.on("error", (err) => { - reject( - new ArduinoCloudError(5, err.toString()), - ); + reject(new ArduinoCloudError(5, err.toString())); }); client.on("message", (topic, msg) => { - if (topic.indexOf('/s/o') > -1) { + if (topic.indexOf("/s/o") > -1) { client.topics[topic].forEach((cb) => { cb(msg.toString()); }); @@ -139,16 +146,16 @@ class ArduinoClientMqtt { const attributeNameId = 1; let valueToSend = {}; - let propertyNameKeyPrevious = ''; - let propertyNameKey = ''; + let propertyNameKeyPrevious = ""; + let propertyNameKey = ""; propertyValue.forEach((p) => { // Support cbor labels - propertyNameKey = p.n !== undefined ? p.n : p['0']; - const propertyNameKeySplit = propertyNameKey.split(':'); + propertyNameKey = p.n !== undefined ? p.n : p["0"]; + const propertyNameKeySplit = propertyNameKey.split(":"); - const valueKey = p.v !== undefined ? 'v' : '2'; - const valueStringKey = p.vs !== undefined ? 'vs' : '3'; - const valueBooleanKey = p.vb !== undefined ? 'vb' : '4'; + const valueKey = p.v !== undefined ? "v" : "2"; + const valueStringKey = p.vs !== undefined ? "vs" : "3"; + const valueBooleanKey = p.vb !== undefined ? "vb" : "4"; let value = null; propertyNameKey = propertyNameKeySplit[propertyNameId]; if (this.propertyCallback[topic][propertyNameKey]) { @@ -160,13 +167,20 @@ class ArduinoClientMqtt { value = p[valueBooleanKey]; } } - if (propertyNameKeyPrevious === '') { + if (propertyNameKeyPrevious === "") { propertyNameKeyPrevious = propertyNameKeySplit[propertyNameId]; } if (propertyNameKeyPrevious !== propertyNameKey) { if (this.propertyCallback[topic][propertyNameKeyPrevious]) { - for(var i=0; i { this.opts.onOffline(); }); } - if (typeof this.opts.onDisconnect === 'function') { + if (typeof this.opts.onDisconnect === "function") { client.on("disconnect", () => { this.opts.onDisconnect(); }); @@ -201,7 +224,7 @@ class ArduinoClientMqtt { disconnect() { return new Promise((resolve, reject) => { if (!this.connection) { - return reject(new Error('disconnection failed: connection closed')); + return reject(new Error("disconnection failed: connection closed")); } try { @@ -231,7 +254,7 @@ class ArduinoClientMqtt { async reconnect() { await this.connection.reconnect(); - }; + } async updateToken(token) { // This infinite loop will exit once the reconnection is successful - @@ -256,7 +279,7 @@ class ArduinoClientMqtt { this.subscribe(subscribeParams.topic, subscribeParams.cb); }); - if (typeof this.opts.onConnected === 'function') { + if (typeof this.opts.onConnected === "function") { // Call the connection callback (with the reconnection param set to true) this.opts.onConnected(true); } @@ -274,12 +297,12 @@ class ArduinoClientMqtt { }); } } - }; + } subscribe(topic, cb) { return new Promise((resolve, reject) => { if (!this.connection) { - return reject(new Error('subscription failed: connection closed')); + return reject(new Error("subscription failed: connection closed")); } return this.connection.subscribe(topic, (err) => { @@ -298,31 +321,29 @@ class ArduinoClientMqtt { unsubscribe(topic) { return new Promise((resolve, reject) => { if (!this.connection) { - return reject(new Error('disconnection failed: connection closed')); + return reject(new Error("disconnection failed: connection closed")); } return this.connection.unsubscribe(topic, null, (err) => { - if (err) - reject(); - else - resolve(topic); + if (err) reject(); + else resolve(topic); }); }); } arrayBufferToBase64(buffer) { - let binary = ''; + let binary = ""; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i += 1) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); - }; + } sendMessage(topic, message) { return new Promise((resolve, reject) => { if (!this.connection) { - return reject(new Error('disconnection failed: connection closed')); + return reject(new Error("disconnection failed: connection closed")); } this.connection.publish(topic, message); @@ -333,18 +354,17 @@ class ArduinoClientMqtt { openCloudMonitor(deviceId, cb) { const cloudMonitorOutputTopic = `/a/d/${deviceId}/s/o`; return subscribe(cloudMonitorOutputTopic, cb); - }; - + } writeCloudMonitor(deviceId, message) { const cloudMonitorInputTopic = `/a/d/${deviceId}/s/i`; return sendMessage(cloudMonitorInputTopic, message); - }; + } closeCloudMonitor(deviceId) { const cloudMonitorOutputTopic = `/a/d/${deviceId}/s/o`; return unsubscribe(cloudMonitorOutputTopic); - }; + } toCloudProtocolV2(cborValue) { const cloudV2CBORValue = {}; @@ -352,49 +372,49 @@ class ArduinoClientMqtt { Object.keys(cborValue).forEach((label) => { switch (label) { - case 'bn': + case "bn": cborLabel = -2; break; - case 'bt': + case "bt": cborLabel = -3; break; - case 'bu': + case "bu": cborLabel = -4; break; - case 'bv': + case "bv": cborLabel = -5; break; - case 'bs': + case "bs": cborLabel = -6; break; - case 'bver': + case "bver": cborLabel = -1; break; - case 'n': + case "n": cborLabel = 0; break; - case 'u': + case "u": cborLabel = 1; break; - case 'v': + case "v": cborLabel = 2; break; - case 'vs': + case "vs": cborLabel = 3; break; - case 'vb': + case "vb": cborLabel = 4; break; - case 'vd': + case "vd": cborLabel = 8; break; - case 's': + case "s": cborLabel = 5; break; - case 't': + case "t": cborLabel = 6; break; - case 'ut': + case "ut": cborLabel = 7; break; default: @@ -405,46 +425,47 @@ class ArduinoClientMqtt { }); return cloudV2CBORValue; - }; + } sendProperty(thingId, name, value, timestamp) { const propertyInputTopic = `/a/t/${thingId}/e/i`; if (timestamp && !Number.isInteger(timestamp)) { - throw new Error('Timestamp must be Integer'); + throw new Error("Timestamp must be Integer"); } - if (name === undefined || typeof name !== 'string') { - throw new Error('Name must be a valid string'); + if (name === undefined || typeof name !== "string") { + throw new Error("Name must be a valid string"); } - if (typeof value === 'object') { + if (typeof value === "object") { const objectKeys = Object.keys(value); - const cborValues = objectKeys.map((key, i) => { - const cborValue = { - n: `${name}:${key}`, - }; - - if (i === 0) { - cborValue.bt = timestamp || new Date().getTime(); - } + const cborValues = objectKeys + .map((key, i) => { + const cborValue = { + n: `${name}:${key}`, + }; + + if (i === 0) { + cborValue.bt = timestamp || new Date().getTime(); + } - switch (typeof value[key]) { - case 'string': - cborValue.vs = value[key]; - break; - case 'number': - cborValue.v = value[key]; - break; - case 'boolean': - cborValue.vb = value[key]; - break; - default: - break; - } + switch (typeof value[key]) { + case "string": + cborValue.vs = value[key]; + break; + case "number": + cborValue.v = value[key]; + break; + case "boolean": + cborValue.vb = value[key]; + break; + default: + break; + } - return cborValue; - }) + return cborValue; + }) .map((cborValue) => { if (this.connectionOptions.useCloudProtocolV2) { return toCloudProtocolV2(cborValue); @@ -462,13 +483,13 @@ class ArduinoClientMqtt { }; switch (typeof value) { - case 'string': + case "string": cborValue.vs = value; break; - case 'number': + case "number": cborValue.v = value; break; - case 'boolean': + case "boolean": cborValue.vb = value; break; default: @@ -480,49 +501,49 @@ class ArduinoClientMqtt { } return sendMessage(propertyInputTopic, CBOR.encode([cborValue], true)); - }; + } getSenml(deviceId, name, value, timestamp) { if (timestamp && !Number.isInteger(timestamp)) { - throw new Error('Timestamp must be Integer'); + throw new Error("Timestamp must be Integer"); } - if (name === undefined || typeof name !== 'string') { - throw new Error('Name must be a valid string'); + if (name === undefined || typeof name !== "string") { + throw new Error("Name must be a valid string"); } - - if (typeof value === 'object') { + if (typeof value === "object") { const objectKeys = Object.keys(value); - const senMls = objectKeys.map((key, i) => { - const senMl = { - n: `${name}:${key}`, - }; + const senMls = objectKeys + .map((key, i) => { + const senMl = { + n: `${name}:${key}`, + }; - if (i === 0) { - senMl.bt = timestamp || new Date().getTime(); + if (i === 0) { + senMl.bt = timestamp || new Date().getTime(); - if (deviceId) { - senMl.bn = `urn:uuid:${deviceId}`; + if (deviceId) { + senMl.bn = `urn:uuid:${deviceId}`; + } } - } - switch (typeof value[key]) { - case 'string': - senMl.vs = value[key]; - break; - case 'number': - senMl.v = value[key]; - break; - case 'boolean': - senMl.vb = value[key]; - break; - default: - break; - } + switch (typeof value[key]) { + case "string": + senMl.vs = value[key]; + break; + case "number": + senMl.v = value[key]; + break; + case "boolean": + senMl.vb = value[key]; + break; + default: + break; + } - return senMl; - }) + return senMl; + }) .map((senMl) => { if (this.connectionOptions.useCloudProtocolV2) { return toCloudProtocolV2(senMl); @@ -544,13 +565,13 @@ class ArduinoClientMqtt { } switch (typeof value) { - case 'string': + case "string": senMl.vs = value; break; - case 'number': + case "number": senMl.v = value; break; - case 'boolean': + case "boolean": senMl.vb = value; break; default: @@ -562,25 +583,25 @@ class ArduinoClientMqtt { } return senMl; - }; + } getCborValue(senMl) { const cborEncoded = CBOR.encode(senMl); return arrayBufferToBase64(cborEncoded); - }; + } - onPropertyValue(thingId, name, cb,nodeId) { + onPropertyValue(thingId, name, cb, nodeId) { if (!name) { - throw new Error('Invalid property name'); + throw new Error("Invalid property name"); } - if (typeof cb !== 'function') { - throw new Error('Invalid callback'); + if (typeof cb !== "function") { + throw new Error("Invalid callback"); } var node; - if(!nodeId){ - node=1 - }else{ - node=nodeId; + if (!nodeId) { + node = 1; + } else { + node = nodeId; } const propOutputTopic = `/a/t/${thingId}/e/o`; @@ -594,66 +615,75 @@ class ArduinoClientMqtt { this.propertyCallback[propOutputTopic][name] = []; this.propertyCallback[propOutputTopic][name].push({ nodeId: node, - callback:cb + callback: cb, }); return this.subscribe(propOutputTopic, cb); } - if (this.propertyCallback[propOutputTopic] && !this.propertyCallback[propOutputTopic][name]) { + if ( + this.propertyCallback[propOutputTopic] && + !this.propertyCallback[propOutputTopic][name] + ) { this.propertyCallback[propOutputTopic][name] = []; this.propertyCallback[propOutputTopic][name].push({ nodeId: node, - callback:cb + callback: cb, }); - }else if(this.propertyCallback[propOutputTopic] && this.propertyCallback[propOutputTopic][name]){ + } else if ( + this.propertyCallback[propOutputTopic] && + this.propertyCallback[propOutputTopic][name] + ) { this.propertyCallback[propOutputTopic][name].push({ nodeId: node, - callback:cb + callback: cb, }); } return Promise.resolve(propOutputTopic); - }; - + } removePropertyValueCallback(thingId, name, nodeId) { if (!name) { - throw new Error('Invalid property name'); + throw new Error("Invalid property name"); } var node; - if(!nodeId){ - node=1 - }else{ - node=nodeId; + if (!nodeId) { + node = 1; + } else { + node = nodeId; } const propOutputTopic = `/a/t/${thingId}/e/o`; - var pos=-1; - for(var i=0; i - - + + - - - - diff --git a/arduino-iot-cloud.js b/arduino-iot-cloud.js index b039aba..a5a40ab 100644 --- a/arduino-iot-cloud.js +++ b/arduino-iot-cloud.js @@ -1,21 +1,21 @@ /* -* Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -* This file is part of node-red-contrib-arduino-iot-cloud. -* Copyright (c) 2019 -* -* This software is released under: -* The GNU General Public License, which covers the main part of -* node-red-contrib-arduino-iot-cloud -* The terms of this license can be found at: -* https://www.gnu.org/licenses/gpl-3.0.en.html -* -* You can be released from the requirements of the above licenses by purchasing -* a commercial license. Buying such a license is mandatory if you want to modify or -* otherwise use the software for commercial activities involving the Arduino -* software without disclosing the source code of your own applications. To purchase -* a commercial license, send an email to license@arduino.cc. -* -*/ + * Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + * This file is part of node-red-contrib-arduino-iot-cloud. + * Copyright (c) 2019 + * + * This software is released under: + * The GNU General Public License, which covers the main part of + * node-red-contrib-arduino-iot-cloud + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ const connectionManager = require("./utils/arduino-connection-manager"); const moment = require("moment"); @@ -27,33 +27,57 @@ module.exports = function (RED) { this.status({}); this.lastValue = undefined; this.organization = config.organization; - if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") { + if ( + connectionConfig && + config.thing !== "" && + config.thing !== "0" && + config.property !== "" && + config.property !== "0" + ) { try { this.thing = config.thing; this.propertyId = config.property; this.propertyName = config.propname; this.propertyVariableName = config.variableName; - this.arduinoClient = await connectionManager.getClientMqtt(connectionConfig, RED); + this.arduinoClient = await connectionManager.getClientMqtt( + connectionConfig, + RED, + ); if (this.arduinoClient && this.arduinoClient.connection.connected) { - await this.arduinoClient.onPropertyValue(this.thing, this.propertyVariableName,(msg) => { - this.send( - { + await this.arduinoClient.onPropertyValue( + this.thing, + this.propertyVariableName, + (msg) => { + this.send({ topic: this.propertyName, payload: msg, - timestamp: (new Date()).getTime() - } - ); - const s = getStatus(msg); - if (s != undefined) - this.status({ fill: "grey", shape: "dot", text: s }); - else - this.status({}); - },config.id); - }else{ - this.status({ fill: "red", shape: "ring", text: "arduino-iot-cloud.status.connection-error" }); + timestamp: new Date().getTime(), + }); + const s = getStatus(msg); + if (s != undefined) + this.status({ fill: "grey", shape: "dot", text: s }); + else this.status({}); + }, + config.id, + ); + } else { + this.status({ + fill: "red", + shape: "ring", + text: "arduino-iot-cloud.status.connection-error", + }); } - this.on('close', function (done) { - connectionManager.deleteClientMqtt(connectionConfig.credentials.clientid, this.thing, this.propertyVariableName,config.id).then(() => { done(); }); + this.on("close", function (done) { + connectionManager + .deleteClientMqtt( + connectionConfig.credentials.clientid, + this.thing, + this.propertyVariableName, + config.id, + ) + .then(() => { + done(); + }); }); //this.poll(connectionConfig); @@ -61,7 +85,7 @@ module.exports = function (RED) { console.log(err); } } - } + }; realConstructor.apply(this, [config]); } RED.nodes.registerType("property in", ArduinoIotInput); @@ -71,73 +95,121 @@ module.exports = function (RED) { RED.nodes.createNode(this, config); const connectionConfig = RED.nodes.getNode(config.connection); this.status({}); - if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") { + if ( + connectionConfig && + config.thing !== "" && + config.thing !== "0" && + config.property !== "" && + config.property !== "0" + ) { try { - if (config.thing !== "" && config.property !== "") { - this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig); - if (this.arduinoRestClient){ + this.arduinoRestClient = + await connectionManager.getClientHttp(connectionConfig); + if (this.arduinoRestClient) { this.arduinoRestClient.openConnections++; this.organization = config.organization; this.thing = config.thing; this.propertyId = config.property; this.propertyName = config.name; this.sendasdevice = config.sendasdevice; - this.device = config.device - const opts = {} + this.device = config.device; + const opts = {}; if (this.organization) { opts.xOrganization = this.organization; - } - this.on('input', async function (msg) { + } + this.on("input", async function (msg) { try { - await this.arduinoRestClient.setProperty(this.thing, this.propertyId, msg.payload, opts, this.sendasdevice ? this.device : undefined); + await this.arduinoRestClient.setProperty( + this.thing, + this.propertyId, + msg.payload, + opts, + this.sendasdevice ? this.device : undefined, + ); var s; if (typeof msg.payload !== "object") { s = getStatus(msg.payload); - }else{ - s="arduino-iot-cloud.status.object-sent"; + } else { + s = "arduino-iot-cloud.status.object-sent"; } if (s != undefined) this.status({ fill: "grey", shape: "dot", text: s }); - else - this.status({}); + else this.status({}); } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if ( + err.response && + err.response.res && + err.response.request + ) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-setting-value" }); + this.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.error-setting-value", + }); } }); - this.on('close', function (done) { - connectionManager.deleteClientHttp(connectionConfig.credentials.clientid).then(() => { done(); }); + this.on("close", function (done) { + connectionManager + .deleteClientHttp(connectionConfig.credentials.clientid) + .then(() => { + done(); + }); + }); + } else { + this.status({ + fill: "red", + shape: "ring", + text: "arduino-iot-cloud.status.connection-error", }); - }else{ - this.status({ fill: "red", shape: "ring", text: "arduino-iot-cloud.status.connection-error" }); } } } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - } } - } + }; realConstructor.apply(this, [config]); } RED.nodes.registerType("property out", ArduinoIotOutput); @@ -150,29 +222,50 @@ module.exports = function (RED) { const node = this; this.timeWindowCount = config.timeWindowCount; this.timeWindowUnit = config.timeWindowUnit; - if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") { + if ( + connectionConfig && + config.thing !== "" && + config.thing !== "0" && + config.property !== "" && + config.property !== "0" + ) { try { - this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig); - if (this.arduinoRestClient){ + this.arduinoRestClient = + await connectionManager.getClientHttp(connectionConfig); + if (this.arduinoRestClient) { this.arduinoRestClient.openConnections++; if (config.thing !== "" && config.property !== "") { this.organization = config.organization; this.thing = config.thing; this.propertyId = config.property; this.propertyName = config.name; - const opts = {} + const opts = {}; if (this.organization) { opts.xOrganization = this.organization; - } - node.on('input', async function () { - try{ + } + node.on("input", async function () { + try { const now = moment(); const end = now.format(); - const count = this.timeWindowCount - if (count !== null && count !== "" && count !== undefined && Number.isInteger(parseInt(count)) && parseInt(count) !== 0) { - const start = now.subtract(count * this.timeWindowUnit, 'second').format(); + const count = this.timeWindowCount; + if ( + count !== null && + count !== "" && + count !== undefined && + Number.isInteger(parseInt(count)) && + parseInt(count) !== 0 + ) { + const start = now + .subtract(count * this.timeWindowUnit, "second") + .format(); - const result = await this.arduinoRestClient.getSeries(this.thing, this.propertyId, start, end, opts); + const result = await this.arduinoRestClient.getSeries( + this.thing, + this.propertyId, + start, + end, + opts, + ); const times = result.responses[0].times; const values = result.responses[0].values; let data = []; @@ -180,63 +273,105 @@ module.exports = function (RED) { values.forEach(function (item, index, array) { data.push({ x: moment(times[index]).unix() * 1000, - y: values[index] + y: values[index], }); }); } - node.send( - { - topic: config.name, - payload: [{ + node.send({ + topic: config.name, + payload: [ + { series: [], - data: [data] - }] - } - ); + data: [data], + }, + ], + }); var str = RED._("arduino-iot-cloud.status.sent"); str += data.length; str += RED._("arduino-iot-cloud.status.elements"); this.status({ fill: "grey", shape: "dot", text: str }); } - }catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + } catch (err) { + if ( + err.response && + err.response.res && + err.response.request + ) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-getting-value" }); + this.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.error-getting-value", + }); } }); - this.on('close', function (done) { - connectionManager.deleteClientHttp(connectionConfig.credentials.clientid).then(() => { done(); }); + this.on("close", function (done) { + connectionManager + .deleteClientHttp(connectionConfig.credentials.clientid) + .then(() => { + done(); + }); }); } - }else{ - this.status({ fill: "red", shape: "ring", text: "arduino-iot-cloud.status.connection-error" }); + } else { + this.status({ + fill: "red", + shape: "ring", + text: "arduino-iot-cloud.status.connection-error", + }); } } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-getting-value" }); + this.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.error-getting-value", + }); } } - } + }; realConstructor.apply(this, [config]); } RED.nodes.registerType("property in hist", ArduinoIotInputHist); @@ -249,161 +384,258 @@ module.exports = function (RED) { this.timeWindowCount = config.timeWindowCount; this.timeWindowUnit = config.timeWindowUnit; this.organization = config.organization; - if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") { + if ( + connectionConfig && + config.thing !== "" && + config.thing !== "0" && + config.property !== "" && + config.property !== "0" + ) { try { - this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig); - if (this.arduinoRestClient){ + this.arduinoRestClient = + await connectionManager.getClientHttp(connectionConfig); + if (this.arduinoRestClient) { this.arduinoRestClient.openConnections++; if (config.thing !== "" && config.property !== "") { this.thing = config.thing; this.propertyId = config.property; this.propertyName = config.name; const pollTime = this.timeWindowCount * this.timeWindowUnit; - if (pollTime !== null && pollTime !== "" && pollTime !== undefined && Number.isInteger(parseInt(pollTime)) && parseInt(pollTime) !== 0) { + if ( + pollTime !== null && + pollTime !== "" && + pollTime !== undefined && + Number.isInteger(parseInt(pollTime)) && + parseInt(pollTime) !== 0 + ) { this.poll(connectionConfig, pollTime, this.organization); - this.on('close', function (done) { - connectionManager.deleteClientHttp(connectionConfig.credentials.clientid).then(() => { done(); }); - if (this.pollTimeoutPoll) - clearTimeout(this.pollTimeoutPoll); - + this.on("close", function (done) { + connectionManager + .deleteClientHttp(connectionConfig.credentials.clientid) + .then(() => { + done(); + }); + if (this.pollTimeoutPoll) clearTimeout(this.pollTimeoutPoll); }); } } - }else{ - this.status({ fill: "red", shape: "ring", text: "arduino-iot-cloud.status.connection-error" }); + } else { + this.status({ + fill: "red", + shape: "ring", + text: "arduino-iot-cloud.status.connection-error", + }); } } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - } } - } + }; realConstructor.apply(this, [config]); } ArduinoIotInputPoll.prototype = { poll: async function (connectionConfig, pollTime, organization) { try { - const opts = {} + const opts = {}; if (organization) { opts.xOrganization = organization; - } - const property = await this.arduinoRestClient.getProperty(this.thing, this.propertyId, opts); - this.send( - { - topic: property.name, - payload: property.last_value, - timestamp: property.value_updated_at - } + } + const property = await this.arduinoRestClient.getProperty( + this.thing, + this.propertyId, + opts, ); + this.send({ + topic: property.name, + payload: property.last_value, + timestamp: property.value_updated_at, + }); const s = getStatus(property.last_value); if (s != undefined) this.status({ fill: "grey", shape: "dot", text: s }); - else - this.status({}); - this.pollTimeoutPoll = setTimeout(() => { this.poll(connectionConfig, pollTime, organization) }, pollTime * 1000); + else this.status({}); + this.pollTimeoutPoll = setTimeout(() => { + this.poll(connectionConfig, pollTime, organization); + }, pollTime * 1000); } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - this.pollTimeoutPoll = setTimeout(() => { this.poll(connectionConfig, pollTime) }, pollTime * 1000); - this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-getting-value" }); + this.pollTimeoutPoll = setTimeout(() => { + this.poll(connectionConfig, pollTime); + }, pollTime * 1000); + this.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.error-getting-value", + }); } - } - } + }, + }; RED.nodes.registerType("property in poll", ArduinoIotInputPoll); - function ArduinoIotInputPush(config) { const realConstructor = async (config) => { RED.nodes.createNode(this, config); const connectionConfig = RED.nodes.getNode(config.connection); this.status({}); const node = this; - if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") { + if ( + connectionConfig && + config.thing !== "" && + config.thing !== "0" && + config.property !== "" && + config.property !== "0" + ) { try { - if (config.thing !== "" && config.property !== "") { - this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig); - if (this.arduinoRestClient){ + this.arduinoRestClient = + await connectionManager.getClientHttp(connectionConfig); + if (this.arduinoRestClient) { this.arduinoRestClient.openConnections++; this.organization = config.organization; this.thing = config.thing; this.propertyId = config.property; this.propertyName = config.name; - const opts = {} + const opts = {}; if (this.organization) { opts.xOrganization = this.organization; - } - node.on('input', async function () { - try{ - const property = await this.arduinoRestClient.getProperty(this.thing, this.propertyId, opts); - this.send( - { - topic: property.name, - payload: property.last_value, - timestamp: property.value_updated_at - } + } + node.on("input", async function () { + try { + const property = await this.arduinoRestClient.getProperty( + this.thing, + this.propertyId, + opts, ); + this.send({ + topic: property.name, + payload: property.last_value, + timestamp: property.value_updated_at, + }); const s = getStatus(property.last_value); if (s != undefined) this.status({ fill: "grey", shape: "dot", text: s }); - else - this.status({}); + else this.status({}); } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if ( + err.response && + err.response.res && + err.response.request + ) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-getting-value" }); + this.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.error-getting-value", + }); } }); - this.on('close', function (done) { - connectionManager.deleteClientHttp(connectionConfig.credentials.clientid).then(() => { done(); }); + this.on("close", function (done) { + connectionManager + .deleteClientHttp(connectionConfig.credentials.clientid) + .then(() => { + done(); + }); + }); + } else { + this.status({ + fill: "red", + shape: "ring", + text: "arduino-iot-cloud.status.connection-error", }); - }else{ - this.status({ fill: "red", shape: "ring", text: "arduino-iot-cloud.status.connection-error" }); } } } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\n'+ - 'statusMessage: ' + err.response.res.statusMessage + '\n' + - 'text: ' + err.response.res.text + '\n'+ - 'HTTP method: ' + err.response.request.method + '\n' + - 'URL request: ' + err.response.request.url + '\n' + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\n" + + "statusMessage: " + + err.response.res.statusMessage + + "\n" + + "text: " + + err.response.res.text + + "\n" + + "HTTP method: " + + err.response.request.method + + "\n" + + "URL request: " + + err.response.request.url + + "\n", ); - }else{ + } else { console.log(err); } - this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-getting-value" }); + this.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.error-getting-value", + }); } } - } + }; realConstructor.apply(this, [config]); } @@ -418,8 +650,8 @@ module.exports = function (RED) { RED.nodes.registerType("arduino-connection", ArduinoConnectionNode, { credentials: { clientid: { type: "password" }, - clientsecret: { type: "password" } - } + clientsecret: { type: "password" }, + }, }); async function getThingsOrProperties(req, res, thingsOrProperties) { @@ -430,75 +662,93 @@ module.exports = function (RED) { arduinoRestClient = await connectionManager.getClientHttp({ credentials: { clientid: req.query.clientid, - clientsecret: req.query.clientsecret - } + clientsecret: req.query.clientsecret, + }, }); } else if (req.query.connectionid) { const connectionConfig = RED.nodes.getNode(req.query.connectionid); if (!connectionConfig) { - str=RED._("arduino-iot-cloud.connection-error.no-cred-available"); + str = RED._("arduino-iot-cloud.connection-error.no-cred-available"); console.log(str); return res.send(JSON.stringify({ error: str })); } - arduinoRestClient = await connectionManager.getClientHttp(connectionConfig); + arduinoRestClient = + await connectionManager.getClientHttp(connectionConfig); } else { - str=RED._("arduino-iot-cloud.connection-error.no-cred-available"); + str = RED._("arduino-iot-cloud.connection-error.no-cred-available"); console.log(str); return res.send(JSON.stringify({ error: str })); } if (thingsOrProperties === "things") { const organization = req.headers.organization; - const opts = {} + const opts = {}; if (organization) { opts.xOrganization = organization; } - return res.send(JSON.stringify(await arduinoRestClient.getThings(opts))); + return res.send( + JSON.stringify(await arduinoRestClient.getThings(opts)), + ); } else if (thingsOrProperties === "properties") { const thing_id = req.query.thing_id; const organization = req.headers.organization; - const opts = {} + const opts = {}; if (organization) { opts.xOrganization = organization; } - return res.send(JSON.stringify(await arduinoRestClient.getProperties(thing_id, opts))); + return res.send( + JSON.stringify(await arduinoRestClient.getProperties(thing_id, opts)), + ); } else if (thingsOrProperties === "device") { const thing_id = req.query.thing_id; const organization = req.headers.organization; - const opts = {} + const opts = {}; if (organization) { opts.xOrganization = organization; } - return res.send(JSON.stringify(await arduinoRestClient.getThing(thing_id, opts))); - }else { - str=RED._("arduino-iot-cloud.connection-error.wrong-param"); + return res.send( + JSON.stringify(await arduinoRestClient.getThing(thing_id, opts)), + ); + } else { + str = RED._("arduino-iot-cloud.connection-error.wrong-param"); console.log(str); return res.send(JSON.stringify({ error: str })); } } catch (err) { - str=RED._("arduino-iot-cloud.connection-error.wrong-cred-sys-unvail"); + str = RED._("arduino-iot-cloud.connection-error.wrong-cred-sys-unvail"); console.log(`Status: ${err.status}, message: ${err.error}`); return res.send(JSON.stringify({ error: str })); } } - RED.httpAdmin.get("/things", RED.auth.needsPermission('Property-in.read'), async function (req, res) { - return getThingsOrProperties(req, res, "things"); - }); + RED.httpAdmin.get( + "/things", + RED.auth.needsPermission("Property-in.read"), + async function (req, res) { + return getThingsOrProperties(req, res, "things"); + }, + ); - RED.httpAdmin.get("/properties", RED.auth.needsPermission('Property-in.read'), async function (req, res) { - return getThingsOrProperties(req, res, "properties"); - }); + RED.httpAdmin.get( + "/properties", + RED.auth.needsPermission("Property-in.read"), + async function (req, res) { + return getThingsOrProperties(req, res, "properties"); + }, + ); - RED.httpAdmin.get("/thing", RED.auth.needsPermission('Property-in.read'), async function (req, res) { - return getThingsOrProperties(req, res, "device"); - }); + RED.httpAdmin.get( + "/thing", + RED.auth.needsPermission("Property-in.read"), + async function (req, res) { + return getThingsOrProperties(req, res, "device"); + }, + ); function getStatus(value) { if (typeof value !== "object") { - if (typeof value === "number" && !(Number.isInteger(value))) + if (typeof value === "number" && !Number.isInteger(value)) return value.toFixed(3); - else - return value; + else return value; } return RED._("arduino-iot-cloud.status.object-injected"); } -} +}; diff --git a/locales/en-US/arduino-iot-cloud.html b/locales/en-US/arduino-iot-cloud.html index 11ba1ba..9958646 100644 --- a/locales/en-US/arduino-iot-cloud.html +++ b/locales/en-US/arduino-iot-cloud.html @@ -54,8 +54,6 @@

Node Properties

Choose a property of the selected Arduino Thing.

Name

Insert a node name. Default is the Arduino Property name.

- - \ No newline at end of file + diff --git a/locales/en-US/arduino-iot-cloud.json b/locales/en-US/arduino-iot-cloud.json index 3854383..85f4a74 100644 --- a/locales/en-US/arduino-iot-cloud.json +++ b/locales/en-US/arduino-iot-cloud.json @@ -1,63 +1,61 @@ { - "arduino-iot-cloud":{ - "config":{ - "node":{ - "connection":"Connection", - "thing":"Thing", + "arduino-iot-cloud": { + "config": { + "node": { + "connection": "Connection", + "thing": "Thing", "property": "Property", - "name":"Name", + "name": "Name", "organization": "Space ID", - "hist-label":"Time filter", - "poll-label":"Poll Every", + "hist-label": "Time filter", + "poll-label": "Poll Every", "device-id": "Device", - "send-mode":"Send as device", - "placeholders":{ - "name":"Name", - "no-thing-selected":"No thing selected", - "thing-select":"Select a thing", + "send-mode": "Send as device", + "placeholders": { + "name": "Name", + "no-thing-selected": "No thing selected", + "thing-select": "Select a thing", "organization": "The Space ID (for maker / pro things)", - "no-things-available":"No things available", - "property-select":"Select a property", - "no-property-available":"No properties available", - "no-property-writable-av":"No writable properties available", - "no-device-select":"No device associated" + "no-things-available": "No things available", + "property-select": "Select a property", + "no-property-available": "No properties available", + "no-property-writable-av": "No writable properties available", + "no-device-select": "No device associated" } }, - "time":{ - "last":"last", - "seconds":"seconds", - "minutes":"minutes", - "hours":"hours", - "days":"days", - "weeks":"weeks" - + "time": { + "last": "last", + "seconds": "seconds", + "minutes": "minutes", + "hours": "hours", + "days": "days", + "weeks": "weeks" }, - "connection":{ - "client-id":"Client ID", - "client-secret":"Client secret", - "placeholders":{ - "name":"Application name", - "client-id":"Client ID", - "client-secret":"Client secret", - "no-conn-selected":"No connection selected" + "connection": { + "client-id": "Client ID", + "client-secret": "Client secret", + "placeholders": { + "name": "Application name", + "client-id": "Client ID", + "client-secret": "Client secret", + "no-conn-selected": "No connection selected" } } - }, - "status":{ - "connection-error":"Connection Error", - "offline":"Offline", - "error-setting-value":"Error setting value", - "error-getting-value":"Error getting value", - "sent":"Sent ", - "elements":" elements", - "object-injected":"Object Injected", - "object-sent":"Object Sent" + "status": { + "connection-error": "Connection Error", + "offline": "Offline", + "error-setting-value": "Error setting value", + "error-getting-value": "Error getting value", + "sent": "Sent ", + "elements": " elements", + "object-injected": "Object Injected", + "object-sent": "Object Sent" }, - "connection-error":{ - "no-cred-available":"No credentials available.", - "wrong-param":"Wrong parameter in getThingsOrProperties.", - "wrong-cred-sys-unvail":"Wrong credentials or system unavailable." + "connection-error": { + "no-cred-available": "No credentials available.", + "wrong-param": "Wrong parameter in getThingsOrProperties.", + "wrong-cred-sys-unvail": "Wrong credentials or system unavailable." } } -} \ No newline at end of file +} diff --git a/utils/arduino-connection-manager.js b/utils/arduino-connection-manager.js index e553778..ecabab8 100644 --- a/utils/arduino-connection-manager.js +++ b/utils/arduino-connection-manager.js @@ -1,29 +1,33 @@ /* -* Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -* This file is part of node-red-contrib-arduino-iot-cloud. -* Copyright (c) 2019 -* -* This software is released under: -* The GNU General Public License, which covers the main part of -* node-red-contrib-arduino-iot-cloud -* The terms of this license can be found at: -* https://www.gnu.org/licenses/gpl-3.0.en.html -* -* You can be released from the requirements of the above licenses by purchasing -* a commercial license. Buying such a license is mandatory if you want to modify or -* otherwise use the software for commercial activities involving the Arduino -* software without disclosing the source code of your own applications. To purchase -* a commercial license, send an email to license@arduino.cc. -* -*/ + * Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + * This file is part of node-red-contrib-arduino-iot-cloud. + * Copyright (c) 2019 + * + * This software is released under: + * The GNU General Public License, which covers the main part of + * node-red-contrib-arduino-iot-cloud + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ -const superagent = require('superagent'); -const ArduinoClientHttp = require('./arduino-iot-cloud-api-wrapper'); -const ArduinoClientMqtt = require('../arduino-iot-client-mqtt/arduino-iot-client-mqtt'); -const accessTokenUri = process.env.NODE_RED_ACCESS_TOKEN_URI || 'https://api2.arduino.cc/iot/v1/clients/token'; -const accessTokenAudience = process.env.NODE_RED_ACCESS_TOKEN_AUDIENCE || 'https://api2.arduino.cc/iot'; -const arduinoIotCloudHost = process.env.NODE_RED_MQTT_HOST || 'wss.iot.arduino.cc'; -const Mutex = require('async-mutex').Mutex; +const superagent = require("superagent"); +const ArduinoClientHttp = require("./arduino-iot-cloud-api-wrapper"); +const ArduinoClientMqtt = require("../arduino-iot-client-mqtt/arduino-iot-client-mqtt"); +const accessTokenUri = + process.env.NODE_RED_ACCESS_TOKEN_URI || + "https://api2.arduino.cc/iot/v1/clients/token"; +const accessTokenAudience = + process.env.NODE_RED_ACCESS_TOKEN_AUDIENCE || "https://api2.arduino.cc/iot"; +const arduinoIotCloudHost = + process.env.NODE_RED_MQTT_HOST || "wss.iot.arduino.cc"; +const Mutex = require("async-mutex").Mutex; /** Connections elem struct * { * clientId: clientId, @@ -37,83 +41,96 @@ const Mutex = require('async-mutex').Mutex; */ var connections = []; const getClientMutex = new Mutex(); -var numRetry=0; - +var numRetry = 0; async function getToken(connectionConfig) { const dataToSend = { - grant_type: 'client_credentials', - client_id: connectionConfig.credentials.clientid, - client_secret: connectionConfig.credentials.clientsecret, - audience: accessTokenAudience + grant_type: "client_credentials", + client_id: connectionConfig.credentials.clientid, + client_secret: connectionConfig.credentials.clientsecret, + audience: accessTokenAudience, }; try { - var res = await superagent - .post(accessTokenUri) - .set('content-type', 'application/x-www-form-urlencoded') - .set('accept', 'json') - .send(dataToSend); + .post(accessTokenUri) + .set("content-type", "application/x-www-form-urlencoded") + .set("accept", "json") + .send(dataToSend); var token = res.body.access_token; var expires_in = res.body.expires_in * 0.8; // needed to change the token before it expires if (token !== undefined) { return { token: token, expires_in: expires_in }; } } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\r'+ - 'statusMessage: ' + err.response.res.statusMessage + '\r' + - 'text: ' + err.response.res.text + '\r'+ - 'HTTP method: ' + err.response.request.method + '\r' + - 'URL request: ' + err.response.request.url + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\r" + + "statusMessage: " + + err.response.res.statusMessage + + "\r" + + "text: " + + err.response.res.text + + "\r" + + "HTTP method: " + + err.response.request.method + + "\r" + + "URL request: " + + err.response.request.url, ); - }else{ + } else { console.log(err); } - } } -function getMqttOptions(clientId,token,RED){ +function getMqttOptions(clientId, token, RED) { return { host: arduinoIotCloudHost, token: token, onDisconnect: async () => { console.log(`connection lost for ${clientId}`); - RED.nodes.eachNode((n)=>{ - if(n.type === "property in"){ + RED.nodes.eachNode((n) => { + if (n.type === "property in") { const node = RED.nodes.getNode(n.id); - node.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.connection-error" }); + node.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.connection-error", + }); } }); await reconnectMqtt(clientId); - }, onOffline: async () => { console.log(`connection lost for ${clientId}`); - RED.nodes.eachNode((n)=>{ - if(n.type === "property in"){ + RED.nodes.eachNode((n) => { + if (n.type === "property in") { const node = RED.nodes.getNode(n.id); - node.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.offline" }); + node.status({ + fill: "red", + shape: "dot", + text: "arduino-iot-cloud.status.offline", + }); } }); }, - onConnected: () =>{ - RED.nodes.eachNode((n)=>{ - if(n.type === "property in"){ + onConnected: () => { + RED.nodes.eachNode((n) => { + if (n.type === "property in") { const node = RED.nodes.getNode(n.id); node.status({}); } }); }, - useCloudProtocolV2: true + useCloudProtocolV2: true, }; } async function getClientMqtt(connectionConfig, RED) { - if (!connectionConfig || !connectionConfig.credentials) { throw new Error("Cannot find connection config or credentials."); } @@ -125,8 +142,14 @@ async function getClientMqtt(connectionConfig, RED) { clientMqtt = new ArduinoClientMqtt.ArduinoClientMqtt(); const tokenInfo = await getToken(connectionConfig); if (tokenInfo !== undefined) { - const ArduinoIotCloudOptions = getMqttOptions(connectionConfig.credentials.clientid,tokenInfo.token,RED) - const timeout = setTimeout(() => { updateToken(connectionConfig) }, tokenInfo.expires_in * 1000); + const ArduinoIotCloudOptions = getMqttOptions( + connectionConfig.credentials.clientid, + tokenInfo.token, + RED, + ); + const timeout = setTimeout(() => { + updateToken(connectionConfig); + }, tokenInfo.expires_in * 1000); connections.push({ clientId: connectionConfig.credentials.clientid, connectionConfig: connectionConfig, @@ -134,7 +157,7 @@ async function getClientMqtt(connectionConfig, RED) { expires_token_ts: tokenInfo.expires_in, clientMqtt: clientMqtt, clientHttp: null, - timeoutUpdateToken: timeout + timeoutUpdateToken: timeout, }); await clientMqtt.connect(ArduinoIotCloudOptions); } else { @@ -145,10 +168,13 @@ async function getClientMqtt(connectionConfig, RED) { clientMqtt = connections[user].clientMqtt; } else { clientMqtt = new ArduinoClientMqtt.ArduinoClientMqtt(); - const ArduinoIotCloudOptions = getMqttOptions(connectionConfig.credentials.clientid,connections[user].token,RED) + const ArduinoIotCloudOptions = getMqttOptions( + connectionConfig.credentials.clientid, + connections[user].token, + RED, + ); connections[user].clientMqtt = clientMqtt; await clientMqtt.connect(ArduinoIotCloudOptions); - } } releaseMutex(); @@ -158,11 +184,9 @@ async function getClientMqtt(connectionConfig, RED) { console.log(err); releaseMutex(); } - } async function getClientHttp(connectionConfig) { - if (!connectionConfig || !connectionConfig.credentials) { throw new Error("Cannot find cooonection config or credentials."); } @@ -171,12 +195,13 @@ async function getClientHttp(connectionConfig) { var user = findUser(connectionConfig.credentials.clientid); var clientHttp; if (user === -1) { - var tokenInfo = await getToken(connectionConfig); if (tokenInfo !== undefined) { clientHttp = new ArduinoClientHttp.ArduinoClientHttp(tokenInfo.token); - var timeout = setTimeout(() => { updateToken(connectionConfig) }, tokenInfo.expires_in * 1000); + var timeout = setTimeout(() => { + updateToken(connectionConfig); + }, tokenInfo.expires_in * 1000); connections.push({ clientId: connectionConfig.credentials.clientid, connectionConfig: connectionConfig, @@ -184,16 +209,16 @@ async function getClientHttp(connectionConfig) { expires_token_ts: tokenInfo.expires_in, clientMqtt: null, clientHttp: clientHttp, - timeoutUpdateToken: timeout + timeoutUpdateToken: timeout, }); - } - } else { if (connections[user].clientHttp !== null) { clientHttp = connections[user].clientHttp; } else { - clientHttp = new ArduinoClientHttp.ArduinoClientHttp(connections[user].token); + clientHttp = new ArduinoClientHttp.ArduinoClientHttp( + connections[user].token, + ); connections[user].clientHttp = clientHttp; } @@ -202,21 +227,29 @@ async function getClientHttp(connectionConfig) { releaseMutex(); return clientHttp; } catch (err) { - if(err.response && err.response.res && err.response.request){ - console.log('statusCode: '+ err.response.res.statusCode +'\r'+ - 'statusMessage: ' + err.response.res.statusMessage + '\r' + - 'text: ' + err.response.res.text + '\r'+ - 'HTTP method: ' + err.response.request.method + '\r' + - 'URL request: ' + err.response.request.url + if (err.response && err.response.res && err.response.request) { + console.log( + "statusCode: " + + err.response.res.statusCode + + "\r" + + "statusMessage: " + + err.response.res.statusMessage + + "\r" + + "text: " + + err.response.res.text + + "\r" + + "HTTP method: " + + err.response.request.method + + "\r" + + "URL request: " + + err.response.request.url, ); - }else{ + } else { console.log(err); } releaseMutex(); - } - } function findUser(clientId) { @@ -226,7 +259,6 @@ function findUser(clientId) { } } return -1; - } async function updateToken(connectionConfig) { @@ -235,23 +267,28 @@ async function updateToken(connectionConfig) { if (user !== -1) { var tokenInfo = await getToken(connectionConfig); if (tokenInfo !== undefined) { - numRetry=0; + numRetry = 0; connections[user].token = tokenInfo.token; connections[user].expires_token_ts = tokenInfo.expires_in; - if(connections[user].clientMqtt){ + if (connections[user].clientMqtt) { connections[user].clientMqtt.updateToken(tokenInfo.token); } - if(connections[user].clientHttp){ + if (connections[user].clientHttp) { connections[user].clientHttp.updateToken(tokenInfo.token); } - connections[user].timeoutUpdateToken = setTimeout(() => { updateToken(connectionConfig) }, tokenInfo.expires_in * 1000); + connections[user].timeoutUpdateToken = setTimeout(() => { + updateToken(connectionConfig); + }, tokenInfo.expires_in * 1000); } else { /*Avoid too many requests addressed to server*/ - if(numRetry < 3){ - connections[user].timeoutUpdateToken = setTimeout(() => { updateToken(connectionConfig) }, 5000); - } - else{ - connections[user].timeoutUpdateToken = setTimeout(() => { updateToken(connectionConfig) }, 60000); + if (numRetry < 3) { + connections[user].timeoutUpdateToken = setTimeout(() => { + updateToken(connectionConfig); + }, 5000); + } else { + connections[user].timeoutUpdateToken = setTimeout(() => { + updateToken(connectionConfig); + }, 60000); } numRetry++; @@ -267,7 +304,11 @@ async function deleteClientMqtt(clientId, thing, propertyName, nodeId) { var user = findUser(clientId); if (user !== -1) { if (connections[user].clientMqtt !== null) { - var ret = await connections[user].clientMqtt.removePropertyValueCallback(thing, propertyName,nodeId); + var ret = await connections[user].clientMqtt.removePropertyValueCallback( + thing, + propertyName, + nodeId, + ); if (ret === 0) { await connections[user].clientMqtt.disconnect(); @@ -306,7 +347,7 @@ async function deleteClientHttp(clientId) { async function reconnectMqtt(clientId) { var user = findUser(clientId); if (user !== -1) { - if(connections[user].clientMqtt){ + if (connections[user].clientMqtt) { await connections[user].clientMqtt.reconnect(); } } diff --git a/utils/arduino-iot-cloud-api-wrapper.js b/utils/arduino-iot-cloud-api-wrapper.js index 1fcaf10..e5847cb 100644 --- a/utils/arduino-iot-cloud-api-wrapper.js +++ b/utils/arduino-iot-cloud-api-wrapper.js @@ -1,30 +1,30 @@ /* -* Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -* This file is part of node-red-contrib-arduino-iot-cloud. -* Copyright (c) 2019 -* -* This software is released under: -* The GNU General Public License, which covers the main part of -* node-red-contrib-arduino-iot-cloud -* The terms of this license can be found at: -* https://www.gnu.org/licenses/gpl-3.0.en.html -* -* You can be released from the requirements of the above licenses by purchasing -* a commercial license. Buying such a license is mandatory if you want to modify or -* otherwise use the software for commercial activities involving the Arduino -* software without disclosing the source code of your own applications. To purchase -* a commercial license, send an email to license@arduino.cc. -* -*/ + * Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + * This file is part of node-red-contrib-arduino-iot-cloud. + * Copyright (c) 2019 + * + * This software is released under: + * The GNU General Public License, which covers the main part of + * node-red-contrib-arduino-iot-cloud + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ // Arduino iot cloud api -'use strict'; +"use strict"; -const ArduinoIotClient = require('@arduino/arduino-iot-client'); +const ArduinoIotClient = require("@arduino/arduino-iot-client"); const client = ArduinoIotClient.ApiClient.instance; // Configure OAuth2 access token for authorization: oauth2 -var oauth2 = client.authentications['oauth2']; +var oauth2 = client.authentications["oauth2"]; const apiProperties = new ArduinoIotClient.PropertiesV2Api(client); const apiSeries = new ArduinoIotClient.SeriesV2Api(client); const apiThings = new ArduinoIotClient.ThingsV2Api(client); @@ -32,8 +32,8 @@ const apiThings = new ArduinoIotClient.ThingsV2Api(client); class ArduinoClientHttp { constructor(token) { this.token = token; - this.openConnections=0; - if(process.env.API_BASE_PATH){ + this.openConnections = 0; + if (process.env.API_BASE_PATH) { client.basePath = process.env.API_BASE_PATH; } } @@ -43,7 +43,7 @@ class ArduinoClientHttp { setProperty(thing_id, property_id, value, opts, device_id = undefined) { const body = JSON.stringify({ value: value, - device_id : device_id + device_id: device_id, }); oauth2.accessToken = this.token; return apiProperties.propertiesV2Publish(thing_id, property_id, body, opts); @@ -61,23 +61,24 @@ class ArduinoClientHttp { oauth2.accessToken = this.token; opts.showProperties = true; const thing = apiThings.thingsV2Show(thingId, opts); - return thing.then(({properties}) => properties); + return thing.then(({ properties }) => properties); } getProperty(thingId, propertyId, opts) { oauth2.accessToken = this.token; return apiProperties.propertiesV2Show(thingId, propertyId, opts); } getSeries(thingId, propertyId, start, end, opts) { - - const body = JSON.stringify({ - requests: [{ - q: "property." + propertyId, - from: start, - to: end, - sort: "ASC", - series_limit: 86400 - }], - resp_version: 1 + const body = JSON.stringify({ + requests: [ + { + q: "property." + propertyId, + from: start, + to: end, + sort: "ASC", + series_limit: 86400, + }, + ], + resp_version: 1, }); oauth2.accessToken = this.token; return apiSeries.seriesV2BatchQueryRaw(body, opts); From 25f0013fcf27e8caa710ef0b16a30a2108bccbde Mon Sep 17 00:00:00 2001 From: lucarin91 Date: Wed, 4 Dec 2024 10:40:49 +0100 Subject: [PATCH 3/4] add ci pipeline --- .github/workflows/fmt-check.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/fmt-check.yml diff --git a/.github/workflows/fmt-check.yml b/.github/workflows/fmt-check.yml new file mode 100644 index 0000000..4ceed1a --- /dev/null +++ b/.github/workflows/fmt-check.yml @@ -0,0 +1,22 @@ +name: Format Check + +on: [push, pull_request] + +jobs: + fmt-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '23' + + - name: Install dev dependencies + run: npm install --only=dev + + - name: Run format check + run: npm run fmt-check From 089caa13b55793818c65e8a21de2ea8f4787ef33 Mon Sep 17 00:00:00 2001 From: lucarin91 Date: Wed, 4 Dec 2024 10:42:33 +0100 Subject: [PATCH 4/4] fixup! add ci pipeline --- .github/workflows/fmt-check.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/fmt-check.yml b/.github/workflows/fmt-check.yml index 4ceed1a..0ea738d 100644 --- a/.github/workflows/fmt-check.yml +++ b/.github/workflows/fmt-check.yml @@ -7,16 +7,16 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v2 - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: '23' + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: "23" - - name: Install dev dependencies - run: npm install --only=dev + - name: Install dev dependencies + run: npm install --only=dev - - name: Run format check - run: npm run fmt-check + - name: Run format check + run: npm run fmt-check pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy