Compare commits

..
Author SHA1 Message Date
raw-designsandClaude Opus 5 743c38e994 fix: Auswahl beim Aufwecken über Touch entfiel manchmal (S3 5.6.3 / P4 1.4.2)
Über den Schalter kam die Abfrage "Espresso oder Cold Extraction" zuverlässig,
über Antippen am Display nur manchmal. Ursache waren zwei unterschiedliche
Bedingungen für dieselbe Sache:

- askMode (zeigt die Auswahl im Dialog) verlangte zusätzlich cxArmable
- choiceRelevant (sendet "wakeEspresso" und schließt damit das Auswahlfenster
  der S3) verlangte es nicht

In der Lücke dazwischen zeigte der Dialog nur "Aufwecken", unterdrückte aber
trotzdem die Rückfrage der S3 - die Frage war auf beiden Seiten weg. Der
Schalterweg hatte diese Lücke nie, weil er allein an der S3 hängt.

P4: beide Stellen benutzen jetzt wake_choice_offered(). Bot der Dialog die
Auswahl nicht an, sendet er "deactivateStandby"; dann öffnet die S3 ihr
eigenes Auswahlfenster und die Frage kommt trotzdem.

S3: Die Fensterentscheidung ist als Bedingungskette geschrieben und hält
Quelle und Ergebnis des letzten Aufweckens fest. /Cold-Extraction zeigt das
an - die bisherige Live-Liste zeigt nur den jetzigen Stand, der beim
Nachsehen längst wieder ein anderer sein kann.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 01:02:30 +02:00
thomas 535f640bbd Merge pull request 'fix: Abfrage beim Aufwecken geht in beide Richtungen (S3 5.6.2 / P4 1.4.1)' (#24) from fix/cold-extraction-abfrage-beide-richtungen into main 2026-08-12 07:20:11 +02:00
raw-designsandClaude Opus 5 f8d51a57b7 fix: Abfrage beim Aufwecken geht in beide Richtungen (S3 5.6.2 / P4 1.4.1)
Die Abfrage erschien nicht, sobald der Cold-Extraction-Modus noch aktiv war — und
da er seit 5.3.1 persistent ist, war er das nach dem ersten kalten Bezug dauerhaft.
Damit erklärt sich, warum weder der Schalter- noch der Touch-Weg etwas zeigte.

Die Bedingung "Modus ist aus" war falsch gedacht ("sonst gibt es nichts zu fragen").
Die Auswahl muss in beide Richtungen gehen, also auch von kalt zurück auf Espresso.
Sonst hängt man nach dem ersten kalten Bezug im kalten Modus fest, ohne beim
Aufwecken je wieder gefragt zu werden.

- Bedingung aus beiden Öffnungspfaden entfernt (Standby-Übergang und Kaltstart).
- Das Fenster bleibt bei aktivem Modus offen; bisher schloss es sofort wieder.
- "wakeEspresso" beendet jetzt einen laufenden Modus, statt ihn nur nicht
  einzuschalten — sonst wäre die Auswahl eine Einbahnstraße.
- Diagnose auf /Cold-Extraction listet den Modus nicht mehr als Voraussetzung,
  sondern zeigt ihn als Ist-Zustand.
- P4: Der Cold-Knopf zeigt den Ist-Zustand ("aktiv lassen" und hervorgehoben, wenn
  der Modus läuft).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 07:19:50 +02:00
thomas 8432bd3f15 Merge pull request 'fix(S3): Auswahl beim Aufwecken auch nach Kaltstart + Diagnose der Bedingungen (5.6.1)' (#23) from fix/cold-extraction-auswahl-diagnose into main 2026-08-12 02:33:54 +02:00
5 changed files with 191 additions and 16 deletions
+30
View File
@@ -1,3 +1,33 @@
Version 5.6.3:
- Bug-Fix: Beim Aufwecken über das Touch-Display blieb die Auswahl „Espresso oder Cold Extraction"
manchmal aus, während sie über den Schalter kam. Ursache waren zwei unterschiedliche Bedingungen für
ein und dieselbe Sache: Das Display prüfte zum ANZEIGEN der Auswahl zusätzlich, ob der Modus gerade
scharfschaltbar ist (cxArmable) - zum UNTERDRÜCKEN der Rückfrage der S3 („wakeEspresso") aber nicht.
Fiel diese Bedingung weg, zeigte der Dialog nur noch „Aufwecken", schloss aber trotzdem das
Auswahlfenster der S3: Die Frage war auf beiden Seiten weg. Der Schalterweg hatte diese Lücke nie,
weil er allein an der S3 hängt. Behoben in der Display-Firmware 1.4.2.
- Diagnose: /Cold-Extraction zeigt jetzt zusätzlich zum Live-Stand der Voraussetzungen auch das
LETZTE Aufwecken - über welchen Weg (Schalter/Timer/Web oder Display, dort mit getroffener Wahl)
und mit welchem Ergebnis (Fenster geöffnet, am Display bereits entschieden, Abfrage aus, kein
Display verbunden, Wartung/Reinigung/Tuning aktiv, nicht freigeschaltet). Hintergrund: Die
Live-Liste zeigt nur den jetzigen Stand - blieb die Frage aus, hatte sich die Lage bis zum
Nachsehen längst wieder geändert, und der Grund war nicht mehr zu ermitteln.
- Die Entscheidung über das Auswahlfenster ist als Bedingungskette geschrieben, damit jeder Zweig
seinen Grund hinterlässt (vorher eine einzige verknüpfte Bedingung ohne Rückmeldung).
Version 5.6.2:
- Bug-Fix: Die Abfrage beim Aufwecken erschien nicht, sobald der Cold-Extraction-Modus noch aktiv war -
und da er persistent ist, war er das nach dem ersten kalten Bezug dauerhaft. Die Bedingung „Modus ist
aus" war falsch gedacht („sonst gibt es nichts zu fragen"): Die Auswahl muss in BEIDE Richtungen gehen,
also auch von kalt zurück auf Espresso. Ohne das hing man nach dem ersten kalten Bezug im kalten Modus
fest, ohne beim Aufwecken je wieder gefragt zu werden.
- „Espresso" beendet jetzt einen laufenden Cold-Extraction-Modus, statt ihn nur nicht einzuschalten.
Sonst wäre die Auswahl eine Einbahnstraße gewesen.
- Das Auswahlfenster bleibt bei aktivem Modus offen (bisher wurde es sofort geschlossen); die Frage
lautet dann „weiter kalt oder zurück auf Espresso?".
- Die Diagnose auf /Cold-Extraction listet den Modus nicht mehr als Voraussetzung, sondern zeigt ihn
als Ist-Zustand an.
Version 5.6.1:
- Diagnose: Die Seite /Cold-Extraction zeigt jetzt live, welche Voraussetzungen der Abfrage beim
Aufwecken erfüllt sind (freigeschaltet, Abfrage eingeschaltet, Touch-Display verbunden, Modus
+114 -9
View File
@@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
* Firmware-Informationen
************************************************************************************/
String version = "5.6.1";
String version = "5.6.3";
String versionHersteller = "Thomas M&uuml;ller";
String versionHerstellerMail =
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
@@ -804,6 +804,28 @@ unsigned long coldExtractionWakeDecisionMs = 0; // Zeitpunkt der letzten ex
const unsigned long COLDEX_WAKE_CHOICE_MS = 20000UL; // Bedenkzeit
const unsigned long COLDEX_WAKE_DECISION_GRACE_MS = 5000UL; // frisch entschieden -> kein Fenster
// Diagnose des letzten Aufweckens: Bleibt die Abfrage aus, ist von aussen nicht erkennbar,
// welche der Voraussetzungen gefehlt hat - die Live-Anzeige auf /Cold-Extraction zeigt nur
// den JETZIGEN Stand, und der kann sich bis zum Nachsehen laengst wieder geaendert haben.
// Deshalb wird das Ergebnis beim Aufwecken festgehalten. Reine Anzeige, keine Logik.
#define COLDEX_WAKE_SRC_UNKNOWN 0 // Schalter, Timer, Web - alles ohne Display-Beteiligung
#define COLDEX_WAKE_SRC_TOUCH 1 // Display: "Aufwecken" (ohne Auswahl im Dialog)
#define COLDEX_WAKE_SRC_ESPRESSO 2 // Display: "Espresso" gewaehlt
#define COLDEX_WAKE_SRC_COLD 3 // Display: "Cold Extraction" gewaehlt
#define COLDEX_WAKE_RES_NONE 0 // seit dem Start noch nicht aufgeweckt
#define COLDEX_WAKE_RES_OPENED 1 // Auswahlfenster geoeffnet
#define COLDEX_WAKE_RES_DECIDED 2 // am Display bereits entschieden -> keine zweite Frage
#define COLDEX_WAKE_RES_ASK_OFF 3 // Abfrage in den Einstellungen ausgeschaltet
#define COLDEX_WAKE_RES_NO_DISP 4 // kein Touch-Display verbunden
#define COLDEX_WAKE_RES_NOT_ARM 5 // Wartung / Reinigung / PID-Tuning aktiv
#define COLDEX_WAKE_RES_DISABLED 6 // Funktion nicht freigeschaltet
uint8_t coldExtractionWakeSource = COLDEX_WAKE_SRC_UNKNOWN; // Quelle des laufenden Aufweckens
unsigned long coldExtractionWakeSourceMs = 0; // ... wann gemeldet (verfaellt)
const unsigned long COLDEX_WAKE_SRC_VALID_MS = 3000UL; // danach gilt sie als veraltet
uint8_t coldExtractionLastWakeSource = COLDEX_WAKE_SRC_UNKNOWN; // ... des letzten, fuer die Anzeige
uint8_t coldExtractionLastWakeResult = COLDEX_WAKE_RES_NONE;
unsigned long coldExtractionLastWakeMs = 0;
const bool defaultColdExtractionEnabled = false;
const float defaultColdExtractionMaxTempC = 30.0f;
const float defaultColdExtractionPreInfusionSeconds = 10.0f;
@@ -5448,12 +5470,38 @@ void loop() {
// Auswahlfenster oeffnen: gilt fuer JEDEN Weg aus dem Standby (Schalter, Touch, Web,
// Timer). Nur sinnvoll mit angeschlossenem Display - ohne das koennte niemand waehlen,
// und die Maschine wuerde nur unnoetig spaeter heizen.
if (coldExtractionEnabled && coldExtractionAskOnWake && touchUartClientActive &&
!coldExtractionModeActive && coldExtractionArmable() &&
(coldExtractionWakeDecisionMs == 0 ||
(currentMillis - coldExtractionWakeDecisionMs) > COLDEX_WAKE_DECISION_GRACE_MS)) {
// Bewusst OHNE Pruefung auf coldExtractionModeActive: Der Modus ist persistent, und
// die Abfrage muss in BEIDE Richtungen gehen - auch von "kalt" zurueck auf Espresso.
// Sonst bliebe man nach dem ersten kalten Bezug dauerhaft im kalten Modus haengen,
// ohne beim Aufwecken je wieder gefragt zu werden.
// Als Kette geschrieben, damit jeder Zweig seinen Grund hinterlaesst (Anzeige auf
// /Cold-Extraction). Ohne das bleibt ein ausgebliebenes Fenster nicht nachvollziehbar.
// Eine gemeldete Quelle gilt nur kurz: Wird der Modus im Standby ueber die
// Einstellseite scharfgestellt, ohne dass ein Aufwecken folgt, darf sie nicht
// spaeter am naechsten Aufwecken kleben.
coldExtractionLastWakeSource =
(coldExtractionWakeSourceMs != 0 &&
(currentMillis - coldExtractionWakeSourceMs) <= COLDEX_WAKE_SRC_VALID_MS)
? coldExtractionWakeSource : COLDEX_WAKE_SRC_UNKNOWN;
coldExtractionWakeSource = COLDEX_WAKE_SRC_UNKNOWN;
coldExtractionWakeSourceMs = 0;
coldExtractionLastWakeMs = currentMillis;
if (!coldExtractionEnabled) {
coldExtractionLastWakeResult = COLDEX_WAKE_RES_DISABLED;
} else if (!coldExtractionAskOnWake) {
coldExtractionLastWakeResult = COLDEX_WAKE_RES_ASK_OFF;
} else if (!touchUartClientActive) {
coldExtractionLastWakeResult = COLDEX_WAKE_RES_NO_DISP;
} else if (!coldExtractionArmable()) {
coldExtractionLastWakeResult = COLDEX_WAKE_RES_NOT_ARM;
} else if (coldExtractionWakeDecisionMs != 0 &&
(currentMillis - coldExtractionWakeDecisionMs) <= COLDEX_WAKE_DECISION_GRACE_MS) {
// Am Display wurde gerade gewaehlt - eine zweite Frage waere nur laestig.
coldExtractionLastWakeResult = COLDEX_WAKE_RES_DECIDED;
} else {
coldExtractionWakeChoicePending = true;
coldExtractionWakeChoiceUntilMs = currentMillis + COLDEX_WAKE_CHOICE_MS;
coldExtractionLastWakeResult = COLDEX_WAKE_RES_OPENED;
}
}
// Gleiche Auswahl nach einem Kaltstart: Wer die Maschine am Netzschalter einschaltet,
@@ -5461,11 +5509,13 @@ void loop() {
// Einmal pro Laufzeit, sobald sich das Display gemeldet hat.
static bool coldExtractionBootChoiceDone = false;
if (!coldExtractionBootChoiceDone && !standbyModeActive && touchUartClientActive &&
coldExtractionEnabled && coldExtractionAskOnWake && !coldExtractionModeActive &&
coldExtractionArmable()) {
coldExtractionEnabled && coldExtractionAskOnWake && coldExtractionArmable()) {
coldExtractionBootChoiceDone = true;
coldExtractionWakeChoicePending = true;
coldExtractionWakeChoiceUntilMs = currentMillis + COLDEX_WAKE_CHOICE_MS;
coldExtractionLastWakeSource = COLDEX_WAKE_SRC_UNKNOWN;
coldExtractionLastWakeResult = COLDEX_WAKE_RES_OPENED;
coldExtractionLastWakeMs = currentMillis;
} else if (!coldExtractionBootChoiceDone && touchUartClientActive && standbyModeActive) {
// Start in den Standby hinein: Der Standby-Uebergang uebernimmt die Abfrage.
coldExtractionBootChoiceDone = true;
@@ -5473,7 +5523,9 @@ void loop() {
// Auswahlfenster beenden: Zeit abgelaufen, Standby, oder es wird ohnehin schon bezogen
if (coldExtractionWakeChoicePending) {
if (standbyModeActive || coldExtractionModeActive || shotActive || shotSoftwareActive ||
// coldExtractionModeActive ist hier KEIN Abbruchgrund: Bei aktivem Modus ist die
// Frage "weiter kalt oder zurueck auf Espresso?" - das Fenster muss offen bleiben.
if (standbyModeActive || shotActive || shotSoftwareActive ||
flushActive || !coldExtractionEnabled ||
(long)(currentMillis - coldExtractionWakeChoiceUntilMs) >= 0) {
coldExtractionWakeChoicePending = false;
@@ -7854,13 +7906,42 @@ void handleColdExtraction(AsyncWebServerRequest *request) {
diag += flag(coldExtractionEnabled, "Funktion freigeschaltet");
diag += flag(coldExtractionAskOnWake, "Abfrage beim Aufwecken eingeschaltet");
diag += flag(touchUartClientActive, "Touch-Display verbunden");
diag += flag(!coldExtractionModeActive, "Cold-Extraction-Modus derzeit aus (sonst gibt es nichts zu fragen)");
diag += flag(coldExtractionArmable(), "Kein Wartungsmodus / Reinigung / PID-Tuning");
diag += "<span style='color:#b0b0b0;'>Aktueller Modus: ";
diag += coldExtractionModeActive ? "Cold Extraction aktiv" : "normal (Espresso)";
diag += " - die Abfrage geht in beide Richtungen.</span><br>";
if (coldExtractionWakeChoicePending) {
long remainMs = (long)(coldExtractionWakeChoiceUntilMs - millis());
long remainSec = (remainMs > 0) ? ((remainMs + 999) / 1000) : 0;
diag += "<b style='color:#66CC66;'>Auswahlfenster ist gerade offen (noch " + String(remainSec) + " s).</b><br>";
}
// Letztes Aufwecken: Die Liste darueber zeigt nur den jetzigen Stand. Blieb die Frage
// aus, ist der Grund von damals hier festgehalten - sonst muesste man ihn raten.
if (coldExtractionLastWakeResult != COLDEX_WAKE_RES_NONE) {
String src;
switch (coldExtractionLastWakeSource) {
case COLDEX_WAKE_SRC_TOUCH: src = "Display (Aufwecken)"; break;
case COLDEX_WAKE_SRC_ESPRESSO: src = "Display (Espresso gew&auml;hlt)"; break;
case COLDEX_WAKE_SRC_COLD: src = "Display (Cold Extraction gew&auml;hlt)"; break;
default: src = "Schalter / Timer / Web"; break;
}
String res;
bool resOk = false;
switch (coldExtractionLastWakeResult) {
case COLDEX_WAKE_RES_OPENED: res = "Auswahlfenster ge&ouml;ffnet"; resOk = true; break;
case COLDEX_WAKE_RES_DECIDED: res = "am Display bereits entschieden"; resOk = true; break;
case COLDEX_WAKE_RES_ASK_OFF: res = "Abfrage ist ausgeschaltet"; break;
case COLDEX_WAKE_RES_NO_DISP: res = "kein Touch-Display verbunden"; break;
case COLDEX_WAKE_RES_NOT_ARM: res = "Wartung / Reinigung / PID-Tuning aktiv"; break;
case COLDEX_WAKE_RES_DISABLED: res = "Funktion nicht freigeschaltet"; break;
default: res = "unbekannt"; break;
}
unsigned long agoSec = (millis() - coldExtractionLastWakeMs) / 1000UL;
diag += "<span style='color:#b0b0b0;'>Letztes Aufwecken vor " + String(agoSec) + " s &uuml;ber " + src +
": <span style='color:";
diag += resOk ? "#66CC66" : "#FFCC00";
diag += ";'>" + res + "</span>.</span><br>";
}
diag += "</small>";
cxFieldsHtml.replace("{CX_ASK_DIAG}", diag);
}
@@ -15728,6 +15809,13 @@ bool executeDashboardAction(const String& action, const String& value,
else if (action == "wakeEspresso") {
coldExtractionWakeDecisionMs = millis();
coldExtractionWakeChoicePending = false;
// "Espresso" heisst auch: einen noch laufenden Cold-Extraction-Modus beenden.
// Ohne das waere die Auswahl eine Einbahnstrasse - der Modus ist persistent und
// liesse sich beim Aufwecken nie wieder abwaehlen.
if (coldExtractionModeActive && !shotActive && !shotSoftwareActive) {
String cxMsg;
setColdExtractionMode(false, cxMsg);
}
if (standbyModeActive) {
standbyModeActive = false;
startupTime = millis();
@@ -17428,6 +17516,23 @@ static bool touchUartHandleCommandLine(const String& line) {
String message = "";
bool settingsChanged = false;
bool pidNeedsUpdate = false;
// Herkunft des Aufweckens fuer die Diagnose festhalten - nur hier ist bekannt, dass
// die Aktion vom Touch-Display kommt und nicht aus der Web-UI. "Cold Extraction"
// schickt erst den Modus und dann das Aufwecken, deshalb darf das nachfolgende
// deactivateStandby die schon gesetzte Quelle nicht ueberschreiben.
if (standbyModeActive) {
if (action == "wakeEspresso") {
coldExtractionWakeSource = COLDEX_WAKE_SRC_ESPRESSO;
coldExtractionWakeSourceMs = millis();
} else if (action == "startColdExtraction") {
coldExtractionWakeSource = COLDEX_WAKE_SRC_COLD;
coldExtractionWakeSourceMs = millis();
} else if (action == "deactivateStandby" &&
coldExtractionWakeSource == COLDEX_WAKE_SRC_UNKNOWN) {
coldExtractionWakeSource = COLDEX_WAKE_SRC_TOUCH;
coldExtractionWakeSourceMs = millis();
}
}
executeDashboardAction(action, value, success, message, settingsChanged, pidNeedsUpdate);
touchUartSendAck(id, success, message);
touchUartSendState();
+17
View File
@@ -1,3 +1,20 @@
Version 1.4.2:
- Bug-Fix: Beim Aufwecken über Antippen erschien die Auswahl „Espresso oder Cold Extraction"
manchmal nicht. Der Dialog entschied nach einer anderen Bedingung, ob er die Auswahl ANZEIGT
(verlangte zusätzlich cxArmable), als er beim Absenden benutzte, um die Rückfrage der S3 zu
UNTERDRÜCKEN. In der Lücke dazwischen zeigte er nur „Aufwecken", sendete aber „wakeEspresso" -
und damit war die Frage auch auf der S3 erledigt. Beide Stellen benutzen jetzt dieselbe Funktion
wake_choice_offered().
- Hat der Dialog die Auswahl nicht angeboten, sendet er wieder „deactivateStandby". Dann öffnet die
S3 ihr eigenes Auswahlfenster, und die Frage kommt trotzdem - derselbe Weg wie beim Aufwecken über
den Schalter. Die Auswahl kann damit nicht mehr ersatzlos entfallen.
Version 1.4.1:
- Passend zu S3 5.6.2: Die Auswahl beim Aufwecken erscheint auch dann, wenn der Cold-Extraction-Modus
bereits aktiv ist - sie geht in beide Richtungen. „Espresso" schaltet den Modus dann ab.
- Der Cold-Extraction-Knopf zeigt den Ist-Zustand: Bei aktivem Modus ist er hervorgehoben und heißt
„Cold Extraction (aktiv lassen)", sonst „Cold Extraction (Wasser bleibt kalt)".
Version 1.4.0:
- Die Auswahl „Espresso oder Cold Extraction" erscheint jetzt auch beim Aufwecken über den Schalter
(braucht S3-Firmware ab 5.6.0). Meldet die S3 ein offenes Auswahlfenster (cxWakeChoice), blendet das
+1 -1
View File
@@ -65,7 +65,7 @@
// -------------------------------------------------------------------------------------
// Firmware
// -------------------------------------------------------------------------------------
#define DISPLAY_FW_VERSION "1.4.0" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
#define DISPLAY_FW_VERSION "1.4.2" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
// -------------------------------------------------------------------------------------
// Protokoll
+29 -6
View File
@@ -598,6 +598,16 @@ static void wake_confirm_show() {
lv_obj_move_foreground(g_wakeConfirm);
}
static void wake_confirm_hide() { if (g_wakeConfirm) lv_obj_add_flag(g_wakeConfirm, LV_OBJ_FLAG_HIDDEN); }
// Einzige Quelle der Wahrheit dafuer, ob der Aufweck-Dialog die Auswahl "Espresso oder Cold
// Extraction" wirklich anbietet. Wird an ZWEI Stellen gebraucht: beim Aufbau des Dialogs und
// beim Absenden. Standen dort verschiedene Bedingungen, konnte der Dialog nur "Aufwecken"
// zeigen, aber trotzdem "wakeEspresso" senden - und damit das Auswahlfenster der S3
// schliessen. Dann war die Frage auf beiden Seiten weg (der Schalterweg hat diese Luecke
// nicht, weil er allein am S3 haengt).
static bool wake_choice_offered() {
return g_state.cxWakeChoice ||
(g_state.cxEnabled && g_state.cxArmable && g_state.cxAskOnWake);
}
static void standby_screen_cb(lv_event_t*) { wake_confirm_show(); } // Tipp auf Standby-Uhr -> nachfragen
static void wake_no_cb(lv_event_t*) { // Abbrechen -> zurueck in die Standby-Helligkeit
wake_confirm_hide();
@@ -609,10 +619,11 @@ static void wake_yes_cb(lv_event_t*) { // Aufwecken -> hell lassen, S3 bee
// g_wakeDialogOpen bleibt true, bis der Standby wirklich endet (ui_update) - sonst wuerde
// das Display in der Uebergangsphase kurz auf Standby-Helligkeit abdunkeln.
if (!g_client) return;
// "wakeEspresso" beendet den Standby UND schliesst das Auswahlfenster der S3. Ohne das
// wuerde nach dem Aufwecken noch einmal gefragt bzw. der Wasserkreis bliebe kurz aus.
const bool choiceRelevant = g_state.cxWakeChoice || (g_state.cxEnabled && g_state.cxAskOnWake);
g_client->sendAction(choiceRelevant ? "wakeEspresso" : "deactivateStandby");
// "wakeEspresso" beendet den Standby UND schliesst das Auswahlfenster der S3 - das ist
// nur dann richtig, wenn dieser Dialog die Auswahl gerade wirklich angeboten hat. Sonst
// "deactivateStandby": dann oeffnet die S3 ihr eigenes Auswahlfenster, und die Frage
// kommt trotzdem (gleicher Weg wie beim Aufwecken ueber den Schalter).
g_client->sendAction(wake_choice_offered() ? "wakeEspresso" : "deactivateStandby");
showToast(g_state.standbyActive ? "Maschine wird aufgeweckt..." : "Espresso: Kessel heizt auf.", false);
}
// Aufwecken direkt in die kalte Extraktion. Reihenfolge ist wichtig: erst den Modus scharf
@@ -3603,9 +3614,10 @@ void ui_update(const MachineState& st) {
// Zwei Faelle: Vorab-Abfrage beim Antippen im Standby (askMode) und das von der S3
// geoeffnete Auswahlfenster NACH dem Aufwecken (choiceOpen) - letzteres deckt auch
// das Aufwecken per Schalter ab, wo es keine Vorab-Abfrage geben kann.
// Kein "!st.cxActive": Der Modus ist persistent, die Auswahl muss in beide
// Richtungen gehen - sonst haengt man nach dem ersten kalten Bezug dauerhaft fest.
const bool choiceOpen = st.cxWakeChoice;
const bool askMode = choiceOpen ||
(st.cxEnabled && st.cxArmable && !st.cxActive && st.cxAskOnWake);
const bool askMode = wake_choice_offered(); // g_state == st (oben zugewiesen)
if (g_wakeColdBtn) {
if (askMode) lv_obj_remove_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN);
@@ -3631,6 +3643,17 @@ void ui_update(const MachineState& st) {
if (g_wakeYesLbl) {
label_set_if_changed(g_wakeYesLbl, askMode ? "Espresso" : "Aufwecken");
}
// Aktive Wahl hervorheben: Bei laufendem Modus ist "Cold Extraction" der Ist-Zustand,
// "Espresso" schaltet ihn ab - andersherum genauso.
if (g_wakeColdBtn && askMode) {
lv_obj_set_style_bg_color(g_wakeColdBtn, st.cxActive ? COL_ACCENT : COL_CARD2, 0);
lv_obj_t* coldLbl = lv_obj_get_child(g_wakeColdBtn, 0);
if (coldLbl) {
lv_obj_set_style_text_color(coldLbl, st.cxActive ? lv_color_hex(0x000000) : COL_ACCENT, 0);
label_set_if_changed(coldLbl, st.cxActive ? "Cold Extraction (aktiv lassen)"
: "Cold Extraction (Wasser bleibt kalt)");
}
}
// Auswahlfenster von der S3 -> Dialog von sich aus zeigen (kein Antippen noetig)
if (choiceOpen && g_wakeConfirm && lv_obj_has_flag(g_wakeConfirm, LV_OBJ_FLAG_HIDDEN)) {
wake_confirm_show();