UNPKG

node-red-contrib-knx-ultimate

Version:

Control your KNX 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.

1,023 lines (903 loc) 87.8 kB
'use strict' <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script> <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script> <script type="text/javascript"> (function () { RED.nodes.registerType("knxUltimateHueLight", { category: "KNX Ultimate", color: "#C0C7E9", defaults: { //buttonState: {value: true}, server: { type: "knxUltimate-config", required: false }, serverHue: { type: "hue-config", required: true }, name: { value: "" }, nameLightSwitch: { value: "" }, GALightSwitch: { value: "" }, dptLightSwitch: { value: "" }, nameLightState: { value: "" }, GALightState: { value: "" }, dptLightState: { value: "" }, nameLightDIM: { value: "" }, GALightDIM: { value: "" }, dptLightDIM: { value: "" }, // TAB Color --------------------------- nameLightColor: { value: "" }, GALightColor: { value: "" }, dptLightColor: { value: "" }, nameLightColorState: { value: "" }, GALightColorState: { value: "" }, dptLightColorState: { value: "" }, // HSV H hue Color change nameLightHSV_H_DIM: { value: "" }, GALightHSV_H_DIM: { value: "" }, dptLightHSV_H_DIM: { value: "" }, nameLightHSV_H_State: { value: "" }, GALightHSV_H_State: { value: "" }, dptLightHSV_H_State: { value: "" }, // HSV S saturation change nameLightHSV_S_DIM: { value: "" }, GALightHSV_S_DIM: { value: "" }, dptLightHSV_S_DIM: { value: "" }, nameLightHSV_S_State: { value: "" }, GALightHSV_S_State: { value: "" }, dptLightHSV_S_State: { value: "" }, // ------------------------------------- nameLightKelvinDIM: { value: "" }, GALightKelvinDIM: { value: "" }, dptLightKelvinDIM: { value: "" }, nameLightKelvinPercentage: { value: "" }, GALightKelvinPercentage: { value: "" }, dptLightKelvinPercentage: { value: "" }, nameLightKelvinPercentageState: { value: "" }, GALightKelvinPercentageState: { value: "" }, dptLightKelvinPercentageState: { value: "" }, nameLightKelvin: { value: "" }, GALightKelvin: { value: "" }, dptLightKelvin: { value: "" }, nameLightKelvinState: { value: "" }, GALightKelvinState: { value: "" }, dptLightKelvinState: { value: "" }, nameLightBrightness: { value: "" }, GALightBrightness: { value: "" }, dptLightBrightness: { value: "" }, nameLightBrightnessState: { value: "" }, GALightBrightnessState: { value: "" }, dptLightBrightnessState: { value: "" }, nameLightBlink: { value: "" }, GALightBlink: { value: "" }, dptLightBlink: { value: "" }, nameLightColorCycle: { value: "" }, GALightColorCycle: { value: "" }, dptLightColorCycle: { value: "" }, nameDaylightSensor: { value: "" }, GADaylightSensor: { value: "" }, dptDaylightSensor: { value: "" }, specifySwitchOnBrightness: { value: "temperature" }, colorAtSwitchOnDayTime: { value: '{"kelvin":3000, "brightness":100 }' }, enableDayNightLighting: { value: "no" }, colorAtSwitchOnNightTime: { value: '{ "kelvin":2700, "brightness":20 }' }, invertDayNight: { value: false }, invertDimTunableWhiteDirection: { value: false }, updateKNXBrightnessStatusOnHUEOnOff: { value: "no" }, dimSpeed: { value: 5000, required: false }, HSVDimSpeed: { value: 5000, required: false }, minDimLevelLight: { value: 10, required: false }, maxDimLevelLight: { value: 100, required: false }, readStatusAtStartup: { value: "yes" }, enableNodePINS: { value: "no" }, outputs: { value: 0 }, inputs: { value: 0 }, hueDevice: { value: "" }, hueDeviceObject: { value: {} }, restoreDayMode: { value: "no" } }, inputs: 0, outputs: 0, icon: "node-hue-icon.svg", label: function () { return this.name || "Hue Light/Outlet"; }, paletteLabel: "Hue Light/Outlet", oneditprepare: function () { // Go to the help panel try { RED.sidebar.show("help"); } catch (error) { } var node = this; if ($("#node-input-server").val() === "_ADD_") { // Node-Red 4.0.x has a bug not selecting the default server node try { $("#node-input-server").prop("selectedIndex", 0); } catch (error) { } } if ($("#node-input-serverHue").val() === "_ADD_") { // Node-Red 4.0.x has a bug not selecting the default server node try { $("#node-input-serverHue").prop("selectedIndex", 0); } catch (error) { } } function onEditPrepare() { // TIMER BLINK #################################################### let blinkStatus = 2; let timerBlinkBackground; function blinkBackground(_elementIDwithHashAtTheBeginning) { if (timerBlinkBackground !== undefined) clearInterval(timerBlinkBackground); timerBlinkBackground = setInterval(() => { if (isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", "lightgreen"); if (!isEven(blinkStatus)) $(_elementIDwithHashAtTheBeginning).css("background-color", ""); blinkStatus += 1; if (blinkStatus >= 14) { clearInterval(timerBlinkBackground); blinkStatus = 2; $(_elementIDwithHashAtTheBeginning).css("background-color", ""); } }, 100); } function isEven(n) { return (n % 2 == 0); } // ################################################################ $("#tabs").tabs(); // Tabs gestione KNX function getDPT(_dpt, _destinationWidget) { // DPT Switch command // ######################## $(_destinationWidget).empty(); $.getJSON("knxUltimateDpts?serverId=" + $("#node-input-server").val() + "&" + { _: new Date().getTime() }, (data) => { data.forEach((dpt) => { if (dpt.value.startsWith(_dpt)) { // Adjustment for HUE Temperature if (dpt.value.startsWith("7.600")) { $(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - KNX Kelvin range 2000-6535k (Homeassistant color_temperature_mode: absolute)")); } else if (dpt.value.startsWith("9.002")) { $(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - HUE Kelvin range 2000-6535k (Homeassistant color_temperature_mode: absolute_float)")); } else if (dpt.value.startsWith("5.001")) { $(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text + " - Homeassistant color_temperature_mode: relative")); } else { $(_destinationWidget).append($("<option></option>").attr("value", dpt.value).text(dpt.text)); } } }); // Eval const format = "node." + _destinationWidget.replace("#node-input-", ""); try { if (format !== undefined) $(_destinationWidget).val(eval(format).toString()); } catch (error) { } }); } function getGroupAddress(_sourceWidgetAutocomplete, _destinationWidgetName, _destinationWidgetDPT, _additionalSearchTerm) { $(_sourceWidgetAutocomplete).autocomplete({ minLength: 0, source: function (request, response) { //$.getJSON("csv", request, function( data, status, xhr ) { $.getJSON("knxUltimatecsv?nodeID=" + $("#node-input-server").val() + "&" + { _: new Date().getTime() }, (data) => { response( $.map(data, function (value, key) { var sSearch = value.ga + " (" + value.devicename + ") DPT" + value.dpt; for (let index = 0; index < _additionalSearchTerm.length; index++) { const sDPT = _additionalSearchTerm[index]; if (htmlUtilsfullCSVSearch(sSearch, request.term + " " + sDPT)) { return { label: value.ga + " # " + value.devicename + " # " + value.dpt, // Label for Display value: value.ga, // Value }; } }; }) ); }); }, 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) { } $(_destinationWidgetName).val(sDevName); var optVal = $(_destinationWidgetDPT + " option:contains('" + ui.item.label.split("#")[2].trim() + "')").attr("value"); // Select the option value $(_destinationWidgetDPT).val(optVal); }, }).focus(function () { $(this).autocomplete('search', $(this).val() + 'exactmatch'); }); } getDPT("1.", "#node-input-dptLightSwitch"); getGroupAddress("#node-input-GALightSwitch", "#node-input-nameLightSwitch", "#node-input-dptLightSwitch", ["1."]); getDPT("1.", "#node-input-dptLightState"); getGroupAddress("#node-input-GALightState", "#node-input-nameLightState", "#node-input-dptLightState", ["1."]); getDPT("3.007", "#node-input-dptLightDIM"); getGroupAddress("#node-input-GALightDIM", "#node-input-nameLightDIM", "#node-input-dptLightDIM", ["3.007"]); getDPT("5.001", "#node-input-dptLightBrightness"); getGroupAddress("#node-input-GALightBrightness", "#node-input-nameLightBrightness", "#node-input-dptLightBrightness", ["5.001"]); getDPT("5.001", "#node-input-dptLightBrightnessState"); getGroupAddress("#node-input-GALightBrightnessState", "#node-input-nameLightBrightnessState", "#node-input-dptLightBrightnessState", ["5.001"]); getDPT("232.600", "#node-input-dptLightColor"); getGroupAddress("#node-input-GALightColor", "#node-input-nameLightColor", "#node-input-dptLightColor", ["232.600"]); getDPT("232.600", "#node-input-dptLightColorState"); getGroupAddress("#node-input-GALightColorState", "#node-input-nameLightColorState", "#node-input-dptLightColorState", ["232.600"]); getDPT("3.007", "#node-input-dptLightKelvinDIM"); getGroupAddress("#node-input-GALightKelvinDIM", "#node-input-nameLightKelvinDIM", "#node-input-dptLightKelvinDIM", ["3.007"]); getDPT("5.001", "#node-input-dptLightKelvinPercentage"); getGroupAddress("#node-input-GALightKelvinPercentage", "#node-input-nameLightKelvinPercentage", "#node-input-dptLightKelvinPercentage", ["5.001"]); getDPT("5.001", "#node-input-dptLightKelvinPercentageState"); getGroupAddress("#node-input-GALightKelvinPercentageState", "#node-input-nameLightKelvinPercentageState", "#node-input-dptLightKelvinPercentageState", ["5.001"]); getDPT("1.", "#node-input-dptLightBlink"); getGroupAddress("#node-input-GALightBlink", "#node-input-nameLightBlink", "#node-input-dptLightBlink", ["1."]); getDPT("1.", "#node-input-dptLightColorCycle"); getGroupAddress("#node-input-GALightColorCycle", "#node-input-nameLightColorCycle", "#node-input-dptLightColorCycle", ["1."]); getDPT("1.", "#node-input-dptDaylightSensor"); getGroupAddress("#node-input-GADaylightSensor", "#node-input-nameDaylightSensor", "#node-input-dptDaylightSensor", ["1."]); getDPT("7.600", "#node-input-dptLightKelvin"); getDPT("9.002", "#node-input-dptLightKelvin"); getDPT("9.002", "#node-input-dptLightKelvinState"); getDPT("7.600", "#node-input-dptLightKelvinState"); getGroupAddress("#node-input-GALightKelvin", "#node-input-nameLightKelvin", "#node-input-dptLightKelvin", ["7.600", "9.002"]); getGroupAddress("#node-input-GALightKelvinState", "#node-input-nameLightKelvinState", "#node-input-dptLightKelvinState", ["7.600", "9.002"]); // HSV ---------------------- // H getDPT("3.007", "#node-input-dptLightHSV_H_DIM"); getGroupAddress("#node-input-GALightHSV_H_DIM", "#node-input-nameLightHSV_H_DIM", "#node-input-dptLightHSV_H_DIM", ["3.007"]); getDPT("5.001", "#node-input-dptLightHSV_H_State"); getGroupAddress("#node-input-GALightHSV_H_State", "#node-input-nameLightHSV_H_State", "#node-input-dptLightHSV_H_State", ["5.001"]); // S getDPT("3.007", "#node-input-dptLightHSV_S_DIM"); getGroupAddress("#node-input-GALightHSV_S_DIM", "#node-input-nameLightHSV_S_DIM", "#node-input-dptLightHSV_S_DIM", ["3.007"]); getDPT("5.001", "#node-input-dptLightHSV_S_State"); getGroupAddress("#node-input-GALightHSV_S_State", "#node-input-nameLightHSV_S_State", "#node-input-dptLightHSV_S_State", ["5.001"]); // V getDPT("3.007", "#node-input-dptLightHSV_V_DIM"); getGroupAddress("#node-input-GALightHSV_V_DIM", "#node-input-nameLightHSV_V_DIM", "#node-input-dptLightHSV_V_DIM", ["3.007"]); getDPT("5.001", "#node-input-dptLightHSV_V_State"); getGroupAddress("#node-input-GALightHSV_V_State", "#node-input-nameLightHSV_V_State", "#node-input-dptLightHSV_V_State", ["5.001"]); // END HSV ---------------------- // Get the HUE capabilities to enable/disable UI parts var getJsonPromise; if ($("#node-input-hueDevice").val() === '') { $("#tabs").hide(); return; } else { $("#tabs").show(); if (getJsonPromise !== undefined) getJsonPromise.abort(); getJsonPromise = $.getJSON("knxUltimateGetLightObject?id=" + $("#node-input-hueDevice").val().split("#")[0] + "&serverId=" + $("#node-input-serverHue").val() + "&" + { _: new Date().getTime() }, (data) => { let oLight = data; // Check if grouped, to hide/show the "Get current" buttons if (oLight.type === "grouped_light") { $("#tabs").tabs("enable", "#tabs-4"); $("#tabs").tabs("enable", "#tabs-3"); $("#tabs").tabs("enable", "#tabs-2"); $("#getColorAtSwitchOnDayTimeButton").show(); $("#getColorAtSwitchOnNightTimeButton").show(); $("#node-input-specifySwitchOnBrightness").empty().append( $("<option>") .val("no") .text("None") ).append( $("<option>") .val("yes") .text("Select color") ).append( $("<option>") .val("temperature") .text("Select temperature and brightness") ); $("#node-input-enableDayNightLighting").empty().append( $("<option>") .val("no") .text("No") ).append( $("<option>") .val("yes") .text("Select color") ).append( $("<option>") .val("temperature") .text("Select temperature and brightness") ); $("#node-input-specifySwitchOnBrightness").val(node.specifySwitchOnBrightness).trigger('change'); $("#node-input-enableDayNightLighting").val(node.enableDayNightLighting).trigger('change'); return; } else { $("#getColorAtSwitchOnDayTimeButton").show(); $("#getColorAtSwitchOnNightTimeButton").show(); $("#node-input-specifySwitchOnBrightness").empty().append( $("<option>") .val("no") .text("None") ); $("#node-input-enableDayNightLighting").empty().append( $("<option>") .val("no") .text("No") ); } $("#tabs").tabs("disable", "#tabs-4"); $("#tabs").tabs("disable", "#tabs-3"); $("#tabs").tabs("disable", "#tabs-2"); $("#divColorsAtSwitchOn").hide(); $("#divColorsAtSwitchOnNightTime").hide(); $("#divTemperatureAtSwitchOn").hide(); $("#divTemperatureAtSwitchOnNightTime").hide(); $("#divColorCycle").hide(); $("#divUpdateKNXBrightnessStatusOnHUEOnOff").hide(); $("#divBehaviourBrightness").hide(); $("#comboTemperatureAtSwitchOn").hide(); $("#comboTemperatureAtSwitchOnNightTime").hide(); // Enable options/tabs one by one if (oLight.dimming !== undefined) { $("#tabs").tabs("enable", "#tabs-2"); $("#divBehaviourBrightness").show(); } if (oLight.color !== undefined) { $("#tabs").tabs("enable", "#tabs-4"); $("#divColorsAtSwitchOn").show(); $("#divColorsAtSwitchOnNightTime").show(); $("#divColorCycle").show(); $("#node-input-specifySwitchOnBrightness").append( $("<option>") .val("yes") .text("Select color") ); $("#node-input-enableDayNightLighting").append( $("<option>") .val("yes") .text("Select color") ); } // Check temperature (if the light supports temperature, it support dimming as well) if (oLight.color_temperature !== undefined) { $("#tabs").tabs("enable", "#tabs-3"); //$("#tabs").tabs("enable", "#tabs-2"); $("#node-input-specifySwitchOnBrightness").append( $("<option>") .val("temperature") .text("Select temperature and brightness") ); $("#node-input-enableDayNightLighting").append( $("<option>") .val("temperature") .text("Select temperature and brightness") ); $("#divTemperatureAtSwitchOn").show(); $("#divTemperatureAtSwitchOnNightTime").show(); $("#divUpdateKNXBrightnessStatusOnHUEOnOff").show(); $("#divBehaviourBrightness").show(); $("#comboTemperatureAtSwitchOn").show(); $("#comboTemperatureAtSwitchOnNightTime").show(); } else { //$("#tabs").tabs("enable", "#tabs-2"); $("#node-input-specifySwitchOnBrightness").append( $("<option>") .val("temperature") .text("Select brightness") ); $("#node-input-enableDayNightLighting").append( $("<option>") .val("temperature") .text("Select brightness") ); $("#comboTemperatureAtSwitchOn").val(0); $("#comboTemperatureAtSwitchOnNightTime").val(0); $("#divTemperatureAtSwitchOn").show(); $("#divTemperatureAtSwitchOnNightTime").show(); $("#divUpdateKNXBrightnessStatusOnHUEOnOff").show(); //$("#divBehaviourBrightness").show(); } $("#node-input-specifySwitchOnBrightness").val(node.specifySwitchOnBrightness).trigger('change'); $("#node-input-enableDayNightLighting").val(node.enableDayNightLighting).trigger('change'); }); setTimeout(function () { if (getJsonPromise !== undefined) getJsonPromise.abort(); }, 10000); } // Show/Hide the div of the color at swich on if (node.specifySwitchOnBrightness === "yes") { $("#divColorsAtSwitchOn").show(); $("#divTemperatureAtSwitchOn").hide(); } else if (node.specifySwitchOnBrightness === "temperature") { $("#divColorsAtSwitchOn").hide(); $("#divTemperatureAtSwitchOn").show(); } else { $("#divColorsAtSwitchOn").hide(); $("#divTemperatureAtSwitchOn").hide(); } $("#node-input-specifySwitchOnBrightness").on("change", function () { if ($("#node-input-specifySwitchOnBrightness").val() === "yes") { $("#divColorsAtSwitchOn").show(); $("#divTemperatureAtSwitchOn").hide(); blinkBackground("#colorPickerDay"); } else if ($("#node-input-specifySwitchOnBrightness").val() === "temperature") { $("#divColorsAtSwitchOn").hide(); $("#divTemperatureAtSwitchOn").show(); } else { $("#divColorsAtSwitchOn").hide(); $("#divTemperatureAtSwitchOn").hide(); } }); // Show/Hide and enable/disable day/night Lighting behaviour if (node.enableDayNightLighting === "yes") { $("#divEnableDayNightLighting").show(); $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page $("#divColorsAtSwitchOnNightTime").show(); $("#divTemperatureAtSwitchOnNightTime").hide(); } else if (node.enableDayNightLighting === "temperature") { $("#divEnableDayNightLighting").show(); $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page $("#divColorsAtSwitchOnNightTime").hide(); $("#divTemperatureAtSwitchOnNightTime").show(); } else { $("#divEnableDayNightLighting").hide(); $("#divCCSBoxAtNightLighting").css({ border: "", "border-radius": "", padding: "" }); } $("#node-input-enableDayNightLighting").on("change", function () { if ($("#node-input-enableDayNightLighting").val() === "yes") { $("#divEnableDayNightLighting").show(); $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page $("#divColorsAtSwitchOnNightTime").show(); $("#divTemperatureAtSwitchOnNightTime").hide(); blinkBackground("#colorPickerNight") $("#getColorAtSwitchOnDayTimeButton").text("Get current"); } else if ($("#node-input-enableDayNightLighting").val() === "temperature") { $("#divEnableDayNightLighting").show(); $("#divCCSBoxAtNightLighting").css({ border: "1px solid dimgrey", "border-radius": "12px", padding: "5px" }); // Add little box to better understand the property page $("#divColorsAtSwitchOnNightTime").hide(); $("#divTemperatureAtSwitchOnNightTime").show(); } else { $("#divEnableDayNightLighting").hide(); $("#divCCSBoxAtNightLighting").css({ border: "", "border-radius": "", padding: "" }); } }); $("#getColorAtSwitchOnDayTimeButton").on("click", function () { $("#getColorAtSwitchOnDayTimeButton").text("Wait..."); let jRet; let sQuery; if ($("#node-input-specifySwitchOnBrightness").val() === "yes") sQuery = "knxUltimateGetHueColor"; if ($("#node-input-specifySwitchOnBrightness").val() === "temperature") sQuery = "knxUltimateGetKelvinColor"; $.getJSON(sQuery + "?id=" + $("#node-input-hueDevice").val().split("#")[0] + "&serverId=" + $("#node-input-serverHue").val() + "&" + { _: new Date().getTime() }, (data) => { $("#node-input-colorAtSwitchOnDayTime").val(data); $("#colorPickerDay").val(data); blinkBackground("#colorPickerDay") $("#getColorAtSwitchOnDayTimeButton").text("Get again"); }); }); $("#getColorAtSwitchOnNightTimeButton").on("click", function () { $("#getColorAtSwitchOnNightTimeButton").text("Wait..."); let jRet; let sQuery; if ($("#node-input-enableDayNightLighting").val() === "yes") sQuery = "knxUltimateGetHueColor"; if ($("#node-input-enableDayNightLighting").val() === "temperature") sQuery = "knxUltimateGetKelvinColor"; $.getJSON(sQuery + "?id=" + $("#node-input-hueDevice").val().split("#")[0] + "&" + { _: new Date().getTime() }, (data) => { $("#node-input-colorAtSwitchOnNightTime").val(data); $("#colorPickerNight").val(data); blinkBackground("#colorPickerNight") $("#getColorAtSwitchOnNightTimeButton").text("Get again"); }); }); // Fill options for minDimLevel and maxDimLevel and comboBrightnessAtSwitchOn (for color brightness at switch on, with temperature toghedher) for (let index = 100; index >= 0; index -= 5) { if (index === 0) { $("#node-input-minDimLevelLight").append($("<option>").val(index).text(index.toString() + "% (Switch Off)")); $("#comboBrightnessAtSwitchOn").append($("<option>").val(index).text(index.toString() + "% (Switch Off)")); $("#comboBrightnessAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "% (Switch Off)")); } else { $("#node-input-minDimLevelLight").append($("<option>").val(index).text(index.toString() + "%")); $("#comboBrightnessAtSwitchOn").append($("<option>").val(index).text(index.toString() + "%")); $("#comboBrightnessAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "%")); } } // Temperatures, from 2000 to 6535K (circa) for (let index = 2000; index <= 6500; index += 100) { if (index === 2200) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (start of philips white ambiance lights range)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (start of philips white ambiance lights range)")); } else if (index === 2700) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (warm white, intimate, cozy, personal, for living rooms)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (warm white, intimate, cozy, personal, for living rooms)")); } else if (index === 3000) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (soft white, warm, calming, for bathrooms and kitchens)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (soft white, warm, calming, for bathrooms and kitchens)")); } else if (index === 3500) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (neutral white, balanced, friendly, inviting, for office spaces and retail)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (not recommended for night time - neutral white, for office spaces and retail)")); } else if (index === 4100) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (cool white, precise, clean, focused, for garages and grocery stores)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (not recommended for night time - cool white, precise, clean, focused, for garages and grocery stores)")); } else if (index === 5000) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (bright white, vibrant, crisp, for warehouses, sports stadiums and healthcare)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (not recommended for night time - bright white, vibrant, crisp, for warehouses, sports stadiums and healthcare)")); } else if (index === 6500) { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K (daylight, alert, energetic, for indoor agriculture)")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K (not recommended for night time - daylight, alert, energetic, for indoor agriculture)")); } else { $("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + "K")); $("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "K")); } } // Calculate kelvin/color let json; node.colorAtSwitchOnDayTime = node.colorAtSwitchOnDayTime.replace("geen", "green"); // Old bug in "geen" property node.colorAtSwitchOnNightTime = node.colorAtSwitchOnNightTime.replace("geen", "green"); // Old bug in "geen" property try { json = JSON.parse(node.colorAtSwitchOnDayTime); } catch (error) { console.log("json = JSON.parse(node.colorAtSwitchOnDayTime) in HTML: " + error.message) } if (json !== undefined && json.kelvin !== undefined) { // Kelvin $("#comboTemperatureAtSwitchOn").val(json.kelvin); $("#comboBrightnessAtSwitchOn").val(json.brightness); if (node.specifySwitchOnBrightness !== 'no') $("#node-input-specifySwitchOnBrightness").val('temperature'); // Adjust in case of mismatch (from old geen bug) } else if (json !== undefined && json.red !== undefined) { // Must transform RGB into HTML HEX color try { $("#node-input-colorAtSwitchOnDayTime").val("#" + rgbHex(json.red, json.green, json.blue)); } catch (error) { } $("#colorPickerDay").val($("#node-input-colorAtSwitchOnDayTime").val()); if (node.specifySwitchOnBrightness !== 'no') $("#node-input-specifySwitchOnBrightness").val('yes'); // Adjust in case of mismatch (from old geen bug) } else { // It's already an HEX color $("#colorPickerDay").val(node.colorAtSwitchOnDayTime); if (node.specifySwitchOnBrightness !== 'no') $("#node-input-specifySwitchOnBrightness").val('yes'); // Adjust in case of mismatch (from old geen bug) } //Night json = undefined; try { json = JSON.parse(node.colorAtSwitchOnNightTime); } catch (error) { } if (json !== undefined && json.kelvin !== undefined) { // Kelvin $("#comboTemperatureAtSwitchOnNightTime").val(json.kelvin); $("#comboBrightnessAtSwitchOnNightTime").val(json.brightness); if (node.enableDayNightLighting !== 'no') $("#node-input-enableDayNightLighting").val('temperature'); // Adjust in case of mismatch (from old geen bug) } else if (json !== undefined && json.red !== undefined) { // Must transform RGB into HTML HEX color try { $("#node-input-colorAtSwitchOnNightTime").val("#" + rgbHex(json.red, json.green, json.blue)); } catch (error) { } $("#colorPickerNight").val($("#node-input-colorAtSwitchOnNightTime").val()); if (node.enableDayNightLighting !== 'no') $("#node-input-enableDayNightLighting").val('yes'); // Adjust in case of mismatch (from old geen bug) } else { // It's already an HEX color $("#colorPickerNight").val(node.colorAtSwitchOnNightTime); if (node.enableDayNightLighting !== 'no') $("#node-input-enableDayNightLighting").val('yes'); // Adjust in case of mismatch (from old geen bug) } $("#comboTemperatureAtSwitchOn, #comboBrightnessAtSwitchOn").on("change", function () { $("#node-input-colorAtSwitchOnDayTime").val('{ "kelvin":' + $("#comboTemperatureAtSwitchOn").val() + ', "brightness":' + $("#comboBrightnessAtSwitchOn").val() + ' }'); }); $("#comboTemperatureAtSwitchOnNightTime, #comboBrightnessAtSwitchOnNightTime").on("change", function () { $("#node-input-colorAtSwitchOnNightTime").val('{ "kelvin":' + $("#comboTemperatureAtSwitchOnNightTime").val() + ', "brightness":' + $("#comboBrightnessAtSwitchOnNightTime").val() + ' }'); }); // Create and put the JSON to node-input-colorAtSwitchOnDayTime $("#colorPickerDay").on("change", function () { $("#node-input-colorAtSwitchOnDayTime").val(this.value); }); $("#colorPickerNight").on("change", function () { $("#node-input-colorAtSwitchOnNightTime").val(this.value); }); $("#node-input-minDimLevelLight").val(node.minDimLevelLight); for (let index = 100; index >= 10; index--) { $("#node-input-maxDimLevelLight").append( $("<option>") .val(index) .text(index.toString() + "%") ); } $("#node-input-maxDimLevelLight").val(node.maxDimLevelLight); } function Go() { $("#waitWindow").hide(); $("#mainWindow").show(); // $.post("banana", { func: "getNameAndTime" }, function (data) { // //alert(data.body); // John // }, "json"); try { RED.sidebar.show("help"); } catch (error) { } onEditPrepare(); node.yamlEditor = RED.editor.createEditor({ id: 'aceEditor', mode: 'ace/mode/text' }); node.yamlEditor.session.setValue(yamelize()); $('[id*="node-input-"]').on('keyup change autocompletechange', function () { try { node.yamlEditor.session.setValue(yamelize()); } catch (error) { } }); } // 19/02/2020 Used to get the server sooner als deploy. $("#node-input-serverHue").change(function () { try { if ($("#node-input-serverHue").val() !== "_ADD_") { $("#waitWindow").show(); $("#mainWindow").hide(); checkConnection(); } else { Go(); } } catch (error) { } }); // Autocomplete suggestion with HUE Lights $("#node-input-name").autocomplete({ minLength: 0, source: function (request, response) { $.getJSON("KNXUltimateGetResourcesHUE?rtype=light&serverId=" + $("#node-input-serverHue").val() + "&" + { _: new Date().getTime() }, (data) => { response( $.map(data.devices, function (value, key) { var sSearch = value.name; if (!value.name.includes("I'm still connecting")) { if (htmlUtilsfullCSVSearch(sSearch, request.term)) { return { hueDevice: value.id, value: value.name, }; } else { return null; } } else { return { hueDevice: value.id, value: value.name, }; } }) ); }); }, select: function (event, ui) { // Distinguish between group of lights an single light. if (ui.item.value.toLowerCase().startsWith("grouped_light")) { $("#node-input-hueDevice").val(ui.item.hueDevice + "#grouped_light"); } else { $("#node-input-hueDevice").val(ui.item.hueDevice + "#light"); } Go(); } }).focus(function () { $(this).autocomplete('search', $(this).val() + 'exactmatch'); }); // Timer connection to backend #################################################### let timerWaitBackEndCounter = 0; function checkConnection() { if (this.timerWaitBackEnd !== undefined) clearTimeout(this.timerWaitBackEnd); if ($("#node-input-serverHue").val() === undefined) { // Someone has FULL deployed node-red, exit. return; } this.timerWaitBackEnd = setTimeout(() => { timerWaitBackEndCounter++; if (timerWaitBackEndCounter > 20) { timerWaitBackEndCounter = 0; const myNotification = RED.notify("Something went wrong. Please make sure the HUE bridge connection is properly configured. For further info, see the node-red log. IF YOU ARE CREATING A NEW HUE CONFIGURATION NODE, PLEASE DEPLOY AND RETRY.", { modal: false, fixed: true, type: 'error', buttons: [ { text: "OK", click: function (e) { myNotification.close(); } }] }) $("#waitWindow").hide(); $("#mainWindow").show(); $("#tabs").hide(); return; } // Check wether the HUE node has is connected and the devices have already been read. // ########################################################## $.getJSON("knxultimateCheckHueConnected?serverId=" + $("#node-input-serverHue").val() + "&" + { _: new Date().getTime() }, (data) => { if (data.ready === true) { Go(); } else { checkConnection(); } }); // ########################################################## }, 500); } if ($("#node-input-serverHue").val() === "_ADD_") { Go(); } else { checkConnection(); } // ################################################################ }, oneditsave: function () { // Return to the info tab try { RED.sidebar.show("info"); } catch (error) { } //RED.sidebar.removeTab("tabNRColor"); if ($("#node-input-enableNodePINS").val() === "yes") { this.outputs = 1; this.inputs = 1; } else { this.outputs = 0; this.inputs = 0; } }, oneditcancel: function () { // Return to the info tab try { RED.sidebar.show("info"); } catch (error) { } //RED.sidebar.removeTab("tabNRColor"); //RED.sidebar.show("help"); }, oneditresize: function (size) { //var height = size.height; //$('.editor-tray-content').css({ "width": "2700px" }); } }); function rgbHex(red, green, blue, alpha) { const toHex = (red, green, blue, alpha) => ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) + alpha; const parseCssRgbString = (input) => { const parts = input.replace(/rgba?\(([^)]+)\)/, '$1').split(/[,\s/]+/).filter(Boolean); if (parts.length < 3) { return; } const parseValue = (value, max) => { value = value.trim(); if (value.endsWith('%')) { return Math.min(Number.parseFloat(value) * max / 100, max); } return Math.min(Number.parseFloat(value), max); }; const red = parseValue(parts[0], 255); const green = parseValue(parts[1], 255); const blue = parseValue(parts[2], 255); let alpha; if (parts.length === 4) { alpha = parseValue(parts[3], 1); } return [red, green, blue, alpha]; }; let isPercent = (red + (alpha || '')).toString().includes('%'); if (typeof red === 'string' && !green) { // Single string parameter. const parsed = parseCssRgbString(red); if (!parsed) { throw new TypeError('Invalid or unsupported color format.'); } isPercent = false; [red, green, blue, alpha] = parsed; } else if (alpha !== undefined) { alpha = Number.parseFloat(alpha); } if (typeof red !== 'number' || typeof green !== 'number' || typeof blue !== 'number' || red > 255 || green > 255 || blue > 255 ) { throw new TypeError('Expected three numbers below 256'); } if (typeof alpha === 'number') { if (!isPercent && alpha >= 0 && alpha <= 1) { alpha = Math.round(255 * alpha); } else if (isPercent && alpha >= 0 && alpha <= 100) { alpha = Math.round(255 * alpha / 100); } else { throw new TypeError(`Expected alpha value (${alpha}) as a fraction or percentage`); } alpha = (alpha | 1 << 8).toString(16).slice(1); // eslint-disable-line no-mixed-operators } else { alpha = ''; } return toHex(red, green, blue, alpha); } function yamelize() { try { // Get the HUE Node properties and transform it into yaml let sYaml = '- name: "' + $("#node-input-name").val() + '"' + "\n"; sYaml += $("#node-input-GALightSwitch").val() != '' ? ' address: "' + $("#node-input-GALightSwitch").val() + '"' + "\n" : ''; sYaml += $("#node-input-GALightState").val() != '' ? ' state_address: "' + $("#node-input-GALightState").val() + '"' + "\n" : ''; sYaml += $("#node-input-GALightBrightness").val() != '' ? ' brightness_address: "' + $("#node-input-GALightBrightness").val() + '"' + "\n" : ''; sYaml += $("#node-input-GALightBrightnessState").val() != '' ? ' brightness_state_address: "' + $("#node-input-GALightBrightnessState").val() + '"' + "\n" : ''; //#region color_temperature_mode // ---------------------------- if ($("#node-input-GALightKelvinPercentage").val !== '') { // color_temperature_mode: relative sYaml += ' color_temperature_mode: relative' + '\n'; sYaml += ' color_temperature_address: "' + $("#node-input-GALightKelvinPercentage").val() + '"\n'; sYaml += $("#node-input-GALightKelvinPercentageState").val() != '' ? ' color_temperature_state_address: "' + $("#node-input-GALightKelvinPercentageState").val() + '"' + "\n" : ''; sYaml += ' min_kelvin: 2200' + '\n'; sYaml += ' max_kelvin: 6500' + '\n'; } else if ($("#node-input-GALightKelvin").val() !== '') { if ($("#node-input-dptLightKelvin").val() === '7.600') { sYaml += ' color_temperature_mode: absolute' + '\n'; sYaml += ' color_temperature_address: "' + $("#node-input-GALightKelvin").val() + '"\n'; if ($("#node-input-dptLightKelvinState").val() === '7.600') { // Add the status only if also 7.600 sYaml += $("#node-input-GALightKelvinState").val() != '' ? ' color_temperature_state_address: "' + $("#node-input-GALightKelvinState").val() + '"' + "\n" : ''; } sYaml += ' min_kelvin: 2200' + '\n'; sYaml += ' max_kelvin: 6500' + '\n'; } else if ($("#node-input-dptLightKelvin").val() === '9.002') { sYaml += ' color_temperature_mode: absolute_float' + '\n'; sYaml += ' color_temperature_address: "' + $("#node-input-GALightKelvin").val() + '"\n'; if ($("#node-input-dptLightKelvinState").val() === '9.002') { // Add the status only if also 7.600 sYaml += $("#node-input-GALightKelvinState").val() != '' ? ' color_temperature_state_address: "' + $("#node-input-GALightKelvinState").val() + '"' + "\n" : ''; } sYaml += ' min_kelvin: 2200' + '\n'; sYaml += ' max_kelvin: 6500' + '\n'; } } // ---------------------------- //#endregion sYaml += $("#node-input-GALightColor").val() != '' ? ' color_address: "' + $("#node-input-GALightColor").val() + '"' + "\n" : ''; sYaml += $("#node-input-GALightColorState").val() != '' ? ' color_state_address: "' + $("#node-input-GALightColorState").val() + '"' + "\n" : ''; return (sYaml); } catch (error) { } } }()) </script> <script type="text/html" data-template-name="knxUltimateHueLight"> <div id="waitWindow"> <br/><br/><p align="center"><i class="fa-solid fa-hourglass-start fa-spin-pulse fa-4x"></i><br/><br/> Wait, i'm talking to your HUE bridge... </p> </div> <div id="mainWindow" hidden> <div class="form-row"> <b>HUE Light/Group node</b>&nbsp&nbsp<span style="color:red" &nbsp &nbsp<i class="fa fa-youtube"></i></span>&nbsp<a target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u>Youtube sample</u></a> <br /> <br /> <p align="center"> <i class="fa-regular fa-lightbulb fa-bounce fa-4x"></i> </p> <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> KNX GW </label> <input type="text" id="node-input-server" /> </div> <div class="form-row"> <label for="node-input-serverHue"> <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAABFUlEQVQ4EZWSsWoCQRCG1yiENEFEi6QSkjqWWoqFoBYJ+Br6JHkMn8Iibd4ihQpaJIhWNkry/ZtdGZY78Qa+m39nZ+dm9s4550awglNBluS/gVtAX6KgDclf68w2OThgfR9iT/jnoEv4TtByDThWTCDKW4SSZTf/zj9/eZbN+izTDuKGimu0vPF8B/YN8aC8LmcOj/AAn9CFTEs70Js/oGqy79C69bqJ5XbQI2kGO5N8QL9D08S8zBtBF5ZaVsznpCMoqJnVdjTpb1Db0fwIWmQV6BLXzFOYgA6/gDVfQN9bBWp2J2hdWDPoBV5FrKnAJutHikk/CHHR8i7x4iG7qQ720IYvu3GFbpHjx3pFrOFYkA354z/5bkK826phyAAAAABJRU5ErkJggg==" /> HUE Bridge </label> <input type="text" id="node-input-serverHue" /> </div> <br /> <p> <b>Philips HUE</b> </p> <div class="form-row"> <label for="node-input-hueDevice"> <i class="fa fa-play-circle"></i>&nbspName</label> <input type="text" id="node-input-name" placeholder="Enter your hue device name" /> <input type="hidden" id="node-input-hueDevice" /> </div> <br /> <div id="tabs" hidden" style="width: 900px;"> <ul> <li><a href="#tabs-1"><i class="fa-solid fa-toggle-on"></i> Switch</a></li> <li><a href="#tabs-2"><i class="fa-solid fa-arrow-up-wide-short"></i> Dim</a></li> <li><a href="#tabs-3"><i class="fa-solid fa-temperature-quarter"></i> Tunable white</a></li> <li><a href="#tabs-4"><i class="fa-solid fa-palette"></i> RGB/HSV</a></li> <li><a href="#tabs-5"><i class="fa-solid fa-heart-circle-check"></i> Effects</a></li> <li><a href="#tabs-6"><i class="fa-solid fa-code-merge"></i> Behaviour</a></li> <li><a href="#tabs-7"><i class="fa-solid fa-house"></i> Home Assistant Export (BETA)</a></li> </ul> <div id="tabs-1"> <p> <div class="form-row"> <label for="node-input-nameLightSwitch" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control</label> <label for="node-input-GALightSwitch" style="width:20px;">GA</label> <input type="text" id="node-input-GALightSwitch" placeholder="Ex: 1/1/1" style="width:70px;margin-left: 5px; text-align: left;"> <label for="node-input-dptLightSwitch" style="width:40px; margin-left: 0px; text-align: right;">DPT</label> <select id="node-input-dptLightSwitch" style="width:140px;"></select> <label for="node-input-nameLightSwitch" style="width:50px; margin-left: 0px; text-align: right;">Name</label> <input type="text" id="node-input-nameLightSwitc