UNPKG

node-red-contrib-knx-ultimate

Version:

Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control, ETS group address importer, and KNX routing between interfaces. Easy to use and highly configurable.

209 lines (194 loc) 8.72 kB
module.exports = function (RED) { // msg is: // // Build final input message object // return { // topic: _outputtopic // , payload: jsValue // , devicename: (typeof _devicename !== 'undefined') ? _devicename : "" // , payloadmeasureunit: sPayloadmeasureunit // , payloadsubtypevalue: sPayloadsubtypevalue // , knx: // { // event: _event // , dpt: sInputDpt // //, details: dpt // , dptdesc: sDptdesc // , source: _srcGA // , destination: _destGA // , rawValue: _Rawvalue // } // }; // The node.exposedGAs is and array of: // { // address, // dpt, // payload // lastupdate // lastupdateLocale // } function knxUltimateGlobalContext (config) { RED.nodes.createNode(this, config) const node = this node.serverKNX = RED.nodes.getNode(config.server) || undefined node.topic = node.name node.name = config.name === undefined ? 'KNXGlobalContext' : config.name node.outputtopic = node.name node.dpt = '' node.notifyreadrequest = false node.notifyreadrequestalsorespondtobus = 'false' node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = '' node.notifyresponse = true node.notifywrite = true node.initialread = false node.listenallga = true node.outputtype = 'write' node.outputRBE = 'false' // Apply or not RBE to the output (Messages coming from flow) node.inputRBE = 'false' // Apply or not RBE to the input (Messages coming from BUS) node.currentPayload = '' // Current value for the RBE input and for the .previouspayload msg node.passthrough = 'no' node.formatmultiplyvalue = 1 node.formatnegativevalue = 'leave' node.formatdecimalsvalue = 999 node.writeExecutionInterval = config.writeExecutionInterval === undefined ? 1000 : config.writeExecutionInterval node.contextStorage = config.contextStorage !== undefined ? config.contextStorage : '' node.exposeAsVariable = config.exposeAsVariable !== undefined ? config.exposeAsVariable : 'exposeAsVariableREADONLY' // Should expose the Group Addresses to the Global Context? node.exposedGAs = [] node.timerExposedGAs = null const pushStatus = (status) => { if (!status) return const provider = node.serverKNX if (provider && typeof provider.applyStatusUpdate === 'function') { provider.applyStatusUpdate(node, status) } else { node.status(status) } } const updateStatus = (status) => { if (!status) return pushStatus(status) } // Used to call the status update from the config node. node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => { try { if (node.serverKNX === null) { updateStatus({ fill: 'red', shape: 'dot', text: '[NO GATEWAY SELECTED]' }); return } GA = GA === undefined ? '' : GA payload = payload === undefined ? '' : payload payload = typeof payload === 'object' ? JSON.stringify(payload) : payload const dDate = new Date() const ts = (node.serverKNX && typeof node.serverKNX.formatStatusTimestamp === 'function') ? node.serverKNX.formatStatusTimestamp(dDate) : `${dDate.getDate()}, ${dDate.toLocaleTimeString()}` updateStatus({ fill, shape, text: GA + ' ' + payload + ' ' + text + ' (' + ts + ')' }) } catch (error) { } } // 02/12/2022 Expose the complete ETS CSV as well if (node.exposeAsVariable !== 'exposeAsVariableNO') { try { node.serverKNX.csv.forEach(element => { node.exposedGAs.push({ address: element.ga, dpt: element.dpt, devicename: element.devicename, payload: undefined, lastupdate: undefined, lastupdateLocale: undefined }) }) } catch (error) { } } // exposeAsVariableREADWRITE // #region "WRITE TO BUS" node.goTimerGo = function () { if (node.timerExposedGAs !== null) clearTimeout(node.timerExposedGAs) // 21/03/2021 node.timerExposedGAs = setTimeout(() => { let oContext = node.context().global.get(node.name + '_WRITE', node.contextStorage) || [] node.context().global.set(node.name + '_WRITE', [], node.contextStorage) // Delete the var for (let index = 0; index < oContext.length; index++) { const element = oContext[index] if (element.hasOwnProperty('address') === false) { node.setNodeStatus({ fill: 'RED', shape: 'dot', text: 'NO Group Address set', payload: '', GA: '', dpt: '', devicename: '' }) RED.log.error('knxUltimateGlobalContext: No group address set in node ' + node.id) oContext = null // 21/03/2022 node.goTimerGo() return } if (element.hasOwnProperty('payload') === false) { node.setNodeStatus({ fill: 'RED', shape: 'dot', text: 'NO payload set', payload: '', GA: '', dpt: '', devicename: '' }) RED.log.error('knxUltimateGlobalContext: No payload set for address ' + element.address + ' in node ' + node.id) oContext = null // 21/03/2022 node.goTimerGo() return } // 13/09/2021 retrieve the datapoint if not specified if (element.hasOwnProperty('dpt') === false || element.dpt === undefined || element.dpt === '') { try { const sDPT = node.serverKNX.csv.find(item => item.ga === element.address).dpt element.dpt = sDPT } catch (error) { node.setNodeStatus({ fill: 'RED', shape: 'dot', text: 'Datapoint not found in CSV for ' + element.address, payload: '', GA: '', dpt: '', devicename: '' }) RED.log.error('knxUltimateGlobalContext: Datapoint not found in CSV for address ' + element.address + ' in node ' + node.id) oContext = null // 21/03/2022 node.goTimerGo() return } } node.setNodeStatus({ fill: 'green', shape: 'dot', text: 'Write', payload: element.payload, GA: element.address, dpt: element.dpt || '', devicename: '' }) node.serverKNX.sendKNXTelegramToKNXEngine({ grpaddr: element.address, payload: element.payload, dpt: element.dpt || '', outputtype: 'write', nodecallerid: node.id }) } oContext = null // 21/03/2022 node.goTimerGo() }, node.writeExecutionInterval) } // 21/02/2021 timer for write to BUS if (node.exposeAsVariable === 'exposeAsVariableREADWRITE') { node.goTimerGo() node.setNodeStatus({ fill: 'green', shape: 'dot', text: 'Start Writing', payload: '', GA: '', dpt: '', devicename: '' }) } else { if (node.timerExposedGAs !== null) clearTimeout(node.timerExposedGAs) node.context().global.set(node.name + '_WRITE', [], node.contextStorage) // Delete the var } // #endregion // This function is called by the knx-ultimate config node, to output a msg.payload. node.handleSend = msg => { if (node.exposeAsVariable !== 'exposeAsVariableNO') { try { var oGa = node.exposedGAs.find(ga => ga.address === msg.knx.destination) } catch (error) { console.log(error) } const dNow = new Date() const lastupdate = dNow.toISOString() const lastupdateLocale = dNow.toLocaleString() if (oGa === undefined) { node.exposedGAs.push({ address: msg.knx.destination, devicename: undefined, dpt: msg.knx.dpt, payload: msg.payload, lastupdate, lastupdateLocale }) } else { oGa.dpt = msg.knx.dpt oGa.payload = msg.payload oGa.lastupdate = lastupdate oGa.lastupdateLocale = lastupdateLocale } // Save into the global Context try { node.context().global.set(node.name + '_READ', node.exposedGAs, node.contextStorage) } catch (error) { console.log(error) } oGa = null // 21/03/2022 } else { node.exposedGAs = [] node.context().global.set(node.name + '_READ', node.exposedGAs, node.contextStorage) } } node.on('input', function (msg) { }) node.on('close', function (done) { if (node.timerExposedGAs !== null) clearTimeout(node.timerExposedGAs) node.exposedGAs = [] if (node.serverKNX) { node.serverKNX.removeClient(node) } done() }) // On each deploy, unsubscribe+resubscribe if (node.serverKNX) { node.serverKNX.removeClient(node) node.serverKNX.addClient(node) } } RED.nodes.registerType('knxUltimateGlobalContext', knxUltimateGlobalContext) }