From f5048abae720c823e7787c25cb5c138609ab71f1 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 5 Nov 2025 16:53:25 +0100 Subject: [PATCH 01/27] formatting --- lib_standalone/ESP32React.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib_standalone/ESP32React.h b/lib_standalone/ESP32React.h index 115a42aae..5468cd564 100644 --- a/lib_standalone/ESP32React.h +++ b/lib_standalone/ESP32React.h @@ -75,8 +75,8 @@ class DummySettings { uint8_t provisionMode = 0; uint32_t publish_time_water = 0; - static void read(DummySettings & settings, JsonObject root){}; - static void read(DummySettings & settings){}; + static void read(DummySettings & settings, JsonObject root) {}; + static void read(DummySettings & settings) {}; static StateUpdateResult update(JsonObject root, DummySettings & settings) { return StateUpdateResult::CHANGED; @@ -85,7 +85,7 @@ class DummySettings { class DummySettingsService : public StatefulService { public: - DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager){}; + DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) {}; void begin(); void loop(); @@ -101,12 +101,12 @@ class ESP32React { public: ESP32React(AsyncWebServer * server, FS * fs) : _settings(server, fs, nullptr) - , _securitySettingsService(server, fs){}; + , _securitySettingsService(server, fs) {}; void begin() { _mqttClient = new espMqttClient(); }; - void loop(){}; + void loop() {}; SecurityManager * getSecurityManager() { return &_securitySettingsService; From 5c07a2c0cc1fdb646b16621f1c0bdea51ba1664c Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 5 Nov 2025 16:54:08 +0100 Subject: [PATCH 02/27] remove specific commands like ntp/enabled (ap/enabled didn't work) and replace with just service name --- src/core/command.cpp | 28 ++++++++++++---------------- src/core/system.cpp | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/core/command.cpp b/src/core/command.cpp index f5ab6fded..0f388d770 100644 --- a/src/core/command.cpp +++ b/src/core/command.cpp @@ -367,6 +367,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha // or a special command like 'info', 'values', 'commands', 'entities' etc bool single_command = (!value || !strlen(value)); if (single_command) { + Serial.println("single_command"); if (!strcmp(cmd, F_(commands))) { return Command::list(device_type, output); } @@ -583,15 +584,13 @@ bool Command::list(const uint8_t device_type, JsonObject output) { output[F_(entities)] = Helpers::translated_word(FL_(entities_cmd)); if (device_type == EMSdevice::DeviceType::SYSTEM) { - output["settings/showertimer"] = Helpers::translated_word(FL_(system_cmd)); - output["settings/showeralert"] = Helpers::translated_word(FL_(system_cmd)); - output["settings/hideled"] = Helpers::translated_word(FL_(system_cmd)); - output["settings/analogenabled"] = Helpers::translated_word(FL_(system_cmd)); - output["mqtt/enabled"] = Helpers::translated_word(FL_(system_cmd)); - output["ntp/enabled"] = Helpers::translated_word(FL_(system_cmd)); - output["ap/enabled"] = Helpers::translated_word(FL_(system_cmd)); - output["syslog/enabled"] = Helpers::translated_word(FL_(system_cmd)); + output["settings"] = Helpers::translated_word(FL_(system_cmd)); + output["mqtt"] = Helpers::translated_word(FL_(system_cmd)); + output["ntp"] = Helpers::translated_word(FL_(system_cmd)); + output["ap"] = Helpers::translated_word(FL_(system_cmd)); + output["syslog"] = Helpers::translated_word(FL_(system_cmd)); } + // create a list of commands we have registered, and sort them std::list sorted_cmds; for (const auto & cf : cmdfunctions_) { @@ -621,14 +620,11 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo } } if (device_type == EMSdevice::DeviceType::SYSTEM) { - sorted_cmds.emplace_back("settings/showertimer"); - sorted_cmds.emplace_back("settings/showeralert"); - sorted_cmds.emplace_back("settings/hideled"); - sorted_cmds.emplace_back("settings/analogenabled"); - sorted_cmds.emplace_back("mqtt/enabled"); - sorted_cmds.emplace_back("ntp/enabled"); - sorted_cmds.emplace_back("ap/enabled"); - sorted_cmds.emplace_back("syslog/enabled"); + sorted_cmds.emplace_back("settings"); + sorted_cmds.emplace_back("mqtt"); + sorted_cmds.emplace_back("ntp"); + sorted_cmds.emplace_back("ap"); + sorted_cmds.emplace_back("syslog"); } sorted_cmds.sort(); // sort them diff --git a/src/core/system.cpp b/src/core/system.cpp index 5ca1e9a4d..dbb9a9132 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1668,6 +1668,21 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output node["timestamp"] = time(nullptr); #endif + // AP Status + node = output["ap"].to(); + EMSESP::esp32React.getAPSettingsService()->read([&](const APSettings & settings) { + node["provisionMode"] = settings.provisionMode; // 0 is on, 2 is off + node["ssid"] = settings.ssid; +#ifndef EMSESP_STANDALONE + node["channel"] = settings.channel; + node["ssidHidden"] = settings.ssidHidden; + node["maxClients"] = settings.maxClients; + node["localIP"] = settings.localIP.toString(); + node["gatewayIP"] = settings.gatewayIP.toString(); + node["subnetMask"] = settings.subnetMask.toString(); +#endif + }); + // MQTT Status node = output["mqtt"].to(); node["MQTTStatus"] = Mqtt::connected() ? F_(connected) : F_(disconnected); From 5908fd9d9c789d970be21d6ecaf6200f96cf2044 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 5 Nov 2025 20:50:29 +0100 Subject: [PATCH 03/27] show language count --- src/core/common.h | 25 +++++++++++++++++++++---- src/core/emsesp.cpp | 7 +++++-- src/core/system.cpp | 15 ++++++++++++++- src/core/system.h | 2 ++ 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/core/common.h b/src/core/common.h index aa49be717..3fce072dc 100644 --- a/src/core/common.h +++ b/src/core/common.h @@ -52,22 +52,39 @@ using string_vector = std::vector; #define F_(string_name) (__pstr__##string_name) #define FL_(list_name) (__pstr__L_##list_name) +// Counter for translations created by MAKE_TRANSLATION +extern uint32_t translation_count_; +#include +#include +inline void increment_translation_count_impl(const char * translation_name) { + // Use a static set to track which translations we've already counted by name + // Using std::string ensures we compare by value, not by pointer address + // This ensures we only count each unique translation once, even if included in multiple files + static std::set counted_translations; + std::string name(translation_name); + if (counted_translations.find(name) == counted_translations.end()) { + extern uint32_t translation_count_; + translation_count_++; + counted_translations.insert(name); + } +} + // The language settings below must match system.cpp #if defined(EMSESP_TEST) // in Test mode use two languages (en & de) to save flash memory needed for the tests #define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr}; -#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; +#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); #elif defined(EMSESP_EN_ONLY) // EN only #define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr}; -#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr}; +#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); #elif defined(EMSESP_DE_ONLY) // EN + DE #define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr}; -#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; +#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); #else #define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; -#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; +#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); #endif #define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index 4ee114cea..270b3ee71 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -26,6 +26,9 @@ static_assert(uuid::thread_safe, "uuid-common must be thread-safe"); static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe"); static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe"); +// Translation counter - incremented by MAKE_TRANSLATION macro (must be global, not in namespace) +uint32_t translation_count_ = 0; + namespace emsesp { // Static member definitions @@ -288,7 +291,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) { shell.printfln(" #read fails (after %d retries): %d", TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_read_fail_count()); shell.printfln(" #write fails (after %d retries): %d", TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_write_fail_count()); shell.printfln(" Rx line quality: %d%%", rxservice_.quality()); - shell.printfln(" Tx line quality: %d%%", (txservice_.read_quality() + txservice_.read_quality()) / 2); + shell.printfln(" Tx line quality: %d%%", (txservice_.read_quality() + txservice_.write_quality()) / 2); shell.println(); } @@ -1691,7 +1694,7 @@ void EMSESP::start() { device_library_ = { #include "device_library.h" }; - LOG_INFO("Loaded EMS device library (%d entries)", device_library_.size()); + LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), ::translation_count_, system_.languages_string().c_str()); system_.reload_settings(); // ... and store some of the settings locally diff --git a/src/core/system.cpp b/src/core/system.cpp index dbb9a9132..8e1effb19 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -107,6 +107,19 @@ bool System::command_send(const char * value, const int8_t id) { return EMSESP::txservice_.send_raw(value); // ignore id } +// return string of languages and count +std::string System::languages_string() { + std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages ("; + for (uint8_t i = 0; i < NUM_LANGUAGES; i++) { + languages_string += languages[i]; + if (i != NUM_LANGUAGES - 1) { + languages_string += ","; + } + } + languages_string += ")"; + return languages_string; +} + // returns last response from MQTT bool System::command_response(const char * value, const int8_t id, JsonObject output) { JsonDocument doc; @@ -1779,7 +1792,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output node["busReadsFailed"] = EMSESP::txservice_.telegram_read_fail_count(); node["busWritesFailed"] = EMSESP::txservice_.telegram_write_fail_count(); node["busRxLineQuality"] = EMSESP::rxservice_.quality(); - node["busTxLineQuality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2; + node["busTxLineQuality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.write_quality()) / 2; // Settings node = output["settings"].to(); diff --git a/src/core/system.h b/src/core/system.h index c151e2fd5..01bf97f88 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -291,6 +291,8 @@ class System { void wifi_reconnect(); void show_users(uuid::console::Shell & shell); + static std::string languages_string(); + uint32_t FStotal() { return fstotal_; } From 12635ff4a567f125004894518486f9e4f97b28d5 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 5 Nov 2025 22:29:15 +0100 Subject: [PATCH 04/27] optimizations and show # entities and languages --- src/core/common.h | 85 +++++++-------- src/core/emsesp.cpp | 248 ++++++++++++++++++++++++++------------------ 2 files changed, 184 insertions(+), 149 deletions(-) diff --git a/src/core/common.h b/src/core/common.h index 3fce072dc..89e354f8f 100644 --- a/src/core/common.h +++ b/src/core/common.h @@ -44,56 +44,39 @@ using string_vector = std::vector; #endif // clang-format off + + #define FPSTR(pstr_pointer) pstr_pointer + #define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal; + #define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name) + + #define F_(string_name) (__pstr__##string_name) + #define FL_(list_name) (__pstr__L_##list_name) + + // Translation counter - capture baseline before any MAKE_TRANSLATION calls + enum { EMSESP_TRANSLATION_COUNT_START = __COUNTER__ }; -#define FPSTR(pstr_pointer) pstr_pointer -#define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal; -#define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name) - -#define F_(string_name) (__pstr__##string_name) -#define FL_(list_name) (__pstr__L_##list_name) - -// Counter for translations created by MAKE_TRANSLATION -extern uint32_t translation_count_; -#include -#include -inline void increment_translation_count_impl(const char * translation_name) { - // Use a static set to track which translations we've already counted by name - // Using std::string ensures we compare by value, not by pointer address - // This ensures we only count each unique translation once, even if included in multiple files - static std::set counted_translations; - std::string name(translation_name); - if (counted_translations.find(name) == counted_translations.end()) { - extern uint32_t translation_count_; - translation_count_++; - counted_translations.insert(name); - } -} - -// The language settings below must match system.cpp -#if defined(EMSESP_TEST) -// in Test mode use two languages (en & de) to save flash memory needed for the tests -#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr}; -#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); -#elif defined(EMSESP_EN_ONLY) -// EN only -#define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr}; -#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); -#elif defined(EMSESP_DE_ONLY) -// EN + DE -#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr}; -#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); -#else -#define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; -#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; static int __translation_counter_##list_name = (increment_translation_count_impl(#list_name), 0); -#endif - -#define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; - -// fixed strings, no translations -#define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr}; - -// with translations -#define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr}; + // The language settings below must match system.cpp + #if defined(EMSESP_EN_ONLY) + // EN only + #define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr}; + #define MAKE_TRANSLATION(list_name, shortname, en, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr}; + #elif defined(EMSESP_TEST) || defined(EMSESP_DE_ONLY) + // EN + DE (Test mode uses two languages to save flash memory) + #define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr}; + #define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr}; + #else + // All languages + #define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; + #define MAKE_TRANSLATION(list_name, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; + #endif + + #define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr}; + + // fixed strings, no translations + #define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr}; + + // with translations + #define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr}; // clang-format on @@ -101,4 +84,8 @@ inline void increment_translation_count_impl(const char * translation_name) { #include "locale_translations.h" #include "locale_common.h" +// Translation count - dynamically calculated at compile-time +enum { EMSESP_TRANSLATION_COUNT_END = __COUNTER__ }; +static constexpr uint16_t EMSESP_TRANSLATION_COUNT = EMSESP_TRANSLATION_COUNT_END - EMSESP_TRANSLATION_COUNT_START - 1; + #endif diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index 270b3ee71..a722d776c 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -26,9 +26,6 @@ static_assert(uuid::thread_safe, "uuid-common must be thread-safe"); static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe"); static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe"); -// Translation counter - incremented by MAKE_TRANSLATION macro (must be global, not in namespace) -uint32_t translation_count_ = 0; - namespace emsesp { // Static member definitions @@ -99,18 +96,35 @@ Preferences EMSESP::nvs_; // NV Storage // for a specific EMS device go and request data values // or if device_id is 0 it will fetch from all our known and active devices void EMSESP::fetch_device_values(const uint8_t device_id) { - for (const auto & emsdevice : emsdevices) { - if ((device_id == 0) || emsdevice->is_device_id(device_id)) { + // Early return if no devices + if (emsdevices.empty()) { + return; + } + + // If device_id is 0, fetch all + if (device_id == 0) { + for (const auto & emsdevice : emsdevices) { emsdevice->fetch_values(); - if (device_id != 0) { - return; // quit, we only want to return the selected device - } + } + return; + } + + // Fetch specific device + for (const auto & emsdevice : emsdevices) { + if (emsdevice->is_device_id(device_id)) { + emsdevice->fetch_values(); + return; // quit, we only want to return the selected device } } } // see if the deviceID exists bool EMSESP::valid_device(const uint8_t device_id) { + // Early return if devices list is empty + if (emsdevices.empty()) { + return false; + } + for (const auto & emsdevice : emsdevices) { if (emsdevice && emsdevice->is_device_id(device_id)) { return true; @@ -121,6 +135,10 @@ bool EMSESP::valid_device(const uint8_t device_id) { // for a specific EMS device type go and request data values void EMSESP::fetch_device_values_type(const uint8_t device_type) { + if (emsdevices.empty()) { + return; + } + for (const auto & emsdevice : emsdevices) { if (emsdevice && (emsdevice->device_type() == device_type)) { emsdevice->fetch_values(); @@ -129,6 +147,10 @@ void EMSESP::fetch_device_values_type(const uint8_t device_type) { } bool EMSESP::cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id) { + if (emsdevices.empty()) { + return false; + } + for (const auto & emsdevice : emsdevices) { if (emsdevice && (emsdevice->device_type() == device_type) && (!device_id || emsdevice->device_id() == device_id)) { return emsdevice->is_readonly(cmd, id); @@ -138,6 +160,10 @@ bool EMSESP::cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, } uint8_t EMSESP::device_id_from_cmd(const uint8_t device_type, const char * cmd, const int8_t id) { + if (emsdevices.empty()) { + return 0; + } + for (const auto & emsdevice : emsdevices) { if (emsdevice && emsdevice->device_type() == device_type && emsdevice->has_cmd(cmd, id)) { return emsdevice->device_id(); @@ -154,10 +180,14 @@ void EMSESP::clear_all_devices() { // return number of devices of a known type uint8_t EMSESP::count_devices(const uint8_t device_type) { + if (emsdevices.empty()) { + return 0; + } + uint8_t count = 0; for (const auto & emsdevice : emsdevices) { - if (emsdevice) { - count += (emsdevice->device_type() == device_type); + if (emsdevice && emsdevice->device_type() == device_type) { + count++; } } return count; @@ -165,10 +195,14 @@ uint8_t EMSESP::count_devices(const uint8_t device_type) { // return total number of devices excluding the Controller uint8_t EMSESP::count_devices() { + if (emsdevices.empty()) { + return 0; + } + uint8_t count = 0; for (const auto & emsdevice : emsdevices) { - if (emsdevice) { - count += (emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER); + if (emsdevice && emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER) { + count++; } } return count; @@ -177,20 +211,22 @@ uint8_t EMSESP::count_devices() { // returns the index of a device if there are more of the same type // or 0 if there is only one or none uint8_t EMSESP::device_index(const uint8_t device_type, const uint8_t unique_id) { - if (count_devices(device_type) <= 1) { - return 0; // none or only 1 device exists - } - uint8_t index = 1; + uint8_t count = 0; + uint8_t index = 0; + uint8_t current_index = 1; + for (const auto & emsdevice : emsdevices) { if (emsdevice->device_type() == device_type) { - // did we find it? + count++; if (emsdevice->unique_id() == unique_id) { - return index; + index = current_index; } - index++; + current_index++; } } - return 0; // didn't find it + + // Return 0 if only one device exists or not found + return (count <= 1) ? 0 : index; } // scans for new devices @@ -245,13 +281,8 @@ uint8_t EMSESP::bus_status() { uint32_t total_fail = txservice_.telegram_read_fail_count() + txservice_.telegram_write_fail_count(); // nothing sent and also no errors - must be ok - if ((total_sent == 0) && (total_fail == 0)) { - return BUS_STATUS_CONNECTED; - } - - // nothing sent, but have Tx errors - if ((total_sent == 0) && (total_fail != 0)) { - return BUS_STATUS_TX_ERRORS; + if (total_sent == 0) { + return (total_fail == 0) ? BUS_STATUS_CONNECTED : BUS_STATUS_TX_ERRORS; } // Tx Failure rate > 10% @@ -849,41 +880,55 @@ std::string EMSESP::pretty_telegram(std::shared_ptr telegram) { std::string src_name(""); std::string dest_name(""); std::string type_name(""); + + // Single loop to find all device information + bool src_found = false; + bool dest_found = false; + bool type_found = false; + for (const auto & emsdevice : emsdevices) { - // get src & dest - if (emsdevice->is_device_id(src)) { - src_name = emsdevice->device_type_name(); - } else if (emsdevice->is_device_id(dest)) { - dest_name = emsdevice->device_type_name(); + // get src name + if (!src_found && emsdevice->is_device_id(src)) { + src_name = emsdevice->device_type_name(); + src_found = true; } - // get the type name - if (type_name.empty()) { + + // get dest name + if (!dest_found && emsdevice->is_device_id(dest)) { + dest_name = emsdevice->device_type_name(); + dest_found = true; + } + + // get the type name (try primary conditions first) + if (!type_found) { if ((telegram->operation == Telegram::Operation::RX_READ && emsdevice->is_device_id(dest)) || (telegram->operation != Telegram::Operation::RX_READ && dest == 0 && emsdevice->is_device_id(src)) || (telegram->operation != Telegram::Operation::RX_READ && src == EMSbus::ems_bus_id() && emsdevice->is_device_id(dest))) { type_name = emsdevice->telegram_type_name(telegram); + if (!type_name.empty()) { + type_found = true; + } } } + + // Early exit if we found everything + if (src_found && dest_found && type_found) { + break; + } } - if (type_name.empty()) { - // fallback, get the type name from src + + // Fallback for type name if not found - try src or dest + if (!type_found && telegram->operation != Telegram::Operation::RX_READ) { for (const auto & emsdevice : emsdevices) { - if (telegram->operation != Telegram::Operation::RX_READ && emsdevice->is_device_id(src)) { + if (emsdevice->is_device_id(src) || emsdevice->is_device_id(dest)) { type_name = emsdevice->telegram_type_name(telegram); - break; + if (!type_name.empty()) { + break; + } } } } - if (type_name.empty()) { - // 2nd fallback, get the type name from dest - for (const auto & emsdevice : emsdevices) { - if (telegram->operation != Telegram::Operation::RX_READ && emsdevice->is_device_id(dest)) { - type_name = emsdevice->telegram_type_name(telegram); - break; - } - } - } // if we can't find names for the devices, use their hex values if (src_name.empty()) { src_name = device_tostring(src); @@ -1012,11 +1057,16 @@ void EMSESP::process_version(std::shared_ptr telegram) { if (telegram->offset != 0) { return; } + + const uint8_t msg_len = telegram->message_length; + // for empty telegram add device with empty product, version and brand - if (telegram->message_length == 0) { + if (msg_len == 0) { (void)add_device(telegram->src, 0, "00.00", 0); return; - } else if (telegram->message_length < 3) { + } + + if (msg_len < 3) { (void)add_device(telegram->src, telegram->message_data[0], "00.00", 0); send_read_request(EMSdevice::EMS_TYPE_NAME, telegram->src, 27); return; @@ -1026,7 +1076,7 @@ void EMSESP::process_version(std::shared_ptr telegram) { uint8_t offset = 0; if (telegram->message_data[0] == 0x00) { // see if we have a 2nd subscriber - if (telegram->message_length > 5 && telegram->message_data[3] != 0x00) { + if (msg_len > 5 && telegram->message_data[3] != 0x00) { offset = 3; } else { return; // ignore whole telegram @@ -1043,7 +1093,7 @@ void EMSESP::process_version(std::shared_ptr telegram) { // some devices store the protocol type (HT3, Buderus) in the last byte uint8_t brand; - if (telegram->message_length >= 10) { + if (msg_len >= 10) { brand = EMSdevice::decode_brand(telegram->message_data[9]); } else { brand = EMSdevice::Brand::NO_BRAND; // unknown @@ -1119,57 +1169,53 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { // calls the associated process function for that EMS device // returns false if the device_id doesn't recognize it // after the telegram has been processed, see if there have been values changed and we need to do a MQTT publish - bool telegram_found = false; - uint8_t device_found = 0; - // broadcast or send to us + bool telegram_found = false; + uint8_t device_found = 0; + EMSdevice * found_device = nullptr; + + // Combined loop: check all conditions in a single pass for (const auto & emsdevice : emsdevices) { + // broadcast or send to us if (emsdevice->is_device_id(telegram->src) && (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id())) { telegram_found = emsdevice->handle_telegram(telegram); - device_found = emsdevice->unique_id(); + found_device = emsdevice.get(); break; } - } - if (!telegram_found) { // check for command to the device - for (const auto & emsdevice : emsdevices) { - if (emsdevice->is_device_id(telegram->dest) && telegram->src != EMSbus::ems_bus_id()) { - telegram_found = emsdevice->handle_telegram(telegram); - device_found = emsdevice->unique_id(); - break; - } - } - } - if (!telegram_found) { - // check for sends to master thermostat - for (const auto & emsdevice : emsdevices) { - if (emsdevice->is_device_id(telegram->src) && telegram->dest == 0x10) { - telegram_found = emsdevice->handle_telegram(telegram); - device_found = emsdevice->unique_id(); - break; - } - } - } - for (const auto & emsdevice : emsdevices) { - if (emsdevice->unique_id() == device_found) { - if (!telegram_found && telegram->message_length > 0) { - emsdevice->add_handlers_ignored(telegram->type_id); - } - if (wait_validate_ == telegram->type_id) { - wait_validate_ = 0; - } - if (Mqtt::connected() && telegram_found - && ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update()) - || (telegram->type_id == publish_id_ && telegram->dest == EMSbus::ems_bus_id()))) { - if (telegram->type_id == publish_id_) { - publish_id_ = 0; - } - emsdevice->has_update(false); // reset flag - if (!Mqtt::publish_single()) { - publish_device_values(emsdevice->device_type()); // publish to MQTT if we explicitly have too - } - } + if (!telegram_found && emsdevice->is_device_id(telegram->dest) && telegram->src != EMSbus::ems_bus_id()) { + telegram_found = emsdevice->handle_telegram(telegram); + found_device = emsdevice.get(); break; } + // check for sends to master thermostat + if (!telegram_found && emsdevice->is_device_id(telegram->src) && telegram->dest == 0x10) { + telegram_found = emsdevice->handle_telegram(telegram); + found_device = emsdevice.get(); + break; + } + } + + if (found_device) { + device_found = found_device->unique_id(); + + // Process the found device directly without another loop + if (!telegram_found && telegram->message_length > 0) { + found_device->add_handlers_ignored(telegram->type_id); + } + if (wait_validate_ == telegram->type_id) { + wait_validate_ = 0; + } + if (Mqtt::connected() && telegram_found + && ((mqtt_.get_publish_onchange(found_device->device_type()) && found_device->has_update()) + || (telegram->type_id == publish_id_ && telegram->dest == EMSbus::ems_bus_id()))) { + if (telegram->type_id == publish_id_) { + publish_id_ = 0; + } + found_device->has_update(false); // reset flag + if (!Mqtt::publish_single()) { + publish_device_values(found_device->device_type()); // publish to MQTT if we explicitly have too + } + } } // handle unknown broadcasted telegrams (or send to us) if (!telegram_found && (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id())) { @@ -1187,6 +1233,10 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { // return true if we have this device already registered bool EMSESP::device_exists(const uint8_t device_id) { + if (emsdevices.empty()) { + return false; + } + for (const auto & emsdevice : emsdevices) { if (emsdevice && emsdevice->is_device_id(device_id)) { return true; @@ -1254,16 +1304,14 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const } // first check to see if we already have it, if so update the record - auto it = emsdevices.begin(); - for (const auto & emsdevice : emsdevices) { - if (emsdevice && emsdevice->is_device_id(device_id)) { - if (product_id == 0 || emsdevice->product_id() != 0) { // update only with valid product_id + for (auto it = emsdevices.begin(); it != emsdevices.end(); ++it) { + if ((*it) && (*it)->is_device_id(device_id)) { + if (product_id == 0 || (*it)->product_id() != 0) { // update only with valid product_id return true; } emsdevices.erase(it); // erase the old device without product_id and re detect break; } - it++; } // look up the rest of the details using the product_id and create the new device object @@ -1694,7 +1742,7 @@ void EMSESP::start() { device_library_ = { #include "device_library.h" }; - LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), ::translation_count_, system_.languages_string().c_str()); + LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), EMSESP_TRANSLATION_COUNT, system_.languages_string().c_str()); system_.reload_settings(); // ... and store some of the settings locally From 6799fe518903feda398b07a1152987724cd7b218 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:55:40 +0100 Subject: [PATCH 05/27] rename Initialising to Booting --- interface/src/i18n/cz/index.ts | 2 +- interface/src/i18n/de/index.ts | 2 +- interface/src/i18n/en/index.ts | 2 +- interface/src/i18n/fr/index.ts | 2 +- interface/src/i18n/it/index.ts | 2 +- interface/src/i18n/nl/index.ts | 2 +- interface/src/i18n/no/index.ts | 2 +- interface/src/i18n/pl/index.ts | 2 +- interface/src/i18n/sk/index.ts | 2 +- interface/src/i18n/sv/index.ts | 2 +- interface/src/i18n/tr/index.ts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/interface/src/i18n/cz/index.ts b/interface/src/i18n/cz/index.ts index b7131532c..85f6f6f37 100644 --- a/interface/src/i18n/cz/index.ts +++ b/interface/src/i18n/cz/index.ts @@ -337,7 +337,7 @@ const cz: Translation = { UPDATE_AVAILABLE: 'aktualizace dostupná', LATEST_VERSION: 'Používáte nejnovější verzi {0}firmwaru', PLEASE_WAIT: 'Prosím čekejte', - RESTARTING_PRE: 'Inicializace', + RESTARTING_PRE: 'Bootování', RESTARTING_POST: 'Příprava', AUTO_SCROLL: 'Automatické rolování', DASHBOARD: 'Dashboard', diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index f77904b6b..6cd8beb8e 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -337,7 +337,7 @@ const de: Translation = { UPDATE_AVAILABLE: 'Firmware-Update verfügbar', LATEST_VERSION: 'Sie verwenden die neueste {0} Firmware-Version', PLEASE_WAIT: 'Bitte warten', - RESTARTING_PRE: 'Initialisierung', + RESTARTING_PRE: 'Booten', RESTARTING_POST: 'Vorbereitung', AUTO_SCROLL: 'Automatisches Scrollen', DASHBOARD: 'Dashboard', diff --git a/interface/src/i18n/en/index.ts b/interface/src/i18n/en/index.ts index d9d18b7d5..08cffa3df 100644 --- a/interface/src/i18n/en/index.ts +++ b/interface/src/i18n/en/index.ts @@ -337,7 +337,7 @@ const en: Translation = { UPDATE_AVAILABLE: 'update available', LATEST_VERSION: 'You are using the latest {0} firmware version', PLEASE_WAIT: 'Please wait', - RESTARTING_PRE: 'Initializing', + RESTARTING_PRE: 'Booting', RESTARTING_POST: 'Preparing', AUTO_SCROLL: 'Auto Scroll', DASHBOARD: 'Dashboard', diff --git a/interface/src/i18n/fr/index.ts b/interface/src/i18n/fr/index.ts index f5a123b60..752f660fb 100644 --- a/interface/src/i18n/fr/index.ts +++ b/interface/src/i18n/fr/index.ts @@ -337,7 +337,7 @@ const fr: Translation = { UPDATE_AVAILABLE: 'mise à jour disponible', LATEST_VERSION: 'Vous utilisez la dernière version {0} du firmware', PLEASE_WAIT: 'Veuillez patienter', - RESTARTING_PRE: 'Initialisation', + RESTARTING_PRE: 'Démarrage', RESTARTING_POST: 'Préparation', AUTO_SCROLL: 'Défilement automatique', DASHBOARD: 'Tableau de bord', diff --git a/interface/src/i18n/it/index.ts b/interface/src/i18n/it/index.ts index 44f60c691..279870901 100644 --- a/interface/src/i18n/it/index.ts +++ b/interface/src/i18n/it/index.ts @@ -337,7 +337,7 @@ const it: Translation = { UPDATE_AVAILABLE: 'aggiornamento disponibile', LATEST_VERSION: 'Stai usando la versione più recente del firmware {0}', PLEASE_WAIT: 'Attendere', - RESTARTING_PRE: 'Inizializzazione', + RESTARTING_PRE: 'Avviamento', RESTARTING_POST: 'Preparazione', AUTO_SCROLL: 'Scorrimento automatico', DASHBOARD: 'Pannello di controllo', diff --git a/interface/src/i18n/nl/index.ts b/interface/src/i18n/nl/index.ts index acdf83c03..fbceaf4ee 100644 --- a/interface/src/i18n/nl/index.ts +++ b/interface/src/i18n/nl/index.ts @@ -337,7 +337,7 @@ const nl: Translation = { UPDATE_AVAILABLE: 'update beschikbaar', LATEST_VERSION: 'U gebruikt de nieuwste {0} firmwareversie', PLEASE_WAIT: 'Een ogenblik geduld', - RESTARTING_PRE: 'Initialiseren', + RESTARTING_PRE: 'Booten', RESTARTING_POST: 'Voorbereiding', AUTO_SCROLL: 'Automatisch Scrollen', DASHBOARD: 'Dashboard', diff --git a/interface/src/i18n/no/index.ts b/interface/src/i18n/no/index.ts index e27d38089..1eb1dd3ba 100644 --- a/interface/src/i18n/no/index.ts +++ b/interface/src/i18n/no/index.ts @@ -337,7 +337,7 @@ const no: Translation = { UPDATE_AVAILABLE: 'oppdatering tilgjengelig', LATEST_VERSION: 'Du bruker den nyeste {0} firmware versjonen', PLEASE_WAIT: 'Vennligst vent', - RESTARTING_PRE: 'Initialiserer', + RESTARTING_PRE: 'Starte', RESTARTING_POST: 'Forbereder', AUTO_SCROLL: 'Automatisk rulling', DASHBOARD: 'Dashboard', diff --git a/interface/src/i18n/pl/index.ts b/interface/src/i18n/pl/index.ts index 9fd1cbc02..8520e86bc 100644 --- a/interface/src/i18n/pl/index.ts +++ b/interface/src/i18n/pl/index.ts @@ -337,7 +337,7 @@ const pl: BaseTranslation = { UPDATE_AVAILABLE: 'aktualizacja dostępna', LATEST_VERSION: 'Jesteś używając najnowszej wersji firmware {0}', PLEASE_WAIT: 'Proszę czekać', - RESTARTING_PRE: 'Inicjalizacja', + RESTARTING_PRE: 'Uruchamianie', RESTARTING_POST: 'Przygotowanie', AUTO_SCROLL: 'Auto Scroll', DASHBOARD: 'Pulpit', diff --git a/interface/src/i18n/sk/index.ts b/interface/src/i18n/sk/index.ts index 737113010..d9349bc2e 100644 --- a/interface/src/i18n/sk/index.ts +++ b/interface/src/i18n/sk/index.ts @@ -337,7 +337,7 @@ const sk: Translation = { UPDATE_AVAILABLE: 'dostupná aktualizácia', LATEST_VERSION: 'Používate poslednú {0} verziu firmvéru', PLEASE_WAIT: 'Čakajte prosím', - RESTARTING_PRE: 'Prebieha inicializácia', + RESTARTING_PRE: 'Bootovanie', RESTARTING_POST: 'Príprava', AUTO_SCROLL: 'Automatické rolovanie', DASHBOARD: 'Panel', diff --git a/interface/src/i18n/sv/index.ts b/interface/src/i18n/sv/index.ts index 48f513017..af1681f5f 100644 --- a/interface/src/i18n/sv/index.ts +++ b/interface/src/i18n/sv/index.ts @@ -337,7 +337,7 @@ const sv: Translation = { UPDATE_AVAILABLE: 'uppdatering tillgänglig', LATEST_VERSION: 'Du använder den senaste {0} firmwareversionen.', PLEASE_WAIT: 'Var god vänta', - RESTARTING_PRE: 'Initialiserar', + RESTARTING_PRE: 'Bootar', RESTARTING_POST: 'Förbereder', AUTO_SCROLL: 'Autoskrolla', DASHBOARD: 'Kontrollpanel', diff --git a/interface/src/i18n/tr/index.ts b/interface/src/i18n/tr/index.ts index d263176bb..2414a867d 100644 --- a/interface/src/i18n/tr/index.ts +++ b/interface/src/i18n/tr/index.ts @@ -337,7 +337,7 @@ const tr: Translation = { UPDATE_AVAILABLE: 'güncellendi!', LATEST_VERSION: 'En son {0} firmware sürümünü kullanıyorsunuz.', PLEASE_WAIT: 'Lütfen bekleyin', - RESTARTING_PRE: 'Başlatılıyor', + RESTARTING_PRE: 'Yükleniyor', RESTARTING_POST: 'Hazırlanıyor', AUTO_SCROLL: 'Otomatik kaydırma', DASHBOARD: 'Kontrol Paneli', From 72b4809ed8e801f052bd9dd6ea79a41233c5af69 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:55:49 +0100 Subject: [PATCH 06/27] update tests --- test/test_api/test_api.h | 62 +++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/test/test_api/test_api.h b/test/test_api/test_api.h index b3c7094c5..ce6dc55b3 100644 --- a/test/test_api/test_api.h +++ b/test/test_api/test_api.h @@ -162,22 +162,24 @@ void test_21() { auto expected_response = "[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " "Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," - "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{},\"mqtt\":{\"MQTTStatus\":\"disconnected\",\"MQTTPublishes\":0," - "\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,\"cleanSession\":false," - "\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,\"haEnabled\":true,\"mqttQos\":0," - "\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,\"publishTimeSolar\":10,\"publishTimeMixer\":" - "10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,\"publish2command\":false,\"sendResponse\":false}," - "\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,\"temperatureSensorFails\":0,\"analogSensors\":4," - "\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":" - "\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0," - "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11," - "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":" - "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true," - "\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My " - "Custom " + "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"enabled\":true,\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/" + "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":0,\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," + "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60," + "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1," + "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10," + "\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false," + "\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0," + "\"temperatureSensorFails\":0,\"analogSensors\":4,\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{" + "\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0," + "\"busReadsFailed\":0,\"busWritesFailed\":0,\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":" + "\"en\",\"txMode\":8,\"emsBusID\":11,\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false," + "\"readonlyMode\":false,\"fahrenheit\":false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true," + "\"telnetEnabled\":true,\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":" + "\"boiler\",\"name\":\"My Custom " "Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":38,\"handlersReceived\":\"0x18\"," "\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 " - "0x2E 0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15," + "0x2E " + "0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15," "\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 " "0x0168\"},{\"type\":\"temperaturesensor\",\"name\":\"temperaturesensor\",\"entities\":2},{\"type\":\"analogsensor\",\"name\":\"analogsensor\"," "\"entities\":4},{\"type\":\"scheduler\",\"name\":\"scheduler\",\"entities\":2},{\"type\":\"custom\",\"name\":\"custom\",\"entities\":4}]}]"; @@ -188,22 +190,24 @@ void test_22() { auto expected_response = "[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " "Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," - "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{},\"mqtt\":{\"MQTTStatus\":\"disconnected\",\"MQTTPublishes\":0," - "\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,\"cleanSession\":false," - "\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,\"haEnabled\":true,\"mqttQos\":0," - "\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,\"publishTimeSolar\":10,\"publishTimeMixer\":" - "10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,\"publish2command\":false,\"sendResponse\":false}," - "\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,\"temperatureSensorFails\":0,\"analogSensors\":4," - "\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":" - "\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0," - "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11," - "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":" - "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true," - "\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My " - "Custom " + "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"enabled\":true,\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/" + "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":0,\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," + "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60," + "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1," + "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10," + "\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false," + "\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0," + "\"temperatureSensorFails\":0,\"analogSensors\":4,\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{" + "\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0," + "\"busReadsFailed\":0,\"busWritesFailed\":0,\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":" + "\"en\",\"txMode\":8,\"emsBusID\":11,\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false," + "\"readonlyMode\":false,\"fahrenheit\":false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true," + "\"telnetEnabled\":true,\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":" + "\"boiler\",\"name\":\"My Custom " "Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":38,\"handlersReceived\":\"0x18\"," "\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 " - "0x2E 0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15," + "0x2E " + "0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15," "\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 " "0x0168\"},{\"type\":\"temperaturesensor\",\"name\":\"temperaturesensor\",\"entities\":2},{\"type\":\"analogsensor\",\"name\":\"analogsensor\"," "\"entities\":4},{\"type\":\"scheduler\",\"name\":\"scheduler\",\"entities\":2},{\"type\":\"custom\",\"name\":\"custom\",\"entities\":4}]}]"; @@ -433,4 +437,4 @@ void run_tests() { RUN_TEST(test_54); } -// ---------- END - CUT HERE ---------- \ No newline at end of file +// ---------- END - CUT HERE ---------- From 5053ad08dd4442e2908e23a4e69f6d34adff206d Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:56:00 +0100 Subject: [PATCH 07/27] add comment --- src/ESP32React/APSettingsService.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ESP32React/APSettingsService.h b/src/ESP32React/APSettingsService.h index 3c76feeb5..67c9e03bf 100644 --- a/src/ESP32React/APSettingsService.h +++ b/src/ESP32React/APSettingsService.h @@ -58,7 +58,7 @@ enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING }; class APSettings { public: - uint8_t provisionMode; + uint8_t provisionMode; // 0 = on, 2 = off String ssid; String password; uint8_t channel; From 4a6ccce09a9a6ab58c8d20b4b10772df8c883d41 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:56:20 +0100 Subject: [PATCH 08/27] add ntp for completeness and testing --- lib_standalone/ESP32React.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib_standalone/ESP32React.h b/lib_standalone/ESP32React.h index 5468cd564..f01b43635 100644 --- a/lib_standalone/ESP32React.h +++ b/lib_standalone/ESP32React.h @@ -55,6 +55,7 @@ class DummySettings { uint16_t publish_time_other = 10; uint16_t publish_time_sensor = 10; uint16_t publish_time_heartbeat = 60; + uint32_t publish_time_water = 0; String hostname = "ems-esp"; String jwtSecret = "ems-esp"; @@ -72,8 +73,12 @@ class DummySettings { String CORSOrigin = "*"; uint8_t tx_power = 0; - uint8_t provisionMode = 0; - uint32_t publish_time_water = 0; + // AP + uint8_t provisionMode = 0; + + // NTP + String server = "pool.ntp.org"; + String tzLabel = "Europe/London"; static void read(DummySettings & settings, JsonObject root) {}; static void read(DummySettings & settings) {}; From c28b098c657644ec0aae84dc771bb18fa03f1aa6 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:56:42 +0100 Subject: [PATCH 09/27] show ntp in info --- src/core/system.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 8e1effb19..7dc913d41 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1452,6 +1452,7 @@ bool System::command_service(const char * cmd, const char * value) { ok = true; } } + int n; if (!ok && Helpers::value2number(value, n)) { #ifndef EMSESP_STANDALONE @@ -1671,15 +1672,19 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output // NTP status node = output["ntp"].to(); -#ifndef EMSESP_STANDALONE - node["NTPStatus"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected"; EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) { +#ifndef EMSESP_STANDALONE node["enabled"] = settings.enabled; +#else + node["enabled"] = true; +#endif node["server"] = settings.server; node["tzLabel"] = settings.tzLabel; }); +#ifndef EMSESP_STANDALONE node["timestamp"] = time(nullptr); #endif + node["NTPStatus"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected"; // AP Status node = output["ap"].to(); From b0d490036f5add97154a28fa43777ffcbf22ecf7 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:56:58 +0100 Subject: [PATCH 10/27] fix bug, only find dest --- src/core/emsesp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index a722d776c..eb097bd5f 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -917,10 +917,10 @@ std::string EMSESP::pretty_telegram(std::shared_ptr telegram) { } } - // Fallback for type name if not found - try src or dest + // Fallback for type name if not found - try dest if (!type_found && telegram->operation != Telegram::Operation::RX_READ) { for (const auto & emsdevice : emsdevices) { - if (emsdevice->is_device_id(src) || emsdevice->is_device_id(dest)) { + if (emsdevice->is_device_id(dest)) { type_name = emsdevice->telegram_type_name(telegram); if (!type_name.empty()) { break; From 0dc3fd43e9a2b69f3a51b5cb87a9bde7b842cfee Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 12:57:13 +0100 Subject: [PATCH 11/27] remove comment, rollback changes --- src/core/command.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/core/command.cpp b/src/core/command.cpp index 0f388d770..aee649dd6 100644 --- a/src/core/command.cpp +++ b/src/core/command.cpp @@ -367,7 +367,6 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha // or a special command like 'info', 'values', 'commands', 'entities' etc bool single_command = (!value || !strlen(value)); if (single_command) { - Serial.println("single_command"); if (!strcmp(cmd, F_(commands))) { return Command::list(device_type, output); } @@ -584,11 +583,14 @@ bool Command::list(const uint8_t device_type, JsonObject output) { output[F_(entities)] = Helpers::translated_word(FL_(entities_cmd)); if (device_type == EMSdevice::DeviceType::SYSTEM) { - output["settings"] = Helpers::translated_word(FL_(system_cmd)); - output["mqtt"] = Helpers::translated_word(FL_(system_cmd)); - output["ntp"] = Helpers::translated_word(FL_(system_cmd)); - output["ap"] = Helpers::translated_word(FL_(system_cmd)); - output["syslog"] = Helpers::translated_word(FL_(system_cmd)); + output["settings/showertimer"] = Helpers::translated_word(FL_(system_cmd)); + output["settings/showeralert"] = Helpers::translated_word(FL_(system_cmd)); + output["settings/hideled"] = Helpers::translated_word(FL_(system_cmd)); + output["settings/analogenabled"] = Helpers::translated_word(FL_(system_cmd)); + output["mqtt/enabled"] = Helpers::translated_word(FL_(system_cmd)); + output["ntp/enabled"] = Helpers::translated_word(FL_(system_cmd)); + output["ap/enabled"] = Helpers::translated_word(FL_(system_cmd)); + output["syslog/enabled"] = Helpers::translated_word(FL_(system_cmd)); } // create a list of commands we have registered, and sort them @@ -620,11 +622,14 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo } } if (device_type == EMSdevice::DeviceType::SYSTEM) { - sorted_cmds.emplace_back("settings"); - sorted_cmds.emplace_back("mqtt"); - sorted_cmds.emplace_back("ntp"); - sorted_cmds.emplace_back("ap"); - sorted_cmds.emplace_back("syslog"); + sorted_cmds.emplace_back("settings/showertimer"); + sorted_cmds.emplace_back("settings/showeralert"); + sorted_cmds.emplace_back("settings/hideled"); + sorted_cmds.emplace_back("settings/analogenabled"); + sorted_cmds.emplace_back("mqtt/enabled"); + sorted_cmds.emplace_back("ntp/enabled"); + sorted_cmds.emplace_back("ap/enabled"); + sorted_cmds.emplace_back("syslog/enabled"); } sorted_cmds.sort(); // sort them From c9bc18cf4b1a0bda5ef49d8e3335868fc00c4ff5 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 6 Nov 2025 18:29:39 +0100 Subject: [PATCH 12/27] fix, attempt #3 --- src/core/emsesp.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index eb097bd5f..e37c498c8 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -917,13 +917,16 @@ std::string EMSESP::pretty_telegram(std::shared_ptr telegram) { } } - // Fallback for type name if not found - try dest + // Fallback for type name if not found - try src first, then dest if (!type_found && telegram->operation != Telegram::Operation::RX_READ) { - for (const auto & emsdevice : emsdevices) { - if (emsdevice->is_device_id(dest)) { - type_name = emsdevice->telegram_type_name(telegram); - if (!type_name.empty()) { - break; + for (int i = 0; i < 2 && type_name.empty(); ++i) { + uint8_t check_id = (i == 0) ? src : dest; + for (const auto & emsdevice : emsdevices) { + if (emsdevice->is_device_id(check_id)) { + type_name = emsdevice->telegram_type_name(telegram); + if (!type_name.empty()) { + break; + } } } } From d65d6f49cda67c1055ac33acfd7c2ab5491ab80f Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 11:30:40 +0100 Subject: [PATCH 13/27] fix FS format, move AP* from Network system info to own node --- src/core/system.cpp | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 7dc913d41..01bc222e5 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1452,7 +1452,7 @@ bool System::command_service(const char * cmd, const char * value) { ok = true; } } - + int n; if (!ok && Helpers::value2number(value, n)) { #ifndef EMSESP_STANDALONE @@ -1661,15 +1661,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output } }); -#ifndef EMSESP_STANDALONE - EMSESP::esp32React.getAPSettingsService()->read([&](const APSettings & settings) { - const char * pM[] = {"always", "disconnected", "never"}; - node["APProvisionMode"] = pM[settings.provisionMode]; - node["APSecurity"] = settings.password.length() ? "wpa2" : "open"; - node["APSSID"] = settings.ssid; - }); -#endif - // NTP status node = output["ntp"].to(); EMSESP::esp32React.getNTPSettingsService()->read([&](const NTPSettings & settings) { @@ -1689,9 +1680,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output // AP Status node = output["ap"].to(); EMSESP::esp32React.getAPSettingsService()->read([&](const APSettings & settings) { - node["provisionMode"] = settings.provisionMode; // 0 is on, 2 is off + const char * pM[] = {"always", "disconnected", "never"}; + node["provisionMode"] = pM[settings.provisionMode]; node["ssid"] = settings.ssid; #ifndef EMSESP_STANDALONE + node["security"] = settings.password.length() ? "wpa2" : "open"; node["channel"] = settings.channel; node["ssidHidden"] = settings.ssidHidden; node["maxClients"] = settings.maxClients; @@ -1991,15 +1984,12 @@ bool System::load_board_profile(std::vector & data, const std::string & // format command - factory reset, removing all config files bool System::command_format(const char * value, const int8_t id) { - LOG_INFO("Removing all config files"); + LOG_INFO("Formatting FS, removing all config files"); #ifndef EMSESP_STANDALONE - // TODO To replaced with LittleFS.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2+ - File root = LittleFS.open(EMSESP_FS_CONFIG_DIRECTORY); - File file; - while ((file = root.openNextFile())) { - String path = file.path(); - file.close(); - LittleFS.remove(path); + if (LittleFS.format()) { + LOG_INFO("FS formatted successfully"); + } else { + LOG_ERROR("Format failed"); } #endif From 5613cde00f4a042984d4766b03536ee0d587d932 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 12:27:08 +0100 Subject: [PATCH 14/27] update defaults --- lib/PButton/PButon.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/PButton/PButon.cpp b/lib/PButton/PButon.cpp index 8324b7728..0b0ece9f1 100644 --- a/lib/PButton/PButon.cpp +++ b/lib/PButton/PButon.cpp @@ -23,10 +23,10 @@ // Constructor PButton::PButton() { // Initialization of default properties - Debounce_ = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms) - DblClickDelay_ = 250; // Max period between clicks for a double click event (in ms) - LongPressDelay_ = 750; // Hold period for a long press event (in ms) - VLongPressDelay_ = 3000; // Hold period for a very long press event (in ms) + Debounce_ = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms) + DblClickDelay_ = 250; // Max period between clicks for a double click event (in ms) + LongPressDelay_ = 9500; // Hold period for a long press event (in ms) + VLongPressDelay_ = 20000; // Hold period for a very long press event (in ms) cb_onClick = nullptr; cb_onDblClick = nullptr; From fbc42fbb1543dccb395719d4d462cf476656c0f4 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 12:41:25 +0100 Subject: [PATCH 15/27] double press resets wifi to AP (https://github.com/emsesp/EMS-ESP32/discussions/2720) --- src/core/system.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 01bc222e5..ac0ce94ef 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -536,8 +536,18 @@ void System::button_OnClick(PButton & b) { // button double click void System::button_OnDblClick(PButton & b) { - LOG_NOTICE("Button pressed - double click - wifi reconnect"); - EMSESP::system_.wifi_reconnect(); + LOG_NOTICE("Button pressed - double click - wifi reconnect to AP"); + // set AP mode to always so will join AP if wifi ssid fails to connect + EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) { + apSettings.provisionMode = AP_MODE_ALWAYS; + return StateUpdateResult::CHANGED; + }); + // remove SSID from network settings + EMSESP::esp32React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) { + networkSettings.ssid = ""; + return StateUpdateResult::CHANGED; + }); + EMSESP::esp32React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password } // button long press From 9a1dd5bb985f8250e7c1f6914b207afad1c15bb5 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 12:47:00 +0100 Subject: [PATCH 16/27] update standalone and test output --- lib_standalone/ESP32React.h | 1 + test/test_api/test_api.h | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib_standalone/ESP32React.h b/lib_standalone/ESP32React.h index f01b43635..4da6036a6 100644 --- a/lib_standalone/ESP32React.h +++ b/lib_standalone/ESP32React.h @@ -21,6 +21,7 @@ #define NTP_SETTINGS_FILE "/config/ntpSettings.json" #define EMSESP_SETTINGS_FILE "/config/emsespSettings.json" +#define AP_MODE_ALWAYS 0 class DummySettings { public: // SYSTEM diff --git a/test/test_api/test_api.h b/test/test_api/test_api.h index ce6dc55b3..52d9eae5f 100644 --- a/test/test_api/test_api.h +++ b/test/test_api/test_api.h @@ -1,4 +1,3 @@ - // ---------- START - CUT HERE ---------- void test_1() { @@ -163,7 +162,7 @@ void test_21() { "[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " "Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"enabled\":true,\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/" - "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":0,\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," + "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60," "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1," "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10," @@ -191,7 +190,7 @@ void test_22() { "[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " "Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," "\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"enabled\":true,\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/" - "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":0,\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," + "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60," "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1," "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10," @@ -437,4 +436,4 @@ void run_tests() { RUN_TEST(test_54); } -// ---------- END - CUT HERE ---------- +// ---------- END - CUT HERE ---------- \ No newline at end of file From 026ea4450eef4550b39c0c82ce23231e0d38b29a Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 15:26:54 +0100 Subject: [PATCH 17/27] error is color red --- interface/src/components/inputs/ValidatedTextField.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/components/inputs/ValidatedTextField.tsx b/interface/src/components/inputs/ValidatedTextField.tsx index b7ccd3ae6..ad1b87f37 100644 --- a/interface/src/components/inputs/ValidatedTextField.tsx +++ b/interface/src/components/inputs/ValidatedTextField.tsx @@ -23,7 +23,9 @@ const ValidatedTextField: FC = ({ <> {errors?.map((e) => ( - {e.message} + + {e.message} + ))} ); From cec5ffd5470f70d7d1b3d7a27a89906da5767c4f Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 15:27:03 +0100 Subject: [PATCH 18/27] package update --- interface/package.json | 2 +- interface/pnpm-lock.yaml | 254 +++++++++++++++++++-------------------- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/interface/package.json b/interface/package.json index 5ddce35a0..cc5c7e633 100644 --- a/interface/package.json +++ b/interface/package.json @@ -63,7 +63,7 @@ "rollup-plugin-visualizer": "^6.0.5", "terser": "^5.44.1", "typescript-eslint": "^8.46.3", - "vite": "^7.2.0", + "vite": "^7.2.2", "vite-plugin-imagemin": "^0.6.1", "vite-tsconfig-paths": "^5.1.4" }, diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index c177c8343..6a19b3e35 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -83,7 +83,7 @@ importers: version: 9.39.1 '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)) + version: 2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.0 version: 6.0.0(prettier@3.6.2) @@ -113,7 +113,7 @@ importers: version: 3.6.2 rollup-plugin-visualizer: specifier: ^6.0.5 - version: 6.0.5(rollup@4.52.5) + version: 6.0.5(rollup@4.53.1) terser: specifier: ^5.44.1 version: 5.44.1 @@ -121,14 +121,14 @@ importers: specifier: ^8.46.3 version: 8.46.3(eslint@9.39.1)(typescript@5.9.3) vite: - specifier: ^7.2.0 - version: 7.2.0(@types/node@24.10.0)(terser@5.44.1) + specifier: ^7.2.2 + version: 7.2.2(@types/node@24.10.0)(terser@5.44.1) vite-plugin-imagemin: specifier: ^0.6.1 - version: 0.6.1(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)) + version: 0.6.1(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)) + version: 5.1.4(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)) packages: @@ -679,113 +679,113 @@ packages: resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} - '@rollup/rollup-android-arm-eabi@4.52.5': - resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + '@rollup/rollup-android-arm-eabi@4.53.1': + resolution: {integrity: sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.5': - resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + '@rollup/rollup-android-arm64@4.53.1': + resolution: {integrity: sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.5': - resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + '@rollup/rollup-darwin-arm64@4.53.1': + resolution: {integrity: sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.5': - resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + '@rollup/rollup-darwin-x64@4.53.1': + resolution: {integrity: sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.5': - resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + '@rollup/rollup-freebsd-arm64@4.53.1': + resolution: {integrity: sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.5': - resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + '@rollup/rollup-freebsd-x64@4.53.1': + resolution: {integrity: sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': - resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.1': + resolution: {integrity: sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.5': - resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + '@rollup/rollup-linux-arm-musleabihf@4.53.1': + resolution: {integrity: sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.5': - resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + '@rollup/rollup-linux-arm64-gnu@4.53.1': + resolution: {integrity: sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.5': - resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + '@rollup/rollup-linux-arm64-musl@4.53.1': + resolution: {integrity: sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.5': - resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + '@rollup/rollup-linux-loong64-gnu@4.53.1': + resolution: {integrity: sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + '@rollup/rollup-linux-ppc64-gnu@4.53.1': + resolution: {integrity: sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': - resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + '@rollup/rollup-linux-riscv64-gnu@4.53.1': + resolution: {integrity: sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.5': - resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + '@rollup/rollup-linux-riscv64-musl@4.53.1': + resolution: {integrity: sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.5': - resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + '@rollup/rollup-linux-s390x-gnu@4.53.1': + resolution: {integrity: sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.5': - resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + '@rollup/rollup-linux-x64-gnu@4.53.1': + resolution: {integrity: sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.5': - resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + '@rollup/rollup-linux-x64-musl@4.53.1': + resolution: {integrity: sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.5': - resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + '@rollup/rollup-openharmony-arm64@4.53.1': + resolution: {integrity: sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.5': - resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + '@rollup/rollup-win32-arm64-msvc@4.53.1': + resolution: {integrity: sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': - resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + '@rollup/rollup-win32-ia32-msvc@4.53.1': + resolution: {integrity: sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': - resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + '@rollup/rollup-win32-x64-gnu@4.53.1': + resolution: {integrity: sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': - resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + '@rollup/rollup-win32-x64-msvc@4.53.1': + resolution: {integrity: sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==} cpu: [x64] os: [win32] @@ -1027,8 +1027,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.24: - resolution: {integrity: sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==} + baseline-browser-mapping@2.8.25: + resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} hasBin: true bin-build@3.0.0: @@ -1117,8 +1117,8 @@ packages: resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} engines: {node: '>=0.10.0'} - caniuse-lite@1.0.30001753: - resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} + caniuse-lite@1.0.30001754: + resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} caw@2.0.1: resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==} @@ -1337,8 +1337,8 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.245: - resolution: {integrity: sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==} + electron-to-chromium@1.5.249: + resolution: {integrity: sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2628,8 +2628,8 @@ packages: rollup: optional: true - rollup@4.52.5: - resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + rollup@4.53.1: + resolution: {integrity: sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2999,8 +2999,8 @@ packages: vite: optional: true - vite@7.2.0: - resolution: {integrity: sha512-C/Naxf8H0pBx1PA4BdpT+c/5wdqI9ILMdwjSMILw7tVIh3JsxzZqdeTLmmdaoh5MYUEOyBnM9K3o0DzoZ/fe+w==} + vite@7.2.2: + resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3598,18 +3598,18 @@ snapshots: dependencies: preact: 10.27.2 - '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))': + '@preact/preset-vite@2.10.2(@babel/core@7.28.5)(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) - '@prefresh/vite': 2.4.11(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)) + '@prefresh/vite': 2.4.11(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)) '@rollup/pluginutils': 4.2.1 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.28.5) debug: 4.4.3 picocolors: 1.1.1 - vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1) - vite-prerender-plugin: 0.5.12(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)) + vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1) + vite-prerender-plugin: 0.5.12(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)) transitivePeerDependencies: - preact - supports-color @@ -3622,7 +3622,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.11(preact@10.27.2)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1))': + '@prefresh/vite@2.4.11(preact@10.27.2)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1))': dependencies: '@babel/core': 7.28.5 '@prefresh/babel-plugin': 0.5.2 @@ -3630,7 +3630,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.27.2 - vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1) + vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1) transitivePeerDependencies: - supports-color @@ -3639,70 +3639,70 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.1 - '@rollup/rollup-android-arm-eabi@4.52.5': + '@rollup/rollup-android-arm-eabi@4.53.1': optional: true - '@rollup/rollup-android-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.53.1': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@rollup/rollup-darwin-arm64@4.53.1': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@rollup/rollup-darwin-x64@4.53.1': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@rollup/rollup-freebsd-arm64@4.53.1': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@rollup/rollup-freebsd-x64@4.53.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@rollup/rollup-linux-arm-gnueabihf@4.53.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@rollup/rollup-linux-arm-musleabihf@4.53.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@rollup/rollup-linux-arm64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.53.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.5': + '@rollup/rollup-linux-riscv64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': + '@rollup/rollup-linux-riscv64-musl@4.53.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': + '@rollup/rollup-linux-s390x-gnu@4.53.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': + '@rollup/rollup-linux-x64-gnu@4.53.1': optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': + '@rollup/rollup-linux-x64-musl@4.53.1': optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': + '@rollup/rollup-openharmony-arm64@4.53.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': + '@rollup/rollup-win32-arm64-msvc@4.53.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': + '@rollup/rollup-win32-ia32-msvc@4.53.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': + '@rollup/rollup-win32-x64-gnu@4.53.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': + '@rollup/rollup-win32-x64-msvc@4.53.1': optional: true '@sindresorhus/is@0.7.0': {} @@ -3963,7 +3963,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.8.24: {} + baseline-browser-mapping@2.8.25: {} bin-build@3.0.0: dependencies: @@ -4020,9 +4020,9 @@ snapshots: browserslist@4.27.0: dependencies: - baseline-browser-mapping: 2.8.24 - caniuse-lite: 1.0.30001753 - electron-to-chromium: 1.5.245 + baseline-browser-mapping: 2.8.25 + caniuse-lite: 1.0.30001754 + electron-to-chromium: 1.5.249 node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.27.0) @@ -4080,7 +4080,7 @@ snapshots: camelcase@2.1.1: {} - caniuse-lite@1.0.30001753: {} + caniuse-lite@1.0.30001754: {} caw@2.0.1: dependencies: @@ -4367,7 +4367,7 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.245: {} + electron-to-chromium@1.5.249: {} emoji-regex@8.0.0: {} @@ -5621,41 +5621,41 @@ snapshots: dependencies: glob: 7.2.3 - rollup-plugin-visualizer@6.0.5(rollup@4.52.5): + rollup-plugin-visualizer@6.0.5(rollup@4.53.1): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.6 yargs: 17.7.2 optionalDependencies: - rollup: 4.52.5 + rollup: 4.53.1 - rollup@4.52.5: + rollup@4.53.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.5 - '@rollup/rollup-android-arm64': 4.52.5 - '@rollup/rollup-darwin-arm64': 4.52.5 - '@rollup/rollup-darwin-x64': 4.52.5 - '@rollup/rollup-freebsd-arm64': 4.52.5 - '@rollup/rollup-freebsd-x64': 4.52.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 - '@rollup/rollup-linux-arm-musleabihf': 4.52.5 - '@rollup/rollup-linux-arm64-gnu': 4.52.5 - '@rollup/rollup-linux-arm64-musl': 4.52.5 - '@rollup/rollup-linux-loong64-gnu': 4.52.5 - '@rollup/rollup-linux-ppc64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-musl': 4.52.5 - '@rollup/rollup-linux-s390x-gnu': 4.52.5 - '@rollup/rollup-linux-x64-gnu': 4.52.5 - '@rollup/rollup-linux-x64-musl': 4.52.5 - '@rollup/rollup-openharmony-arm64': 4.52.5 - '@rollup/rollup-win32-arm64-msvc': 4.52.5 - '@rollup/rollup-win32-ia32-msvc': 4.52.5 - '@rollup/rollup-win32-x64-gnu': 4.52.5 - '@rollup/rollup-win32-x64-msvc': 4.52.5 + '@rollup/rollup-android-arm-eabi': 4.53.1 + '@rollup/rollup-android-arm64': 4.53.1 + '@rollup/rollup-darwin-arm64': 4.53.1 + '@rollup/rollup-darwin-x64': 4.53.1 + '@rollup/rollup-freebsd-arm64': 4.53.1 + '@rollup/rollup-freebsd-x64': 4.53.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.1 + '@rollup/rollup-linux-arm-musleabihf': 4.53.1 + '@rollup/rollup-linux-arm64-gnu': 4.53.1 + '@rollup/rollup-linux-arm64-musl': 4.53.1 + '@rollup/rollup-linux-loong64-gnu': 4.53.1 + '@rollup/rollup-linux-ppc64-gnu': 4.53.1 + '@rollup/rollup-linux-riscv64-gnu': 4.53.1 + '@rollup/rollup-linux-riscv64-musl': 4.53.1 + '@rollup/rollup-linux-s390x-gnu': 4.53.1 + '@rollup/rollup-linux-x64-gnu': 4.53.1 + '@rollup/rollup-linux-x64-musl': 4.53.1 + '@rollup/rollup-openharmony-arm64': 4.53.1 + '@rollup/rollup-win32-arm64-msvc': 4.53.1 + '@rollup/rollup-win32-ia32-msvc': 4.53.1 + '@rollup/rollup-win32-x64-gnu': 4.53.1 + '@rollup/rollup-win32-x64-msvc': 4.53.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -5970,7 +5970,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-imagemin@0.6.1(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)): + vite-plugin-imagemin@0.6.1(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)): dependencies: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -5995,11 +5995,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 pathe: 0.2.0 - vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1) + vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1) transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.12(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)): + vite-prerender-plugin@0.5.12(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -6007,26 +6007,26 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0-pre2 - vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1) + vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1) - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.0(@types/node@24.10.0)(terser@5.44.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)(terser@5.44.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.2.0(@types/node@24.10.0)(terser@5.44.1) + vite: 7.2.2(@types/node@24.10.0)(terser@5.44.1) transitivePeerDependencies: - supports-color - typescript - vite@7.2.0(@types/node@24.10.0)(terser@5.44.1): + vite@7.2.2(@types/node@24.10.0)(terser@5.44.1): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.5 + rollup: 4.53.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.0 From 0bf60394fe8b7b821e99455ccde863413330a9b7 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 15:27:10 +0100 Subject: [PATCH 19/27] dev-26 --- src/emsesp_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emsesp_version.h b/src/emsesp_version.h index caaa33454..cc360f69c 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.3-dev.25" +#define EMSESP_APP_VERSION "3.7.3-dev.26" From 18d67d088ef0065f165f28dfd4398cc5039c7e3e Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 15:27:16 +0100 Subject: [PATCH 20/27] update --- project-words.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project-words.txt b/project-words.txt index feffbe623..f5d4b01b6 100644 --- a/project-words.txt +++ b/project-words.txt @@ -1435,4 +1435,5 @@ teddybear washingmachine switchprogram brotlin -fanspd \ No newline at end of file +fanspd +currhum \ No newline at end of file From b98cbd3ec52e96356cc56479eceb430a72fff434 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 16:27:25 +0100 Subject: [PATCH 21/27] add mock sensor data --- mock-api/restServer.ts | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/mock-api/restServer.ts b/mock-api/restServer.ts index cb3627521..5f0d7d09a 100644 --- a/mock-api/restServer.ts +++ b/mock-api/restServer.ts @@ -276,10 +276,10 @@ function updateMask(entity: any, de: any, dd: any) { const old_custom_name = dd.nodes[dd_objIndex].cn; console.log( 'comparing names, old (' + - old_custom_name + - ') with new (' + - new_custom_name + - ')' + old_custom_name + + ') with new (' + + new_custom_name + + ')' ); if (old_custom_name !== new_custom_name) { changed = true; @@ -375,15 +375,15 @@ function check_upgrade(version: string) { console.log( 'Upgrade this version (' + - THIS_VERSION + - ') to dev (' + - dev_version + - ') is ' + - (DEV_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') + - ' and to stable (' + - stable_version + - ') is ' + - (STABLE_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') + THIS_VERSION + + ') to dev (' + + dev_version + + ') is ' + + (DEV_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') + + ' and to stable (' + + stable_version + + ') is ' + + (STABLE_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') ); data = { emsesp_version: THIS_VERSION, @@ -965,14 +965,17 @@ const emsesp_sensordata = { { id: '28-233D-9497-0C03', n: 'Dallas 1', t: 25.7, o: 1.2, u: 1 }, { id: '28-243D-7437-1E3A', n: 'Dallas 2 outside', t: 26.1, o: 0, u: 1 }, { id: '28-243E-7437-1E3B', n: 'Zolder', t: 27.1, o: 0, u: 1 }, - { id: '28-183D-1892-0C33', n: 'Roof', o: 2, u: 1 } // no temperature + { id: '28-183D-1892-0C33', n: 'Roof', o: 2, u: 1 }, // no temperature + { id: '28_1767_7B13_2502', n: 'gateway_temperature', t: 28.1, o: 0, u: 1 } // internal system temp ], // as: [], as: [ - { id: 1, g: 36, n: 'motor', v: 0, u: 0, o: 17, f: 0, t: 0, d: false }, - { id: 2, g: 37, n: 'External switch', v: 13, u: 0, o: 17, f: 0, t: 1, d: false }, - { id: 3, g: 39, n: 'Pulse count', v: 144, u: 0, o: 0, f: 0, t: 2, d: false }, - { id: 4, g: 40, n: 'Pressure', v: 16, u: 17, o: 0, f: 0, t: 3, d: false } + { id: 1, g: 35, n: 'motor', v: 0, u: 0, o: 17, f: 0, t: 0, d: false, s: false }, + { id: 2, g: 37, n: 'External switch', v: 13, u: 0, o: 17, f: 0, t: 1, d: false, s: false }, + { id: 3, g: 39, n: 'Pulse count', v: 144, u: 0, o: 0, f: 0, t: 2, d: false, s: false }, + { id: 4, g: 40, n: 'Pressure', v: 16, u: 17, o: 0, f: 0, t: 3, d: false, s: false }, + { id: 6, g: 39, n: 'core_voltage', v: 3.34, u: 23, o: 0, f: 0, t: 3, d: false, s: true }, + { id: 7, g: 36, n: 'supply_voltage', v: 12.21, u: 23, o: 0, f: 0, t: 3, d: false, s: true } ], analog_enabled: true }; @@ -4811,6 +4814,7 @@ router u: as.uom, t: as.type, d: as.deleted, + s: as.is_system, v: 0 // must be added for demo only }); } else { From 5f0df140b0d0dda50c4491c1d673474c248d4aa2 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 16:28:04 +0100 Subject: [PATCH 22/27] factory reset show system monitor --- interface/src/app/settings/Settings.tsx | 213 +++++++++++++----------- 1 file changed, 114 insertions(+), 99 deletions(-) diff --git a/interface/src/app/settings/Settings.tsx b/interface/src/app/settings/Settings.tsx index 57472392d..608c37ee9 100644 --- a/interface/src/app/settings/Settings.tsx +++ b/interface/src/app/settings/Settings.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import AccessTimeIcon from '@mui/icons-material/AccessTime'; import CancelIcon from '@mui/icons-material/Cancel'; @@ -30,11 +30,14 @@ import { SectionContent, useLayoutTitle } from 'components'; import ListMenuItem from 'components/layout/ListMenuItem'; import { useI18nContext } from 'i18n/i18n-react'; +import SystemMonitor from '../status/SystemMonitor'; + const Settings = () => { const { LL } = useI18nContext(); useLayoutTitle(LL.SETTINGS(0)); const [confirmFactoryReset, setConfirmFactoryReset] = useState(false); + const [restarting, setRestarting] = useState(); const { send: sendAPI } = useRequest((data: APIcall) => API(data), { immediate: false @@ -42,6 +45,7 @@ const Settings = () => { const doFormat = useCallback(async () => { await sendAPI({ device: 'system', cmd: 'format', id: 0 }).then(() => { + setRestarting(true); setConfirmFactoryReset(false); }); }, [sendAPI]); @@ -54,120 +58,131 @@ const Settings = () => { setConfirmFactoryReset(true); }, []); - return ( - - - + const content = useMemo(() => { + return ( + <> + + - + - + - + - + - + - + - - + + - - {LL.FACTORY_RESET()} - {LL.SYSTEM_FACTORY_TEXT_DIALOG()} - - + + {LL.FACTORY_RESET()} + {LL.SYSTEM_FACTORY_TEXT_DIALOG()} + + + + + + + + + - - + + + ); + }, [ + LL, + handleFactoryResetClick, + handleFactoryResetClose, + doFormat, + confirmFactoryReset, + restarting + ]); - - - - - - - ); + return {restarting ? : content}; }; export default Settings; From 3fd05c8eb742553d64b7837240af0765f0fae0ba Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 16:28:26 +0100 Subject: [PATCH 23/27] fix lint error --- interface/src/app/settings/ApplicationSettings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/app/settings/ApplicationSettings.tsx b/interface/src/app/settings/ApplicationSettings.tsx index 26692109d..854c347ca 100644 --- a/interface/src/app/settings/ApplicationSettings.tsx +++ b/interface/src/app/settings/ApplicationSettings.tsx @@ -37,13 +37,13 @@ import { validate } from 'validators'; import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app'; import { BOARD_PROFILES } from '../main/types'; -import type { APIcall, Settings } from '../main/types'; +import type { APIcall, BoardProfileKey, Settings } from '../main/types'; import { createSettingsValidator } from '../main/validators'; export function boardProfileSelectItems() { return Object.keys(BOARD_PROFILES).map((code) => ( - {BOARD_PROFILES[code]} + {BOARD_PROFILES[code as BoardProfileKey]} )); } From dc838639b2a7344995e4063823960f17e125ff5c Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 8 Nov 2025 16:30:40 +0100 Subject: [PATCH 24/27] add is_system to analog sensor so internal sensors cannot be removed --- .../src/app/main/SensorsAnalogDialog.tsx | 1 + interface/src/app/main/types.ts | 1 + src/core/analogsensor.cpp | 56 +++++++++++-------- src/core/analogsensor.h | 11 +++- src/web/WebCustomizationService.cpp | 30 +++++----- src/web/WebCustomizationService.h | 5 +- src/web/WebDataService.cpp | 1 + 7 files changed, 62 insertions(+), 43 deletions(-) diff --git a/interface/src/app/main/SensorsAnalogDialog.tsx b/interface/src/app/main/SensorsAnalogDialog.tsx index 2294a1991..5e43cde81 100644 --- a/interface/src/app/main/SensorsAnalogDialog.tsx +++ b/interface/src/app/main/SensorsAnalogDialog.tsx @@ -447,6 +447,7 @@ const SensorsAnalogDialog = ({