mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
Scheduler conditions: allow system
This commit is contained in:
@@ -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)
|
- 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)
|
- 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)
|
- 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
|
## Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ build_flags =
|
|||||||
[env:espressi32_v3]
|
[env:espressi32_v3]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
platform_packages=
|
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
|
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
|
|||||||
if (single_command) {
|
if (single_command) {
|
||||||
// exception 1: anything that is from System
|
// exception 1: anything that is from System
|
||||||
// exception 2: boiler coldshot command
|
// 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) {
|
if (get_attributes) {
|
||||||
LOG_DEBUG("Calling %s command '%s' to retrieve attributes", dname, cmd);
|
LOG_DEBUG("Calling %s command '%s' to retrieve attributes", dname, cmd);
|
||||||
|
|||||||
@@ -771,6 +771,10 @@ bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8
|
|||||||
return webCustomEntityService.get_value_info(root, cmd);
|
return webCustomEntityService.get_value_info(root, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (devicetype == DeviceType::SYSTEM) {
|
||||||
|
return system_.get_value_info(root, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
char error[100];
|
char error[100];
|
||||||
snprintf(error, sizeof(error), "cannot find values for entity '%s'", cmd);
|
snprintf(error, sizeof(error), "cannot find values for entity '%s'", cmd);
|
||||||
root["message"] = error;
|
root["message"] = error;
|
||||||
|
|||||||
@@ -1272,6 +1272,44 @@ bool System::saveSettings(const char * filename, const char * section, JsonObjec
|
|||||||
return false; // not found
|
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
|
// export status information including the device information
|
||||||
// http://ems-esp/api/system/info
|
// http://ems-esp/api/system/info
|
||||||
bool System::command_info(const char * value, const int8_t id, JsonObject output) {
|
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["tx mode"] = settings.tx_mode;
|
||||||
node["ems bus id"] = settings.ems_bus_id;
|
node["ems bus id"] = settings.ems_bus_id;
|
||||||
node["shower timer"] = settings.shower_timer;
|
node["shower timer"] = settings.shower_timer;
|
||||||
|
node["shower min duration"] = settings.shower_min_duration; // seconds
|
||||||
node["shower alert"] = settings.shower_alert;
|
node["shower alert"] = settings.shower_alert;
|
||||||
node["shpwe_min_duration"] = settings.shower_min_duration; // seconds
|
|
||||||
if (settings.shower_alert) {
|
if (settings.shower_alert) {
|
||||||
node["shower alert coldshot"] = settings.shower_alert_coldshot; // seconds
|
node["shower alert coldshot"] = settings.shower_alert_coldshot; // seconds
|
||||||
node["shower alert trigger"] = settings.shower_alert_trigger; // minutes
|
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["product id"] = emsdevice->product_id();
|
||||||
obj["version"] = emsdevice->version();
|
obj["version"] = emsdevice->version();
|
||||||
obj["entities"] = emsdevice->count_entities();
|
obj["entities"] = emsdevice->count_entities();
|
||||||
char result[300];
|
char result[500];
|
||||||
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::RECEIVED);
|
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::RECEIVED);
|
||||||
if (result[0] != '\0') {
|
if (result[0] != '\0') {
|
||||||
obj["handlers received"] = result; // don't show handlers if there aren't any
|
obj["handlers received"] = result; // don't show handlers if there aren't any
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class System {
|
|||||||
static bool command_commands(const char * value, const int8_t id, JsonObject output);
|
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_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 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)
|
#if defined(EMSESP_TEST)
|
||||||
static bool command_test(const char * value, const int8_t id);
|
static bool command_test(const char * value, const int8_t id);
|
||||||
|
|||||||
@@ -369,7 +369,9 @@ bool WebSchedulerService::command(const char * cmd, const char * data) {
|
|||||||
bool WebSchedulerService::onChange(const char * cmd) {
|
bool WebSchedulerService::onChange(const char * cmd) {
|
||||||
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
||||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_ONCHANGE && Helpers::toLower(scheduleItem.time) == Helpers::toLower(cmd)) {
|
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());
|
// 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()).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,7 +383,7 @@ void WebSchedulerService::condition() {
|
|||||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_CONDITION) {
|
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_CONDITION) {
|
||||||
auto match = compute(scheduleItem.time.c_str());
|
auto match = compute(scheduleItem.time.c_str());
|
||||||
#ifdef EMESESP_DEBUG
|
#ifdef EMESESP_DEBUG
|
||||||
emsesp::EMSESP::logger().debug("condition match: %s", match.c_str());
|
// emsesp::EMSESP::logger().debug("condition match: %s", match.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (!match.empty() && match[0] == '1') {
|
if (!match.empty() && match[0] == '1') {
|
||||||
if (scheduleItem.retry_cnt == 0xFF) { // default unswitched
|
if (scheduleItem.retry_cnt == 0xFF) { // default unswitched
|
||||||
|
|||||||
@@ -303,16 +303,27 @@ std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
|
|||||||
return queue;
|
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"
|
// replace commands like "<device>/<hc>/<cmd>" with its value"
|
||||||
std::string commands(std::string & expr) {
|
std::string commands(std::string & expr) {
|
||||||
for (uint8_t device = 0; device < emsesp::EMSdevice::DeviceType::UNKNOWN; device++) {
|
for (uint8_t device = 0; device < emsesp::EMSdevice::DeviceType::UNKNOWN; device++) {
|
||||||
const char * d = emsesp::EMSdevice::device_type_2_device_name(device);
|
const char * d = emsesp::EMSdevice::device_type_2_device_name(device);
|
||||||
auto f = expr.find(d);
|
auto f = expr.find(d);
|
||||||
while (f != std::string::npos) {
|
while (f != std::string::npos) {
|
||||||
auto e = expr.find_first_of(" )=<>|&+-*", f);
|
auto e = expr.find_first_of(")=<>|&+-*!", f);
|
||||||
if (e == std::string::npos) {
|
if (e == std::string::npos) {
|
||||||
e = expr.length();
|
e = expr.length();
|
||||||
}
|
}
|
||||||
|
while (e > 0 && expr[e - 1] == ' ') { // remove blanks from end
|
||||||
|
e--;
|
||||||
|
}
|
||||||
char cmd[COMMAND_MAX_LENGTH];
|
char cmd[COMMAND_MAX_LENGTH];
|
||||||
size_t l = e - f;
|
size_t l = e - f;
|
||||||
if (l >= sizeof(cmd) - 1) {
|
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);
|
emsesp::Command::process(cmd_s.c_str(), true, input, output);
|
||||||
if (output.containsKey("api_data")) {
|
if (output.containsKey("api_data")) {
|
||||||
std::string data = output["api_data"].as<std::string>();
|
std::string data = output["api_data"].as<std::string>();
|
||||||
// set strings in quotations for something like "3-way-valve"
|
if (!isnum(data)) {
|
||||||
if (isdigit(data[0] == 0 && data[0] != '-')) {
|
|
||||||
data.insert(data.begin(), '"');
|
data.insert(data.begin(), '"');
|
||||||
data.insert(data.end(), '"');
|
data.insert(data.end(), '"');
|
||||||
}
|
}
|
||||||
@@ -346,7 +356,7 @@ std::string commands(std::string & expr) {
|
|||||||
return 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") {
|
if (s[0] == '1' || s == "on" || s == "ON" || s == "true") {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -356,15 +366,16 @@ int islogic(const std::string & s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isnum(const std::string & s) {
|
std::string to_string(double d) {
|
||||||
if (isdigit(s[0]) || (s[0] == '-' && isdigit(s[1]))) {
|
if (d == static_cast<int>(d)) {
|
||||||
return true;
|
return std::to_string(static_cast<int>(d));
|
||||||
}
|
}
|
||||||
return false;
|
return std::to_string(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string compute(const std::string & expr) {
|
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);
|
commands(expr_new);
|
||||||
// emsesp::EMSESP::logger().info("calculate: %s", expr_new.c_str());
|
// emsesp::EMSESP::logger().info("calculate: %s", expr_new.c_str());
|
||||||
const auto tokens = exprToTokens(expr_new);
|
const auto tokens = exprToTokens(expr_new);
|
||||||
@@ -399,10 +410,10 @@ std::string compute(const std::string & expr) {
|
|||||||
if (!isnum(rhs)) {
|
if (!isnum(rhs)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
stack.push_back(std::to_string(-1 * std::stod(rhs)));
|
stack.push_back(to_string(-1 * std::stod(rhs)));
|
||||||
break;
|
break;
|
||||||
case '!':
|
case '!':
|
||||||
stack.push_back(islogic(rhs) == 0 ? "1" : "0");
|
stack.push_back(to_logic(rhs) == 0 ? "1" : "0");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,9 +479,9 @@ std::string compute(const std::string & expr) {
|
|||||||
if (stack.size() < 2) {
|
if (stack.size() < 2) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const auto rhs = islogic(stack.back());
|
const auto rhs = to_logic(stack.back());
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
const auto lhs = islogic(stack.back());
|
const auto lhs = to_logic(stack.back());
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
switch (token.str[0]) {
|
switch (token.str[0]) {
|
||||||
default:
|
default:
|
||||||
@@ -502,22 +513,22 @@ std::string compute(const std::string & expr) {
|
|||||||
return "";
|
return "";
|
||||||
break;
|
break;
|
||||||
case '^':
|
case '^':
|
||||||
stack.push_back(std::to_string(pow(lhs, rhs)));
|
stack.push_back(to_string(pow(lhs, rhs)));
|
||||||
break;
|
break;
|
||||||
case '*':
|
case '*':
|
||||||
stack.push_back(std::to_string(lhs * rhs));
|
stack.push_back(to_string(lhs * rhs));
|
||||||
break;
|
break;
|
||||||
case '/':
|
case '/':
|
||||||
stack.push_back(std::to_string(lhs / rhs));
|
stack.push_back(to_string(lhs / rhs));
|
||||||
break;
|
break;
|
||||||
case '%':
|
case '%':
|
||||||
stack.push_back(std::to_string(static_cast<int>(lhs) % static_cast<int>(rhs)));
|
stack.push_back(std::to_string(static_cast<int>(lhs) % static_cast<int>(rhs)));
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
stack.push_back(std::to_string(lhs + rhs));
|
stack.push_back(to_string(lhs + rhs));
|
||||||
break;
|
break;
|
||||||
case '-':
|
case '-':
|
||||||
stack.push_back(std::to_string(lhs - rhs));
|
stack.push_back(to_string(lhs - rhs));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
Reference in New Issue
Block a user