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, and KNX routing between interfaces. Easy to use and highly configurable.
2,370 lines • 120 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 HUE",
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: "" },
nameLightEffect: { value: "" },
GALightEffect: { value: "" },
dptLightEffect: { value: "" },
nameLightEffectStatus: { value: "" },
GALightEffectStatus: { value: "" },
dptLightEffectStatus: { value: "" },
effectRules: { 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" }, // Starting from v 4.1.31
updateLocalStateFromKNXWrite: { value: true } // Starting from v 4.1.31
},
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; // Starting from v 4.1.31
$("#node-input-updateLocalStateFromKNXWrite").prop("checked", node.updateLocalStateFromKNXWrite === true || node.updateLocalStateFromKNXWrite === "true"); // Starting from v 4.1.31
const ensureConfigSelection = (selector) => {
if ($(selector).val() !== "_ADD_") return;
try {
$(selector).prop("selectedIndex", 0);
} catch (error) {
// Ignore UI quirks for legacy Node-RED versions
}
};
["#node-input-serverHue"].forEach(ensureConfigSelection);
function ensureVerticalTabsStyle() {
if ($('#knxUltimateHueLightVerticalTabs').length) return;
const style = `
<style id="knxUltimateHueLightVerticalTabs">
.hue-vertical-tabs.ui-tabs.ui-widget.ui-widget-content.ui-corner-all {
display: flex;
border: none;
padding: 0;
}
.hue-vertical-tabs > ul.ui-tabs-nav {
flex: 0 0 180px;
border-right: 1px solid #ccc;
border-left: none;
border-top: none;
border-bottom: none;
padding: 0.5em 0.3em;
}
.hue-vertical-tabs > ul.ui-tabs-nav li {
float: none;
width: 100%;
margin: 0 0 2px 0;
}
.hue-vertical-tabs > ul.ui-tabs-nav li a {
display: block;
width: 100%;
white-space: normal;
position: relative;
border-bottom: none !important;
}
.hue-vertical-tabs > ul.ui-tabs-nav li.ui-tabs-active {
border-bottom: none !important;
}
.hue-vertical-tabs > ul.ui-tabs-nav li.ui-tabs-active a::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 50%;
height: 3px;
background: currentColor;
}
.hue-vertical-tabs .ui-tabs-panel {
flex: 1;
padding: 0.8em 1em;
box-sizing: border-box;
border: none;
background: transparent;
}
.hue-vertical-tabs .form-row > dt {
flex: 1 1 auto;
margin: 0;
}
.hue-vertical-tabs hr {
width: 100%;
border: 0;
border-top: 1px solid #ccc;
margin: 8px 0;
}
</style>`;
$('head').append(style);
}
function onEditPrepare() {
ensureVerticalTabsStyle();
const $knxServerInput = $("#node-input-server");
const KNX_EMPTY_VALUES = new Set(['', 'none', '_ADD_', '__NONE__']);
const $hueServerInput = $("#node-input-serverHue");
const $hueDeviceInput = $("#node-input-hueDevice");
const $deviceNameInput = $("#node-input-name");
const $refreshDevicesButton = $(".hue-refresh-devices");
const $locateDeviceButton = $(".hue-locate-device");
const $hueDevicesLoading = $(".hue-devices-loading");
let cachedHueDevices = Array.isArray(node._cachedHueLightDevices) ? node._cachedHueLightDevices : [];
node._cachedHueLightDevices = cachedHueDevices;
const defaultHueDevicePlaceholder = $deviceNameInput.attr('placeholder') || '';
let showingNoHueDevicesPlaceholder = false;
const HUE_EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__', '__null__', 'null', 'undefined']);
let locateSessionActive = false;
let locateAutoResetTimer = null;
let locatePendingRequest = null;
node.__stopHueLocateSession = null;
node.__cleanupNodeRemovalListener = null;
if (!node.__locateSessionInfo) node.__locateSessionInfo = null;
node.__hueLocateActive = false;
const clearLocateAutoReset = () => {
if (locateAutoResetTimer !== null) {
clearTimeout(locateAutoResetTimer);
locateAutoResetTimer = null;
}
};
const scheduleLocateAutoReset = (durationMs) => {
clearLocateAutoReset();
const ms = Number(durationMs);
if (!Number.isFinite(ms) || ms <= 0) return;
locateAutoResetTimer = setTimeout(() => {
updateLocateButtonState(false);
}, ms);
};
const updateLocateButtonState = (isActive) => {
locateSessionActive = !!isActive;
node.__hueLocateActive = locateSessionActive;
if (!locateSessionActive) {
clearLocateAutoReset();
node.__locateSessionInfo = null;
}
if (!$locateDeviceButton.length) return;
const $icon = $locateDeviceButton.find('i').first();
if ($icon.length) {
$icon.removeClass('fa-stop fa-play').addClass(locateSessionActive ? 'fa fa-stop' : 'fa fa-play');
}
const title = locateSessionActive
? (node._('knxUltimateHueLight.locate_stop_title') || 'Stop Hue locate')
: (node._('knxUltimateHueLight.locate_start_title') || 'Locate selected Hue device');
$locateDeviceButton.attr('title', title);
};
const removeNodeRemovalListener = () => {
if (typeof node.__cleanupNodeRemovalListener === 'function') {
try {
RED.events.removeListener('nodes:remove', node.__cleanupNodeRemovalListener);
} catch (error) { /* empty */ }
node.__cleanupNodeRemovalListener = null;
}
};
const collectRemovedIds = (input, bucket = new Set()) => {
if (!input) return bucket;
if (Array.isArray(input)) {
input.forEach((entry) => collectRemovedIds(entry, bucket));
return bucket;
}
if (typeof input === 'string') {
bucket.add(input);
return bucket;
}
if (typeof input === 'object') {
if (input.id) bucket.add(input.id);
if (input.nodes) collectRemovedIds(input.nodes, bucket);
}
return bucket;
};
const handleNodeRemoved = (payload) => {
const ids = collectRemovedIds(payload);
if (ids.has(node.id) && typeof node.__stopHueLocateSession === 'function') {
node.__stopHueLocateSession();
removeNodeRemovalListener();
}
};
RED.events.on('nodes:remove', handleNodeRemoved);
node.__cleanupNodeRemovalListener = handleNodeRemoved;
const buildLocateContext = ({ allowStored = false } = {}) => {
const hueServerId = resolveHueServerValue({ allowStored });
const rawDevice = getHueDeviceValue({ allowStored });
if (!hueServerId || !rawDevice) return null;
const parts = String(rawDevice).split('#');
const deviceId = (parts[0] || '').trim();
if (deviceId === '') return null;
const deviceType = (parts[1] || 'light').trim() || 'light';
return { serverId: hueServerId, deviceId, deviceType };
};
const performLocateRequest = ({ silent = false, allowStoredContext = false, action } = {}) => {
const context = buildLocateContext({ allowStored: allowStoredContext });
if (!context) {
if (!silent) {
const message = !resolveHueServerValue({ allowStored: true })
? (node._('knxUltimateHueLight.locate_no_bridge') || 'Select a Hue bridge first')
: (node._('knxUltimateHueLight.locate_no_device') || 'Select a Hue device first');
RED.notify(message, 'warning');
}
updateLocateButtonState(false);
return null;
}
if ($locateDeviceButton.length) {
$locateDeviceButton.prop('disabled', true);
}
const effectiveAction = action || (locateSessionActive ? 'stop' : 'start');
const request = $.ajax({
type: 'POST',
url: 'KNXUltimateLocateHueDevice',
data: {
serverId: context.serverId,
deviceId: context.deviceId,
deviceType: context.deviceType,
action: effectiveAction
}
});
locatePendingRequest = request;
if (!allowStoredContext && effectiveAction === 'start') {
node.__locateSessionInfo = {
serverId: context.serverId,
deviceId: context.deviceId,
deviceType: context.deviceType
};
} else if (!node.__locateSessionInfo && effectiveAction !== 'stop') {
node.__locateSessionInfo = {
serverId: context.serverId,
deviceId: context.deviceId,
deviceType: context.deviceType
};
}
request.done((response) => {
const statusValue = (response && typeof response.status === 'string') ? response.status.toLowerCase() : 'started';
let messageKey;
if (effectiveAction === 'stop' || statusValue === 'stopped') {
messageKey = 'knxUltimateHueLight.locate_stopped';
node.__locateSessionInfo = null;
updateLocateButtonState(false);
clearLocateAutoReset();
} else if (effectiveAction === 'start' || statusValue === 'started') {
messageKey = 'knxUltimateHueLight.locate_started';
node.__locateSessionInfo = {
serverId: context.serverId,
deviceId: context.deviceId,
deviceType: context.deviceType
};
updateLocateButtonState(true);
scheduleLocateAutoReset(response && typeof response.expiresInMs === 'number' ? response.expiresInMs : 600000);
} else {
messageKey = 'knxUltimateHueLight.locate_success';
node.__locateSessionInfo = null;
updateLocateButtonState(false);
clearLocateAutoReset();
}
if (!silent) {
const message = node._(messageKey) || (statusValue === 'stopped' ? 'Locate stopped' : 'Locate command sent');
RED.notify(message, 'success');
}
}).fail((xhr) => {
let message;
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
message = xhr.responseJSON.error;
}
updateLocateButtonState(false);
clearLocateAutoReset();
if (!silent) {
RED.notify(message || (node._('knxUltimateHueLight.locate_error') || 'Unable to locate Hue device'), 'error');
}
}).always(() => {
locatePendingRequest = null;
if ($locateDeviceButton.length) {
$locateDeviceButton.prop('disabled', false);
}
});
return request;
};
const resolveKnxServerValue = () => {
const domValue = $knxServerInput.val();
if (domValue !== undefined && domValue !== null && domValue !== '') {
return domValue;
}
if (node.server !== undefined && node.server !== null) {
return node.server;
}
return '';
};
const hasKnxServerSelected = () => {
const val = resolveKnxServerValue();
if (val === undefined || val === null) return false;
return !KNX_EMPTY_VALUES.has(val);
};
const resolveHueServerValue = ({ allowStored = false } = {}) => {
if ($hueServerInput.length) {
const domValue = $hueServerInput.val();
if (domValue !== undefined && domValue !== null) {
const trimmed = String(domValue).trim();
if (trimmed !== '' && !HUE_EMPTY_SERVER_VALUES.has(trimmed.toLowerCase())) return trimmed;
}
}
if (node.serverHue !== undefined && node.serverHue !== null) {
const stored = String(node.serverHue).trim();
if (stored !== '' && !HUE_EMPTY_SERVER_VALUES.has(stored.toLowerCase())) return stored;
}
if (allowStored && node.__locateSessionInfo && node.__locateSessionInfo.serverId) {
return node.__locateSessionInfo.serverId;
}
return '';
};
const getHueServerConfig = () => {
const id = resolveHueServerValue();
if (id === '') return null;
try {
return RED.nodes.node(id) || null;
} catch (error) {
return null;
}
};
const resolveDeviceTypeSuffix = (deviceType) => {
const normalized = (deviceType || '').toLowerCase();
return normalized === 'grouped_light' ? '#grouped_light' : '#light';
};
const applyHueDevicesPlaceholder = (hasDevices) => {
if (!$deviceNameInput.length) return;
if (hasDevices) {
if (showingNoHueDevicesPlaceholder) {
showingNoHueDevicesPlaceholder = false;
$deviceNameInput.attr('placeholder', defaultHueDevicePlaceholder);
}
return;
}
const message = node._('knxUltimateHueLight.no_devices') || defaultHueDevicePlaceholder;
showingNoHueDevicesPlaceholder = true;
$deviceNameInput.attr('placeholder', message);
if (($deviceNameInput.val() || '').trim() === '') {
$deviceNameInput.val('');
}
};
const filterHueDevices = (devices, term) => {
const cleaned = (term || '').replace(/exactmatch/gi, '').trim();
return $.map(devices, (value) => {
if (!value || !value.id) return null;
const deviceName = value.name || value.id;
if (cleaned !== '' && !htmlUtilsfullCSVSearch(deviceName, cleaned)) {
return null;
}
const suffix = resolveDeviceTypeSuffix(value.deviceType);
return {
hueDevice: `${value.id}${suffix}`,
value: deviceName,
deviceObject: value.deviceObject || value,
deviceType: value.deviceType || 'light'
};
});
};
const fetchHueDevices = (term, respond, { forceRefresh = false } = {}) => {
const hueServer = getHueServerConfig();
if (!hueServer) {
applyHueDevicesPlaceholder(true);
if (typeof respond === 'function') respond([]);
node.__locateSessionInfo = null;
updateLocateButtonState(false);
return;
}
if (!forceRefresh && Array.isArray(cachedHueDevices) && cachedHueDevices.length > 0) {
applyHueDevicesPlaceholder(cachedHueDevices.length > 0);
if (typeof respond === 'function') respond(filterHueDevices(cachedHueDevices, term));
return;
}
if ($hueDevicesLoading.length) {
$hueDevicesLoading.show();
}
const refreshQuery = forceRefresh ? '&forceRefresh=1' : '';
$.getJSON(`KNXUltimateGetResourcesHUE?rtype=light&serverId=${encodeURIComponent(hueServer.id)}${refreshQuery}&_=${Date.now()}`, (data) => {
const listCandidates = Array.isArray(data) ? data : (Array.isArray(data?.devices) ? data.devices : []);
cachedHueDevices = listCandidates.map((value) => {
const deviceObject = value.deviceObject || value;
const rawId = deviceObject?.id || value.id || value.rid || '';
const trimmedId = typeof rawId === 'string' ? rawId.trim() : rawId;
if (trimmedId === undefined || trimmedId === null || String(trimmedId).trim() === '') return null;
return {
id: String(trimmedId).trim(),
name: value.name || value.metadata?.name || deviceObject?.metadata?.name || '',
deviceType: deviceObject?.type || value.type || '',
deviceObject
};
}).filter(Boolean);
node._cachedHueLightDevices = cachedHueDevices;
applyHueDevicesPlaceholder(cachedHueDevices.length > 0);
if (typeof respond === 'function') respond(filterHueDevices(cachedHueDevices, term));
}).always(() => {
if ($hueDevicesLoading.length) {
$hueDevicesLoading.hide();
}
}).fail(() => {
cachedHueDevices = [];
node._cachedHueLightDevices = cachedHueDevices;
applyHueDevicesPlaceholder(false);
if ($hueDevicesLoading.length) {
$hueDevicesLoading.hide();
}
if (typeof respond === 'function') respond([]);
node.__locateSessionInfo = null;
updateLocateButtonState(false);
});
};
// 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);
}
// ################################################################
const $effectStorage = $("#node-input-effectRules");
let parsedEffectRules = [];
if (Array.isArray(node.effectRules)) {
parsedEffectRules = JSON.parse(JSON.stringify(node.effectRules));
} else {
try {
parsedEffectRules = JSON.parse(node.effectRules || '[]');
} catch (error) {
parsedEffectRules = [];
}
}
if (!Array.isArray(parsedEffectRules)) parsedEffectRules = [];
node.effectRules = parsedEffectRules;
const $effectList = $("#node-input-effect-rule-container");
const syncEffectRulesStorage = () => {
try {
$effectStorage.val(JSON.stringify(node.effectRules || []));
} catch (error) {
$effectStorage.val('[]');
}
};
const rebuildEffectRulesFromUI = () => {
const collected = [];
try {
const items = $effectList.editableList('items');
items.each(function () {
const $row = $(this);
const knxValue = $row.find('.rowEffectKNXValue').val();
const hueEffect = $row.find('.rowEffectHueEffect').val();
if (hueEffect && hueEffect !== '') {
collected.push({
knxValue: knxValue !== undefined && knxValue !== null ? knxValue : '',
hueEffect
});
}
});
} catch (error) { }
node.effectRules = collected;
syncEffectRulesStorage();
};
const bindEffectRowEvents = ($row) => {
$row.find('.rowEffectKNXValue').on('input change', rebuildEffectRulesFromUI);
$row.find('.rowEffectHueEffect').on('change', rebuildEffectRulesFromUI);
};
syncEffectRulesStorage();
const $effectContainer = $("#divHueEffectsContainer");
const $effectContent = $("#divHueEffectsContent");
const $effectNoSupport = $("#divHueEffectsNoSupport");
let effectOptions = [];
const normalizeEffectEntry = (effect, { fallbackLabel } = {}) => {
if (effect === undefined || effect === null) return null;
if (typeof effect === 'string') {
const trimmed = effect.trim();
return trimmed === '' ? null : { value: trimmed, label: trimmed };
}
if (typeof effect === 'object') {
const nested = (candidate) => {
if (!candidate || typeof candidate !== 'object') return undefined;
return candidate.value ?? candidate.effect ?? candidate.id ?? candidate.code ?? candidate.type ?? candidate.name ?? candidate.title;
};
const statusCandidate = typeof effect.status === 'object' ? nested(effect.status) : undefined;
const rawValue = effect.value ?? effect.effect ?? statusCandidate ?? effect.status ?? effect.id ?? effect.type ?? effect.code;
const rawLabel = effect.label ?? effect.name ?? effect.title ?? effect.display ?? effect.display_name ?? effect.text ?? effect.description ?? (typeof effect.status === 'object' ? (effect.status.name ?? effect.status.title ?? effect.status.label) : undefined);
const value = rawValue !== undefined && rawValue !== null ? String(rawValue).trim() : '';
const labelSource = rawLabel !== undefined && rawLabel !== null ? rawLabel : (fallbackLabel !== undefined ? fallbackLabel : rawValue);
const label = labelSource !== undefined && labelSource !== null ? String(labelSource).trim() : '';
if (value === '') return null;
return { value, label: label === '' ? value : label };
}
const stringified = String(effect).trim();
return stringified === '' ? null : { value: stringified, label: stringified };
};
const collectEffectFallbacks = () => {
const fallback = [];
if (Array.isArray(node.effectRules)) {
node.effectRules.forEach((rule) => {
const entry = normalizeEffectEntry(rule ? rule.hueEffect : null);
if (entry) fallback.push(entry);
});
}
return fallback;
};
const collectHueDeviceObjectEffects = () => {
const fallback = [];
if (node.hueDeviceObject && node.hueDeviceObject.effects && Array.isArray(node.hueDeviceObject.effects.status_values)) {
node.hueDeviceObject.effects.status_values.forEach((raw) => {
const entry = normalizeEffectEntry(raw);
if (entry) fallback.push(entry);
});
}
return fallback;
};
const getAllEffectOptions = () => {
const combined = [];
const pushUnique = (entry, { forceFront = false } = {}) => {
if (!entry || !entry.value) return;
const existingIndex = combined.findIndex((candidate) => candidate.value === entry.value);
if (existingIndex !== -1) {
if (forceFront && existingIndex > 0) {
const [existing] = combined.splice(existingIndex, 1);
combined.unshift(existing);
}
return;
}
if (forceFront) {
combined.unshift(entry);
} else {
combined.push(entry);
}
};
pushUnique({ value: 'no_effect', label: 'no_effect' }, { forceFront: true });
effectOptions.forEach((option) => pushUnique(option));
collectHueDeviceObjectEffects().forEach((option) => pushUnique(option));
collectEffectFallbacks().forEach((option) => pushUnique(option));
return combined;
};
function populateEffectSelect($select, selectedValue) {
const targetValue = selectedValue !== undefined && selectedValue !== null
? String(selectedValue).trim()
: '';
const entries = getAllEffectOptions();
$select.empty();
entries.forEach((entry) => {
$select.append($("<option></option>").attr("value", entry.value).text(entry.label));
});
if (targetValue && entries.some((entry) => entry.value === targetValue)) {
$select.val(targetValue);
} else if (!targetValue && entries.length > 0) {
$select.val(entries[0].value);
}
return entries.map((entry) => entry.value);
}
function decorateEffectValueInput() { }
function refreshEffectRows() {
if (!$effectList.data('effectListInitialized')) return;
const items = $effectList.editableList('items');
items.each(function () {
const $row = $(this);
const $select = $row.find('select.rowEffectHueEffect');
const currentValue = $select.val();
populateEffectSelect($select, currentValue);
decorateEffectValueInput($row.find('.rowEffectKNXValue'));
});
}
function setAvailableEffects(effects) {
const sanitized = [];
if (Array.isArray(effects)) {
effects.forEach((raw) => {
const entry = normalizeEffectEntry(raw);
if (entry) sanitized.push(entry);
});
}
effectOptions = sanitized;
const hasMappings = Array.isArray(node.effectRules) && node.effectRules.length > 0;
const hasOptions = effectOptions.length > 0;
if (!hasOptions && !hasMappings) {
$effectContainer.show();
$effectContent.hide();
$effectNoSupport.show();
} else {
$effectContainer.show();
$effectContent.show();
$effectNoSupport.toggle(!hasOptions);
}
$("#node-input-effect-autofill").prop('disabled', !hasOptions);
refreshEffectRows();
}
function decorateEffectValueInput() { }
function ensureEffectEditableList() {
if ($effectList.data('effectListInitialized')) return;
$effectList.editableList({
addItem: function (container, i, opt) {
const data = opt && opt.rule ? opt.rule : (opt || {});
const row = $('<div/>', { class: 'form-row effect-rule-row' }).appendTo(container);
const $valueInput = $('<input/>', {
class: 'rowEffectKNXValue',
type: 'text',
placeholder: node._('knxUltimateHueLight.effect_knx_value_placeholder') || 'Value',
style: 'width:40%; margin-left:0; text-align:left;'
}).appendTo(row);
$valueInput.val(data.knxValue || '');
decorateEffectValueInput($valueInput);
$('<span/>', { html: ' → ', style: 'display:inline-block; margin:0 8px;' }).appendTo(row);
const $select = $('<select/>', {
class: 'rowEffectHueEffect',
style: 'width:45%;'
}).appendTo(row);
const availableOptions = populateEffectSelect($select, data.hueEffect);
if ((!data || !data.hueEffect) && availableOptions.length > 0) {
$select.val(availableOptions[0]);
}
bindEffectRowEvents(row);
},
removable: true,
sortable: false,
removeItem: function () {
rebuildEffectRulesFromUI();
}
});
$effectList.data('effectListInitialized', true);
}
ensureEffectEditableList();
const initialEffects = (node.hueDeviceObject && node.hueDeviceObject.effects && Array.isArray(node.hueDeviceObject.effects.status_values))
? node.hueDeviceObject.effects.status_values
: [];
setAvailableEffects(initialEffects);
if (Array.isArray(node.effectRules) && node.effectRules.length > 0) {
const items = $effectList.editableList('items');
items.each(function () { $(this).remove(); });
node.effectRules.forEach((rule) => {
$effectList.editableList('addItem', { rule });
});
refreshEffectRows();
rebuildEffectRulesFromUI();
}
$("#node-input-effect-autofill").off('click').on('click', function () {
if (effectOptions.length === 0) return;
const items = $effectList.editableList('items');
items.each(function () { $(this).remove(); });
effectOptions.forEach((effect) => {
if (!effect || !effect.value) return;
$effectList.editableList('addItem', { rule: { knxValue: effect.value, hueEffect: effect.value } });
});
refreshEffectRows();
rebuildEffectRulesFromUI();
});
$('#node-input-dptLightEffect').on('change', () => {
const items = $effectList.editableList('items');
items.each(function () {
decorateEffectValueInput($(this).find('.rowEffectKNXValue'));
});
});
const $tabs = $("#tabs");
const $pinSectionRow = $("#node-input-enableNodePINS").closest('.form-row');
const $pinSelect = $("#node-input-enableNodePINS");
const $pinInfoRow = $pinSectionRow.next('.form-tips');
$tabs.addClass('hue-vertical-tabs');
$tabs.tabs(); // Tabs gestione KNX
$tabs.find('ul').addClass('ui-tabs-nav');
$tabs.find('li').removeClass('ui-corner-top').addClass('ui-corner-left');
function getDPT(_dpt, _destinationWidget) {
// DPT Switch command
// ########################
const prefixes = Array.isArray(_dpt) ? _dpt : [_dpt];
$(_destinationWidget).empty();
if (!hasKnxServerSelected()) {
return;
}
const serverId = resolveKnxServerValue();
$.getJSON(`knxUltimateDpts?serverId=${serverId}&_=${Date.now()}`, (data) => {
data.forEach((dpt) => {
if (prefixes.some((prefix) => prefix === "" || dpt.value.startsWith(prefix))) {
// 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) { }
if (_destinationWidget === '#node-input-dptLightEffect') {
$(_destinationWidget).trigger('change');
}
});
}
function getGroupAddress(_sourceWidgetAutocomplete, _destinationWidgetName, _destinationWidgetDPT, _additionalSearchTerm) {
$(_sourceWidgetAutocomplete).autocomplete({
minLength: 0,
source: function (request, response) {
if (!hasKnxServerSelected()) {
response([]);
return;
}
const serverId = resolveKnxServerValue();
$.getJSON(`knxUltimatecsv?nodeID=${serverId}&_=${Date.now()}`, (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");
const $dptSelect = $(_destinationWidgetDPT);
if (optVal !== undefined && optVal !== null) {
$dptSelect.val(optVal).trigger('change');
} else {
// Ensure downstream widgets refresh even when the DPT is missing
$dptSelect.trigger('change');
}
},
}).focus(function () {
$(this).autocomplete('search', $(this).val() + 'exactmatch');
});
try {
if (hasKnxServerSelected()) {
const srv = RED.nodes.node(resolveKnxServerValue());
if (srv && srv.id) KNX_enableSecureFormatting($(_sourceWidgetAutocomplete), srv.id);
}
} catch (e) { }
}
const effectDptPrefixes = ["1.", "2.", "5.", "6.", "7.", "8.", "9.", "16.", "20."];
const refreshKnxBindings = () => {
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(effectDptPrefixes, "#node-input-dptLightEffect");
getGroupAddress("#node-input-GALightEffect", "#node-input-nameLightEffect", "#node-input-dptLightEffect", effectDptPrefixes);
getDPT(effectDptPrefixes, "#node-input-dptLightEffectStatus");
getGroupAddress("#node-input-GALightEffectStatus", "#node-input-nameLightEffectStatus", "#node-input-dptLightEffectStatus", effectDptPrefixes);
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 ----------------------
};
refreshKnxBindings();
const getHueDeviceValue = ({ allowStored = false } = {}) => {
if ($hueDeviceInput.length) {
const domValue = $hueDeviceInput.val();
if (domValue !== undefined && domValue !== null && domValue !== '') {
return domValue;
}
}
if (node.hueDevice !== undefined && node.hueDevice !== null && node.hueDevice !== '') {
return node.hueDevice;
}
if (allowStored && node.__locateSessionInfo && node.__locateSessionInfo.deviceId) {
const suffix = node.__locateSessionInfo.deviceType || 'light';
return `${node.__locateSessionInfo.deviceId}#${suffix}`;
}
return '';
};
const updateTabsVisibility = () => {
const knxSelected = hasKnxServerSelected();
const hueDeviceSelected = getHueDeviceValue() !== '';
const shouldShowTabs = knxSelected && hueDeviceSelected;
if (shouldShowTabs) {
$tabs.show();
try { $tabs.tabs('refresh'); } catch (error) { /* empty */ }
} else {
$tabs.hide();
}
if ($pinSelect.length) {
const desiredPins = knxSelected ? 'no' : 'yes';
if ($pinSelect.val() !== desiredPins) {
$pinSelect.val(desiredPins).trigger('change');
}
}
if ($pinSectionRow.length) {
$pinSectionRow.show();
}
if ($pinInfoRow.length) {
$pinInfoRow.show();
}
};
updateTabsVisibility();
$knxServerInput.on('change.knxUltimateHueLight', () => {
refreshKnxBindings();
updateTabsVisibility();
});
$hueDeviceInput.on('change.knxUltimateHueLight input.knxUltimateHueLight', updateTabsVisibility);
if (($deviceNameInput.val() || '').trim() !== '') {
applyHueDevicesPlaceholder(true);
} else {
applyHueDevicesPlaceholder(cachedHueDevices.length > 0);
}
if ($deviceNameInput.length) {
if ($deviceNameInput.data('ui-autocomplete')) {
try { $deviceNameInput.autocomplete('destroy'); } catch (error) { /* empty */ }
}
$deviceNameInput.autocomplete({
minLength: 0,
source(request, response) {
fetchHueDevices(request.term, response);
},
select(event, ui) {
if (!ui.item || !ui.item.hueDevice || ui.item.hueDevice === 'error') {
event.preventDefault();
return;
}
$deviceNameInput.val(ui.item.value || '');
node.name = ui.item.value || node.name;
$hueDeviceInput.val(ui.item.hueDevice);
node.hueDevice = ui.item.hueDevice;
node.hueDeviceObject = ui.item.deviceObject || { type: ui.item.deviceType };
node.__locateSessionInfo = null;
updateTabsVisibility();
setTimeout(() => {
try { $deviceNameInput.autocomplete('close'); } catch (error) { /* empty */ }
Go();
}, 0);
},
focus(event, ui) {
event.preventDefault();
$deviceNameInput.val(ui.item && ui.item.value ? ui.item.value : '');
}
}).focus(function () {
$(this).autocomplete('search', `${$(this).val()}exactmatch`);
}).on('input.knxUltimateHueLight', function () {
if ($(this).val().trim() === '') {
$hueDeviceInput.val('');
node.hueDevice = '';
node.__locateSessionInfo = null;
updateTabsVisibility();
updateLocateButtonState(false);
}
});
}
if ($refreshDevicesButton.length) {
$refreshDevicesButton.off('.knxUltimateHueLight').on('click.knxUltimateHueLight', () => {
cachedHueDevices = [];
node._cachedHueLightDevices = cachedHueDevices;
fetchHueDevices('', () => {
if ($deviceNameInput.length) {
$deviceNameInput.autocomplete('search', `${$deviceNameInput.val()}exactmatch`);
}
}, { forceRefresh: true });
});
}
if ($locateDeviceButton.length) {
$locateDeviceButton.off('.knxUltimateHueLight').on('click.knxUltimateHueLight', () => {
const desiredAction = locateSessionActive ? 'stop' : 'start';
performLocateRequest({ silent: false, action: desiredAction });
});
updateLocateButtonState(false);
}
if ($hueServerInput.length) {
$hueServerInput.off('.knxUltimateHueLightDevices').on('change.knxUltimateHueLightDevices', () => {
cachedHueDevices = [];
node._cachedHueLightDevices = cachedHueDevices;
showingNoHueDevicesPlaceholder = false;
applyHueDevicesPlaceholder(true);
node.__locateSessionInfo = null;
updateLocateButtonState(false);
if ($hueDevicesLoading.length) {
$hueDevicesLoading.hide();
}
});
}
node.__stopHueLocateSession = () => {
clearLocateAutoReset();
removeNodeRemovalListener();
const pending = locatePendingRequest;
if (pending && typeof pending.always === 'function') {
pending.always(() => {
if (node.__locateSessionInfo || locateSessionActive) {
const followUp = performLocateRequest({ silent: true, allowStoredContext: true, action: 'stop' });
if (!followUp || typeof followUp.then !== 'function') {
updateLocateButtonState(false);
}
} else {
updateLocateButtonState(false);
}
});
return;
}
if (locateSessionActive || (node.__locateSessionInfo && node.__locateSessionInfo.deviceId)) {
const request = performLocateRequest({ silent: true, allowStoredContext: true, action: 'stop' });
if (!request || typeof request.then !== 'function') {
updateLocateButtonState(false);
}
} else {
updateLocateButtonState(false);
}
};
// Get the HUE capabilities to enable/disable UI parts
var getJsonPromise;
const initialHueDeviceRaw = getHueDeviceValue();
if (initialHueDeviceRaw === '') {
updateTabsVisibility();
return;
} else {
$hueDeviceInput.val(initialHueDeviceRaw);
updateTabsVisibility();
if (getJsonPromise !== undefined) getJsonPromise.abort();
getJsonPromise = $.getJSON("knxUltimateGetLightObject?id=" + initialHueDeviceRaw.split("#")[0] + "&serverId=" + $("#node-input-serverHue").val() + "&" + { _: new Date().getTime() }, (data) => {
let oLight = data;
const effects = (oLight && oLight.effects && Array.isArray(oLight.effects.status_values))
? oLight.effects.status_values
: [];
setAvailableEffects(effects);
// 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(node._("knxUltimateHueLight.none"))
).append(
$("<option>")
.val("yes")
.text(node._("knxUltimateHueLight.select_color"))
).append(
$("<option>")
.val("temperature")
.text(node._("knxUltimateHueLight.select_temperature_brightness"))
);
$("#node-input-enableDayNightLighting").empty().append(
$("<option>")
.val("no")
.text(node._("knxUltimateHueLight.opt_no"))
).append(
$("<option>")
.val("yes")
.text(node._("knxUltimateHueLight.select_color"))
).append(
$("<option>")
.val("temperature")
.text(node._("knxUltimateHueLight.select_temperature_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(node._("knxUltimateHueLight.none"))
);
$("#node-input-enableDayNightLighting").empty().append(
$("<option>")
.val("no")
.text(node._("knxUltimateHueLight.opt_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(node._("knxUltimateHueLight.select_color"))
);
$("#node-input-enableDayNightLighting").append(
$("<option>")
.val("yes")
.text(node._("knxUltimateHueLight.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(node._("knxUltimateHueLight.select_temperature_brightness"))
);
$("#node-input-enableDayNightLighting").append(
$("<option>")
.val("temperature")
.text(node._("knxUltimateHueLight.select_temperature_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(node._("knxUltimateHueLight.select_brightness"))
);
$("#node-input-enableDayNightLighting").append(
$("<option>")
.val("temperature")
.text(node._("knxUltimateHueLight.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(node._("knxUltimateHueLight.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(node._("knxUltimateHueLight.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(node._("knxUltimateHueLight.get_again"));
});
});
$("#getColorAtSwitchOnNightTimeButton").on("click", function () {
$("#getColorAtSwitchOnNightTimeButton").text(node._("knxUltimateHueLight.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(node._("knxUltimateHueLight.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() + "% " + node._("knxUltimateHueLight.switch_off")));
$("#comboBrightnessAtSwitchOn").append($("<option>").val(index).text(index.toString() + "% " + node._("knxUltimateHueLight.switch_off")));
$("#comboBrightnessAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + "% " + node._("knxUltimateHueLight.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() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_2200")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_2200")));
} else if (index === 2700) {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_2700")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_2700")));
} else if (index === 3000) {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_3000")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_3000")));
} else if (index === 3500) {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_3500_day")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_3500_night")));
} else if (index === 4100) {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_4100_day")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_4100_night")));
} else if (index === 5000) {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_5000_day")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_5000_night")));
} else if (index === 6500) {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_6500_day")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix") + " " + node._("knxUltimateHueLight.temp_desc_6500_night")));
} else {
$("#comboTemperatureAtSwitchOn").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix")));
$("#comboTemperatureAtSwitchOnNightTime").append($("<option>").val(index).text(index.toString() + node._("knxUltimateHueLight.k_suffix")));
}
}
// 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();
}
// 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) { }
});
// 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;
}
const nodeRef = this;
let collectedEffectRules = [];
try {
const effectItems = $("#node-input-effect-rule-container").editableList('items');
effectItems.each(function () {
const $row = $(this);
const knxValue = $row.find('.rowEffectKNXValue').val();
const hueEffect = $row.find('.rowEffectHueEffect').val();
if (hueEffect && hueEffect !== '') {
collectedEffectRules.push({
knxValue: knxValue !== undefined && knxValue !== null ? knxValue : '',
hueEffect
});
}
});
} catch (error) {
collectedEffectRules = [];
}
nodeRef.effectRules = collectedEffectRules;
const serialized = JSON.stringify(collectedEffectRules);
$("#node-input-effectRules").val(serialized);
this.effectRules = serialized;
this.updateLocalStateFromKNXWrite = $("#node-input-updateLocalStateFromKNXWrite").is(":checked"); // Starting from v 4.1.31
this._cachedHueLightDevices = [];
if (typeof this.__stopHueLocateSession === 'function') {
try { this.__stopHueLocateSession(); } catch (error) { /* empty */ }
this.__stopHueLocateSession = null;
}
if (typeof this.__cleanupNodeRemovalListener === 'function') {
try { RED.events.removeListener('nodes:remove', this.__cleanupNodeRemovalListener); } catch (error) { /* empty */ }
this.__cleanupNodeRemovalListener = null;
}
this.__locateSessionInfo = null;
},
oneditcancel: function () {
// Return to the info tab
try {
RED.sidebar.show("info");
} catch (error) { }
//RED.sidebar.removeTab("tabNRColor");
//RED.sidebar.show("help");
this._cachedHueLightDevices = [];
if (typeof this.__stopHueLocateSession === 'function') {
try { this.__stopHueLocateSession(); } catch (error) { /* empty */ }
this.__stopHueLocateSession = null;
}
if (typeof this.__cleanupNodeRemovalListener === 'function') {
try { RED.events.removeListener('nodes:remove', this.__cleanupNodeRemovalListener); } catch (error) { /* empty */ }
this.__cleanupNodeRemovalListener = null;
}
this.__locateSessionInfo = null;
},
oneditdelete: function () {
if (typeof this.oneditclose === 'function') {
try { this.oneditclose(); } catch (error) { /* empty */ }
}
},
oneditclose: function () {
if (typeof this.__stopHueLocateSession === 'function') {
try { this.__stopHueLocateSession(); } catch (error) { /* empty */ }
}
if (typeof this.__cleanupNodeRemovalListener === 'function') {
try { RED.events.removeListener('nodes:remove', this.__cleanupNodeRemovalListener); } catch (error) { /* empty */ }
this.__cleanupNodeRemovalListener = null;
}
this.__stopHueLocateSession = null;
this.__hueLocateActive = false;
this.__locateSessionInfo = null;
},
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);
}
}())
</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-2x"></i><br/><br/>
<span data-i18n="hue-config.properties.wait_message"></span>
</p>
</div>
<div id="mainWindow" hidden>
<div class="form-row">
<b><span data-i18n="knxUltimateHueLight.title"></span></b>  <span style="color:red"    <i class="fa fa-youtube"></i></span> <a
target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u><span data-i18n="common.youtube_sample"></span></u></a>
<br />
<br />
<p align="center">
<i class="fa-regular fa-lightbulb fa-bounce fa-2x"></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>
<span data-i18n="common.knx_gw"></span>
</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==" />
<span data-i18n="common.hue_bridge"></span>
</label>
<input type="text" id="node-input-serverHue" />
</div>
<br />
<p>
<b><span data-i18n="common.philips_hue"></span></b>
</p>
<div class="form-row">
<label for="node-input-name">
<i class="fa fa-play-circle"></i> <span data-i18n="common.name"></span>
</label>
<input type="text" id="node-input-name" placeholder="Enter your hue device name"
style="flex:1 1 240px; min-width:240px; max-width:240px;" />
<button type="button" class="red-ui-button hue-refresh-devices"
style="margin-left:6px; color:#1b7d33; border-color:#1b7d33;" title="Refresh Hue devices">
<i class="fa fa-sync"></i>
</button>
<button type="button" class="red-ui-button hue-locate-device"
style="margin-left:6px; color:#ff9800; border-color:#ff9800;" title="Locate selected Hue device">
<i class="fa fa-play"></i>
</button>
<span class="hue-devices-loading" style="margin-left:6px; display:none; color:#1b7d33;">
<i class="fa fa-circle-notch fa-spin"></i>
</span>
<input type="hidden" id="node-input-hueDevice" />
</div>
<br />
<div id="tabs" style="display:none; width: 900px;">
<ul>
<li><a href="#tabs-1"><i class="fa-solid fa-toggle-on"></i> <span data-i18n="knxUltimateHueLight.tabs.switch"></span></a></li>
<li><a href="#tabs-2"><i class="fa-solid fa-arrow-up-wide-short"></i> <span data-i18n="knxUltimateHueLight.tabs.dim"></span></a></li>
<li><a href="#tabs-3"><i class="fa-solid fa-temperature-quarter"></i> <span data-i18n="knxUltimateHueLight.tabs.tunable_white"></span></a></li>
<li><a href="#tabs-4"><i class="fa-solid fa-palette"></i> <span data-i18n="knxUltimateHueLight.tabs.rgb_hsv"></span></a></li>
<li><a href="#tabs-5"><i class="fa-solid fa-heart-circle-check"></i> <span data-i18n="knxUltimateHueLight.tabs.effects"></span></a></li>
<li><a href="#tabs-6"><i class="fa-solid fa-code-merge"></i> <span data-i18n="knxUltimateHueLight.tabs.behaviour"></span></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> <span data-i18n="knxUltimateHueLight.control"></span></label>
<label for="node-input-GALightSwitch" style="width:20px;"><span data-i18n="common.ga"></span></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;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightSwitch" style="width:140px;"></select>
<label for="node-input-nameLightSwitch" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
<input type="text" id="node-input-nameLightSwitch" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightState" style="width:110px;"><i class="fa fa-question-circle"></i> <span data-i18n="knxUltimateHueLight.status"></span></label>
<label for="node-input-GALightState" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightState" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightState" style="width:140px;"></select>
<label for="node-input-nameLightState" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightState" style="width:190px;margin-left: 5px; text-align: left;">
</div>
</p>
</div>
<div id="tabs-2">
<p>
<img src="resources/node-red-contrib-knx-ultimate/dim.png" style="width: 900px;">
<div class="form-row">
<label for="node-input-nameLightDIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control dim</label>
<label for="node-input-GALightDIM" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightDIM" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightDIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightDIM" style="width:140px;"></select>
<label for="node-input-nameLightDIM" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightDIM" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightBrightness" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
%</label>
<label for="node-input-GALightBrightness" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightBrightness" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightBrightness" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightBrightness" style="width:140px;"></select>
<label for="node-input-nameLightBrightness" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightBrightness" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightBrightnessState" style="width:110px;"><i class="fa fa-question-circle"></i> Status %</label>
<label for="node-input-GALightBrightnessState" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightBrightnessState" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightBrightnessState"
style="width:40px; margin-left: 0px; text-align: right;">DPT</label>
<select id="node-input-dptLightBrightnessState" style="width:140px;"></select>
<label for="node-input-nameLightBrightnessState" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightBrightnessState"
style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-dimSpeed" style="width:260px">
<i class="fa fa-bolt"></i> Dim Speed (ms)
</label>
<input type="text" id="node-input-dimSpeed" placeholder='Default is 5000' style="width:210px">
</div>
<div class="form-row">
<label for="node-input-minDimLevelLight" style="width:260px;">
<i class="fa fa-clone"></i> Min Dim Brightness
</label>
<select id="node-input-minDimLevelLight">
<option value="useHueLightLevel" data-i18n="knxUltimateHueLight.use_min_brightness"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-maxDimLevelLight" style="width:260px;">
<i class="fa fa-clone"></i> Max Dim Brightness
</label>
<select id="node-input-maxDimLevelLight"></select>
</div>
</p>
</div>
<div id="tabs-3">
<p>
<img src="resources/node-red-contrib-knx-ultimate/tunablewhite.png" style="width: 900px;">
<div class="form-row">
<label for="node-input-nameLightKelvinDIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control dim</label>
<label for="node-input-GALightKelvinDIM" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightKelvinDIM" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightKelvinDIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightKelvinDIM" style="width:140px;"></select>
<label for="node-input-nameLightKelvinDIM" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
<input type="text" id="node-input-nameLightKelvinDIM" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightKelvinPercentage" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
%</label>
<label for="node-input-GALightKelvinPercentage" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightKelvinPercentage" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightKelvinPercentage" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightKelvinPercentage" style="width:140px;"></select>
<label for="node-input-nameLightKelvinPercentage"
style="width:50px; margin-left: 0px; text-align: right;">Name</label>
<input type="text" id="node-input-nameLightKelvinPercentage" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightKelvinPercentageState" style="width:110px;"><i class="fa fa-question-circle"></i> Status
%</label>
<label for="node-input-GALightKelvinPercentageState" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightKelvinPercentageState" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightKelvinPercentageState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightKelvinPercentageState" style="width:140px;"></select>
<label for="node-input-nameLightKelvinPercentageState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
<input type="text" id="node-input-nameLightKelvinPercentageState" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightKelvin" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control
Kelvin</label>
<label for="node-input-GALightKelvin" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightKelvin" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightKelvin" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightKelvin" style="width:140px;"></select>
<label for="node-input-nameLightKelvin" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
<input type="text" id="node-input-nameLightKelvin" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightKelvinState" style="width:110px;"><i class="fa fa-question-circle"></i> Status
Kelvin</label>
<label for="node-input-GALightKelvinState" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightKelvinState" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightKelvinState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightKelvinState" style="width:140px;"></select>
<label for="node-input-nameLightKelvinState" style="width:50px; margin-left: 0px; text-align: right;"><span data-i18n="common.name"></span></label>
<input type="text" id="node-input-nameLightKelvinState" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label style="width:170px" for="node-input-invertDimTunableWhiteDirection">
<i class="fa fa-shuffle"></i> Invert dim direction
</label>
<input type="checkbox" id="node-input-invertDimTunableWhiteDirection"
style="display:inline-block; width:auto; vertical-align:top;" />
</div>
</p>
</div>
<div id="tabs-4">
<p>
<img src="resources/node-red-contrib-knx-ultimate/rgb.png" style="width: 900px;">
<p><b> RGB section</b></p>
<div class="form-row">
<label for="node-input-nameLightColor" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control rgb</label>
<label for="node-input-GALightColor" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightColor" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightColor" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightColor" style="width:140px;"></select>
<label for="node-input-nameLightColor" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightColor" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightColorState" style="width:110px;"><i class="fa fa-question-circle"></i> Status rgb</label>
<label for="node-input-GALightColorState" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightColorState" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightColorState" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightColorState" style="width:140px;"></select>
<label for="node-input-nameLightColorState" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightColorState" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<p><b> HSV section</b></p>
<!-- // HSV Color change
// nameLightHSV_H_DIM: { value: "" },
// GALightHSV_H_DIM: { value: "" },
// dptLightHSV_H_DIM: { value: "" }, -->
<div class="form-row">
<label for="node-input-nameLightHSV_H_DIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control H dim</label>
<label for="node-input-GALightHSV_H_DIM" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightHSV_H_DIM" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightHSV_H_DIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightHSV_H_DIM" style="width:140px;"></select>
<label for="node-input-nameLightHSV_H_DIM" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightHSV_H_DIM" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightHSV_H_State" style="width:110px;"><i class="fa fa-question-circle"></i> Status H %</label>
<label for="node-input-GALightHSV_H_State" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightHSV_H_State" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightHSV_H_State" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightHSV_H_State" style="width:140px;"></select>
<label for="node-input-nameLightHSV_H_State" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightHSV_H_State" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<!-- // HSV 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: "" }, -->
<div class="form-row">
<label for="node-input-nameLightHSV_S_DIM" style="width:110px;"><i class="fa fa-play-circle-o"></i> Control S dim</label>
<label for="node-input-GALightHSV_S_DIM" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightHSV_S_DIM" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightHSV_S_DIM" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightHSV_S_DIM" style="width:140px;"></select>
<label for="node-input-nameLightHSV_S_DIM" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightHSV_S_DIM" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightHSV_S_State" style="width:110px;"><i class="fa fa-question-circle"></i> Status S %</label>
<label for="node-input-GALightHSV_S_State" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightHSV_S_State" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightHSV_S_State" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightHSV_S_State" style="width:140px;"></select>
<label for="node-input-nameLightHSV_S_State" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightHSV_S_State" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<!-- // HSV V Brightness change
nameLightHSV_V_DIM: { value: "" },
GALightHSV_V_DIM: { value: "" },
dptLightHSV_V_DIM: { value: "" },
nameLightHSV_V_State: { value: "" },
GALightHSV_V_State: { value: "" },
dptLightHSV_V_State: { value: "" }, -->
<div class="form-row">
<label for="node-input-HSVDimSpeed" style="width:260px">
<i class="fa fa-bolt"></i> Dim Speed (ms)
</label>
<input type="text" id="node-input-HSVDimSpeed" placeholder='Default is 5000' style="width:210px">
</div>
</p>
</div>
<div id="tabs-5">
<p>
<div class="form-tips" style="margin-bottom:6px;">
<i class="fa fa-circle-info" style="color:forestgreen; margin-right:4px;"></i>
<span data-i18n="knxUltimateHueLight.effect_base_label"></span>
</div>
<div class="form-row">
<label for="node-input-nameLightBlink" style="width:110px;"><i class="fa fa-play-circle-o"></i> Blink</label>
<label for="node-input-GALightBlink" style="width:20px;"><span
data-i18n="knxUltimateHueLight.node-input-GALightState"></span></label>
<input type="text" id="node-input-GALightBlink" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightBlink" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightBlink" style="width:140px;"></select>
<label for="node-input-nameLightBlink" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightBlink" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div id="divColorCycle">
<div class="form-row">
<label for="node-input-nameLightColorCycle" style="width:110px;"><i class="fa fa-play-circle-o"></i> Color
Cycle</label>
<label for="node-input-GALightColorCycle" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightColorCycle" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightColorCycle" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightColorCycle" style="width:140px;"></select>
<label for="node-input-nameLightColorCycle" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightColorCycle" style="width:190px;margin-left: 5px; text-align: left;">
</div>
</div>
<hr style="margin:12px 0;">
<div id="divHueEffectsContainer">
<div class="form-tips" style="margin-bottom:6px;">
<i class="fa fa-circle-info" style="color:forestgreen; margin-right:4px;"></i>
<span data-i18n="knxUltimateHueLight.effect_native_label"></span>
</div>
<div id="divHueEffectsNoSupport" class="form-tips" data-i18n="knxUltimateHueLight.effect_not_supported" style="display:none;"></div>
<div id="divHueEffectsContent" style="display:none;">
<div class="form-row">
<label for="node-input-nameLightEffect" style="width:110px;"><i class="fa fa-wand-magic-sparkles"></i> <span data-i18n="knxUltimateHueLight.effect_command"></span></label>
<label for="node-input-GALightEffect" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightEffect" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightEffect" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightEffect" style="width:140px;"></select>
<label for="node-input-nameLightEffect" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightEffect" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label for="node-input-nameLightEffectStatus" style="width:110px;"><i class="fa fa-circle-info"></i> <span data-i18n="knxUltimateHueLight.effect_status"></span></label>
<label for="node-input-GALightEffectStatus" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GALightEffectStatus" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptLightEffectStatus" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptLightEffectStatus" style="width:140px;"></select>
<label for="node-input-nameLightEffectStatus" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameLightEffectStatus" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row" id="divEffectMappings">
<label style="width:110px;"><i class="fa fa-list-check"></i> <span data-i18n="knxUltimateHueLight.effect_mapping"></span></label>
<div style="margin-left:5px; width: calc(100% - 120px);">
<input type="hidden" id="node-input-effectRules">
<ol id="node-input-effect-rule-container" style="margin:0; padding:0;"></ol>
<div style="margin-top:5px;">
<button type="button" id="node-input-effect-autofill" class="red-ui-button"><span data-i18n="knxUltimateHueLight.effect_autofill"></span></button>
</div>
<div class="form-tips">
<i class="fa fa-circle-info" style="color:forestgreen; margin-right:4px;"></i>
<span data-i18n="knxUltimateHueLight.effect_tip"></span>
</div>
<div class="form-tips">
<i class="fa fa-circle-info" style="color:forestgreen; margin-right:4px;"></i>
<span data-i18n="knxUltimateHueLight.effect_tip_status"></span>
</div>
</div>
</div>
</div>
</div>
</p>
</div>
<div id="tabs-6">
<p>
<div class="form-row">
<label style="width:260px;" for="node-input-readStatusAtStartup"><i class="fa fa-question-circle"></i> <span data-i18n="knxUltimateHueLight.read_status_startup"></span></label>
<select id="node-input-readStatusAtStartup">
<option value="no" data-i18n="knxUltimateHueLight.opt_no"></option>
<option value="yes" data-i18n="knxUltimateHueLight.opt_yes_emit"></option>
</select>
</div>
<div class="form-row" id="divUpdateKNXBrightnessStatusOnHUEOnOff">
<label style="width:260px;" for="node-input-updateKNXBrightnessStatusOnHUEOnOff">
<i class="fa fa-tag"></i> <span data-i18n="knxUltimateHueLight.knx_brightness_status"></span>
</label>
<select id="node-input-updateKNXBrightnessStatusOnHUEOnOff">
<option value="onhueoff" data-i18n="knxUltimateHueLight.knx_brightness_onhueoff"></option>
<option value="no" data-i18n="knxUltimateHueLight.knx_brightness_no"></option>
</select>
</div>
<div class="form-row">
<label style="width:260px;" for="node-input-updateLocalStateFromKNXWrite">
<i class="fa fa-database"></i> <span data-i18n="knxUltimateHueLight.update_local_state_from_knx_write"></span>
</label>
<input type="checkbox" id="node-input-updateLocalStateFromKNXWrite" style="display:inline-block; width:auto; vertical-align:top;" />
</div>
<div class="form-tips" style="margin-left:260px;" data-i18n="knxUltimateHueLight.update_local_state_from_knx_write_hint"></div><br/>
<div id ="divBehaviourBrightness">
<div class="form-row">
<label for="node-input-specifySwitchOnBrightness" style="width:260px;">
<i class="fa fa-tag"></i> <span data-i18n="knxUltimateHueLight.switch_on_behaviour"></span>
</label>
<select id="node-input-specifySwitchOnBrightness">
<option value="no" data-i18n="knxUltimateHueLight.none"></option>
<option value="temperature" data-i18n="knxUltimateHueLight.select_temperature_brightness"></option>
<option value="yes" data-i18n="knxUltimateHueLight.select_color"></option>
</select>
</div>
<div class="form-row" id="divColorsAtSwitchOn">
<label for="node-input-colorAtSwitchOnDayTime" style="width:260px">
</label>
<input type="hidden" id="node-input-colorAtSwitchOnDayTime">
<input type="color" id="colorPickerDay" style="width:260px">
<button id="getColorAtSwitchOnDayTimeButton" type="button" class="red-ui-button"><span data-i18n="knxUltimateHueLight.get_current"></span></button>
</div>
<div class="form-row" id="divTemperatureAtSwitchOn" hidden>
<label for="node-input-colorAtSwitchOnDayTime" style="width:260px">
</label>
<select style="width:12%;" id="comboTemperatureAtSwitchOn"></select>
<select style="width:25%;" id="comboBrightnessAtSwitchOn"></select>
</div>
<div id="divCCSBoxAtNightLighting">
<div class="form-row">
<label for="node-input-enableDayNightLighting" style="width:260px;">
<i class="fa fa-clone"></i> <span data-i18n="knxUltimateHueLight.night_lighting"></span>
</label>
<select id="node-input-enableDayNightLighting">
<option value="no" data-i18n="knxUltimateHueLight.no_night_lighting"></option>
<!-- <option value="temperature">Select temperature and brightness</option>
<option value="yes">Select color</option> -->
</select>
</div>
<div id="divEnableDayNightLighting">
<div class="form-row" id="divColorsAtSwitchOnNightTime">
<label for="node-input-colorAtSwitchOnNightTime" style="width:260px"></label>
<input type="hidden" id="node-input-colorAtSwitchOnNightTime">
<input type="color" id="colorPickerNight" style="width:260px">
<button id="getColorAtSwitchOnNightTimeButton" type="button" class="red-ui-button"><span data-i18n="knxUltimateHueLight.get_current"></span></button>
</div>
<div class="form-row" id="divTemperatureAtSwitchOnNightTime" hidden>
<label for="node-input-colorAtSwitchOnNightTime" style="width:260px"></label>
<select style="width:25%;" id="comboTemperatureAtSwitchOnNightTime"></select>
<select style="width:25%;" id="comboBrightnessAtSwitchOnNightTime"></select>
</div>
<div class="form-row">
<label for="node-input-nameDaylightSensor" style="width:110px;"><i class="fa fa-clock-o"></i>
<span data-i18n="knxUltimateHueLight.day_night"></span></label>
<label for="node-input-GADaylightSensor" style="width:20px;"><span data-i18n="common.ga"></span></label>
<input type="text" id="node-input-GADaylightSensor" placeholder="Ex: 1/1/1"
style="width:70px;margin-left: 5px; text-align: left;">
<label for="node-input-dptDaylightSensor" style="width:40px; margin-left: 0px; text-align: right;"><span data-i18n="common.dpt"></span></label>
<select id="node-input-dptDaylightSensor" style="width:140px;"></select>
<label for="node-input-nameDaylightSensor" style="width:50px; margin-left: 0px; text-align: right;"><span
data-i18n="knxUltimateHueLight.node-input-name"></span></label>
<input type="text" id="node-input-nameDaylightSensor" style="width:190px;margin-left: 5px; text-align: left;">
</div>
<div class="form-row">
<label style="width:170px" for="node-input-invertDayNight">
<i class="fa fa-shuffle"></i> <span data-i18n="knxUltimateHueLight.invert_day_night"></span>
</label>
<input type="checkbox" id="node-input-invertDayNight"
style="display:inline-block; width:auto; vertical-align:top;" />
</div>
<div class="form-row">
<label for="node-input-restoreDayMode" style="width:260px;">
<i class="fa fa-circle"></i> <span data-i18n="knxUltimateHueLight.override_night_mode"></span>
</label>
<select id="node-input-restoreDayMode">
<option value="no" data-i18n="knxUltimateHueLight.override_no"></option>
<option value="setDayByFastSwitchLightSingle" data-i18n="knxUltimateHueLight.override_set_day_fast_this"></option>
<option value="setDayByFastSwitchLightALL" data-i18n="knxUltimateHueLight.override_set_day_fast_all"></option>
</select>
</div>
</div>
</div>
<br/>
</div>
<div class="form-row">
<label for="node-input-enableNodePINS" style="width:260px;">
<i class="fa fa-circle"></i> <span data-i18n="knxUltimateHueLight.node_pins"></span>
</label>
<select id="node-input-enableNodePINS">
<option value="no" data-i18n="knxUltimateHueLight.node_pins_hide"></option>
<option value="yes" data-i18n="knxUltimateHueLight.node_pins_show"></option>
</select>
</div>
</p>
</div>
</div>
</div>
<br />