From fb8deb41f9df193b9515fd7882d2b0dad3f72079 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 21 Jun 2024 08:28:07 +0200 Subject: [PATCH 1/5] remove string conversion --- src/web/WebSchedulerService.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index dd331fcb7..6f4515539 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -372,7 +372,7 @@ bool WebSchedulerService::onChange(const char * cmd) { #ifdef EMESESP_DEBUG // emsesp::EMSESP::logger().debug(scheduleItem.cmd.c_str()); #endif - return command(scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str()).c_str()); + return command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); } } return false; @@ -381,13 +381,13 @@ bool WebSchedulerService::onChange(const char * cmd) { void WebSchedulerService::condition() { for (ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_CONDITION) { - auto match = compute(scheduleItem.time.c_str()); + auto match = compute(scheduleItem.time); #ifdef EMESESP_DEBUG // emsesp::EMSESP::logger().debug("condition match: %s", match.c_str()); #endif if (!match.empty() && match[0] == '1') { if (scheduleItem.retry_cnt == 0xFF) { // default unswitched - scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str()).c_str()) ? 1 : 0xFF; + scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 1 : 0xFF; } } else if (scheduleItem.retry_cnt == 1) { scheduleItem.retry_cnt = 0xFF; @@ -420,7 +420,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.cmd.c_str(), compute(scheduleItem.value.c_str()).c_str()) ? 0xFF : 0; + scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 0xFF : 0; } } last_tm_min = -1; // startup done, now use for RTC @@ -438,7 +438,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.cmd.c_str(), compute(scheduleItem.value.c_str()).c_str()); + command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); } } last_uptime_min = uptime_min; @@ -455,7 +455,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.cmd.c_str(), compute(scheduleItem.value.c_str()).c_str()); + command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); } } last_tm_min = tm->tm_min; From 3ff3e8a8cf9a415d8e1d0dca5b9d6956341355ba Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 21 Jun 2024 11:03:33 +0200 Subject: [PATCH 2/5] system commands case independend, logic checks, small fixes --- interface/src/i18n/de/index.ts | 2 +- src/command.cpp | 5 ++--- src/system.cpp | 25 ++++++++++++++++++++----- src/web/shuntingYard.hpp | 17 ++++++++++++++--- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/interface/src/i18n/de/index.ts b/interface/src/i18n/de/index.ts index d59f7e195..7d2206ee4 100644 --- a/interface/src/i18n/de/index.ts +++ b/interface/src/i18n/de/index.ts @@ -121,7 +121,7 @@ const de: Translation = { READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)', UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten', REMOTE_TIMEOUT: 'Timeout', - REMOTE_TIMEOUT_EN: 'Deaktitiere Remote bei fehender Temperatur', + REMOTE_TIMEOUT_EN: 'Deaktitiere Remote bei fehlender Temperatur', HEATINGOFF: 'Heizen ausschalten beim EMS-ESP Start', MIN_DURATION: 'Dauer bis die Dusche erkannt wrid', ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren', diff --git a/src/command.cpp b/src/command.cpp index fbd132a8f..afdbdaa8d 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -320,8 +320,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // for example info, values, commands, etc bool single_command = (!value || !strlen(value)); if (single_command) { - // exception 1: anything that is from System - // exception 2: boiler coldshot command + // exception: boiler coldshot command bool get_attributes = (!cf || !cf->cmdfunction_json_) && (strcmp(cmd, F_(coldshot)) != 0); if (get_attributes) { @@ -444,7 +443,7 @@ void Command::erase_device_commands(const uint8_t device_type) { auto it = cmdfunctions_.end(); do { int i = it - cmdfunctions_.begin(); - if (cmdfunctions_[i].device_type_==device_type) { + if (cmdfunctions_[i].device_type_ == device_type) { cmdfunctions_.erase(it); } } while (it-- > cmdfunctions_.begin()); diff --git a/src/system.cpp b/src/system.cpp index d01ea90e0..9ee69cb31 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -1290,10 +1290,25 @@ bool System::get_value_info(JsonObject root, const char * command) { } if (command_info("", 0, root)) { std::string s; - if (dash && root[cmd].containsKey(dash)) { - s = root[cmd][dash].as(); - } else if (root.containsKey(cmd)) { - s = root[cmd].as(); + // Loop through all the key-value pairs in root to find the key case independent + if (dash) { // search the nest first + for (JsonPair p : root) { + if (p.value().is() && Helpers::toLower(p.key().c_str()) == cmd) { + for (JsonPair p1 : p.value().as()) { + if (Helpers::toLower(p1.key().c_str()) == dash && !p1.value().is()) { + s = p1.value().as(); + break; + } + } + } + } + } else { + for (JsonPair p : root) { + if (Helpers::toLower(p.key().c_str()) == cmd && !p.value().is()) { + s = p.value().as(); + break; + } + } } if (!s.empty()) { root.clear(); @@ -1306,7 +1321,7 @@ bool System::get_value_info(JsonObject root, const char * command) { } } root.clear(); - LOG_ERROR("system command not found: %s from %s", cmd, command); + LOG_ERROR("system command '%s' not found", command); return false; } diff --git a/src/web/shuntingYard.hpp b/src/web/shuntingYard.hpp index 10e2fa08e..0db7cf897 100644 --- a/src/web/shuntingYard.hpp +++ b/src/web/shuntingYard.hpp @@ -56,6 +56,7 @@ class Token { const bool rightAssociative; }; +// find tokens std::deque exprToTokens(const std::string & expr) { std::deque tokens; @@ -204,7 +205,7 @@ std::deque exprToTokens(const std::string & expr) { return tokens; } - +// sort tokens to RPN form std::deque shuntingYard(const std::deque & tokens) { std::deque queue; std::vector stack; @@ -303,6 +304,7 @@ std::deque shuntingYard(const std::deque & tokens) { return queue; } +// check if string is a number bool isnum(const std::string & s) { if (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos)) { return true; @@ -356,6 +358,7 @@ std::string commands(std::string & expr) { return expr; } +// checks for logic value int to_logic(const std::string & s) { if (s[0] == '1' || s == "on" || s == "ON" || s == "true") { return 1; @@ -363,9 +366,10 @@ int to_logic(const std::string & s) { if (s[0] == '0' || s == "off" || s == "OFF" || s == "false") { return 0; } - return 0; + return -1; } +// number to string std::string to_string(double d) { if (d == static_cast(d)) { return std::to_string(static_cast(d)); @@ -373,8 +377,9 @@ std::string to_string(double d) { return std::to_string(d); } +// RPN calculator std::string compute(const std::string & expr) { - auto expr_new = expr; //emsesp::Helpers::toLower(expr); + auto expr_new = emsesp::Helpers::toLower(expr); // emsesp::EMSESP::logger().info("calculate: %s", expr_new.c_str()); commands(expr_new); // emsesp::EMSESP::logger().info("calculate: %s", expr_new.c_str()); @@ -413,6 +418,9 @@ std::string compute(const std::string & expr) { stack.push_back(to_string(-1 * std::stod(rhs))); break; case '!': + if (to_logic(rhs) < 0) { + return ""; + } stack.push_back(to_logic(rhs) == 0 ? "1" : "0"); break; } @@ -483,6 +491,9 @@ std::string compute(const std::string & expr) { stack.pop_back(); const auto lhs = to_logic(stack.back()); stack.pop_back(); + if (rhs < 0 || lhs < 0) { + return ""; + } switch (token.str[0]) { default: return ""; From 920f24b45a9f095dff01c6d363c91b9628cd1234 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 21 Jun 2024 12:19:57 +0200 Subject: [PATCH 3/5] add mock api example --- mock-api/rest_server.ts | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts index 5f1ceb9d3..22dad9910 100644 --- a/mock-api/rest_server.ts +++ b/mock-api/rest_server.ts @@ -2087,6 +2087,19 @@ let emsesp_customentities = { value_type: 0, writeable: false, value: 0 + }, + { + id: 2, + ram: 1, + device_id: 0, + type_id: 0, + offset: 0, + factor: 1, + name: 'setpoint', + uom: 1, + value_type: 0, + writeable: true, + value: 21 } ] }; @@ -2129,11 +2142,29 @@ let emsesp_schedule = { cmd: 'system/restart', value: '', name: 'auto_restart' + }, + { + id: 5, + active: false, + flags: 130, + time: 'system/network info/rssi < -70', + cmd: 'system/restart', + value: '', + name: 'bad_wifi' + }, + { + id: 6, + active: false, + flags: 129, + time: 'boiler/outdoortemp', + cmd: 'boiler/selflowtemp', + value: '(custom/setpoint - boiler/outdoortemp) * 2.8 + 3', + name: 'heatingcurve' } ] }; -// SCHEDULE +// MODULES let emsesp_modules = { // 'modules': [] "modules": [ From a8adc26fc47ed06f06f9caaf9acac57b17e3ad40 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 21 Jun 2024 12:20:27 +0200 Subject: [PATCH 4/5] add unused cases for sonar scanner --- src/web/shuntingYard.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/web/shuntingYard.hpp b/src/web/shuntingYard.hpp index 0db7cf897..62bf3ca60 100644 --- a/src/web/shuntingYard.hpp +++ b/src/web/shuntingYard.hpp @@ -283,8 +283,10 @@ std::deque shuntingYard(const std::deque & tokens) { stack.pop_back(); } break; + case Token::Type::Unknown: default: return {}; + break; } } @@ -424,7 +426,6 @@ std::string compute(const std::string & expr) { stack.push_back(to_logic(rhs) == 0 ? "1" : "0"); break; } - } break; case Token::Type::Compare: { if (stack.size() < 2) { @@ -543,9 +544,12 @@ std::string compute(const std::string & expr) { break; } } break; - + case Token::Type::LeftParen: + case Token::Type::RightParen: + case Token::Type::Unknown: default: return ""; + break; } } return stack.back(); From 649734df43ea21a4553f4e2aa8845978c07e582b Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 21 Jun 2024 12:21:01 +0200 Subject: [PATCH 5/5] set default 00:00 for schedule time --- interface/src/project/Scheduler.tsx | 4 ++-- interface/src/project/SchedulerDialog.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/project/Scheduler.tsx b/interface/src/project/Scheduler.tsx index 1262f0030..7b76d2d03 100644 --- a/interface/src/project/Scheduler.tsx +++ b/interface/src/project/Scheduler.tsx @@ -227,7 +227,7 @@ const Scheduler: FC = () => { {flag === ScheduleFlag.SCHEDULE_TIMER ? LL.TIMER(0) : flag === ScheduleFlag.SCHEDULE_ONCHANGE - ? 'OnChange' + ? 'On Change' : flag === ScheduleFlag.SCHEDULE_CONDITION ? 'Condition' : dow[Math.log(flag) / Math.log(2)]} @@ -244,7 +244,7 @@ const Scheduler: FC = () => { data={{ nodes: schedule .filter((si) => !si.deleted) - .sort((a, b) => a.time.localeCompare(b.time)) + .sort((a, b) => a.cmd.localeCompare(b.cmd)) }} theme={schedule_theme} layout={{ custom: true }} diff --git a/interface/src/project/SchedulerDialog.tsx b/interface/src/project/SchedulerDialog.tsx index 324eba784..b629e99ce 100644 --- a/interface/src/project/SchedulerDialog.tsx +++ b/interface/src/project/SchedulerDialog.tsx @@ -297,7 +297,7 @@ const SchedulerDialog = ({ name="time" label={isCondition ? 'Condition' : 'On Change Value'} fullWidth - value={editItem.time} + value={editItem.time == "00:00" ? editItem.time = "" : editItem.time} margin="normal" onChange={updateFormValue} /> @@ -307,7 +307,7 @@ const SchedulerDialog = ({ name="time" type="time" label={isTimer ? LL.TIMER(1) : LL.TIME(1)} - value={editItem.time} + value={editItem.time == "" ? editItem.time = "00:00" : editItem.time} margin="normal" onChange={updateFormValue} />