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 { - 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; 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