@bitpoolos/edge-bacnet
Version:
A bacnet gateway for node-red
982 lines (907 loc) • 35.3 kB
JavaScript
/*
MIT License Copyright 2021, 2022 - Bitpool Pty Ltd
*/
module.exports = function (RED) {
const { BacnetClient } = require("./bacnet_client");
const { BacnetClientConfig, getIpAddress } = require("./common");
const { BacnetServer } = require("./bacnet_server.js");
function BitpoolBacnetGatewayDevice(config) {
RED.nodes.createNode(this, config);
var node = this;
var nodeContext = this.context().flow;
//bacnet local device info
this.localDeviceAddress = config.local_device_address;
this.local_interface_name = config.local_interface_name;
this.local_device_port = config.local_device_port;
this.apduSize = config.apduSize;
this.maxSegments = config.maxSegments;
this.apduTimeout = config.apduTimeout;
this.broadCastAddr = config.broadCastAddr;
this.discover_polling_schedule = config.discover_polling_schedule;
this.device_read_schedule = config.device_read_schedule;
this.manual_instance_range_enabled = config.manual_instance_range_enabled;
this.manual_instance_range_start = config.manual_instance_range_start;
this.manual_instance_range_end = config.manual_instance_range_end;
this.nodeName = config.name;
this.toRestartNodeRed = config.toRestartNodeRed;
this.deviceId = config.deviceId;
this.logErrorToConsole = config.logErrorToConsole;
this.bacnetServerEnabled = config.serverEnabled;
this.retries = config.retries;
this.bacnetServer = nodeContext.get("bacnetServer") || null;
this.deviceRangeRegisters = config.deviceRangeRegisters;
this.portRangeRegisters = config.portRangeRegisters;
this.cacheFileEnabled = config.cacheFileEnabled;
this.sanitise_device_schedule = config.sanitise_device_schedule;
this.enable_device_discovery = config.enable_device_discovery;
//client and config store
this.bacnetConfig = nodeContext.get("bacnetConfig");
this.bacnetClient = nodeContext.get("bacnetClient");
// client and config names
this.bacnetClientName = "bacnetClientWrite" + node.id;
this.bacnetConfigName = "bacnetConfigWrite" + node.id;
//determines whether or not to log a found device on whoIs response
this.toLogIam = config.toLogIam;
try {
node.bacnetConfig = new BacnetClientConfig(
node.apduTimeout,
node.localDeviceAddress,
node.local_device_port,
node.apduSize,
node.maxSegments,
node.broadCastAddr,
node.discover_polling_schedule,
node.toRestartNodeRed,
node.deviceId,
node.manual_instance_range_enabled,
node.manual_instance_range_start,
node.manual_instance_range_end,
node.device_read_schedule,
node.retries,
node.cacheFileEnabled,
node.sanitise_device_schedule,
node.portRangeRegisters.filter((ele) => ele.enabled === true),
node.enable_device_discovery
);
if (typeof node.bacnetClient !== "undefined") {
node.bacnetClient.removeAllListeners();
bindEventListeners();
node.bacnetClient.reinitializeClient(node.bacnetConfig);
} else {
node.bacnetClient = new BacnetClient(node.bacnetConfig);
nodeContext.set("bacnetClient", node.bacnetClient);
nodeContext.set("serverWritePropEvent", false);
}
node.bacnetClient.scanMatrix = node.deviceRangeRegisters.filter((ele) => ele.enabled === true);
if (node.bacnetServerEnabled == true && node.bacnetClient && node.bacnetServer) {
node.bacnetServer.deviceId = node.deviceId;
}
node.bacnetClient.bacnetServerEnabled = node.bacnetServerEnabled;
if (node.bacnetServerEnabled == true && node.bacnetClient) {
if (node.bacnetServer == null) {
node.bacnetServer = new BacnetServer(node.bacnetClient, node.deviceId, RED.version());
nodeContext.set("bacnetServer", node.bacnetServer);
}
} else if (node.bacnetServerEnabled == false) {
node.bacnetServer = null;
}
// Clears event handlers of all listeners, avoiding memory leak
node.bacnetClient.removeAllListeners();
} catch (e) {
console.log("Bacnet gateway client init error: ", e);
}
try {
// Value response event handler for READ commands
node.bacnetClient.on("values", (values, outputType, objectPropertyType, readNodeName, deviceIndex, devicesToRead) => {
if (typeof values !== "undefined" && Object.keys(values).length) {
let publishText = `Publishing ${readNodeName} ${deviceIndex} / ${devicesToRead} `;
node.status({ fill: "blue", shape: "dot", text: publishText });
if (deviceIndex == devicesToRead) {
setTimeout(() => {
node.status({});
}, 3000);
}
let useDeviceName = outputType.useDeviceName;
if (outputType.json && !outputType.mqtt && !outputType.pointJson) {
//json
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
//simpleWithStatus
sendSimpleWithStatus(values, readNodeName, true, useDeviceName);
} else if (
objectPropertyType.fullObject &&
!objectPropertyType.simplePayload &&
!objectPropertyType.simpleWithStatus
) {
//fullObject
sendJsonAsMqtt(values, readNodeName, useDeviceName);
} else if (
objectPropertyType.simplePayload &&
!objectPropertyType.fullObject &&
!objectPropertyType.simpleWithStatus
) {
//simplePayload
sendSimpleJson(values, readNodeName, useDeviceName);
}
} else if (outputType.mqtt && !outputType.json && !outputType.pointJson) {
//mqtt
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
//simpleWithStatus
sendSimpleWithStatus(values, readNodeName, false, useDeviceName);
} else if (
objectPropertyType.fullObject &&
!objectPropertyType.simplePayload &&
!objectPropertyType.simpleWithStatus
) {
//fullObject
sendAsMqtt(values, readNodeName, useDeviceName);
} else if (
objectPropertyType.simplePayload &&
!objectPropertyType.fullObject &&
!objectPropertyType.simpleWithStatus
) {
//simplePayload
sendSimpleMqtt(values, readNodeName, useDeviceName);
}
} else if (outputType.pointJson && !outputType.json && !outputType.mqtt) {
//pointJson
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
//simpleWithStatus
sendSimpleWithStatus(values, readNodeName, false, useDeviceName);
} else if (
objectPropertyType.fullObject &&
!objectPropertyType.simplePayload &&
!objectPropertyType.simpleWithStatus
) {
//fullObject
sendIndividualMsgJson(values, readNodeName, useDeviceName);
} else if (
objectPropertyType.simplePayload &&
!objectPropertyType.fullObject &&
!objectPropertyType.simpleWithStatus
) {
//simplePayload
sendSimpleJsonPerPoint(values, readNodeName, useDeviceName);
}
}
}
});
// Who Is / Iam event handler
node.bacnetClient.on("deviceFound", (device) => {
if (node.toLogIam) {
if (device.source) {
node.warn(
`BACnet-MS/TP device found: ${device.deviceId} - ${device.address} - Network Id: ${device.source.net} - Mac: ${device.source.adr[0]}`
);
} else {
node.warn(`BACnet device found: ${device.deviceId} - ${device.address}`);
}
}
});
node.bacnetClient.on("bacnetErrorLog", (param1, param2) => {
logOut(param1, param2);
});
} catch (e) {
console.log("Bacnet node event handler error: ", e);
}
if (
node.nodeName !== "gateway" &&
node.nodeName !== "" &&
node.nodeName !== "null" &&
node.nodeName !== "undefined" &&
typeof node.nodeName == "string"
) {
if (node.bacnetServerEnabled == true && node.bacnetClient) {
node.bacnetServer.setDeviceName(node.nodeName);
}
}
if (node.bacnetServerEnabled == true && node.bacnetClient && node.bacnetServer) {
try {
// Clean up any existing listeners to prevent stale references
node.bacnetServer.removeAllListeners("writeProperty");
// Store the event handler function so we can clean it up later
node.writePropertyHandler = (topic, newValue) => {
let formattedTopic = topic;
if (
node.nodeName !== "gateway" &&
node.nodeName !== "" &&
node.nodeName !== "null" &&
node.nodeName !== "undefined" &&
typeof node.nodeName == "string"
) {
formattedTopic = `${node.nodeName}/BITPOOL_EDGE_BACNET_GATEWAY/BACNET_SERVER/${topic}`;
} else {
formattedTopic = `BITPOOL_EDGE_BACNET_GATEWAY/BACNET_SERVER/${topic}`;
}
node.send({ payload: newValue, topic: formattedTopic });
};
node.bacnetServer.on("writeProperty", node.writePropertyHandler);
} catch (e) {
console.log("Bacnet gateway node server writePoperty error: ", e);
}
}
try {
node.on("input", function (msg) {
if (msg.topic && msg.payload !== null) {
if (node.bacnetServer) {
node.bacnetServer.addObject(msg.topic, msg.payload);
}
}
if (msg.type == "Read") {
node.bacnetClient.doRead(msg.options, msg.outputType, msg.objectPropertyType, msg.readNodeName);
} else if (msg.type == "Write") {
node.bacnetClient.doWrite(msg.value, msg.options);
} else if (msg.doDiscover == true) {
node.status({ fill: "blue", shape: "dot", text: "Sending global Who is" });
node.bacnetClient.globalWhoIs();
setTimeout(() => {
node.status({});
}, 2000);
} else if (msg.payload == "BindEvents") {
node.bacnetClient.removeAllListeners();
bindEventListeners();
} else if (msg.doUpdatePriorityDevices == true && msg.priorityDevices !== null) {
node.bacnetClient
.updatePriorityQueue(msg.priorityDevices)
.then(function (result) {})
.catch(function (error) {
logOut("Error updating priorityQueue: ", error);
});
} else if (msg.testFunc == true) {
node.bacnetClient.testFunction(msg.address, msg.port, msg.type, msg.instance, msg.property, nodeWarn);
} else if (msg.applyDisplayNames) {
node.status({ fill: "blue", shape: "dot", text: "Updating display names" });
setTimeout(() => {
node.status({});
}, 2000);
node.bacnetClient
.applyDisplayNames(msg.pointsToRead)
.then(function (result) {})
.catch(function (error) {
logOut("Error in applyDisplayNames: ", error);
});
} else if (msg.forceUpdateDevices == true) {
node.bacnetClient.forceUpdateDevices(msg.deviceIdArray);
} else if (msg.reinitializeBacnetServer == true) {
if (node.bacnetServerEnabled == true && node.bacnetClient) {
node.bacnetServer.clearServerPoints();
if (msg.responseTopic) {
node.send({ topic: msg.responseTopic, payload: "Server sucessfully reinitialized" });
}
node.status({ fill: "blue", shape: "dot", text: "Reinitializing Server" });
setTimeout(() => {
node.status({});
}, 2000);
}
}
});
node.on("close", function () {
// Clean up the writeProperty event listener
if (node.bacnetServer && node.writePropertyHandler) {
node.bacnetServer.removeListener("writeProperty", node.writePropertyHandler);
node.writePropertyHandler = null;
}
// Reset the serverWritePropEvent flag so it can be re-registered
nodeContext.set("serverWritePropEvent", false);
});
} catch (e) {
console.log("Bacnet node event handler error: ", e);
}
//route handler for network data
RED.httpAdmin.get("/bitpool-bacnet-data/getNetworkTree", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient: ", node.bacnetClient);
//no bacnet client present
res.send(false);
} else {
node.bacnetClient
.getNetworkTreeData()
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error getting network data: ", error);
});
}
});
//route handler for rebuild data model command
RED.httpAdmin.get("/bitpool-bacnet-data/rebuildDataModel", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient: ", node.bacnetClient);
//no bacnet client present
res.send(false);
} else {
node.bacnetClient
.rebuildDataModel()
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error getting network data: ", error);
});
}
});
//route handler for the clear Bacnet server points function
RED.httpAdmin.get("/bitpool-bacnet-data/clearBacnetServerPoints", function (req, res) {
if (node.bacnetServerEnabled == true && node.bacnetClient) {
node.bacnetServer.clearServerPoints();
res.send(true);
} else {
res.send(false);
}
});
//route handler for the clear Bacnet server point function
RED.httpAdmin.post("/bitpool-bacnet-data/clearBacnetServerPoint", function (req, res) {
if (node.bacnetServerEnabled == true && node.bacnetClient) {
node.bacnetServer
.clearServerPoint(req)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
});
} else {
res.send(result);
}
});
//route handler for the retrieve Bacnet server points function
RED.httpAdmin.get("/bitpool-bacnet-data/getBacnetServerPoints", function (req, res) {
if (node.bacnetServerEnabled == true && node.bacnetClient) {
node.bacnetServer
.getServerPoints()
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error getting server points: ", error);
});
} else {
res.send([]);
}
});
//route handler for network data
RED.httpAdmin.get("/bitpool-bacnet-data/getNetworkInterfaces", function (req, res) {
getIpAddress()
.then(function (result) {
res.send(result);
})
.catch(function (error) {
logOut("Error getting network interfaces for client: ", error);
});
});
//route handler for getting device list
RED.httpAdmin.get("/bitpool-bacnet-data/getDeviceList", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while getting device list: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.getDeviceList()
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error getting network data: ", error);
});
}
});
//route handler for updating device list
RED.httpAdmin.post("/bitpool-bacnet-data/updateDeviceList", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while getting device list: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.updateDeviceList(req)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error getting network data: ", error);
});
}
});
//route handler for getting data model
RED.httpAdmin.get("/bitpool-bacnet-data/getDataModel", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while getting data model: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.getDataModel()
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error getting data model: ", error);
});
}
});
//route handler for updating data model
RED.httpAdmin.post("/bitpool-bacnet-data/updateDataModel", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while getting data model: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.updateDataModel(req)
.then(function (result) {
res.send(true);
})
.catch(function (error) {
res.send(error);
logOut("Error getting data model: ", error);
});
}
});
//route handler for purge device
RED.httpAdmin.post("/bitpool-bacnet-data/purgeDevice", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while getting device list: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.purgeDevice(req.body.d)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error purging device: ", error);
});
}
});
//route handler for updatePointsForDevice
RED.httpAdmin.post("/bitpool-bacnet-data/updatePointsForDevice", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while updating device points list: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.updatePointsForDevice(req.body.d)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error updating device: ", error);
});
}
});
//route handler for updatePoint
RED.httpAdmin.post("/bitpool-bacnet-data/updatePoint", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while updating device points list: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.updateIndividualPoint(req.body.d, req.body.k)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error updating device: ", error);
});
}
});
//route handler for setDeviceDisplayName
RED.httpAdmin.post("/bitpool-bacnet-data/setDeviceDisplayName", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while updating device points list: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.setDeviceDisplayName(req.body.d, req.body.n)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error setting device display name: ", error);
});
}
});
//route handler for setPointDisplayName
RED.httpAdmin.post("/bitpool-bacnet-data/setPointDisplayName", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while updating point name: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.setPointDisplayName(req.body.k, req.body.p, req.body.n)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error setting device display name: ", error);
});
}
});
//route handler for applyDisplayNames
RED.httpAdmin.post("/bitpool-bacnet-data/applyDisplayNames", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while applying display names: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.applyDisplayNames(req.body.pointsToRead)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error applying display names: ", error);
});
}
});
//route handler for importReadList
RED.httpAdmin.post("/bitpool-bacnet-data/importReadList", function (req, res) {
if (!node.bacnetClient) {
logOut("Issue with the bacnetClient while updating point name: ", node.bacnetClient);
res.send(false);
} else {
node.bacnetClient
.importReadList(req.body.p)
.then(function (result) {
res.send(result);
})
.catch(function (error) {
res.send(error);
logOut("Error setting device display name: ", error);
});
}
});
function bindEventListeners() {
// Value response event handler for READ commands
try {
node.bacnetClient.on("values", (values, outputType, objectPropertyType, readNodeName, deviceIndex, devicesToRead) => {
if (typeof values !== "undefined" && Object.keys(values).length) {
let publishText = `Publishing ${readNodeName} ${deviceIndex} / ${devicesToRead} `;
node.status({ fill: "blue", shape: "dot", text: publishText });
if (deviceIndex == devicesToRead) {
setTimeout(() => {
node.status({});
}, 3000);
}
let useDeviceName = outputType.useDeviceName;
if (outputType.json && !outputType.mqtt && !outputType.pointJson) {
//json
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
//simpleWithStatus
sendSimpleWithStatus(values, readNodeName, true, useDeviceName);
} else if (
objectPropertyType.fullObject &&
!objectPropertyType.simplePayload &&
!objectPropertyType.simpleWithStatus
) {
//fullObject
sendJsonAsMqtt(values, readNodeName, useDeviceName);
} else if (
objectPropertyType.simplePayload &&
!objectPropertyType.fullObject &&
!objectPropertyType.simpleWithStatus
) {
//simplePayload
sendSimpleJson(values, readNodeName, useDeviceName);
}
} else if (outputType.mqtt && !outputType.json && !outputType.pointJson) {
//mqtt
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
//simpleWithStatus
sendSimpleWithStatus(values, readNodeName, false, useDeviceName);
} else if (
objectPropertyType.fullObject &&
!objectPropertyType.simplePayload &&
!objectPropertyType.simpleWithStatus
) {
//fullObject
sendAsMqtt(values, readNodeName, useDeviceName);
} else if (
objectPropertyType.simplePayload &&
!objectPropertyType.fullObject &&
!objectPropertyType.simpleWithStatus
) {
//simplePayload
sendSimpleMqtt(values, readNodeName, useDeviceName);
}
} else if (outputType.pointJson && !outputType.json && !outputType.mqtt) {
//pointJson
if (objectPropertyType.simpleWithStatus && !objectPropertyType.fullObject && !objectPropertyType.simplePayload) {
//simpleWithStatus
sendSimpleWithStatus(values, readNodeName, false, useDeviceName);
} else if (
objectPropertyType.fullObject &&
!objectPropertyType.simplePayload &&
!objectPropertyType.simpleWithStatus
) {
//fullObject
sendIndividualMsgJson(values, readNodeName, useDeviceName);
} else if (
objectPropertyType.simplePayload &&
!objectPropertyType.fullObject &&
!objectPropertyType.simpleWithStatus
) {
//simplePayload
sendSimpleJsonPerPoint(values, readNodeName, useDeviceName);
}
}
}
});
// Who Is / Iam event handler
node.bacnetClient.on("deviceFound", (device) => {
if (node.toLogIam) node.warn(`BACnet device found: ${device.deviceId} - ${device.address}`);
});
} catch (e) {
console.log("Bacnet gateway BindListeners error: ", e);
}
}
function logOut(param1, param2) {
if (node.logErrorToConsole == true) {
if (arguments.length == 1) {
console.log("BACnet Error: ");
console.log(param1);
} else if (arguments.length == 2) {
console.log("BACnet Error: ");
console.log(param1, param2);
}
}
}
function getTopicString(type, useDeviceName, readNodeName, device, pointName, prop) {
let topic = "";
if (
node.nodeName !== "gateway" &&
node.nodeName !== "" &&
node.nodeName !== "null" &&
node.nodeName !== "undefined" &&
typeof node.nodeName == "string"
) {
if (readNodeName !== "" && readNodeName !== null && readNodeName !== undefined) {
if (useDeviceName) {
topic = `${node.nodeName}/${readNodeName}/${device}`;
} else {
topic = `${node.nodeName}/${readNodeName}`;
}
} else {
if (useDeviceName) {
topic = `${node.nodeName}/${device}`;
} else {
topic = `${node.nodeName}`;
}
}
} else {
if (readNodeName !== "" && readNodeName !== null && readNodeName !== undefined) {
if (useDeviceName) {
topic = `BITPOOL_BACNET_GATEWAY/${readNodeName}/${device}`;
} else {
topic = `BITPOOL_BACNET_GATEWAY/${readNodeName}`;
}
} else {
if (useDeviceName) {
topic = `BITPOOL_BACNET_GATEWAY/${device}`;
} else {
topic = `BITPOOL_BACNET_GATEWAY`;
}
}
}
switch (type) {
case "sendSimpleWithStatus":
topic += `/${pointName}`;
break;
case "sendSimpleWithStatusJson":
//do nothing
break;
case "sendSimpleMqtt":
topic += `/${pointName}`;
break;
case "sendAsMqtt":
topic += `/${pointName}/${prop}`;
break;
case "sendSimpleJson":
//do nothing
break;
case "sendJsonAsMqtt":
//do nothing
break;
case "sendIndividualMsgJson":
topic += `/${pointName}`;
break;
case "sendSimpleJsonPerPoint":
topic += `/${pointName}`;
break;
default:
break;
}
return topic;
}
sendSimpleWithStatus = function (values, readNodeName, isJson, useDeviceName) {
let devices = Object.keys(values);
devices.forEach(function (device) {
if (device !== "_msgid") {
let points = values[device];
let structuredObject = {};
let msgg = {};
for (let point in points) {
if (points[point] && "presentValue" in points[point]) {
let pointName = getPointName(points[point], point);
let topic = getTopicString("sendSimpleWithStatus", useDeviceName, readNodeName, device, pointName);
msgg.topic = topic;
let payload = {
presentValue: points[point]["presentValue"],
timestamp: points[point]["timestamp"],
status: points[point]["status"],
};
if (isJson) {
//json payload
structuredObject[point] = payload;
} else {
//mqtt payload
msgg.payload = payload;
node.send(msgg);
msgg = {};
}
}
}
if (isJson) {
//json payload
let topic = getTopicString("sendSimpleWithStatusJson", useDeviceName, readNodeName, device);
msgg.topic = topic;
msgg.payload = structuredObject;
node.send(msgg);
msgg = {};
}
}
});
};
sendSimpleMqtt = function (values, readNodeName, useDeviceName) {
let devices = Object.keys(values);
devices.forEach(function (device) {
if (device !== "_msgid") {
let points = values[device];
for (var point in points) {
if (points[point]) {
let pointName = getPointName(points[point], point);
let pointProps = Object.keys(points[point]);
pointProps.forEach(function (prop) {
let msg = {};
if (prop == "presentValue") {
let topic = getTopicString("sendSimpleMqtt", useDeviceName, readNodeName, device, pointName);
msg.topic = topic;
msg.payload = points[point][prop];
node.send(msg);
}
});
}
}
}
});
};
// Breaks down response JSON object into mqtt topic / payload
sendAsMqtt = function (values, readNodeName, useDeviceName) {
let devices = Object.keys(values);
devices.forEach(function (device) {
if (device !== "_msgid") {
let points = values[device];
for (var point in points) {
if (points[point]) {
let pointName = getPointName(points[point], point);
let pointProps = Object.keys(points[point]);
pointProps.forEach(function (prop) {
let msg = {};
if (prop !== "objectName") {
let topic = getTopicString("sendAsMqtt", useDeviceName, readNodeName, device, pointName, prop);
msg.topic = topic;
msg.payload = points[point][prop];
node.send(msg);
}
});
}
}
}
});
};
sendSimpleJson = function (values, readNodeName, useDeviceName) {
let devices = Object.keys(values);
devices.forEach(function (device) {
let msgg = {};
if (device !== "_msgid") {
let value = {
[device]: {},
};
let points = values[device];
for (var point in points) {
if (points[point]) {
let pointName = getPointName(points[point], point);
let pointProps = Object.keys(points[point]);
pointProps.forEach(function (prop) {
if (prop == "presentValue") {
value[device][point] = {
presentValue: points[point][prop],
};
}
});
}
}
let topic = getTopicString("sendSimpleJson", useDeviceName, readNodeName, device);
msgg.topic = topic;
msgg.payload = value[device];
node.send(msgg);
}
});
};
sendJsonAsMqtt = function (values, readNodeName, useDeviceName) {
if (typeof values == "object") {
let keys = Object.keys(values);
keys.forEach(function (key) {
let points = values[key];
let msgg = {};
let structuredObject = {};
let topic = getTopicString("sendJsonAsMqtt", useDeviceName, readNodeName, key);
msgg.topic = topic;
for (let point in points) {
let pointName = getPointName(points[point], point);
structuredObject[pointName] = points[point];
}
msgg.payload = structuredObject;
node.send(msgg);
});
}
};
sendIndividualMsgJson = function (values, readNodeName, useDeviceName) {
if (typeof values == "object") {
let keys = Object.keys(values);
keys.forEach(function (key) {
let points = values[key];
let msgg = {};
for (let point in points) {
let pointName = getPointName(points[point], point);
let topic = getTopicString("sendIndividualMsgJson", useDeviceName, readNodeName, key, pointName);
msgg.topic = topic;
msgg.payload = points[point];
node.send(msgg);
msgg = {};
}
});
}
};
sendSimpleJsonPerPoint = function (values, readNodeName, useDeviceName) {
let devices = Object.keys(values);
devices.forEach(function (device) {
if (device !== "_msgid") {
let points = values[device];
let msgg = {};
for (let point in points) {
if (points[point] && "presentValue" in points[point]) {
let pointName = getPointName(points[point], point);
let topic = getTopicString("sendSimpleJsonPerPoint", useDeviceName, readNodeName, device, pointName);
msgg.topic = topic;
let payload = {
presentValue: points[point]["presentValue"],
};
msgg.payload = payload;
node.send(msgg);
msgg = {};
}
}
}
});
};
function getPointName(object, pointName) {
if (object.displayName) {
return object.displayName;
}
return pointName;
}
function nodeWarn(message) {
node.warn(message);
}
}
RED.nodes.registerType("Bacnet-Gateway", BitpoolBacnetGatewayDevice);
};