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
HTML
'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>  <span style="color:red"    <i class="fa fa-youtube"></i></span> <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> Name</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