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.

216 lines (209 loc) 9.89 kB
/* eslint-disable max-len */ const dptlib = require('knxultimate').dptlib;//require('knxultimate').dptlib; const loggerClass = require('./utils/sysLogger') module.exports = function (RED) { function knxUltimateHueScene(config) { RED.nodes.createNode(this, config); const node = this; node.serverKNX = RED.nodes.getNode(config.server) || undefined; node.serverHue = RED.nodes.getNode(config.serverHue) || undefined; node.topic = node.name; node.name = config.name === undefined ? 'Hue' : config.name; node.dpt = ''; node.notifyreadrequest = false; node.notifyreadrequestalsorespondtobus = 'false'; node.notifyreadrequestalsorespondtobusdefaultvalueifnotinitialized = ''; node.notifyresponse = false; node.notifywrite = true; node.initialread = true; node.listenallga = true; // Don't remove 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.hueDevice = config.hueDevice; node.initializingAtStart = false; try { node.sysLogger = new loggerClass({ loglevel: node.serverKNX.loglevel, setPrefix: node.type + " <" + (node.name || node.id || '') + ">" }); } catch (error) { console.log(error.stack) } // Multi scene config.GAsceneMulti = config.GAsceneMulti === undefined ? '' : config.GAsceneMulti; config.namesceneMulti = config.namesceneMulti === undefined ? '' : config.namesceneMulti; config.dptsceneMulti = config.dptsceneMulti === undefined ? '' : config.dptsceneMulti; config.rules = config.rules === undefined ? [] : config.rules; config.selectedModeTabNumber = config.selectedModeTabNumber === undefined ? 0 : Number(config.selectedModeTabNumber); // Transform as number // Used to call the status update from the config node. node.setNodeStatus = ({ fill, shape, text, payload, }) => { try { if (payload === undefined) payload = ''; const dDate = new Date(); payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString(); node.sKNXNodeStatusText = `|KNX: ${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`; node.status({ fill, shape, text: (node.sHUENodeStatusText || '') + ' ' + (node.sKNXNodeStatusText || '') }); } catch (error) { } }; // Used to call the status update from the HUE config node. node.setNodeStatusHue = ({ fill, shape, text, payload }) => { try { if (payload === undefined) payload = ''; const dDate = new Date(); payload = typeof payload === "object" ? JSON.stringify(payload) : payload.toString(); node.sHUENodeStatusText = `|HUE: ${text} ${payload} (${dDate.getDate()}, ${dDate.toLocaleTimeString()})`; node.status({ fill, shape, text: node.sHUENodeStatusText + ' ' + (node.sKNXNodeStatusText || '') }); } catch (error) { } }; // This function is called by the knx-hue config node node.handleSend = msg => { let state = {}; if (config.selectedModeTabNumber === 0) { // Sigle try { switch (msg.knx.destination) { case config.GAscene: msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptscene)); if (config.dptscene.startsWith("1.")) { if (msg.payload === true) { state = { recall: { action: config.hueSceneRecallType } }; node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene'); } else { // Turn off all light belonging to the scene (async () => { try { node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, { on: { on: false } }, 'stopScene'); } catch (error) { if (node.sysLogger !== undefined && node.sysLogger !== null) node.sysLogger.info('KNXUltimateHUEConfig: classHUE: handleQueue: stopScene: ' + error.message); } })(); } } else { if (Number(config.valscene) === msg.payload.scenenumber && msg.payload.save_recall === 0) { state = { recall: { action: config.hueSceneRecallType } }; node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene'); } } node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: msg.payload }); break; default: break; } } catch (error) { node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE single: ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' }); } } else if (config.selectedModeTabNumber === 1) { // Multi try { if (config.GAsceneMulti !== undefined && config.dptsceneMulti !== undefined && config.rules.length !== 0) { if (config.GAsceneMulti === msg.knx.destination) { msg.payload = dptlib.fromBuffer(msg.knx.rawValue, dptlib.resolve(config.dptsceneMulti)); // row is: { rowRuleKNXSceneNumber: rowRuleKNXSceneNumber, rowRuleHUESceneName: rowRuleHUESceneName, rowRuleHUESceneID:rowRuleHUESceneID, rowRuleRecallAs:rowRuleRecallAs} config.rules.forEach(row => { if (row.rowRuleKNXSceneNumber !== undefined && row.rowRuleHUESceneID !== undefined && row.rowRuleRecallAs !== undefined && Number(row.rowRuleKNXSceneNumber) === Number(msg.payload.scenenumber) && Number(msg.payload.save_recall) === 0 && node.serverHue.hueManager !== undefined) { state = { recall: { action: row.rowRuleRecallAs } }; node.serverHue.hueManager.writeHueQueueAdd(row.rowRuleHUESceneID, state, 'setScene'); node.setNodeStatusHue({ fill: 'green', shape: 'dot', text: 'KNX->HUE', payload: msg.payload }); } }); } } } catch (error) { node.status({ fill: 'red', shape: 'dot', text: 'KNX->HUE multi: ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' }); } } }; node.handleSendHUE = (_event) => { try { if (Number(config.selectedModeTabNumber) === 0 && config.hueDevice !== undefined && _event.id === config.hueDevice) { // Single mode const knxMsgPayload = {}; knxMsgPayload.topic = config.GAsceneStatus; knxMsgPayload.dpt = config.dptsceneStatus; if (_event.hasOwnProperty("status") && _event.status.hasOwnProperty("active")) { knxMsgPayload.payload = _event.status.active !== "inactive"; // Send to KNX bus if (knxMsgPayload.topic !== "" && knxMsgPayload.topic !== undefined && node.serverKNX !== undefined) { node.serverKNX.sendKNXTelegramToKNXEngine({ grpaddr: knxMsgPayload.topic, payload: knxMsgPayload.payload, dpt: knxMsgPayload.dpt, outputtype: "write", nodecallerid: node.id, }); } node.status({ fill: "blue", shape: "dot", text: `HUE->KNX ${JSON.stringify(knxMsgPayload.payload)} (${new Date().getDate()}, ${new Date().toLocaleTimeString()})`, }); // Output the msg to the flow node.send(_event); } } else if (Number(config.selectedModeTabNumber) === 1 && config.rules !== undefined) { // Multi mode config.rules.forEach(row => { if (row.rowRuleHUESceneID !== undefined && _event.id === row.rowRuleHUESceneID) { // Output the msg to the flow node.send(_event); } }); } } catch (error) { node.status({ fill: 'red', shape: 'dot', text: 'HUE->KNX error ' + error.message + ' (' + new Date().getDate() + ', ' + new Date().toLocaleTimeString() + ')' }); } }; // On each deploy, unsubscribe+resubscribe if (node.serverKNX) { node.serverKNX.removeClient(node); node.serverKNX.addClient(node); } if (node.serverHue) { node.serverHue.removeClient(node); node.serverHue.addClient(node); } node.on('input', (msg, send, done) => { try { const state = RED.util.cloneMessage(msg); node.serverHue.hueManager.writeHueQueueAdd(config.hueDevice, state, 'setScene'); node.setNodeStatusHue({ fill: "green", shape: "dot", text: "->HUE", payload: "Flow msg.", }); } catch (error) { node.setNodeStatusHue({ fill: "red", shape: "dot", text: "->HUE", payload: error.message, }); } // Once finished, call 'done'. // This call is wrapped in a check that 'done' exists // so the node will work in earlier versions of Node-RED (<1.0) if (done) { done(); } }); node.on('close', function (done) { if (node.serverKNX) { node.serverKNX.removeClient(node); } if (node.serverHue) { node.serverHue.removeClient(node); } done(); }); } RED.nodes.registerType('knxUltimateHueScene', knxUltimateHueScene); };