From 33aacb9752dc5618ae7e80991292eb2b4ee5d018 Mon Sep 17 00:00:00 2001
From: Mirko Curtolo <mirko.curtolo86@gmail.com>
Date: Wed, 24 Jul 2024 12:17:05 +0200
Subject: [PATCH 1/6] Extend Space ID support to all nodes

---
 arduino-iot-cloud.html                 |  17 +++
 arduino-iot-cloud.js                   |  27 ++--
 package-lock.json                      | 169 +++++++++++++------------
 package.json                           |   2 +-
 utils/arduino-connection-manager.js    |   2 +-
 utils/arduino-iot-cloud-api-wrapper.js |  12 +-
 6 files changed, 133 insertions(+), 96 deletions(-)

diff --git a/arduino-iot-cloud.html b/arduino-iot-cloud.html
index 2a390c5..b8025df 100644
--- a/arduino-iot-cloud.html
+++ b/arduino-iot-cloud.html
@@ -45,6 +45,7 @@
             property: {value: "", validate: validator},
             name: {value: "", validate: validator},
             propname: {value: ""},
+            organization: {value: ""},
             defaultname: {value: true}
         };
 
@@ -424,6 +425,10 @@
     <label for="node-input-connection"><i class="fa fa-random fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.connection"></span></label>
     <input type="text" id="node-input-connection">
   </div>
+  <div class="form-row">
+    <label for="node-input-organization"><i class="fa fa-tag fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.organization"></span></label>
+    <input type="text" id="node-input-organization" data-i18n="[placeholder]arduino-iot-cloud.config.node.placeholders.organization">
+  </div>
   <div class="form-row">
       <label for="node-input-thing"><i class="fa fa-cubes fa-fw"></i> <span data-i18n="arduino-iot-cloud.config.node.thing"></span></label>
       <select id="node-input-thing">
@@ -455,6 +460,10 @@
     <label for="node-input-connection"><i class="fa fa-random fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.connection"></span></label>
     <input type="text" id="node-input-connection">
   </div>
+  <div class="form-row">
+    <label for="node-input-organization"><i class="fa fa-tag fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.organization"></span></label>
+    <input type="text" id="node-input-organization" data-i18n="[placeholder]arduino-iot-cloud.config.node.placeholders.organization">
+  </div>
   <div class="form-row">
     <label for="node-input-thing"><i class="fa fa-cubes fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.thing"></span></label>
     <select id="node-input-thing">
@@ -492,6 +501,10 @@
     <label for="node-input-connection"><i class="fa fa-random fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.connection"></span></label>
     <input type="text" id="node-input-connection">
   </div>
+  <div class="form-row">
+    <label for="node-input-organization"><i class="fa fa-tag fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.organization"></span></label>
+    <input type="text" id="node-input-organization" data-i18n="[placeholder]arduino-iot-cloud.config.node.placeholders.organization">
+  </div>
   <div class="form-row">
     <label for="node-input-thing"><i class="fa fa-cubes fa-fw"></i> <span data-i18n="arduino-iot-cloud.config.node.thing"></span></label>
     <select id="node-input-thing">
@@ -527,6 +540,10 @@
     <label for="node-input-connection"><i class="fa fa-random fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.connection"></span></label>
     <input type="text" id="node-input-connection">
   </div>
+  <div class="form-row">
+    <label for="node-input-organization"><i class="fa fa-tag fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.organization"></span></label>
+    <input type="text" id="node-input-organization" data-i18n="[placeholder]arduino-iot-cloud.config.node.placeholders.organization">
+  </div>
   <div class="form-row">
     <label for="node-input-thing"><i class="fa fa-cubes fa-fw"></i><span data-i18n="arduino-iot-cloud.config.node.thing"></span></label>
     <select id="node-input-thing">
diff --git a/arduino-iot-cloud.js b/arduino-iot-cloud.js
index 0951af2..8202835 100644
--- a/arduino-iot-cloud.js
+++ b/arduino-iot-cloud.js
@@ -84,10 +84,13 @@ module.exports = function (RED) {
               this.propertyName = config.name;
               this.sendasdevice = config.sendasdevice;
               this.device = config.device
-              
+              const opts = {}
+              if (this.organization) {
+                opts.xOrganization = this.organization;
+              }            
               this.on('input', async function (msg) {
                 try {
-                  await this.arduinoRestClient.setProperty(this.thing, this.propertyId, msg.payload, 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);
@@ -157,6 +160,10 @@ module.exports = function (RED) {
               this.thing = config.thing;
               this.propertyId = config.property;
               this.propertyName = config.name;
+              const opts = {}
+              if (this.organization) {
+                opts.xOrganization = this.organization;
+              }  
               node.on('input', async function () {
                 try{
                   const now = moment();
@@ -165,7 +172,7 @@ module.exports = function (RED) {
                   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);
+                    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 = [];
@@ -241,19 +248,19 @@ module.exports = function (RED) {
       this.status({});
       this.timeWindowCount = config.timeWindowCount;
       this.timeWindowUnit = config.timeWindowUnit;
+      this.organization = config.organization;
       if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") {
         try {
           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 pollTime = this.timeWindowCount * this.timeWindowUnit;
               if (pollTime !== null && pollTime !== "" && pollTime !== undefined && Number.isInteger(parseInt(pollTime)) && parseInt(pollTime) !== 0) {
-                this.poll(connectionConfig, pollTime);
+                this.poll(connectionConfig, pollTime, this.organization);
                 this.on('close', function (done) {
                   connectionManager.deleteClientHttp(connectionConfig.credentials.clientid).then(() => { done(); });
                   if (this.pollTimeoutPoll)
@@ -283,9 +290,13 @@ module.exports = function (RED) {
     realConstructor.apply(this, [config]);
   }
   ArduinoIotInputPoll.prototype = {
-    poll: async function (connectionConfig, pollTime) {
+    poll: async function (connectionConfig, pollTime, organization) {
       try {
-        const property = await this.arduinoRestClient.getProperty(this.thing, this.propertyId);
+        const opts = {}
+        if (organization) {
+          opts.xOrganization = organization;
+        }  
+        const property = await this.arduinoRestClient.getProperty(this.thing, this.propertyId, opts);
         this.send(
           {
             topic: property.name,
@@ -298,7 +309,7 @@ module.exports = function (RED) {
           this.status({ fill: "grey", shape: "dot", text: s });
         else
           this.status({});
-        this.pollTimeoutPoll = setTimeout(() => { this.poll(connectionConfig, pollTime) }, pollTime * 1000);
+        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'+
diff --git a/package-lock.json b/package-lock.json
index 3f2b6a6..4c4b437 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
       "version": "1.0.10",
       "license": "GNU AFFERO GENERAL PUBLIC LICENSE",
       "dependencies": {
-        "@arduino/arduino-iot-client": "^1.4.2",
+        "@arduino/arduino-iot-client": "github:arduino/iot-client-js",
         "@arduino/cbor-js": "github:arduino/cbor-js",
         "async-mutex": "^0.1.4",
         "jws": "^3.2.2",
@@ -32,70 +32,79 @@
       }
     },
     "node_modules/@arduino/arduino-iot-client": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/@arduino/arduino-iot-client/-/arduino-iot-client-1.4.2.tgz",
-      "integrity": "sha512-mlIBype4l+kSnGnNH8hO3CjmoDHRJpLCpjiySiKmNL0z87Wcz+1b5LLiTT/748UmbB/2FrgPZsDn8RVbB+FAhw==",
+      "version": "2.0.4",
+      "resolved": "git+ssh://git@github.com/arduino/iot-client-js.git#18e50fd5a420a26c025b59cd936e7d23e86d9955",
+      "license": "GPLv3",
       "dependencies": {
         "@babel/cli": "^7.0.0",
-        "superagent": "3.7.0"
+        "superagent": "^5.3.0"
       }
     },
     "node_modules/@arduino/arduino-iot-client/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
+      "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
       "dependencies": {
-        "ms": "^2.1.1"
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
       }
     },
-    "node_modules/@arduino/arduino-iot-client/node_modules/form-data": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
-      "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+    "node_modules/@arduino/arduino-iot-client/node_modules/ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+    },
+    "node_modules/@arduino/arduino-iot-client/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
       "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
       },
       "engines": {
-        "node": ">= 0.12"
+        "node": ">= 6"
       }
     },
-    "node_modules/@arduino/arduino-iot-client/node_modules/mime": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
-      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+    "node_modules/@arduino/arduino-iot-client/node_modules/semver": {
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
       "bin": {
-        "mime": "cli.js"
+        "semver": "bin/semver.js"
       },
       "engines": {
-        "node": ">=4"
+        "node": ">=10"
       }
     },
-    "node_modules/@arduino/arduino-iot-client/node_modules/ms": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
-    },
     "node_modules/@arduino/arduino-iot-client/node_modules/superagent": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.7.0.tgz",
-      "integrity": "sha512-/8trxO6NbLx4YXb7IeeFTSmsQ35pQBiTBsLNvobZx7qBzBeHYvKCyIIhW2gNcWbLzYxPAjdgFbiepd8ypwC0Gw==",
-      "deprecated": "Please upgrade to v7.0.2+ of superagent.  We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing.  See the releases tab for more information at <https://github.com/visionmedia/superagent/releases>.",
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz",
+      "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==",
+      "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net",
       "dependencies": {
-        "component-emitter": "^1.2.0",
-        "cookiejar": "^2.1.0",
-        "debug": "^3.1.0",
-        "extend": "^3.0.0",
-        "form-data": "^2.3.1",
-        "formidable": "^1.1.1",
-        "methods": "^1.1.1",
-        "mime": "^1.4.1",
-        "qs": "^6.5.1",
-        "readable-stream": "^2.0.5"
+        "component-emitter": "^1.3.0",
+        "cookiejar": "^2.1.2",
+        "debug": "^4.1.1",
+        "fast-safe-stringify": "^2.0.7",
+        "form-data": "^3.0.0",
+        "formidable": "^1.2.2",
+        "methods": "^1.1.2",
+        "mime": "^2.4.6",
+        "qs": "^6.9.4",
+        "readable-stream": "^3.6.0",
+        "semver": "^7.3.2"
       },
       "engines": {
-        "node": ">= 4.0"
+        "node": ">= 7.0.0"
       }
     },
     "node_modules/@arduino/cbor-js": {
@@ -3312,57 +3321,57 @@
       }
     },
     "@arduino/arduino-iot-client": {
-      "version": "1.4.2",
-      "resolved": "https://registry.npmjs.org/@arduino/arduino-iot-client/-/arduino-iot-client-1.4.2.tgz",
-      "integrity": "sha512-mlIBype4l+kSnGnNH8hO3CjmoDHRJpLCpjiySiKmNL0z87Wcz+1b5LLiTT/748UmbB/2FrgPZsDn8RVbB+FAhw==",
+      "version": "git+ssh://git@github.com/arduino/iot-client-js.git#18e50fd5a420a26c025b59cd936e7d23e86d9955",
+      "from": "@arduino/arduino-iot-client@arduino/iot-client-js",
       "requires": {
         "@babel/cli": "^7.0.0",
-        "superagent": "3.7.0"
+        "superagent": "^5.3.0"
       },
       "dependencies": {
         "debug": {
-          "version": "3.2.7",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+          "version": "4.3.5",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
+          "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.1.2"
           }
         },
-        "form-data": {
-          "version": "2.5.1",
-          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
-          "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        },
+        "readable-stream": {
+          "version": "3.6.2",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+          "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
           "requires": {
-            "asynckit": "^0.4.0",
-            "combined-stream": "^1.0.6",
-            "mime-types": "^2.1.12"
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
           }
         },
-        "mime": {
-          "version": "1.6.0",
-          "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
-          "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
-        },
-        "ms": {
-          "version": "2.1.3",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
-          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+        "semver": {
+          "version": "7.6.3",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+          "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
         },
         "superagent": {
-          "version": "3.7.0",
-          "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.7.0.tgz",
-          "integrity": "sha512-/8trxO6NbLx4YXb7IeeFTSmsQ35pQBiTBsLNvobZx7qBzBeHYvKCyIIhW2gNcWbLzYxPAjdgFbiepd8ypwC0Gw==",
+          "version": "5.3.1",
+          "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz",
+          "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==",
           "requires": {
-            "component-emitter": "^1.2.0",
-            "cookiejar": "^2.1.0",
-            "debug": "^3.1.0",
-            "extend": "^3.0.0",
-            "form-data": "^2.3.1",
-            "formidable": "^1.1.1",
-            "methods": "^1.1.1",
-            "mime": "^1.4.1",
-            "qs": "^6.5.1",
-            "readable-stream": "^2.0.5"
+            "component-emitter": "^1.3.0",
+            "cookiejar": "^2.1.2",
+            "debug": "^4.1.1",
+            "fast-safe-stringify": "^2.0.7",
+            "form-data": "^3.0.0",
+            "formidable": "^1.2.2",
+            "methods": "^1.1.2",
+            "mime": "^2.4.6",
+            "qs": "^6.9.4",
+            "readable-stream": "^3.6.0",
+            "semver": "^7.3.2"
           }
         }
       }
diff --git a/package.json b/package.json
index 938bde3..079a5d0 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     }
   },
   "dependencies": {
-    "@arduino/arduino-iot-client": "^1.4.2",
+    "@arduino/arduino-iot-client": "github:arduino/iot-client-js",
     "@arduino/cbor-js": "github:arduino/cbor-js",
     "async-mutex": "^0.1.4",
     "jws": "^3.2.2",
diff --git a/utils/arduino-connection-manager.js b/utils/arduino-connection-manager.js
index f5ca626..e553778 100644
--- a/utils/arduino-connection-manager.js
+++ b/utils/arduino-connection-manager.js
@@ -115,7 +115,7 @@ function getMqttOptions(clientId,token,RED){
 async function getClientMqtt(connectionConfig, RED) {
 
   if (!connectionConfig || !connectionConfig.credentials) {
-    throw new Error("Cannot find cooonection config or credentials.");
+    throw new Error("Cannot find connection config or credentials.");
   }
   const releaseMutex = await getClientMutex.acquire();
   try {
diff --git a/utils/arduino-iot-cloud-api-wrapper.js b/utils/arduino-iot-cloud-api-wrapper.js
index d631ec5..1fcaf10 100644
--- a/utils/arduino-iot-cloud-api-wrapper.js
+++ b/utils/arduino-iot-cloud-api-wrapper.js
@@ -40,13 +40,13 @@ class ArduinoClientHttp {
   updateToken(token) {
     this.token = token;
   }
-  setProperty(thing_id, property_id, value, device_id = undefined) {
+  setProperty(thing_id, property_id, value, opts, device_id = undefined) {
     const body = JSON.stringify({
       value: value,
       device_id : device_id
     });
     oauth2.accessToken = this.token;
-    return apiProperties.propertiesV2Publish(thing_id, property_id, body);
+    return apiProperties.propertiesV2Publish(thing_id, property_id, body, opts);
   }
   getThings(opts) {
     oauth2.accessToken = this.token;
@@ -63,11 +63,11 @@ class ArduinoClientHttp {
     const thing = apiThings.thingsV2Show(thingId, opts);
     return thing.then(({properties}) => properties);
   }
-  getProperty(thingId, propertyId) {
+  getProperty(thingId, propertyId, opts) {
     oauth2.accessToken = this.token;
-    return apiProperties.propertiesV2Show(thingId, propertyId);
+    return apiProperties.propertiesV2Show(thingId, propertyId, opts);
   }
-  getSeries(thingId, propertyId, start, end) {
+  getSeries(thingId, propertyId, start, end, opts) {
 
     const body =  JSON.stringify({
       requests: [{
@@ -80,7 +80,7 @@ class ArduinoClientHttp {
       resp_version: 1
     });
     oauth2.accessToken = this.token;
-    return apiSeries.seriesV2BatchQueryRaw(body);
+    return apiSeries.seriesV2BatchQueryRaw(body, opts);
   }
 }
 exports.ArduinoClientHttp = ArduinoClientHttp;

From 92f2a51c83254c154a7cb0a6943bc9bd6f4ebdcf Mon Sep 17 00:00:00 2001
From: Mirko Curtolo <mirko.curtolo86@gmail.com>
Date: Wed, 24 Jul 2024 15:22:17 +0200
Subject: [PATCH 2/6] Update rest client and fix inject node

---
 arduino-iot-cloud.js |  6 +++++-
 package-lock.json    | 10 +++++-----
 package.json         |  2 +-
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/arduino-iot-cloud.js b/arduino-iot-cloud.js
index 8202835..b039aba 100644
--- a/arduino-iot-cloud.js
+++ b/arduino-iot-cloud.js
@@ -347,9 +347,13 @@ module.exports = function (RED) {
               this.thing = config.thing;
               this.propertyId = config.property;
               this.propertyName = config.name;
+              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);
+                  const property = await this.arduinoRestClient.getProperty(this.thing, this.propertyId, opts);
                   this.send(
                     {
                       topic: property.name,
diff --git a/package-lock.json b/package-lock.json
index 4c4b437..b550bb9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@arduino/node-red-contrib-arduino-iot-cloud",
-  "version": "1.0.10",
+  "version": "1.1.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "@arduino/node-red-contrib-arduino-iot-cloud",
-      "version": "1.0.10",
+      "version": "1.1.0",
       "license": "GNU AFFERO GENERAL PUBLIC LICENSE",
       "dependencies": {
         "@arduino/arduino-iot-client": "github:arduino/iot-client-js",
@@ -32,8 +32,8 @@
       }
     },
     "node_modules/@arduino/arduino-iot-client": {
-      "version": "2.0.4",
-      "resolved": "git+ssh://git@github.com/arduino/iot-client-js.git#18e50fd5a420a26c025b59cd936e7d23e86d9955",
+      "version": "2.0.5",
+      "resolved": "git+ssh://git@github.com/arduino/iot-client-js.git#bc4991edf2bbd64571e2b52f79d16d392c8d01f2",
       "license": "GPLv3",
       "dependencies": {
         "@babel/cli": "^7.0.0",
@@ -3321,7 +3321,7 @@
       }
     },
     "@arduino/arduino-iot-client": {
-      "version": "git+ssh://git@github.com/arduino/iot-client-js.git#18e50fd5a420a26c025b59cd936e7d23e86d9955",
+      "version": "git+ssh://git@github.com/arduino/iot-client-js.git#bc4991edf2bbd64571e2b52f79d16d392c8d01f2",
       "from": "@arduino/arduino-iot-client@arduino/iot-client-js",
       "requires": {
         "@babel/cli": "^7.0.0",
diff --git a/package.json b/package.json
index 079a5d0..7127cfc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@arduino/node-red-contrib-arduino-iot-cloud",
-  "version": "1.0.10",
+  "version": "1.1.0",
   "description": "Node-RED nodes to talk to Arduino IoT Cloud",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"

From c424f9d34035e2afcb6c1bab1cafd3eaa878708a Mon Sep 17 00:00:00 2001
From: mirkokurt <mirko.curtolo86@gmail.com>
Date: Thu, 5 Dec 2024 14:04:10 -0100
Subject: [PATCH 3/6] fix: add the organization header to the token request for
 http nodes

---
 arduino-iot-cloud.js                | 12 ++++++------
 utils/arduino-connection-manager.js | 20 ++++++++++++--------
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/arduino-iot-cloud.js b/arduino-iot-cloud.js
index b039aba..315fe51 100644
--- a/arduino-iot-cloud.js
+++ b/arduino-iot-cloud.js
@@ -75,7 +75,7 @@ module.exports = function (RED) {
         try {
 
           if (config.thing !== "" && config.property !== "") {
-            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig);
+            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
             if (this.arduinoRestClient){
               this.arduinoRestClient.openConnections++;
               this.organization = config.organization;
@@ -152,7 +152,7 @@ module.exports = function (RED) {
       this.timeWindowUnit = config.timeWindowUnit;
       if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") {
         try {
-          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig);
+          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
           if (this.arduinoRestClient){
             this.arduinoRestClient.openConnections++;
             if (config.thing !== "" && config.property !== "") {
@@ -251,7 +251,7 @@ module.exports = function (RED) {
       this.organization = config.organization;
       if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") {
         try {
-          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig);
+          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
           if (this.arduinoRestClient){
             this.arduinoRestClient.openConnections++;
             if (config.thing !== "" && config.property !== "") {
@@ -340,7 +340,7 @@ module.exports = function (RED) {
         try {
 
           if (config.thing !== "" && config.property !== "") {
-            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig);
+            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
             if (this.arduinoRestClient){
               this.arduinoRestClient.openConnections++;
               this.organization = config.organization;
@@ -432,7 +432,7 @@ module.exports = function (RED) {
             clientid: req.query.clientid,
             clientsecret: req.query.clientsecret
           }
-        });
+        }, this.organization);
       } else if (req.query.connectionid) {
         const connectionConfig = RED.nodes.getNode(req.query.connectionid);
         if (!connectionConfig) {
@@ -440,7 +440,7 @@ module.exports = function (RED) {
           console.log(str);
           return res.send(JSON.stringify({ error: str }));
         }
-        arduinoRestClient = await connectionManager.getClientHttp(connectionConfig);
+        arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
       } else {
         str=RED._("arduino-iot-cloud.connection-error.no-cred-available");
         console.log(str);
diff --git a/utils/arduino-connection-manager.js b/utils/arduino-connection-manager.js
index e553778..0abee24 100644
--- a/utils/arduino-connection-manager.js
+++ b/utils/arduino-connection-manager.js
@@ -40,7 +40,7 @@ const getClientMutex = new Mutex();
 var numRetry=0;
 
 
-async function getToken(connectionConfig) {
+async function getToken(connectionConfig, organizationID) {
   const dataToSend = {
       grant_type: 'client_credentials',
       client_id: connectionConfig.credentials.clientid,
@@ -49,12 +49,16 @@ async function getToken(connectionConfig) {
   };
 
   try {
+    var req = superagentsuperagent
+      .post(accessTokenUri)
+      .set('content-type', 'application/x-www-form-urlencoded')
+      .set('accept', 'json')
 
-    var res = await superagent
-              .post(accessTokenUri)
-              .set('content-type', 'application/x-www-form-urlencoded')
-              .set('accept', 'json')
-              .send(dataToSend);
+    if (organizationID) {
+      req.set('X-Organization', organizationID)
+    }
+
+    var res = await req.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) {
@@ -161,7 +165,7 @@ async function getClientMqtt(connectionConfig, RED) {
 
 }
 
-async function getClientHttp(connectionConfig) {
+async function getClientHttp(connectionConfig, organizationID) {
 
   if (!connectionConfig || !connectionConfig.credentials) {
     throw new Error("Cannot find cooonection config or credentials.");
@@ -172,7 +176,7 @@ async function getClientHttp(connectionConfig) {
     var clientHttp;
     if (user === -1) {
 
-      var tokenInfo = await getToken(connectionConfig);
+      var tokenInfo = await getToken(connectionConfig, organizationID);
       if (tokenInfo !== undefined) {
         clientHttp = new ArduinoClientHttp.ArduinoClientHttp(tokenInfo.token);
 

From 8b7562c6e1d542678642587cb82ef3e85218b4a0 Mon Sep 17 00:00:00 2001
From: mirkokurt <mirko.curtolo86@gmail.com>
Date: Tue, 18 Feb 2025 14:34:33 -0100
Subject: [PATCH 4/6] fix: fix a glitch of the UI when the node configuration
 is re-opened after the first configuration

---
 arduino-iot-cloud.html |  6 ++++--
 package-lock.json      | 10 +++++-----
 package.json           |  2 +-
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/arduino-iot-cloud.html b/arduino-iot-cloud.html
index b8025df..1d74d5b 100644
--- a/arduino-iot-cloud.html
+++ b/arduino-iot-cloud.html
@@ -128,13 +128,15 @@
                 $("#node-input-organization").change(() => {
                     const connection = $("#node-input-connection").val();
                     const organization = $("#node-input-organization").val();
-                    this.organization = organization;
+                    
                     if (connection === "_ADD_") {
                         $("#node-input-organization").empty();
                         str = this._("arduino-iot-cloud.config.connection.placeholders.no-conn-selected");
                         $("<option value='" + "" + "' > " + str + "</option>").appendTo("#node-input-thing");
                         $("#node-input-thing").trigger("change");
-                    } else {
+                    } 
+                    if (this.organization != organization) {
+                        this.organization = organization;
                         $("select#node-input-thing").empty();
                         initThings(connection, this._, null, organization);
                     }
diff --git a/package-lock.json b/package-lock.json
index b550bb9..7e78a84 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
       "version": "1.1.0",
       "license": "GNU AFFERO GENERAL PUBLIC LICENSE",
       "dependencies": {
-        "@arduino/arduino-iot-client": "github:arduino/iot-client-js",
+        "@arduino/arduino-iot-client": "^3.0.0",
         "@arduino/cbor-js": "github:arduino/cbor-js",
         "async-mutex": "^0.1.4",
         "jws": "^3.2.2",
@@ -32,8 +32,8 @@
       }
     },
     "node_modules/@arduino/arduino-iot-client": {
-      "version": "2.0.5",
-      "resolved": "git+ssh://git@github.com/arduino/iot-client-js.git#bc4991edf2bbd64571e2b52f79d16d392c8d01f2",
+      "version": "3.0.0",
+      "resolved": "git+ssh://git@github.com/arduino/iot-client-js.git#ac903b60b4001cf7282144dc26a73d19f57a7cfa",
       "license": "GPLv3",
       "dependencies": {
         "@babel/cli": "^7.0.0",
@@ -3321,8 +3321,8 @@
       }
     },
     "@arduino/arduino-iot-client": {
-      "version": "git+ssh://git@github.com/arduino/iot-client-js.git#bc4991edf2bbd64571e2b52f79d16d392c8d01f2",
-      "from": "@arduino/arduino-iot-client@arduino/iot-client-js",
+      "version": "git+ssh://git@github.com/arduino/iot-client-js.git#ac903b60b4001cf7282144dc26a73d19f57a7cfa",
+      "from": "@arduino/arduino-iot-client@^3.0.0",
       "requires": {
         "@babel/cli": "^7.0.0",
         "superagent": "^5.3.0"
diff --git a/package.json b/package.json
index 7127cfc..a520771 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     }
   },
   "dependencies": {
-    "@arduino/arduino-iot-client": "github:arduino/iot-client-js",
+    "@arduino/arduino-iot-client": "^3.0.0",
     "@arduino/cbor-js": "github:arduino/cbor-js",
     "async-mutex": "^0.1.4",
     "jws": "^3.2.2",

From 81da80bb3a12c861c0edd9c3f52c1e8fd7b4bb00 Mon Sep 17 00:00:00 2001
From: mirkokurt <mirko.curtolo86@gmail.com>
Date: Thu, 20 Feb 2025 08:17:43 -0100
Subject: [PATCH 5/6] fix: typo

---
 utils/arduino-connection-manager.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/arduino-connection-manager.js b/utils/arduino-connection-manager.js
index 0abee24..de718f5 100644
--- a/utils/arduino-connection-manager.js
+++ b/utils/arduino-connection-manager.js
@@ -49,7 +49,7 @@ async function getToken(connectionConfig, organizationID) {
   };
 
   try {
-    var req = superagentsuperagent
+    var req = superagent
       .post(accessTokenUri)
       .set('content-type', 'application/x-www-form-urlencoded')
       .set('accept', 'json')

From fa5401f4e81045e8c541857e82022540d13af751 Mon Sep 17 00:00:00 2001
From: Luca Rinaldi <lucarin@protonmail.com>
Date: Fri, 21 Feb 2025 10:05:09 +0100
Subject: [PATCH 6/6] Token refresh only in case of errors (#56)

* feat: reduce token refresh

* improve refresh token http

* fix get properties

* fix errors

* remove token refresh loop for http client

* remove expiration field

* make a single refresh token

* fix: add organizationID to getToken method

---------

Co-authored-by: mirkokurt <mirko.curtolo86@gmail.com>
---
 .../arduino-iot-client-mqtt.js                |   9 +-
 arduino-iot-cloud.js                          |  10 +-
 utils/arduino-connection-manager.js           | 317 +++++++-----------
 utils/arduino-iot-cloud-api-wrapper.js        |  85 +++--
 4 files changed, 189 insertions(+), 232 deletions(-)

diff --git a/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js b/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js
index edd9759..cd92d5e 100644
--- a/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js
+++ b/arduino-iot-client-mqtt/arduino-iot-client-mqtt.js
@@ -230,7 +230,7 @@ class ArduinoClientMqtt {
   }
 
   async reconnect() {
-    await this.connection.reconnect();
+    this.connection.reconnect();
   };
 
   async updateToken(token) {
@@ -241,7 +241,7 @@ class ArduinoClientMqtt {
       try {
         if (this.connection) {
           // Disconnect to the connection that is using the old token
-          await this.connection.end();
+          this.connection.end();
 
           // Remove the connection
           this.connection = null;
@@ -627,6 +627,9 @@ class ArduinoClientMqtt {
       node=nodeId;
     }
     const propOutputTopic = `/a/t/${thingId}/e/o`;
+    if (!this.propertyCallback[propOutputTopic] || !this.propertyCallback[propOutputTopic][name]) {
+      return Promise.resolve(this.numSubscriptions);
+    }
     var pos=-1;
     for(var i=0; i<this.propertyCallback[propOutputTopic][name].length; i++){
       var cbObject=this.propertyCallback[propOutputTopic][name][i];
@@ -656,4 +659,4 @@ function toArrayBuffer(buf) {
   return ab;
 }
 
-exports.ArduinoClientMqtt = ArduinoClientMqtt;
\ No newline at end of file
+exports.ArduinoClientMqtt = ArduinoClientMqtt;
diff --git a/arduino-iot-cloud.js b/arduino-iot-cloud.js
index 315fe51..4277a73 100644
--- a/arduino-iot-cloud.js
+++ b/arduino-iot-cloud.js
@@ -75,7 +75,7 @@ module.exports = function (RED) {
         try {
 
           if (config.thing !== "" && config.property !== "") {
-            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
+            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
             if (this.arduinoRestClient){
               this.arduinoRestClient.openConnections++;
               this.organization = config.organization;
@@ -152,7 +152,7 @@ module.exports = function (RED) {
       this.timeWindowUnit = config.timeWindowUnit;
       if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") {
         try {
-          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
+          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
           if (this.arduinoRestClient){
             this.arduinoRestClient.openConnections++;
             if (config.thing !== "" && config.property !== "") {
@@ -251,7 +251,7 @@ module.exports = function (RED) {
       this.organization = config.organization;
       if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") {
         try {
-          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
+          this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
           if (this.arduinoRestClient){
             this.arduinoRestClient.openConnections++;
             if (config.thing !== "" && config.property !== "") {
@@ -340,7 +340,7 @@ module.exports = function (RED) {
         try {
 
           if (config.thing !== "" && config.property !== "") {
-            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, this.organization);
+            this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
             if (this.arduinoRestClient){
               this.arduinoRestClient.openConnections++;
               this.organization = config.organization;
@@ -476,7 +476,7 @@ module.exports = function (RED) {
       }
     } catch (err) {
       str=RED._("arduino-iot-cloud.connection-error.wrong-cred-sys-unvail");
-      console.log(`Status: ${err.status}, message: ${err.error}`);
+      console.log(`getThingsOrProperties status: ${err.status}, message: ${err.error} (error: ${err})`);
       return res.send(JSON.stringify({ error: str }));
     }
   }
diff --git a/utils/arduino-connection-manager.js b/utils/arduino-connection-manager.js
index de718f5..05ecf60 100644
--- a/utils/arduino-connection-manager.js
+++ b/utils/arduino-connection-manager.js
@@ -24,67 +24,42 @@ const accessTokenUri = process.env.NODE_RED_ACCESS_TOKEN_URI || 'https://api2.ar
 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
+
+const mqttMutex = new Mutex();
+/** mqttConnections elem struct
  * {
  *  clientId: clientId,
  *  connectionConfig: connectionConfig,
- *  token: token,
- *  expires_token_ts: ts,
  *  clientMqtt: clientMqttobj,
+ * }
+ */
+var mqttConnections = [];
+const httpMutex = new Mutex();
+/** httpConnections elem struct
+ * {
+ *  clientId: clientId,
+ *  connectionConfig: connectionConfig,
  *  clientHttp: clientHttpobj,
- *  timeoutUpdateToken: timeout
  * }
  */
-var connections = [];
-const getClientMutex = new Mutex();
-var numRetry=0;
-
-
-async function getToken(connectionConfig, organizationID) {
-  const dataToSend = {
-      grant_type: 'client_credentials',
-      client_id: connectionConfig.credentials.clientid,
-      client_secret: connectionConfig.credentials.clientsecret,
-      audience: accessTokenAudience
-  };
-
-  try {
-    var req = superagent
-      .post(accessTokenUri)
-      .set('content-type', 'application/x-www-form-urlencoded')
-      .set('accept', 'json')
-
-    if (organizationID) {
-      req.set('X-Organization', organizationID)
-    }
-
-    var res = await req.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 };
+var httpConnections = [];
+
+function getMqttOptions(clientId, token, RED){
+   async function reconnect() {
+    console.log('Reconnecting to MQTT');
+    const releaseMutex = await mqttMutex.acquire();
+    let id = findUser(mqttConnections, clientId);
+    if (id !== -1) {
+      let token = await waitForToken(mqttConnections[id].connectionConfig);
+      await mqttConnections[id].clientMqtt.updateToken(token);
     }
-  } 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
-      );
-    }else{
-      console.log(err);
-    }
-
+    releaseMutex();
   }
-}
 
-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"){
           const node = RED.nodes.getNode(n.id);
@@ -92,17 +67,23 @@ function getMqttOptions(clientId,token,RED){
         }
       });
 
-      await reconnectMqtt(clientId);
+      console.log('Disconnected from MQTT');
 
+      await new Promise((resolve) => setTimeout(resolve, 1000));
+      await reconnect();
     },
     onOffline: async () => {
-      console.log(`connection lost for ${clientId}`);
       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" });
         }
       });
+
+      console.log('Offline from MQTT');
+
+      await new Promise((resolve) => setTimeout(resolve, 1000));
+      await reconnect();
     },
     onConnected: () =>{
       RED.nodes.eachNode((n)=>{
@@ -111,211 +92,151 @@ function getMqttOptions(clientId,token,RED){
           node.status({});
         }
       });
+
+      console.log('Connected to MQTT'); 
     },
     useCloudProtocolV2: true
   };
 }
 
 async function getClientMqtt(connectionConfig, RED) {
-
   if (!connectionConfig || !connectionConfig.credentials) {
     throw new Error("Cannot find connection config or credentials.");
   }
-  const releaseMutex = await getClientMutex.acquire();
+  const releaseMutex = await mqttMutex.acquire();
   try {
-    let user = findUser(connectionConfig.credentials.clientid);
     let clientMqtt;
-    if (user === -1) {
+    let id = findUser(mqttConnections, connectionConfig.credentials.clientid);
+    if (id === -1) {
+      let token = await waitForToken(connectionConfig);
       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);
-        connections.push({
-          clientId: connectionConfig.credentials.clientid,
-          connectionConfig: connectionConfig,
-          token: tokenInfo.token,
-          expires_token_ts: tokenInfo.expires_in,
-          clientMqtt: clientMqtt,
-          clientHttp: null,
-          timeoutUpdateToken: timeout
-        });
-        await clientMqtt.connect(ArduinoIotCloudOptions);
-      } else {
-        clientMqtt = undefined;
-      }
+      mqttConnections.push({
+        clientId: connectionConfig.credentials.clientid,
+        connectionConfig: connectionConfig,
+        clientMqtt: clientMqtt,
+      });
+      await clientMqtt.connect(
+        getMqttOptions(connectionConfig.credentials.clientid, token, RED),
+      ); 
     } else {
-      if (connections[user].clientMqtt !== null) {
-        clientMqtt = connections[user].clientMqtt;
-      } else {
-        clientMqtt = new ArduinoClientMqtt.ArduinoClientMqtt();
-        const ArduinoIotCloudOptions = getMqttOptions(connectionConfig.credentials.clientid,connections[user].token,RED)
-        connections[user].clientMqtt = clientMqtt;
-        await clientMqtt.connect(ArduinoIotCloudOptions);
-
-      }
+      clientMqtt = mqttConnections[id].clientMqtt;
     }
-    releaseMutex();
-
     return clientMqtt;
   } catch (err) {
     console.log(err);
+  } finally {
     releaseMutex();
   }
-
 }
 
 async function getClientHttp(connectionConfig, organizationID) {
-
   if (!connectionConfig || !connectionConfig.credentials) {
     throw new Error("Cannot find cooonection config or credentials.");
   }
-  const releaseMutex = await getClientMutex.acquire();
+  const releaseMutex = await httpMutex.acquire();
   try {
-    var user = findUser(connectionConfig.credentials.clientid);
+    var id = findUser(httpConnections, connectionConfig.credentials.clientid);
     var clientHttp;
-    if (user === -1) {
-
-      var tokenInfo = await getToken(connectionConfig, organizationID);
-      if (tokenInfo !== undefined) {
-        clientHttp = new ArduinoClientHttp.ArduinoClientHttp(tokenInfo.token);
-
-        var timeout = setTimeout(() => { updateToken(connectionConfig) }, tokenInfo.expires_in * 1000);
-        connections.push({
-          clientId: connectionConfig.credentials.clientid,
-          connectionConfig: connectionConfig,
-          token: tokenInfo.token,
-          expires_token_ts: tokenInfo.expires_in,
-          clientMqtt: null,
-          clientHttp: clientHttp,
-          timeoutUpdateToken: timeout
-        });
-
-      }
-
+    if (id === -1) {
+      clientHttp = new ArduinoClientHttp.ArduinoClientHttp(async () => await getToken(connectionConfig, organizationID));
+      httpConnections.push({
+        clientId: connectionConfig.credentials.clientid,
+        connectionConfig: connectionConfig,
+        clientHttp: clientHttp,
+      });
     } else {
-      if (connections[user].clientHttp !== null) {
-        clientHttp = connections[user].clientHttp;
-      } else {
-        clientHttp = new ArduinoClientHttp.ArduinoClientHttp(connections[user].token);
-
-        connections[user].clientHttp = clientHttp;
-      }
+      clientHttp = httpConnections[id].clientHttp;
     }
-
-    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
-      );
-    }else{
-      console.log(err);
-    }
-
+    console.log(err);
+  } finally {
     releaseMutex();
+  }
+}
 
+async function deleteClientMqtt(clientId, thing, propertyName, nodeId) {
+  const releaseMutex = await mqttMutex.acquire();
+  var id = findUser(mqttConnections, clientId);
+  if (id !== -1) {
+    var ret = await mqttConnections[id].clientMqtt.removePropertyValueCallback(thing, propertyName, nodeId);
+    if (ret === 0) {
+      await mqttConnections[id].clientMqtt.disconnect();
+      delete mqttConnections[id].clientMqtt;
+      mqttConnections[id].clientMqtt = null;
+      mqttConnections.splice(id, 1);
+    }
   }
+  releaseMutex();
+}
 
+async function deleteClientHttp(clientId) {
+  const releaseMutex = await httpMutex.acquire();
+  var id = findUser(httpConnections, clientId);
+  if (id !== -1) {
+    if (httpConnections[id].clientHttp !== null) {
+      httpConnections[id].clientHttp.openConnections--;
+      if (httpConnections[id].clientHttp.openConnections === 0) {
+        httpConnections.splice(id, 1);
+      }
+    }
+  }
+  releaseMutex();
 }
 
-function findUser(clientId) {
+function findUser(connections, clientId) {
   for (var i = 0; i < connections.length; i++) {
     if (connections[i].clientId === clientId) {
       return i;
     }
   }
   return -1;
-
 }
 
-async function updateToken(connectionConfig) {
-  try {
-    var user = findUser(connectionConfig.credentials.clientid);
-    if (user !== -1) {
-      var tokenInfo = await getToken(connectionConfig);
-      if (tokenInfo !== undefined) {
-        numRetry=0;
-        connections[user].token = tokenInfo.token;
-        connections[user].expires_token_ts = tokenInfo.expires_in;
-        if(connections[user].clientMqtt){
-          connections[user].clientMqtt.updateToken(tokenInfo.token);
-        }
-        if(connections[user].clientHttp){
-          connections[user].clientHttp.updateToken(tokenInfo.token);
-        }
-        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);
-        }
-
-        numRetry++;
-      }
+async function waitForToken(connectionConfig, organizationID) {
+  let delay = 200;
+  while (true) {
+    let token = await getToken(connectionConfig, organizationID);
+    if (token) {
+      return token;
     }
-  } catch (err) {
-    console.log(err);
+    await new Promise((resolve) => setTimeout(resolve, delay));
+    delay = Math.min(delay * 2, 5000);
   }
 }
 
-async function deleteClientMqtt(clientId, thing, propertyName, nodeId) {
-  const releaseMutex = await getClientMutex.acquire();
-  var user = findUser(clientId);
-  if (user !== -1) {
-    if (connections[user].clientMqtt !== null) {
-      var ret = await connections[user].clientMqtt.removePropertyValueCallback(thing, propertyName,nodeId);
+async function getToken(connectionConfig, organizationID) {
+  const dataToSend = {
+      grant_type: 'client_credentials',
+      client_id: connectionConfig.credentials.clientid,
+      client_secret: connectionConfig.credentials.clientsecret,
+      audience: accessTokenAudience
+  };
 
-      if (ret === 0) {
-        await connections[user].clientMqtt.disconnect();
-        delete connections[user].clientMqtt;
-        connections[user].clientMqtt = null;
-        if (connections[user].clientHttp === null) {
-          if (connections[user].timeoutUpdateToken)
-            clearTimeout(connections[user].timeoutUpdateToken);
-          connections.splice(user, 1);
-        }
-      }
-    }
-  }
-  releaseMutex();
-}
+  try {
+    var req = superagent
+    .post(accessTokenUri)
+    .set('content-type', 'application/x-www-form-urlencoded')
+    .set('accept', 'json')
 
-async function deleteClientHttp(clientId) {
-  const releaseMutex = await getClientMutex.acquire();
-  var user = findUser(clientId);
-  if (user !== -1) {
-    if (connections[user].clientHttp !== null) {
-      connections[user].clientHttp.openConnections--;
-      if (connections[user].clientHttp.openConnections === 0) {
-        connections[user].clientHttp = null;
-      }
-    }
-    if (connections[user].clientMqtt === null) {
-      if (connections[user].timeoutUpdateToken)
-        clearTimeout(connections[user].timeoutUpdateToken);
-      connections.splice(user, 1);
+    if (organizationID) {
+      req.set('X-Organization', organizationID)
     }
-  }
-  releaseMutex();
-}
 
-async function reconnectMqtt(clientId) {
-  var user = findUser(clientId);
-  if (user !== -1) {
-    if(connections[user].clientMqtt){
-      await connections[user].clientMqtt.reconnect();
+    var res = await req.send(dataToSend);
+    var token = res.body.access_token;
+    if (token !== undefined) {
+      return token;
+    }
+  } catch (err) {
+    if(err.response && err.response.res){
+      console.log("cannot get token: " + err.response.res.statusCode + ' ' + err.response.res.statusMessage);
+    }else{
+      console.log(err);
     }
   }
 }
-
+ 
 exports.getClientMqtt = getClientMqtt;
 exports.getClientHttp = getClientHttp;
 exports.deleteClientMqtt = deleteClientMqtt;
diff --git a/utils/arduino-iot-cloud-api-wrapper.js b/utils/arduino-iot-cloud-api-wrapper.js
index 1fcaf10..f4b8624 100644
--- a/utils/arduino-iot-cloud-api-wrapper.js
+++ b/utils/arduino-iot-cloud-api-wrapper.js
@@ -30,46 +30,79 @@ const apiSeries = new ArduinoIotClient.SeriesV2Api(client);
 const apiThings = new ArduinoIotClient.ThingsV2Api(client);
 
 class ArduinoClientHttp {
-  constructor(token) {
-    this.token = token;
+  constructor(getToken) {
     this.openConnections=0;
+    oauth2.accessToken = "";
     if(process.env.API_BASE_PATH){
       client.basePath = process.env.API_BASE_PATH;
     }
+    
+    // wrap the functions with refresh token logic 
+    let refreshingToken = null;
+    function withTokenRefresh(fn) {
+      return async (...args) => {
+        try {
+          return await fn(...args);
+        } catch (e) {
+          if (e.status === 401) {
+            // make sure only one refresh token is in progress
+            if (!refreshingToken) {
+              refreshingToken = (async () => {
+                try {
+                  oauth2.accessToken = await getToken();
+                } finally {
+                  refreshingToken = null;
+                }
+              })();
+            }
+            await refreshingToken;
+
+            // eagerly retry the request
+            if (oauth2.accessToken) {
+              return await fn(...args);
+            }
+          }
+          throw e;
+        }
+      };
+    }
+    this.wrappedPropertiesV2Publish = withTokenRefresh(apiProperties.propertiesV2Publish.bind(apiProperties));
+    this.wrappedThingsV2List = withTokenRefresh(apiThings.thingsV2List.bind(apiThings));
+    this.wrappedThingsV2Show = withTokenRefresh(apiThings.thingsV2Show.bind(apiThings));
+    this.wrappedPropertiesV2Show = withTokenRefresh(apiProperties.propertiesV2Show.bind(apiProperties));
+    this.wrappedSeriesV2BatchQueryRaw = withTokenRefresh(apiSeries.seriesV2BatchQueryRaw.bind(apiSeries));
   }
-  updateToken(token) {
-    this.token = token;
-  }
-  setProperty(thing_id, property_id, value, opts, device_id = undefined) {
+
+
+  async 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);
+    return await this.wrappedPropertiesV2Publish(thing_id, property_id, body, opts);
   }
-  getThings(opts) {
-    oauth2.accessToken = this.token;
-    return apiThings.thingsV2List(opts);
+
+  async getThings(opts = {}) {
+    return await this.wrappedThingsV2List(opts);
   }
-  getThing(thingId, opts) {
-    oauth2.accessToken = this.token;
+
+  async getThing(thingId, opts = {}) {
     opts.showDeleted = false;
-    return apiThings.thingsV2Show(thingId, opts);
+    return await this.wrappedThingsV2Show(thingId, opts);
   }
-  getProperties(thingId, opts) {
-    oauth2.accessToken = this.token;
+
+  async getProperties(thingId, opts = {}) {
     opts.showProperties = true;
-    const thing = apiThings.thingsV2Show(thingId, opts);
-    return thing.then(({properties}) => properties);
+    const { properties } = await this.wrappedThingsV2Show(thingId, opts);
+    return properties;
   }
-  getProperty(thingId, propertyId, opts) {
-    oauth2.accessToken = this.token;
-    return apiProperties.propertiesV2Show(thingId, propertyId, opts);
+
+  async getProperty(thingId, propertyId, opts = {}) {
+    return await this.wrappedPropertiesV2Show(thingId, propertyId, opts);
   }
-  getSeries(thingId, propertyId, start, end, opts) {
 
-    const body =  JSON.stringify({
+  async getSeries(_thingId, propertyId, start, end, opts = {}) {
+    const body = JSON.stringify({
       requests: [{
         q: "property." + propertyId,
         from: start,
@@ -79,8 +112,8 @@ class ArduinoClientHttp {
       }],
       resp_version: 1
     });
-    oauth2.accessToken = this.token;
-    return apiSeries.seriesV2BatchQueryRaw(body, opts);
+    return await this.wrappedSeriesV2BatchQueryRaw(body, opts);
   }
 }
+
 exports.ArduinoClientHttp = ArduinoClientHttp;