@arduino/node-red-contrib-arduino-iot-cloud
Version:
Node-RED nodes to talk to Arduino IoT Cloud
505 lines (478 loc) • 21.8 kB
JavaScript
/*
* 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");
module.exports = function (RED) {
function ArduinoIotInput(config) {
const realConstructor = async (config) => {
RED.nodes.createNode(this, config);
const connectionConfig = RED.nodes.getNode(config.connection);
this.status({});
this.lastValue = undefined;
this.organization = config.organization;
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);
if (this.arduinoClient && this.arduinoClient.connection.connected) {
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" });
}
this.on('close', function (done) {
connectionManager.deleteClientMqtt(connectionConfig.credentials.clientid, this.thing, this.propertyVariableName,config.id).then(() => { done(); });
});
//this.poll(connectionConfig);
} catch (err) {
console.log(err);
}
}
}
realConstructor.apply(this, [config]);
}
RED.nodes.registerType("property in", ArduinoIotInput);
function ArduinoIotOutput(config) {
const realConstructor = async (config) => {
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") {
try {
if (config.thing !== "" && config.property !== "") {
this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
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 = {}
if (this.organization) {
opts.xOrganization = this.organization;
}
this.on('input', async function (msg) {
try {
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";
}
if (s != undefined)
this.status({ fill: "grey", shape: "dot", text: s });
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'
);
}else{
console.log(err);
}
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(); });
});
}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'
);
}else{
console.log(err);
}
}
}
}
realConstructor.apply(this, [config]);
}
RED.nodes.registerType("property out", ArduinoIotOutput);
function ArduinoIotInputHist(config) {
const realConstructor = async (config) => {
RED.nodes.createNode(this, config);
const connectionConfig = RED.nodes.getNode(config.connection);
this.status({});
const node = this;
this.timeWindowCount = config.timeWindowCount;
this.timeWindowUnit = config.timeWindowUnit;
if (connectionConfig && config.thing !== "" && config.thing !== "0" && config.property !== "" && config.property !== "0") {
try {
this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
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 = {}
if (this.organization) {
opts.xOrganization = this.organization;
}
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 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 = [];
if (values && times) {
values.forEach(function (item, index, array) {
data.push({
x: moment(times[index]).unix() * 1000,
y: values[index]
});
});
}
node.send(
{
topic: config.name,
payload: [{
series: [],
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'
);
}else{
console.log(err);
}
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(); });
});
}
}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'
);
}else{
console.log(err);
}
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);
function ArduinoIotInputPoll(config) {
const realConstructor = async (config) => {
RED.nodes.createNode(this, config);
const connectionConfig = RED.nodes.getNode(config.connection);
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, config.organization);
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) {
this.poll(connectionConfig, pollTime, this.organization);
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" });
}
} 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{
console.log(err);
}
}
}
}
realConstructor.apply(this, [config]);
}
ArduinoIotInputPoll.prototype = {
poll: async function (connectionConfig, pollTime, organization) {
try {
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 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);
} 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{
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" });
}
}
}
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") {
try {
if (config.thing !== "" && config.property !== "") {
this.arduinoRestClient = await connectionManager.getClientHttp(connectionConfig, config.organization);
if (this.arduinoRestClient){
this.arduinoRestClient.openConnections++;
this.organization = config.organization;
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, 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({});
} 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{
console.log(err);
}
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(); });
});
}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'
);
}else{
console.log(err);
}
this.status({ fill: "red", shape: "dot", text: "arduino-iot-cloud.status.error-getting-value" });
}
}
}
realConstructor.apply(this, [config]);
}
RED.nodes.registerType("property in push", ArduinoIotInputPush);
function ArduinoConnectionNode(config) {
RED.nodes.createNode(this, config);
this.applicationname = config.applicationname;
this.clientid = config.clientid;
this.clientsecret = config.clientsecret;
}
RED.nodes.registerType("arduino-connection", ArduinoConnectionNode, {
credentials: {
clientid: { type: "password" },
clientsecret: { type: "password" }
}
});
async function getThingsOrProperties(req, res, thingsOrProperties) {
let arduinoRestClient;
var str;
try {
if (req.query.clientid || req.query.clientsecret) {
arduinoRestClient = await connectionManager.getClientHttp({
credentials: {
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) {
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, this.organization);
} else {
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 = {}
if (organization) {
opts.xOrganization = organization;
}
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 = {}
if (organization) {
opts.xOrganization = organization;
}
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 = {}
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");
console.log(str);
return res.send(JSON.stringify({ error: str }));
}
} catch (err) {
str=RED._("arduino-iot-cloud.connection-error.wrong-cred-sys-unvail");
console.log(`getThingsOrProperties status: ${err.status}, message: ${err.error} (error: ${err})`);
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("/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");
});
function getStatus(value) {
if (typeof value !== "object") {
if (typeof value === "number" && !(Number.isInteger(value)))
return value.toFixed(3);
else
return value;
}
return RED._("arduino-iot-cloud.status.object-injected");
}
}