diff --git a/interface/package.json b/interface/package.json index 511b8fc51..6cfa89f3a 100644 --- a/interface/package.json +++ b/interface/package.json @@ -30,7 +30,7 @@ "@mui/material": "^5.16.4", "@table-library/react-table-library": "4.1.7", "@types/lodash-es": "^4.17.12", - "@types/node": "^20.14.10", + "@types/node": "^20.14.11", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", @@ -44,7 +44,7 @@ "react-dom": "latest", "react-dropzone": "^14.2.3", "react-icons": "^5.2.1", - "react-router-dom": "^6.25.0", + "react-router-dom": "^6.25.1", "react-toastify": "^10.0.5", "typesafe-i18n": "^5.26.2", "typescript": "^5.5.3" diff --git a/interface/src/framework/system/ESPSystemStatus.tsx b/interface/src/framework/system/ESPSystemStatus.tsx index b06b96f14..5cdc61bbf 100644 --- a/interface/src/framework/system/ESPSystemStatus.tsx +++ b/interface/src/framework/system/ESPSystemStatus.tsx @@ -26,6 +26,8 @@ import { useRequest } from 'alova'; import { ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; +import BBQKeesIcon from '../../project/bbqkees.svg'; + function formatNumber(num: number) { return new Intl.NumberFormat().format(num); } @@ -51,8 +53,15 @@ const ESPSystemStatus: FC = () => { - - + + {data.model ? ( + + ) : ( + + )} { formatNumber(data.free_heap) + ' KB (' + formatNumber(data.max_alloc_heap) + - ' KB alloc)' + ' KB max alloc)' } /> diff --git a/interface/src/project/bbqkees.svg b/interface/src/project/bbqkees.svg new file mode 100644 index 000000000..9191aa470 --- /dev/null +++ b/interface/src/project/bbqkees.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/yarn.lock b/interface/yarn.lock index 2fc3b20ec..42ea62bac 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -1778,12 +1778,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.14.10": - version: 20.14.10 - resolution: "@types/node@npm:20.14.10" +"@types/node@npm:^20.14.11": + version: 20.14.11 + resolution: "@types/node@npm:20.14.11" dependencies: undici-types: "npm:~5.26.4" - checksum: 10c0/0b06cff14365c2d0085dc16cc8cbea5c40ec09cfc1fea966be9eeecf35562760bfde8f88e86de6edfaf394501236e229d9c1084fad04fb4dec472ae245d8ae69 + checksum: 10c0/5306becc0ff41d81b1e31524bd376e958d0741d1ce892dffd586b9ae0cb6553c62b0d62abd16da8bea6b9a2c17572d360450535d7c073794b0cef9cb4e39691e languageName: node linkType: hard @@ -2024,7 +2024,7 @@ __metadata: "@trivago/prettier-plugin-sort-imports": "npm:^4.3.0" "@types/babel__core": "npm:^7" "@types/lodash-es": "npm:^4.17.12" - "@types/node": "npm:^20.14.10" + "@types/node": "npm:^20.14.11" "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" "@types/react-router-dom": "npm:^5.3.3" @@ -2043,7 +2043,7 @@ __metadata: react-dom: "npm:latest" react-dropzone: "npm:^14.2.3" react-icons: "npm:^5.2.1" - react-router-dom: "npm:^6.25.0" + react-router-dom: "npm:^6.25.1" react-toastify: "npm:^10.0.5" rollup-plugin-visualizer: "npm:^5.12.0" terser: "npm:^5.31.3" @@ -6185,27 +6185,27 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^6.25.0": - version: 6.25.0 - resolution: "react-router-dom@npm:6.25.0" +"react-router-dom@npm:^6.25.1": + version: 6.25.1 + resolution: "react-router-dom@npm:6.25.1" dependencies: "@remix-run/router": "npm:1.18.0" - react-router: "npm:6.25.0" + react-router: "npm:6.25.1" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 10c0/a837686f3ce9a12224d97e9b200a0e38575f32a568c80d5d8470e8f14208986ab266c1d6ec95e903ab3b1c3d1273cfe886401c33bdef393aa2c7eef4d6987692 + checksum: 10c0/15e2b5bf89a26db9a108d19a4e0e2054180bfb1f5f62662dd93ad697ee1bdc91a8041efd762d552c95e65fc06ca0cb0c1e88acdeeaf03aba37f7a29e470c7cc4 languageName: node linkType: hard -"react-router@npm:6.25.0": - version: 6.25.0 - resolution: "react-router@npm:6.25.0" +"react-router@npm:6.25.1": + version: 6.25.1 + resolution: "react-router@npm:6.25.1" dependencies: "@remix-run/router": "npm:1.18.0" peerDependencies: react: ">=16.8" - checksum: 10c0/82a7e9de69f444c57bbece2904518f1279a8ef80563172fff36cec3b74d854ae5702c560b6f9e02a5439b756f884dc16b81996ba613b3b6c0b31cd16bd1bc189 + checksum: 10c0/a7e824c1f6d9641beabc23111865ddd2525b3794403e07b297fc2bdd4cddec93e166aacdb9d2602768864d70f3bf490f59eeab8474a04ae1f13a832f305eeec3 languageName: node linkType: hard diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts index c6df591d5..4da77f457 100644 --- a/mock-api/rest_server.ts +++ b/mock-api/rest_server.ts @@ -369,6 +369,7 @@ const ESPsystem_status = { psram_size: 0, free_psram: 0, has_loader: true, + // model: '' model: 'BBQKees Electronics EMS Gateway E32 V2 (E32 V2.0 P3/2024011)' }; diff --git a/src/command.cpp b/src/command.cpp index 643fbb8ae..eb1d46041 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -334,10 +334,10 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // first see if there is a command registered and it's valid auto cf = find_command(device_type, device_id, cmd, flag); if (!cf) { - LOG_WARNING("Command failed: invalid command '%s'", cmd ? cmd : ""); + LOG_WARNING("Command failed: unknown command '%s'", cmd ? cmd : ""); // if we don't alread have a message set, set it to invalid command if (!output["message"]) { - output["message"] = "invalid command"; + output["message"] = "unknown command"; } return CommandRet::ERROR; } @@ -360,39 +360,45 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * snprintf(info_s, sizeof(info_s), "'%s/%s'", dname, cmd); } - if (single_command) { - LOG_DEBUG(("%sCalling command %s"), ro.c_str(), info_s); - } else { - if (id > 0) { - LOG_INFO(("%sCalling command %s with value %s and id %d on device 0x%02X"), ro.c_str(), info_s, value, id, device_id); - } else { - LOG_INFO(("%sCalling command %s with value %s"), ro.c_str(), info_s, value); - } - } - - // call the function based on type, either with a json package or no parameters + // call the function based on command function type + // commands return true or false only (bool) uint8_t return_code = CommandRet::OK; if (cf->cmdfunction_json_) { - // JSON + // handle commands that report back a JSON body return_code = ((cf->cmdfunction_json_)(value, id, output)) ? CommandRet::OK : CommandRet::ERROR; } else if (cf->cmdfunction_) { - // Normal command + // if it's a read only command and we're trying to set a value, return an error if (!single_command && EMSESP::cmd_is_readonly(device_type, device_id, cmd, id)) { return_code = CommandRet::INVALID; // error on readonly or invalid hc } else { + // call it... return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR; } } - // report back. If not OK show output from error, other return the HTTP code + // report back. If not OK show output from error, otherwise return the HTTP code if (return_code != CommandRet::OK) { + char error[100]; if (single_command) { - LOG_ERROR("Command '%s' failed with error '%s'", cmd, FL_(cmdRet)[return_code]); + snprintf(error, sizeof(error), "Command '%s' failed (%s)", cmd, FL_(cmdRet)[return_code]); } else { - LOG_ERROR("Command '%s: %s' failed with error '%s'", cmd, value, FL_(cmdRet)[return_code]); + snprintf(error, sizeof(error), "Command '%s: %s' failed (%s)", cmd, value, FL_(cmdRet)[return_code]); + } + output.clear(); + output["message"] = error; + LOG_WARNING(error); + } else { + if (single_command) { + LOG_DEBUG(("%sCalling command %s"), ro.c_str(), info_s); + } else { + if (id > 0) { + LOG_INFO(("%sCalling command %s with value %s and id %d on device 0x%02X"), ro.c_str(), info_s, value, id, device_id); + } else { + LOG_INFO(("%sCalling command %s with value %s"), ro.c_str(), info_s, value); + } } - return message(return_code, "callback function failed", output); } + return return_code; } diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index a8c36e1a2..ea57ee4a7 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -252,7 +252,7 @@ std::shared_ptr Thermostat::heating_circuit(const in // if its a new one, the heating circuit object will be created and also the fetch flags set std::shared_ptr Thermostat::heating_circuit(std::shared_ptr telegram) { // look through the Monitor and Set arrays to see if there is a match - uint8_t hc_num = 0; + uint8_t hc_num = 0; // 0 means we haven't found it yet bool toggle_ = false; // search device-id types for remote thermostats first, they have only a single typeid for all hcs @@ -271,6 +271,7 @@ std::shared_ptr Thermostat::heating_circuit(std::sha } } } + // not found, search status message/set types if (hc_num == 0) { for (uint8_t i = 0; i < set_typeids.size(); i++) { diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 3e4ba7f3f..c1da03c52 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -109,6 +109,7 @@ class Thermostat : public EMSdevice { return hc_num_; } + // returns heating circuit number 0..9 uint8_t hc() const { return hc_num_ - 1; } diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 40617e002..df55e7a67 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -332,7 +332,7 @@ void EMSdevice::fetch_values() { // toggle on/off automatic fetch for a telegramID void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) { #if defined(EMSESP_DEBUG) - EMSESP::logger().debug("Toggling fetch for deviceID 0x%02X, telegramID 0x%02X to %d", device_id(), telegram_id, toggle); + EMSESP::logger().debug("Setting fetch to %d for deviceID 0x%02X, telegramID 0x%02X", toggle, device_id(), telegram_id); #endif for (auto & tf : telegram_functions_) { diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 07061764c..18ff6ac2e 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -746,15 +746,22 @@ void EMSESP::publish_response(std::shared_ptr telegram) { // for other types like sensors, scheduler, custom entities it will process single commands like 'info', 'values', 'commands'... bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8_t id, const uint8_t devicetype) { // check first for EMS devices + bool found_device = false; for (const auto & emsdevice : emsdevices) { if (emsdevice->device_type() == devicetype) { + found_device = true; if (emsdevice->get_value_info(root, cmd, id)) { return true; } } } + // if the EMS device was valid, but the cmd not found show an error + if (found_device) { + root["message"] = std::string("unknown command ") + cmd; + return false; + } - // check for other devices... + // check for other non EMS devices... // temperature sensor if (devicetype == DeviceType::TEMPERATURESENSOR) { @@ -788,7 +795,7 @@ bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8 bool EMSESP::return_not_found(JsonObject output, const char * msg, const char * cmd) { output.clear(); char error[100]; - snprintf(error, sizeof(error), "cannot find %s in '%s'", msg, cmd); + snprintf(error, sizeof(error), "cannot find %s in %s", msg, cmd); output["message"] = error; return false; } diff --git a/src/locale_translations.h b/src/locale_translations.h index b89441e91..8401a5c63 100644 --- a/src/locale_translations.h +++ b/src/locale_translations.h @@ -59,9 +59,9 @@ MAKE_WORD_TRANSLATION(water_device, "Water Module", "Wassermodul", "", "", "Modu MAKE_WORD_TRANSLATION(pool_device, "Pool Module", "Poolmodul", "", "", "Moduł basenu", "", "", "", "", "") // TODO translate // commands -MAKE_WORD_TRANSLATION(info_cmd, "lists all values", "Liste aller Werte", "lijst van alle waardes", "", "wyświetl wszystkie wartości", "Viser alle verdier", "", "Tüm değerleri listele", "elenca tutti i valori", "zobraziť všetky hodnoty") // TODO translate -MAKE_WORD_TRANSLATION(commands_cmd, "lists all commands", "Liste aller Kommandos", "lijst van alle commando's", "", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy") // TODO translate -MAKE_WORD_TRANSLATION(entities_cmd, "lists all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "", "wyświetl wszsytkie encje", "Viser alle enheter", "", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity") // TODO translate +MAKE_WORD_TRANSLATION(info_cmd, "list all values", "Liste aller Werte", "lijst van alle waardes", "", "wyświetl wszystkie wartości", "Viser alle verdier", "", "Tüm değerleri listele", "elenca tutti i valori", "zobraziť všetky hodnoty") // TODO translate +MAKE_WORD_TRANSLATION(commands_cmd, "list all commands", "Liste aller Kommandos", "lijst van alle commando's", "", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy") // TODO translate +MAKE_WORD_TRANSLATION(entities_cmd, "list all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "", "wyświetl wszsytkie encje", "Viser alle enheter", "", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity") // TODO translate MAKE_WORD_TRANSLATION(send_cmd, "send a telegram", "Sende EMS-Telegramm", "stuur een telegram", "", "wyślij telegram", "send et telegram", "", "Bir telegram gönder", "invia un telegramma", "poslať telegram") // TODO translate MAKE_WORD_TRANSLATION(setiovalue_cmd, "set io value", "Setze Wertevorgabe", "instellen standaardwaarde", "", "ustaw wartość", "sett en io verdi", "", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io") // TODO translate MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Sysloglevel", "aanpassen log niveau", "", "zmień poziom log-u", "endre loggnivå", "", "Kayıt seviyesini değiştir", "cambia livello registrazione", "") // TODO translate diff --git a/src/mqtt.cpp b/src/mqtt.cpp index bb52d35a4..06d1151f1 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -579,7 +579,7 @@ void Mqtt::ha_status() { } #endif - // TODO camelCase + // These come from the heartbeat MQTT topic publish_system_ha_sensor_config(DeviceValueType::STRING, "EMS Bus", "bus_status", DeviceValueUOM::NONE); publish_system_ha_sensor_config(DeviceValueType::STRING, "Uptime", "uptime", DeviceValueUOM::NONE); publish_system_ha_sensor_config(DeviceValueType::INT8, "Uptime (sec)", "uptime_sec", DeviceValueUOM::SECONDS); @@ -592,6 +592,9 @@ void Mqtt::ha_status() { publish_system_ha_sensor_config(DeviceValueType::INT8, "Tx reads", "txreads", DeviceValueUOM::NONE); publish_system_ha_sensor_config(DeviceValueType::INT8, "Tx writes", "txwrites", DeviceValueUOM::NONE); publish_system_ha_sensor_config(DeviceValueType::INT8, "Tx fails", "txfails", DeviceValueUOM::NONE); + + // This comes from the info MQTT topic + publish_system_ha_sensor_config(DeviceValueType::STRING, "Version", "version", DeviceValueUOM::NONE); } // add sub or pub task to the queue. @@ -980,7 +983,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev readonly_sensors = false; break; default: - // plain old sensor, and make read-only + // plain old sensor, and make it read-only break; } } @@ -1083,12 +1086,18 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev free(F_name); // very important! doc["name"] = ha_name; - // not needed for commands if (type != DeviceValueType::CMD) { // state topic, except for commands char stat_t[MQTT_TOPIC_MAX_SIZE]; - snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), tag_to_topic(device_type, tag).c_str()); + + // This is where we determine which MQTT topic to pull the data from + // There is one exception for DeviceType::SYSTEM, which uses the heartbeat topic, and when fetching the version we want to take this from the info topic instead + if ((device_type == EMSdevice::DeviceType::SYSTEM) && (strncmp(entity, "version", 7) == 0)) { + snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), F_(info)); + } else { + snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), tag_to_topic(device_type, tag).c_str()); + } doc["stat_t"] = stat_t; // value template diff --git a/src/system.cpp b/src/system.cpp index b661416f3..0d76b31ed 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -668,7 +668,6 @@ void System::send_info_mqtt() { } // create the json for heartbeat -// TODO camelCase void System::heartbeat_json(JsonObject output) { switch (EMSESP::bus_status()) { case EMSESP::BUS_STATUS_OFFLINE: @@ -1414,7 +1413,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output #endif EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { if (WiFi.status() == WL_CONNECTED && !settings.bssid.isEmpty()) { - node["BSSID"] = "set"; // TODO why is this not the actual value? + node["BSSID"] = "set"; // TODO why is this not the actual value?? } node["TxPower setting"] = settings.tx_power; node["static ip config"] = settings.staticIPConfig; diff --git a/src/test/test.cpp b/src/test/test.cpp index 3fbf43c6f..d36b89b45 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -119,8 +119,8 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { // RC300WWmode2(0x31D), data: 00 00 09 07 uart_telegram({0x10, 00, 0xFF, 00, 02, 0x1D, 00, 00, 0x09, 0x07}); - // 2nd thermostat - // Thermostat RCPLUSStatusMessage_HC2(0x01A6) + // 2nd thermostat on HC2 + // Thermostat RC300Monitor(0x02A6) uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA6, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); @@ -599,8 +599,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const if (command == "2thermostats") { shell.printfln("Testing multiple thermostats..."); test("2thermostats"); - shell.invoke_command("show values"); - shell.invoke_command("show devices"); + // shell.invoke_command("show values"); + // shell.invoke_command("show devices"); ok = true; } @@ -963,17 +963,27 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // load devices test("boiler"); // test("thermostat"); + test("2thermostats"); if (single) { // run dedicated tests only - EMSESP::webCustomEntityService.test(); // custom entities - EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS - EMSESP::temperaturesensor_.test(); // add temperature sensors - EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions + // EMSESP::webCustomEntityService.test(); // custom entities + // EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS + // EMSESP::temperaturesensor_.test(); // add temperature sensors + // EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions - // request.url("/api/analogsensor/test_analog10/bad"); + // request.url("/api/thermostat/commands"); // EMSESP::webAPIService.webAPIService(&request); + request.url("/api/thermostat/hc1/mode2"); + EMSESP::webAPIService.webAPIService(&request); + + request.url("/api/thermostat/hc2/mode"); + EMSESP::webAPIService.webAPIService(&request); + + request.url("/api/thermostat/hc1/mode"); + EMSESP::webAPIService.webAPIService(&request); + } else { EMSESP::webCustomEntityService.test(); // custom entities EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index cdb323d31..af835df54 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -124,15 +124,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { // call command uint8_t return_code = Command::process(request->url().c_str(), is_admin, input, output); - if (return_code != CommandRet::OK) { - char error[100]; - if (output.size()) { - snprintf(error, sizeof(error), "API call failed. %s (%s)", (const char *)output["message"], Command::return_code_string(return_code).c_str()); - } else { - snprintf(error, sizeof(error), "API call failed (%s)", Command::return_code_string(return_code).c_str()); - } - emsesp::EMSESP::logger().err(error); api_fails_++; } @@ -142,10 +134,10 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { if (api_data) { request->send(200, "text/plain; charset=utf-8", api_data); #if defined(EMSESP_STANDALONE) + Serial.println(); Serial.printf("%sweb output: %s[%s] %s(200)%s ", COLOR_WHITE, COLOR_BRIGHT_CYAN, request->url().c_str(), COLOR_BRIGHT_GREEN, COLOR_MAGENTA); serializeJson(output, Serial); Serial.println(COLOR_RESET); - Serial.println(); #endif api_count_++; delete response; @@ -166,11 +158,11 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { api_count_++; #if defined(EMSESP_STANDALONE) + Serial.println(); Serial.printf("%sweb output: %s[%s]", COLOR_WHITE, COLOR_BRIGHT_CYAN, request->url().c_str()); Serial.printf(" %s(%d)%s ", ret_codes[return_code] == 200 ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_RED, ret_codes[return_code], COLOR_YELLOW); serializeJson(output, Serial); Serial.println(COLOR_RESET); - Serial.println(); #endif } diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index e9a942749..88abddcb1 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -329,7 +329,7 @@ bool WebSchedulerService::has_commands() { } // execute scheduled command -bool WebSchedulerService::command(const char * cmd, const char * data) { +bool WebSchedulerService::command(const char * name, const char * cmd, const char * data) { JsonDocument doc_input; JsonObject input = doc_input.to(); if (strlen(data)) { // empty data queries a value @@ -347,7 +347,7 @@ bool WebSchedulerService::command(const char * cmd, const char * data) { if (return_code == CommandRet::OK) { #if defined(EMSESP_DEBUG) - EMSESP::logger().debug("Scheduled command '%s' with data '%s' was successful", cmd, data); + EMSESP::logger().debug("Schedule command '%s' with data '%s' was successful", cmd, data); #endif if (strlen(data) == 0 && output.size()) { Mqtt::queue_publish("response", output); @@ -357,14 +357,10 @@ bool WebSchedulerService::command(const char * cmd, const char * data) { char error[100]; if (output.size()) { - snprintf(error, - sizeof(error), - "Scheduled command %s failed with error: %s (%s)", - cmd, - (const char *)output["message"], - Command::return_code_string(return_code).c_str()); + // check for empty name + snprintf(error, sizeof(error), "Schedule %s: %s", name ? name : "", (const char *)output["message"]); // use error message if we have it } else { - snprintf(error, sizeof(error), "Scheduled command %s failed with error %s", cmd, Command::return_code_string(return_code).c_str()); + snprintf(error, sizeof(error), "Schedule %s: command %s failed with error %s", name, cmd, Command::return_code_string(return_code).c_str()); } emsesp::EMSESP::logger().warning(error); @@ -381,7 +377,7 @@ bool WebSchedulerService::onChange(const char * cmd) { #ifdef EMESESP_DEBUG // emsesp::EMSESP::logger().debug(scheduleItem.cmd.c_str()); #endif - cmd_ok |= command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); + cmd_ok |= command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); } } return cmd_ok; @@ -396,7 +392,7 @@ void WebSchedulerService::condition() { #endif if (!match.empty() && match[0] == '1') { if (scheduleItem.retry_cnt == 0xFF) { // default unswitched - scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 1 : 0xFF; + scheduleItem.retry_cnt = command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 1 : 0xFF; } } else if (scheduleItem.retry_cnt == 1) { scheduleItem.retry_cnt = 0xFF; @@ -429,7 +425,7 @@ void WebSchedulerService::loop() { if (last_tm_min == -2) { for (ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0) { - scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 0xFF : 0; + scheduleItem.retry_cnt = command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 0xFF : 0; } } last_tm_min = -1; // startup done, now use for RTC @@ -442,12 +438,13 @@ void WebSchedulerService::loop() { // retry startup commands not yet executed if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0 && scheduleItem.retry_cnt < MAX_STARTUP_RETRIES) { - scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : scheduleItem.retry_cnt + 1; + scheduleItem.retry_cnt = + command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : scheduleItem.retry_cnt + 1; } // scheduled timer commands if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0 && (uptime_min % scheduleItem.elapsed_min == 0)) { - command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); + command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); } } last_uptime_min = uptime_min; @@ -464,7 +461,7 @@ void WebSchedulerService::loop() { for (const ScheduleItem & scheduleItem : *scheduleItems_) { uint8_t dow = scheduleItem.flags & SCHEDULEFLAG_SCHEDULE_TIMER ? 0 : scheduleItem.flags; if (scheduleItem.active && (real_dow & dow) && real_min == scheduleItem.elapsed_min) { - command(scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); + command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()); } } last_tm_min = tm->tm_min; @@ -502,41 +499,41 @@ void WebSchedulerService::test() { // should output 'locale is en' test_value = "\"locale is \"system/settings/locale"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); // test with negative value // should output 'rssi is -23' test_value = "\"rssi is \"0+system/network/rssi"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); // should output 'rssi is -23 dbm' test_value = "\"rssi is \"(system/network/rssi)\" dBm\""; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "(custom/seltemp/value)"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "\"seltemp=\"(custom/seltemp/value)"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "(custom/seltemp)"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "(boiler/outdoortemp)"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "boiler/flowtempoffset"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "(boiler/flowtempoffset/value)"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); test_value = "(boiler/storagetemp1/value)"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); // (14 - 40) * 2.8 + 5 = -67.8 test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; - command(test_cmd.c_str(), compute(test_value).c_str()); + command("test", test_cmd.c_str(), compute(test_value).c_str()); } #endif diff --git a/src/web/WebSchedulerService.h b/src/web/WebSchedulerService.h index 042298d61..b0a50a059 100644 --- a/src/web/WebSchedulerService.h +++ b/src/web/WebSchedulerService.h @@ -73,7 +73,7 @@ class WebSchedulerService : public StatefulService { #ifndef EMSESP_STANDALONE private: #endif - bool command(const char * cmd, const char * data); + bool command(const char * name, const char * cmd, const char * data); void condition(); HttpEndpoint _httpEndpoint;