Scheduler conditions: allow system

This commit is contained in:
MichaelDvP
2024-06-20 15:44:02 +02:00
parent 95d404551c
commit 6c111c7816
8 changed files with 86 additions and 29 deletions

View File

@@ -22,6 +22,7 @@
- timeout for remote thermostat emulation [#1680](https://github.com/emsesp/EMS-ESP32/discussions/1680), [#1774](https://github.com/emsesp/EMS-ESP32/issues/1774)
- CR120 thermostat as own model() [#1779](https://github.com/emsesp/EMS-ESP32/discussions/1779)
- Modules - external linkable module library [#1778](https://github.com/emsesp/EMS-ESP32/issues/1778)
- Scheduler onChange and Conditions [#1806](https://github.com/emsesp/EMS-ESP32/issues/1806)
## Fixed

View File

@@ -195,7 +195,7 @@ build_flags =
[env:espressi32_v3]
platform = espressif32
platform_packages=
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.0-rc2
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1
framework = arduino
board = esp32dev

View File

@@ -322,7 +322,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
if (single_command) {
// exception 1: anything that is from System
// exception 2: boiler coldshot command
bool get_attributes = (!cf || !cf->cmdfunction_json_) && (device_type > EMSdevice::DeviceType::SYSTEM) && (strcmp(cmd, F_(coldshot)) != 0);
bool get_attributes = (!cf || !cf->cmdfunction_json_) && (strcmp(cmd, F_(coldshot)) != 0);
if (get_attributes) {
LOG_DEBUG("Calling %s command '%s' to retrieve attributes", dname, cmd);

View File

@@ -771,6 +771,10 @@ bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8
return webCustomEntityService.get_value_info(root, cmd);
}
if (devicetype == DeviceType::SYSTEM) {
return system_.get_value_info(root, cmd);
}
char error[100];
snprintf(error, sizeof(error), "cannot find values for entity '%s'", cmd);
root["message"] = error;

View File

@@ -1272,6 +1272,44 @@ bool System::saveSettings(const char * filename, const char * section, JsonObjec
return false; // not found
}
bool System::get_value_info(JsonObject root, const char * command) {
if (command == nullptr || strlen(command) == 0) {
LOG_ERROR("empty system command");
return false;
}
char cmd[COMMAND_MAX_LENGTH];
strlcpy(cmd, command, sizeof(cmd));
char * val = strstr(cmd, "/value");
if (val) {
val[0] = '\0';
}
char * dash = strchr(cmd, '/');
if (dash) {
*dash = '\0';
dash++;
}
if (command_info("", 0, root)) {
std::string s;
if (dash && root[cmd].containsKey(dash)) {
s = root[cmd][dash].as<std::string>();
} else if (root.containsKey(cmd)) {
s = root[cmd].as<std::string>();
}
if (!s.empty()) {
root.clear();
if (val) {
root["api_data"] = s;
} else {
root["value"] = s;
}
return true;
}
}
root.clear();
LOG_ERROR("system command not found: %s from %s", cmd, command);
return false;
}
// export status information including the device information
// http://ems-esp/api/system/info
bool System::command_info(const char * value, const int8_t id, JsonObject output) {
@@ -1457,8 +1495,8 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["tx mode"] = settings.tx_mode;
node["ems bus id"] = settings.ems_bus_id;
node["shower timer"] = settings.shower_timer;
node["shower min duration"] = settings.shower_min_duration; // seconds
node["shower alert"] = settings.shower_alert;
node["shpwe_min_duration"] = settings.shower_min_duration; // seconds
if (settings.shower_alert) {
node["shower alert coldshot"] = settings.shower_alert_coldshot; // seconds
node["shower alert trigger"] = settings.shower_alert_trigger; // minutes
@@ -1503,7 +1541,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
obj["product id"] = emsdevice->product_id();
obj["version"] = emsdevice->version();
obj["entities"] = emsdevice->count_entities();
char result[300];
char result[500];
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::RECEIVED);
if (result[0] != '\0') {
obj["handlers received"] = result; // don't show handlers if there aren't any

View File

@@ -61,6 +61,7 @@ class System {
static bool command_commands(const char * value, const int8_t id, JsonObject output);
static bool command_response(const char * value, const int8_t id, JsonObject output);
static bool command_allvalues(const char * value, const int8_t id, JsonObject output);
static bool get_value_info(JsonObject root, const char *cmd);
#if defined(EMSESP_TEST)
static bool command_test(const char * value, const int8_t id);

View File

@@ -369,7 +369,9 @@ bool WebSchedulerService::command(const char * cmd, const char * data) {
bool WebSchedulerService::onChange(const char * cmd) {
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_ONCHANGE && Helpers::toLower(scheduleItem.time) == Helpers::toLower(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());
}
}
@@ -381,7 +383,7 @@ void WebSchedulerService::condition() {
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_CONDITION) {
auto match = compute(scheduleItem.time.c_str());
#ifdef EMESESP_DEBUG
emsesp::EMSESP::logger().debug("condition match: %s", match.c_str());
// emsesp::EMSESP::logger().debug("condition match: %s", match.c_str());
#endif
if (!match.empty() && match[0] == '1') {
if (scheduleItem.retry_cnt == 0xFF) { // default unswitched

View File

@@ -303,16 +303,27 @@ std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
return queue;
}
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;
}
return false;
}
// replace commands like "<device>/<hc>/<cmd>" with its value"
std::string commands(std::string & expr) {
for (uint8_t device = 0; device < emsesp::EMSdevice::DeviceType::UNKNOWN; device++) {
const char * d = emsesp::EMSdevice::device_type_2_device_name(device);
auto f = expr.find(d);
while (f != std::string::npos) {
auto e = expr.find_first_of(" )=<>|&+-*", f);
auto e = expr.find_first_of(")=<>|&+-*!", f);
if (e == std::string::npos) {
e = expr.length();
}
while (e > 0 && expr[e - 1] == ' ') { // remove blanks from end
e--;
}
char cmd[COMMAND_MAX_LENGTH];
size_t l = e - f;
if (l >= sizeof(cmd) - 1) {
@@ -330,8 +341,7 @@ std::string commands(std::string & expr) {
emsesp::Command::process(cmd_s.c_str(), true, input, output);
if (output.containsKey("api_data")) {
std::string data = output["api_data"].as<std::string>();
// set strings in quotations for something like "3-way-valve"
if (isdigit(data[0] == 0 && data[0] != '-')) {
if (!isnum(data)) {
data.insert(data.begin(), '"');
data.insert(data.end(), '"');
}
@@ -346,7 +356,7 @@ std::string commands(std::string & expr) {
return expr;
}
int islogic(const std::string & s) {
int to_logic(const std::string & s) {
if (s[0] == '1' || s == "on" || s == "ON" || s == "true") {
return 1;
}
@@ -356,15 +366,16 @@ int islogic(const std::string & s) {
return 0;
}
bool isnum(const std::string & s) {
if (isdigit(s[0]) || (s[0] == '-' && isdigit(s[1]))) {
return true;
std::string to_string(double d) {
if (d == static_cast<int>(d)) {
return std::to_string(static_cast<int>(d));
}
return false;
return std::to_string(d);
}
std::string compute(const std::string & expr) {
auto expr_new = emsesp::Helpers::toLower(expr);
auto expr_new = expr; //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());
const auto tokens = exprToTokens(expr_new);
@@ -399,10 +410,10 @@ std::string compute(const std::string & expr) {
if (!isnum(rhs)) {
return "";
}
stack.push_back(std::to_string(-1 * std::stod(rhs)));
stack.push_back(to_string(-1 * std::stod(rhs)));
break;
case '!':
stack.push_back(islogic(rhs) == 0 ? "1" : "0");
stack.push_back(to_logic(rhs) == 0 ? "1" : "0");
break;
}
@@ -468,9 +479,9 @@ std::string compute(const std::string & expr) {
if (stack.size() < 2) {
return "";
}
const auto rhs = islogic(stack.back());
const auto rhs = to_logic(stack.back());
stack.pop_back();
const auto lhs = islogic(stack.back());
const auto lhs = to_logic(stack.back());
stack.pop_back();
switch (token.str[0]) {
default:
@@ -502,22 +513,22 @@ std::string compute(const std::string & expr) {
return "";
break;
case '^':
stack.push_back(std::to_string(pow(lhs, rhs)));
stack.push_back(to_string(pow(lhs, rhs)));
break;
case '*':
stack.push_back(std::to_string(lhs * rhs));
stack.push_back(to_string(lhs * rhs));
break;
case '/':
stack.push_back(std::to_string(lhs / rhs));
stack.push_back(to_string(lhs / rhs));
break;
case '%':
stack.push_back(std::to_string(static_cast<int>(lhs) % static_cast<int>(rhs)));
break;
case '+':
stack.push_back(std::to_string(lhs + rhs));
stack.push_back(to_string(lhs + rhs));
break;
case '-':
stack.push_back(std::to_string(lhs - rhs));
stack.push_back(to_string(lhs - rhs));
break;
}
} break;