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, KNX AI for diagnosticsand KNX routing between interfaces. Easy to use and highly configurable.

510 lines (457 loc) 26.9 kB
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script> <script type="text/javascript"> RED.nodes.registerType('knxUltimateSceneController', { category: "KNX Ultimate", color: '#C7E9C0', defaults: { //buttonState: {value: true}, server: { type: "knxUltimate-config", required: true }, name: { value: "" }, outputtopic: { value: "" }, topic: { value: "" }, dpt: { value: "" }, topicTrigger: { value: "true" }, topicSave: { value: "" }, dptSave: { value: "" }, topicSaveTrigger: { value: "true" }, property: { value: "payload", required: true, validate: RED.validators.typedInput("propertyType") }, propertyType: { value: "msg" }, rules: { value: [{ t: "eq", v: "", vt: "str" }] } }, inputs: 1, outputs: 1, icon: "node-scene-icon.svg", label: function () { return (this.outputRBE == true ? "|rbe| " : "") + (this.name || this.topic || "KNX Scene Controller") + (this.inputRBE == true ? " |rbe|" : "") }, paletteLabel: "KNX Scene Controller", // button: { // enabled: function() { // // return whether or not the button is enabled, based on the current // // configuration of the node // return !this.changed // }, // visible: function() { // // return whether or not the button is visible, based on the current // // configuration of the node // return this.hasButton // }, // //toggle: "buttonState", // onclick: function() {} // }, oneditprepare: function () { // Go to the help panel try { RED.sidebar.show("help"); } catch (error) { } var node = this; var oNodeServer = RED.nodes.node($("#node-input-server").val()); // Store the config-node // 19/02/2020 Used to get the server sooner als deploy. $("#node-input-server").change(function () { try { oNodeServer = RED.nodes.node($(this).val()); } catch (error) { } }); // DPT of Scene Recall // ######################## $.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => { data.forEach(dpt => { $("#node-input-dpt").append($("<option></option>") .attr("value", dpt.value) .text(dpt.text)) }); $("#node-input-dpt").val(this.dpt) }) // Autocomplete suggestion with ETS csv File $("#node-input-topic").autocomplete({ minLength: 0, source: function (request, response) { //$.getJSON("csv", request, function( data, status, xhr ) { $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => { response($.map(data, function (value, key) { var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt); if (htmlUtilsfullCSVSearch(sSearch, request.term)) { return { label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display value: value.ga // Value } } else { return null; } })); }); }, select: function (event, ui) { // Sets Datapoint and device name automatically var sDevName = ui.item.label.split("#")[1].trim(); try { sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim(); } catch (error) { } $('#node-input-name').val("Recall: " + sDevName + "/" + $('#node-input-name').val().split("/")[1]); var optVal = $("#node-input-dpt option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value'); var $dptSelect = $("#node-input-dpt"); if (optVal !== undefined && optVal !== null) { $dptSelect.val(optVal).trigger('change'); } else { $dptSelect.trigger('change'); } } }).focus(function () { $(this).autocomplete('search', $(this).val() + 'exactmatch'); }); try { if (oNodeServer && oNodeServer.id) KNX_enableSecureFormatting($("#node-input-topicSave"), oNodeServer.id); } catch (e) {} try { if (oNodeServer && oNodeServer.id) KNX_enableSecureFormatting($("#node-input-topic"), oNodeServer.id); } catch (e) {} // 19/03/2020 Adjust trigger value accordingly $("#node-input-dpt").on("change", function () { if ($(this).val() !== null) { if ($(this).val().indexOf("3.007") > -1) { // It's a DIM. Suggest the right value $("#node-input-topicTrigger").val("{decr_incr:1, data:5}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT3007"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } else if ($(this).val().indexOf("18.001") > -1) { // It's a scene actuator. Suggest the right value $("#node-input-topicTrigger").val("{save_recall:0, scenenumber:2}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT18001"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } else if ($(this).val().indexOf("232.600") > -1) { // It's a scene actuator. Suggest the right value $("#node-input-topicTrigger").val("{red:0, green:0, blue:0}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT232600"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } else if ($(this).val().indexOf("251.600") > -1) { // It's a scene actuator. Suggest the right value $("#node-input-topicTrigger").val("{red:0, green:0, blue:0, white:0, mR:1, mG:1, mB:1, mW:1}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT251600"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } } }); // ######################## // DPT of Scene Save // ######################## $.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => { data.forEach(dpt => { $("#node-input-dptSave").append($("<option></option>") .attr("value", dpt.value) .text(dpt.text)) }); $("#node-input-dptSave").val(this.dptSave) }) // Autocomplete suggestion with ETS csv File $("#node-input-topicSave").autocomplete({ minLength: 0, source: function (request, response) { $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => { response($.map(data, function (value, key) { var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt); if (htmlUtilsfullCSVSearch(sSearch, request.term)) { return { label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display value: value.ga // Value } } else { return null; } })); }); }, select: function (event, ui) { // Sets Datapoint and device name automatically var sDevName = ui.item.label.split("#")[1].trim(); try { sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim(); } catch (error) { } $('#node-input-name').val($('#node-input-name').val().split("/")[0] + "/Save: " + sDevName); var optVal = $("#node-input-dptSave option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value'); var $dptSelect = $("#node-input-dptSave"); if (optVal !== undefined && optVal !== null) { $dptSelect.val(optVal).trigger('change'); } else { $dptSelect.trigger('change'); } } }).focus(function () { $(this).autocomplete('search', $(this).val() + 'exactmatch'); }); // 02/04/2020 Adjust trigger value accordingly $("#node-input-dptSave").on("change", function () { if ($(this).val() !== null) { if ($(this).val().indexOf("3.007") > -1) { // It's a DIM. Suggest the right value $("#node-input-topicSaveTrigger").val("{decr_incr:1, data:5}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT3007"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } else if ($(this).val().indexOf("18.001") > -1) { // It's a scene actuator. Suggest the right value $("#node-input-topicSaveTrigger").val("{save_recall:1, scenenumber:2}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT18001"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } else if ($(this).val().indexOf("232.600") > -1) { // It's a scene actuator. Suggest the right value $("#node-input-topicSaveTrigger").val("{red:0, green:0, blue:0}"); var myNotification = RED.notify(node._("knxUltimateSceneController.advanced.notify-DPT232600"), { modal: true, fixed: true, type: 'info', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) } } }); // ######################## // Scene configuration var previousValueType = { value: "prev", label: this._("switch.previous"), hasValue: false }; function resizeRule(rule) { } $("#node-input-rule-container").css('min-height', '150px').css('min-width', '450px').editableList({ addItem: function (container, i, opt) { // row, index, data // opt.r is: { topic: rowRuleTopic, devicename: rowRuleDeviceName, dpt:rowRuleDPT, send: rowRuleSend} if (!opt.hasOwnProperty('r')) { opt.r = {}; // Delete saved scene // ########################################################## $.getJSON("knxultimatescenecontrollerdelete?FileName=" + node.id + "&serverId=" + $("node-input-server").val(), new Date().getTime(), (data) => { }); // ########################################################## } var rule = opt.r; if (!opt.hasOwnProperty('i')) { opt._i = Math.floor((0x99999 - 0x10000) * Math.random()).toString(); } container.css({ overflow: 'hidden', whiteSpace: 'nowrap' }); var row = $('<div class="form-row"/>').appendTo(container); var row2 = $('<div class="form-row"/>', { style: "padding-top: 5px; padding-left: 5px;" }).appendTo(container); var oTopicField = $("<input/>", { class: "rowRuleTopic", type: "text", placeholder: "1/1/1, wait or device name", style: "width:140px; margin-left: 5px; text-align: left;" }).appendTo(row); var oDPTField = $('<select/>', { class: "rowRuleDPT", type: "text", style: "width:160px; margin-left: 5px; text-align: left;" }).appendTo(row); var finalspan = $('<span/>', { style: "" }).appendTo(row); finalspan.append(' &#8594; <span class="node-input-rule-index"></span> '); var oSendField = $('<input/>', { class: "rowRuleSend", type: "text", placeholder: "Value", style: "width:150px; margin-left: 5px; text-align: left;" }).appendTo(row); var orowRuleDeviceName = $('<input/>', { class: "rowRuleDeviceName", type: "text", style: "width:95%; margin-left: 0px; text-align: left;font-style: italic;", placeholder: "Device Name" }).appendTo(row2); oTopicField.on("change", function () { resizeRule(container); }); $.getJSON('knxUltimateDpts?serverId=' + $("#node-input-server").val(), (data) => { data.forEach(dpt => { oDPTField.append($("<option></option>") .attr("value", dpt.value) .text(dpt.text)) }); oDPTField.val(rule.dpt); }) // Autocomplete suggestion with ETS csv File oTopicField.autocomplete({ minLength: 0, source: function (request, response) { $.getJSON("knxUltimatecsv?nodeID=" + oNodeServer.id, (data) => { response($.map(data, function (value, key) { var sSearch = (value.ga + " (" + value.devicename + ") DPT" + value.dpt); if (htmlUtilsfullCSVSearch(sSearch, request.term)) { return { label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display value: value.ga // Value } } else { return null; } })); }); }, select: function (event, ui) { // Sets Datapoint and device name automatically var sDevName = ui.item.label.split("#")[1].trim(); try { sDevName = sDevName.substr(sDevName.indexOf(")") + 1).trim(); orowRuleDeviceName.val(sDevName); } catch (error) { } var optVal = $(".rowRuleDPT option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr('value'); var $dptSelect = oDPTField; if (optVal !== undefined && optVal !== null) { $dptSelect.val(optVal).trigger('change'); } else { $dptSelect.trigger('change'); } } }); oTopicField.on('focus.knxUltimateSceneController click.knxUltimateSceneController', function () { try { $(this).autocomplete('search', ''); } catch (error) { /* empty */ } }); try { if (oNodeServer && oNodeServer.id) KNX_enableSecureFormatting(oTopicField, oNodeServer.id); } catch (e) {} oTopicField.val(rule.topic); oSendField.val(rule.send); orowRuleDeviceName.val(rule.devicename); oTopicField.change(); }, removeItem: function (opt) { // Delete saved scene // ########################################################## $.getJSON("knxultimatescenecontrollerdelete?FileName=" + node.id, new Date().getTime(), (data) => { }); // ########################################################## }, resizeItem: resizeRule, sortItems: function (rules) { }, sortable: true, removable: true }); // Put some spaces after the container $('<br/><br/><br/>').insertAfter($("#node-input-rule-container")); // 10/03/2020 For each rule, create a row for (var i = 0; i < this.rules.length; i++) { var rule = this.rules[i]; $("#node-input-rule-container").editableList('addItem', { r: rule, i: i }); } }, oneditsave: function () { // Return to the info tab try { RED.sidebar.show("info"); } catch (error) { } var node = this; var rules = $("#node-input-rule-container").editableList('items'); node.rules = []; rules.each(function (i) { var rule = $(this); var rowRuleTopic = rule.find(".rowRuleTopic").val(); var rowRuleDPT = rule.find(".rowRuleDPT").val(); var rowRuleSend = rule.find(".rowRuleSend").val(); var rowRuleDeviceName = rule.find(".rowRuleDeviceName").val(); node.rules.push({ topic: rowRuleTopic, devicename: rowRuleDeviceName, dpt: rowRuleDPT, send: rowRuleSend }); }); this.propertyType = $("#node-input-property").typedInput('type'); }, oneditresize: function (size) { var node = this; var rows = $("#dialog-form>div:not(.node-input-rule-container-row)"); var height = size.height; for (var i = 0; i < rows.length; i++) { height -= $(rows[i]).outerHeight(true); } var editorRow = $("#dialog-form>div.node-input-rule-container-row"); height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom"))); height += 16; $("#node-input-rule-container").editableList('height', height); } }) </script> <script type="text/html" data-template-name="knxUltimateSceneController"> <div class="form-row"> <b><span data-i18n="knxUltimateSceneController.title"></span></b> <br/><br/> <label for="node-input-server"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKnRFWHRDcmVhdGlvbiBUaW1lAEZyIDYgQXVnIDIwMTAgMjE6NTI6MTkgKzAxMDD84aS8AAAAB3RJTUUH3gYYCicNV+4WIQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAACUSURBVHjaY2CgFZg5c+Z/ZEyWAZ8+f/6/ZsWs/xoamqMGkGrA6Wla/1+fVARjEBuGsSoGmY4eZSCNL59d/g8DIDbIAHR14OgFGQByKjIGKX5+6/T///8gGMQGiV1+/B0Fg70GIkD+RMYgxf/O5/7//2MSmAZhkBi6OrgB6Bg5DGB4ajr3f2xqsYYLSDE2THJUDg0AAAqyDVd4tp4YAAAAAElFTkSuQmCC"></img> <span data-i18n="knxUltimateSceneController.properties.node-input-server"></span> </label> <input type="text" id="node-input-server"> </div> <div id="GAandDPT"> <div class="form-row"> <label for="node-input-topic" style="width:100px;"><i class="fa fa-play"></i> <span data-i18n="knxUltimateSceneController.properties.node-input-topic"></span></label> <input type="text" id="node-input-topic" placeholder="Ex: 1/1/1" style="width:80px;margin-left: 5px; text-align: left;"> <label for="node-input-dpt" style="width:90px; margin-left: 0px; text-align: right;"><img src="https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/config/device.png"> <span data-i18n="knxUltimateSceneController.properties.node-input-dpt"></span> </label> <select id="node-input-dpt" style="width:140px;"></select> <label for="node-input-topicTrigger" style="width:60px;text-align: right;"><i class="fa fa-bolt"></i> <span data-i18n="knxUltimateSceneController.properties.node-input-topicTrigger"></span></label> <input type="text" id="node-input-topicTrigger" placeholder="Ex: 5 or true" style="width:100px;margin-left: 5px; text-align: left;"> </div> <div class="form-row"> <label for="node-input-topicSave" style="width:100px;"><i class="fa fa-floppy-o"></i> <span data-i18n="knxUltimateSceneController.properties.node-input-topicSave"></span></label> <input type="text" id="node-input-topicSave" placeholder="Ex: 1/1/2" style="width:80px;margin-left: 5px; text-align: left;"> <label for="node-input-dptSave" style="width:90px; margin-left: 0px; text-align: right;"><img src="https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/config/device.png"> <span data-i18n="knxUltimateSceneController.properties.node-input-dpt"></span> </label> <select id="node-input-dptSave" style="width:140px;"></select> <label for="node-input-topicSaveTrigger" style="width:60px;text-align: right;"><i class="fa fa-bolt"></i> <span data-i18n="knxUltimateSceneController.properties.node-input-topicTrigger"></span></label> <input type="text" id="node-input-topicSaveTrigger" data-i18n="[placeholder]knxUltimateSceneController.placeholder.valueexample" style="width:100px;margin-left: 5px; text-align: left;"> </div> </div> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="knxUltimateSceneController.properties.node-input-name"></span> </label> <input type="text" id="node-input-name" data-i18n="[placeholder]knxUltimateSceneController.properties.node-input-name" style="flex:1 1 240px; min-width:240px; max-width:240px;"> </div> <div class="form-row" id="divTopic"> <label for="node-input-outputtopic"><i class="fa fa-tasks"></i> <span data-i18n="knxUltimateSceneController.properties.node-input-outputtopic"></span> </label> <input type="text" id="node-input-outputtopic" data-i18n="[placeholder]knxUltimateSceneController.placeholder.leaveempty"> </div> <dt><i class="fa fa-code-fork"></i>&nbsp; <span data-i18n="knxUltimateSceneController.other.sceneConfig"></dt> <div class="form-row node-input-rule-container-row"> <ol id="node-input-rule-container"></ol> </div> <div class="form-row"> <p><span data-i18n="knxUltimateSceneController.other.add"></p> </div> </script>