diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md
index 29795b514..cc8ecfb8d 100644
--- a/CHANGELOG_LATEST.md
+++ b/CHANGELOG_LATEST.md
@@ -9,6 +9,7 @@
- humidity for ventilation devices
- telegrams for RC100H, hc2 (seen on discord)
- names for BC400, GB192i, read temperatures for low loss header and heatblock [#1317](https://github.com/emsesp/EMS-ESP32/discussions/1317)
+- option for `forceheatingoff` [#1262](https://github.com/emsesp/EMS-ESP32/issues/1262)
## Fixed
diff --git a/interface/package.json b/interface/package.json
index e14ed9cd0..971fa141a 100644
--- a/interface/package.json
+++ b/interface/package.json
@@ -26,9 +26,9 @@
"@mui/material": "^5.14.12",
"@table-library/react-table-library": "4.1.7",
"@types/lodash-es": "^4.17.9",
- "@types/node": "^20.8.2",
+ "@types/node": "^20.8.3",
"@types/react": "^18.2.25",
- "@types/react-dom": "^18.2.10",
+ "@types/react-dom": "^18.2.11",
"@types/react-router-dom": "^5.3.3",
"alova": "^2.13.1",
"async-validator": "^4.2.5",
@@ -53,7 +53,7 @@
"@types/babel__core": "^7",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
- "eslint": "^8.50.0",
+ "eslint": "^8.51.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.0.0",
diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts
index 52c149107..011ca2b14 100644
--- a/interface/src/i18n/de/index.ts
+++ b/interface/src/i18n/de/index.ts
@@ -126,6 +126,7 @@ const de: Translation = {
BYPASS_TOKEN: 'Zugriffstoken-Autorisierung bei API-Aufrufen umgehen',
READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)',
UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten',
+ HEATINGOFF: 'Heizen ausschalten beim EMS-ESP Start',
ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren',
ENABLE_SHOWER_ALERT: 'Duschalarm aktivieren',
TRIGGER_TIME: 'Auslösezeit',
diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts
index bbf506d57..61a14cf1e 100644
--- a/interface/src/i18n/en/index.ts
+++ b/interface/src/i18n/en/index.ts
@@ -126,6 +126,7 @@ const en: Translation = {
BYPASS_TOKEN: 'Bypass Access Token authorization on API calls',
READONLY: 'Enable read-only mode (blocks all outgoing EMS Tx Write commands)',
UNDERCLOCK_CPU: 'Underclock CPU speed',
+ HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Enable Shower Timer',
ENABLE_SHOWER_ALERT: 'Enable Shower Alert',
TRIGGER_TIME: 'Trigger Time',
diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts
index d7af38ef0..69784aadd 100644
--- a/interface/src/i18n/fr/index.ts
+++ b/interface/src/i18n/fr/index.ts
@@ -126,6 +126,7 @@ const fr: Translation = {
BYPASS_TOKEN: 'Contourner l\'autorisation du jeton d\'accès sur les appels API',
READONLY: 'Activer le mode lecture uniquement (bloque toutes les commandes EMS sortantes en écriture Tx)',
UNDERCLOCK_CPU: 'Underclock du CPU',
+ HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
ENABLE_SHOWER_TIMER: 'Activer la minuterie de la douche',
ENABLE_SHOWER_ALERT: 'Activer les alertes de durée de douche',
TRIGGER_TIME: 'Durée avant déclenchement',
diff --git a/interface/src/i18n/it/index.ts b/interface/src/i18n/it/index.ts
index 238f18347..6a1a433f2 100644
--- a/interface/src/i18n/it/index.ts
+++ b/interface/src/i18n/it/index.ts
@@ -128,6 +128,7 @@ const it: Translation = {
BYPASS_TOKEN: 'Ignora autorizzazione del token di accesso sulle chiamate API',
READONLY: 'Abilita modalità sola-lettura (blocca tutti i comandi di scrittura EMS Tx in uscita)',
UNDERCLOCK_CPU: 'Abbassa velocità della CPU',
+ HEATINGOFF: 'Avviamento caldaia con riscaldamento forzato spento',
ENABLE_SHOWER_TIMER: 'Abilita timer doccia',
ENABLE_SHOWER_ALERT: 'Abilita avviso doccia',
TRIGGER_TIME: 'Tempo di avvio',
diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts
index c28555ae9..3007ba17b 100644
--- a/interface/src/i18n/nl/index.ts
+++ b/interface/src/i18n/nl/index.ts
@@ -126,6 +126,7 @@ const nl: Translation = {
BYPASS_TOKEN: 'API Access Token authenticatie uitschakelen',
READONLY: 'Activeer read-only modus (blokkeert alle outgaande EMS Tx schrijf commandos)',
UNDERCLOCK_CPU: 'Underclock CPU snelheid',
+ HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
TRIGGER_TIME: 'Trigger tijd',
diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts
index a14df03e4..404e31978 100644
--- a/interface/src/i18n/no/index.ts
+++ b/interface/src/i18n/no/index.ts
@@ -126,6 +126,7 @@ const no: Translation = {
BYPASS_TOKEN: 'Utelat Aksess Token authorisering av API kall',
READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)',
UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet',
+ HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer',
ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling',
TRIGGER_TIME: 'Aktiveringstid',
diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts
index 5a4f1d170..4d073f702 100644
--- a/interface/src/i18n/pl/index.ts
+++ b/interface/src/i18n/pl/index.ts
@@ -126,6 +126,7 @@ const pl: BaseTranslation = {
BYPASS_TOKEN: 'Pomiń autoryzację tokenem w wywołaniach API',
READONLY: 'Tryb pracy "tylko do odczytu" (blokuje wszystkie komendy zapisu na magistralę EMS)',
UNDERCLOCK_CPU: 'Obniż taktowanie CPU',
+ HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
ENABLE_SHOWER_TIMER: 'Aktywuj minutnik prysznica',
ENABLE_SHOWER_ALERT: 'Aktywuj alarm prysznica',
TRIGGER_TIME: 'Wyzwalaj po czasie',
diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts
index 90e5dc265..881fc728f 100644
--- a/interface/src/i18n/sv/index.ts
+++ b/interface/src/i18n/sv/index.ts
@@ -126,6 +126,7 @@ const sv: Translation = {
BYPASS_TOKEN: 'Inaktivera Token-autensiering för API-anrop',
READONLY: 'Aktivera read-only (blockerar alla utgående skrivkommandon mot EMS-bussen)',
UNDERCLOCK_CPU: 'Nedklocka Processorhastighet',
+ HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
ENABLE_SHOWER_TIMER: 'Aktivera Dusch-timer',
ENABLE_SHOWER_ALERT: 'Aktivera Dusch-varning',
TRIGGER_TIME: 'Aktiveringstid',
diff --git a/interface/src/i18n/tr/index.ts b/interface/src/i18n/tr/index.ts
index a9464d2a3..ee03575c8 100644
--- a/interface/src/i18n/tr/index.ts
+++ b/interface/src/i18n/tr/index.ts
@@ -126,6 +126,7 @@ const tr: Translation = {
BYPASS_TOKEN: 'API bağlantılarında Erişim Jeton onaylamasını geç',
READONLY: 'Salt okunur modu devreye al (bütün giden EMS Tx Yazma komutlarını engeller)',
UNDERCLOCK_CPU: 'İşlemci hızını düşür',
+ HEATINGOFF: 'Start boiler with forced heating off', // TODO translate
ENABLE_SHOWER_TIMER: 'Duş Sayacını Devreye Al',
ENABLE_SHOWER_ALERT: 'Duş Alarmını Devreye Al',
TRIGGER_TIME: 'Tetikleme Zamanı',
diff --git a/interface/src/project/SettingsApplication.tsx b/interface/src/project/SettingsApplication.tsx
index 8ac035068..1f145af17 100644
--- a/interface/src/project/SettingsApplication.tsx
+++ b/interface/src/project/SettingsApplication.tsx
@@ -425,6 +425,11 @@ const SettingsApplication: FC = () => {
label={LL.UNDERCLOCK_CPU()}
disabled={saving}
/>
+ }
+ label={LL.HEATINGOFF()}
+ disabled={saving}
+ />
}
diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts
index 611dbc7fd..74f0dded6 100644
--- a/interface/src/project/types.ts
+++ b/interface/src/project/types.ts
@@ -7,6 +7,7 @@ export interface Settings {
syslog_mark_interval: number;
syslog_host: string;
syslog_port: number;
+ boiler_heatingoff: boolean;
shower_timer: boolean;
shower_alert: boolean;
shower_alert_coldshot: number;
diff --git a/interface/yarn.lock b/interface/yarn.lock
index 209be2f3d..b2b675036 100644
--- a/interface/yarn.lock
+++ b/interface/yarn.lock
@@ -769,10 +769,10 @@ __metadata:
languageName: node
linkType: hard
-"@eslint/js@npm:8.50.0":
- version: 8.50.0
- resolution: "@eslint/js@npm:8.50.0"
- checksum: 92cb0a823869e85f287bd172f14a6a20d7d65c3f4db886a0356a9efebfe8fe519e9ead84a5687bd18f45eca417bdcce96e3b83fe3feae8baf0f8f44d14073bae
+"@eslint/js@npm:8.51.0":
+ version: 8.51.0
+ resolution: "@eslint/js@npm:8.51.0"
+ checksum: c126d15213d938c72062b8f04388c084ba778771f2409ce508aa4b78152bf57e442b4c7996f632577b642101da5b41df101aece775fcc213a3159f55bcc4bdee
languageName: node
linkType: hard
@@ -1477,10 +1477,10 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:^20.8.2":
- version: 20.8.2
- resolution: "@types/node@npm:20.8.2"
- checksum: e9952db222dd3e1cca1107d1b2aaec4e93b4af8b4fc32b42dd4fac3719f98c14edb8c591829c972d2f6e2b527bbb34af53608f6a7973f4a7dbd1d3bc929bbe8d
+"@types/node@npm:^20.8.3":
+ version: 20.8.3
+ resolution: "@types/node@npm:20.8.3"
+ checksum: 83511d7c310100f3fddbbf3a28f0049d60da6ffc4255231845b2e2189d7ff104727647d2b05d2a29b0af2ad61b529a4897056b3798e0cba027c629f3b13d7e82
languageName: node
linkType: hard
@@ -1505,12 +1505,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/react-dom@npm:^18.2.10":
- version: 18.2.10
- resolution: "@types/react-dom@npm:18.2.10"
+"@types/react-dom@npm:^18.2.11":
+ version: 18.2.11
+ resolution: "@types/react-dom@npm:18.2.11"
dependencies:
"@types/react": "*"
- checksum: ac4056c3b8ba5461a58a7933e1f98d772e877f275a1b4169a936391900aec8f3c8e6125ccbbda530842cd9d108a2be8c994c7f48bfd801f89702e2de9064d834
+ checksum: 3ba42df0dc1e8a26baed9668b9e2b5aea7c8e28289cf5baa656c1a07c6f83cd3c3360a374e00f96c01ce914950105d14d3ecd59be75cf215b8d3657b0b5d7785
languageName: node
linkType: hard
@@ -1717,15 +1717,15 @@ __metadata:
"@table-library/react-table-library": 4.1.7
"@types/babel__core": ^7
"@types/lodash-es": ^4.17.9
- "@types/node": ^20.8.2
+ "@types/node": ^20.8.3
"@types/react": ^18.2.25
- "@types/react-dom": ^18.2.10
+ "@types/react-dom": ^18.2.11
"@types/react-router-dom": ^5.3.3
"@typescript-eslint/eslint-plugin": ^6.7.4
"@typescript-eslint/parser": ^6.7.4
alova: ^2.13.1
async-validator: ^4.2.5
- eslint: ^8.50.0
+ eslint: ^8.51.0
eslint-config-airbnb: ^19.0.4
eslint-config-airbnb-typescript: ^17.1.0
eslint-config-prettier: ^9.0.0
@@ -3088,14 +3088,14 @@ eslint-plugin-prettier@alpha:
languageName: node
linkType: hard
-"eslint@npm:^8.50.0":
- version: 8.50.0
- resolution: "eslint@npm:8.50.0"
+"eslint@npm:^8.51.0":
+ version: 8.51.0
+ resolution: "eslint@npm:8.51.0"
dependencies:
"@eslint-community/eslint-utils": ^4.2.0
"@eslint-community/regexpp": ^4.6.1
"@eslint/eslintrc": ^2.1.2
- "@eslint/js": 8.50.0
+ "@eslint/js": 8.51.0
"@humanwhocodes/config-array": ^0.11.11
"@humanwhocodes/module-importer": ^1.0.1
"@nodelib/fs.walk": ^1.2.8
@@ -3131,7 +3131,7 @@ eslint-plugin-prettier@alpha:
text-table: ^0.2.0
bin:
eslint: bin/eslint.js
- checksum: 91629528cb240bc61b25480574d35cd54ed444cb61a70fa76f7d5ab26af2b637b94bf8fba94403c9052c1baa944a169b6ab9cc8070496e925f7eeef730ff9038
+ checksum: b534962c60cb2ad219d20a33f93c80e8ea5dd89f390f7bab44c80df32134db0a87e73e7ccd2928d87498c0595128ee29b4dba8a1f1abbbb3da9c3fb0418ecdcc
languageName: node
linkType: hard
diff --git a/src/default_settings.h b/src/default_settings.h
index 009a5f25e..92ae49a8e 100644
--- a/src/default_settings.h
+++ b/src/default_settings.h
@@ -61,6 +61,10 @@
#define EMSESP_DEFAULT_TRACELOG_RAW false
#endif
+#ifndef EMSESP_DEFAULT_BOILER_HEATINGOFF
+#define EMSESP_DEFAULT_BOILER_HEATINGOFF false
+#endif
+
#ifndef EMSESP_DEFAULT_SHOWER_TIMER
#define EMSESP_DEFAULT_SHOWER_TIMER false
#endif
diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp
index 7bfa5b89b..7e016ae8d 100644
--- a/src/devices/boiler.cpp
+++ b/src/devices/boiler.cpp
@@ -119,7 +119,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// reset is a command uses a dummy variable which is always zero, shown as blank, but provides command enum options
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &reset_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
has_update(reset_, 0);
-
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
+ &forceHeatingOff_,
+ DeviceValueType::BOOL,
+ FL_(forceHeatingOff),
+ DeviceValueUOM::NONE,
+ MAKE_CF_CB(set_forceHeatingOff));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingActive_, DeviceValueType::BOOL, FL_(heatingActive), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &tapwaterActive_, DeviceValueType::BOOL, FL_(tapwaterActive), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
@@ -963,6 +968,11 @@ void Boiler::check_active() {
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
}
+ if (!Helpers::hasValue(forceHeatingOff_, EMS_VALUE_BOOL)) {
+ EMSESP::webSettingsService.read([&](WebSettings & settings) { forceHeatingOff_ = (settings.boiler_heatingoff || selFlowTemp_ == 0) ? 1 : 0; });
+ has_update(&forceHeatingOff_);
+ }
+
// calculate energy for boiler 0x08 from stored modulation an time in units of 0.01 Wh
if (model() != EMS_DEVICE_FLAG_HEATPUMP) {
// remember values from last call
@@ -1220,6 +1230,11 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr telegram) {
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
has_update(telegram, heatWorkMin_, 19, 3); // force to 3 bytes
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
+
+ if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
+ uint8_t data[] = {0, 0, 0, 0};
+ write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
+ }
}
/*
@@ -1250,6 +1265,11 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr telegram
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
has_update(telegram, heatingPumpMod_, 25);
// temperature measurements at 4, see #620
+
+ if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
+ uint8_t data[] = {0, 0, 0, 0};
+ write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
+ }
}
/*
@@ -1499,6 +1519,12 @@ void Boiler::process_UBASetPoints(std::shared_ptr telegram) {
has_update(telegram, setFlowTemp_, 0); // boiler set temp from thermostat
has_update(telegram, setBurnPow_, 1); // max burner power in %
has_update(telegram, wwSetPumpPower_, 2); // ww pump speed/power?
+
+ // overwrite other settings on receive?
+ if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0x08) {
+ uint8_t data[] = {0, 0, 0, 0};
+ write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
+ }
}
#pragma GCC diagnostic push
@@ -2049,9 +2075,6 @@ bool Boiler::set_ww_chargeOptimization(const char * value, const int8_t id) {
return true;
}
-
-
-
// set dhw max power
bool Boiler::set_ww_maxpower(const char * value, const int8_t id) {
int v;
@@ -2360,12 +2383,10 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
} else if (num == 1) {
// LOG_INFO("Reset boiler maintenance message");
write_command(0x05, 0x08, 0xFF, 0x1C);
- has_update(&reset_);
return true;
} else if (num == 2) {
// LOG_INFO("Reset boiler error message");
write_command(0x05, 0x00, 0x5A); // error reset
- has_update(&reset_);
return true;
}
return false;
@@ -2768,6 +2789,22 @@ bool Boiler::set_wwAltOpPrio(const char * value, const int8_t id) {
return false;
}
+bool Boiler::set_forceHeatingOff(const char * value, const int8_t id) {
+ bool v;
+ if (Helpers::value2bool(value, v)) {
+ has_update(forceHeatingOff_, v);
+ if (!v && Helpers::hasValue(heatingTemp_)) {
+ uint8_t data[] = {heatingTemp_,
+ (Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100),
+ (Helpers::hasValue(pumpModMax_) ? pumpModMax_ : (uint8_t)0),
+ 0};
+ write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
+ }
+ return true;
+ }
+ return false;
+}
+
// energy counters. Setting an invalid value does not update, but trigger a store.
bool Boiler::set_nrgHeat(const char * value, const int8_t id) {
float v;
diff --git a/src/devices/boiler.h b/src/devices/boiler.h
index de18b68e3..26bbc8ce1 100644
--- a/src/devices/boiler.h
+++ b/src/devices/boiler.h
@@ -88,7 +88,6 @@ class Boiler : public EMSdevice {
uint32_t wwWorkM_; // DHW minutes
int8_t wwHystOn_;
int8_t wwHystOff_;
- uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
uint16_t wwMixerTemp_; // mixing temperature
uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3)
uint16_t wwSolarTemp_;
@@ -96,6 +95,10 @@ class Boiler : public EMSdevice {
uint8_t wwAltOpPrioHeat_; // alternating operation, prioritize heat time
uint8_t wwAltOpPrioWw_; // alternating operation, prioritize dhw time
+ // special function
+ uint8_t forceHeatingOff_;
+ uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
+
// main
uint8_t reset_; // for reset command
uint8_t heatingActive_; // Central heating is on/off
@@ -470,6 +473,8 @@ class Boiler : public EMSdevice {
inline bool set_wwAltOpPrioWw(const char * value, const int8_t id) {
return set_wwAltOpPrio(value, 3);
}
+ bool set_forceHeatingOff(const char * value, const int8_t id);
+
/*
bool set_hybridStrategy(const char * value, const int8_t id);
bool set_switchOverTemp(const char * value, const int8_t id);
diff --git a/src/locale_translations.h b/src/locale_translations.h
index 8ff6e15cc..e8add620b 100644
--- a/src/locale_translations.h
+++ b/src/locale_translations.h
@@ -283,6 +283,7 @@ MAKE_TRANSLATION(haclimate, "haclimate", "Discovery current room temperature", "
// Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it
// Boiler
+MAKE_TRANSLATION(forceHeatingOff, "heatingoff", "force heating off", "Heizen abschalten", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(wwtapactivated, "wwtapactivated", "turn on/off", "Durchlauferhitzer aktiv", "zet aan/uit", "på/av", "system przygotowywania", "Varmtvann active", "ecs activée", "aç/kapa", "commuta on/off")
MAKE_TRANSLATION(reset, "reset", "reset", "Reset", "Reset", "Nollställ", "kasowanie komunikatu", "nullstill", "reset", "Sıfırla", "Reset")
MAKE_TRANSLATION(oilPreHeat, "oilpreheat", "oil preheating", "Ölvorwärmung", "Olie voorverwarming", "Förvärmning olja", "podgrzewanie oleju", "oljeforvarming", "préchauffage de l'huile", "Yakıt Ön ısıtma devrede", "preriscaldamento olio")
diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp
index 73db24fb3..8aa6a1fa3 100644
--- a/src/web/WebSettingsService.cpp
+++ b/src/web/WebSettingsService.cpp
@@ -46,6 +46,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
root["syslog_mark_interval"] = settings.syslog_mark_interval;
root["syslog_host"] = settings.syslog_host;
root["syslog_port"] = settings.syslog_port;
+ root["boiler_heatingoff"] = settings.boiler_heatingoff;
root["shower_timer"] = settings.shower_timer;
root["shower_alert"] = settings.shower_alert;
root["shower_alert_coldshot"] = settings.shower_alert_coldshot;
@@ -273,8 +274,9 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
EMSESP::trace_raw(settings.trace_raw);
- settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
- settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
+ settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
+ settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
+ settings.boiler_heatingoff = root["boiler_heatingoff"] | EMSESP_DEFAULT_BOILER_HEATINGOFF;
settings.fahrenheit = root["fahrenheit"];
EMSESP::system_.fahrenheit(settings.fahrenheit);
diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h
index 0e892328e..c887cdf09 100644
--- a/src/web/WebSettingsService.h
+++ b/src/web/WebSettingsService.h
@@ -33,6 +33,7 @@ class WebSettings {
String locale;
uint8_t tx_mode;
uint8_t ems_bus_id;
+ bool boiler_heatingoff;
bool shower_timer;
bool shower_alert;
uint8_t shower_alert_trigger;