UNPKG

node-red-contrib-knx-ultimate

Version:

Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control, ETS group address importer, and KNX routing between interfaces. Easy to use and highly configurable.

2,370 lines 120 kB
'use strict'

<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/11f26b4500.js"></script>

<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>

<script type="text/javascript">
  (function () {

    RED.nodes.registerType("knxUltimateHueLight", {
      category: "KNX Ultimate 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: '&nbsp;&rarr;&nbsp;', 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>&nbsp&nbsp<span style="color:red" &nbsp &nbsp<i class="fa fa-youtube"></i></span>&nbsp<a
        target="_blank" href="https://youtu.be/jjEUI1J8bkA"><u><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 />