From a0196ff9b2a34366dab7ef0a8562b4084c02cd0f Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 29 May 2026 16:14:59 +0100 Subject: [PATCH 1/5] add comment --- src/web/WebStatusService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index fbc8f99e3..7966d04fc 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -470,7 +470,7 @@ bool WebStatusService::refresh_versions_cache() { ssl_client.println("Connection: close"); ssl_client.print("\r\n"); - // wait for the first byte + // wait for the first byte. The 5 seconds is GitHub's SLO uint32_t ms = millis(); while (ssl_client.connected() && !ssl_client.available() && millis() - ms < 5000) { delay(1); From c7816a644fd59ccf42113fbf375aeef4036b9e68 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 29 May 2026 16:15:29 +0100 Subject: [PATCH 2/5] prevent message command parsing URLs twice --- src/web/WebSchedulerService.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 9e526a119..7705de4b0 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -431,6 +431,18 @@ bool WebSchedulerService::onChange(const char * cmd) { return false; } +// system/message evaluates its own argument later (deferred via raw_value, computed in loop()), +// so pre-computing it here would make any {url} or expression inside it run twice. Pass +// system/message its value raw; compute() everything else as before. +// templated because ScheduleItem's strings use a PSRAM allocator, not std::string. +template +static std::string compute_cmd_value(const C & cmd, const V & value) { + if (Helpers::toLower(cmd.c_str()) == "system/message") { + return std::string(value.c_str()); + } + return compute(value.c_str()); +} + // handle condition schedules, parse string stored in schedule.time field void WebSchedulerService::condition() { for (ScheduleItem & scheduleItem : *scheduleItems_) { @@ -440,7 +452,7 @@ void WebSchedulerService::condition() { // EMSESP::logger().debug("condition match: %s", match.c_str()); #endif if (match.length() == 1 && match[0] == '1' && scheduleItem.retry_cnt == 0xFF) { - scheduleItem.retry_cnt = command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())) ? 1 : 0xFF; + scheduleItem.retry_cnt = command(scheduleItem.name, scheduleItem.cmd.c_str(), compute_cmd_value(scheduleItem.cmd, scheduleItem.value)) ? 1 : 0xFF; } else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) { scheduleItem.retry_cnt = 0xFF; } else if (match.length() != 1) { // the match is not boolean @@ -472,13 +484,13 @@ void WebSchedulerService::loop() { // check if we have onChange events while (!cmd_changed_.empty()) { ScheduleItem si = *cmd_changed_.front(); - command(si.name, si.cmd.c_str(), compute(si.value.c_str())); + command(si.name, si.cmd.c_str(), compute_cmd_value(si.cmd, si.value)); cmd_changed_.pop_front(); } for (ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) { - command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())); + command(scheduleItem.name, scheduleItem.cmd.c_str(), compute_cmd_value(scheduleItem.cmd, scheduleItem.value)); // scheduleItem.active = false; publish_single(scheduleItem.name, false); if (EMSESP::mqtt_.get_publish_onchange(0)) { @@ -498,7 +510,7 @@ void WebSchedulerService::loop() { if (last_tm_min == -2) { for (ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0) { - scheduleItem.retry_cnt = command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())) ? 0xFF : 0; + scheduleItem.retry_cnt = command(scheduleItem.name, scheduleItem.cmd.c_str(), compute_cmd_value(scheduleItem.cmd, scheduleItem.value)) ? 0xFF : 0; } } last_tm_min = -1; // startup done, now use for RTC @@ -516,7 +528,7 @@ void WebSchedulerService::loop() { // scheduled timer commands if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0 && (uptime_min % scheduleItem.elapsed_min == 0)) { - command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())); + command(scheduleItem.name, scheduleItem.cmd.c_str(), compute_cmd_value(scheduleItem.cmd, scheduleItem.value)); } } last_uptime_min = uptime_min; @@ -533,7 +545,7 @@ void WebSchedulerService::loop() { for (const ScheduleItem & scheduleItem : *scheduleItems_) { uint8_t dow = scheduleItem.flags & SCHEDULEFLAG_SCHEDULE_TIMER ? 0 : scheduleItem.flags; if (scheduleItem.active && (real_dow & dow) && real_min == scheduleItem.elapsed_min) { - command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())); + command(scheduleItem.name, scheduleItem.cmd.c_str(), compute_cmd_value(scheduleItem.cmd, scheduleItem.value)); } } last_tm_min = tm->tm_min; @@ -545,7 +557,7 @@ bool WebSchedulerService::executeSchedule(const char * name) { for (ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE && strcmp(scheduleItem.name, name) == 0) { EMSESP::logger().info("Executing schedule '%s'", name); - return command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str())); + return command(scheduleItem.name, scheduleItem.cmd.c_str(), compute_cmd_value(scheduleItem.cmd, scheduleItem.value)); } } EMSESP::logger().warning("Schedule '%s' not found", name); From 7b6d60691ca549c7b1286fa2274477c5eec6f635 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 29 May 2026 16:15:54 +0100 Subject: [PATCH 3/5] add missing "\r\n" to GET header --- src/core/shuntingYard.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/shuntingYard.cpp b/src/core/shuntingYard.cpp index f15c54fc7..9f833892a 100644 --- a/src/core/shuntingYard.cpp +++ b/src/core/shuntingYard.cpp @@ -748,15 +748,19 @@ int http_request(std::string url, const std::string & method, const std::string ssl_client->print(value.c_str()); } else { ssl_client->println("Connection: close"); + ssl_client->print("\r\n"); // terminate headers - without this the server never responds } + auto ms = millis(); while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) { - delay(0); + delay(1); } + while (ssl_client->available()) { result += (char)ssl_client->read(); } ssl_client->stop(); + index = result.find_first_of(' '); if (index != std::string::npos) { httpResult = stoi(result.substr(index + 1, 3)); @@ -817,7 +821,8 @@ std::string compute(const std::string & expr) { std::string value = doc[value_s] | ""; std::string method = doc[method_s] | "GET"; std::string result; - int httpResult = http_request(url, method, value, doc[header_s].as(), result); + + int httpResult = http_request(url, method, value, doc[header_s].as(), result); if (httpResult == 200) { std::string key = doc[key_s] | ""; JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body @@ -847,6 +852,7 @@ std::string compute(const std::string & expr) { } expr_new.replace(f, e - f, result); } else if (httpResult != 0) { + // httpResult of 0 means no url EMSESP::logger().warning("URL command failed with https code: %d, response: %s", httpResult, result.c_str()); } } From 844ee751b7d1e3fbd5332cbc5026223228f32330 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 29 May 2026 16:16:05 +0100 Subject: [PATCH 4/5] add esp32_exception_decoder --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index e6c7d83f7..f35b66211 100644 --- a/platformio.ini +++ b/platformio.ini @@ -85,7 +85,7 @@ build_unflags = extra_scripts = post:scripts/rename_fw.py ; renames the firmware .bin file monitor_speed = 115200 -monitor_filters = direct +monitor_filters = direct, esp32_exception_decoder build_type = release board_build.filesystem = littlefs board_build.littlefs_version = 2.0 From d79f27422f50dd8d556f27556b7f48b80c09ab34 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 29 May 2026 16:17:38 +0100 Subject: [PATCH 5/5] package update --- interface/pnpm-lock.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 47b2c7e77..80b5b8b96 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -606,8 +606,8 @@ packages: resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} - '@rollup/pluginutils@5.3.0': - resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -1185,8 +1185,8 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.363: - resolution: {integrity: sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA==} + electron-to-chromium@1.5.364: + resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -3430,7 +3430,7 @@ snapshots: '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7) '@prefresh/vite': 2.4.12(preact@10.29.2)(vite@8.0.14(@types/node@25.9.1)(terser@5.48.0)) - '@rollup/pluginutils': 5.3.0 + '@rollup/pluginutils': 5.4.0 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7) debug: 4.4.3 magic-string: 0.30.21 @@ -3519,7 +3519,7 @@ snapshots: estree-walker: 2.0.2 picomatch: 2.3.2 - '@rollup/pluginutils@5.3.0': + '@rollup/pluginutils@5.4.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 @@ -3847,7 +3847,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.10.32 caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.363 + electron-to-chromium: 1.5.364 node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -4202,7 +4202,7 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.363: {} + electron-to-chromium@1.5.364: {} emoji-regex@10.6.0: {}