Merge pull request #3009 from MichaelDvP/core3

https client for scheduler/shuntingYard
This commit is contained in:
Proddy
2026-04-15 21:34:03 +02:00
committed by GitHub
3 changed files with 1686 additions and 1778 deletions

View File

@@ -380,12 +380,16 @@ std::string commands(std::string & expr, bool quotes) {
if (return_code != CommandRet::OK && return_code != CommandRet::NO_VALUE) {
return expr = "";
}
std::string data = output["api_data"] | "";
std::string data;
if (output["api_data"].is<std::string>()) {
data = output["api_data"].as<std::string>();
if (!isnum(data) && quotes) {
data.insert(data.begin(), '"');
data.insert(data.end(), '"');
}
} else {
serializeJson(output, data);
}
expr.replace(f, l, data);
e = f + data.length();
expr_new = Helpers::toLower(expr);
@@ -700,7 +704,6 @@ std::string compute(const std::string & expr) {
std::string cmd = expr_new.substr(f, e - f).c_str();
JsonDocument doc;
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
HTTPClient http;
std::string url, header_s, value_s, method_s, key_s, keys_s;
// search keys lower case
for (JsonPair p : doc.as<JsonObject>()) {
@@ -720,33 +723,126 @@ std::string compute(const std::string & expr) {
keys_s = p.key().c_str();
}
}
if (http.begin(url.c_str())) {
int httpResult = 0;
for (JsonPair p : doc[header_s].as<JsonObject>()) {
http.addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
}
bool content_set = false;
std::string value = doc[value_s] | "";
std::string method = doc[method_s] | "get";
// if there is data, force a POST
std::string method = doc[method_s] | "GET";
if (value.length()) {
method = "POST";
}
std::string result;
int httpResult = 0;
#ifndef NO_TLS_SUPPORT
if (Helpers::toLower(url.c_str()).starts_with("https://")) {
WiFiClient * basic_client = new WiFiClient;
ESP_SSLClient * ssl_client = new ESP_SSLClient;
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
ssl_client->setBufferSizes(1024, 1024);
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
url.replace(0, 8, "");
std::string host = url;
auto index = url.find_first_of('/');
if (index != std::string::npos) {
host = url.substr(0, index);
url.replace(0, index, "");
}
/*
index = host.find_first_of('@');
std::string auth;
if (index != std::string::npos) {
auth = base64::encode(host.substr(0, index));
host.replace(0, index, "");
}
*/
ssl_client->setClient(basic_client);
if (ssl_client->connect(host.c_str(), 443)) {
if (value.length() || Helpers::toLower(method) == "post") {
if (value.find_first_of('{') != std::string::npos) {
http.addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
ssl_client->print("POST ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
for (JsonPair p : doc[header_s].as<JsonObject>()) {
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
httpResult = http.POST(value.c_str());
if (!content_set) {
ssl_client->print("Content-Type: ");
if (value.starts_with('{')) {
ssl_client->println(asyncsrv::T_application_json);
} else {
httpResult = http.GET(); // normal GET
ssl_client->println(asyncsrv::T_text_plain);
}
}
ssl_client->print("Content-Length: ");
ssl_client->println(value.length());
ssl_client->println("Connection: close");
ssl_client->print("\r\n");
ssl_client->print(value.c_str());
} else {
ssl_client->print("GET ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
for (JsonPair p : doc[header_s].as<JsonObject>()) {
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
ssl_client->println("Connection: close");
}
auto ms = millis();
while (!ssl_client->available() && millis() - ms < 3000) {
delay(0);
}
while (ssl_client->available()) {
result += (char)ssl_client->read();
}
ssl_client->stop();
index = result.find_first_of(' ');
if (index != std::string::npos) {
httpResult = stoi(result.substr(index + 1, 3));
}
index = result.find("\r\n\r\n");
if (index != std::string::npos) {
result.replace(0, index + 4, "");
}
}
delete ssl_client;
delete basic_client;
} else
#endif
if (Helpers::toLower(url.c_str()).starts_with("http://")) {
HTTPClient * http = new HTTPClient;
if (http->begin(url.c_str())) {
for (JsonPair p : doc[header_s].as<JsonObject>()) {
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
}
if (value.length() || Helpers::toLower(method) == "post") {
if (!content_set) {
http->addHeader("Content-Type", value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain);
}
httpResult = http->POST(value.c_str());
} else {
httpResult = http->GET(); // normal GET
}
if (httpResult > 0) {
std::string result = http.getString().c_str();
result = http->getString().c_str();
}
}
http->end();
delete http;
}
if (httpResult == 200) {
std::string key = doc[key_s] | "";
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
if (doc[keys_s].is<JsonArray>()) {
keys_doc.set(doc[keys_s].as<JsonArray>());
}
JsonArray keys = keys_doc.as<JsonArray>();
if (key.length() || !keys.isNull()) {
doc.clear();
if (DeserializationError::Ok == deserializeJson(doc, result)) {
@@ -767,9 +863,9 @@ std::string compute(const std::string & expr) {
}
}
}
expr_new.replace(f, e - f, result.c_str());
}
http.end();
expr_new.replace(f, e - f, result);
} else if (httpResult != 0) {
EMSESP::logger().warning("URL command failed with https code: %d, response: %s", httpResult, result.c_str());
}
}
f = expr_new.find_first_of('{', e);

View File

@@ -140,14 +140,10 @@
bool success = false;
#ifndef NO_TLS_SUPPORT
WiFiClient * basic_client;
ESP_SSLClient * ssl_client;
ReadyClient * r_client; // rClient(ssl_client);
SMTPClient * smtp; // smtp(rClient);
basic_client = new WiFiClient;
ssl_client = new ESP_SSLClient;
r_client = new ReadyClient(*ssl_client);
smtp = new SMTPClient(*r_client);
WiFiClient * basic_client = new WiFiClient;
ESP_SSLClient * ssl_client = new ESP_SSLClient;
ReadyClient * r_client = new ReadyClient(*ssl_client);
SMTPClient * smtp = new SMTPClient(*r_client);
ssl_client->setClient(basic_client);
ssl_client->setInsecure();
@@ -196,7 +192,16 @@
// msg.headers.addCustom("Importance", PRIORITY);
// msg.headers.addCustom("X-MSMail-Priority", PRIORITY);
// msg.headers.addCustom("X-Priority", PRIORITY_NUM);
EMSESP::webSchedulerService.computed_value.clear();
EMSESP::webSchedulerService.raw_value = body.c_str();
for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) {
delay(1);
}
if (!EMSESP::webSchedulerService.computed_value.empty()) {
body = EMSESP::webSchedulerService.computed_value.c_str();
EMSESP::webSchedulerService.computed_value.clear();
EMSESP::webSchedulerService.computed_value.shrink_to_fit(); // free allocated memory
}
msg.text.body(body);
// bodyText.replace("\r\n", "<br>\r\n");
@@ -351,7 +356,8 @@
LOG_INFO("Message: %s", EMSESP::webSchedulerService.computed_value.c_str()); // send to log
Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled
output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API
EMSESP::webSchedulerService.computed_value.clear();
EMSESP::webSchedulerService.computed_value.shrink_to_fit();
return true;
}
@@ -1427,300 +1433,6 @@
bool System::check_restore() {
bool reboot_required = false; // true if we need to reboot
#ifndef EMSESP_STANDALONE
File new_file = LittleFS.open(TEMP_FILENAME_PATH);
if (new_file) {
JsonDocument jsonDocument;
DeserializationError error = deserializeJson(jsonDocument, new_file);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject input = jsonDocument.as<JsonObject>();
// see what type of file it is, either settings or customization. anything else is ignored
std::string settings_type = input["type"];
LOG_INFO("Restoring '%s' settings...", settings_type.c_str());
// system backup, which is a consolidated json object with all the settings files
if (settings_type == "systembackup") {
reboot_required = true;
JsonArray sections = input["systembackup"].as<JsonArray>();
for (JsonObject section : sections) {
std::string section_type = section["type"];
LOG_DEBUG("Restoring '%s' section...", section_type.c_str());
if (section_type == "settings") {
saveSettings(NETWORK_SETTINGS_FILE, section);
saveSettings(AP_SETTINGS_FILE, section);
saveSettings(MQTT_SETTINGS_FILE, section);
saveSettings(NTP_SETTINGS_FILE, section);
saveSettings(SECURITY_SETTINGS_FILE, section);
saveSettings(EMSESP_SETTINGS_FILE, section);
}
if (section_type == "schedule") {
saveSettings(EMSESP_SCHEDULER_FILE, section);
}
if (section_type == "customizations") {
saveSettings(EMSESP_CUSTOMIZATION_FILE, section);
}
if (section_type == "entities") {
saveSettings(EMSESP_CUSTOMENTITY_FILE, section);
}
if (section_type == "modules") {
saveSettings(EMSESP_MODULES_FILE, section);
}
if (section_type == "customSupport") {
// it's a custom support, extract json and write to /config/customSupport.json file
File customSupportFile = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "w");
if (customSupportFile) {
serializeJson(section, customSupportFile);
customSupportFile.close();
LOG_INFO("Custom support file updated");
} else {
LOG_ERROR("Failed to save custom support file");
}
}
if (section_type == "nvs") {
// Restore NVS values
JsonArray nvs_entries = section["nvs"].as<JsonArray>();
for (JsonObject entry : nvs_entries) {
std::string key = entry["key"] | "";
int type = entry["type"] | NVS_TYPE_ANY;
switch (type) {
case NVS_TYPE_I8:
if (entry["value"].is<JsonVariantConst>()) {
int8_t v = entry["value"];
EMSESP::nvs_.putChar(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_U8:
if (entry["value"].is<JsonVariantConst>()) {
uint8_t v = entry["value"];
EMSESP::nvs_.putUChar(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_I32:
if (entry["value"].is<JsonVariantConst>()) {
int32_t v = entry["value"];
EMSESP::nvs_.putInt(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_U32:
if (entry["value"].is<JsonVariantConst>()) {
uint32_t v = entry["value"];
EMSESP::nvs_.putUInt(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_I64:
if (entry["value"].is<JsonVariantConst>()) {
int64_t v = entry["value"];
EMSESP::nvs_.putLong64(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_U64:
if (entry["value"].is<JsonVariantConst>()) {
uint64_t v = entry["value"];
EMSESP::nvs_.putULong64(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_BLOB:
// used for double values
if (entry["value"].is<JsonVariantConst>()) {
double v = entry["value"];
EMSESP::nvs_.putDouble(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_STR:
case NVS_TYPE_ANY:
default:
if (entry["value"].is<JsonVariantConst>()) {
std::string v = entry["value"];
EMSESP::nvs_.putString(key.c_str(), v.c_str());
LOG_DEBUG("Restored NVS value: %s = %s", key.c_str(), v.c_str());
}
break;
}
}
}
}
}
// It's a single settings file. Parse each section separately. If it's system related it will require a reboot
else if (settings_type == "settings") {
reboot_required = saveSettings(NETWORK_SETTINGS_FILE, input);
reboot_required |= saveSettings(AP_SETTINGS_FILE, input);
reboot_required |= saveSettings(MQTT_SETTINGS_FILE, input);
reboot_required |= saveSettings(NTP_SETTINGS_FILE, input);
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, input);
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, input);
} else if (settings_type == "customizations") {
saveSettings(EMSESP_CUSTOMIZATION_FILE, input);
} else if (settings_type == "schedule") {
saveSettings(EMSESP_SCHEDULER_FILE, input);
} else if (settings_type == "entities") {
saveSettings(EMSESP_CUSTOMENTITY_FILE, input);
} else if (settings_type == "customSupport") {
// it's a custom support file - save it to /config
new_file.close();
if (LittleFS.rename(TEMP_FILENAME_PATH, EMSESP_CUSTOMSUPPORT_FILE)) {
LOG_INFO("Custom support file stored");
return false; // no need to reboot
} else {
LOG_ERROR("Failed to save custom support file");
}
} else {
LOG_ERROR("Unrecognized file uploaded");
}
} else {
LOG_ERROR("Unrecognized file uploaded, not json.");
}
// close (just in case) and remove the temp file
new_file.close();
LittleFS.remove(TEMP_FILENAME_PATH);
}
#endif
return reboot_required;
}
// handle upgrades from previous versions
// this function will not be called on a clean install, with no settings files yet created
// returns true if we need a reboot
bool System::check_upgrade() {
bool missing_version = true;
std::string settingsVersion;
// fetch current version from settings file
EMSESP::webSettingsService.read([&](WebSettings const & settings) { settingsVersion = settings.version.c_str(); });
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
if (missing_version) {
LOG_WARNING("No version information found. Assuming version 3.5.0");
settingsVersion = "3.5.0"; // this was the last stable version without version info
}
version::Semver200_version settings_version(settingsVersion);
version::Semver200_version this_version(EMSESP_APP_VERSION);
std::string settings_version_type = settings_version.prerelease().empty() ? "" : ("-" + settings_version.prerelease());
std::string this_version_type = this_version.prerelease().empty() ? "" : ("-" + this_version.prerelease());
bool save_version = true;
bool reboot_required = false;
LOG_DEBUG("Checking for version upgrades from v%d.%d.%d%s",
settings_version.major(),
settings_version.minor(),
settings_version.patch(),
settings_version_type.c_str());
// compare versions
if (this_version > settings_version) {
// we need to do an upgrade
if (missing_version) {
LOG_NOTICE("Upgrading to version %d.%d.%d%s", this_version.major(), this_version.minor(), this_version.patch(), this_version_type.c_str());
} else {
LOG_NOTICE("Upgrading from version %d.%d.%d%s to %d.%d.%d%s",
settings_version.major(),
settings_version.minor(),
settings_version.patch(),
settings_version_type.c_str(),
this_version.major(),
this_version.minor(),
this_version.patch(),
this_version_type.c_str());
}
// if we're coming from 3.4.4 or 3.5.0b14 which had no version stored then we need to apply new settings
if (missing_version) {
LOG_INFO("Upgrade: Setting MQTT Entity ID format to older v3.4 format (0)");
EMSESP::esp32React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) {
mqttSettings.entity_format = Mqtt::entityFormat::SINGLE_LONG; // use old Entity ID format from v3.4
return StateUpdateResult::CHANGED;
});
} else if (settings_version.major() == 3 && settings_version.minor() <= 6) {
EMSESP::esp32React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) {
if (mqttSettings.entity_format == 1) {
mqttSettings.entity_format = Mqtt::entityFormat::SINGLE_OLD; // use old Entity ID format from v3.6
LOG_INFO("Upgrade: Setting MQTT Entity ID format to v3.6 format (3)");
return StateUpdateResult::CHANGED;
} else if (mqttSettings.entity_format == 2) {
mqttSettings.entity_format = Mqtt::entityFormat::MULTI_OLD; // use old Entity ID format from v3.6
LOG_INFO("Upgrade: Setting MQTT Entity ID format to v3.6 format (4)");
return StateUpdateResult::CHANGED;
}
return StateUpdateResult::UNCHANGED;
});
}
// changes pre < v3.7.0
if (settings_version.major() == 3 && settings_version.minor() < 7) {
// network changes
// 1) WiFi Tx Power is now using the value * 4 (was 20)
// 2) WiFi sleep is now off by default (was on)
EMSESP::esp32React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
auto changed = StateUpdateResult::UNCHANGED;
if (networkSettings.tx_power == 20) {
networkSettings.tx_power = WIFI_POWER_19_5dBm; // use 19.5 as we don't have 20 anymore
LOG_INFO("Upgrade: Setting WiFi TX Power to Auto");
changed = StateUpdateResult::CHANGED;
}
if (networkSettings.nosleep != true) {
networkSettings.nosleep = true;
LOG_INFO("Upgrade: Disabling WiFi nosleep");
changed = StateUpdateResult::CHANGED;
}
return changed;
});
}
// changes to application settings
EMSESP::webSettingsService.update([&](WebSettings & settings) {
// force web buffer to 25 for those boards without psram
if ((EMSESP::system_.PSram() == 0) && (settings.weblog_buffer != 25)) {
settings.weblog_buffer = 25;
return StateUpdateResult::CHANGED;
}
return StateUpdateResult::UNCHANGED;
});
} else if (this_version < settings_version) {
// downgrading
LOG_NOTICE("Downgrading from version %d.%d.%d%s to version %d.%d.%d%s",
settings_version.major(),
settings_version.minor(),
settings_version.patch(),
settings_version_type.c_str(),
this_version.major(),
this_version.minor(),
this_version.patch(),
this_version_type.c_str());
} else {
save_version = false; // same version, do nothing
}
// if we did a change, set the new version and save it, no need to reboot
if (save_version) {
EMSESP::webSettingsService.update([&](WebSettings & settings) {
settings.version = EMSESP_APP_VERSION;
LOG_DEBUG("Upgrade: Setting version to %s", EMSESP_APP_VERSION);
return StateUpdateResult::CHANGED;
});
}
if (reboot_required) {
LOG_INFO("Upgrade: Rebooting to apply changes");
return true; // need reboot
}
return false; // no reboot required
}
#ifndef EMSESP_STANDALONE
// map each config filename to its human-readable section key
static const std::pair<const char *, const char *> SECTION_MAP[] = {

View File

@@ -363,39 +363,139 @@ bool WebSchedulerService::command(const char * name, const std::string & command
commands(s, false);
url.replace(q + 1, l, s);
}
if (http.begin(url.c_str())) {
// add any given headers
for (JsonPair p : doc["header"].as<JsonObject>()) {
http.addHeader(p.key().c_str(), p.value().as<String>().c_str());
}
std::string value = doc["value"] | data.c_str(); // extract value if its in the command, or take the data
std::string value = doc["value"] | data; // extract value if its in the command, or take the data
std::string method = doc["method"] | "GET"; // default GET
commands(value, false);
// if there is data, force a POST
if (value.length()) {
method = "POST";
}
std::string result;
int httpResult = 0;
if (value.length() || method == "post") { // we have all lowercase
if (value.find_first_of('{') != std::string::npos) {
http.addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
#ifndef NO_TLS_SUPPORT
if (Helpers::toLower(url.c_str()).starts_with("https://")) {
WiFiClient * basic_client = new WiFiClient;
ESP_SSLClient * ssl_client = new ESP_SSLClient;
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
ssl_client->setBufferSizes(1024, 1024);
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
url.replace(0, 8, "");
std::string host = url;
auto index = url.find_first_of('/');
if (index != std::string::npos) {
host = url.substr(0, index);
url.replace(0, index, "");
}
httpResult = http.POST(value.c_str());
// EMSESP::logger().debug("Host: %s, URL: %s", host.c_str(), url.c_str());
ssl_client->setClient(basic_client);
if (ssl_client->connect(host.c_str(), 443)) {
if (value.length() || Helpers::toLower(method) == "post") {
// EMSESP::logger().debug("POST %s HTTP/1.1", url.c_str());
ssl_client->print("POST ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
bool content_set = false;
for (JsonPair p : doc["header"].as<JsonObject>()) {
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
if (!content_set) {
ssl_client->print("Content-Type: ");
if (value.starts_with('{')) {
ssl_client->println(asyncsrv::T_application_json);
} else {
httpResult = http.GET(); // normal GET
ssl_client->println(asyncsrv::T_text_plain);
}
http.end();
}
ssl_client->print("Content-Length: ");
ssl_client->println(value.length());
ssl_client->println("Connection: close");
ssl_client->print("\r\n");
ssl_client->print(value.c_str());
} else {
// EMSESP::logger().debug("GET %s HTTP/1.1", url.c_str());
ssl_client->print("GET ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
for (JsonPair p : doc["header"].as<JsonObject>()) {
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
ssl_client->println("Connection: close");
}
auto ms = millis();
while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) {
delay(0);
}
while (ssl_client->available()) {
result += (char)ssl_client->read();
}
ssl_client->stop();
// EMSESP::logger().debug("HTTPS response: %s", result.c_str());
index = result.find_first_of(' ');
if (index != std::string::npos) {
httpResult = stoi(result.substr(index + 1, 3));
// EMSESP::logger().debug("HTTPS code: %i", httpResult);
}
index = result.find("\r\n\r\n");
if (index != std::string::npos) {
result.replace(0, index + 4, "");
// EMSESP::logger().debug("HTTPS response: %s", result.c_str());
}
} else {
EMSESP::logger().warning("HTTPS connection failed");
}
delete ssl_client;
delete basic_client;
// check HTTP return code
if (httpResult != 200) {
char error[100];
snprintf(error, sizeof(error), "Schedule %s: URL command failed with http code %d", name, httpResult);
EMSESP::logger().warning(error);
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
return false;
}
return true;
} else
#endif
if (Helpers::toLower(url.c_str()).starts_with("http://")) {
HTTPClient * http = new HTTPClient;
if (http->begin(url.c_str())) {
bool content_set = false;
for (JsonPair p : doc["header"].as<JsonObject>()) {
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
content_set |= p.key() == "content-type";
}
// if there is data, force a POST
if (Helpers::toLower(method) == "post") { // we have all lowercase
if (!content_set) {
// http->addHeader("Content-Type", value.find_first_of('{') != std::string::npos ? "application/json" : "text/plain");
if (value.starts_with('{')) {
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
} else {
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_text_plain, false); // auto-set to JSON
}
}
httpResult = http->POST(value.c_str());
} else {
httpResult = http->GET(); // normal GET
if (httpResult > 0) {
result = http->getString().c_str();
}
}
}
http->end();
delete http;
// check HTTP return code
if (httpResult != 200) {
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
return false;
}
#if defined(EMSESP_DEBUG)
char msg[100];
snprintf(msg, sizeof(msg), "Schedule %s: URL command successful with http code %d", name, httpResult);
EMSESP::logger().debug(msg);
EMSESP::logger().debug("Schedule %s: URL '%s' command successful with http code %d", name, url.c_str(), httpResult);
#endif
return true;
}