feat: Cold Extraction warnt bei fehlender Waage (S3 5.3.4 / P4 1.1.2)

Ohne aktive und verbundene Waage greifen weder das Zielgewicht noch die
Stillstands-Erkennung — der Bezug endet dann erst nach der maximalen Bezugsdauer,
die damit faktisch zur Dosierung wird. Bisher passierte diese Degradierung
stillschweigend.

Die Funktion bleibt ohne Waage bewusst nutzbar (Zeitsteuerung als Rückfallebene),
damit ein kurzzeitig abgemeldeter HX711 die kalte Extraktion nicht komplett
blockiert. Angezeigt wird der Zustand jetzt an vier Stellen: dauerhafter
Warnhinweis auf /Brew-Control (Zielgewicht-Feld zusätzlich als "nur mit Waage
wirksam" beschriftet), Hinweiszeile unter dem Dashboard-Schalter, Statustext
"Cold Extraction bereit (ohne Waage: Zeitsteuerung)" und eine einmalige Meldung
beim Aktivieren.

P4: Statuszeile zeigt "ohne Waage, Ende nach Zeit" in Warnfarbe statt des blauen
Normalhinweises; Erklärzeile am Schalter und Parameter-Anzeige auf der Brew-Seite
ergänzen den Hinweis und nennen bei aktivem Modus ohne Waage die Zeit statt des
Zielgewichts. Neuer cxNotice-Code 4 (additiv, Protokoll bleibt v2).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
raw-designs
2026-08-11 02:05:50 +02:00
co-authored by Claude Opus 5
parent 4cc9ceea7c
commit b6155acafe
6 changed files with 103 additions and 18 deletions
+29 -5
View File
@@ -3446,6 +3446,9 @@ void ui_update(const MachineState& st) {
// Cold-Extraction-Hinweis: erklaert dauerhaft, warum der Wasserkreis nicht heizt.
// Transiente Meldung (abgewiesener Start / Abbruch) hat Vorrang vor dem Dauerzustand.
// Ohne nutzbare Waage laeuft die kalte Extraktion rein zeitgesteuert (Zielgewicht und
// Stillstands-Erkennung greifen nicht) - das wird dauerhaft mit angezeigt.
const bool cxScaleOk = st.scaleEnabled && st.scaleConnected;
if (st.cxNotice == 2) {
status_set(ST_COLDEX, true, LV_SYMBOL_WARNING " Cold Extraction beendet: kein Zulauf",
COL_WARN, lv_color_hex(0x000000));
@@ -3456,6 +3459,9 @@ void ui_update(const MachineState& st) {
String t = "Cold Extraction nicht möglich - ";
t += coldex_block_text(st.cxBlock, st.cxMaxTemp, st.tempW);
status_set(ST_COLDEX, true, t.c_str(), COL_WARN, lv_color_hex(0x000000));
} else if (st.cxNotice == 4) {
status_set(ST_COLDEX, true, LV_SYMBOL_WARNING " Cold Extraction ohne Waage: Bezug endet nach Zeit",
COL_WARN, lv_color_hex(0x000000));
} else if (st.cxActive && st.cxResting) {
status_set(ST_COLDEX, true, "Cold Extraction: Pumpenpause (Pumpenschutz)",
COL_ACCENT, lv_color_hex(0x000000));
@@ -3463,6 +3469,11 @@ void ui_update(const MachineState& st) {
String t = "Cold Extraction aktiv, Bezug gesperrt - ";
t += coldex_block_text(st.cxBlock, st.cxMaxTemp, st.tempW);
status_set(ST_COLDEX, true, t.c_str(), COL_WARN, lv_color_hex(0x000000));
} else if (st.cxActive && !cxScaleOk) {
// Dauerhinweis, solange ohne Waage gefahren wird: das Zielgewicht greift nicht,
// der Bezug endet erst nach der maximalen Bezugsdauer.
status_set(ST_COLDEX, true, "Cold Extraction aktiv - ohne Waage, Ende nach Zeit",
COL_WARN, lv_color_hex(0x000000));
} else if (st.cxActive) {
status_set(ST_COLDEX, true, "Cold Extraction aktiv - Wasserkreis heizt nicht",
lv_color_hex(0x1976d2), COL_TEXT);
@@ -3845,13 +3856,19 @@ void ui_update(const MachineState& st) {
if (usable) lv_obj_remove_state(swColdEx, LV_STATE_DISABLED);
else lv_obj_add_state(swColdEx, LV_STATE_DISABLED);
if (lblColdExHint && st.cxEnabled) {
const bool scaleOk = st.scaleEnabled && st.scaleConnected;
String hint;
if (st.cxActive) {
hint = "Wasserkreis heizt nicht (bleibt über Standby und Neustart aktiv). ";
if (st.cxAllowed) {
char b[96];
snprintf(b, sizeof(b), "Bezug regulär starten - Ziel %.0f g, max %.0f s.",
(double)st.cxTarget, (double)st.cxMaxSecs);
if (scaleOk) {
snprintf(b, sizeof(b), "Bezug regulär starten - Ziel %.0f g, max %.0f s.",
(double)st.cxTarget, (double)st.cxMaxSecs);
} else {
snprintf(b, sizeof(b), "Bezug regulär starten - Ende nach %.0f s.",
(double)st.cxMaxSecs);
}
hint += b;
} else {
hint += coldex_block_text(st.cxBlock, st.cxMaxTemp, st.tempW);
@@ -3865,18 +3882,25 @@ void ui_update(const MachineState& st) {
(double)st.tempW, (double)st.cxMaxTemp);
hint = b;
}
if (!scaleOk) {
hint += " Ohne Waage: Zielgewicht und Stillstands-Erkennung greifen nicht, "
"der Bezug endet erst nach der maximalen Bezugsdauer.";
}
label_set_if_changed(lblColdExHint, hint.c_str());
}
}
if (lblBrewColdEx) {
if (st.cxEnabled) {
char b[256]; // der Text ist ~205 Zeichen lang (das ° zaehlt als 2 Bytes)
char b[320]; // Grundtext ~205 Zeichen (das ° zaehlt als 2 Bytes) + Waagen-Hinweis
snprintf(b, sizeof(b),
"Cold Extraction (nur Anzeige, Einstellung in der Web-UI):\n"
"Freigabe < %.1f °C | Vorbenetzung %.0f s | Ziel %.0f g | max. %.0f s\n"
"Geschaltet wird der Modus auf der Seite \"Temperaturen\" unter \"Modi\".",
"Geschaltet wird der Modus auf der Seite \"Temperaturen\" unter \"Modi\".%s",
(double)st.cxMaxTemp, (double)st.cxPreInfSec,
(double)st.cxTarget, (double)st.cxMaxSecs);
(double)st.cxTarget, (double)st.cxMaxSecs,
(st.scaleEnabled && st.scaleConnected)
? ""
: "\nOhne Waage ist das Zielgewicht wirkungslos - der Bezug endet nach der max. Dauer.");
label_set_if_changed(lblBrewColdEx, b);
lv_obj_remove_flag(lblBrewColdEx, LV_OBJ_FLAG_HIDDEN);
} else {