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 and ETS group address importer. Easy to use and highly configurable.

191 lines (178 loc) 8.16 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 // Used to call the status update from the config node. node.setNodeStatus = ({ fill, shape, text, payload, GA, dpt, devicename }) => { try { if (node.serverKNX === null) { node.status({ 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() node.status({ fill, shape, text: GA + ' ' + payload + ' ' + text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' }) } 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) }