node-red-contrib-datacake-helpers
Version:
Node-RED nodes to interact with the Datacake GraphQL API for KPIs and device stats. Includes other helper nodes for device management.
197 lines (169 loc) • 7.41 kB
JavaScript
const axios = require('axios');
module.exports = function(RED) {
function DatacakeGraphQLDeviceNode(config) {
RED.nodes.createNode(this, config);
const node = this;
// Get configuration node
this.datacakeConfig = RED.nodes.getNode(config.datacakeConfig);
this.deviceId = config.deviceId;
// Store available devices from the config
this.availableDevices = config.availableDevices || [];
if (!this.datacakeConfig) {
this.status({ fill: "red", shape: "ring", text: "Missing configuration" });
return;
}
this.status({ fill: "green", shape: "dot", text: "Ready" });
// Function to fetch device data from Datacake
const fetchDeviceData = async () => {
if (!node.deviceId) {
node.error("Device ID not configured");
node.status({ fill: "red", shape: "ring", text: "Device ID not configured" });
return null;
}
try {
const response = await axios({
url: 'https://api.datacake.co/graphql/',
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Token ${node.datacakeConfig.credentials.workspaceToken}`
},
data: {
query: `
query {
device(deviceId:"${node.deviceId}") {
id
serialNumber
verboseName
location
online
lastHeard
lastHeardThreshold
currentMeasurements(allActiveFields: true) {
field {
id
fieldName
fieldType
unit
}
value
valueString
}
}
}
`
}
});
if (response.data.errors) {
throw new Error(response.data.errors[0].message);
}
return response.data;
} catch (error) {
node.error(`Error fetching device data: ${error.message}`);
node.status({ fill: "red", shape: "ring", text: `Error: ${error.message}` });
return null;
}
};
// Process the raw data into a flattened structure
const processDeviceData = (data) => {
if (!data || !data.data || !data.data.device) return null;
const device = data.data.device;
// Create a base object with device information
const result = {
id: device.id,
serialNumber: device.serialNumber,
verboseName: device.verboseName,
location: device.location,
online: device.online,
lastHeard: device.lastHeard,
lastHeardThreshold: device.lastHeardThreshold
};
// Add flattened measurements
if (device.currentMeasurements && Array.isArray(device.currentMeasurements)) {
device.currentMeasurements.forEach(measurement => {
const fieldName = measurement.field.fieldName;
const unit = measurement.field.unit;
// Use valueString if available, otherwise use value
const value = measurement.valueString !== ""
? measurement.valueString
: measurement.value;
// Store the raw value
result[fieldName] = value;
// Store field metadata
result[`${fieldName}_metadata`] = {
id: measurement.field.id,
type: measurement.field.fieldType,
unit: unit
};
});
}
return result;
};
// Listen for incoming messages
node.on('input', async function(msg, send, done) {
node.status({ fill: "blue", shape: "dot", text: "Fetching data..." });
const data = await fetchDeviceData();
if (data) {
const processedData = processDeviceData(data);
if (processedData) {
msg.payload = processedData;
node.status({ fill: "green", shape: "dot", text: "Data fetched" });
send(msg);
} else {
node.status({ fill: "yellow", shape: "ring", text: "No valid data" });
}
}
if (done) {
done();
}
});
node.on('close', function() {
// Clean up
});
}
// Function to dynamically fetch device list for the editor
RED.httpAdmin.get('/datacakegraphql/devices', RED.auth.needsPermission('datacakegraphql.read'), async function(req, res) {
if (!req.query.configId) {
res.status(400).json({ error: "No config node ID provided" });
return;
}
const configNode = RED.nodes.getNode(req.query.configId);
if (!configNode) {
res.status(400).json({ error: "Config node not found" });
return;
}
try {
const response = await axios({
url: 'https://api.datacake.co/graphql/',
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Token ${configNode.credentials.workspaceToken}`
},
data: {
query: `
query {
allDevices(inWorkspace:"${configNode.workspaceUuid}", searchTags:[], searchName:"") {
id
serialNumber
verboseName
location
tags
online
}
}
`
}
});
if (response.data.errors) {
throw new Error(response.data.errors[0].message);
}
res.status(200).json(response.data);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
RED.nodes.registerType("datacakegraphql-device", DatacakeGraphQLDeviceNode, {
credentials: {}
});
}