Statuswoerter NTP (gruen), RTC (gelb, blinkt bei leerer Batterie) und ESP (rot), per Weboberflaeche schaltbar

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
lukarioandClaude Sonnet 5.5 committed 2026-10-19 20:10:21 +02:00
1 parent 20fc9092e6
commit 76d6ee900c
2 files changed
+206 -30

No files matched your search

+103 -15
View File
@@ -28,8 +28,9 @@ RTC_DS1307 rtc;
#define RTC_SDA D2
#define RTC_SCL D1
bool rtcOk = false;
bool rtcBatteryEmpty = false; // RTC hat beim Start die Zeit verloren
volatile bool ntpJustSynced = false;
void onTimeSet(){ ntpJustSynced = true; }
void onTimeSet(bool fromSntp){ if(fromSntp) ntpJustSynced = true; }
// ================= LED =================
#define LED_PIN D4
@@ -48,7 +49,7 @@ String ntpServer = DEFAULT_NTP;
String tzString = DEFAULT_TZ;
// ================= SETTINGS =================
#define SETTINGS_MAGIC 0x57554852UL // erkennt gueltige EEPROM-Daten
#define SETTINGS_MAGIC 0x57554853UL // erkennt gueltige EEPROM-Daten
struct Settings {
uint32_t magic;
@@ -61,6 +62,8 @@ struct Settings {
uint32_t bgColor;
bool backgroundMode;
bool nightBackgroundDisabled;
bool statusLeds;
int ntpShowSec;
char ntpServer[64];
char tzString[64];
} settings;
@@ -70,6 +73,8 @@ uint32_t currentColor = 0x00FFFF;
uint32_t bgColor = 0x000000;
bool backgroundMode = false;
bool nightBackgroundDisabled = false;
bool statusLeds = true; // Statuswoerter NTP/RTC/ESP anzeigen
int ntpShowSec = 10; // Mindestdauer der gruenen NTP-Anzeige
bool forceInstant = false;
bool updateNeeded = true;
int brightness = 120;
@@ -99,6 +104,8 @@ void saveSettings(){
settings.bgColor = bgColor;
settings.backgroundMode = backgroundMode;
settings.nightBackgroundDisabled = nightBackgroundDisabled;
settings.statusLeds = statusLeds;
settings.ntpShowSec = ntpShowSec;
strncpy(settings.ntpServer, ntpServer.c_str(), sizeof(settings.ntpServer) - 1);
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
strncpy(settings.tzString, tzString.c_str(), sizeof(settings.tzString) - 1);
@@ -120,6 +127,8 @@ void loadSettings(){
bgColor = settings.bgColor & 0xFFFFFF;
backgroundMode = settings.backgroundMode;
nightBackgroundDisabled = settings.nightBackgroundDisabled;
statusLeds = settings.statusLeds;
ntpShowSec = constrain(settings.ntpShowSec, 1, 60);
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
settings.tzString[sizeof(settings.tzString) - 1] = '\0';
ntpServer = String(settings.ntpServer);
@@ -135,8 +144,15 @@ void loadSettings(){
#define NTP_GUELTIG_MS 7200000UL // NTP-Sync gilt 2 h als aktuell
unsigned long lastRtcReadMs = 0;
unsigned long lastNtpMs = 0; // 0 = noch kein NTP-Sync seit Start
bool ntpShowing = false; // NTP-Wort leuchtet gerade
unsigned long ntpShowUntil = 0;
void readRtcToSystem(){
bool ntpAktuell(){
return lastNtpMs != 0 && millis() - lastNtpMs < NTP_GUELTIG_MS;
}
// Liest die RTC; liefert false, wenn sie keine gueltige Zeit hat (Batterie leer).
bool readRtcToSystem(){
#ifdef RTC_VORHANDEN
lastRtcReadMs = millis();
#if defined(RTC_MODUL_DS3231)
@@ -149,8 +165,10 @@ void readRtcToSystem(){
struct timeval tv = { (time_t)n.unixtime(), 0 };
settimeofday(&tv, nullptr);
ntpJustSynced = false; // der eigene Aufruf ist keine NTP-Zeit
return true;
}
#endif
return false;
}
void initRtc(){
@@ -158,27 +176,72 @@ void initRtc(){
Wire.begin(RTC_SDA, RTC_SCL);
if(!rtc.begin()) return;
rtcOk = true;
readRtcToSystem();
// Nach dem Nachstellen per NTP ist der Batteriestatus nicht mehr erkennbar,
// daher wird er nur beim Start bestimmt.
rtcBatteryEmpty = !readRtcToSystem();
#endif
}
// Nach jeder NTP-Synchronisation die RTC nachstellen; ohne aktuellen NTP-Sync
// stuendlich die Systemzeit aus der RTC auffrischen.
void updateRtc(){
#ifdef RTC_VORHANDEN
if(!rtcOk) return;
void updateTimeSources(){
if(ntpJustSynced){
ntpJustSynced = false;
if(timeSynced()){
lastNtpMs = millis() ? millis() : 1;
rtc.adjust(DateTime((uint32_t)time(nullptr)));
ntpShowing = true;
ntpShowUntil = millis() + (unsigned long)ntpShowSec * 1000UL;
#ifdef RTC_VORHANDEN
if(rtcOk) rtc.adjust(DateTime((uint32_t)time(nullptr)));
#endif
}
}
bool ntpAktuell = lastNtpMs != 0 && millis() - lastNtpMs < NTP_GUELTIG_MS;
if(!ntpAktuell && millis() - lastRtcReadMs >= RTC_INTERVAL_MS) readRtcToSystem();
if(ntpShowing && (long)(millis() - ntpShowUntil) >= 0) ntpShowing = false;
#ifdef RTC_VORHANDEN
if(rtcOk && !rtcBatteryEmpty && !ntpAktuell() && millis() - lastRtcReadMs >= RTC_INTERVAL_MS)
readRtcToSystem();
#endif
}
// ================= STATUS-WOERTER =================
// NTP (gruen): waehrend/kurz nach dem NTP-Abgleich
// RTC (gelb): RTC vorhanden, aber kein aktueller NTP-Abgleich; bei leerer Batterie
// blinkt das Wort im 10-s-Takt
// ESP (rot): weder gueltige RTC noch NTP
#define STATUS_GRUEN 0x00FF00
#define STATUS_GELB 0xFFFF00
#define STATUS_ROT 0xFF0000
struct StatusFarben { uint32_t ntp, rtc, esp; };
StatusFarben statusAktuell = {0, 0, 0};
StatusFarben berechneStatus(){
StatusFarben st = {0, 0, 0};
if(!statusLeds) return st;
bool ntpOk = ntpAktuell();
bool rtcGut = rtcOk && !rtcBatteryEmpty;
if(ntpShowing) st.ntp = STATUS_GRUEN;
if(rtcOk){
if(rtcBatteryEmpty) st.rtc = ((millis() / 10000UL) & 1) ? 0 : STATUS_GELB;
else if(!ntpOk) st.rtc = STATUS_GELB;
}
if(!rtcGut && !ntpOk) st.esp = STATUS_ROT;
return st;
}
// Aenderung der Statusanzeige erkennen (loest ein Neuzeichnen aus)
bool statusGeaendert(){
StatusFarben st = berechneStatus();
bool changed = st.ntp != statusAktuell.ntp || st.rtc != statusAktuell.rtc || st.esp != statusAktuell.esp;
statusAktuell = st;
return changed;
}
void applyStatus(){
for(int i=34;i<=36;i++) if(statusAktuell.ntp) targetBuffer[i] = statusAktuell.ntp;
for(int i=56;i<=58;i++) if(statusAktuell.rtc) targetBuffer[i] = statusAktuell.rtc;
for(int i=67;i<=69;i++) if(statusAktuell.esp) targetBuffer[i] = statusAktuell.esp;
}
// ================= HELFER =================
bool isNightTime(struct tm *t){
if(!autoNight) return false;
@@ -348,10 +411,21 @@ void showTimeWords(int h, int m){
else if(hh == 7) word_SIEBEN(); else if(hh == 8) word_ACHT(); else if(hh == 9) word_NEUN();
else if(hh == 10) word_ZEHN_H(); else if(hh == 11) word_ELF(); else word_ZWOELF();
applyStatus();
if(forceInstant){ showInstant(); forceInstant = false; }
else { fadeToTargetSmooth(20, 10); }
}
// Solange noch keine Zeit bekannt ist, nur Hintergrund und Statuswoerter zeigen
void showStatusOnly(){
for(int i=0;i<NUMPIXELS;i++) currentBuffer[i] = targetBuffer[i];
uint32_t activeBg = backgroundMode ? bgColor : 0;
for(int i=0;i<NUMPIXELS;i++) targetBuffer[i] = activeBg;
applyStatus();
showInstant();
}
// ================= WEB HANDLERS =================
String jsonEscape(const String &s){
String out;
@@ -396,7 +470,9 @@ void handleStatus(){
json += "\"nightStart\":" + String(nightStart) + ",";
json += "\"nightEnd\":" + String(nightEnd) + ",";
json += "\"bg\":" + String(backgroundMode ? 1 : 0) + ",";
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0);
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0) + ",";
json += "\"statusLeds\":" + String(statusLeds ? 1 : 0) + ",";
json += "\"ntpShowSec\":" + String(ntpShowSec);
json += "}";
server.send(200, "application/json", json);
}
@@ -434,6 +510,10 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
<input type='range' id='nbr' min='0' max='255' oninput="f('/nightBrightness?val='+this.value)">
<input type='checkbox' id='nightBgOff' onchange="f('/nightBgOff?val='+(this.checked?1:0))"> Hintergrund in Nacht aus
</div>
<div class='card'>
<input type='checkbox' id='sl' onchange="f('/statusLeds?val='+(this.checked?1:0))"> <b>Status-Anzeige</b> (NTP gr&uuml;n, RTC gelb, ESP rot)<br><br>
NTP-Anzeige mind. <input type='number' id='nsec' min='1' max='60' style='width:50px' oninput="if(this.value!=='')f('/ntpShowSec?val='+this.value)"> Sekunden
</div>
<div class='card'>
<label>NTP Server:</label>
<input type='text' id='ntpS' onchange="f('/setNTP?val='+encodeURIComponent(this.value))">
@@ -450,7 +530,7 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
const s=(i,v,c=false)=>{const e=document.getElementById(i);if(e&&document.activeElement!==e){if(c)e.checked=(v==1);else e.value=v;}};
s('col',d.color);s('br',d.brightness);s('night',d.night,true);s('nS',d.nightStart);s('nE',d.nightEnd);
s('nbr',d.nightBrightness);s('bg',d.bg,true);s('bgcol',d.bgColor);s('ntpS',d.ntpServer);s('tzS',d.tzString);
s('nightBgOff',d.nightBgOff,true);
s('nightBgOff',d.nightBgOff,true);s('sl',d.statusLeds,true);s('nsec',d.ntpShowSec);
}).catch(()=>{document.getElementById('status').innerText='Verbindung unterbrochen...';})}
setInterval(u, 2500); u();
</script></body></html>
@@ -480,6 +560,8 @@ void handleNightStart(){ int v; if(!argInRange(0,23,v)) return; nightStart=v; fo
void handleNightEnd(){ int v; if(!argInRange(0,23,v)) return; nightEnd=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNightBrightness(){ int v; if(!argInRange(0,255,v)) return; nightBrightness=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNightBgOff(){ int v; if(!argInRange(0,1,v)) return; nightBackgroundDisabled=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleStatusLeds(){ int v; if(!argInRange(0,1,v)) return; statusLeds=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNtpShowSec(){ int v; if(!argInRange(1,60,v)) return; ntpShowSec=v; sendOK(); }
void handleBG(){ int v; if(!argInRange(0,1,v)) return; backgroundMode=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNTP(){
@@ -523,6 +605,8 @@ void setup(){
server.on("/nightBrightness", handleNightBrightness);
server.on("/nightBgOff", handleNightBgOff);
server.on("/bg", handleBG);
server.on("/statusLeds", handleStatusLeds);
server.on("/ntpShowSec", handleNtpShowSec);
server.on("/setNTP", handleNTP);
server.on("/setTZ", handleTZ);
server.on("/save", handleSave);
@@ -531,10 +615,14 @@ void setup(){
void loop(){
server.handleClient();
updateRtc();
updateTimeSources();
if(statusGeaendert()){ forceInstant = true; updateNeeded = true; }
// Erst anzeigen, wenn die Zeit per NTP synchronisiert ist
if(!timeSynced()) return;
// Ohne bekannte Zeit nur die Statuswoerter anzeigen
if(!timeSynced()){
if(updateNeeded){ updateNeeded = false; forceInstant = false; showStatusOnly(); }
return;
}
time_t now = time(nullptr);
struct tm *t = localtime(&now);
+103 -15
View File
@@ -28,8 +28,9 @@ RTC_DS1307 rtc;
#define RTC_SDA D2
#define RTC_SCL D1
bool rtcOk = false;
bool rtcBatteryEmpty = false; // RTC hat beim Start die Zeit verloren
volatile bool ntpJustSynced = false;
void onTimeSet(){ ntpJustSynced = true; }
void onTimeSet(bool fromSntp){ if(fromSntp) ntpJustSynced = true; }
// ================= LED =================
#define LED_PIN D4
@@ -48,7 +49,7 @@ String ntpServer = DEFAULT_NTP;
String tzString = DEFAULT_TZ;
// ================= SETTINGS =================
#define SETTINGS_MAGIC 0x57554852UL // erkennt gueltige EEPROM-Daten
#define SETTINGS_MAGIC 0x57554853UL // erkennt gueltige EEPROM-Daten
struct Settings {
uint32_t magic;
@@ -61,6 +62,8 @@ struct Settings {
uint32_t bgColor;
bool backgroundMode;
bool nightBackgroundDisabled;
bool statusLeds;
int ntpShowSec;
char ntpServer[64];
char tzString[64];
} settings;
@@ -70,6 +73,8 @@ uint32_t currentColor = 0x00FFFF;
uint32_t bgColor = 0x000000;
bool backgroundMode = false;
bool nightBackgroundDisabled = false;
bool statusLeds = true; // Statuswoerter NTP/RTC/ESP anzeigen
int ntpShowSec = 10; // Mindestdauer der gruenen NTP-Anzeige
bool forceInstant = false;
bool updateNeeded = true;
int brightness = 120;
@@ -99,6 +104,8 @@ void saveSettings(){
settings.bgColor = bgColor;
settings.backgroundMode = backgroundMode;
settings.nightBackgroundDisabled = nightBackgroundDisabled;
settings.statusLeds = statusLeds;
settings.ntpShowSec = ntpShowSec;
strncpy(settings.ntpServer, ntpServer.c_str(), sizeof(settings.ntpServer) - 1);
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
strncpy(settings.tzString, tzString.c_str(), sizeof(settings.tzString) - 1);
@@ -120,6 +127,8 @@ void loadSettings(){
bgColor = settings.bgColor & 0xFFFFFF;
backgroundMode = settings.backgroundMode;
nightBackgroundDisabled = settings.nightBackgroundDisabled;
statusLeds = settings.statusLeds;
ntpShowSec = constrain(settings.ntpShowSec, 1, 60);
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
settings.tzString[sizeof(settings.tzString) - 1] = '\0';
ntpServer = String(settings.ntpServer);
@@ -135,8 +144,15 @@ void loadSettings(){
#define NTP_GUELTIG_MS 7200000UL // NTP-Sync gilt 2 h als aktuell
unsigned long lastRtcReadMs = 0;
unsigned long lastNtpMs = 0; // 0 = noch kein NTP-Sync seit Start
bool ntpShowing = false; // NTP-Wort leuchtet gerade
unsigned long ntpShowUntil = 0;
void readRtcToSystem(){
bool ntpAktuell(){
return lastNtpMs != 0 && millis() - lastNtpMs < NTP_GUELTIG_MS;
}
// Liest die RTC; liefert false, wenn sie keine gueltige Zeit hat (Batterie leer).
bool readRtcToSystem(){
#ifdef RTC_VORHANDEN
lastRtcReadMs = millis();
#if defined(RTC_MODUL_DS3231)
@@ -149,8 +165,10 @@ void readRtcToSystem(){
struct timeval tv = { (time_t)n.unixtime(), 0 };
settimeofday(&tv, nullptr);
ntpJustSynced = false; // der eigene Aufruf ist keine NTP-Zeit
return true;
}
#endif
return false;
}
void initRtc(){
@@ -158,27 +176,72 @@ void initRtc(){
Wire.begin(RTC_SDA, RTC_SCL);
if(!rtc.begin()) return;
rtcOk = true;
readRtcToSystem();
// Nach dem Nachstellen per NTP ist der Batteriestatus nicht mehr erkennbar,
// daher wird er nur beim Start bestimmt.
rtcBatteryEmpty = !readRtcToSystem();
#endif
}
// Nach jeder NTP-Synchronisation die RTC nachstellen; ohne aktuellen NTP-Sync
// stuendlich die Systemzeit aus der RTC auffrischen.
void updateRtc(){
#ifdef RTC_VORHANDEN
if(!rtcOk) return;
void updateTimeSources(){
if(ntpJustSynced){
ntpJustSynced = false;
if(timeSynced()){
lastNtpMs = millis() ? millis() : 1;
rtc.adjust(DateTime((uint32_t)time(nullptr)));
ntpShowing = true;
ntpShowUntil = millis() + (unsigned long)ntpShowSec * 1000UL;
#ifdef RTC_VORHANDEN
if(rtcOk) rtc.adjust(DateTime((uint32_t)time(nullptr)));
#endif
}
}
bool ntpAktuell = lastNtpMs != 0 && millis() - lastNtpMs < NTP_GUELTIG_MS;
if(!ntpAktuell && millis() - lastRtcReadMs >= RTC_INTERVAL_MS) readRtcToSystem();
if(ntpShowing && (long)(millis() - ntpShowUntil) >= 0) ntpShowing = false;
#ifdef RTC_VORHANDEN
if(rtcOk && !rtcBatteryEmpty && !ntpAktuell() && millis() - lastRtcReadMs >= RTC_INTERVAL_MS)
readRtcToSystem();
#endif
}
// ================= STATUS-WOERTER =================
// NTP (gruen): waehrend/kurz nach dem NTP-Abgleich
// RTC (gelb): RTC vorhanden, aber kein aktueller NTP-Abgleich; bei leerer Batterie
// blinkt das Wort im 10-s-Takt
// ESP (rot): weder gueltige RTC noch NTP
#define STATUS_GRUEN 0x00FF00
#define STATUS_GELB 0xFFFF00
#define STATUS_ROT 0xFF0000
struct StatusFarben { uint32_t ntp, rtc, esp; };
StatusFarben statusAktuell = {0, 0, 0};
StatusFarben berechneStatus(){
StatusFarben st = {0, 0, 0};
if(!statusLeds) return st;
bool ntpOk = ntpAktuell();
bool rtcGut = rtcOk && !rtcBatteryEmpty;
if(ntpShowing) st.ntp = STATUS_GRUEN;
if(rtcOk){
if(rtcBatteryEmpty) st.rtc = ((millis() / 10000UL) & 1) ? 0 : STATUS_GELB;
else if(!ntpOk) st.rtc = STATUS_GELB;
}
if(!rtcGut && !ntpOk) st.esp = STATUS_ROT;
return st;
}
// Aenderung der Statusanzeige erkennen (loest ein Neuzeichnen aus)
bool statusGeaendert(){
StatusFarben st = berechneStatus();
bool changed = st.ntp != statusAktuell.ntp || st.rtc != statusAktuell.rtc || st.esp != statusAktuell.esp;
statusAktuell = st;
return changed;
}
void applyStatus(){
for(int i=34;i<=36;i++) if(statusAktuell.ntp) targetBuffer[i] = statusAktuell.ntp;
for(int i=56;i<=58;i++) if(statusAktuell.rtc) targetBuffer[i] = statusAktuell.rtc;
for(int i=67;i<=69;i++) if(statusAktuell.esp) targetBuffer[i] = statusAktuell.esp;
}
// ================= HELFER =================
bool isNightTime(struct tm *t){
if(!autoNight) return false;
@@ -348,10 +411,21 @@ void showTimeWords(int h, int m){
else if(hh == 7) word_SIEBEN(); else if(hh == 8) word_ACHT(); else if(hh == 9) word_NEUN();
else if(hh == 10) word_ZEHN_H(); else if(hh == 11) word_ELF(); else word_ZWOELF();
applyStatus();
if(forceInstant){ showInstant(); forceInstant = false; }
else { fadeToTargetSmooth(20, 10); }
}
// Solange noch keine Zeit bekannt ist, nur Hintergrund und Statuswoerter zeigen
void showStatusOnly(){
for(int i=0;i<NUMPIXELS;i++) currentBuffer[i] = targetBuffer[i];
uint32_t activeBg = backgroundMode ? bgColor : 0;
for(int i=0;i<NUMPIXELS;i++) targetBuffer[i] = activeBg;
applyStatus();
showInstant();
}
// ================= WEB HANDLERS =================
String jsonEscape(const String &s){
String out;
@@ -396,7 +470,9 @@ void handleStatus(){
json += "\"nightStart\":" + String(nightStart) + ",";
json += "\"nightEnd\":" + String(nightEnd) + ",";
json += "\"bg\":" + String(backgroundMode ? 1 : 0) + ",";
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0);
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0) + ",";
json += "\"statusLeds\":" + String(statusLeds ? 1 : 0) + ",";
json += "\"ntpShowSec\":" + String(ntpShowSec);
json += "}";
server.send(200, "application/json", json);
}
@@ -434,6 +510,10 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
<input type='range' id='nbr' min='0' max='255' oninput="f('/nightBrightness?val='+this.value)">
<input type='checkbox' id='nightBgOff' onchange="f('/nightBgOff?val='+(this.checked?1:0))"> Hintergrund in Nacht aus
</div>
<div class='card'>
<input type='checkbox' id='sl' onchange="f('/statusLeds?val='+(this.checked?1:0))"> <b>Status-Anzeige</b> (NTP gr&uuml;n, RTC gelb, ESP rot)<br><br>
NTP-Anzeige mind. <input type='number' id='nsec' min='1' max='60' style='width:50px' oninput="if(this.value!=='')f('/ntpShowSec?val='+this.value)"> Sekunden
</div>
<div class='card'>
<label>NTP Server:</label>
<input type='text' id='ntpS' onchange="f('/setNTP?val='+encodeURIComponent(this.value))">
@@ -450,7 +530,7 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
const s=(i,v,c=false)=>{const e=document.getElementById(i);if(e&&document.activeElement!==e){if(c)e.checked=(v==1);else e.value=v;}};
s('col',d.color);s('br',d.brightness);s('night',d.night,true);s('nS',d.nightStart);s('nE',d.nightEnd);
s('nbr',d.nightBrightness);s('bg',d.bg,true);s('bgcol',d.bgColor);s('ntpS',d.ntpServer);s('tzS',d.tzString);
s('nightBgOff',d.nightBgOff,true);
s('nightBgOff',d.nightBgOff,true);s('sl',d.statusLeds,true);s('nsec',d.ntpShowSec);
}).catch(()=>{document.getElementById('status').innerText='Verbindung unterbrochen...';})}
setInterval(u, 2500); u();
</script></body></html>
@@ -480,6 +560,8 @@ void handleNightStart(){ int v; if(!argInRange(0,23,v)) return; nightStart=v; fo
void handleNightEnd(){ int v; if(!argInRange(0,23,v)) return; nightEnd=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNightBrightness(){ int v; if(!argInRange(0,255,v)) return; nightBrightness=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNightBgOff(){ int v; if(!argInRange(0,1,v)) return; nightBackgroundDisabled=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleStatusLeds(){ int v; if(!argInRange(0,1,v)) return; statusLeds=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNtpShowSec(){ int v; if(!argInRange(1,60,v)) return; ntpShowSec=v; sendOK(); }
void handleBG(){ int v; if(!argInRange(0,1,v)) return; backgroundMode=v; forceInstant=true; updateNeeded=true; sendOK(); }
void handleNTP(){
@@ -523,6 +605,8 @@ void setup(){
server.on("/nightBrightness", handleNightBrightness);
server.on("/nightBgOff", handleNightBgOff);
server.on("/bg", handleBG);
server.on("/statusLeds", handleStatusLeds);
server.on("/ntpShowSec", handleNtpShowSec);
server.on("/setNTP", handleNTP);
server.on("/setTZ", handleTZ);
server.on("/save", handleSave);
@@ -531,10 +615,14 @@ void setup(){
void loop(){
server.handleClient();
updateRtc();
updateTimeSources();
if(statusGeaendert()){ forceInstant = true; updateNeeded = true; }
// Erst anzeigen, wenn die Zeit per NTP synchronisiert ist
if(!timeSynced()) return;
// Ohne bekannte Zeit nur die Statuswoerter anzeigen
if(!timeSynced()){
if(updateNeeded){ updateNeeded = false; forceInstant = false; showStatusOnly(); }
return;
}
time_t now = time(nullptr);
struct tm *t = localtime(&now);