Location Speichern geht? abrufen der gespeicherten location aus properties muss noch gemacht werden

This commit is contained in:
Carsten Graf
2025-06-11 22:52:30 +02:00
parent 22cc4fe99c
commit b6fa2d69e7
8 changed files with 319 additions and 14 deletions

View File

@@ -333,3 +333,84 @@
gap: 10px; gap: 10px;
} }
} }
.section select {
width: 100%;
padding: 12px 16px;
font-size: 16px;
font-family: inherit;
border: 2px solid #e1e5e9;
border-radius: 8px;
background-color: white;
background-image: url("data:image/svg+xml;charset=US-ASCII,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'><path fill='%23666' d='M2 0L0 2h4zm0 5L0 3h4z'/></svg>");
background-repeat: no-repeat;
background-position: right 12px center;
background-size: 12px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
transition: all 0.3s ease;
}
.section select:hover {
border-color: #007bff;
box-shadow: 0 2px 8px rgba(0, 123, 255, 0.1);
}
.section select:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
.section select:disabled {
background-color: #f8f9fa;
color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
border-color: #dee2e6;
}
.section select:disabled:hover {
border-color: #dee2e6;
box-shadow: none;
}
/* Option Styling */
.section select option {
padding: 8px;
font-size: 16px;
background-color: white;
color: #333;
}
.section select option:hover {
background-color: #f8f9fa;
}
.section select option:disabled {
color: #6c757d;
background-color: #f8f9fa;
}
/* Form Group für bessere Abstände */
.section .form-group {
margin-bottom: 20px;
}
.section .form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
font-size: 14px;
}
/* Responsive Design für kleinere Bildschirme */
@media (max-width: 768px) {
.section select {
font-size: 16px; /* Verhindert Zoom auf iOS */
padding: 14px 16px;
}
}

View File

@@ -174,6 +174,26 @@
</form> </form>
</div> </div>
<div class="section">
<h2>📍 Standort</h2>
<div id="locationRestrictionNotice" class="restriction-notice" style="display: none;">
🔒 Standort-Konfiguration ist nur mit Lizenz Level 3 oder höher verfügbar. Aktuelle Lizenz: Level <span id="currentLocationLicenseLevel">0</span>
</div>
<form id="locationForm">
<div class="form-group">
<label for="locationSelect">Standort auswählen:</label>
<select id="locationSelect" name="location" required>
<option value="">Bitte wählen...</option>
</select>
</div>
<div class="button-group">
<button type="submit" id="locationSubmitBtn" class="btn btn-primary">
💾 Standort speichern
</button>
</div>
</form>
</div>
<!-- OTA Update Section --> <!-- OTA Update Section -->
<div class="section"> <div class="section">
<h2>🔄 OTA Update</h2> <h2>🔄 OTA Update</h2>
@@ -251,6 +271,7 @@
updateCurrentTimeDisplay(); updateCurrentTimeDisplay();
loadLicence(); loadLicence();
loadWifiSettings(); loadWifiSettings();
loadLocations();
}; };
// Aktuelle Zeit anzeigen (Live-Update) // Aktuelle Zeit anzeigen (Live-Update)
@@ -429,6 +450,7 @@
// Check license level and update OTA button accordingly // Check license level and update OTA button accordingly
updateOTAButtonAccess(data.tier || 0); updateOTAButtonAccess(data.tier || 0);
updateWifiButtonAccess(data.tier || 0) updateWifiButtonAccess(data.tier || 0)
updateLocationAccess(data.tier || 0);
}) })
.catch((error) => console.log("Info konnte nicht geladen werden")); .catch((error) => console.log("Info konnte nicht geladen werden"));
} }
@@ -494,6 +516,9 @@
showMessage("Bitte WLAN-Namen eingeben", "error"); showMessage("Bitte WLAN-Namen eingeben", "error");
return; return;
} }
if (!confirm("Der Server wird nach dem setzten neu gestartet. Fortsetzten?")) {
return;
}
fetch("/api/set-wifi", { fetch("/api/set-wifi", {
method: "POST", method: "POST",
@@ -762,6 +787,113 @@
); );
} }
//location functions
// Locations laden und Dropdown befüllen
function loadLocations() {
fetch("/api/location/")
.then((response) => response.json())
.then((data) => {
const select = document.getElementById("locationSelect");
// Vorhandene Optionen löschen (außer der ersten "Bitte wählen...")
while (select.children.length > 1) {
select.removeChild(select.lastChild);
}
// Neue Optionen aus Backend-Response hinzufügen
data.forEach((location) => {
const option = document.createElement("option");
option.value = location.id;
option.textContent = location.name;
select.appendChild(option);
});
// Aktuell gespeicherten Standort laden
loadCurrentLocation();
})
.catch((error) => {
console.log("Locations konnten nicht geladen werden:", error);
showMessage("Fehler beim Laden der Standorte", "error");
});
}
// Aktuell gespeicherten Standort laden
function loadCurrentLocation() {
fetch("/api/get-location")
.then((response) => response.json())
.then((data) => {
if (data.locationId) {
document.getElementById("locationSelect").value = data.locationId;
}
})
.catch((error) => {
console.log("Aktueller Standort konnte nicht geladen werden:", error);
});
}
// Location-Zugriff basierend auf Lizenz-Level kontrollieren
function updateLocationAccess(licenseLevel) {
const locationSubmitBtn = document.getElementById("locationSubmitBtn");
const locationRestrictionNotice = document.getElementById("locationRestrictionNotice");
const locationCurrentLevelSpan = document.getElementById("currentLocationLicenseLevel");
const locationSelect = document.getElementById("locationSelect");
const level = parseInt(licenseLevel) || 0;
if (level >= 3) {
// Lizenz Level 3 oder höher - Form aktivieren
locationSubmitBtn.classList.remove("btn-disabled");
locationSubmitBtn.disabled = false;
locationSelect.disabled = false;
locationRestrictionNotice.style.display = "none";
} else {
// Lizenz Level unter 3 - Form deaktivieren
locationSubmitBtn.classList.add("btn-disabled");
locationSubmitBtn.disabled = true;
locationSelect.disabled = true;
locationRestrictionNotice.style.display = "block";
locationCurrentLevelSpan.textContent = level;
}
}
// Location Form Handler
document.getElementById("locationForm").addEventListener("submit", function(e) {
e.preventDefault();
const locationSubmitBtn = document.getElementById("locationSubmitBtn");
// Lizenz-Level prüfen
if (locationSubmitBtn.disabled || locationSubmitBtn.classList.contains("btn-disabled")) {
showMessage("Standort-Konfiguration erfordert Lizenz Level 3 oder höher", "error");
return;
}
const locationId = document.getElementById("locationSelect").value;
if (!locationId) {
showMessage("Bitte einen Standort auswählen", "error");
return;
}
// Standort an Backend senden
fetch("/api/set-location", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: "locationId=" + encodeURIComponent(locationId),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showMessage("Standort erfolgreich gespeichert!", "success");
} else {
showMessage("Fehler beim Speichern des Standorts", "error");
}
})
.catch((error) => showMessage("Verbindungsfehler", "error"));
});
// Status-Nachricht anzeigen // Status-Nachricht anzeigen
function showMessage(message, type) { function showMessage(message, type) {
const statusDiv = document.getElementById("statusMessage"); const statusDiv = document.getElementById("statusMessage");

View File

@@ -82,7 +82,7 @@ UserData checkUser(const String& uid) {
} }
//Function to enter user data into the database //Function to enter user data into the database
bool enterUserData(const String& uid, const String& firstname, const String& lastname, int alter) { bool enterUserData(const String& uid, const String& firstname, const String& lastname, const String& geburtsdatum, int alter) {
if (!backendOnline()) { if (!backendOnline()) {
Serial.println("No internet connection, cannot enter user data."); Serial.println("No internet connection, cannot enter user data.");
return false; return false;
@@ -98,6 +98,7 @@ bool enterUserData(const String& uid, const String& firstname, const String& las
requestDoc["uid"] = uid; requestDoc["uid"] = uid;
requestDoc["vorname"] = firstname; requestDoc["vorname"] = firstname;
requestDoc["nachname"] = lastname; requestDoc["nachname"] = lastname;
requestDoc["geburtsdatum"] = geburtsdatum;
requestDoc["alter"] = alter; requestDoc["alter"] = alter;
String requestBody; String requestBody;
@@ -116,13 +117,43 @@ bool enterUserData(const String& uid, const String& firstname, const String& las
} }
} }
JsonDocument getAllLocations() {
JsonDocument locations; // Allocate memory for the JSON document
if (!backendOnline()) {
Serial.println("No internet connection, cannot fetch locations.");
return locations; // Return an empty document
}
HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/api/location/");
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
DeserializationError error = deserializeJson(locations, payload);
if (error) {
Serial.println("Failed to parse locations JSON.");
}
} else {
Serial.printf("Failed to fetch locations, HTTP code: %d\n", httpCode);
}
http.end();
return locations; // Return the populated JSON document
}
// Keep this for backward compatibility // Keep this for backward compatibility
bool userExists(const String& uid) { bool userExists(const String& uid) {
return checkUser(uid).exists; return checkUser(uid).exists;
} }
//Routes from the Frontend into here and then into DB backend.
void setupBackendRoutes(AsyncWebServer& server) { void setupBackendRoutes(AsyncWebServer& server) {
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
@@ -142,6 +173,15 @@ void setupBackendRoutes(AsyncWebServer& server) {
// Handle user retrieval logic here // Handle user retrieval logic here
}); });
//Location routes /api/location/
server.on("/api/location/", HTTP_GET, [](AsyncWebServerRequest *request){
String result;
serializeJson(getAllLocations(), result);
request->send(200, "application/json", result);
});
// Add more routes as needed // Add more routes as needed
} }

View File

@@ -184,24 +184,35 @@ void saveWifiSettings() {
preferences.putString("ssid", ssidSTA); preferences.putString("ssid", ssidSTA);
preferences.putString("password", passwordSTA); preferences.putString("password", passwordSTA);
preferences.end(); preferences.end();
Serial.printf("WLAN-Einstellungen gespeichert: SSID=%s, Passwort=%s\n", ssidSTA, passwordSTA); delay(500); // Warte 2 Sekunden, bevor der Neustart erfolgt
ESP.restart(); // Neustart des ESP32
}
void loadLocationSettings() {
preferences.begin("location", true);
masterlocation = preferences.getString("location", "");
preferences.end();
}
void saveLocationSettings() {
preferences.begin("location", false);
preferences.putString("location", masterlocation);
preferences.end();
} }
void loadWifiSettings() { void loadWifiSettings() {
preferences.begin("wifi", true); preferences.begin("wifi", true);
// Neue Werte laden und dynamisch zuweisen
String ssid = preferences.getString("ssid", ""); String ssid = preferences.getString("ssid", "");
String password = preferences.getString("password", ""); String password = preferences.getString("password", "");
ssidSTA = strdup(ssid.c_str()); ssidSTA = strdup(ssid.c_str());
passwordSTA = strdup(password.c_str()); passwordSTA = strdup(password.c_str());
preferences.end(); preferences.end();
// Debug-Ausgabe
Serial.printf("WLAN-Einstellungen geladen: SSID=%s, Passwort=%s\n", ssidSTA, passwordSTA);
} }
int checkLicence() { int checkLicence() {
loadLicenceFromPrefs(); loadLicenceFromPrefs();
String id = getUniqueDeviceID(); String id = getUniqueDeviceID();

View File

@@ -50,6 +50,7 @@ int learningStep = 0; // 0=Start1, 1=Stop1, 2=Start2, 3=Stop2
unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default
unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms) unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms)
bool wifimodeAP = false; // AP-Modus deaktiviert bool wifimodeAP = false; // AP-Modus deaktiviert
String masterlocation;
//Function Declarations //Function Declarations
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len); void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len);
@@ -68,6 +69,8 @@ void saveSettings();
void loadSettings(); void loadSettings();
void loadWifiSettings(); void loadWifiSettings();
void saveWifiSettings(); void saveWifiSettings();
void loadLocationSettings();
void saveLocationSettings();
void unlearnButton(); void unlearnButton();
int checkLicence(); int checkLicence();
String getTimerDataJSON(); String getTimerDataJSON();

View File

@@ -200,10 +200,11 @@ server.on("/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {},
String uid = doc["uid"] | ""; String uid = doc["uid"] | "";
String vorname = doc["vorname"] | ""; String vorname = doc["vorname"] | "";
String nachname = doc["nachname"] | ""; String nachname = doc["nachname"] | "";
String geburtsdatum = doc["geburtsdatum"] | "";
int alter = doc["alter"] | 0; int alter = doc["alter"] | 0;
// Validate the data // Validate the data
if (uid.isEmpty() || vorname.isEmpty() || nachname.isEmpty() || alter <= 0) { if (uid.isEmpty() || vorname.isEmpty() || nachname.isEmpty() || geburtsdatum.isEmpty() || alter <= 0) {
Serial.println("Ungültige Eingabedaten"); Serial.println("Ungültige Eingabedaten");
response["success"] = false; response["success"] = false;
response["error"] = "Ungültige Eingabedaten"; response["error"] = "Ungültige Eingabedaten";
@@ -215,7 +216,7 @@ server.on("/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {},
Serial.println("Nachname: " + nachname); Serial.println("Nachname: " + nachname);
Serial.println("Alter: " + String(alter)); Serial.println("Alter: " + String(alter));
bool dbSuccess = enterUserData(uid, vorname, nachname, alter); bool dbSuccess = enterUserData(uid, vorname, nachname, geburtsdatum, alter);
if (dbSuccess) { if (dbSuccess) {
response["success"] = true; response["success"] = true;

View File

@@ -199,7 +199,6 @@ void setupRoutes(){
String result; String result;
serializeJson(doc, result); serializeJson(doc, result);
request->send(200, "application/json", result); request->send(200, "application/json", result);
Serial.println("WiFi-Settings updated (nur bis zum Neustart aktiv!)");
} else { } else {
request->send(400, "application/json", "{\"success\":false,\"error\":\"SSID fehlt\"}"); request->send(400, "application/json", "{\"success\":false,\"error\":\"SSID fehlt\"}");
} }
@@ -215,6 +214,42 @@ void setupRoutes(){
request->send(200, "application/json", result); request->send(200, "application/json", result);
}); });
server.on("/api/set-location", HTTP_POST, [](AsyncWebServerRequest *request) {
Serial.println("/api/set-location called");
String id, name;
if (request->hasParam("id", true)) {
id = request->getParam("id", true)->value();
}
if (request->hasParam("name", true)) {
name = request->getParam("name", true)->value();
}
masterlocation = name;
saveLocationSettings();
// Rückmeldung
DynamicJsonDocument doc(64);
doc["success"] = true;
String result;
serializeJson(doc, result);
request->send(200, "application/json", result);
});
server.on("/api/get-location", HTTP_GET, [](AsyncWebServerRequest *request){
DynamicJsonDocument doc(128);
doc["locationid"] = masterlocation ? masterlocation : "";
String result;
serializeJson(doc, result);
request->send(200, "application/json", result);
});
// Statische Dateien // Statische Dateien
server.serveStatic("/", SPIFFS, "/"); server.serveStatic("/", SPIFFS, "/");
server.begin(); server.begin();
@@ -222,6 +257,8 @@ void setupRoutes(){
} }
void setupWebSocket() { void setupWebSocket() {
ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_CONNECT) { if (type == WS_EVT_CONNECT) {

View File

@@ -23,7 +23,7 @@ void setupWifi() {
Serial.println("Access Point SSID: " + String(ssidSTA)); Serial.println("Access Point SSID: " + String(ssidSTA));
Serial.println("Access Point PW: " + String(passwordSTA)); Serial.println("Access Point PW: " + String(passwordSTA));
if ((ssidSTA == nullptr) || (passwordSTA == nullptr)) { if (ssidSTA == nullptr || passwordSTA == nullptr || String(ssidSTA).isEmpty() || String(passwordSTA).isEmpty() ) {
Serial.println("Fehler: ssidSTA oder passwordSTA ist null!"); Serial.println("Fehler: ssidSTA oder passwordSTA ist null!");
WiFi.mode(WIFI_MODE_AP); WiFi.mode(WIFI_MODE_AP);
WiFi.softAP(ssidAP, passwordAP); WiFi.softAP(ssidAP, passwordAP);