UNPKG

@metz-connect/node-red-ewio2

Version:

Provides nodes to access EWIO2 interfaces like IOs and metering - from Node-RED

264 lines (251 loc) 16.3 kB
/** * @file Node to handle EWIO2 digital inputs. * @author Metz Connect */ /** * ewio2 digital input node namespace. * @namespace DigitalInNode */ module.exports = function(RED) { /** * Stores settings from digital IN configuration menu. * Checks if all required settings are available, otherwise shows error status. * * If the node is restarted, disable or deleted, this node is deregistered from registed nodes of ewio2Connections map. * @memberof DigitalInNode * @param {Object} config - digital IN settings from digital IN menu (frontend). */ function EwioDigitalInputsNode(config) { const MD5 = require("crypto-js/md5"); const {log, pubSub, sendStatusOutput} = require("./backend/general"); RED.nodes.createNode(this,config); this.ewio2 = config.ewio2; this.name = config.name; this.ioportDi = config.ioportDi; this.value = config.value; this.valueText = config.valueText; this.pulseCntr = config.pulseCntr; this.inputSignal = config.inputSignal; this.risingEdge = config.risingEdge; this.fallingEdge = config.fallingEdge; this.outputTopic = config.outputTopic; let node = this; log(node, "create node"); log(node, 'ewio2 ID: ' + JSON.stringify(this.ewio2, null, 4)); this.status({}); if(this.ewio2 === "") { log(node, 'no EWIO2 selected'); this.status({fill:"red",shape:"ring",text:"@metz-connect/node-red-ewio2/ewio2:status.missingEwio"}); } else { const configNode = RED.nodes.getNode(this.ewio2); if(!configNode) { log(node, 'no EWIO2 (deactivated?)'); this.status({fill:"red",shape:"ring",text:"@metz-connect/node-red-ewio2/ewio2:status.configNodeMissing"}); } else if(configNode.host === "") { log(node, 'no host selected'); this.status({fill:"red",shape:"ring",text:"@metz-connect/node-red-ewio2/ewio2:status.missingHost"}); } else if(configNode.credentials.username === "") { log(node, 'no username selected'); this.status({fill:"red",shape:"ring",text:"@metz-connect/node-red-ewio2/ewio2:status.missingUser"}); } else if(configNode.credentials.password === "") { log(node, 'no password selected'); this.status({fill:"red",shape:"ring",text:"@metz-connect/node-red-ewio2/ewio2:status.missingPw"}); } else if(configNode.encryption === "") { log(node, 'no encryption selected'); this.status({fill:"red",shape:"ring",text:"@metz-connect/node-red-ewio2/ewio2:status.missingEnc"}); } else { const configNodeTls = RED.nodes.getNode(configNode.tlsConfig); log(node, 'user: ' + JSON.stringify(configNode.credentials.username, null, 4)); log(node, 'user: ' + JSON.stringify(configNode.credentials.username, null, 4)); log(node, 'pw: ' + JSON.stringify(configNode.credentials.password, null, 4)); log(node, 'enc: ' + JSON.stringify(configNode.encryption, null, 4)); var configData = {configNodeId: this.ewio2, host: configNode.host, user: configNode.credentials.username, pw: MD5(configNode.credentials.password), enc: configNode.encryption}; // same function name as in other nodes (pubSub in ewio2Connection.js publishes general errors to all nodes) function showStatus(status) { log(node, 'DI recv ioport: ' + status.addr + '; ioport: ' + this.ioportDi + '; node.id: ' + node.id + '; this.ewio2: ' + this.ewio2 + '; status.configNodeId: ' + status.configNodeId + "; msg: " + status.message); // selected EWIO2 configuration node ID is equal to received (from EWIO2 livechannel or from ewio2Connections map) EWIO2 configuration node ID (for this node) if (config.ewio2 == status.configNodeId) { log(node, 'updated nodeID: ' + node.id); node.status({fill: status.color, shape: status.shape, text: status.message}); sendStatusOutput(status, node, RED); } } log(node, 'subscribe "show-status": ' + node.id); pubSub.subscribe("show-status", showStatus); // handles digital IN errors received from pubSub in ewio2Connection.js and shows status by calling showStatus fct above (in digital-inputs.js) function showStatusDi(status) { // received IO port (from EWIO2 livechannel or from ewio2Connections map) is equal to selected IO port (for this node) // received IO port may be empty for errors not related to an IO (e.g. "wrong credentials") if (status.addr && config.ioportDi) { if (status.addr === config.ioportDi) { showStatus(status); } } } pubSub.subscribe("show-status-di", showStatusDi); function sendValueDi(valueStruct) { // configuration node ID fits if (valueStruct.configNodeId === config.ewio2) { // port address (e.g. 00_0) fits (IO type already known by publish/subscribe topic ("send-value-di) if (valueStruct.addr === config.ioportDi) { let value; // invalid values are 'undefined' if (!valueStruct.value || valueStruct.value === "ERROR") { value = undefined; } else { // pulse counter checked (activated) flag changed if (valueStruct.updated & 0x04) { // convert pulseCntrChecked ("1" --> "true"; "0" --> "false"), due to different formats of Node-RED and EWIO2 node.pulseCntr = (valueStruct.pulseCntrChecked === "1") ? "true" : "false"; } // rising edge flag set and HIGH received or pulse cntr value updated or falling edge flag set and LOW received or requested value (getValue) if ((config.risingEdge && (valueStruct.value === "1" || valueStruct.updated & 0x02)) || (config.fallingEdge && valueStruct.value === "0") || (valueStruct.updated & 0x08)) { // pulse counter value --> convert to Number if (node.pulseCntr === "true") { if (valueStruct.updated & 0x02 || valueStruct.updated & 0x08) { value = Number(valueStruct.pulseCntrValue); } // pulse counter activated else { if (valueStruct.updated & 0x04) { if (node.outputTopic) { node.send({payload: Number(valueStruct.pulseCntrValue), topic: node.outputTopic}); } else { node.send({payload: Number(valueStruct.pulseCntrValue)}); } } return; } } // signal counter else { if (valueStruct.updated & 0x01 || valueStruct.updated & 0x08) { // convert to boolean value = (valueStruct.value === "1"); } // pulse counter deactivated else { if (valueStruct.updated & 0x04) { if (node.outputTopic) { node.send({payload: (valueStruct.value === "1"), topic: node.outputTopic }); } else { node.send({payload: (valueStruct.value === "1")}); } } return; } } } else { return; } } log(node, 'onConnect getValue DONE ' + node.id + '; value: ' + JSON.stringify(valueStruct, null, 4) + "; typeof: " + typeof value); var msg = { payload: value }; if (node.outputTopic) { msg = { payload: value, topic: node.outputTopic }; } node.send(msg); } } } pubSub.subscribe("send-value-di", sendValueDi); // establish automatically connection to EWIO2 and retrieve value once, to also be able to listen to further msgs on livedata channel if (this.risingEdge || this.fallingEdge) { (async function () { const {getValue} = require("./backend/ewio2Connection"); // Eclipse shows 'await' warning due to jsdoc @return comment await getValue(configData, "di", node.ioportDi, node.id, RED, configNodeTls); })(); } node.on('input', async function(msg, send, done) { const {getValue, setValue} = require("./backend/ewio2Connection"); // only get value when 'trigger on input signal' checkbox is checked if (this.inputSignal) { log(node, 'msg.topic: ' + msg.topic); if (typeof msg.topic === "string") { // read value from EWIO2 if (msg.topic === "trigger") { // Eclipse shows 'await' warning due to jsdoc @return comment const values = await getValue(configData, "di", this.ioportDi, node.id, RED, configNodeTls); } // set pulse counter / signal input for port else if (msg.topic === "pulse") { if (typeof msg.payload === "number" || typeof msg.payload === "boolean") { // keep boolean true / false and convert number equal 0 -> false / number > 0 or < 0 -> true msg.payload = (msg.payload) ? 1 : 0; log(node, 'msg.payload: ' + msg.payload); await setValue(configData, msg.payload, "di", this.ioportDi, node.id, RED, "setS0", configNodeTls); log(node, 'di setS0 DONE ' + node.id); } else { log(node, 'msg.payload WRONG TYPE'); pubSub.publish("show-status-di", {"color": "red", "shape": "ring", "message": "@metz-connect/node-red-ewio2/ewio2:status.noBoolOrNumber", "configNodeId": this.ewio2, "addr": this.ioportDi}); } } // set pulse counter value else if (msg.topic === "counter") { if (typeof msg.payload === "number") { if (this.pulseCntr === "true") { log(node, 'msg.payload: ' + msg.payload); await setValue(configData, msg.payload, "di", this.ioportDi, node.id, RED, "setPC", configNodeTls); log(node, 'di setPC DONE ' + node.id); } // not a pulse counter else { log(node, 'not a pulse counter'); pubSub.publish("show-status-di", {"color": "red", "shape": "ring", "message": "@metz-connect/node-red-ewio2/ewio2:status.notPulseCntr", "configNodeId": this.ewio2, "addr": this.ioportDi}); } } // payload is not a 'number' else { log(node, 'msg.payload WRONG TYPE'); pubSub.publish("show-status-di", {"color": "red", "shape": "ring", "message": "@metz-connect/node-red-ewio2/ewio2:status.noNumber", "configNodeId": this.ewio2, "addr": this.ioportDi}); } } // unknown topic else { log(node, 'wrong topic'); pubSub.publish("show-status-di", {"color": "red", "shape": "ring", "message": "@metz-connect/node-red-ewio2/ewio2:status.wrongMsgTopic", "configNodeId": this.ewio2, "addr": this.ioportDi}); } } else { log(node, 'wrong topic type (not string)'); pubSub.publish("show-status-di", {"color": "red", "shape": "ring", "message": "@metz-connect/node-red-ewio2/ewio2:status.wrongMsgTopicType", "configNodeId": this.ewio2, "addr": this.ioportDi}); } } else { log(node, 'trigger on input signal not active'); pubSub.publish("show-status-di", {"color": "yellow", "shape": "ring", "message": "@metz-connect/node-red-ewio2/ewio2:status.triggerInputInactive", "configNodeId": this.ewio2, "addr": this.ioportDi}); } if (done) { done(); } }); node.on("close", function(done) { const {deregisterNode} = require("./backend/ewio2Connection"); log(node, 'node disabled / deleted / restarted -> unsubscribe "show-status": ' + node.id); pubSub.unsubscribe("show-status"); pubSub.unsubscribe("show-status-di"); pubSub.unsubscribe("send-value-di"); log(node, 'node disabled / deleted -> deregister from websocket connection": ' + node.id); const userData = {configNodeId: configNode.id, host: configNode.host, user: configNode.credentials.username, pw: MD5(configNode.credentials.password), enc: configNode.encryption}; deregisterNode( function () { log(node, 'callback deregisterNode fct DONE'); done(); }, userData, node.id, RED); }); } } } RED.nodes.registerType("EWIO2 - Digital IN",EwioDigitalInputsNode); }