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.

366 lines (327 loc) 16.8 kB
<script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script> <script type="text/javascript"> RED.nodes.registerType('hue-config', { category: 'config', defaults: { host: { value: "" }, name: { value: "Hue Bridge" }, bridgeid: { value: "" } }, credentials: { username: { type: "password" }, clientkey: { type: "password" } }, oneditprepare: function () { // Go to the help panel try { RED.sidebar.show("help"); } catch (error) { } var node = this; var $hostInput = $("#node-config-input-host"); var $refreshHueBridges = $("#refreshHueBridges"); var $bridgeDatalist = $("#hue-bridge-hosts"); var discoveredHueBridges = []; var $usernameInput = $("#node-config-input-username"); var $clientKeyInput = $("#node-config-input-clientkey"); var $manualCredentialsButton = $("#manualCredentials"); function renderHueBridgeOptions (bridges) { discoveredHueBridges = Array.isArray(bridges) ? bridges : []; if ($bridgeDatalist.length) { $bridgeDatalist.empty(); discoveredHueBridges.forEach(function (bridge) { var ip = bridge.internalipaddress || bridge.ipaddress || bridge.ip || ""; if (!ip) return; var labelParts = [ip]; var bridgeId = bridge.id || bridge.bridgeid; if (bridgeId) { labelParts.push("(" + bridgeId + ")"); } $("<option>") .attr("value", ip) .text(labelParts.join(" ")) .appendTo($bridgeDatalist); }); } if ($hostInput.length && !$hostInput.val() && discoveredHueBridges.length > 0) { var firstBridge = null; for (var idx = 0; idx < discoveredHueBridges.length; idx++) { var candidate = discoveredHueBridges[idx]; if (candidate && candidate.internalipaddress) { firstBridge = candidate; break; } } if (!firstBridge) { firstBridge = discoveredHueBridges[0]; } var defaultIp = (firstBridge.internalipaddress || firstBridge.ipaddress || firstBridge.ip || ""); if (defaultIp) { $hostInput.val(defaultIp); } } } function runHueBridgeDiscovery (options) { var settings = options || {}; if ($refreshHueBridges.length) { $refreshHueBridges.addClass("fa-spin"); } $.getJSON("KNXUltimateDiscoverHueBridges", function (data) { if (data && !data.error) { renderHueBridgeOptions(data); } else if (data && data.error && !settings.silent) { RED.notify(data.error, { modal: false, fixed: false, type: "error" }); } }).fail(function (jqXHR, textStatus, errorThrown) { if (!settings.silent) { var message = errorThrown || textStatus || "Unknown error"; RED.notify("Hue bridge discovery failed: " + message, { modal: false, fixed: false, type: "error" }); } }).always(function () { if ($refreshHueBridges.length) { $refreshHueBridges.removeClass("fa-spin"); } }); } if ($refreshHueBridges.length) { $refreshHueBridges.on("click", function () { runHueBridgeDiscovery(); }); } if ($manualCredentialsButton.length) { $manualCredentialsButton.on("click", function () { $("#mainWindow").show(); $("#waitWindow").hide(); $("#divDetails").show(); if ($usernameInput.val() === "_PWRD_") $usernameInput.val(""); if ($clientKeyInput.val() === "_PWRD_") $clientKeyInput.val(""); try { $usernameInput.focus(); } catch (e) { } $manualCredentialsButton.hide(); }); } runHueBridgeDiscovery({ silent: true }); function loadHueCredentials () { if (!node || !node.id) return; $.getJSON("KNXUltimateGetPlainHueBridgeCredentials?serverId=" + node.id, function (data) { if (data && !data.error) { if (data.username) { $usernameInput.val(data.username); } else if ($usernameInput.val() === "_PWRD_") { $usernameInput.val(""); } if (data.clientkey) { $clientKeyInput.val(data.clientkey); } else if ($clientKeyInput.val() === "_PWRD_") { $clientKeyInput.val(""); } } }).fail(function () { if ($usernameInput.val() === "_PWRD_") $usernameInput.val(""); if ($clientKeyInput.val() === "_PWRD_") $clientKeyInput.val(""); }); } loadHueCredentials(); if (this.bridgeid === undefined || this.bridgeid === '') { $("#divDetails").hide(); if ($manualCredentialsButton.length) $manualCredentialsButton.show(); } else { $("#divDetails").show(); if ($manualCredentialsButton.length) $manualCredentialsButton.hide(); } // #region "CONNECTION TO THE BRIDGE" $("#getinfocam").click(function () { $.getJSON("KNXUltimateGetHueBridgeInfo?IP=" + $("#node-config-input-host").val() + "&serverId=" + node.id, (data) => { if (data.hasOwnProperty("error")) { RED.notify(JSON.stringify(data.error), { modal: true, fixed: false, type: 'error' }); return; } $("#node-config-input-bridgeid").val(data.bridgeid); $("#node-config-input-name").val(data.name); $("#mainWindow").hide(); $("#waitWindow").show(); var polling = true; var retryTimer = null; var inflightRequest = null; var myNotification; function cleanupPolling () { polling = false; if (retryTimer) { clearTimeout(retryTimer); retryTimer = null; } if (inflightRequest && typeof inflightRequest.abort === 'function') { try { inflightRequest.abort() } catch (e) { } } inflightRequest = null; } function restoreEditorView () { $("#mainWindow").show(); $("#waitWindow").hide(); } function scheduleRetry () { if (!polling) return; retryTimer = setTimeout(attemptRegistration, 2000); } function handleRegistrationSuccess (registration) { cleanupPolling(); if (registration && registration.bridge) { if (registration.bridge.data && registration.bridge.data.name) { $("#node-config-input-name").val(registration.bridge.data.name); } if (registration.bridge.data && registration.bridge.data.bridgeid) { $("#node-config-input-bridgeid").val(registration.bridge.data.bridgeid); } } if (registration && registration.user) { if (registration.user.username) $("#node-config-input-username").val(registration.user.username); if (registration.user.clientkey) $("#node-config-input-clientkey").val(registration.user.clientkey); } restoreEditorView(); $("#divDetails").show(); if ($manualCredentialsButton.length) $manualCredentialsButton.hide(); if (myNotification) myNotification.close(); } function handleRegistrationError (message, { retryAllowed = false } = {}) { if (retryAllowed) { scheduleRetry(); return; } cleanupPolling(); restoreEditorView(); if (myNotification) myNotification.close(); var displayMessage = message && String(message).trim(); if (!displayMessage) { displayMessage = RED._("hue-config.properties.registration_failed"); } RED.notify(displayMessage, { modal: false, fixed: false, type: 'error' }); } function attemptRegistration () { if (!polling) return; inflightRequest = $.getJSON("KNXUltimateRegisterToHueBridge?IP=" + $("#node-config-input-host").val() + "&serverId=" + node.id, function (data) { if (!polling) return; if (data && !data.error) { handleRegistrationSuccess(data); return; } var errorMessage = data && data.error ? data.error.toString() : ''; var lower = errorMessage.toLowerCase(); var shouldRetry = lower.includes('link button') || lower.includes('101'); handleRegistrationError(errorMessage || 'Unknown response from Hue bridge.', { retryAllowed: shouldRetry }); }).fail(function (jqXHR, textStatus, errorThrown) { if (!polling) return; var message = errorThrown || textStatus || RED._("hue-config.properties.registration_unreachable"); handleRegistrationError(message, { retryAllowed: true }); }).always(function () { inflightRequest = null; }); } var waitingMessage = RED._("hue-config.properties.link_button_wait"); myNotification = RED.notify(waitingMessage, { modal: true, fixed: true, type: 'info', buttons: [ { text: "CANCEL", click: function () { cleanupPolling(); restoreEditorView(); if (myNotification) myNotification.close(); } } ] }); attemptRegistration(); }).error(function (jqXHR, textStatus, errorThrown) { RED.notify("Something went wrong. Please check the bridge's IP.", { modal: false, fixed: false, type: 'error' }) return; }); }); //#endregion }, oneditsave: function () { // Return to the info tab try { RED.sidebar.show("info"); } catch (error) { } }, label: function () { return typeof this.name === undefined ? "Hue Bridge " + this.host : this.name + " " + this.host; } }); </script> <script type="text/html" data-template-name="hue-config"> <div id="waitWindow" hidden> <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"> <div class="form-row"> <b><span data-i18n="hue-config.properties.title"></span></b> <p align='center'> <img src='https://raw.githubusercontent.com/Supergiovane/node-red-contrib-knx-ultimate/master/img/huehub.jpg' width='30%'></p> <p><span data-i18n="hue-config.properties.caution"></span></p> </div> <div class="form-row"> <label for="node-config-input-host"> <i class="fa fa-server"></i> <span data-i18n="hue-config.properties.host"></span></label> <input type="text" id="node-config-input-host" data-i18n="[placeholder]hue-config.properties.host_placeholder" placeholder="Write here the HUE bridge's IP, then click CONNECT" list="hue-bridge-hosts"> <i id="refreshHueBridges" class="fa fa-refresh" style="cursor:pointer; margin-left:6px;" title="Refresh Hue bridge discovery"></i> <datalist id="hue-bridge-hosts"></datalist> </div> <div class="form-row" id="divConnectButton"> <label><i class="fa fa-sign-in"></i> <span data-i18n="hue-config.properties.getinfocam"></span></label> <input type="button" id="getinfocam" class="ui-button ui-corner-all ui-widget" style="background-color:#AEE1FF;width:150px" data-i18n="[value]hue-config.properties.connect" value="CONNECT"> <input type="button" id="manualCredentials" class="ui-button ui-corner-all ui-widget" style="background-color:#FFE4A7;width:200px;margin-left:8px" data-i18n="[value]hue-config.properties.manual_credentials" value="I ALREADY HAVE THE CREDENTIALS"> </div> <div id="divDetails" hidden> <div class="form-row"> <label for="node-config-input-name"> <i class="fa fa-tag"></i> <span data-i18n="hue-config.properties.node-config-input-name"></span> </label> <input type="text" id="node-config-input-name" style="margin-left:5px;"> </div> <div class="form-row"> <label for="node-config-input-bridgeid"> <i class="fa fa-tag"></i> <span data-i18n="hue-config.properties.bridge_id"></span> </label> <input type="text" id="node-config-input-bridgeid"> </div> <div class="form-row"> <label for="node-config-input-username"> <span data-i18n="hue-config.properties.username"></span></label> <input type="text" id="node-config-input-username" placeholder=""> </div> <div class="form-row"> <label for="node-config-input-clientkey"> <span data-i18n="hue-config.properties.client_key"></span></label> <input type="text" id="node-config-input-clientkey" placeholder=""> </div> </div> </div> </script>