Location Speichern geht? abrufen der gespeicherten location aus properties muss noch gemacht werden
This commit is contained in:
@@ -333,3 +333,84 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -174,6 +174,26 @@
|
||||
</form>
|
||||
</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 -->
|
||||
<div class="section">
|
||||
<h2>🔄 OTA Update</h2>
|
||||
@@ -251,6 +271,7 @@
|
||||
updateCurrentTimeDisplay();
|
||||
loadLicence();
|
||||
loadWifiSettings();
|
||||
loadLocations();
|
||||
};
|
||||
|
||||
// Aktuelle Zeit anzeigen (Live-Update)
|
||||
@@ -429,6 +450,7 @@
|
||||
// Check license level and update OTA button accordingly
|
||||
updateOTAButtonAccess(data.tier || 0);
|
||||
updateWifiButtonAccess(data.tier || 0)
|
||||
updateLocationAccess(data.tier || 0);
|
||||
})
|
||||
.catch((error) => console.log("Info konnte nicht geladen werden"));
|
||||
}
|
||||
@@ -494,6 +516,9 @@
|
||||
showMessage("Bitte WLAN-Namen eingeben", "error");
|
||||
return;
|
||||
}
|
||||
if (!confirm("Der Server wird nach dem setzten neu gestartet. Fortsetzten?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch("/api/set-wifi", {
|
||||
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
|
||||
function showMessage(message, type) {
|
||||
const statusDiv = document.getElementById("statusMessage");
|
||||
|
||||
@@ -82,7 +82,7 @@ UserData checkUser(const String& uid) {
|
||||
}
|
||||
|
||||
//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()) {
|
||||
Serial.println("No internet connection, cannot enter user data.");
|
||||
return false;
|
||||
@@ -98,6 +98,7 @@ bool enterUserData(const String& uid, const String& firstname, const String& las
|
||||
requestDoc["uid"] = uid;
|
||||
requestDoc["vorname"] = firstname;
|
||||
requestDoc["nachname"] = lastname;
|
||||
requestDoc["geburtsdatum"] = geburtsdatum;
|
||||
requestDoc["alter"] = alter;
|
||||
|
||||
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
|
||||
bool userExists(const String& uid) {
|
||||
return checkUser(uid).exists;
|
||||
}
|
||||
|
||||
|
||||
//Routes from the Frontend into here and then into DB backend.
|
||||
|
||||
void setupBackendRoutes(AsyncWebServer& server) {
|
||||
|
||||
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
@@ -142,6 +173,15 @@ void setupBackendRoutes(AsyncWebServer& server) {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -184,24 +184,35 @@ void saveWifiSettings() {
|
||||
preferences.putString("ssid", ssidSTA);
|
||||
preferences.putString("password", passwordSTA);
|
||||
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() {
|
||||
preferences.begin("wifi", true);
|
||||
|
||||
// Neue Werte laden und dynamisch zuweisen
|
||||
String ssid = preferences.getString("ssid", "");
|
||||
String password = preferences.getString("password", "");
|
||||
ssidSTA = strdup(ssid.c_str());
|
||||
passwordSTA = strdup(password.c_str());
|
||||
|
||||
preferences.end();
|
||||
|
||||
// Debug-Ausgabe
|
||||
Serial.printf("WLAN-Einstellungen geladen: SSID=%s, Passwort=%s\n", ssidSTA, passwordSTA);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int checkLicence() {
|
||||
loadLicenceFromPrefs();
|
||||
String id = getUniqueDeviceID();
|
||||
|
||||
@@ -50,6 +50,7 @@ int learningStep = 0; // 0=Start1, 1=Stop1, 2=Start2, 3=Stop2
|
||||
unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default
|
||||
unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms)
|
||||
bool wifimodeAP = false; // AP-Modus deaktiviert
|
||||
String masterlocation;
|
||||
|
||||
//Function Declarations
|
||||
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len);
|
||||
@@ -68,6 +69,8 @@ void saveSettings();
|
||||
void loadSettings();
|
||||
void loadWifiSettings();
|
||||
void saveWifiSettings();
|
||||
void loadLocationSettings();
|
||||
void saveLocationSettings();
|
||||
void unlearnButton();
|
||||
int checkLicence();
|
||||
String getTimerDataJSON();
|
||||
@@ -200,10 +200,11 @@ server.on("/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {},
|
||||
String uid = doc["uid"] | "";
|
||||
String vorname = doc["vorname"] | "";
|
||||
String nachname = doc["nachname"] | "";
|
||||
String geburtsdatum = doc["geburtsdatum"] | "";
|
||||
int alter = doc["alter"] | 0;
|
||||
|
||||
// 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");
|
||||
response["success"] = false;
|
||||
response["error"] = "Ungültige Eingabedaten";
|
||||
@@ -215,7 +216,7 @@ server.on("/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {},
|
||||
Serial.println("Nachname: " + nachname);
|
||||
Serial.println("Alter: " + String(alter));
|
||||
|
||||
bool dbSuccess = enterUserData(uid, vorname, nachname, alter);
|
||||
bool dbSuccess = enterUserData(uid, vorname, nachname, geburtsdatum, alter);
|
||||
|
||||
if (dbSuccess) {
|
||||
response["success"] = true;
|
||||
|
||||
@@ -199,7 +199,6 @@ void setupRoutes(){
|
||||
String result;
|
||||
serializeJson(doc, result);
|
||||
request->send(200, "application/json", result);
|
||||
Serial.println("WiFi-Settings updated (nur bis zum Neustart aktiv!)");
|
||||
} else {
|
||||
request->send(400, "application/json", "{\"success\":false,\"error\":\"SSID fehlt\"}");
|
||||
}
|
||||
@@ -215,6 +214,42 @@ void setupRoutes(){
|
||||
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
|
||||
server.serveStatic("/", SPIFFS, "/");
|
||||
server.begin();
|
||||
@@ -222,6 +257,8 @@ void setupRoutes(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void setupWebSocket() {
|
||||
ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
|
||||
@@ -23,7 +23,7 @@ void setupWifi() {
|
||||
Serial.println("Access Point SSID: " + String(ssidSTA));
|
||||
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!");
|
||||
WiFi.mode(WIFI_MODE_AP);
|
||||
WiFi.softAP(ssidAP, passwordAP);
|
||||
|
||||
Reference in New Issue
Block a user