From 6c6b5b060d65f8c874b576b44f83fce9da3601fa Mon Sep 17 00:00:00 2001 From: wingphil Date: Thu, 6 Mar 2025 13:27:12 +0000 Subject: [PATCH 01/10] test: Copy shuntingYard tests so that we can assert the output --- test/test_api/test_api.cpp | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/test_api/test_api.cpp b/test/test_api/test_api.cpp index 8338af0c1..3eac21233 100644 --- a/test/test_api/test_api.cpp +++ b/test/test_api/test_api.cpp @@ -23,6 +23,8 @@ #include #include "ESPAsyncWebServer.h" #include "web/WebAPIService.h" +#include +#include "core/shuntingYard.hpp" using namespace emsesp; @@ -333,6 +335,55 @@ void run_console_tests() { RUN_TEST(console_test3); } +// test shunting yard +void shuntingYard_tests() { + std::string test_value, expected_result; + + expected_result = "locale is en"; + test_value = "\"locale is \"system/settings/locale"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + // test with negative value + expected_result = "rssi is -23"; + test_value = "\"rssi is \"0+system/network/rssi"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + expected_result = "rssi is -23 dbm"; + test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + expected_result = "14"; + test_value = "(custom/seltemp/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + expected_result = "seltemp=14"; + test_value = "\"seltemp=\"(custom/seltemp/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + expected_result = "14"; + test_value = "(custom/seltemp)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + // note: the following will fail unless test("boiler") is loaded before hand + + expected_result = "40"; + test_value = "boiler/flowtempoffset"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + expected_result = "40"; + test_value = "(boiler/flowtempoffset/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + expected_result = "53.8"; + test_value = "(boiler/storagetemp1/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + + // (14 - 40) * 2.8 + 5 = -67.8 + expected_result = "-67.8"; + test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + // auto-generate the tests void create_tests() { // These match the calls in test_api.h @@ -455,6 +506,7 @@ int main() { run_tests(); // execute the generated tests run_manual_tests(); // execute some other manual tests from this file run_console_tests(); // execute some console tests + RUN_TEST(shuntingYard_tests); // execute the shuntingYard tests return UNITY_END(); } From 4474868afca85a6eb8d2c7772bfefbb43a7c3ab5 Mon Sep 17 00:00:00 2001 From: wingphil Date: Thu, 6 Mar 2025 13:59:20 +0000 Subject: [PATCH 02/10] Add tests for inline conditionals, try to capture current behaviour --- test/test_api/test_api.cpp | 136 +++++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 27 deletions(-) diff --git a/test/test_api/test_api.cpp b/test/test_api/test_api.cpp index 3eac21233..a1e718ea3 100644 --- a/test/test_api/test_api.cpp +++ b/test/test_api/test_api.cpp @@ -335,55 +335,137 @@ void run_console_tests() { RUN_TEST(console_test3); } -// test shunting yard -void shuntingYard_tests() { - std::string test_value, expected_result; - - expected_result = "locale is en"; - test_value = "\"locale is \"system/settings/locale"; +void shuntingYard_test1() { + std::string expected_result = "locale is en"; + std::string test_value = "\"locale is \"system/settings/locale"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} +void shuntingYard_test2() { // test with negative value - expected_result = "rssi is -23"; - test_value = "\"rssi is \"0+system/network/rssi"; + std::string expected_result = "rssi is -23"; + std::string test_value = "\"rssi is \"0+system/network/rssi"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - expected_result = "rssi is -23 dbm"; - test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; +void shuntingYard_test3() { + std::string expected_result = "rssi is -23 dbm"; + std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - expected_result = "14"; - test_value = "(custom/seltemp/value)"; +void shuntingYard_test4() { + std::string expected_result = "14"; + std::string test_value = "(custom/seltemp/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - expected_result = "seltemp=14"; - test_value = "\"seltemp=\"(custom/seltemp/value)"; +void shuntingYard_test5() { + std::string expected_result = "seltemp=14"; + std::string test_value = "\"seltemp=\"(custom/seltemp/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - expected_result = "14"; - test_value = "(custom/seltemp)"; +void shuntingYard_test6() { + std::string expected_result = "14"; + std::string test_value = "(custom/seltemp)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - // note: the following will fail unless test("boiler") is loaded before hand - - expected_result = "40"; - test_value = "boiler/flowtempoffset"; +void shuntingYard_test7() { + std::string expected_result = "40"; + std::string test_value = "boiler/flowtempoffset"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - expected_result = "40"; - test_value = "(boiler/flowtempoffset/value)"; +void shuntingYard_test8() { + std::string expected_result = "40"; + std::string test_value = "(boiler/flowtempoffset/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} - expected_result = "53.8"; - test_value = "(boiler/storagetemp1/value)"; +void shuntingYard_test9() { + std::string expected_result = "53.8"; + std::string test_value = "(boiler/storagetemp1/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} +void shuntingYard_test10() { // (14 - 40) * 2.8 + 5 = -67.8 - expected_result = "-67.8"; - test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; + std::string expected_result = "-67.8"; + std::string test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } +void shuntingYard_test11() { + std::string expected_result = "4"; + std::string test_value = "1 > 2 ? 3 : 4"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test12() { + std::string expected_result = "3"; + std::string test_value = "1 < 2 ? 3 : 4"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test13() { + std::string expected_result = "5"; + std::string test_value = "1 < 2 ? (3 < 4 ? 5 : 6) : 7"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test14() { + std::string expected_result = "6"; + std::string test_value = "1 < 2 ? (3 > 4 ? 5 : 6) : 7"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test15() { + std::string expected_result = "3"; + std::string test_value = "1 < 2 ? 3 : (4 < 5 ? 6 : 7)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test16() { + std::string expected_result = "6"; + std::string test_value = "1 > 2 ? 3 : (4 < 5 ? 6 : 7)"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test17() { + std::string expected_result = "7"; + std::string test_value = "1 > 2 ? 3 : (4 > 5 ? 6 : 7)"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test18() { + std::string expected_result = "44"; + std::string test_value = "(1 > 2 ? 3 : 4) + (10 > 20 ? 30 : 40)"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void run_shuntingYard_tests() { + RUN_TEST(shuntingYard_test1); + RUN_TEST(shuntingYard_test2); + RUN_TEST(shuntingYard_test3); + RUN_TEST(shuntingYard_test4); + RUN_TEST(shuntingYard_test5); + RUN_TEST(shuntingYard_test6); + RUN_TEST(shuntingYard_test7); + RUN_TEST(shuntingYard_test8); + RUN_TEST(shuntingYard_test9); + RUN_TEST(shuntingYard_test10); + RUN_TEST(shuntingYard_test11); + RUN_TEST(shuntingYard_test12); + RUN_TEST(shuntingYard_test13); + RUN_TEST(shuntingYard_test14); + RUN_TEST(shuntingYard_test15); + RUN_TEST(shuntingYard_test16); + RUN_TEST(shuntingYard_test17); + RUN_TEST(shuntingYard_test18); +} + // auto-generate the tests void create_tests() { // These match the calls in test_api.h @@ -506,7 +588,7 @@ int main() { run_tests(); // execute the generated tests run_manual_tests(); // execute some other manual tests from this file run_console_tests(); // execute some console tests - RUN_TEST(shuntingYard_tests); // execute the shuntingYard tests + run_shuntingYard_tests(); // execute the shuntingYard tests return UNITY_END(); } From 45eca462e74d7130c668c37462e80680af1264f2 Mon Sep 17 00:00:00 2001 From: wingphil Date: Thu, 6 Mar 2025 14:07:51 +0000 Subject: [PATCH 03/10] test: Move shuntingYard tests to new file --- test/test_api/test_api.cpp | 134 +-------------------------- test/test_api/test_shuntingYard.hpp | 136 ++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 133 deletions(-) create mode 100644 test/test_api/test_shuntingYard.hpp diff --git a/test/test_api/test_api.cpp b/test/test_api/test_api.cpp index a1e718ea3..987d26325 100644 --- a/test/test_api/test_api.cpp +++ b/test/test_api/test_api.cpp @@ -23,8 +23,7 @@ #include #include "ESPAsyncWebServer.h" #include "web/WebAPIService.h" -#include -#include "core/shuntingYard.hpp" +#include "test_shuntingYard.hpp" using namespace emsesp; @@ -335,137 +334,6 @@ void run_console_tests() { RUN_TEST(console_test3); } -void shuntingYard_test1() { - std::string expected_result = "locale is en"; - std::string test_value = "\"locale is \"system/settings/locale"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test2() { - // test with negative value - std::string expected_result = "rssi is -23"; - std::string test_value = "\"rssi is \"0+system/network/rssi"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test3() { - std::string expected_result = "rssi is -23 dbm"; - std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test4() { - std::string expected_result = "14"; - std::string test_value = "(custom/seltemp/value)"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test5() { - std::string expected_result = "seltemp=14"; - std::string test_value = "\"seltemp=\"(custom/seltemp/value)"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test6() { - std::string expected_result = "14"; - std::string test_value = "(custom/seltemp)"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test7() { - std::string expected_result = "40"; - std::string test_value = "boiler/flowtempoffset"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test8() { - std::string expected_result = "40"; - std::string test_value = "(boiler/flowtempoffset/value)"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test9() { - std::string expected_result = "53.8"; - std::string test_value = "(boiler/storagetemp1/value)"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test10() { - // (14 - 40) * 2.8 + 5 = -67.8 - std::string expected_result = "-67.8"; - std::string test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test11() { - std::string expected_result = "4"; - std::string test_value = "1 > 2 ? 3 : 4"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test12() { - std::string expected_result = "3"; - std::string test_value = "1 < 2 ? 3 : 4"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test13() { - std::string expected_result = "5"; - std::string test_value = "1 < 2 ? (3 < 4 ? 5 : 6) : 7"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test14() { - std::string expected_result = "6"; - std::string test_value = "1 < 2 ? (3 > 4 ? 5 : 6) : 7"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test15() { - std::string expected_result = "3"; - std::string test_value = "1 < 2 ? 3 : (4 < 5 ? 6 : 7)"; - TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test16() { - std::string expected_result = "6"; - std::string test_value = "1 > 2 ? 3 : (4 < 5 ? 6 : 7)"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test17() { - std::string expected_result = "7"; - std::string test_value = "1 > 2 ? 3 : (4 > 5 ? 6 : 7)"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void shuntingYard_test18() { - std::string expected_result = "44"; - std::string test_value = "(1 > 2 ? 3 : 4) + (10 > 20 ? 30 : 40)"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); -} - -void run_shuntingYard_tests() { - RUN_TEST(shuntingYard_test1); - RUN_TEST(shuntingYard_test2); - RUN_TEST(shuntingYard_test3); - RUN_TEST(shuntingYard_test4); - RUN_TEST(shuntingYard_test5); - RUN_TEST(shuntingYard_test6); - RUN_TEST(shuntingYard_test7); - RUN_TEST(shuntingYard_test8); - RUN_TEST(shuntingYard_test9); - RUN_TEST(shuntingYard_test10); - RUN_TEST(shuntingYard_test11); - RUN_TEST(shuntingYard_test12); - RUN_TEST(shuntingYard_test13); - RUN_TEST(shuntingYard_test14); - RUN_TEST(shuntingYard_test15); - RUN_TEST(shuntingYard_test16); - RUN_TEST(shuntingYard_test17); - RUN_TEST(shuntingYard_test18); -} - // auto-generate the tests void create_tests() { // These match the calls in test_api.h diff --git a/test/test_api/test_shuntingYard.hpp b/test/test_api/test_shuntingYard.hpp new file mode 100644 index 000000000..e3a5fa5ef --- /dev/null +++ b/test/test_api/test_shuntingYard.hpp @@ -0,0 +1,136 @@ + +#include +#include +#include +#include "core/shuntingYard.hpp" + +void shuntingYard_test1() { + std::string expected_result = "locale is en"; + std::string test_value = "\"locale is \"system/settings/locale"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test2() { + // test with negative value + std::string expected_result = "rssi is -23"; + std::string test_value = "\"rssi is \"0+system/network/rssi"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test3() { + std::string expected_result = "rssi is -23 dbm"; + std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test4() { + std::string expected_result = "14"; + std::string test_value = "(custom/seltemp/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test5() { + std::string expected_result = "seltemp=14"; + std::string test_value = "\"seltemp=\"(custom/seltemp/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test6() { + std::string expected_result = "14"; + std::string test_value = "(custom/seltemp)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test7() { + std::string expected_result = "40"; + std::string test_value = "boiler/flowtempoffset"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test8() { + std::string expected_result = "40"; + std::string test_value = "(boiler/flowtempoffset/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test9() { + std::string expected_result = "53.8"; + std::string test_value = "(boiler/storagetemp1/value)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test10() { + // (14 - 40) * 2.8 + 5 = -67.8 + std::string expected_result = "-67.8"; + std::string test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test11() { + std::string expected_result = "4"; + std::string test_value = "1 > 2 ? 3 : 4"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test12() { + std::string expected_result = "3"; + std::string test_value = "1 < 2 ? 3 : 4"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test13() { + std::string expected_result = "5"; + std::string test_value = "1 < 2 ? (3 < 4 ? 5 : 6) : 7"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test14() { + std::string expected_result = "6"; + std::string test_value = "1 < 2 ? (3 > 4 ? 5 : 6) : 7"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test15() { + std::string expected_result = "3"; + std::string test_value = "1 < 2 ? 3 : (4 < 5 ? 6 : 7)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test16() { + std::string expected_result = "6"; + std::string test_value = "1 > 2 ? 3 : (4 < 5 ? 6 : 7)"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test17() { + std::string expected_result = "7"; + std::string test_value = "1 > 2 ? 3 : (4 > 5 ? 6 : 7)"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test18() { + std::string expected_result = "44"; + std::string test_value = "(1 > 2 ? 3 : 4) + (10 > 20 ? 30 : 40)"; + //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void run_shuntingYard_tests() { + RUN_TEST(shuntingYard_test1); + RUN_TEST(shuntingYard_test2); + RUN_TEST(shuntingYard_test3); + RUN_TEST(shuntingYard_test4); + RUN_TEST(shuntingYard_test5); + RUN_TEST(shuntingYard_test6); + RUN_TEST(shuntingYard_test7); + RUN_TEST(shuntingYard_test8); + RUN_TEST(shuntingYard_test9); + RUN_TEST(shuntingYard_test10); + RUN_TEST(shuntingYard_test11); + RUN_TEST(shuntingYard_test12); + RUN_TEST(shuntingYard_test13); + RUN_TEST(shuntingYard_test14); + RUN_TEST(shuntingYard_test15); + RUN_TEST(shuntingYard_test16); + RUN_TEST(shuntingYard_test17); + RUN_TEST(shuntingYard_test18); +} From 9874ecde820c998d57d5bd3d0d105540fbc7b30c Mon Sep 17 00:00:00 2001 From: wingphil Date: Fri, 7 Mar 2025 16:53:41 +0000 Subject: [PATCH 04/10] feat: Support nested conditionals in shuntingYard --- src/core/shuntingYard.hpp | 59 +++++++++++++++++++++-------- test/test_api/test_shuntingYard.hpp | 37 ++++++++++-------- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/core/shuntingYard.hpp b/src/core/shuntingYard.hpp index 3448fa1fc..932b21294 100644 --- a/src/core/shuntingYard.hpp +++ b/src/core/shuntingYard.hpp @@ -626,6 +626,27 @@ std::string calculate(const std::string & expr) { return result; } +void skipBrackets(const std::string & expr, size_t & pos, const int direction) { + int i = pos; + char open = '(', close = ')'; + if (direction == -1) { + open = ')'; close = '('; + } + size_t depth = 1; + while ((i >= 0) && (i < expr.size()) && depth > 0) { + if (expr[i] == open) depth++; + if (expr[i] == close) depth--; + i += direction; + } + if (depth > 0) { + // We didn't find the close bracket so return the end of the string + pos = direction == 1 ? expr.size() : 0; + } else { + pos = i - direction * 2; + } + +} + // check for multiple instances of ? : std::string compute(const std::string & expr) { auto expr_new = emsesp::Helpers::toLower(expr); @@ -681,31 +702,37 @@ std::string compute(const std::string & expr) { f = expr_new.find_first_of('{', e); } - // positions: q-questionmark, c-colon - auto q = expr_new.find_first_of('?'); - while (q != std::string::npos) { - // find corresponding colon - auto c1 = expr_new.find_first_of(':', q + 1); - auto q1 = expr_new.find_first_of('?', q + 1); - while (q1 < c1 && q1 != std::string::npos && c1 != std::string::npos) { - q1 = expr_new.find_first_of('?', q1 + 1); - c1 = expr_new.find_first_of(':', c1 + 1); - } - if (c1 == std::string::npos) { + // ? : + // We don't allow nested conditionals in but we do in or + // Therefore if you nest a conditional, it's ? is always to the right of the parent one + // Therefore the rightmost ? never has nested conditionals + // We will also insist that a nested conditional is surrounded by brackets + // positions: s-start, q-questionmark, c-colon, e:end + size_t q; + while ((q = expr_new.find_last_of('?')) != std::string::npos) { + // find corresponding colon, remember we know there's no nested ones to worry about + size_t c = expr_new.find_first_of(':', q); + if (c == std::string::npos) { return ""; // error: missing colon } - std::string cond = calculate(expr_new.substr(0, q)); + // Find the start of + size_t s = q - 1; + skipBrackets(expr, s, -1); + // Find the end of + size_t e = c + 1; + skipBrackets(expr, e, +1); + + std::string cond = calculate(expr_new.substr(s, q - s)); if (cond.length() == 0) { return ""; } else if (cond[0] == '1') { - expr_new.erase(c1); // remove second expression after colon - expr_new.erase(0, q + 1); // remove condition before questionmark + expr_new.erase(c, e + 1 - c); // remove second expression after colon + expr_new.erase(s, q + 1 - s); // remove condition before questionmark } else if (cond[0] == '0') { - expr_new.erase(0, c1 + 1); // remove condition and first expression + expr_new.erase(s, c + 1 - s); // remove condition and first expression } else { return ""; // error } - q = expr_new.find_first_of('?'); // search next instance } return calculate(expr_new); diff --git a/test/test_api/test_shuntingYard.hpp b/test/test_api/test_shuntingYard.hpp index e3a5fa5ef..7c44de813 100644 --- a/test/test_api/test_shuntingYard.hpp +++ b/test/test_api/test_shuntingYard.hpp @@ -80,38 +80,44 @@ void shuntingYard_test12() { void shuntingYard_test13() { std::string expected_result = "5"; - std::string test_value = "1 < 2 ? (3 < 4 ? 5 : 6) : 7"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + std::string test_value = "1<2?(3<4?5:6):7"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test14() { - std::string expected_result = "6"; - std::string test_value = "1 < 2 ? (3 > 4 ? 5 : 6) : 7"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + std::string expected_result = "7"; + std::string test_value = "1>2?(3<4?5:6):7"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test15() { - std::string expected_result = "3"; - std::string test_value = "1 < 2 ? 3 : (4 < 5 ? 6 : 7)"; + std::string expected_result = "6"; + std::string test_value = "1<2?(3>4?5:6):7"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test16() { - std::string expected_result = "6"; - std::string test_value = "1 > 2 ? 3 : (4 < 5 ? 6 : 7)"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + std::string expected_result = "3"; + std::string test_value = "1<2?3:(4<5?6:7)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test17() { - std::string expected_result = "7"; - std::string test_value = "1 > 2 ? 3 : (4 > 5 ? 6 : 7)"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + std::string expected_result = "6"; + std::string test_value = "1>2?3:(4<5?6:7)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test18() { + std::string expected_result = "7"; + std::string test_value = "1>2?3:(4>5?6:7)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + +void shuntingYard_test19() { std::string expected_result = "44"; - std::string test_value = "(1 > 2 ? 3 : 4) + (10 > 20 ? 30 : 40)"; - //TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); + std::string test_value = "(1>2?3:4)+(10>20?30:40)"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void run_shuntingYard_tests() { @@ -133,4 +139,5 @@ void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test16); RUN_TEST(shuntingYard_test17); RUN_TEST(shuntingYard_test18); + RUN_TEST(shuntingYard_test19); } From a6f77250b5de39f1b7304de0c884aefb35fbe9d2 Mon Sep 17 00:00:00 2001 From: wingphil Date: Fri, 7 Mar 2025 17:08:05 +0000 Subject: [PATCH 05/10] Format with clang --- src/core/shuntingYard.hpp | 14 ++++++----- test/test_api/test_api.cpp | 6 ++--- test/test_api/test_shuntingYard.hpp | 38 ++++++++++++++--------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/core/shuntingYard.hpp b/src/core/shuntingYard.hpp index 932b21294..030c52734 100644 --- a/src/core/shuntingYard.hpp +++ b/src/core/shuntingYard.hpp @@ -627,15 +627,18 @@ std::string calculate(const std::string & expr) { } void skipBrackets(const std::string & expr, size_t & pos, const int direction) { - int i = pos; + int i = pos; char open = '(', close = ')'; if (direction == -1) { - open = ')'; close = '('; + open = ')'; + close = '('; } size_t depth = 1; while ((i >= 0) && (i < expr.size()) && depth > 0) { - if (expr[i] == open) depth++; - if (expr[i] == close) depth--; + if (expr[i] == open) + depth++; + if (expr[i] == close) + depth--; i += direction; } if (depth > 0) { @@ -644,7 +647,6 @@ void skipBrackets(const std::string & expr, size_t & pos, const int direction) { } else { pos = i - direction * 2; } - } // check for multiple instances of ? : @@ -726,7 +728,7 @@ std::string compute(const std::string & expr) { if (cond.length() == 0) { return ""; } else if (cond[0] == '1') { - expr_new.erase(c, e + 1 - c); // remove second expression after colon + expr_new.erase(c, e + 1 - c); // remove second expression after colon expr_new.erase(s, q + 1 - s); // remove condition before questionmark } else if (cond[0] == '0') { expr_new.erase(s, c + 1 - s); // remove condition and first expression diff --git a/test/test_api/test_api.cpp b/test/test_api/test_api.cpp index 987d26325..ff7b15ee0 100644 --- a/test/test_api/test_api.cpp +++ b/test/test_api/test_api.cpp @@ -453,9 +453,9 @@ int main() { // UNITY_BEGIN(); - run_tests(); // execute the generated tests - run_manual_tests(); // execute some other manual tests from this file - run_console_tests(); // execute some console tests + run_tests(); // execute the generated tests + run_manual_tests(); // execute some other manual tests from this file + run_console_tests(); // execute some console tests run_shuntingYard_tests(); // execute the shuntingYard tests return UNITY_END(); diff --git a/test/test_api/test_shuntingYard.hpp b/test/test_api/test_shuntingYard.hpp index 7c44de813..31fb0c93b 100644 --- a/test/test_api/test_shuntingYard.hpp +++ b/test/test_api/test_shuntingYard.hpp @@ -6,117 +6,117 @@ void shuntingYard_test1() { std::string expected_result = "locale is en"; - std::string test_value = "\"locale is \"system/settings/locale"; + std::string test_value = "\"locale is \"system/settings/locale"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test2() { // test with negative value std::string expected_result = "rssi is -23"; - std::string test_value = "\"rssi is \"0+system/network/rssi"; + std::string test_value = "\"rssi is \"0+system/network/rssi"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test3() { std::string expected_result = "rssi is -23 dbm"; - std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; + std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test4() { std::string expected_result = "14"; - std::string test_value = "(custom/seltemp/value)"; + std::string test_value = "(custom/seltemp/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test5() { std::string expected_result = "seltemp=14"; - std::string test_value = "\"seltemp=\"(custom/seltemp/value)"; + std::string test_value = "\"seltemp=\"(custom/seltemp/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test6() { std::string expected_result = "14"; - std::string test_value = "(custom/seltemp)"; + std::string test_value = "(custom/seltemp)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test7() { std::string expected_result = "40"; - std::string test_value = "boiler/flowtempoffset"; + std::string test_value = "boiler/flowtempoffset"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test8() { std::string expected_result = "40"; - std::string test_value = "(boiler/flowtempoffset/value)"; + std::string test_value = "(boiler/flowtempoffset/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test9() { std::string expected_result = "53.8"; - std::string test_value = "(boiler/storagetemp1/value)"; + std::string test_value = "(boiler/storagetemp1/value)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test10() { // (14 - 40) * 2.8 + 5 = -67.8 std::string expected_result = "-67.8"; - std::string test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; + std::string test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test11() { std::string expected_result = "4"; - std::string test_value = "1 > 2 ? 3 : 4"; + std::string test_value = "1 > 2 ? 3 : 4"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test12() { std::string expected_result = "3"; - std::string test_value = "1 < 2 ? 3 : 4"; + std::string test_value = "1 < 2 ? 3 : 4"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test13() { std::string expected_result = "5"; - std::string test_value = "1<2?(3<4?5:6):7"; + std::string test_value = "1<2?(3<4?5:6):7"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test14() { std::string expected_result = "7"; - std::string test_value = "1>2?(3<4?5:6):7"; + std::string test_value = "1>2?(3<4?5:6):7"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test15() { std::string expected_result = "6"; - std::string test_value = "1<2?(3>4?5:6):7"; + std::string test_value = "1<2?(3>4?5:6):7"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test16() { std::string expected_result = "3"; - std::string test_value = "1<2?3:(4<5?6:7)"; + std::string test_value = "1<2?3:(4<5?6:7)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test17() { std::string expected_result = "6"; - std::string test_value = "1>2?3:(4<5?6:7)"; + std::string test_value = "1>2?3:(4<5?6:7)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test18() { std::string expected_result = "7"; - std::string test_value = "1>2?3:(4>5?6:7)"; + std::string test_value = "1>2?3:(4>5?6:7)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } void shuntingYard_test19() { std::string expected_result = "44"; - std::string test_value = "(1>2?3:4)+(10>20?30:40)"; + std::string test_value = "(1>2?3:4)+(10>20?30:40)"; TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } From 72feefe709283fe2bc347997462c33ff53b5fb54 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sat, 8 Mar 2025 16:50:12 +0100 Subject: [PATCH 06/10] allow brackets in conditions (idea from philwingfield) --- src/core/shuntingYard.hpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/core/shuntingYard.hpp b/src/core/shuntingYard.hpp index 3448fa1fc..b631d896b 100644 --- a/src/core/shuntingYard.hpp +++ b/src/core/shuntingYard.hpp @@ -93,6 +93,9 @@ std::deque exprToTokens(const std::string & expr) { } else if (strncmp(p, "pow", 3) == 0) { p += 2; tokens.emplace_back(Token::Type::Unary, "p", 5); + } else if (strncmp(p, "hex", 3) == 0) { + p += 2; + tokens.emplace_back(Token::Type::Unary, "h", 5); } else if (*p >= 'a' && *p <= 'z') { const auto * b = p; while ((*p >= 'a' && *p <= 'z') || (*p == '_')) { @@ -490,6 +493,9 @@ std::string calculate(const std::string & expr) { case 'p': stack.push_back(to_string(std::pow(std::stod(rhs), 2))); break; + case 'h': + stack.push_back(to_string(std::stoi(rhs, 0, 16))); + break; } } break; case Token::Type::Compare: { @@ -694,14 +700,29 @@ std::string compute(const std::string & expr) { if (c1 == std::string::npos) { return ""; // error: missing colon } - std::string cond = calculate(expr_new.substr(0, q)); + auto s = q; // start search brackets + int br = 0; // count brackets + while (s > 0 && (br || expr_new[s - 1] != '(')) { + s--; + br += (expr_new[s] == '(' ? -1 : expr_new[s] == ')' ? 1 : 0); + } + std::string cond = calculate(expr_new.substr(s, q - s)); if (cond.length() == 0) { return ""; } else if (cond[0] == '1') { - expr_new.erase(c1); // remove second expression after colon - expr_new.erase(0, q + 1); // remove condition before questionmark + auto e = expr_new.length(); // end + if (s) { // there was a opening bracket, find the closing one + e = c1; + br = 0; + while (e < expr_new.length() && (br || expr_new[e + 1] != ')')) { + e++; + br += (expr_new[e] == ')' ? -1 : expr_new[e] == '(' ? 1 : 0); + } + } + expr_new.erase(c1, e + 1 - c1); // remove second expression after colon + expr_new.erase(s, q + 1 - s); // remove condition before questionmark } else if (cond[0] == '0') { - expr_new.erase(0, c1 + 1); // remove condition and first expression + expr_new.erase(s, c1 + 1 - s); // remove condition and first expression } else { return ""; // error } From d769999f1074a0369bd75fd00f7443121c9f2bbc Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 10 Mar 2025 08:00:50 +0100 Subject: [PATCH 07/10] shuntingYard get api-data lowerCase, fix #2457 --- src/core/shuntingYard.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/shuntingYard.hpp b/src/core/shuntingYard.hpp index b631d896b..8de767b0d 100644 --- a/src/core/shuntingYard.hpp +++ b/src/core/shuntingYard.hpp @@ -383,7 +383,7 @@ std::string commands(std::string & expr, bool quotes = true) { data.insert(data.begin(), '"'); data.insert(data.end(), '"'); } - expr.replace(f, l, data); + expr.replace(f, l, emsesp::Helpers::toLower(data)); e = f + data.length(); } else { return expr = ""; From 2e79c3a5c6565917c5b491af982c1dcc51850a97 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 10 Mar 2025 10:56:31 +0100 Subject: [PATCH 08/10] enlarge tx queue (overflow in #2455) --- src/core/telegram.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/telegram.h b/src/core/telegram.h index c8a0d472c..622b7a832 100644 --- a/src/core/telegram.h +++ b/src/core/telegram.h @@ -37,7 +37,7 @@ #define MAX_TX_TELEGRAMS 200 // size of Tx queue #else #define MAX_RX_TELEGRAMS 10 // size of Rx queue -#define MAX_TX_TELEGRAMS 100 // size of Tx queue +#define MAX_TX_TELEGRAMS 160 // size of Tx queue #endif // default values for null values From 7fd735f667a5bc3c191a557b0086abf426674d89 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 10 Mar 2025 11:02:03 +0100 Subject: [PATCH 09/10] rename remoteseltemp to cooltemp, fix #2456 --- docs/Modbus-Entity-Registers.md | 2 +- docs/dump_entities.csv | 12 ++++++------ src/core/locale_translations.h | 3 ++- src/core/modbus_entity_parameters.hpp | 2 +- src/devices/thermostat.cpp | 10 +++++----- src/devices/thermostat.h | 8 ++++---- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/Modbus-Entity-Registers.md b/docs/Modbus-Entity-Registers.md index 8df89219d..7f9d2108d 100644 --- a/docs/Modbus-Entity-Registers.md +++ b/docs/Modbus-Entity-Registers.md @@ -4849,7 +4849,7 @@ | hc1.controlmode | control mode | enum [optimized\|simple\|n/a\|room\|power] | | true | HC | 25 | 1 | 1 | | hc1.program | program | enum [prog 1\|prog 2] | | true | HC | 26 | 1 | 1 | | hc1.tempautotemp | temporary set temperature automode | int8 (>=-1<=30) | C | true | HC | 27 | 1 | 1/2 | -| hc1.remoteseltemp | temporary set temperature from remote | int8 (>=-1<=30) | C | true | HC | 28 | 1 | 1/2 | +| hc1.cooltemp | cooling temperature | int8 (>=-1<=30) | C | true | HC | 28 | 1 | 1/2 | | hc1.fastheatup | fast heatup | uint8 (>=0<=100) | % | true | HC | 29 | 1 | 1 | | hc1.switchonoptimization | switch-on optimization | boolean | | true | HC | 30 | 1 | 1 | | hc1.reducemode | reduce mode | enum [outdoor\|room\|reduce] | | true | HC | 31 | 1 | 1 | diff --git a/docs/dump_entities.csv b/docs/dump_entities.csv index 2f39ed05e..8fed5d32b 100644 --- a/docs/dump_entities.csv +++ b/docs/dump_entities.csv @@ -3412,7 +3412,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/ "UI800, BC400",thermostat,4,hc1.controlmode,control mode,enum [weather compensated\|outside basepoint\|n/a\|room\|power\|constant], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1 "UI800, BC400",thermostat,4,hc1.program,program,enum [prog 1\|prog 2], ,true,select.thermostat_hc1_program,select.thermostat_hc1_program,6,1,1,26,1 "UI800, BC400",thermostat,4,hc1.tempautotemp,temporary set temperature automode,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_automode,number.thermostat_hc1_tempautotemp,6,1,1/2,27,1 -"UI800, BC400",thermostat,4,hc1.remoteseltemp,temporary set temperature from remote,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_from_remote,number.thermostat_hc1_remoteseltemp,6,1,1/2,28,1 +"UI800, BC400",thermostat,4,hc1.cooltemp,cooling temperature,int8 (>=-1<=30),C,true,number.thermostat_hc1_cooling_temperature,number.thermostat_hc1_cooltemp,6,1,1/2,28,1 "UI800, BC400",thermostat,4,hc1.fastheatup,fast heatup,uint8 (>=0<=100),%,true,number.thermostat_hc1_fast_heatup,number.thermostat_hc1_fastheatup,6,1,1,29,1 "UI800, BC400",thermostat,4,hc1.switchonoptimization,switch-on optimization,boolean, ,true,switch.thermostat_hc1_switch-on_optimization,switch.thermostat_hc1_switchonoptimization,6,1,1,30,1 "UI800, BC400",thermostat,4,hc1.reducemode,reduce mode,enum [outdoor\|room\|reduce], ,true,select.thermostat_hc1_reduce_mode,select.thermostat_hc1_reducemode,6,1,1,31,1 @@ -3796,7 +3796,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/ "RC200, CW100, CR120, CR50",thermostat,157,hc1.controlmode,control mode,enum [optimized\|simple\|n/a\|room\|power], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1 "RC200, CW100, CR120, CR50",thermostat,157,hc1.program,program,enum [prog 1\|prog 2], ,true,select.thermostat_hc1_program,select.thermostat_hc1_program,6,1,1,26,1 "RC200, CW100, CR120, CR50",thermostat,157,hc1.tempautotemp,temporary set temperature automode,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_automode,number.thermostat_hc1_tempautotemp,6,1,1/2,27,1 -"RC200, CW100, CR120, CR50",thermostat,157,hc1.remoteseltemp,temporary set temperature from remote,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_from_remote,number.thermostat_hc1_remoteseltemp,6,1,1/2,28,1 +"RC200, CW100, CR120, CR50",thermostat,157,hc1.cooltemp,cooling temperature,int8 (>=-1<=30),C,true,number.thermostat_hc1_cooling_temperature,number.thermostat_hc1_cooltemp,6,1,1/2,28,1 "RC200, CW100, CR120, CR50",thermostat,157,hc1.fastheatup,fast heatup,uint8 (>=0<=100),%,true,number.thermostat_hc1_fast_heatup,number.thermostat_hc1_fastheatup,6,1,1,29,1 "RC200, CW100, CR120, CR50",thermostat,157,hc1.switchonoptimization,switch-on optimization,boolean, ,true,switch.thermostat_hc1_switch-on_optimization,switch.thermostat_hc1_switchonoptimization,6,1,1,30,1 "RC200, CW100, CR120, CR50",thermostat,157,hc1.reducemode,reduce mode,enum [outdoor\|room\|reduce], ,true,select.thermostat_hc1_reduce_mode,select.thermostat_hc1_reducemode,6,1,1,31,1 @@ -3884,7 +3884,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/ "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.controlmode,control mode,enum [weather compensated\|outside basepoint\|n/a\|room\|power\|constant], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1 "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.program,program,enum [prog 1\|prog 2], ,true,select.thermostat_hc1_program,select.thermostat_hc1_program,6,1,1,26,1 "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.tempautotemp,temporary set temperature automode,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_automode,number.thermostat_hc1_tempautotemp,6,1,1/2,27,1 -"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.remoteseltemp,temporary set temperature from remote,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_from_remote,number.thermostat_hc1_remoteseltemp,6,1,1/2,28,1 +"RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.cooltemp,cooling temperature,int8 (>=-1<=30),C,true,number.thermostat_hc1_cooling_temperature,number.thermostat_hc1_cooltemp,6,1,1/2,28,1 "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.fastheatup,fast heatup,uint8 (>=0<=100),%,true,number.thermostat_hc1_fast_heatup,number.thermostat_hc1_fastheatup,6,1,1,29,1 "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.switchonoptimization,switch-on optimization,boolean, ,true,switch.thermostat_hc1_switch-on_optimization,switch.thermostat_hc1_switchonoptimization,6,1,1,30,1 "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410",thermostat,158,hc1.reducemode,reduce mode,enum [outdoor\|room\|reduce], ,true,select.thermostat_hc1_reduce_mode,select.thermostat_hc1_reducemode,6,1,1,31,1 @@ -3975,7 +3975,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/ "RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.controlmode,control mode,enum [optimized\|simple\|n/a\|room\|power], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1 "RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.program,program,enum [prog 1\|prog 2], ,true,select.thermostat_hc1_program,select.thermostat_hc1_program,6,1,1,26,1 "RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.tempautotemp,temporary set temperature automode,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_automode,number.thermostat_hc1_tempautotemp,6,1,1/2,27,1 -"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.remoteseltemp,temporary set temperature from remote,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_from_remote,number.thermostat_hc1_remoteseltemp,6,1,1/2,28,1 +"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.cooltemp,cooling temperature,int8 (>=-1<=30),C,true,number.thermostat_hc1_cooling_temperature,number.thermostat_hc1_cooltemp,6,1,1/2,28,1 "RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.fastheatup,fast heatup,uint8 (>=0<=100),%,true,number.thermostat_hc1_fast_heatup,number.thermostat_hc1_fastheatup,6,1,1,29,1 "RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.switchonoptimization,switch-on optimization,boolean, ,true,switch.thermostat_hc1_switch-on_optimization,switch.thermostat_hc1_switchonoptimization,6,1,1,30,1 "RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.reducemode,reduce mode,enum [outdoor\|room\|reduce], ,true,select.thermostat_hc1_reduce_mode,select.thermostat_hc1_reducemode,6,1,1,31,1 @@ -4064,7 +4064,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/ "Rego 2000/3000",thermostat,172,hc1.controlmode,control mode,enum [weather compensated\|outside basepoint\|n/a\|room\|power\|constant], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1 "Rego 2000/3000",thermostat,172,hc1.program,program,enum [prog 1\|prog 2], ,true,select.thermostat_hc1_program,select.thermostat_hc1_program,6,1,1,26,1 "Rego 2000/3000",thermostat,172,hc1.tempautotemp,temporary set temperature automode,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_automode,number.thermostat_hc1_tempautotemp,6,1,1/2,27,1 -"Rego 2000/3000",thermostat,172,hc1.remoteseltemp,temporary set temperature from remote,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_from_remote,number.thermostat_hc1_remoteseltemp,6,1,1/2,28,1 +"Rego 2000/3000",thermostat,172,hc1.cooltemp,cooling temperature,int8 (>=-1<=30),C,true,number.thermostat_hc1_cooling_temperature,number.thermostat_hc1_cooltemp,6,1,1/2,28,1 "Rego 2000/3000",thermostat,172,hc1.fastheatup,fast heatup,uint8 (>=0<=100),%,true,number.thermostat_hc1_fast_heatup,number.thermostat_hc1_fastheatup,6,1,1,29,1 "Rego 2000/3000",thermostat,172,hc1.switchonoptimization,switch-on optimization,boolean, ,true,switch.thermostat_hc1_switch-on_optimization,switch.thermostat_hc1_switchonoptimization,6,1,1,30,1 "Rego 2000/3000",thermostat,172,hc1.reducemode,reduce mode,enum [outdoor\|room\|reduce], ,true,select.thermostat_hc1_reduce_mode,select.thermostat_hc1_reducemode,6,1,1,31,1 @@ -4181,7 +4181,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/ "Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.controlmode,control mode,enum [weather compensated\|outside basepoint\|n/a\|room\|power\|constant], ,true,select.thermostat_hc1_control_mode,select.thermostat_hc1_controlmode,6,1,1,25,1 "Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.program,program,enum [prog 1\|prog 2], ,true,select.thermostat_hc1_program,select.thermostat_hc1_program,6,1,1,26,1 "Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.tempautotemp,temporary set temperature automode,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_automode,number.thermostat_hc1_tempautotemp,6,1,1/2,27,1 -"Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.remoteseltemp,temporary set temperature from remote,int8 (>=-1<=30),C,true,number.thermostat_hc1_temporary_set_temperature_from_remote,number.thermostat_hc1_remoteseltemp,6,1,1/2,28,1 +"Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.cooltemp,cooling temperature,int8 (>=-1<=30),C,true,number.thermostat_hc1_cooling_temperature,number.thermostat_hc1_cooltemp,6,1,1/2,28,1 "Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.fastheatup,fast heatup,uint8 (>=0<=100),%,true,number.thermostat_hc1_fast_heatup,number.thermostat_hc1_fastheatup,6,1,1,29,1 "Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.switchonoptimization,switch-on optimization,boolean, ,true,switch.thermostat_hc1_switch-on_optimization,switch.thermostat_hc1_switchonoptimization,6,1,1,30,1 "Rego 3000, UI800, Logamatic BC400",thermostat,253,hc1.reducemode,reduce mode,enum [outdoor\|room\|reduce], ,true,select.thermostat_hc1_reduce_mode,select.thermostat_hc1_reducemode,6,1,1,31,1 diff --git a/src/core/locale_translations.h b/src/core/locale_translations.h index 5f795415d..fee95d4c2 100644 --- a/src/core/locale_translations.h +++ b/src/core/locale_translations.h @@ -711,7 +711,8 @@ MAKE_TRANSLATION(nighttemp2, "nighttemp", "night temperature T1", "Nachttemperat MAKE_TRANSLATION(ecotemp, "ecotemp", "eco temperature", "eco Temperatur", "Temperatuur eco", "Eko-temperatur", "temperatura w trybie eko", "øko temperatur", "température éco", "eko sıcaklık", "Temperatura eco", "eko teplota", "eko teplota") MAKE_TRANSLATION(manualtemp, "manualtemp", "manual temperature", "manuelle Temperatur", "Temperatuur handmatig", "Temperatur Manuell", "temperatura ustawiona ręcznie", "manuell temperatur", "température manuelle", "manuel sıcaklık", "temperatura manuale", "manuálna teplota", "manuální teplota") MAKE_TRANSLATION(tempautotemp, "tempautotemp", "temporary set temperature automode", "temporäre Solltemperatur Automatikmodus", "Streeftemperatuur automodus tijdelijk", "Temporär Aktivering av Auto-läge", "zadana temperatura w pomieszczeniu w trybie \"auto\" (tymczasowa)", "temporær valgt temp i automodus", "température temporaire mode automatique", "geçici ayarlı sıcaklık otomatik mod", "impostare temporaneamente temperatura automatica", "automatický režim dočasnej nastavenej teploty", "dočasné nastavení teploty v automatickém režimu") -MAKE_TRANSLATION(remoteseltemp, "remoteseltemp", "temporary set temperature from remote", "temporäre Solltemperatur Remote", "Temperatuur van afstandsbedieding", "Temperatur från fjärruppkoppling", "zadana zdalnie temperatura a pomieszczeniu (tymczasowa)", "temporær valgt temp fra fjernbetjening", "température temporaire depuis télécommande", "geçici ayarlı sıcaklık uzaktan", "Temperatura temporanea da remoto", "dočasne nastavená teplota z diaľkového ovládania", "dočasné nastavení teploty z dálkového ovladače") +MAKE_TRANSLATION(cooltemp, "cooltemp", "cooling temperature", "Kühltemperatur", "Temperatuur koelbedrijf", "Temperatur Kyla", "temperatura ciepła", "chłodzenie temperatur", "température kjøling", "soğuyor sıcaklık", "temperatura calore", "chladenie teplota", "chlazení teplota") +// MAKE_TRANSLATION(remoteseltemp, "remoteseltemp", "temporary set temperature from remote", "temporäre Solltemperatur Remote", "Temperatuur van afstandsbedieding", "Temperatur från fjärruppkoppling", "zadana zdalnie temperatura a pomieszczeniu (tymczasowa)", "temporær valgt temp fra fjernbetjening", "température temporaire depuis télécommande", "geçici ayarlı sıcaklık uzaktan", "Temperatura temporanea da remoto", "dočasne nastavená teplota z diaľkového ovládania", "dočasné nastavení teploty z dálkového ovladače") MAKE_TRANSLATION(comforttemp, "comforttemp", "comfort temperature", "Komforttemperatur", "Comforttemperatuur", "Komforttemperatur", "temperatura w trybie komfort", "komforttemperatur", "température confort", "konfor sıcaklığı", "temperatura comfort", "komfortná teplota", "komfortní teplota") MAKE_TRANSLATION(summertemp, "summertemp", "summer temperature", "Sommertemperatur", "Zomertemperatuur", "Sommartemperatur", "temperatura przełączania lato/zima", "Sommertemperatur", "température été", "yaz sıcaklığı", "temperatura estiva", "letná teplota", "letní teplota") MAKE_TRANSLATION(designtemp, "designtemp", "design temperature", "Auslegungstemperatur", "Ontwerptemperatuur", "Design-temperatur", "temperatura projektowa", "designtemperatur", "température conception", "özel sıcaklık", "temperatura predefinita", "návrhová teplota", "dimenzovaná teplota") diff --git a/src/core/modbus_entity_parameters.hpp b/src/core/modbus_entity_parameters.hpp index 26d16a6e7..4d074aa27 100644 --- a/src/core/modbus_entity_parameters.hpp +++ b/src/core/modbus_entity_parameters.hpp @@ -318,7 +318,7 @@ const std::initializer_list Modbus::modbus_register_ma REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(controlmode), 25, 1), // controlmode REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(program), 26, 1), // program REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(tempautotemp), 27, 1), // tempautotemp - REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(remoteseltemp), 28, 1), // remoteseltemp + REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(cooltemp), 28, 1), // cooltemp REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(fastheatup), 29, 1), // fastheatup REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(switchonoptimization), 30, 1), // switchonoptimization REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(reducemode), 31, 1), // reducemode diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 350b750ea..6137a1360 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1166,7 +1166,7 @@ void Thermostat::process_RC300Set(std::shared_ptr telegram) { has_enumupdate(telegram, hc->reducemode, 5, 1); // 1-outdoor temp threshold, 2-room temp threshold, 3-reduced mode has_update(telegram, hc->reducetemp, 9); has_update(telegram, hc->noreducetemp, 12); - has_update(telegram, hc->remoteseltemp, 17); // see https://github.com/emsesp/EMS-ESP32/issues/590 + has_update(telegram, hc->cooltemp, 17); // see https://github.com/emsesp/EMS-ESP32/issues/590 and 2456 has_enumupdate(telegram, hc->switchProgMode, 19, 1); // 1-level, 2-absolute has_update(telegram, hc->redThreshold, 20); has_update(telegram, hc->boost, 23); @@ -3827,7 +3827,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co factor = 1; // to write 0xFF } break; - case HeatingCircuit::Mode::REMOTESELTEMP: + case HeatingCircuit::Mode::COOLTEMP: offset = 0x11; break; case HeatingCircuit::Mode::COMFORT: @@ -4686,12 +4686,12 @@ void Thermostat::register_device_values_hc(std::shared_ptrremoteseltemp, + &hc->cooltemp, DeviceValueType::INT8, DeviceValueNumOp::DV_NUMOP_DIV2, - FL_(remoteseltemp), + FL_(cooltemp), DeviceValueUOM::DEGREES, - MAKE_CF_CB(set_remoteseltemp), + MAKE_CF_CB(set_cooltemp), -1, 30); register_device_value(tag, &hc->fastHeatup, DeviceValueType::UINT8, FL_(fastheatup), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_fastheatup)); diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index cba27470a..843872f52 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -39,7 +39,7 @@ class Thermostat : public EMSdevice { int16_t remotetemp; // for readback uint8_t remotehum; // for readback uint8_t tempautotemp; - int8_t remoteseltemp; + int8_t cooltemp; uint8_t mode; uint8_t mode_new; uint8_t modetype; @@ -159,7 +159,7 @@ class Thermostat : public EMSdevice { ON, DAYLOW, DAYMID, - REMOTESELTEMP, + COOLTEMP, COOLSTART, UNKNOWN @@ -571,8 +571,8 @@ class Thermostat : public EMSdevice { inline bool set_roominfluence(const char * value, const int8_t id) { return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true); } - inline bool set_remoteseltemp(const char * value, const int8_t id) { - return set_temperature_value(value, id, HeatingCircuit::Mode::REMOTESELTEMP); + inline bool set_cooltemp(const char * value, const int8_t id) { + return set_temperature_value(value, id, HeatingCircuit::Mode::COOLTEMP); } inline bool set_coolstart(const char * value, const int8_t id) { return set_temperature_value(value, id, HeatingCircuit::Mode::COOLSTART); From 9d1ee275331b36af265c814e4b5a03c16636eb89 Mon Sep 17 00:00:00 2001 From: wingphil Date: Mon, 10 Mar 2025 10:09:44 +0000 Subject: [PATCH 10/10] Add test for nested conditionals without brackets --- test/test_api/test_shuntingYard.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_api/test_shuntingYard.hpp b/test/test_api/test_shuntingYard.hpp index 31fb0c93b..d3e2b2c0e 100644 --- a/test/test_api/test_shuntingYard.hpp +++ b/test/test_api/test_shuntingYard.hpp @@ -120,6 +120,12 @@ void shuntingYard_test19() { TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); } +void shuntingYard_test20() { + std::string expected_result = "8"; + std::string test_value = "1<2 ? 3>4 ? 5 : 6<7 ? 8 : 9 : 10"; + TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str()); +} + void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test1); RUN_TEST(shuntingYard_test2); @@ -140,4 +146,5 @@ void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test17); RUN_TEST(shuntingYard_test18); RUN_TEST(shuntingYard_test19); + RUN_TEST(shuntingYard_test20); }