mirror of
https://github.com/HendrikRauh/dmx-interface.git
synced 2026-03-09 05:20:21 +00:00
chore(format): initial formatting
This commit is contained in:
parent
fa08fcfe65
commit
008c79852b
21 changed files with 1021 additions and 1082 deletions
607
data/index.html
607
data/index.html
|
|
@ -1,328 +1,313 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Konfiguration</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<script type="module" src="/input-visibility.js" defer></script>
|
||||
<script type="module" src="/loading-screen.js" defer></script>
|
||||
<script type="module" src="/load-data.js" defer></script>
|
||||
<script type="module" src="/networks.js" defer></script>
|
||||
<script type="module" src="/submit.js" defer></script>
|
||||
<script type="module" src="/reset.js" defer></script>
|
||||
<script type="module" src="/range-input.js" defer></script>
|
||||
<script type="module" src="/status.js" defer></script>
|
||||
<script type="module" src="/websocket.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section class="loading-screen">
|
||||
<div class="spinner-container">
|
||||
<!-- h2 is filled dynamically -->
|
||||
<h2></h2>
|
||||
<div class="spinner"></div>
|
||||
<button
|
||||
class="reload"
|
||||
type="button"
|
||||
onclick="window.location.reload()"
|
||||
>
|
||||
Seite neu laden
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Konfiguration</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<script type="module" src="/input-visibility.js" defer></script>
|
||||
<script type="module" src="/loading-screen.js" defer></script>
|
||||
<script type="module" src="/load-data.js" defer></script>
|
||||
<script type="module" src="/networks.js" defer></script>
|
||||
<script type="module" src="/submit.js" defer></script>
|
||||
<script type="module" src="/reset.js" defer></script>
|
||||
<script type="module" src="/range-input.js" defer></script>
|
||||
<script type="module" src="/status.js" defer></script>
|
||||
<script type="module" src="/websocket.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section class="loading-screen">
|
||||
<div class="spinner-container">
|
||||
<!-- h2 is filled dynamically -->
|
||||
<h2></h2>
|
||||
<div class="spinner"></div>
|
||||
<button
|
||||
class="reload"
|
||||
type="button"
|
||||
onclick="window.location.reload()"
|
||||
>
|
||||
Seite neu laden
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content hidden">
|
||||
<div class="status">
|
||||
<!-- placeholder for wifi icon -->
|
||||
<img class="connection-icon" src="" alt="" />
|
||||
<span>Temp.: <span class="cpu-temp"></span> °C</span>
|
||||
<span>Heap: <span class="heap-percentage"></span> %</span>
|
||||
<span>PSRAM: <span class="psram-percentage"></span> %</span>
|
||||
<span>Uptime: <span class="uptime"></span></span>
|
||||
<section class="content hidden">
|
||||
<div class="status">
|
||||
<!-- placeholder for wifi icon -->
|
||||
<img class="connection-icon" src="" alt="" />
|
||||
<span>Temp.: <span class="cpu-temp"></span> °C</span>
|
||||
<span>Heap: <span class="heap-percentage"></span> %</span>
|
||||
<span>PSRAM: <span class="psram-percentage"></span> %</span>
|
||||
<span>Uptime: <span class="uptime"></span></span>
|
||||
|
||||
<button type="button" class="expand-status icon-button">
|
||||
<img src="/icons/open.svg" alt="Mehr" />
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="expand-status icon-button">
|
||||
<img src="/icons/open.svg" alt="Mehr" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<dialog class="dialog-status">
|
||||
<div class="dialog-header">
|
||||
<h2 class="model"></h2>
|
||||
<form method="dialog">
|
||||
<button type="submit" class="icon-button">
|
||||
<img src="/icons/close.svg" alt="Schließen" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<dialog class="dialog-status">
|
||||
<div class="dialog-header">
|
||||
<h2 class="model"></h2>
|
||||
<form method="dialog">
|
||||
<button type="submit" class="icon-button">
|
||||
<img src="/icons/close.svg" alt="Schließen" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="dialog-status-content">
|
||||
<div class="card">
|
||||
<span>Signalstärke</span>
|
||||
<span class="centered-vertical">
|
||||
<img
|
||||
class="connection-icon small"
|
||||
src=""
|
||||
alt=""
|
||||
/>
|
||||
<span><span class="rssi"></span> dBm</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="dialog-status-content">
|
||||
<div class="card">
|
||||
<span>Signalstärke</span>
|
||||
<span class="centered-vertical">
|
||||
<img class="connection-icon small" src="" alt="" />
|
||||
<span><span class="rssi"></span> dBm</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>Uptime</span>
|
||||
<span class="uptime"></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>Uptime</span>
|
||||
<span class="uptime"></span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>CPU-Temperatur</span>
|
||||
<span><span class="cpu-temp"></span> °C</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>CPU-Temperatur</span>
|
||||
<span><span class="cpu-temp"></span> °C</span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>CPU Cycle Count</span>
|
||||
<span class="cpu-cycle-count"></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>CPU Cycle Count</span>
|
||||
<span class="cpu-cycle-count"></span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>Heap</span>
|
||||
<span><span class="heap-percentage"></span> %</span>
|
||||
<span
|
||||
><span class="heap-used"></span> /
|
||||
<span class="heap-total"></span
|
||||
></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>Heap</span>
|
||||
<span><span class="heap-percentage"></span> %</span>
|
||||
<span
|
||||
><span class="heap-used"></span> /
|
||||
<span class="heap-total"></span
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>PSRAM</span>
|
||||
<span
|
||||
><span class="psram-percentage"></span> %</span
|
||||
>
|
||||
<span
|
||||
><span class="psram-used"></span> /
|
||||
<span class="psram-total"></span
|
||||
></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>PSRAM</span>
|
||||
<span><span class="psram-percentage"></span> %</span>
|
||||
<span
|
||||
><span class="psram-used"></span> /
|
||||
<span class="psram-total"></span
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>CPU-Taktfrequenz</span>
|
||||
<span><span class="cpu-freq"></span> MHz</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>CPU-Taktfrequenz</span>
|
||||
<span><span class="cpu-freq"></span> MHz</span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>MAC-Adresse</span>
|
||||
<span class="mac"></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>MAC-Adresse</span>
|
||||
<span class="mac"></span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>Script-Hash</span>
|
||||
<span class="hash"></span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span>Script-Hash</span>
|
||||
<span class="hash"></span>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span>SDK-Version</span>
|
||||
<span class="sdk-version"></span>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
<div class="card">
|
||||
<span>SDK-Version</span>
|
||||
<span class="sdk-version"></span>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<form class="config">
|
||||
<h1>Konfiguration</h1>
|
||||
<fieldset>
|
||||
<legend>Verbindung</legend>
|
||||
<label>
|
||||
<span>IP-Zuweisung:</span>
|
||||
<select
|
||||
name="ip-method"
|
||||
id="input-ip-method"
|
||||
title="IP-"
|
||||
required
|
||||
>
|
||||
<option value="0">Statisch</option>
|
||||
<option value="1">DHCP</option>
|
||||
</select>
|
||||
</label>
|
||||
<div data-field="input-ip-method" data-values="0">
|
||||
<label>
|
||||
<span>IP-Adresse:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="ip"
|
||||
id="input-ip"
|
||||
placeholder="IP-Adresse"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Subnetzmaske:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="subnet"
|
||||
id="input-subnet"
|
||||
placeholder="Subnetzmaske"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Gateway:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="gateway"
|
||||
id="input-gateway"
|
||||
placeholder="Gateway"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label>
|
||||
<span>Verbindungsmethode:</span>
|
||||
<select
|
||||
name="connection"
|
||||
id="input-connection"
|
||||
title="Verbindung"
|
||||
required
|
||||
>
|
||||
<option value="0">WiFi-Station</option>
|
||||
<option value="1">WiFi-AccessPoint</option>
|
||||
<option value="2">Ethernet</option>
|
||||
</select>
|
||||
</label>
|
||||
<div data-field="input-connection" data-values="1">
|
||||
<label>
|
||||
<span>SSID:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="ssid"
|
||||
id="input-ssid"
|
||||
placeholder="SSID"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div data-field="input-connection" data-values="0">
|
||||
<label>
|
||||
<span>Netzwerk:</span>
|
||||
<select
|
||||
name="ssid"
|
||||
id="select-network"
|
||||
title="Netzwerk"
|
||||
required
|
||||
></select>
|
||||
<button
|
||||
type="button"
|
||||
id="refresh-networks"
|
||||
class="icon-button"
|
||||
>
|
||||
<img
|
||||
src="/icons/refresh.svg"
|
||||
alt="Neu laden"
|
||||
/>
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
<div data-field="input-connection" data-values="0|1">
|
||||
<label>
|
||||
<span>Password:</span>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
id="input-password"
|
||||
placeholder="Passwort"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Input/Output 1</legend>
|
||||
<label class="switch">
|
||||
<span>Output</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="direction-1"
|
||||
id="input-direction-1"
|
||||
data-value-not-checked="0"
|
||||
data-value-checked="1"
|
||||
/>
|
||||
<span class="slider"></span>
|
||||
<span>Input</span>
|
||||
</label>
|
||||
<label>
|
||||
ArtNet-Universe:
|
||||
<input
|
||||
type="number"
|
||||
name="universe-1"
|
||||
id="universe-1"
|
||||
placeholder="Universe"
|
||||
min="0"
|
||||
max="15"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Input/Output 2</legend>
|
||||
<label class="switch">
|
||||
<span>Output</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="direction-2"
|
||||
id="input-direction-2"
|
||||
data-value-not-checked="0"
|
||||
data-value-checked="1"
|
||||
/>
|
||||
<span class="slider"></span>
|
||||
<span>Input</span>
|
||||
</label>
|
||||
<label>
|
||||
ArtNet-Universe:
|
||||
<input
|
||||
type="number"
|
||||
name="universe-2"
|
||||
id="universe-2"
|
||||
placeholder="Universe"
|
||||
min="0"
|
||||
max="15"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Sonstiges</legend>
|
||||
<label>
|
||||
LED-Helligkeit
|
||||
<div>
|
||||
<input
|
||||
type="range"
|
||||
name="led-brightness"
|
||||
id="led-brightness"
|
||||
min="0"
|
||||
max="255"
|
||||
class="range"
|
||||
/>
|
||||
<span class="range-value"></span>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<span>Aktion bei Knopfdruck:</span>
|
||||
<select
|
||||
name="button-action"
|
||||
id="input-button-action"
|
||||
title="Aktion bei Knopfdruck"
|
||||
required
|
||||
>
|
||||
<option value="0">Nichts</option>
|
||||
<option value="1">
|
||||
Konfiguration zurücksetzen
|
||||
</option>
|
||||
<option value="2">Neustart</option>
|
||||
</select>
|
||||
</label>
|
||||
</fieldset>
|
||||
<form class="config">
|
||||
<h1>Konfiguration</h1>
|
||||
<fieldset>
|
||||
<legend>Verbindung</legend>
|
||||
<label>
|
||||
<span>IP-Zuweisung:</span>
|
||||
<select
|
||||
name="ip-method"
|
||||
id="input-ip-method"
|
||||
title="IP-"
|
||||
required
|
||||
>
|
||||
<option value="0">Statisch</option>
|
||||
<option value="1">DHCP</option>
|
||||
</select>
|
||||
</label>
|
||||
<div data-field="input-ip-method" data-values="0">
|
||||
<label>
|
||||
<span>IP-Adresse:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="ip"
|
||||
id="input-ip"
|
||||
placeholder="IP-Adresse"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Subnetzmaske:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="subnet"
|
||||
id="input-subnet"
|
||||
placeholder="Subnetzmaske"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Gateway:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="gateway"
|
||||
id="input-gateway"
|
||||
placeholder="Gateway"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label>
|
||||
<span>Verbindungsmethode:</span>
|
||||
<select
|
||||
name="connection"
|
||||
id="input-connection"
|
||||
title="Verbindung"
|
||||
required
|
||||
>
|
||||
<option value="0">WiFi-Station</option>
|
||||
<option value="1">WiFi-AccessPoint</option>
|
||||
<option value="2">Ethernet</option>
|
||||
</select>
|
||||
</label>
|
||||
<div data-field="input-connection" data-values="1">
|
||||
<label>
|
||||
<span>SSID:</span>
|
||||
<input
|
||||
type="text"
|
||||
name="ssid"
|
||||
id="input-ssid"
|
||||
placeholder="SSID"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div data-field="input-connection" data-values="0">
|
||||
<label>
|
||||
<span>Netzwerk:</span>
|
||||
<select
|
||||
name="ssid"
|
||||
id="select-network"
|
||||
title="Netzwerk"
|
||||
required
|
||||
></select>
|
||||
<button type="button" id="refresh-networks" class="icon-button">
|
||||
<img src="/icons/refresh.svg" alt="Neu laden" />
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
<div data-field="input-connection" data-values="0|1">
|
||||
<label>
|
||||
<span>Password:</span>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
id="input-password"
|
||||
placeholder="Passwort"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Input/Output 1</legend>
|
||||
<label class="switch">
|
||||
<span>Output</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="direction-1"
|
||||
id="input-direction-1"
|
||||
data-value-not-checked="0"
|
||||
data-value-checked="1"
|
||||
/>
|
||||
<span class="slider"></span>
|
||||
<span>Input</span>
|
||||
</label>
|
||||
<label>
|
||||
ArtNet-Universe:
|
||||
<input
|
||||
type="number"
|
||||
name="universe-1"
|
||||
id="universe-1"
|
||||
placeholder="Universe"
|
||||
min="0"
|
||||
max="15"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Input/Output 2</legend>
|
||||
<label class="switch">
|
||||
<span>Output</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="direction-2"
|
||||
id="input-direction-2"
|
||||
data-value-not-checked="0"
|
||||
data-value-checked="1"
|
||||
/>
|
||||
<span class="slider"></span>
|
||||
<span>Input</span>
|
||||
</label>
|
||||
<label>
|
||||
ArtNet-Universe:
|
||||
<input
|
||||
type="number"
|
||||
name="universe-2"
|
||||
id="universe-2"
|
||||
placeholder="Universe"
|
||||
min="0"
|
||||
max="15"
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Sonstiges</legend>
|
||||
<label>
|
||||
LED-Helligkeit
|
||||
<div>
|
||||
<input
|
||||
type="range"
|
||||
name="led-brightness"
|
||||
id="led-brightness"
|
||||
min="0"
|
||||
max="255"
|
||||
class="range"
|
||||
/>
|
||||
<span class="range-value"></span>
|
||||
</div>
|
||||
</label>
|
||||
<label>
|
||||
<span>Aktion bei Knopfdruck:</span>
|
||||
<select
|
||||
name="button-action"
|
||||
id="input-button-action"
|
||||
title="Aktion bei Knopfdruck"
|
||||
required
|
||||
>
|
||||
<option value="0">Nichts</option>
|
||||
<option value="1">Konfiguration zurücksetzen</option>
|
||||
<option value="2">Neustart</option>
|
||||
</select>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="reset">Zurücksetzen</button>
|
||||
<button type="submit">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
<div class="buttons">
|
||||
<button type="reset">Zurücksetzen</button>
|
||||
<button type="submit">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ const dynamicInputs = form.querySelectorAll("[data-field][data-values]");
|
|||
document.addEventListener("change", updateVisibility);
|
||||
|
||||
function updateVisibility() {
|
||||
dynamicInputs.forEach((element) => {
|
||||
const input = form.querySelector(`#${element.dataset.field}`);
|
||||
if (element.dataset.values.split("|").includes(input.value)) {
|
||||
element.classList.remove("hidden");
|
||||
element
|
||||
.querySelectorAll("input, select, button, textarea")
|
||||
.forEach((childInput) => (childInput.disabled = false));
|
||||
} else {
|
||||
element.classList.add("hidden");
|
||||
element
|
||||
.querySelectorAll("input, select, button, textarea")
|
||||
.forEach((childInput) => (childInput.disabled = true));
|
||||
}
|
||||
});
|
||||
dynamicInputs.forEach(element => {
|
||||
const input = form.querySelector(`#${element.dataset.field}`);
|
||||
if (element.dataset.values.split("|").includes(input.value)) {
|
||||
element.classList.remove("hidden");
|
||||
element
|
||||
.querySelectorAll("input, select, button, textarea")
|
||||
.forEach(childInput => (childInput.disabled = false));
|
||||
} else {
|
||||
element.classList.add("hidden");
|
||||
element
|
||||
.querySelectorAll("input, select, button, textarea")
|
||||
.forEach(childInput => (childInput.disabled = true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateVisibility();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
showLoadingScreen,
|
||||
showError,
|
||||
hideLoadingScreen,
|
||||
showLoadingScreen,
|
||||
showError,
|
||||
hideLoadingScreen,
|
||||
} from "./loading-screen.js";
|
||||
|
||||
const form = document.querySelector("form.config");
|
||||
|
|
@ -9,46 +9,46 @@ const form = document.querySelector("form.config");
|
|||
export let data = {};
|
||||
|
||||
export async function loadData(timeout = null) {
|
||||
const req = await fetch("/config", {
|
||||
method: "GET",
|
||||
signal: timeout !== null ? AbortSignal.timeout(timeout) : undefined,
|
||||
});
|
||||
if (!req.ok) {
|
||||
throw new Error(`Response status: ${req.status}`);
|
||||
}
|
||||
const req = await fetch("/config", {
|
||||
method: "GET",
|
||||
signal: timeout !== null ? AbortSignal.timeout(timeout) : undefined,
|
||||
});
|
||||
if (!req.ok) {
|
||||
throw new Error(`Response status: ${req.status}`);
|
||||
}
|
||||
|
||||
const json = await req.json();
|
||||
console.log(json);
|
||||
return json;
|
||||
const json = await req.json();
|
||||
console.log(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
export function writeDataToInput(data) {
|
||||
console.log("write data");
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
const element = document.querySelector(`[name=${key}]`);
|
||||
console.log(key, element);
|
||||
console.log("write data");
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
const element = document.querySelector(`[name=${key}]`);
|
||||
console.log(key, element);
|
||||
|
||||
if (element.type === "checkbox") {
|
||||
element.checked = value;
|
||||
} else {
|
||||
element.value = value;
|
||||
}
|
||||
|
||||
if (element.type === "range") {
|
||||
// update text next to the slider by sending an event
|
||||
element.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
}
|
||||
if (element.type === "checkbox") {
|
||||
element.checked = value;
|
||||
} else {
|
||||
element.value = value;
|
||||
}
|
||||
// send "change" event
|
||||
form.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
|
||||
if (element.type === "range") {
|
||||
// update text next to the slider by sending an event
|
||||
element.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
}
|
||||
}
|
||||
// send "change" event
|
||||
form.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
}
|
||||
|
||||
showLoadingScreen("Konfiguration wird geladen...");
|
||||
try {
|
||||
data = await loadData();
|
||||
hideLoadingScreen();
|
||||
writeDataToInput(data);
|
||||
data = await loadData();
|
||||
hideLoadingScreen();
|
||||
writeDataToInput(data);
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
showError("Die Konfiguration konnte nicht geladen werden.");
|
||||
console.log(error.message);
|
||||
showError("Die Konfiguration konnte nicht geladen werden.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,36 +5,36 @@ const spinner = loadingScreen.querySelector(".spinner");
|
|||
const reloadBtn = loadingScreen.querySelector(".reload");
|
||||
|
||||
export function showLoadingScreen(msg) {
|
||||
hide(content, reloadBtn);
|
||||
show(loadingScreen, spinner);
|
||||
loadingMsg.classList.remove("error");
|
||||
loadingMsg.textContent = msg;
|
||||
hide(content, reloadBtn);
|
||||
show(loadingScreen, spinner);
|
||||
loadingMsg.classList.remove("error");
|
||||
loadingMsg.textContent = msg;
|
||||
}
|
||||
|
||||
export function showError(msg) {
|
||||
showLoadingScreen(msg);
|
||||
loadingMsg.innerHTML +=
|
||||
"<br/>Stelle sicher, dass du mit dem DMX-Interface verbunden bist und die IP-Adresse stimmt.";
|
||||
show(reloadBtn);
|
||||
hide(spinner);
|
||||
loadingMsg.classList.add("error");
|
||||
showLoadingScreen(msg);
|
||||
loadingMsg.innerHTML +=
|
||||
"<br/>Stelle sicher, dass du mit dem DMX-Interface verbunden bist und die IP-Adresse stimmt.";
|
||||
show(reloadBtn);
|
||||
hide(spinner);
|
||||
loadingMsg.classList.add("error");
|
||||
}
|
||||
|
||||
export function hideLoadingScreen() {
|
||||
hide(loadingScreen, reloadBtn);
|
||||
show(content);
|
||||
loadingMsg.classList.remove("error");
|
||||
loadingMsg.textContent = "";
|
||||
hide(loadingScreen, reloadBtn);
|
||||
show(content);
|
||||
loadingMsg.classList.remove("error");
|
||||
loadingMsg.textContent = "";
|
||||
}
|
||||
|
||||
function show(...elements) {
|
||||
for (const element of elements) {
|
||||
element.classList.remove("hidden");
|
||||
}
|
||||
for (const element of elements) {
|
||||
element.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function hide(...elements) {
|
||||
for (const element of elements) {
|
||||
element.classList.add("hidden");
|
||||
}
|
||||
for (const element of elements) {
|
||||
element.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,67 +7,67 @@ const refreshIcon = refreshButton.querySelector("img");
|
|||
let isLoading = false;
|
||||
|
||||
refreshButton.addEventListener("click", async () => {
|
||||
// check if interface is in WiFi-AccessPoint mode
|
||||
if (data.connection == 1) {
|
||||
alert(
|
||||
"Beim WLAN-Scan wird die Verbindung hardwarebedingt kurzzeitig" +
|
||||
"unterbrochen.\n" +
|
||||
"Möglicherweise muss das Interface neu verbunden werden."
|
||||
);
|
||||
}
|
||||
updateNetworks();
|
||||
// check if interface is in WiFi-AccessPoint mode
|
||||
if (data.connection == 1) {
|
||||
alert(
|
||||
"Beim WLAN-Scan wird die Verbindung hardwarebedingt kurzzeitig" +
|
||||
"unterbrochen.\n" +
|
||||
"Möglicherweise muss das Interface neu verbunden werden."
|
||||
);
|
||||
}
|
||||
updateNetworks();
|
||||
});
|
||||
|
||||
// check if connected via WiFi-Station
|
||||
if (data.connection === 0) {
|
||||
// show currently connected WiFi
|
||||
insertNetworks([data.ssid]);
|
||||
// show currently connected WiFi
|
||||
insertNetworks([data.ssid]);
|
||||
}
|
||||
|
||||
function insertNetworks(networks) {
|
||||
networkDropdown.textContent = ""; // clear dropdown
|
||||
networkDropdown.textContent = ""; // clear dropdown
|
||||
|
||||
for (const ssid of networks) {
|
||||
const option = document.createElement("option");
|
||||
option.value = ssid;
|
||||
option.innerText = ssid;
|
||||
networkDropdown.appendChild(option);
|
||||
}
|
||||
for (const ssid of networks) {
|
||||
const option = document.createElement("option");
|
||||
option.value = ssid;
|
||||
option.innerText = ssid;
|
||||
networkDropdown.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadNetworks() {
|
||||
if (isLoading) return;
|
||||
if (isLoading) return;
|
||||
|
||||
isLoading = true;
|
||||
refreshButton.classList.remove("error-bg");
|
||||
refreshIcon.classList.add("spinning");
|
||||
isLoading = true;
|
||||
refreshButton.classList.remove("error-bg");
|
||||
refreshIcon.classList.add("spinning");
|
||||
|
||||
try {
|
||||
const res = await fetch("/networks", {
|
||||
method: "GET",
|
||||
});
|
||||
try {
|
||||
const res = await fetch("/networks", {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw Error(`response status: ${res.status}`);
|
||||
}
|
||||
|
||||
const networks = await res.json();
|
||||
|
||||
refreshIcon.classList.remove("spinning");
|
||||
isLoading = false;
|
||||
// remove duplicate values
|
||||
return Array.from(new Set(networks));
|
||||
} catch (e) {
|
||||
refreshIcon.classList.remove("spinning");
|
||||
refreshButton.classList.add("error-bg");
|
||||
isLoading = false;
|
||||
return [];
|
||||
if (!res.ok) {
|
||||
throw Error(`response status: ${res.status}`);
|
||||
}
|
||||
|
||||
const networks = await res.json();
|
||||
|
||||
refreshIcon.classList.remove("spinning");
|
||||
isLoading = false;
|
||||
// remove duplicate values
|
||||
return Array.from(new Set(networks));
|
||||
} catch (e) {
|
||||
refreshIcon.classList.remove("spinning");
|
||||
refreshButton.classList.add("error-bg");
|
||||
isLoading = false;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function updateNetworks() {
|
||||
const networks = await loadNetworks();
|
||||
if (networks) {
|
||||
insertNetworks(["", ...networks]);
|
||||
}
|
||||
const networks = await loadNetworks();
|
||||
if (networks) {
|
||||
insertNetworks(["", ...networks]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
document.querySelector("form.config").addEventListener("input", (event) => {
|
||||
if (event.target.classList.contains("range")) {
|
||||
updateValue(event.target);
|
||||
}
|
||||
document.querySelector("form.config").addEventListener("input", event => {
|
||||
if (event.target.classList.contains("range")) {
|
||||
updateValue(event.target);
|
||||
}
|
||||
});
|
||||
|
||||
function updateValue(slider) {
|
||||
const percentage = Math.round((slider.value / slider.max) * 100);
|
||||
slider.nextElementSibling.innerText = `${percentage}%`;
|
||||
const percentage = Math.round((slider.value / slider.max) * 100);
|
||||
slider.nextElementSibling.innerText = `${percentage}%`;
|
||||
}
|
||||
|
||||
document.querySelectorAll("input[type='range'].range").forEach((element) => {
|
||||
updateValue(element);
|
||||
document.querySelectorAll("input[type='range'].range").forEach(element => {
|
||||
updateValue(element);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ import { updateConfig } from "/submit.js";
|
|||
|
||||
const form = document.querySelector("form.config");
|
||||
|
||||
form.addEventListener("reset", async (event) => {
|
||||
event.preventDefault();
|
||||
form.addEventListener("reset", async event => {
|
||||
event.preventDefault();
|
||||
|
||||
const ok = confirm(
|
||||
"Sicher, dass du alle Einstellungen zurücksetzen möchtest?"
|
||||
);
|
||||
if (ok) {
|
||||
updateConfig({
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
const ok = confirm(
|
||||
"Sicher, dass du alle Einstellungen zurücksetzen möchtest?"
|
||||
);
|
||||
if (ok) {
|
||||
updateConfig({
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
135
data/status.js
135
data/status.js
|
|
@ -5,108 +5,105 @@ const statusDialog = document.querySelector(".dialog-status");
|
|||
const expandButton = document.querySelector(".expand-status");
|
||||
|
||||
expandButton.addEventListener("click", () => {
|
||||
statusDialog.showModal();
|
||||
statusDialog.showModal();
|
||||
});
|
||||
|
||||
registerCallback("status", setStatus);
|
||||
initWebSocket();
|
||||
|
||||
function setStatus(status) {
|
||||
setValue("model", status.chip.model);
|
||||
setValue("mac", formatMac(status.chip.mac));
|
||||
setValue("sdk-version", status.sdkVersion);
|
||||
setValue("model", status.chip.model);
|
||||
setValue("mac", formatMac(status.chip.mac));
|
||||
setValue("sdk-version", status.sdkVersion);
|
||||
|
||||
setValue("rssi", status.connection.signalStrength);
|
||||
const icon = selectConnectionIcon(status.connection.signalStrength);
|
||||
document.querySelectorAll(".connection-icon").forEach((img) => {
|
||||
img.src = `/icons/${icon}`;
|
||||
});
|
||||
setValue("rssi", status.connection.signalStrength);
|
||||
const icon = selectConnectionIcon(status.connection.signalStrength);
|
||||
document.querySelectorAll(".connection-icon").forEach(img => {
|
||||
img.src = `/icons/${icon}`;
|
||||
});
|
||||
|
||||
setValue("cpu-freq", status.chip.cpuFreqMHz);
|
||||
setValue("cpu-cycle-count", status.chip.cycleCount);
|
||||
setValue("cpu-temp", status.chip.tempC);
|
||||
setValue("cpu-freq", status.chip.cpuFreqMHz);
|
||||
setValue("cpu-cycle-count", status.chip.cycleCount);
|
||||
setValue("cpu-temp", status.chip.tempC);
|
||||
|
||||
const usedHeap = status.heap.total - status.heap.free;
|
||||
setValue("heap-used", formatBytes(usedHeap));
|
||||
setValue("heap-total", formatBytes(status.heap.total));
|
||||
setValue(
|
||||
"heap-percentage",
|
||||
Math.round((usedHeap / status.heap.total) * 100)
|
||||
);
|
||||
const usedHeap = status.heap.total - status.heap.free;
|
||||
setValue("heap-used", formatBytes(usedHeap));
|
||||
setValue("heap-total", formatBytes(status.heap.total));
|
||||
setValue("heap-percentage", Math.round((usedHeap / status.heap.total) * 100));
|
||||
|
||||
const usedPsram = status.psram.total - status.psram.free;
|
||||
setValue("psram-used", formatBytes(usedPsram));
|
||||
setValue("psram-total", formatBytes(status.psram.total));
|
||||
setValue(
|
||||
"psram-percentage",
|
||||
Math.round((usedPsram / status.psram.total) * 100)
|
||||
);
|
||||
const usedPsram = status.psram.total - status.psram.free;
|
||||
setValue("psram-used", formatBytes(usedPsram));
|
||||
setValue("psram-total", formatBytes(status.psram.total));
|
||||
setValue(
|
||||
"psram-percentage",
|
||||
Math.round((usedPsram / status.psram.total) * 100)
|
||||
);
|
||||
|
||||
setValue("uptime", parseDuration(status.uptime));
|
||||
setValue("uptime", parseDuration(status.uptime));
|
||||
|
||||
setValue("hash", parseHash(status.sketch.md5));
|
||||
setValue("hash", parseHash(status.sketch.md5));
|
||||
}
|
||||
|
||||
function setValue(className, value) {
|
||||
document.querySelectorAll("." + className).forEach((element) => {
|
||||
element.innerText = value;
|
||||
});
|
||||
document.querySelectorAll("." + className).forEach(element => {
|
||||
element.innerText = value;
|
||||
});
|
||||
}
|
||||
|
||||
function parseDuration(ms) {
|
||||
const date = new Date(ms);
|
||||
const time =
|
||||
date.getUTCHours().toString().padStart(2, "0") +
|
||||
":" +
|
||||
date.getUTCMinutes().toString().padStart(2, "0") +
|
||||
" h";
|
||||
const days = Math.floor(date.getTime() / (1000 * 60 * 60 * 24));
|
||||
const date = new Date(ms);
|
||||
const time =
|
||||
date.getUTCHours().toString().padStart(2, "0") +
|
||||
":" +
|
||||
date.getUTCMinutes().toString().padStart(2, "0") +
|
||||
" h";
|
||||
const days = Math.floor(date.getTime() / (1000 * 60 * 60 * 24));
|
||||
|
||||
return days !== 0 ? `${days} Tage, ${time}` : time;
|
||||
return days !== 0 ? `${days} Tage, ${time}` : time;
|
||||
}
|
||||
|
||||
function parseHash(hash) {
|
||||
return hash.toUpperCase().substring(0, 16);
|
||||
return hash.toUpperCase().substring(0, 16);
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
const units = ["Bytes", "KB", "MB", "GB"];
|
||||
const units = ["Bytes", "KB", "MB", "GB"];
|
||||
|
||||
let value = bytes;
|
||||
let index = 0;
|
||||
while (value >= 1000) {
|
||||
value /= 1000;
|
||||
index++;
|
||||
}
|
||||
let value = bytes;
|
||||
let index = 0;
|
||||
while (value >= 1000) {
|
||||
value /= 1000;
|
||||
index++;
|
||||
}
|
||||
|
||||
return `${Math.round(value * 10) / 10} ${units[index]}`;
|
||||
return `${Math.round(value * 10) / 10} ${units[index]}`;
|
||||
}
|
||||
|
||||
function formatMac(decimalMac) {
|
||||
const octets = decimalMac.toString(16).toUpperCase().match(/../g) || [];
|
||||
return octets.reverse().join(":");
|
||||
const octets = decimalMac.toString(16).toUpperCase().match(/../g) || [];
|
||||
return octets.reverse().join(":");
|
||||
}
|
||||
|
||||
function selectConnectionIcon(signalStrength) {
|
||||
// access point
|
||||
if (data.connection == 1) {
|
||||
return "hotspot.svg";
|
||||
}
|
||||
// access point
|
||||
if (data.connection == 1) {
|
||||
return "hotspot.svg";
|
||||
}
|
||||
|
||||
// ethernet
|
||||
if (data.connection == 2) {
|
||||
return "lan.svg";
|
||||
}
|
||||
// ethernet
|
||||
if (data.connection == 2) {
|
||||
return "lan.svg";
|
||||
}
|
||||
|
||||
// station
|
||||
if (signalStrength >= -50) {
|
||||
return "signal4.svg";
|
||||
}
|
||||
if (signalStrength >= -60) {
|
||||
return "signal3.svg";
|
||||
}
|
||||
if (signalStrength >= -70) {
|
||||
return "signal2.svg";
|
||||
}
|
||||
return "signal1.svg";
|
||||
// station
|
||||
if (signalStrength >= -50) {
|
||||
return "signal4.svg";
|
||||
}
|
||||
if (signalStrength >= -60) {
|
||||
return "signal3.svg";
|
||||
}
|
||||
if (signalStrength >= -70) {
|
||||
return "signal2.svg";
|
||||
}
|
||||
return "signal1.svg";
|
||||
}
|
||||
|
|
|
|||
352
data/style.css
352
data/style.css
|
|
@ -1,330 +1,330 @@
|
|||
:root {
|
||||
--color-primary: #087e8b;
|
||||
--color-on-primary: white;
|
||||
--color-background: #222;
|
||||
--color-surface: #333;
|
||||
--color-danger: #fa2b58;
|
||||
--appended-item-size: 2.5rem;
|
||||
--color-primary: #087e8b;
|
||||
--color-on-primary: white;
|
||||
--color-background: #222;
|
||||
--color-surface: #333;
|
||||
--color-danger: #fa2b58;
|
||||
--appended-item-size: 2.5rem;
|
||||
|
||||
color-scheme: dark;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: linear-gradient(to left, #065760, black, black, #065760);
|
||||
color: white;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: linear-gradient(to left, #065760, black, black, #065760);
|
||||
color: white;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: var(--color-background);
|
||||
max-width: 700px;
|
||||
padding: 8px max(5%, 8px);
|
||||
margin: 0 auto;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
background-color: var(--color-background);
|
||||
max-width: 700px;
|
||||
padding: 8px max(5%, 8px);
|
||||
margin: 0 auto;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
form > * {
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-radius: 8px;
|
||||
border-color: white;
|
||||
border-radius: 8px;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
display: block;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
label span {
|
||||
flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
label div {
|
||||
width: clamp(200px, 100%, 400px);
|
||||
width: clamp(200px, 100%, 400px);
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
background-color: var(--color-background);
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--color-background);
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
select:has(+ .icon-button),
|
||||
label div input[type="range"] {
|
||||
width: calc(clamp(200px, 100%, 400px) - var(--appended-item-size) - 8px);
|
||||
width: calc(clamp(200px, 100%, 400px) - var(--appended-item-size) - 8px);
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
accent-color: var(--color-primary);
|
||||
accent-color: var(--color-primary);
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
inset: none;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-on-primary);
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
inset: none;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-on-primary);
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
button[type="reset"] {
|
||||
background-color: var(--color-danger);
|
||||
background-color: var(--color-danger);
|
||||
}
|
||||
|
||||
:is(div:has(:is(input, select)), input, select, label)
|
||||
+ :is(div:has(:is(input, select)), input, select, label) {
|
||||
margin-top: 8px;
|
||||
+ :is(div:has(:is(input, select)), input, select, label) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
label.switch {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
label.switch input {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
label.switch .slider {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 1em;
|
||||
width: 2em;
|
||||
background-color: #444;
|
||||
border-radius: 1em;
|
||||
border: 4px solid #444;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 1em;
|
||||
width: 2em;
|
||||
background-color: #444;
|
||||
border-radius: 1em;
|
||||
border: 4px solid #444;
|
||||
}
|
||||
|
||||
label.switch .slider::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
top: 50%;
|
||||
background-color: white;
|
||||
transition: all 0.1s linear;
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
top: 50%;
|
||||
background-color: white;
|
||||
transition: all 0.1s linear;
|
||||
}
|
||||
|
||||
label.switch:active .slider::before {
|
||||
transform: scale(1.3);
|
||||
transform-origin: 50% 50%;
|
||||
transform: scale(1.3);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
label.switch input:not(:checked) + .slider::before {
|
||||
left: 0%;
|
||||
translate: 0 -50%;
|
||||
left: 0%;
|
||||
translate: 0 -50%;
|
||||
}
|
||||
|
||||
label.switch input:checked + .slider::before {
|
||||
left: 100%;
|
||||
translate: -100% -50%;
|
||||
left: 100%;
|
||||
translate: -100% -50%;
|
||||
}
|
||||
|
||||
dialog {
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
max-height: 80%;
|
||||
overflow: auto;
|
||||
background-color: var(--color-background);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
max-height: 80%;
|
||||
overflow: auto;
|
||||
background-color: var(--color-background);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
dialog::backdrop {
|
||||
background-color: #000a;
|
||||
background-color: #000a;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
& button {
|
||||
margin: 0;
|
||||
}
|
||||
& button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--color-surface);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-surface);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.card > * {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card > :first-child {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 8px;
|
||||
color: var(--color-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.loading-screen {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--color-danger);
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
.error-bg {
|
||||
background-color: var(--color-danger) !important;
|
||||
background-color: var(--color-danger) !important;
|
||||
}
|
||||
|
||||
button.reload {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.spinner-container {
|
||||
width: min(max-content, 100%);
|
||||
width: min(max-content, 100%);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: conic-gradient(transparent 150deg, var(--color-primary));
|
||||
--outer-diameter: 50px;
|
||||
width: var(--outer-diameter);
|
||||
height: var(--outer-diameter);
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: conic-gradient(transparent 150deg, var(--color-primary));
|
||||
--outer-diameter: 50px;
|
||||
width: var(--outer-diameter);
|
||||
height: var(--outer-diameter);
|
||||
border-radius: 50%;
|
||||
|
||||
animation-name: spin;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
animation-name: spin;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.spinner::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
display: block;
|
||||
--spinner-border: 5px;
|
||||
top: var(--spinner-border);
|
||||
left: var(--spinner-border);
|
||||
position: absolute;
|
||||
content: "";
|
||||
display: block;
|
||||
--spinner-border: 5px;
|
||||
top: var(--spinner-border);
|
||||
left: var(--spinner-border);
|
||||
|
||||
--inner-diameter: calc(var(--outer-diameter) - 2 * var(--spinner-border));
|
||||
width: var(--inner-diameter);
|
||||
height: var(--inner-diameter);
|
||||
--inner-diameter: calc(var(--outer-diameter) - 2 * var(--spinner-border));
|
||||
width: var(--inner-diameter);
|
||||
height: var(--inner-diameter);
|
||||
|
||||
background-color: var(--color-background);
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-background);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
padding: 8px;
|
||||
font-size: 0;
|
||||
width: var(--appended-item-size);
|
||||
height: var(--appended-item-size);
|
||||
padding: 8px;
|
||||
font-size: 0;
|
||||
width: var(--appended-item-size);
|
||||
height: var(--appended-item-size);
|
||||
}
|
||||
|
||||
.spinning {
|
||||
animation-name: spin;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
animation-name: spin;
|
||||
animation-duration: 0.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
div:has(.range-value) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.range-value {
|
||||
width: var(--appended-item-size);
|
||||
height: var(--appended-item-size);
|
||||
text-align: center;
|
||||
line-height: var(--appended-item-size);
|
||||
width: var(--appended-item-size);
|
||||
height: var(--appended-item-size);
|
||||
text-align: center;
|
||||
line-height: var(--appended-item-size);
|
||||
}
|
||||
|
||||
.status {
|
||||
background-color: var(--color-surface);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--color-surface);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.dialog-status-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.connection-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.connection-icon.small {
|
||||
padding: 0;
|
||||
height: 1em;
|
||||
padding: 0;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.centered-vertical {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
|
|
|||
104
data/submit.js
104
data/submit.js
|
|
@ -1,74 +1,74 @@
|
|||
import { loadData, writeDataToInput } from "./load-data.js";
|
||||
import {
|
||||
showLoadingScreen,
|
||||
hideLoadingScreen,
|
||||
showError,
|
||||
showLoadingScreen,
|
||||
hideLoadingScreen,
|
||||
showError,
|
||||
} from "./loading-screen.js";
|
||||
|
||||
const form = document.querySelector("form.config");
|
||||
|
||||
function parseValue(input) {
|
||||
if (input.type === "checkbox") {
|
||||
return input.checked
|
||||
? input.dataset.valueChecked
|
||||
: input.dataset.valueNotChecked;
|
||||
}
|
||||
if (input.type === "checkbox") {
|
||||
return input.checked
|
||||
? input.dataset.valueChecked
|
||||
: input.dataset.valueNotChecked;
|
||||
}
|
||||
|
||||
if (input.value === "") {
|
||||
return null;
|
||||
}
|
||||
if (input.value === "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.type === "number" || input.type === "range") {
|
||||
const number = Number(input.value);
|
||||
return Number.isNaN(number) ? null : number;
|
||||
}
|
||||
if (input.type === "number" || input.type === "range") {
|
||||
const number = Number(input.value);
|
||||
return Number.isNaN(number) ? null : number;
|
||||
}
|
||||
|
||||
return input.value;
|
||||
return input.value;
|
||||
}
|
||||
|
||||
form.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
const inputFields = document.querySelectorAll(
|
||||
"form :is(input, select, textarea):not(:disabled)"
|
||||
);
|
||||
form.addEventListener("submit", event => {
|
||||
event.preventDefault();
|
||||
const inputFields = document.querySelectorAll(
|
||||
"form :is(input, select, textarea):not(:disabled)"
|
||||
);
|
||||
|
||||
const data = Array.from(inputFields).reduce((data, input) => {
|
||||
data[input.name] = parseValue(input);
|
||||
return data;
|
||||
}, {});
|
||||
console.log(data);
|
||||
const data = Array.from(inputFields).reduce((data, input) => {
|
||||
data[input.name] = parseValue(input);
|
||||
return data;
|
||||
}, {});
|
||||
console.log(data);
|
||||
|
||||
updateConfig({
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
updateConfig({
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
});
|
||||
|
||||
export async function updateConfig(fetchOptions) {
|
||||
showLoadingScreen("Konfiguration anwenden und ESP neustarten...");
|
||||
showLoadingScreen("Konfiguration anwenden und ESP neustarten...");
|
||||
|
||||
try {
|
||||
const res = await fetch("/config", fetchOptions);
|
||||
if (!res.ok) {
|
||||
throw new Error(`Response status: ${res.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
showError(error.message);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
const res = await fetch("/config", fetchOptions);
|
||||
if (!res.ok) {
|
||||
throw new Error(`Response status: ${res.status}`);
|
||||
}
|
||||
const data = await loadData(5000);
|
||||
writeDataToInput(data);
|
||||
hideLoadingScreen();
|
||||
|
||||
break;
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
showError(error.message);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
const data = await loadData(5000);
|
||||
writeDataToInput(data);
|
||||
hideLoadingScreen();
|
||||
|
||||
break;
|
||||
} catch (error) {
|
||||
// retry loading config until successful
|
||||
}
|
||||
// retry loading config until successful
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,34 +4,34 @@ let ws;
|
|||
let callbacks = {};
|
||||
|
||||
export function initWebSocket() {
|
||||
if (ws) return;
|
||||
if (ws) return;
|
||||
|
||||
ws = new WebSocket(gateway);
|
||||
ws = new WebSocket(gateway);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.info("WebSocket connection opened");
|
||||
};
|
||||
ws.onopen = () => {
|
||||
console.info("WebSocket connection opened");
|
||||
};
|
||||
|
||||
ws.onclose = (event) => {
|
||||
console.info("WebSocket connection closed, reason:", event.reason);
|
||||
ws = null;
|
||||
};
|
||||
ws.onclose = event => {
|
||||
console.info("WebSocket connection closed, reason:", event.reason);
|
||||
ws = null;
|
||||
};
|
||||
|
||||
ws.onerror = (event) => {
|
||||
console.warn("WebSocket encountered error, closing socket.", event);
|
||||
ws.close();
|
||||
ws = null;
|
||||
};
|
||||
ws.onerror = event => {
|
||||
console.warn("WebSocket encountered error, closing socket.", event);
|
||||
ws.close();
|
||||
ws = null;
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const message = JSON.parse(event.data);
|
||||
console.log("received websocket data", message);
|
||||
if (message.type in callbacks) {
|
||||
callbacks[message.type](message.data);
|
||||
}
|
||||
};
|
||||
ws.onmessage = event => {
|
||||
const message = JSON.parse(event.data);
|
||||
console.log("received websocket data", message);
|
||||
if (message.type in callbacks) {
|
||||
callbacks[message.type](message.data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function registerCallback(type, callback) {
|
||||
callbacks[type] = callback;
|
||||
callbacks[type] = callback;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue