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 and ETS group address importer. Easy to use and highly configurable.

719 lines (673 loc) 29.8 kB
<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 () { let $tabs = null; let $requiresBridgeElems = null; let $knxSections = null; let $deviceName = null; let $refreshButton = null; let $loadingIndicator = null; let $outputInfo = null; let $toggleCheckbox = null; let $statusRows = null; let $fixedValueSection = null; let $switchSendInput = null; let $dimSendInput = null; let $dptShortRelease = null; let $dptShortReleaseStatus = null; let $dptRepeat = null; let $gaShortRelease = null; let $gaShortReleaseStatus = null; let $gaRepeat = null; let cachedDevices = []; let defaultDevicePlaceholder = ''; let showingNoDevicesPlaceholder = false; let currentNode = null; const EMPTY_SERVER_VALUES = new Set(['', 'none', '_add_', '__none__', '__null__', 'null', 'undefined']); const ensureVerticalTabsStyle = () => { if ($('#knxUltimateHueButtonVerticalTabs').length) return; const style = ` <style id="knxUltimateHueButtonVerticalTabs"> .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 144px; 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: nowrap; 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 { display: flex; flex-wrap: nowrap; align-items: center; gap: 4px; } .hue-vertical-tabs .form-row > dt { flex: 1 1 auto; margin: 0; } .hue-vertical-tabs .hue-form-tip { display: flex; align-items: center; gap: 6px; width: 100%; margin-left: 0 !important; max-width: none; color: #1b7d33; margin-bottom: 6px; padding: 6px 10px; box-sizing: border-box; } .hue-vertical-tabs .hue-form-tip .fa { color: forestgreen; flex: 0 0 auto; } .hue-vertical-tabs .hue-form-tip span { flex: 1 1 auto; min-width: 0; white-space: normal; } </style>`; $('head').append(style); }; const detachHandlers = () => { $('#node-input-server').off('.knxUltimateHueButton'); $('#node-input-serverHue').off('.knxUltimateHueButton'); if ($deviceName) { $deviceName.off('.knxUltimateHueButton'); if ($deviceName.data('ui-autocomplete')) { try { $deviceName.autocomplete('destroy'); } catch (error) { /* empty */ } } } if ($refreshButton) { $refreshButton.off('.knxUltimateHueButton'); } if ($toggleCheckbox) { $toggleCheckbox.off('.knxUltimateHueButton'); } const autocompleteTargets = [$gaShortRelease, $gaShortReleaseStatus, $gaRepeat]; autocompleteTargets.forEach(($input) => { if ($input) { $input.off('.knxUltimateHueButton'); if ($input.data('ui-autocomplete')) { try { $input.autocomplete('destroy'); } catch (error) { /* empty */ } } } }); if ($switchSendInput && $switchSendInput.data('typedInput')) { try { $switchSendInput.typedInput('destroy'); } catch (error) { /* empty */ } } if ($dimSendInput && $dimSendInput.data('typedInput')) { try { $dimSendInput.typedInput('destroy'); } catch (error) { /* empty */ } } }; const ensureConfigSelection = (selector) => { if ($(selector).val() !== '_ADD_') return; try { $(selector).prop('selectedIndex', 0); } catch (error) { /* empty */ } }; const resolveServerId = (value) => { if (value === undefined || value === null) return null; if (value === false) return null; if (typeof value === 'string') { const trimmed = value.trim(); if (trimmed === '') return null; if (EMPTY_SERVER_VALUES.has(trimmed.toLowerCase())) return null; return trimmed; } const asString = String(value).trim(); if (asString === '' || EMPTY_SERVER_VALUES.has(asString.toLowerCase())) return null; return value; }; const getKnxServer = (allowFallback = true) => { const resolved = resolveServerId($('#node-input-server').val()); if (resolved) return RED.nodes.node(resolved); if (!allowFallback) return null; const fallback = resolveServerId(currentNode ? currentNode.server : null); return fallback ? RED.nodes.node(fallback) : null; }; const getHueServer = (allowFallback = true) => { const resolved = resolveServerId($('#node-input-serverHue').val()); if (resolved) return RED.nodes.node(resolved); if (!allowFallback) return null; const fallback = resolveServerId(currentNode ? currentNode.serverHue : null); return fallback ? RED.nodes.node(fallback) : null; }; const hasKnxSelection = () => { const resolved = resolveServerId($('#node-input-server').val()); if (resolved) return true; if ($('#node-input-server').length) return false; return resolveServerId(currentNode ? currentNode.server : null) !== null; }; const hasHueSelection = () => { const resolved = resolveServerId($('#node-input-serverHue').val()); if (resolved) return true; if ($('#node-input-serverHue').length) return false; return resolveServerId(currentNode ? currentNode.serverHue : null) !== null; }; const applyNoDevicesPlaceholder = (hasDevices) => { if (!$deviceName) return; if (hasDevices) { if (showingNoDevicesPlaceholder) { showingNoDevicesPlaceholder = false; $deviceName.attr('placeholder', defaultDevicePlaceholder); } return; } const message = RED._('node-red-contrib-knx-ultimate/knxUltimateHueButton:knxUltimateHueButton.no_devices'); showingNoDevicesPlaceholder = true; $deviceName.attr('placeholder', message); if (($deviceName.val() || '').trim() === '') { $deviceName.val(''); } }; const filterDevices = (devices, term) => { const cleaned = (term || '').replace(/exactmatch/gi, '').trim(); return $.map(devices, (value) => { const sSearch = value.name; if (cleaned === '' || htmlUtilsfullCSVSearch(sSearch, cleaned)) { return { hueDevice: value.id, value: value.name, deviceObject: value.deviceObject || value, }; } return null; }); }; const fetchDevices = (hueServer, term, response, { forceRefresh = false } = {}) => { if (!hueServer) { applyNoDevicesPlaceholder(true); response([]); return; } if (!forceRefresh && cachedDevices.length > 0) { applyNoDevicesPlaceholder(cachedDevices.length > 0); response(filterDevices(cachedDevices, term)); return; } if ($loadingIndicator) $loadingIndicator.show(); $.getJSON(`KNXUltimateGetResourcesHUE?rtype=button&serverId=${hueServer.id}&_=${Date.now()}`, (data) => { const listCandidates = Array.isArray(data) ? data : (Array.isArray(data?.devices) ? data.devices : []); cachedDevices = listCandidates.map((value) => { if (value.deviceObject) return value; return { id: value.id || value.rid, name: value.name || value.metadata?.name || '', deviceObject: value, }; }); if (currentNode) currentNode._cachedButtonDevices = cachedDevices; applyNoDevicesPlaceholder(cachedDevices.length > 0); response(filterDevices(cachedDevices, term)); }).always(() => { if ($loadingIndicator) $loadingIndicator.hide(); }).fail(() => { cachedDevices = []; if (currentNode) currentNode._cachedButtonDevices = cachedDevices; applyNoDevicesPlaceholder(false); response([]); }); }; const loadDptOptions = (serverId, nodeRef) => { if (!$dptShortRelease || !$dptShortReleaseStatus || !$dptRepeat) return; $dptShortRelease.empty(); $dptShortReleaseStatus.empty(); $dptRepeat.empty(); const validId = resolveServerId(serverId); if (!validId) { return; } $.getJSON(`knxUltimateDpts?serverId=${validId}`, (data) => { const referenceNode = nodeRef || currentNode || {}; const targetShort = referenceNode.dptshort_release || '1.001'; const targetShortStatus = referenceNode.dptshort_releaseStatus || referenceNode.dptshort_release || '1.001'; const targetRepeat = referenceNode.dptrepeat || '3.007'; data.forEach((dpt) => { if (dpt.value.startsWith('1.')) { const option = $('<option></option>').attr('value', dpt.value).text(dpt.text); const optionStatus = option.clone(); $dptShortRelease.append(option); $dptShortReleaseStatus.append(optionStatus); } if (dpt.value.startsWith('3.007')) { $dptRepeat.append($('<option></option>').attr('value', dpt.value).text(dpt.text)); } }); if ($dptShortRelease.children().length) { $dptShortRelease.val(targetShort); } if ($dptShortReleaseStatus.children().length) { $dptShortReleaseStatus.val(targetShortStatus); } if ($dptRepeat.children().length) { $dptRepeat.val(targetRepeat); } }); }; const attachGroupAddressAutocomplete = ({ $input, $name, $dptSelect, filterFn }) => { if (!$input || !$input.length) return; $input.autocomplete({ minLength: 0, source(request, response) { const rawValue = $('#node-input-server').val(); const serverId = resolveServerId(rawValue === undefined ? (currentNode ? currentNode.server : null) : rawValue); const server = serverId ? RED.nodes.node(serverId) : null; if (!server) { response([]); return; } $.getJSON(`knxUltimatecsv?nodeID=${server.id}`, (data) => { const matches = []; data.forEach((value) => { if (filterFn && !filterFn(value)) return; const sSearch = `${value.ga} (${value.devicename}) DPT${value.dpt}`; if (htmlUtilsfullCSVSearch(sSearch, request.term)) { matches.push({ label: `${value.ga} # ${value.devicename} # ${value.dpt}`, value: value.ga, }); } }); response(matches); }); }, select(event, ui) { let sDevName = ui.item.label.split('#')[1]?.trim() || ''; try { sDevName = sDevName.substr(sDevName.indexOf(')') + 1).trim(); } catch (error) { /* empty */ } if ($name) $name.val(sDevName); if ($dptSelect) { const dptLabel = ui.item.label.split('#')[2]?.trim(); const optVal = dptLabel ? $dptSelect.find(`option:contains('${dptLabel}')`).attr('value') : undefined; if (optVal !== undefined && optVal !== null) { $dptSelect.val(optVal).trigger('change'); } else { $dptSelect.trigger('change'); } } }, }); $input.on('focus.knxUltimateHueButton', function () { $(this).autocomplete('search', `${$(this).val()}exactmatch`); }); try { const serverId = resolveServerId($('#node-input-server').val() || (currentNode ? currentNode.server : null)); const server = serverId ? RED.nodes.node(serverId) : null; if (server && server.id) KNX_enableSecureFormatting($input, server.id); } catch (error) { /* empty */ } }; const hasKNXServerSelected = () => { let domValue = $('#node-input-server').val(); if (domValue === undefined && currentNode) domValue = currentNode.server; const knxServerId = resolveServerId(domValue); return Boolean(knxServerId); }; const updateToggleSections = () => { const toggled = $toggleCheckbox && $toggleCheckbox.is(':checked'); if ($statusRows) { if (toggled) { $statusRows.show(); } else { $statusRows.hide(); } } if ($fixedValueSection) { if (toggled) { $fixedValueSection.hide(); } else { $fixedValueSection.show(); } } }; const updateTabsVisibility = () => { if (!$tabs) return; const hueDomValue = $('#node-input-serverHue').val(); const hueServerId = resolveServerId(hueDomValue === undefined ? (currentNode ? currentNode.serverHue : null) : hueDomValue); const knxSelected = hasKNXServerSelected(); if (hueServerId) { $requiresBridgeElems.show(); } else { $requiresBridgeElems.hide(); } if (hueServerId && knxSelected) { $tabs.show(); $tabs.tabs('refresh'); } else { $tabs.hide(); } if ($outputInfo) { if (knxSelected) { $outputInfo.hide(); } else { $outputInfo.show(); } } }; const updateKNXVisibility = () => { const knxSelected = hasKNXServerSelected(); if (knxSelected) { $knxSections.show(); } else { $knxSections.hide(); } updateTabsVisibility(); }; RED.nodes.registerType('knxUltimateHueButton', { category: 'KNX Ultimate', color: '#C0C7E9', defaults: { server: { type: 'knxUltimate-config', required: false }, serverHue: { type: 'hue-config', required: true }, name: { value: '' }, nameDim: { value: '' }, GArepeat: { value: '' }, dptrepeat: { value: '3.007' }, nameshort_release: { value: '' }, GAshort_release: { value: '' }, dptshort_release: { value: '1.001' }, nameshort_releaseStatus: { value: '' }, GAshort_releaseStatus: { value: '' }, dptshort_releaseStatus: { value: '1.001' }, toggleValues: { value: true }, hueDevice: { value: '' }, switchSend: { value: true }, dimSend: { value: 'up' }, }, inputs: 0, outputs: 1, icon: 'node-hue-icon.svg', label() { return this.name || 'Hue Button'; }, paletteLabel: 'Hue Button', oneditprepare() { try { RED.sidebar.show('help'); } catch (error) { /* empty */ } const node = this; currentNode = node; ensureConfigSelection('#node-input-serverHue'); ensureVerticalTabsStyle(); $tabs = $('#hue-button-tabs'); $requiresBridgeElems = $('.hue-requires-bridge'); $knxSections = $('.hue-knx-section'); $deviceName = $('#node-input-name'); $refreshButton = $('.hue-refresh-devices'); $loadingIndicator = $('.hue-devices-loading'); $outputInfo = $('.hue-output-info'); $toggleCheckbox = $('#node-input-toggleValues'); $statusRows = $('.hue-status-row'); $fixedValueSection = $('.hue-fixed-values'); $switchSendInput = $('#node-input-switchSend'); $dimSendInput = $('#node-input-dimSend'); $dptShortRelease = $('#node-input-dptshort_release'); $dptShortReleaseStatus = $('#node-input-dptshort_releaseStatus'); $dptRepeat = $('#node-input-dptrepeat'); $gaShortRelease = $('#node-input-GAshort_release'); $gaShortReleaseStatus = $('#node-input-GAshort_releaseStatus'); $gaRepeat = $('#node-input-GArepeat'); cachedDevices = Array.isArray(node._cachedButtonDevices) ? node._cachedButtonDevices : []; node._cachedButtonDevices = cachedDevices; defaultDevicePlaceholder = $deviceName.attr('placeholder') || ''; showingNoDevicesPlaceholder = false; $tabs.addClass('hue-vertical-tabs'); $tabs.tabs(); $tabs.find('li').removeClass('ui-corner-top').addClass('ui-corner-left'); const initialServerDomValue = $('#node-input-server').val(); const initialServerId = initialServerDomValue === undefined ? node.server : initialServerDomValue; loadDptOptions(initialServerId, node); attachGroupAddressAutocomplete({ $input: $gaShortRelease, $name: $('#node-input-nameshort_release'), $dptSelect: $dptShortRelease, filterFn: (value) => value.dpt && value.dpt.startsWith('1.'), }); attachGroupAddressAutocomplete({ $input: $gaShortReleaseStatus, $name: $('#node-input-nameshort_releaseStatus'), $dptSelect: $dptShortReleaseStatus, filterFn: (value) => value.dpt && value.dpt.startsWith('1.'), }); attachGroupAddressAutocomplete({ $input: $gaRepeat, $name: $('#node-input-nameDim'), $dptSelect: $dptRepeat, filterFn: (value) => value.dpt && value.dpt.startsWith('3.007'), }); if ($switchSendInput) { $switchSendInput.typedInput({ type: 'bool', types: ['bool'], }); const initialSwitch = node.switchSend === undefined ? true : node.switchSend; $switchSendInput.typedInput('value', initialSwitch ? 'true' : 'false'); } if ($dimSendInput) { $dimSendInput.typedInput({ type: 'direction', types: [{ value: 'direction', options: [ { value: 'up', label: RED._('node-red-contrib-knx-ultimate/knxUltimateHueButton:knxUltimateHueButton.dim_up') || 'Up' }, { value: 'down', label: RED._('node-red-contrib-knx-ultimate/knxUltimateHueButton:knxUltimateHueButton.dim_down') || 'Down' }, { value: 'stop', label: RED._('node-red-contrib-knx-ultimate/knxUltimateHueButton:knxUltimateHueButton.dim_stop') || 'Stop' }, ], }], }); const initialDim = typeof node.dimSend === 'string' ? node.dimSend : 'up'; $dimSendInput.typedInput('value', initialDim || 'up'); } const initialToggle = node.toggleValues !== false; if ($toggleCheckbox) { $toggleCheckbox.prop('checked', initialToggle); $toggleCheckbox.on('change.knxUltimateHueButton', () => { updateToggleSections(); }); } updateToggleSections(); if ($deviceName) { $deviceName.autocomplete({ minLength: 0, source(request, response) { const hueDomValue = $('#node-input-serverHue').val(); const hueServerId = resolveServerId(hueDomValue === undefined ? node.serverHue : hueDomValue); const hueServer = hueServerId ? RED.nodes.node(hueServerId) : null; if (!hueServer) { response([]); return; } fetchDevices(hueServer, request.term, response); }, select(event, ui) { $('#node-input-hueDevice').val(ui.item.hueDevice); }, }); $deviceName.on('focus.knxUltimateHueButton', function () { $(this).autocomplete('search', `${$(this).val()}exactmatch`); }); } if ($refreshButton) { $refreshButton.on('click.knxUltimateHueButton', () => { cachedDevices = []; node._cachedButtonDevices = cachedDevices; const hueDomValue = $('#node-input-serverHue').val(); const hueServerId = resolveServerId(hueDomValue === undefined ? node.serverHue : hueDomValue); const hueServer = hueServerId ? RED.nodes.node(hueServerId) : null; if (!hueServer) return; fetchDevices(hueServer, '', () => { if ($deviceName) { $deviceName.autocomplete('search', `${$deviceName.val()}exactmatch`); } }, { forceRefresh: true }); }); } $('#node-input-server').on('change.knxUltimateHueButton', function () { const serverId = $(this).val(); loadDptOptions(serverId, node); updateKNXVisibility(); }); $('#node-input-serverHue').on('change.knxUltimateHueButton', function () { const hueServerId = resolveServerId($(this).val()); cachedDevices = []; node._cachedButtonDevices = cachedDevices; if ($loadingIndicator) $loadingIndicator.hide(); showingNoDevicesPlaceholder = false; if ($deviceName) { $deviceName.attr('placeholder', defaultDevicePlaceholder); } if (!hueServerId) { applyNoDevicesPlaceholder(true); } updateTabsVisibility(); }); updateKNXVisibility(); }, oneditsave() { try { RED.sidebar.show('info'); } catch (error) { /* empty */ } detachHandlers(); cachedDevices = []; this._cachedButtonDevices = []; this.toggleValues = $toggleCheckbox ? $toggleCheckbox.is(':checked') : true; currentNode = null; }, oneditcancel() { try { RED.sidebar.show('info'); } catch (error) { /* empty */ } detachHandlers(); cachedDevices = []; this._cachedButtonDevices = []; currentNode = null; }, }); }()); </script> <script type="text/html" data-template-name="knxUltimateHueButton"> <div class="form-row"> <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" /> <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> <div class="form-row hue-requires-bridge"> <label for="node-input-name"> <i class="fa fa-tag"></i> <span data-i18n="knxUltimateHueButton.hue_sensor"></span> </label> <input type="text" id="node-input-name" placeholder="Hue button" 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;"> <i class="fa fa-sync"></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> </div> <div id="hue-button-tabs"> <ul> <li><a href="#hue-button-tab-switch"><i class="fa fa-toggle-on"></i> <span data-i18n="knxUltimateHueButton.tabs.switch"></span></a></li> <li><a href="#hue-button-tab-dim"><i class="fa fa-sun"></i> <span data-i18n="knxUltimateHueButton.tabs.dim"></span></a></li> <li><a href="#hue-button-tab-behaviour"><i class="fa fa-gear"></i> <span data-i18n="knxUltimateHueButton.tabs.behaviour"></span></a></li> </ul> <div id="hue-button-tab-switch"> <div class="form-tips hue-form-tip hue-knx-section"> <i class="fa fa-circle-info"></i> <span data-i18n="knxUltimateHueButton.switch_info"></span> </div> <div class="form-row hue-knx-section"> <label for="node-input-GAshort_release" style="width:70px;"><span data-i18n="common.ga"></span></label> <input type="text" id="node-input-GAshort_release" style="width:80px; text-align:left;" placeholder="1/1/1"> <label for="node-input-dptshort_release" style="width:40px; text-align:right;"><span data-i18n="common.dpt"></span></label> <select id="node-input-dptshort_release" style="width:120px;"></select> <label for="node-input-nameshort_release" style="width:50px; text-align:right;"><span data-i18n="common.name"></span></label> <input type="text" id="node-input-nameshort_release" style="flex:1 1 140px; min-width:120px; text-align:left;" placeholder="Switch action"> </div> <div class="form-row hue-knx-section hue-status-row"> <label for="node-input-GAshort_releaseStatus" style="width:70px;"><span data-i18n="knxUltimateHueButton.switch_status"></span></label> <input type="text" id="node-input-GAshort_releaseStatus" style="width:80px; text-align:left;" placeholder="1/1/2"> <label for="node-input-dptshort_releaseStatus" style="width:40px; text-align:right;"><span data-i18n="common.dpt"></span></label> <select id="node-input-dptshort_releaseStatus" style="width:120px;"></select> <label for="node-input-nameshort_releaseStatus" style="width:50px; text-align:right;"><span data-i18n="common.name"></span></label> <input type="text" id="node-input-nameshort_releaseStatus" style="flex:1 1 140px; min-width:120px; text-align:left;" placeholder="Switch status"> </div> </div> <div id="hue-button-tab-dim"> <div class="form-tips hue-form-tip hue-knx-section"> <i class="fa fa-circle-info"></i> <span data-i18n="knxUltimateHueButton.dim_info"></span> </div> <div class="form-row hue-knx-section"> <label for="node-input-GArepeat" style="width:70px;"><span data-i18n="common.ga"></span></label> <input type="text" id="node-input-GArepeat" style="width:80px; text-align:left;" placeholder="1/1/3"> <label for="node-input-dptrepeat" style="width:40px; text-align:right;"><span data-i18n="common.dpt"></span></label> <select id="node-input-dptrepeat" style="width:120px;"></select> <label for="node-input-nameDim" style="width:50px; text-align:right;"><span data-i18n="common.name"></span></label> <input type="text" id="node-input-nameDim" style="flex:1 1 140px; min-width:120px; text-align:left;" placeholder="Dim action"> </div> </div> <div id="hue-button-tab-behaviour"> <div class="form-tips hue-form-tip"> <i class="fa fa-circle-info"></i> <span data-i18n="knxUltimateHueButton.behaviour_info"></span> </div> <div class="form-row"> <input type="checkbox" id="node-input-toggleValues" style="width:auto;"> <label for="node-input-toggleValues" style="flex:1 1 auto;"> <span data-i18n="knxUltimateHueButton.toggle_values"></span> </label> </div> <div class="form-row hue-status-row" style="margin-left:24px;"> <span data-i18n="knxUltimateHueButton.toggle_values_hint"></span> </div> <div class="hue-fixed-values" style="margin-top:8px;"> <div class="form-row"> <label for="node-input-switchSend" style="width:130px;"><span data-i18n="knxUltimateHueButton.switch_send"></span></label> <input type="text" id="node-input-switchSend" style="width:160px;"> </div> <div class="form-row"> <label for="node-input-dimSend" style="width:130px;"><span data-i18n="knxUltimateHueButton.dim_send"></span></label> <input type="text" id="node-input-dimSend" style="width:160px;"> </div> </div> </div> </div> <div class="form-tips hue-form-tip hue-output-info" style="display:none;"> <i class="fa fa-circle-info"></i> <span data-i18n="knxUltimateHueButton.output_info"></span> </div> <input type="hidden" id="node-input-hueDevice"> </script>