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.

224 lines (205 loc) 8.77 kB
const KNXAddress = require('knxultimate').KNXAddress const _ = require('lodash') module.exports = function (RED) { function knxUltimateViewer (config) { RED.nodes.createNode(this, config) const node = this node.serverKNX = RED.nodes.getNode(config.server) || undefined 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) } if (node.serverKNX === undefined) { updateStatus({ fill: 'red', shape: 'dot', text: '[THE GATEWAY NODE HAS BEEN DISABLED]' }) return } node.topic = node.name node.name = config.name === undefined ? 'KNXViewer' : 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 = 2 node.timerPIN3 = null node.exposedGAs = [] // 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() updateStatus({ fill, shape, text: GA + ' ' + payload + ' ' + text + ' (' + dDate.getDate() + ', ' + dDate.toLocaleTimeString() + ')' }) } catch (error) { /* empty */ } } // This function is called by the knx-ultimate config node, to output a msg.payload. node.handleSend = msg => { try { var oGa = node.exposedGAs.find(ga => ga.address === msg.knx.destination) } catch (error) { } const sDeviceName = msg.devicename === node.name ? 'Import ETS file to view the group address name' : msg.devicename // The ETS file hasn't been imported const sAddressRAW = KNXAddress.createFromString(msg.knx.destination, KNXAddress.TYPE_GROUP).get() // Address as number (for ordering later) if (oGa === undefined) { node.exposedGAs.push({ address: msg.knx.destination, addressRAW: sAddressRAW, dpt: msg.knx.dpt, payload: msg.payload, devicename: sDeviceName, lastupdate: new Date(), rawPayload: 'HEX Raw: ' + msg.knx.rawValue.toString('hex') || '?', payloadmeasureunit: (msg.payloadmeasureunit !== 'unknown' ? ' ' + msg.payloadmeasureunit : '') }) } else { oGa.dpt = msg.knx.dpt oGa.payload = msg.payload oGa.devicename = sDeviceName oGa.lastupdate = new Date() oGa.rawPayload = 'HEX Raw: ' + msg.knx.rawValue.toString('hex') || '?' oGa.payloadmeasureunit = (msg.payloadmeasureunit !== 'unknown' ? ' ' + msg.payloadmeasureunit : '') } // Output the payload const PIN1 = node.createPayloadPIN1() const PIN2 = node.createPayloadPIN2() const PIN3 = node.createPayloadPIN3() node.send([PIN1, PIN2, PIN3]) } node.createPayloadPIN1 = () => { const aSorted = node.exposedGAs.sort((a, b) => { if (a.addressRAW !== undefined && b.addressRAW !== undefined) { return a.addressRAW > b.addressRAW ? 1 : -1 } else { return a.addressRAW !== undefined ? 1 : -1 } }) let sPayload = '' const sHead = `<div class="main"><table><caption>Current received KNX Group address values</caption> <thead> <tr> <th> GA </th> <th> Value </th> <th> DPT </th> <th> Last updated </th> <th> Group Address Name </th> </tr> </thead> <tbody>` const sFooter = `</tbody><tfoot> <tr> <th scope="row">Count</th> <td>` + aSorted.length + `</td> </tr> </tfoot> </table></div>` try { for (let index = 0; index < aSorted.length; index++) { const element = aSorted[index] sPayload += '<tr><td>' + element.address + '</td>' if (typeof element.payload === 'boolean' && element.payload === true) { sPayload += '<td><b><font color=green>True</font></b></td>' } else if (typeof element.payload === 'boolean' && element.payload === false) { sPayload += '<td><font color=red>False</font></td>' } else if (typeof element.payload === 'object' && !isNaN(Date.parse(element.payload))) { // The payload is a datetime sPayload += '<td>' + element.payload.toLocaleString() + '</td>' } else if (typeof element.payload === 'object') { // Is maybe a JSON? try { // sPayload += '<td>' + JSON.stringify(element.payload) + '</td>' sPayload += '<td><i>' + element.rawPayload + '</i></td>' } catch (error) { sPayload += '<td>' + element.payload + '</td>' } } else { sPayload += '<td>' + element.payload + element.payloadmeasureunit + '</td>' } sPayload += '<td>' + element.dpt + '</td>' sPayload += '<td>' + element.lastupdate.toLocaleString() + '</td>' sPayload += '<td><font style="font-size: smaller;">' + element.devicename + '</font></td></tr>' } } catch (error) { } return { topic: node.name, payload: sHead + sPayload + sFooter } } node.createPayloadPIN2 = () => { return { topic: node.name, payload: node.exposedGAs } } node.createPayloadPIN3 = () => { // Object containing the telegram in the queue // node.writeQueueAdd({ // grpaddr: _oClient.topic, // payload: "", // dpt: "", // outputtype: "read", // nodecallerid: _oClient.id, // }); let sHead = '' let sFooter = '' let sPayload = '' try { const aItems = _.clone(node.serverKNX.knxConnection.commandQueue) if (aItems === undefined) return sHead = `<div class="main"><table><caption>Queue of outgoing telegrams to the KNX BUS. The more the count,</br>the more congested is the KNX BUS.</caption> <thead> <tr> <th> Channel ID </th> <th> Sequence counter </th> <th> Type of packet</th> <th> Status </th> </tr> </thead> <tbody>` sFooter = `</tbody><tfoot> <tr> <th scope="row">Count</th> <td>` + aItems.length + `</td> </tr> </tfoot> </table></div>` for (let index = 0; index < aItems.length; index++) { const element = aItems[index] sPayload += '<tr><td>' + element.knxPacket.channelID + '</td>' sPayload += '<td><b><font color=green>' + element.knxPacket.seqCounter + '</font></b></td>' sPayload += '<td>' + element.knxPacket.type + '</td>' sPayload += '<td>' + element.knxPacket.status + '</td></tr>' } } catch (error) { } return { topic: node.name, payload: sHead + sPayload + sFooter } } // if (timerPIN3 !== null) clearInterval(timerPIN3); // timerPIN3 = setInterval(() => { // let PIN3 = node.createPayloadPIN3(); // node.send([null, null, PIN3]); // }, 200); node.on('input', function (msg) { }) node.on('close', function (done) { if (timerPIN3 !== null) clearInterval(timerPIN3) 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('knxUltimateViewer', knxUltimateViewer) }