Files
AquaMasterMQTT/src/timesync.h
2025-09-20 01:04:00 +02:00

241 lines
7.3 KiB
C

// Zeit-bezogene Variablen und Includes
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESPAsyncWebServer.h>
#include <sys/time.h>
#include <time.h>
// Globale Zeitvariablen
struct timeval tv;
struct timezone tz;
time_t now;
struct tm timeinfo;
// Initialisiert die Zeit-API und richtet die HTTP-Endpunkte ein.
void setupTimeAPI(AsyncWebServer &server);
// Gibt die aktuelle Zeit als JSON-String zurück.
String getCurrentTimeJSON();
// Setzt die Systemzeit auf den angegebenen Zeitstempel (Sekunden seit 1970).
bool setSystemTime(long timestamp);
// Hilfsfunktion: Gibt die aktuelle Zeit als JSON-String zurück.
String getCurrentTimeJSON() {
gettimeofday(&tv, &tz);
now = tv.tv_sec;
StaticJsonDocument<200> doc;
doc["timestamp"] = (long)now;
doc["success"] = true;
// Zusätzliche Zeitinformationen
gmtime_r(&now, &timeinfo);
char timeStr[64];
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &timeinfo);
doc["formatted"] = String(timeStr);
doc["year"] = timeinfo.tm_year + 1900;
doc["month"] = timeinfo.tm_mon + 1;
doc["day"] = timeinfo.tm_mday;
doc["hour"] = timeinfo.tm_hour;
doc["minute"] = timeinfo.tm_min;
doc["second"] = timeinfo.tm_sec;
String response;
serializeJson(doc, response);
return response;
}
void syncTimeWithNTP(const char *ntpServer = "pool.ntp.org",
long gmtOffset_sec = 3600, int daylightOffset_sec = 0) {
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Serial.println("Warte auf NTP-Zeit (max 5s)...");
struct tm timeinfo;
unsigned long start = millis();
bool synced = false;
while (millis() - start < 5000) {
if (getLocalTime(&timeinfo, 10)) { // 10ms Timeout pro Versuch
synced = true;
break;
}
delay(10); // Kurze Pause, damit der Loop nicht blockiert
}
if (synced) {
Serial.println("\nNTP-Zeit synchronisiert!");
Serial.printf("Aktuelle Zeit: %02d:%02d:%02d\n", timeinfo.tm_hour,
timeinfo.tm_min, timeinfo.tm_sec);
} else {
Serial.println("\nNTP-Sync fehlgeschlagen (Timeout nach 5s)");
}
}
// Hilfsfunktion: Setzt die Systemzeit auf den angegebenen Zeitstempel.
bool setSystemTime(long timestamp) {
struct timeval tv;
tv.tv_sec = timestamp;
tv.tv_usec = 0;
if (settimeofday(&tv, NULL) == 0) {
Serial.println("Zeit erfolgreich gesetzt: " + String(timestamp));
return true;
} else {
Serial.println("Fehler beim Setzen der Zeit");
return false;
}
}
// Initialisiert die Zeit-API und richtet die HTTP-Endpunkte ein.
void setupTimeAPI(AsyncWebServer &server) {
// API-Endpunkt: Aktuelle Zeit abrufen
server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request) {
String response = getCurrentTimeJSON();
request->send(200, "application/json", response);
});
// API-Endpunkt: Zeit setzen
server.on("/api/set-time", HTTP_POST, [](AsyncWebServerRequest *request) {
StaticJsonDocument<100> doc;
if (request->hasParam("timestamp", true)) {
String timestampStr = request->getParam("timestamp", true)->value();
long timestamp = timestampStr.toInt();
if (timestamp > 0) {
bool success = setSystemTime(timestamp);
doc["success"] = success;
if (success) {
doc["message"] = "Zeit erfolgreich gesetzt";
doc["timestamp"] = timestamp;
} else {
doc["message"] = "Fehler beim Setzen der Zeit";
}
} else {
doc["success"] = false;
doc["message"] = "Ungültiger Timestamp";
}
} else {
doc["success"] = false;
doc["message"] = "Timestamp-Parameter fehlt";
}
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// Alternative Implementierung für manuelle Datum/Zeit-Eingabe
server.on("/api/set-datetime", HTTP_POST, [](AsyncWebServerRequest *request) {
StaticJsonDocument<150> doc;
if (request->hasParam("year", true) && request->hasParam("month", true) &&
request->hasParam("day", true) && request->hasParam("hour", true) &&
request->hasParam("minute", true) &&
request->hasParam("second", true)) {
struct tm timeinfo;
timeinfo.tm_year =
request->getParam("year", true)->value().toInt() - 1900;
timeinfo.tm_mon = request->getParam("month", true)->value().toInt() - 1;
timeinfo.tm_mday = request->getParam("day", true)->value().toInt();
timeinfo.tm_hour = request->getParam("hour", true)->value().toInt();
timeinfo.tm_min = request->getParam("minute", true)->value().toInt();
timeinfo.tm_sec = request->getParam("second", true)->value().toInt();
time_t timestamp = mktime(&timeinfo);
if (timestamp != -1) {
bool success = setSystemTime(timestamp);
doc["success"] = success;
if (success) {
doc["message"] = "Zeit erfolgreich gesetzt";
doc["timestamp"] = (long)timestamp;
} else {
doc["message"] = "Fehler beim Setzen der Zeit";
}
} else {
doc["success"] = false;
doc["message"] = "Ungültiges Datum/Zeit";
}
} else {
doc["success"] = false;
doc["message"] = "Datum/Zeit-Parameter fehlen";
}
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// Erweiterte Zeit-Informationen (optional)
server.on("/api/time/info", HTTP_GET, [](AsyncWebServerRequest *request) {
gettimeofday(&tv, &tz);
now = tv.tv_sec;
gmtime_r(&now, &timeinfo);
StaticJsonDocument<400> doc;
doc["timestamp"] = (long)now;
doc["uptime"] = millis();
// Formatierte Zeitstrings
char buffer[64];
strftime(buffer, sizeof(buffer), "%Y-%m-%d", &timeinfo);
doc["date"] = String(buffer);
strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
doc["time"] = String(buffer);
strftime(buffer, sizeof(buffer), "%A", &timeinfo);
doc["weekday"] = String(buffer);
strftime(buffer, sizeof(buffer), "%B", &timeinfo);
doc["month_name"] = String(buffer);
// Zusätzliche Infos
doc["day_of_year"] = timeinfo.tm_yday + 1;
doc["week_of_year"] = (timeinfo.tm_yday + 7 - timeinfo.tm_wday) / 7;
doc["is_dst"] = timeinfo.tm_isdst;
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
Serial.println("Zeit-API initialisiert");
}
// Hilfsfunktion: Zeit-Validierung
// Prüft, ob die übergebenen Datums- und Zeitwerte gültig sind.
bool isValidDateTime(int year, int month, int day, int hour, int minute,
int second) {
if (year < 2020 || year > 2099)
return false;
if (month < 1 || month > 12)
return false;
if (day < 1 || day > 31)
return false;
if (hour < 0 || hour > 23)
return false;
if (minute < 0 || minute > 59)
return false;
if (second < 0 || second > 59)
return false;
// Erweiterte Validierung für Monatstage
int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Schaltjahr-Prüfung
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
daysInMonth[1] = 29;
}
return day <= daysInMonth[month - 1];
}
// Gibt den aktuellen Zeitstempel in Millisekunden seit 1970 zurück.
uint64_t getCurrentTimestampMs() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000LL + (uint64_t)tv.tv_usec / 1000LL;
}