diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 6dcd6c7fa..48d17f1e2 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -51,9 +51,9 @@ void WebScheduler::read(WebScheduler & webScheduler, JsonObject root) { for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) { JsonObject si = schedule.add(); si["id"] = counter++; // id is only used to render the table and must be unique - si["active"] = scheduleItem.flags ? scheduleItem.active : false; si["flags"] = scheduleItem.flags; - si["time"] = scheduleItem.time; + si["active"] = scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? scheduleItem.active : false; + si["time"] = scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? scheduleItem.time : ""; si["cmd"] = scheduleItem.cmd; si["value"] = scheduleItem.value; si["name"] = scheduleItem.name; @@ -76,7 +76,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu auto si = ScheduleItem(); si.active = schedule["active"]; si.flags = schedule["flags"]; - si.time = schedule["time"].as(); + si.time = si.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE ? "" : schedule["time"].as(); si.cmd = schedule["cmd"].as(); si.value = schedule["value"].as(); si.name = schedule["name"].as(); @@ -322,6 +322,8 @@ bool WebSchedulerService::has_commands() { return false; } +#include "shuntingYard.hpp" + // execute scheduled command bool WebSchedulerService::command(const char * name, const std::string & command, const std::string & data) { std::string cmd = Helpers::toLower(command); @@ -331,13 +333,19 @@ bool WebSchedulerService::command(const char * name, const std::string & command // shelly(get): http:///relais/0?turn=on // parse json JsonDocument doc; - if (DeserializationError::Ok == deserializeJson(doc, cmd)) { + if (deserializeJson(doc, cmd) == DeserializationError::Ok) { HTTPClient http; int httpResult = 0; - String url = doc["url"]; - if (http.begin(url)) { - // It's an HTTP call - + String url = doc["url"] | ""; + // for a GET with parameters replace commands with values + auto q = url.indexOf('?'); + if (q != -1) { + auto s = url.substring(q + 1); + std::string v = s.c_str(); + commands(v, false); + url.replace(s, v.c_str()); + } + if (url.startsWith("http") && http.begin(url)) { // add any given headers for (JsonPair p : doc["header"].as()) { http.addHeader(p.key().c_str(), p.value().as().c_str()); @@ -424,8 +432,6 @@ bool WebSchedulerService::onChange(const char * cmd) { return false; } -#include "shuntingYard.hpp" - // handle condition schedules, parse string stored in schedule.time field void WebSchedulerService::condition() { for (ScheduleItem & scheduleItem : *scheduleItems_) { diff --git a/src/web/shuntingYard.hpp b/src/web/shuntingYard.hpp index 747a78ba8..aa19ec813 100644 --- a/src/web/shuntingYard.hpp +++ b/src/web/shuntingYard.hpp @@ -31,7 +31,7 @@ class Token { public: - enum class Type { + enum class Type : uint8_t { Unknown, Number, String, @@ -332,12 +332,13 @@ bool isnum(const std::string & s) { // replace commands like "//" with its value" -std::string commands(std::string & expr) { +std::string commands(std::string & expr, bool quotes = true) { 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); + // entity names are alphanumeric or _ + auto e = expr.find_first_not_of("/._abcdefghijklmnopqrstuvwxyz0123456789", f); if (e == std::string::npos) { e = expr.length(); } @@ -361,7 +362,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(); - if (!isnum(data)) { + if (!isnum(data) && quotes) { data.insert(data.begin(), '"'); data.insert(data.end(), '"'); } @@ -596,7 +597,7 @@ std::string compute(const std::string & expr) { auto expr_new = emsesp::Helpers::toLower(expr); // search json with url: - auto f = expr_new.find_first_of("{"); + auto f = expr_new.find_first_of('{'); while (f != std::string::npos) { auto e = f + 1; for (uint8_t i = 1; i > 0; e++) { @@ -612,8 +613,8 @@ std::string compute(const std::string & expr) { JsonDocument doc; if (DeserializationError::Ok == deserializeJson(doc, cmd)) { HTTPClient http; - String url = doc["url"]; - if (http.begin(url)) { + String url = doc["url"] | ""; + if (url.startsWith("http") && http.begin(url)) { int httpResult = 0; for (JsonPair p : doc["header"].as()) { http.addHeader(p.key().c_str(), p.value().as().c_str()); @@ -647,20 +648,22 @@ std::string compute(const std::string & expr) { } // positions: q-questionmark, c-colon - auto q = expr_new.find_first_of("?"); + 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); + 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); + q1 = expr_new.find_first_of('?', q1 + 1); + c1 = expr_new.find_first_of(':', c1 + 1); } if (c1 == std::string::npos) { return ""; // error: missing colon } std::string cond = calculate(expr_new.substr(0, q)); - if (cond[0] == '1') { + 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 } else if (cond[0] == '0') { @@ -668,7 +671,7 @@ std::string compute(const std::string & expr) { } else { return ""; // error } - q = expr_new.find_first_of("?"); // search next instance + q = expr_new.find_first_of('?'); // search next instance } return calculate(expr_new);