feat: Cold Extraction im Standby scharf schaltbar (S3 5.3.5 / P4 1.1.3) #17
@@ -1,3 +1,19 @@
|
|||||||
|
Version 5.3.5:
|
||||||
|
- Cold Extraction lässt sich jetzt auch im Standby und bei noch warmem Kessel scharf schalten.
|
||||||
|
Bisher galt „Standby aktiv" und „Kessel zu warm" auch fürs Aktivieren - das war falsch herum
|
||||||
|
gedacht: Der Modus schaltet die Heizung AB, man kam aber erst an den Schalter, nachdem die
|
||||||
|
Maschine aufgeweckt war und bereits zu heizen begonnen hatte. Genau der Ablauf, den die
|
||||||
|
Funktion vermeiden soll.
|
||||||
|
- Scharfschalten und Beziehen sind jetzt zwei getrennte Bedingungen:
|
||||||
|
- Scharfschalten (neues State-Feld „cxArmable"): blockiert nur durch fehlende Freischaltung,
|
||||||
|
Wartungsmodus, Reinigungsassistent, PID-Tuning oder einen laufenden Bezug/Spülvorgang.
|
||||||
|
- Bezug (unverändert „cxAllowed"): zusätzlich kein Standby, Kessel unter der Freigabeschwelle,
|
||||||
|
gültiger Sensorwert.
|
||||||
|
Ist der Modus scharf, der Bezug aber noch gesperrt, sagen Web-UI und Display das jetzt
|
||||||
|
ausdrücklich („Modus aktiv, Bezug noch gesperrt: …").
|
||||||
|
- Wird bei warmem Kessel aktiviert, geht die Wasserheizung sofort aus und der Kessel kühlt ab;
|
||||||
|
die Rückmeldung weist darauf hin, dass Bezüge bis zum Unterschreiten der Schwelle gesperrt sind.
|
||||||
|
|
||||||
Version 5.3.4:
|
Version 5.3.4:
|
||||||
- Cold Extraction weist jetzt deutlich darauf hin, wenn keine Waage aktiv/verbunden ist. Ohne Waage
|
- Cold Extraction weist jetzt deutlich darauf hin, wenn keine Waage aktiv/verbunden ist. Ohne Waage
|
||||||
greifen weder das Zielgewicht noch die Stillstands-Erkennung - der Bezug endet dann erst nach der
|
greifen weder das Zielgewicht noch die Stillstands-Erkennung - der Bezug endet dann erst nach der
|
||||||
|
|||||||
@@ -323,8 +323,17 @@ Alle gültigen `action`-Strings aus `executeDashboardAction()` (Stand v4.8.1, Ze
|
|||||||
| `stopColdExtraction` | — | Modus beenden |
|
| `stopColdExtraction` | — | Modus beenden |
|
||||||
| `toggleColdExtraction` | — | Modus umschalten |
|
| `toggleColdExtraction` | — | Modus umschalten |
|
||||||
|
|
||||||
Alle drei Aktionen werden abgewiesen (`ok:false` + `message`), solange die Bedingungen nicht
|
Für das Bedienelement zählt `cxArmable`, **nicht** `cxAllowed`: Der Modus schaltet die
|
||||||
erfüllt sind — Hauptfall ist ein zu warmer Wasserkessel (`cxAllowed:false`). Der Modus ist
|
Wasserheizung ab und muss deshalb auch im Standby und bei noch warmem Kessel scharf zu
|
||||||
|
stellen sein — sonst heizt die Maschine nach dem Aufwecken an, bevor man an den Schalter
|
||||||
|
kommt. `cxAllowed` gilt nur für den einzelnen Bezug; ist der Modus scharf und `cxAllowed`
|
||||||
|
false, sollte die UI „Modus aktiv, Bezug noch gesperrt" zeigen.
|
||||||
|
|
||||||
|
Ein Aufwecken direkt in die kalte Extraktion sendet `startColdExtraction` **vor**
|
||||||
|
`deactivateStandby` — in dieser Reihenfolge, damit der Wasserkreis gar nicht erst anheizt.
|
||||||
|
|
||||||
|
Die Aktionen werden abgewiesen (`ok:false` + `message`), solange die Bedingungen nicht
|
||||||
|
erfüllt sind. Der Modus ist
|
||||||
**persistent** (EEPROM) und überlebt Standby und Neustart; er endet nur durch explizites
|
**persistent** (EEPROM) und überlebt Standby und Neustart; er endet nur durch explizites
|
||||||
Ausschalten, durch Entzug der Freischaltung auf `/Brew-Control` oder durch einen Werksreset.
|
Ausschalten, durch Entzug der Freischaltung auf `/Brew-Control` oder durch einen Werksreset.
|
||||||
Das P4-UI sollte `cxActive` deshalb dauerhaft sichtbar spiegeln — sonst wundert sich der
|
Das P4-UI sollte `cxActive` deshalb dauerhaft sichtbar spiegeln — sonst wundert sich der
|
||||||
@@ -383,7 +392,8 @@ Aus `getTouchUartStatus()`. Für sprach-unabhängige UI-Logik/Icons auf dem P4 v
|
|||||||
| `cxEnabled` | bool | Funktion in der Web-UI freigeschaltet (sonst Bedienelement ausblenden) |
|
| `cxEnabled` | bool | Funktion in der Web-UI freigeschaltet (sonst Bedienelement ausblenden) |
|
||||||
| `cxActive` | bool | Modus scharf: Wasserkreis heizt nicht |
|
| `cxActive` | bool | Modus scharf: Wasserkreis heizt nicht |
|
||||||
| `cxShot` | bool | Der laufende Bezug ist eine kalte Extraktion |
|
| `cxShot` | bool | Der laufende Bezug ist eine kalte Extraktion |
|
||||||
| `cxAllowed` | bool | Freigabe **jetzt** (Wasserkessel kalt genug, kein Standby/Wartung/Tuning) |
|
| `cxAllowed` | bool | **Bezug** jetzt möglich (Wasserkessel kalt genug, kein Standby/Wartung/Tuning) |
|
||||||
|
| `cxArmable` | bool | **Modus umschaltbar** — auch im Standby und bei warmem Kessel (ab S3 5.3.5) |
|
||||||
| `cxResting` | bool | Zwangspause des Pumpenschutzes läuft (Bezug läuft weiter) |
|
| `cxResting` | bool | Zwangspause des Pumpenschutzes läuft (Bezug läuft weiter) |
|
||||||
| `cxMaxTemp` | float | Freigabeschwelle in °C (für eigene Hinweistexte am Display) |
|
| `cxMaxTemp` | float | Freigabeschwelle in °C (für eigene Hinweistexte am Display) |
|
||||||
| `cxTarget` | float | Zielgewicht in g |
|
| `cxTarget` | float | Zielgewicht in g |
|
||||||
|
|||||||
+55
-7
@@ -369,7 +369,7 @@ Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire);
|
|||||||
* Firmware-Informationen
|
* Firmware-Informationen
|
||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
|
|
||||||
String version = "5.3.4";
|
String version = "5.3.5";
|
||||||
String versionHersteller = "Thomas Müller";
|
String versionHersteller = "Thomas Müller";
|
||||||
String versionHerstellerMail =
|
String versionHerstellerMail =
|
||||||
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
"<a href='mailto:thomas@mueller.black' class='info-link'>thomas@mueller.black</a>";
|
||||||
@@ -4119,7 +4119,7 @@ bool setColdExtractionMode(bool enable, String& message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
String reason = coldExtractionBlockReason();
|
String reason = coldExtractionArmBlockReason();
|
||||||
if (reason.length() > 0) {
|
if (reason.length() > 0) {
|
||||||
message = reason;
|
message = reason;
|
||||||
return false;
|
return false;
|
||||||
@@ -4132,6 +4132,13 @@ bool setColdExtractionMode(bool enable, String& message) {
|
|||||||
resetColdExtractionRuntime();
|
resetColdExtractionRuntime();
|
||||||
persistColdExtractionMode();
|
persistColdExtractionMode();
|
||||||
message = "Cold Extraction aktiviert - Wasserkreis heizt nicht (bleibt auch nach Standby/Neustart aktiv).";
|
message = "Cold Extraction aktiviert - Wasserkreis heizt nicht (bleibt auch nach Standby/Neustart aktiv).";
|
||||||
|
if (standbyModeActive) {
|
||||||
|
message += " Nach dem Aufwecken bleibt der Wasserkreis aus.";
|
||||||
|
} else if (!coldExtractionTemperatureOk()) {
|
||||||
|
// Scharfschalten bei warmem Kessel ist erlaubt (Heizung geht sofort aus), ein
|
||||||
|
// Bezug aber erst, wenn der Kessel unter die Schwelle gefallen ist.
|
||||||
|
message += " Der Kessel ist noch zu warm - er kuehlt jetzt ab, Bezuege sind bis dahin gesperrt.";
|
||||||
|
}
|
||||||
if (!coldExtractionScaleUsable()) {
|
if (!coldExtractionScaleUsable()) {
|
||||||
// Ohne Waage faellt die Dosierung auf die maximale Bezugsdauer zurueck - das
|
// Ohne Waage faellt die Dosierung auf die maximale Bezugsdauer zurueck - das
|
||||||
// muss der Benutzer wissen, sonst wundert er sich ueber das Ergebnis.
|
// muss der Benutzer wissen, sonst wundert er sich ueber das Ergebnis.
|
||||||
@@ -4160,6 +4167,37 @@ bool coldExtractionShotAllowed() {
|
|||||||
return coldExtractionModeActive && coldExtractionConditionsOk();
|
return coldExtractionModeActive && coldExtractionConditionsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/************************************************************************************
|
||||||
|
* Scharfschalten vs. Beziehen - bewusst zwei verschiedene Bedingungen
|
||||||
|
************************************************************************************
|
||||||
|
* Der Modus schaltet die Wasserheizung AB. Das an Standby oder an die Kesseltemperatur
|
||||||
|
* zu koppeln waere falsch herum: Im Standby kaeme man gar nicht an den Schalter, und
|
||||||
|
* nach dem Aufwecken heizt die Maschine sofort los - man muesste erst aufheizen lassen,
|
||||||
|
* um danach auf das Abkuehlen zu warten. Genau der Fall, den die Funktion vermeiden soll.
|
||||||
|
* Deshalb: Scharfschalten ist im Standby und bei warmem Kessel erlaubt; ueber den
|
||||||
|
* einzelnen Bezug entscheidet weiterhin coldExtractionShotAllowed().
|
||||||
|
************************************************************************************/
|
||||||
|
bool coldExtractionArmable() {
|
||||||
|
return coldExtractionEnabled && !wartungsModusAktiv && !cleaningAssistantActive &&
|
||||||
|
!autoTuneWasserActive && !autoTuneDampfActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
String coldExtractionArmBlockReason() {
|
||||||
|
if (!coldExtractionEnabled) {
|
||||||
|
return "Cold Extraction ist nicht freigeschaltet.";
|
||||||
|
}
|
||||||
|
if (wartungsModusAktiv) {
|
||||||
|
return "Cold Extraction nicht moeglich: Wartungsmodus aktiv.";
|
||||||
|
}
|
||||||
|
if (cleaningAssistantActive) {
|
||||||
|
return "Cold Extraction nicht moeglich: Reinigungsassistent aktiv.";
|
||||||
|
}
|
||||||
|
if (autoTuneWasserActive || autoTuneDampfActive) {
|
||||||
|
return "Cold Extraction nicht moeglich: PID-Tuning laeuft.";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
// Pumpensteuerung des kalten Bezugs: Vorbenetzung ungepulst, danach Puls-Duty.
|
// Pumpensteuerung des kalten Bezugs: Vorbenetzung ungepulst, danach Puls-Duty.
|
||||||
// Zusaetzlich Pumpenschutz ueber die kumulierte Einschaltzeit (Zwangspause) - die Olab
|
// Zusaetzlich Pumpenschutz ueber die kumulierte Einschaltzeit (Zwangspause) - die Olab
|
||||||
// hat zwar einen Thermoschutz, ein Auslösen mitten im Bezug wuerde aber den Shot
|
// hat zwar einen Thermoschutz, ein Auslösen mitten im Bezug wuerde aber den Shot
|
||||||
@@ -15011,15 +15049,21 @@ static const char dashboardJavaScript[] PROGMEM = R"rawliteral(
|
|||||||
if (coldexRow) { coldexRow.style.display = (data.cxEnabled === true) ? '' : 'none'; }
|
if (coldexRow) { coldexRow.style.display = (data.cxEnabled === true) ? '' : 'none'; }
|
||||||
if (coldexToggleEl) {
|
if (coldexToggleEl) {
|
||||||
updateToggleState(coldexToggleEl, data.cxActive);
|
updateToggleState(coldexToggleEl, data.cxActive);
|
||||||
coldexToggleEl.disabled = (data.cxEnabled !== true) || (data.cxActive !== true && data.cxAllowed !== true);
|
// Umschalten haengt an cxArmable, NICHT an cxAllowed: Der Modus muss auch im
|
||||||
|
// Standby und bei noch warmem Kessel scharf zu stellen sein.
|
||||||
|
coldexToggleEl.disabled = (data.cxEnabled !== true) || (data.cxArmable !== true);
|
||||||
}
|
}
|
||||||
if (coldexHint) {
|
if (coldexHint) {
|
||||||
let hintText = '';
|
let hintText = '';
|
||||||
if (data.cxEnabled === true && data.cxAllowed !== true && data.cxActive !== true) {
|
if (data.cxEnabled === true && data.cxAllowed !== true) {
|
||||||
if (typeof data.tempW === 'number' && typeof data.cxMaxTemp === 'number' && data.tempW >= data.cxMaxTemp) {
|
// Gilt auch bei aktivem Modus: dann ist er scharf, aber der Bezug noch gesperrt
|
||||||
hintText = 'Wasserkessel zu warm (' + data.tempW.toFixed(1) + ' °C, benötigt < ' + data.cxMaxTemp.toFixed(1) + ' °C).';
|
const prefix = (data.cxActive === true) ? 'Modus aktiv, Bezug noch gesperrt: ' : '';
|
||||||
|
if (data.standbyActive === true) {
|
||||||
|
hintText = prefix + 'Standby aktiv - Bezug erst nach dem Aufwecken.';
|
||||||
|
} else if (typeof data.tempW === 'number' && typeof data.cxMaxTemp === 'number' && data.tempW >= data.cxMaxTemp) {
|
||||||
|
hintText = prefix + 'Wasserkessel zu warm (' + data.tempW.toFixed(1) + ' °C, benötigt < ' + data.cxMaxTemp.toFixed(1) + ' °C).';
|
||||||
} else {
|
} else {
|
||||||
hintText = 'Cold Extraction derzeit nicht möglich.';
|
hintText = prefix + 'Bezug derzeit nicht möglich.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Waagen-Hinweis gilt zusätzlich und unabhängig von der Temperatur-Freigabe
|
// Waagen-Hinweis gilt zusätzlich und unabhängig von der Temperatur-Freigabe
|
||||||
@@ -16191,6 +16235,8 @@ void buildDashboardJson(char *buffer, size_t bufferSize) {
|
|||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxEnabled\":%s,", coldExtractionEnabled ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxEnabled\":%s,", coldExtractionEnabled ? "true" : "false");
|
||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxActive\":%s,", coldExtractionModeActive ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxActive\":%s,", coldExtractionModeActive ? "true" : "false");
|
||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxAllowed\":%s,", coldExtractionConditionsOk() ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxAllowed\":%s,", coldExtractionConditionsOk() ? "true" : "false");
|
||||||
|
// cxArmable = Modus umschaltbar (auch im Standby und bei warmem Kessel); cxAllowed = Bezug moeglich
|
||||||
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxArmable\":%s,", coldExtractionArmable() ? "true" : "false");
|
||||||
dtostrf(coldExtractionMaxTempC, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMaxTemp\":%s,", floatBuf);
|
dtostrf(coldExtractionMaxTempC, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMaxTemp\":%s,", floatBuf);
|
||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleModeActive\":%s,", scaleModeActive ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"scaleModeActive\":%s,", scaleModeActive ? "true" : "false");
|
||||||
dtostrf(brewByTimeTargetSeconds, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"bbtSecs\":%s,", floatBuf);
|
dtostrf(brewByTimeTargetSeconds, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"bbtSecs\":%s,", floatBuf);
|
||||||
@@ -16517,6 +16563,8 @@ static void buildTouchUartStateJson(char *buffer, size_t bufferSize) {
|
|||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxActive\":%s,", coldExtractionModeActive ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxActive\":%s,", coldExtractionModeActive ? "true" : "false");
|
||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxShot\":%s,", coldExtractionShotActive ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxShot\":%s,", coldExtractionShotActive ? "true" : "false");
|
||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxAllowed\":%s,", coldExtractionConditionsOk() ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxAllowed\":%s,", coldExtractionConditionsOk() ? "true" : "false");
|
||||||
|
// cxArmable = Modus umschaltbar (auch im Standby und bei warmem Kessel); cxAllowed = Bezug moeglich
|
||||||
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxArmable\":%s,", coldExtractionArmable() ? "true" : "false");
|
||||||
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxResting\":%s,", coldExtractionPumpResting ? "true" : "false");
|
offset += snprintf(buffer + offset, bufferSize - offset, "\"cxResting\":%s,", coldExtractionPumpResting ? "true" : "false");
|
||||||
dtostrf(coldExtractionMaxTempC, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMaxTemp\":%s,", floatBuf);
|
dtostrf(coldExtractionMaxTempC, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxMaxTemp\":%s,", floatBuf);
|
||||||
dtostrf(coldExtractionTargetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxTarget\":%s,", floatBuf);
|
dtostrf(coldExtractionTargetGrams, 4, 1, floatBuf); offset += snprintf(buffer + offset, bufferSize - offset, "\"cxTarget\":%s,", floatBuf);
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
Version 1.1.3:
|
||||||
|
- Aufweck-Dialog hat einen dritten Knopf: „Aufwecken mit Cold Extraction" (braucht S3 ab 5.3.5).
|
||||||
|
Er stellt den Modus scharf und beendet erst danach den Standby - in dieser Reihenfolge, damit
|
||||||
|
der Wasserkreis gar nicht erst anheizt. Ohne diesen Weg musste man normal aufwecken (Maschine
|
||||||
|
heizt an) und den Modus danach umschalten. Der Knopf erscheint nur, wenn die Funktion
|
||||||
|
freigeschaltet und umschaltbar ist und der Modus nicht ohnehin schon läuft.
|
||||||
|
- Der Schalter „Cold Extraction" auf der Temperatur-Seite hängt jetzt am neuen State-Feld
|
||||||
|
cxArmable statt an cxAllowed: umschaltbar auch im Standby und bei warmem Kessel, gesperrt nur
|
||||||
|
während eines laufenden kalten Bezugs. Bei älterer S3-Firmware fällt das Feld auf cxEnabled
|
||||||
|
zurück, der Schalter bleibt also bedienbar.
|
||||||
|
- Erklärzeile im Standby: „Nach dem Aufwecken bleibt der Wasserkreis aus."
|
||||||
|
|
||||||
Version 1.1.2:
|
Version 1.1.2:
|
||||||
- Cold Extraction: Fehlt eine nutzbare Waage, weist das Display jetzt dauerhaft darauf hin.
|
- Cold Extraction: Fehlt eine nutzbare Waage, weist das Display jetzt dauerhaft darauf hin.
|
||||||
Ohne Waage greifen weder Zielgewicht noch Stillstands-Erkennung, der Bezug endet erst nach der
|
Ohne Waage greifen weder Zielgewicht noch Stillstands-Erkennung, der Bezug endet erst nach der
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
// -------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------
|
||||||
// Firmware
|
// Firmware
|
||||||
// -------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------
|
||||||
#define DISPLAY_FW_VERSION "1.1.2" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
|
#define DISPLAY_FW_VERSION "1.1.3" // Firmware-Stand der P4-Display-Steuerung (Info-Seite)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------
|
||||||
// Protokoll
|
// Protokoll
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ struct MachineState {
|
|||||||
bool cxEnabled = false; // Funktion auf der S3 freigeschaltet (sonst Bedienelement ausblenden)
|
bool cxEnabled = false; // Funktion auf der S3 freigeschaltet (sonst Bedienelement ausblenden)
|
||||||
bool cxActive = false; // Modus scharf: Wasserkreis heizt nicht (persistent, seit S3 5.3.1)
|
bool cxActive = false; // Modus scharf: Wasserkreis heizt nicht (persistent, seit S3 5.3.1)
|
||||||
bool cxShot = false; // der laufende Bezug ist eine kalte Extraktion
|
bool cxShot = false; // der laufende Bezug ist eine kalte Extraktion
|
||||||
bool cxAllowed = false; // Freigabe jetzt (Wasserkessel kalt genug, kein Standby/Wartung/Tuning)
|
bool cxAllowed = false; // Bezug jetzt moeglich (Wasserkessel kalt genug, kein Standby/Wartung/Tuning)
|
||||||
|
bool cxArmable = false; // Modus umschaltbar - auch im Standby und bei warmem Kessel (seit S3 5.3.5)
|
||||||
bool cxResting = false; // Zwangspause des Pumpenschutzes laeuft (Bezug laeuft weiter)
|
bool cxResting = false; // Zwangspause des Pumpenschutzes laeuft (Bezug laeuft weiter)
|
||||||
float cxMaxTemp = 0; // Freigabeschwelle in C
|
float cxMaxTemp = 0; // Freigabeschwelle in C
|
||||||
float cxTarget = 0; // Zielgewicht in g
|
float cxTarget = 0; // Zielgewicht in g
|
||||||
|
|||||||
@@ -225,6 +225,8 @@ void ProtocolClient::applyState(const String& json) {
|
|||||||
s.cxActive = doc["cxActive"] | false;
|
s.cxActive = doc["cxActive"] | false;
|
||||||
s.cxShot = doc["cxShot"] | false;
|
s.cxShot = doc["cxShot"] | false;
|
||||||
s.cxAllowed = doc["cxAllowed"] | false;
|
s.cxAllowed = doc["cxAllowed"] | false;
|
||||||
|
// Fehlt bei S3-FW < 5.3.5 -> auf cxEnabled zurueckfallen, sonst waere der Schalter tot
|
||||||
|
s.cxArmable = doc["cxArmable"] | (doc["cxEnabled"] | false);
|
||||||
s.cxResting = doc["cxResting"] | false;
|
s.cxResting = doc["cxResting"] | false;
|
||||||
s.cxMaxTemp = doc["cxMaxTemp"] | s.cxMaxTemp;
|
s.cxMaxTemp = doc["cxMaxTemp"] | s.cxMaxTemp;
|
||||||
s.cxTarget = doc["cxTarget"] | s.cxTarget;
|
s.cxTarget = doc["cxTarget"] | s.cxTarget;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ static lv_timer_t* g_toastTimer = nullptr;
|
|||||||
static lv_obj_t* g_standbyScreen = nullptr; // Vollbild-Overlay im Standby (Uhr)
|
static lv_obj_t* g_standbyScreen = nullptr; // Vollbild-Overlay im Standby (Uhr)
|
||||||
static lv_obj_t* g_standbyClock = nullptr;
|
static lv_obj_t* g_standbyClock = nullptr;
|
||||||
static lv_obj_t* g_wakeConfirm = nullptr; // Bestaetigungsdialog "Aus dem Standby aufwecken?"
|
static lv_obj_t* g_wakeConfirm = nullptr; // Bestaetigungsdialog "Aus dem Standby aufwecken?"
|
||||||
|
static lv_obj_t* g_wakeColdBtn = nullptr; // "Aufwecken mit Cold Extraction" (nur wenn freigeschaltet)
|
||||||
static bool g_wakeDialogOpen = false; // Dialog sichtbar -> Display hell schalten (lesbar)
|
static bool g_wakeDialogOpen = false; // Dialog sichtbar -> Display hell schalten (lesbar)
|
||||||
static bool g_standbyShown = false; // Standby-Uhr-Overlay zuletzt als sichtbar geschaltet (fuer Ein-/Ausblend-Uebergang)
|
static bool g_standbyShown = false; // Standby-Uhr-Overlay zuletzt als sichtbar geschaltet (fuer Ein-/Ausblend-Uebergang)
|
||||||
|
|
||||||
@@ -572,6 +573,17 @@ static void wake_yes_cb(lv_event_t*) { // Aufwecken -> hell lassen, S3 bee
|
|||||||
// das Display in der Uebergangsphase kurz auf Standby-Helligkeit abdunkeln.
|
// das Display in der Uebergangsphase kurz auf Standby-Helligkeit abdunkeln.
|
||||||
if (g_client) { g_client->sendAction("deactivateStandby"); showToast("Maschine wird aufgeweckt...", false); }
|
if (g_client) { g_client->sendAction("deactivateStandby"); showToast("Maschine wird aufgeweckt...", false); }
|
||||||
}
|
}
|
||||||
|
// Aufwecken direkt in die kalte Extraktion. Reihenfolge ist wichtig: erst den Modus scharf
|
||||||
|
// stellen, dann den Standby beenden - sonst heizt der Wasserkreis in der Zwischenzeit an,
|
||||||
|
// und genau das soll die Funktion ja verhindern.
|
||||||
|
static void wake_cold_cb(lv_event_t*) {
|
||||||
|
wake_confirm_hide();
|
||||||
|
if (g_client) {
|
||||||
|
g_client->sendAction("startColdExtraction");
|
||||||
|
g_client->sendAction("deactivateStandby");
|
||||||
|
showToast("Aufwecken mit Cold Extraction...", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
bool ui_wake_dialog_open() { return g_wakeDialogOpen; } // fuer die Helligkeitssteuerung in onState
|
bool ui_wake_dialog_open() { return g_wakeDialogOpen; } // fuer die Helligkeitssteuerung in onState
|
||||||
|
|
||||||
// =====================================================================================
|
// =====================================================================================
|
||||||
@@ -3224,6 +3236,13 @@ void ui_init(ProtocolClient* client) {
|
|||||||
lv_obj_t* wakeYes = th_accent_button(wakeBtns, "Aufwecken");
|
lv_obj_t* wakeYes = th_accent_button(wakeBtns, "Aufwecken");
|
||||||
lv_obj_set_width(wakeYes, 150);
|
lv_obj_set_width(wakeYes, 150);
|
||||||
lv_obj_add_event_cb(wakeYes, wake_yes_cb, LV_EVENT_CLICKED, nullptr);
|
lv_obj_add_event_cb(wakeYes, wake_yes_cb, LV_EVENT_CLICKED, nullptr);
|
||||||
|
// Zweite Zeile: direkt in die kalte Extraktion aufwecken. Ohne diesen Weg muesste man
|
||||||
|
// erst normal aufwecken (Maschine heizt an) und den Modus danach umschalten.
|
||||||
|
// Nur sichtbar, wenn die S3 die Funktion freigeschaltet meldet (siehe ui_update).
|
||||||
|
g_wakeColdBtn = th_button(wakeCard, "Aufwecken mit Cold Extraction", COL_CARD2, COL_ACCENT);
|
||||||
|
lv_obj_set_width(g_wakeColdBtn, LV_PCT(100));
|
||||||
|
lv_obj_add_event_cb(g_wakeColdBtn, wake_cold_cb, LV_EVENT_CLICKED, nullptr);
|
||||||
|
lv_obj_add_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN);
|
||||||
lv_obj_add_flag(g_wakeConfirm, LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(g_wakeConfirm, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
|
||||||
// OTA-Overlay (Vollbild, waehrend Firmware-Update)
|
// OTA-Overlay (Vollbild, waehrend Firmware-Update)
|
||||||
@@ -3449,6 +3468,12 @@ void ui_update(const MachineState& st) {
|
|||||||
// Ohne nutzbare Waage laeuft die kalte Extraktion rein zeitgesteuert (Zielgewicht und
|
// Ohne nutzbare Waage laeuft die kalte Extraktion rein zeitgesteuert (Zielgewicht und
|
||||||
// Stillstands-Erkennung greifen nicht) - das wird dauerhaft mit angezeigt.
|
// Stillstands-Erkennung greifen nicht) - das wird dauerhaft mit angezeigt.
|
||||||
const bool cxScaleOk = st.scaleEnabled && st.scaleConnected;
|
const bool cxScaleOk = st.scaleEnabled && st.scaleConnected;
|
||||||
|
// "Aufwecken mit Cold Extraction" nur anbieten, wenn die Funktion freigeschaltet und
|
||||||
|
// umschaltbar ist und der Modus nicht ohnehin schon laeuft.
|
||||||
|
if (g_wakeColdBtn) {
|
||||||
|
if (st.cxEnabled && st.cxArmable && !st.cxActive) lv_obj_remove_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
else lv_obj_add_flag(g_wakeColdBtn, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
}
|
||||||
if (st.cxNotice == 2) {
|
if (st.cxNotice == 2) {
|
||||||
status_set(ST_COLDEX, true, LV_SYMBOL_WARNING " Cold Extraction beendet: kein Zulauf",
|
status_set(ST_COLDEX, true, LV_SYMBOL_WARNING " Cold Extraction beendet: kein Zulauf",
|
||||||
COL_WARN, lv_color_hex(0x000000));
|
COL_WARN, lv_color_hex(0x000000));
|
||||||
@@ -3851,8 +3876,9 @@ void ui_update(const MachineState& st) {
|
|||||||
if (lblColdExHint) lv_obj_add_flag(lblColdExHint, LV_OBJ_FLAG_HIDDEN);
|
if (lblColdExHint) lv_obj_add_flag(lblColdExHint, LV_OBJ_FLAG_HIDDEN);
|
||||||
}
|
}
|
||||||
sw_set(swColdEx, st.cxActive);
|
sw_set(swColdEx, st.cxActive);
|
||||||
// Einschalten nur bei Freigabe; Ausschalten immer - ausser waehrend eines kalten Bezugs
|
// Umschalten haengt an cxArmable, NICHT an cxAllowed: Der Modus muss auch im Standby
|
||||||
bool usable = st.cxEnabled && (st.cxActive ? !st.cxShot : st.cxAllowed);
|
// und bei warmem Kessel scharf zu stellen sein. Waehrend eines kalten Bezugs gesperrt.
|
||||||
|
bool usable = st.cxEnabled && st.cxArmable && !(st.cxActive && st.cxShot);
|
||||||
if (usable) lv_obj_remove_state(swColdEx, LV_STATE_DISABLED);
|
if (usable) lv_obj_remove_state(swColdEx, LV_STATE_DISABLED);
|
||||||
else lv_obj_add_state(swColdEx, LV_STATE_DISABLED);
|
else lv_obj_add_state(swColdEx, LV_STATE_DISABLED);
|
||||||
if (lblColdExHint && st.cxEnabled) {
|
if (lblColdExHint && st.cxEnabled) {
|
||||||
@@ -3860,7 +3886,9 @@ void ui_update(const MachineState& st) {
|
|||||||
String hint;
|
String hint;
|
||||||
if (st.cxActive) {
|
if (st.cxActive) {
|
||||||
hint = "Wasserkreis heizt nicht (bleibt über Standby und Neustart aktiv). ";
|
hint = "Wasserkreis heizt nicht (bleibt über Standby und Neustart aktiv). ";
|
||||||
if (st.cxAllowed) {
|
if (st.standbyActive) {
|
||||||
|
hint += "Nach dem Aufwecken bleibt der Wasserkreis aus.";
|
||||||
|
} else if (st.cxAllowed) {
|
||||||
char b[96];
|
char b[96];
|
||||||
if (scaleOk) {
|
if (scaleOk) {
|
||||||
snprintf(b, sizeof(b), "Bezug regulär starten - Ziel %.0f g, max %.0f s.",
|
snprintf(b, sizeof(b), "Bezug regulär starten - Ziel %.0f g, max %.0f s.",
|
||||||
|
|||||||
Reference in New Issue
Block a user