Files
AquaMasterMQTT/data/settings.html
2025-06-05 17:38:34 +02:00

738 lines
25 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ninjacross Timer - Einstellungen</title>
<link rel="stylesheet" href="settings.css" />
</head>
<body>
<div class="container">
<div class="header">
<h1>⏱️ Ninjacross Timer</h1>
<p>Einstellungen & Konfiguration</p>
</div>
<div class="content">
<div class="nav-buttons">
<a href="/" class="nav-button">🏠 Hauptseite</a>
<a
href="/settings"
class="nav-button"
style="background: #667eea; color: white; border-color: #667eea"
>⚙️ Einstellungen</a
>
</div>
<div id="statusMessage" class="status-message"></div>
<div class="section">
<h2>🕐 Datum & Uhrzeit</h2>
<div class="current-time" id="currentTime">
Aktuelle Zeit: Laden...
</div>
<form id="timeForm">
<div class="time-row">
<div class="form-group time-input">
<label for="currentDate">Datum:</label>
<input type="date" id="currentDate" name="currentDate" />
</div>
<div class="form-group time-input">
<label for="currentTimeInput">Uhrzeit:</label>
<input
type="time"
id="currentTimeInput"
name="currentTime"
step="1"
/>
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-secondary">
🕐 Uhrzeit setzen
</button>
<button
type="button"
onclick="syncWithBrowserTime()"
class="btn btn-secondary"
>
💻 Browser-Zeit übernehmen
</button>
</div>
</form>
</div>
<div class="section">
<h2>🔧 Grundeinstellungen</h2>
<form id="settingsForm">
<div class="form-group">
<label for="maxTime">Maximale Zeit (Sekunden):</label>
<input
type="number"
id="maxTime"
name="maxTime"
min="10"
max="3600"
value="120"
title="Zeit nach der eine Bahn automatisch zurückgesetzt wird"
/>
</div>
<div class="form-group">
<label for="maxTimeDisplay"
>Wie lange bleibt die letzte Zeit stehen (Sekunden):</label
>
<input
type="number"
id="maxTimeDisplay"
name="maxTimeDisplay"
min="1"
max="3600"
value="20"
title="Zeit nach der die angezeigte Zeit zurückgesetzt wird"
/>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary">
💾 Einstellungen speichern
</button>
</div>
</form>
</div>
<div class="section">
<h2>🏆 Zeiten verwalten</h2>
<div class="button-group">
<button onclick="resetBestTimes()" class="btn btn-danger">
🔄 Beste Zeiten zurücksetzen
</button>
</div>
</div>
<div class="section">
<h2>📡 Button-Konfiguration</h2>
<div class="button-group">
<button onclick="startLearningMode()" class="btn btn-primary">
🎯 Anlernmodus starten
</button>
<button onclick="resetButtonAssign()" class="btn btn-danger">
❌ Buttons verlernen
</button>
<button onclick="showButtonStatus()" class="btn btn-primary">
📊 Button-Status anzeigen
</button>
</div>
<div id="learningMode" class="learning-mode">
<h3>🎯 Anlernmodus aktiv</h3>
<p id="learningInstruction" class="pulse">
Drücken Sie jetzt den Button für: <strong>Bahn 1 Start</strong>
</p>
<button onclick="cancelLearningMode()" class="btn btn-danger">
❌ Abbrechen
</button>
</div>
</div>
<div class="section" d="wifiSection" style="display: none;">
<h2>📡 WLAN-Konfiguration</h2>
<form id="wifiForm">
<div class="form-group">
<label for="wifi-ssid">WLAN Name (SSID):</label>
<input
type="text"
id="wifi-ssid"
name="ssid"
placeholder="WLAN-Name eingeben"
required
/>
</div>
<div class="form-group">
<label for="wifi-password">WLAN Passwort:</label>
<input
type="password"
id="wifi-password"
name="password"
placeholder="WLAN-Passwort eingeben"
/>
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary">
💾 WLAN-Einstellungen speichern
</button>
</div>
</form>
</div>
<div class="section">
<h2>🔄 OTA Update</h2>
<div id="otaRestrictionNotice" class="restriction-notice" style="display: none;">
🔒 OTA Updates sind nur mit Lizenz Level 2 oder höher verfügbar. Aktuelle Lizenz: Level <span id="currentLicenseLevel">0</span>
</div>
<div class="button-group">
<button
id="otaUpdateBtn"
onclick="performOTAUpdate()"
class="btn btn-danger"
>
🔄 Update durchführen
</button>
</div>
</div>
<div class="section">
<h2> System-Information</h2>
<div
id="systemInfo"
style="
background: white;
padding: 20px;
border-radius: 10px;
font-family: monospace;
font-size: 14px;
"
>
<div>IP-Adresse: <span id="ipAddress">Laden...</span></div>
<div>Kanal: <span id="channel">Laden...</span></div>
<div>MAC-Adresse: <span id="macAddress">Laden...</span></div>
<div>Freier Speicher: <span id="freeMemory">Laden...</span></div>
<div>Verbundene Buttons: <span id="connectedButtons">Laden...</span></div>
<div>Lizenz gültig: <span id="isLicenceValid">Laden...</span></div>
<div>Lizenz Level: <span id="licenceLevel">Laden...</span></div>
</div>
</div>
<div class="section">
<h2>🔧 Lizenz</h2>
<form id="licenceForm">
<div class="form-group">
<label for="licencekey">Lizensschlüssel:</label>
<input type="text" id="licencekey" name="licence" value="Key" />
</div>
<div class="button-group">
<button type="submit" class="btn btn-primary">
💾 Lizenz speichern
</button>
</div>
</form>
</div>
</div>
</div>
<script>
let learningStep = 0;
const learningSteps = [
"Bahn 1 Start",
"Bahn 1 Stop",
"Bahn 2 Start",
"Bahn 2 Stop",
];
// Einstellungen laden beim Seitenaufruf
window.onload = function () {
loadSettings();
loadSystemInfo();
loadCurrentTime();
updateCurrentTimeDisplay();
loadLicence();
loadWifiSettings();
};
// Aktuelle Zeit anzeigen (Live-Update)
function updateCurrentTimeDisplay() {
setInterval(() => {
fetch("/api/time")
.then((response) => response.json())
.then((data) => {
if (data.timestamp) {
const date = new Date(data.timestamp * 1000);
const timeString = date.toLocaleString("de-DE", {
weekday: "long",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
document.getElementById(
"currentTime"
).textContent = `System Zeit: ${timeString}`;
} else {
document.getElementById("currentTime").textContent =
"Aktuelle Zeit: Fehler beim Laden";
}
})
.catch((error) => {
document.getElementById("currentTime").textContent =
"Aktuelle Zeit: Fehler beim Laden";
});
}, 1000);
}
// Aktuelle Zeit vom Server laden
function loadCurrentTime() {
fetch("/api/time")
.then((response) => response.json())
.then((data) => {
if (data.timestamp) {
const date = new Date(data.timestamp * 1000);
document.getElementById("currentDate").value = date
.toISOString()
.split("T")[0];
document.getElementById("currentTimeInput").value = date
.toTimeString()
.split(" ")[0];
}
})
.catch((error) => {
console.log(
"Zeit konnte nicht geladen werden, verwende Browser-Zeit"
);
syncWithBrowserTime();
});
}
// Browser-Zeit in die Eingafelder übernehmen
function syncWithBrowserTime() {
const now = new Date();
document.getElementById("currentDate").value = now
.toISOString()
.split("T")[0];
document.getElementById("currentTimeInput").value = now
.toTimeString()
.split(" ")[0];
showMessage("Browser-Zeit übernommen", "info");
// Jetzt auch direkt an den Server senden:
const dateValue = document.getElementById("currentDate").value;
const timeValue = document.getElementById("currentTimeInput").value;
if (!dateValue || !timeValue) {
showMessage("Bitte Datum und Uhrzeit eingeben", "error");
return;
}
const datetime = new Date(`${dateValue}T${timeValue}`);
const timestamp = Math.floor(datetime.getTime() / 1000);
fetch("/api/set-time", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "timestamp=" + encodeURIComponent(timestamp),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage("Uhrzeit erfolgreich gesetzt!", "success");
} else {
showMessage("Fehler beim Setzen der Uhrzeit", "error");
}
})
.catch((error) =>
showMessage("Verbindungsfehler beim Setzen der Zeit", "error")
);
}
// Zeit setzen
document.getElementById("timeForm")
.addEventListener("submit", function (e) {
e.preventDefault();
const dateValue = document.getElementById("currentDate").value;
const timeValue = document.getElementById("currentTimeInput").value;
if (!dateValue || !timeValue) {
showMessage("Bitte Datum und Uhrzeit eingeben", "error");
return;
}
const datetime = new Date(`${dateValue}T${timeValue}`);
const timestamp = Math.floor(datetime.getTime() / 1000);
fetch("/api/set-time", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "timestamp=" + encodeURIComponent(timestamp),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage("Uhrzeit erfolgreich gesetzt!", "success");
} else {
showMessage("Fehler beim Setzen der Uhrzeit", "error");
}
})
.catch((error) =>
showMessage("Verbindungsfehler beim Setzen der Zeit", "error")
);
});
document.getElementById("licenceForm").addEventListener("submit", function(e) {
e.preventDefault();
saveLicence();
});
// Einstellungen laden
function loadSettings() {
fetch("/api/get-settings")
.then((response) => response.json())
.then((data) => {
document.getElementById("maxTime").value = data.maxTime || 300;
document.getElementById("maxTimeDisplay").value =
data.maxTimeDisplay || 20;
})
.catch((error) =>
showMessage("Fehler beim Laden der Einstellungen", "error")
);
}
// System-Informationen laden
function loadSystemInfo() {
fetch("/api/info")
.then((response) => response.json())
.then((data) => {
document.getElementById("ipAddress").textContent =
data.ip || "Unbekannt";
document.getElementById("channel").textContent =
data.channel || "Unbekannt";
document.getElementById("macAddress").textContent =
data.mac || "Unbekannt";
document.getElementById("freeMemory").textContent =
(data.freeMemory || 0) + " Bytes";
document.getElementById("connectedButtons").textContent =
data.connectedButtons || 0;
document.getElementById("isLicenceValid").textContent =
data.valid || "";
document.getElementById("licenceLevel").textContent =
data.tier || "";
// Check license level and update OTA button accordingly
updateOTAButtonAccess(data.tier || 0);
})
.catch((error) => console.log("Info konnte nicht geladen werden"));
}
function loadLicence() {
fetch("/api/get-licence")
.then((response) => response.json())
.then((data) => {
document.getElementById("licencekey").value = data.licence || "";
})
.catch((error) =>
showMessage("Fehler beim Laden der Lizenz", "error")
);
}
function saveLicence() {
const licence = document.getElementById("licencekey").value;
fetch("/api/set-licence", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "licence=" + encodeURIComponent(licence),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage("Lizenz erfolgreich gespeichert!", "success");
// Reload system info to update license level and OTA access
setTimeout(() => {
loadSystemInfo();
}, 1000);
} else {
showMessage("Fehler beim Speichern der Lizenz", "error");
}
})
.catch((error) => showMessage("Verbindungsfehler", "error"));
}
// WLAN-Einstellungen laden
function loadWifiSettings() {
fetch("/api/get-wifi")
.then(response => response.json())
.then(data => {
document.getElementById("wifi-ssid").value = data.ssid || "";
document.getElementById("wifi-password").value = data.password || "";
})
.catch(error => showMessage("Fehler beim Laden der WLAN-Einstellungen", "error"));
}
// WLAN-Einstellungen beim Laden der Seite abrufen
window.addEventListener('load', loadWifiSettings);
// WLAN-Formular Handler
document.getElementById("wifiForm").addEventListener("submit", function(e) {
e.preventDefault();
const ssid = document.getElementById("wifi-ssid").value;
const password = document.getElementById("wifi-password").value;
if (!ssid) {
showMessage("Bitte WLAN-Namen eingeben", "error");
return;
}
fetch("/api/set-wifi", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: `ssid=${encodeURIComponent(ssid)}&password=${encodeURIComponent(password)}`,
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMessage("WLAN-Einstellungen erfolgreich gespeichert!", "success");
} else {
showMessage(data.error || "Fehler beim Speichern der WLAN-Einstellungen", "error");
}
})
.catch(error => showMessage("Verbindungsfehler", "error"));
});
// Update OTA button access based on license level
function updateOTAButtonAccess(licenseLevel) {
const otaButton = document.getElementById("otaUpdateBtn");
const restrictionNotice = document.getElementById("otaRestrictionNotice");
const currentLevelSpan = document.getElementById("currentLicenseLevel");
const level = parseInt(licenseLevel) || 0;
if (level >= 2) {
// License level 2 or higher - enable OTA
otaButton.classList.remove("btn-disabled");
otaButton.disabled = false;
restrictionNotice.style.display = "none";
} else {
// License level below 2 - disable OTA
otaButton.classList.add("btn-disabled");
otaButton.disabled = true;
restrictionNotice.style.display = "block";
currentLevelSpan.textContent = level;
}
}
// OTA Update function with license check
function performOTAUpdate() {
const otaButton = document.getElementById("otaUpdateBtn");
if (otaButton.disabled || otaButton.classList.contains("btn-disabled")) {
showMessage("OTA Update erfordert Lizenz Level 2 oder höher", "error");
return;
}
if (confirm("Möchten Sie wirklich ein OTA Update durchführen? Das Gerät wird während des Updates neu gestartet.")) {
window.location.href = '/update';
}
}
// Einstellungen speichern
document.getElementById("settingsForm")
.addEventListener("submit", function (e) {
e.preventDefault();
const maxTime = parseInt(document.getElementById("maxTime").value);
const maxTimeDisplay = parseInt(
document.getElementById("maxTimeDisplay").value
);
fetch("/api/set-max-time", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body:
"maxTime=" +
encodeURIComponent(maxTime) +
"&maxTimeDisplay=" +
encodeURIComponent(maxTimeDisplay),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage(
"Einstellungen erfolgreich gespeichert!",
"success"
);
} else {
showMessage("Fehler beim Speichern der Einstellungen", "error");
}
})
.catch((error) => showMessage("Verbindungsfehler", "error"));
});
// Beste Zeiten zurücksetzen
function resetBestTimes() {
if (confirm("Möchten Sie wirklich alle besten Zeiten zurücksetzen?")) {
fetch("/api/reset-best", { method: "POST" })
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage(
"Beste Zeiten erfolgreich zurückgesetzt!",
"success"
);
} else {
showMessage("Fehler beim Zurücksetzen", "error");
}
})
.catch((error) => showMessage("Verbindungsfehler", "error"));
}
}
// Anlernmodus starten
function startLearningMode() {
learningStep = 0;
document.getElementById("learningMode").style.display = "block";
updateLearningInstruction();
fetch("/api/start-learning", { method: "POST" })
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage("Anlernmodus gestartet", "info");
pollLearningStatus();
} else {
showMessage("Fehler beim Starten des Anlernmodus", "error");
cancelLearningMode();
}
})
.catch((error) => {
showMessage("Verbindungsfehler", "error");
cancelLearningMode();
});
}
// Anlernmodus abbrechen
function cancelLearningMode() {
document.getElementById("learningMode").style.display = "none";
fetch("/api/stop-learning", { method: "POST" })
.then((response) => response.json())
.then((data) => {
showMessage("Anlernmodus abgebrochen", "info");
})
.catch((error) => console.log("Fehler beim Abbrechen"));
}
// Anlern-Anweisung aktualisieren
function updateLearningInstruction() {
if (learningStep < learningSteps.length) {
document.getElementById(
"learningInstruction"
).innerHTML = `Drücken Sie jetzt den Button für: <strong>${learningSteps[learningStep]}</strong>`;
}
}
// Anlern-Status abfragen
function pollLearningStatus() {
const pollInterval = setInterval(() => {
fetch("/api/learn/status")
.then((response) => response.json())
.then((data) => {
if (!data.active) {
clearInterval(pollInterval);
document.getElementById("learningMode").style.display = "none";
if (data.completed) {
showMessage("Alle Buttons erfolgreich angelernt!", "success");
}
return;
}
if (data.step !== learningStep) {
learningStep = data.step;
updateLearningInstruction();
if (learningStep > 0) {
showMessage(
`${learningSteps[learningStep - 1]} erfolgreich angelernt!`,
"success"
);
}
}
})
.catch((error) => {
clearInterval(pollInterval);
cancelLearningMode();
});
}, 1000);
}
function resetButtonAssign() {
if (
confirm("Möchten Sie wirklich alle Button-Zuweisungen zurücksetzen?")
) {
fetch("/api/unlearn-button", { method: "POST" })
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage(
"Button-Zuweisungen erfolgreich zurückgesetzt!",
"success"
);
loadSystemInfo();
} else {
showMessage(
"Fehler beim Zurücksetzen der Button-Zuweisungen",
"error"
);
}
})
.catch((error) => showMessage("Verbindungsfehler", "error"));
}
}
// Button-Status anzeigen
function showButtonStatus() {
fetch("/api/buttons/status")
.then((response) => response.json())
.then((data) => {
let statusText = "Button-Status:\n\n";
statusText += `Bahn 1 Start: ${
data.lane1Start ? "Konfiguriert" : "Nicht konfiguriert"
}\n`;
statusText += `Bahn 1 Stop: ${
data.lane1Stop ? "Konfiguriert" : "Nicht konfiguriert"
}\n`;
statusText += `Bahn 2 Start: ${
data.lane2Start ? "Konfiguriert" : "Nicht konfiguriert"
}\n`;
statusText += `Bahn 2 Stop: ${
data.lane2Stop ? "Konfiguriert" : "Nicht konfiguriert"
}\n`;
alert(statusText);
})
.catch((error) =>
showMessage("Fehler beim Laden des Button-Status", "error")
);
}
// Status-Nachricht anzeigen
function showMessage(message, type) {
const statusDiv = document.getElementById("statusMessage");
statusDiv.textContent = message;
statusDiv.className = `status-message status-${type}`;
statusDiv.style.display = "block";
setTimeout(() => {
statusDiv.style.display = "none";
}, 5000);
}
// System-Info alle 30 Sekunden aktualisieren
setInterval(loadSystemInfo, 30000);
</script>
</body>
</html>