From 167f194bdfdc7b998e4a951c6742b0ba62fe167d Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 14 Sep 2026 19:33:20 +0200 Subject: [PATCH] add auto check for firmware updates --- CHANGELOG_LATEST.md | 1 + interface/src/app/main/types.ts | 1 + interface/src/app/settings/ApplicationSettings.tsx | 12 ++++++++++++ interface/src/i18n/cs/index.ts | 1 + interface/src/i18n/de/index.ts | 1 + interface/src/i18n/en/index.ts | 1 + interface/src/i18n/fr/index.ts | 1 + interface/src/i18n/it/index.ts | 1 + interface/src/i18n/nl/index.ts | 1 + interface/src/i18n/no/index.ts | 1 + interface/src/i18n/pl/index.ts | 1 + interface/src/i18n/sk/index.ts | 1 + interface/src/i18n/sv/index.ts | 1 + interface/src/i18n/tr/index.ts | 1 + mock-api/restServer.ts | 1 + src/core/default_settings.h | 4 ++++ src/core/system.cpp | 2 ++ src/core/system.h | 9 +++++++++ src/web/WebSettingsService.cpp | 7 +++++++ src/web/WebSettingsService.h | 1 + src/web/WebStatusService.cpp | 9 +++++++-- 21 files changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 8b3705c4d..2d40cf906 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -17,6 +17,7 @@ This release is based on the latest Espressif/Arduino core version 3. It brings - pc0Flow in l/min [#3224](https://github.com/emsesp/EMS-ESP32/issues/3224) - remove HA config for excluded entities [#3229](https://github.com/emsesp/EMS-ESP32/issues/3229) - `show system` reports the Ethernet status on boards with a PHY, so a failed initialisation or a link that never came up is visible instead of Ethernet being omitted completely. Also shows the loop task's lowest free stack, to help diagnose reboots +- new Application Setting "Automatically check for firmware updates", off by default. EMS-ESP only contacts emsesp.org for the latest version when it's enabled, so the Version page shows the available versions ## Fixed diff --git a/interface/src/app/main/types.ts b/interface/src/app/main/types.ts index 93ce12f4f..20bb515e3 100644 --- a/interface/src/app/main/types.ts +++ b/interface/src/app/main/types.ts @@ -55,6 +55,7 @@ export interface Settings { email_subject: string; developer_mode: boolean; disable_reset: boolean; + auto_fw_check: boolean; } export enum busConnectionStatus { diff --git a/interface/src/app/settings/ApplicationSettings.tsx b/interface/src/app/settings/ApplicationSettings.tsx index d9626d41b..84f790fc8 100644 --- a/interface/src/app/settings/ApplicationSettings.tsx +++ b/interface/src/app/settings/ApplicationSettings.tsx @@ -512,6 +512,18 @@ const ApplicationSettings = () => { )} + Firmware + + } + label={LL.AUTO_FW_CHECK()} + /> + {LL.SENSORS()} diff --git a/interface/src/i18n/cs/index.ts b/interface/src/i18n/cs/index.ts index 395a7cf77..6d69a3612 100644 --- a/interface/src/i18n/cs/index.ts +++ b/interface/src/i18n/cs/index.ts @@ -321,6 +321,7 @@ const cs: Translation = { DASHBOARD: 'Dashboard', DEVELOPER_MODE: 'Režim vývojáře', DISABLE_RESET: 'Zakázat obnovení továrního nastavení', + AUTO_FW_CHECK: 'Automaticky kontrolovat aktualizace firmwaru', BYTES: 'Bajty', BITMASK: 'Bit Mask', DUPLICATE: 'Duplikát', diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index a356c4af0..2a2e62d9e 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -321,6 +321,7 @@ const de: Translation = { DASHBOARD: 'Dashboard', DEVELOPER_MODE: 'Entwicklermodus', DISABLE_RESET: 'Werkseinstellungen deaktivieren', + AUTO_FW_CHECK: 'Automatisch nach Firmware-Updates suchen', BYTES: 'Bytes', BITMASK: 'Bit Maske', DUPLICATE: 'Kopieren', diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index ee4dde8b5..e629d5bd2 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -321,6 +321,7 @@ const en: Translation = { DASHBOARD: 'Dashboard', DEVELOPER_MODE: 'Developer Mode', DISABLE_RESET: 'Disable Factory Reset', + AUTO_FW_CHECK: 'Automatically check for firmware updates', BYTES: 'Bytes', BITMASK: 'Bit Mask', DUPLICATE: 'Duplicate', diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts index b6245bfbe..cbab25f0b 100644 --- a/interface/src/i18n/fr/index.ts +++ b/interface/src/i18n/fr/index.ts @@ -321,6 +321,7 @@ const fr: Translation = { DASHBOARD: 'Tableau de bord', DEVELOPER_MODE: 'Mode développeur', DISABLE_RESET: 'Désactiver la réinitialisation de fabrique', + AUTO_FW_CHECK: 'Vérifier automatiquement les mises à jour du firmware', BYTES: 'Octets', BITMASK: 'Masque de bits', DUPLICATE: 'Dupliquer', diff --git a/interface/src/i18n/it/index.ts b/interface/src/i18n/it/index.ts index 6a5bc8b00..7198b8f47 100644 --- a/interface/src/i18n/it/index.ts +++ b/interface/src/i18n/it/index.ts @@ -321,6 +321,7 @@ const it: Translation = { DASHBOARD: 'Pannello di controllo', DEVELOPER_MODE: 'Modalità sviluppatore', DISABLE_RESET: 'Disabilita la ripristino di fabbrica', + AUTO_FW_CHECK: 'Controlla automaticamente gli aggiornamenti del firmware', BYTES: 'Byte', BITMASK: 'Bitmask', DUPLICATE: 'Duplicato', diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index 82ebaf690..91fc47cb9 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -321,6 +321,7 @@ const nl: Translation = { DASHBOARD: 'Dashboard', DEVELOPER_MODE: 'Ontwikkelaarsmodus', DISABLE_RESET: 'Fabrieksinstellingen uitschakelen', + AUTO_FW_CHECK: 'Automatisch controleren op firmware-updates', BYTES: 'Bytes', BITMASK: 'Bit Mask', DUPLICATE: 'Duplicaat', diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts index e75c0156e..b3dbd0b0e 100644 --- a/interface/src/i18n/no/index.ts +++ b/interface/src/i18n/no/index.ts @@ -321,6 +321,7 @@ const no: Translation = { DASHBOARD: 'Dashboard', DEVELOPER_MODE: 'Utvikler modus', DISABLE_RESET: 'Deaktiver fabrikkinnstillinger', + AUTO_FW_CHECK: 'Se automatisk etter nye firmware versjoner', BYTES: 'Bytes', BITMASK: 'Bitmask', DUPLICATE: 'Duplikat', diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index a258e5e3a..88ece133c 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -321,6 +321,7 @@ const pl: BaseTranslation = { DASHBOARD: 'Pulpit', DEVELOPER_MODE: 'Tryb programisty', DISABLE_RESET: 'Wyłącz reset fabryczny', + AUTO_FW_CHECK: 'Automatycznie sprawdzaj aktualizacje firmware', BYTES: 'Bajty', BITMASK: 'Bit Mask', DUPLICATE: 'Duplicate', diff --git a/interface/src/i18n/sk/index.ts b/interface/src/i18n/sk/index.ts index 9e5efbd7c..3802340e2 100644 --- a/interface/src/i18n/sk/index.ts +++ b/interface/src/i18n/sk/index.ts @@ -321,6 +321,7 @@ const sk: Translation = { DASHBOARD: 'Panel', DEVELOPER_MODE: 'Režim vývojára', DISABLE_RESET: 'Zakázať továrenské nastavenia', + AUTO_FW_CHECK: 'Automaticky kontrolovať aktualizácie firmvéru', BYTES: 'Bytov', BITMASK: 'Bitová maska', DUPLICATE: 'Duplicitné', diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts index af7351901..c9eeeda57 100644 --- a/interface/src/i18n/sv/index.ts +++ b/interface/src/i18n/sv/index.ts @@ -321,6 +321,7 @@ const sv: Translation = { DASHBOARD: 'Kontrollpanel', DEVELOPER_MODE: 'Utvecklarläge', DISABLE_RESET: 'Inaktivera fabriksåterställning', + AUTO_FW_CHECK: 'Sök automatiskt efter nya firmwareversioner', BYTES: 'Bytes', BITMASK: 'Bitmask', DUPLICATE: 'Dublett', diff --git a/interface/src/i18n/tr/index.ts b/interface/src/i18n/tr/index.ts index c214abe47..e9415c8ae 100644 --- a/interface/src/i18n/tr/index.ts +++ b/interface/src/i18n/tr/index.ts @@ -321,6 +321,7 @@ const tr: Translation = { DASHBOARD: 'Kontrol Paneli', DEVELOPER_MODE: 'Geliştirici Modu', DISABLE_RESET: 'Fabrika ayarlarını devre dışı bırak', + AUTO_FW_CHECK: 'Firmware güncellemelerini otomatik olarak denetle', BYTES: 'Bayt', BITMASK: 'Bit Maskesi', DUPLICATE: 'Çift', diff --git a/mock-api/restServer.ts b/mock-api/restServer.ts index ea02c45e3..39de9f867 100644 --- a/mock-api/restServer.ts +++ b/mock-api/restServer.ts @@ -66,6 +66,7 @@ let settings = { modbus_timeout: 10000, developer_mode: true, disable_reset: false, + auto_fw_check: false, email_enabled: false, email_security: 2, email_server: 'smtp.gmail.com', diff --git a/src/core/default_settings.h b/src/core/default_settings.h index 681622a68..b423413db 100644 --- a/src/core/default_settings.h +++ b/src/core/default_settings.h @@ -121,6 +121,10 @@ #define EMSESP_DEFAULT_TELNET_ENABLED true #endif +#ifndef EMSESP_DEFAULT_AUTO_FW_CHECK +#define EMSESP_DEFAULT_AUTO_FW_CHECK false +#endif + #ifndef EMSESP_DEFAULT_MODBUS_ENABLED #define EMSESP_DEFAULT_MODBUS_ENABLED false #endif diff --git a/src/core/system.cpp b/src/core/system.cpp index fa7b16d19..819bf392d 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -720,6 +720,7 @@ void System::store_settings(WebSettings & settings) { system_name_ = settings.system_name; developer_mode_ = settings.developer_mode; disable_reset_ = settings.disable_reset; + auto_fw_check_ = settings.auto_fw_check; } // Starts up core services @@ -2733,6 +2734,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output node["forceHeatingOff"] = settings.boiler_heatingoff; node["developerMode"] = settings.developer_mode; node["disableReset"] = settings.disable_reset; + node["autoFwCheck"] = settings.auto_fw_check; }); // Devices - show EMS devices if we have any diff --git a/src/core/system.h b/src/core/system.h index 7c720a5a4..64f2d1254 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -207,6 +207,14 @@ class System { disable_reset_ = disable_reset; } + bool auto_fw_check() { + return auto_fw_check_; + } + + void auto_fw_check(bool auto_fw_check) { + auto_fw_check_ = auto_fw_check; + } + // Boolean Format API/MQTT uint8_t bool_format() { return bool_format_; @@ -447,6 +455,7 @@ class System { uint32_t modbus_timeout_; bool developer_mode_; bool disable_reset_; + bool auto_fw_check_; uint32_t fstotal_; uint32_t psram_; uint32_t appused_; diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index f60bc460f..47d5aefea 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -85,6 +85,7 @@ void WebSettings::read(WebSettings & settings, JsonObject root) { root["modbus_timeout"] = settings.modbus_timeout; root["developer_mode"] = settings.developer_mode; root["disable_reset"] = settings.disable_reset; + root["auto_fw_check"] = settings.auto_fw_check; root["email_enabled"] = settings.email_enabled; root["email_security"] = settings.email_security; root["email_server"] = settings.email_server; @@ -310,6 +311,12 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) { settings.disable_reset = root["disable_reset"]; EMSESP::system_.disable_reset(settings.disable_reset); + settings.auto_fw_check = root["auto_fw_check"] | EMSESP_DEFAULT_AUTO_FW_CHECK; + EMSESP::system_.auto_fw_check(settings.auto_fw_check); + if (settings.auto_fw_check && !original_settings.auto_fw_check) { + EMSESP::webStatusService.schedule_versions_refresh(); // just switched on, so look straight away + } + settings.bool_dashboard = root["bool_dashboard"] | EMSESP_DEFAULT_BOOL_FORMAT; EMSESP::system_.bool_dashboard(settings.bool_dashboard); diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index d8a600f81..a042040ce 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -122,6 +122,7 @@ class WebSettings { bool developer_mode; // developer mode bool disable_reset; // disable reset + bool auto_fw_check; // periodically check emsesp.org for a newer firmware version static void read(WebSettings & settings, JsonObject root); static StateUpdateResult update(JsonObject root, WebSettings & settings); diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 2a5decf59..8ce5ff465 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -402,10 +402,15 @@ void WebStatusService::schedule_versions_refresh() { versions_next_fetch_ms_ = next; } -// periodic refresh (1 hour) of the cached versions.json -// runs on the main loop task, which has a much bigger stack than AsyncTCP needed for https +// periodic refresh (1 hour) of the cached versions.json, only when auto_fw_check is enabled +// runs on the main loop task so the blocking fetch never happens in an AsyncTCP callback void WebStatusService::loop() { #ifndef EMSESP_STANDALONE + // only reach out to emsesp.org if the user has asked us to + if (!EMSESP::system_.auto_fw_check()) { + return; + } + // need a network if (!EMSESP::network_.network_connected()) { return;