diff --git a/interface/package.json b/interface/package.json index ba8e0301b..06a9f0717 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.11", + "@types/node": "^20.14.12", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", @@ -47,7 +47,7 @@ "react-router-dom": "^6.25.1", "react-toastify": "^10.0.5", "typesafe-i18n": "^5.26.2", - "typescript": "^5.5.3" + "typescript": "^5.5.4" }, "devDependencies": { "@babel/core": "^7.24.9", @@ -59,7 +59,7 @@ "concurrently": "^8.2.2", "eslint": "^9.7.0", "eslint-config-prettier": "^9.1.0", - "preact": "^10.22.1", + "preact": "^10.23.0", "prettier": "^3.3.3", "rollup-plugin-visualizer": "^5.12.0", "terser": "^5.31.3", diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index e3e1f679a..9e8d97327 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -9,7 +9,6 @@ import DeviceHubIcon from '@mui/icons-material/DeviceHub'; import DirectionsBusIcon from '@mui/icons-material/DirectionsBus'; import LogoDevIcon from '@mui/icons-material/LogoDev'; import MemoryIcon from '@mui/icons-material/Memory'; -import PermScanWifiIcon from '@mui/icons-material/PermScanWifi'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import RefreshIcon from '@mui/icons-material/Refresh'; import RouterIcon from '@mui/icons-material/Router'; @@ -34,7 +33,6 @@ import { import * as SystemApi from 'api/system'; -import * as EMSESP from 'app/main/api'; import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova'; import { busConnectionStatus } from 'app/main/types'; diff --git a/interface/yarn.lock b/interface/yarn.lock index d82e9f326..a67fdcdba 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -1839,12 +1839,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.14.11": - version: 20.14.11 - resolution: "@types/node@npm:20.14.11" +"@types/node@npm:^20.14.12": + version: 20.14.12 + resolution: "@types/node@npm:20.14.12" dependencies: undici-types: "npm:~5.26.4" - checksum: 10c0/5306becc0ff41d81b1e31524bd376e958d0741d1ce892dffd586b9ae0cb6553c62b0d62abd16da8bea6b9a2c17572d360450535d7c073794b0cef9cb4e39691e + checksum: 10c0/59bc5fa11fdd23fd517f859063118f54a1ab53d3399ef63c926f8902429d7453abc0db22ef4b0a6110026b6ab81b6472fee894e1d235c24b01a0b3e10cfae0bb languageName: node linkType: hard @@ -2085,7 +2085,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.11" + "@types/node": "npm:^20.14.12" "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" "@types/react-router-dom": "npm:^5.3.3" @@ -2098,7 +2098,7 @@ __metadata: jwt-decode: "npm:^4.0.0" lodash-es: "npm:^4.17.21" mime-types: "npm:^2.1.35" - preact: "npm:^10.22.1" + preact: "npm:^10.23.0" prettier: "npm:^3.3.3" react: "npm:latest" react-dom: "npm:latest" @@ -2109,7 +2109,7 @@ __metadata: rollup-plugin-visualizer: "npm:^5.12.0" terser: "npm:^5.31.3" typesafe-i18n: "npm:^5.26.2" - typescript: "npm:^5.5.3" + typescript: "npm:^5.5.4" typescript-eslint: "npm:8.0.0-alpha.10" vite: "npm:^5.3.4" vite-plugin-imagemin: "npm:^0.6.1" @@ -6070,10 +6070,10 @@ __metadata: languageName: node linkType: hard -"preact@npm:^10.22.1": - version: 10.22.1 - resolution: "preact@npm:10.22.1" - checksum: 10c0/9163b97d6fc0ce6b945ed77695d00c4fa07e317d0723e7b9d10c748153d30596abab8b26861ae45591e47bff25515da91406ce7f1c9e66cd9cac7e7f6c927930 +"preact@npm:^10.23.0": + version: 10.23.0 + resolution: "preact@npm:10.23.0" + checksum: 10c0/00bcf490ce13c0fd70e3d2e2418466419b31f201b28d35f173d7ddcb66b421a3f6ca569541de581cbbf4ec7b2adef3f391d631dca7a4fd86708d81ca08259c1c languageName: node linkType: hard @@ -7297,23 +7297,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.5.3": - version: 5.5.3 - resolution: "typescript@npm:5.5.3" +"typescript@npm:^5.5.4": + version: 5.5.4 + resolution: "typescript@npm:5.5.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/f52c71ccbc7080b034b9d3b72051d563601a4815bf3e39ded188e6ce60813f75dbedf11ad15dd4d32a12996a9ed8c7155b46c93a9b9c9bad1049766fe614bbdd + checksum: 10c0/422be60f89e661eab29ac488c974b6cc0a660fb2228003b297c3d10c32c90f3bcffc1009b43876a082515a3c376b1eefcce823d6e78982e6878408b9a923199c languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.5.3#optional!builtin": - version: 5.5.3 - resolution: "typescript@patch:typescript@npm%3A5.5.3#optional!builtin::version=5.5.3&hash=b45daf" +"typescript@patch:typescript@npm%3A^5.5.4#optional!builtin": + version: 5.5.4 + resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=b45daf" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/5a437c416251334deeaf29897157032311f3f126547cfdc4b133768b606cb0e62bcee733bb97cf74c42fe7268801aea1392d8e40988cdef112e9546eba4c03c5 + checksum: 10c0/10dd9881baba22763de859e8050d6cb6e2db854197495c6f1929b08d1eb2b2b00d0b5d9b0bcee8472f1c3f4a7ef6a5d7ebe0cfd703f853aa5ae465b8404bc1ba languageName: node linkType: hard diff --git a/mock-api/package.json b/mock-api/package.json index 082d68200..ceb516ae8 100644 --- a/mock-api/package.json +++ b/mock-api/package.json @@ -12,7 +12,6 @@ "dependencies": { "@msgpack/msgpack": "^2.8.0", "@types/multer": "^1.4.11", - "compression": "^1.7.4", "express": "^4.19.2", "itty-router": "^5.0.17", "multer": "^1.4.5-lts.1" diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index 39902f7f1..f36231f73 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -705,13 +705,13 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int output["api_data"] = data; // always as a string return true; } - return EMSESP::return_not_found(output, "attribute", sensor_name); // not found + return EMSESP::return_not_found(output, attribute_s, sensor_name); // not found } return true; // found a match, exit } } - return EMSESP::return_not_found(output, cmd, F_(analogsensor)); // not found + return false; // not found } void AnalogSensor::addSensorJson(JsonObject output, const Sensor & sensor) { diff --git a/src/command.cpp b/src/command.cpp index 851d8682f..b0ed8aa33 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -63,7 +63,6 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec int8_t id_n = -1; // default hc // check for a device as first item in the path - // if its not a known device (thermostat, boiler etc) look for any special MQTT subscriptions const char * device_s = nullptr; if (!num_paths) { // we must look for the device in the JSON body @@ -72,7 +71,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec } } else { // extract it from the path - device_s = p.paths().front().c_str(); // get the device (boiler, thermostat, system etc) + device_s = p.paths().front().c_str(); // get the device type name (boiler, thermostat, system etc) } // validate the device, make sure it exists @@ -172,7 +171,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec } uint8_t device_type = EMSdevice::device_name_2_device_type(device_p); - if (CommandRet::OK != Command::call(device_type, data_s, "", true, id_d, output)) { + if (Command::call(device_type, data_s, "", true, id_d, output) != CommandRet::OK) { return CommandRet::INVALID; } @@ -340,15 +339,16 @@ 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 + // see if there is a command registered and it's valid auto cf = find_command(device_type, device_id, cmd, flag); if (!cf) { - std::string err = std::string("unknown command ") + cmd; - LOG_WARNING("Command failed: %s", err.c_str()); - - // if we don't alread have a message set, set it to invalid command - if (!output["message"]) { + // if we don't already have a message set, set it to invalid command + if (output["message"]) { + LOG_WARNING("Command failed: %s", output["message"].as()); + } else { + std::string err = "no " + std::string(cmd) + " in " + dname; output["message"] = err; + LOG_WARNING("Command failed: %s", err.c_str()); } return CommandRet::ERROR; } @@ -382,7 +382,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * 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... + // call the command... return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR; } } @@ -400,12 +400,13 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * LOG_WARNING(error); } else { if (single_command) { - LOG_DEBUG(("%sCalling command %s"), ro.c_str(), info_s); + // log as DEBUG (TRACE) regarless if compiled with EMSESP_DEBUG + logger_.debug(("%sCalled 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); + LOG_INFO(("%sCalled 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); + LOG_INFO(("%sCalled command %s with value %s"), ro.c_str(), info_s, value); } } } @@ -506,10 +507,11 @@ std::string Command::tagged_cmd(const std::string & cmd, const uint8_t flag) { // list all commands for a specific device, output as json bool Command::list(const uint8_t device_type, JsonObject output) { - // force add info and commands for those non-EMS devices + // check of it a 'commands' command if (device_type == EMSdevice::DeviceType::TEMPERATURESENSOR || device_type == EMSdevice::DeviceType::ANALOGSENSOR) { output[F_(info)] = Helpers::translated_word(FL_(info_cmd)); output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd)); + output[F_(values)] = Helpers::translated_word(FL_(values_cmd)); } else if (cmdfunctions_.empty()) { output["message"] = "no commands available"; return false; @@ -544,13 +546,14 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo } } - // non EMS devices always have an info and commands command + // non EMS devices always have an info, commands and values bool show_info = (device_type == EMSdevice::DeviceType::TEMPERATURESENSOR || device_type == EMSdevice::DeviceType::ANALOGSENSOR || device_type == EMSdevice::DeviceType::SCHEDULER || device_type == EMSdevice::DeviceType::CUSTOM); if (!verbose && show_info) { sorted_cmds.push_back(F_(info)); sorted_cmds.push_back(F_(commands)); + sorted_cmds.push_back(F_(values)); } sorted_cmds.sort(); // sort them @@ -570,9 +573,11 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo // we hard code 'info' and 'commands' commands so print them first if (show_info) { - shell.printf(" info:\t\t\t\t%slists all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN); + shell.printf(" info:\t\t\t\t%slist all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN); shell.println(COLOR_RESET); - shell.printf(" commands:\t\t\t%slists all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN); + shell.printf(" commands:\t\t\t%slist all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN); + shell.println(COLOR_RESET); + shell.printf(" values:\t\t\t%slist all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN); shell.println(COLOR_RESET); } @@ -612,7 +617,6 @@ bool Command::device_has_commands(const uint8_t device_type) { return true; // we always have System } - // if there are no entries to scheduler/custom/temperaturesensor/analogsensor, don't error but return a message if (device_type == EMSdevice::DeviceType::SCHEDULER) { return true; } @@ -631,7 +635,6 @@ bool Command::device_has_commands(const uint8_t device_type) { for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice && (emsdevice->device_type() == device_type)) { - // device found, now see if it has any commands for (const auto & cf : cmdfunctions_) { if (cf.device_type_ == device_type) { return true; diff --git a/src/console.cpp b/src/console.cpp index 1067f592f..53eb61691 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -575,17 +575,17 @@ static void setup_commands(std::shared_ptr & commands) { return; } else { // show message if no data returned (e.g. for analogsensor, temperaturesensor, custom) - shell.println("No data."); + shell.println("Command executed. Check log for messages."); return; } - } - - if (return_code == CommandRet::NOT_FOUND) { + } else if (return_code == CommandRet::NOT_FOUND) { shell.println("Unknown command"); shell.print("Available commands are: "); Command::show(shell, device_type, false); // non-verbose mode - } else if (return_code != CommandRet::OK) { - shell.printfln("Bad syntax (error code %d)", return_code); + } else if ((return_code == CommandRet::ERROR) || (return_code == CommandRet::FAIL)) { + shell.printfln("Bad syntax. Check arguments."); + } else { + shell.printfln("Command failed with error code %d", return_code); } }, [](Shell & shell, const std::vector & current_arguments, const std::string & next_argument) -> std::vector { diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index d2f255ef2..ac8902250 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -212,9 +212,6 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) { if (!strcmp(lowtopic, F_(thermostat))) { return DeviceType::THERMOSTAT; } - if (!strcmp(lowtopic, F_(system))) { - return DeviceType::SYSTEM; - } if (!strcmp(lowtopic, F_(scheduler))) { return DeviceType::SCHEDULER; } @@ -227,12 +224,6 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) { if (!strcmp(lowtopic, F_(mixer))) { return DeviceType::MIXER; } - if (!strcmp(lowtopic, F_(temperaturesensor))) { - return DeviceType::TEMPERATURESENSOR; - } - if (!strcmp(lowtopic, F_(analogsensor))) { - return DeviceType::ANALOGSENSOR; - } if (!strcmp(lowtopic, F_(switch))) { return DeviceType::SWITCH; } @@ -248,9 +239,6 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) { if (!strcmp(lowtopic, F_(heatsource))) { return DeviceType::HEATSOURCE; } - if (!strcmp(lowtopic, F_(custom))) { - return DeviceType::CUSTOM; - } if (!strcmp(lowtopic, F_(ventilation))) { return DeviceType::VENTILATION; } @@ -261,6 +249,23 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) { return DeviceType::POOL; } + // non EMS + if (!strcmp(lowtopic, F_(custom))) { + return DeviceType::CUSTOM; + } + if (!strcmp(lowtopic, F_(temperaturesensor))) { + return DeviceType::TEMPERATURESENSOR; + } + if (!strcmp(lowtopic, F_(analogsensor))) { + return DeviceType::ANALOGSENSOR; + } + if (!strcmp(lowtopic, F_(scheduler))) { + return DeviceType::SCHEDULER; + } + if (!strcmp(lowtopic, F_(system))) { + return DeviceType::SYSTEM; + } + return DeviceType::UNKNOWN; } @@ -1598,18 +1603,14 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t // if we're filtering on an attribute, go find it if (attribute_s) { -#if defined(EMSESP_DEBUG) - EMSESP::logger().debug("[DEBUG] fetching single attribute %s", attribute_s); -#endif if (json.containsKey(attribute_s)) { std::string data = json[attribute_s].as(); output.clear(); output["api_data"] = data; // always as string return true; } - return EMSESP::return_not_found(output, "attribute", command_s); // not found + return EMSESP::return_not_found(output, attribute_s, command_s); // not found } - return true; } } diff --git a/src/emsesp.cpp b/src/emsesp.cpp index ba181a051..c49316807 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -742,8 +742,8 @@ void EMSESP::publish_response(std::shared_ptr telegram) { buffer = nullptr; } -// builds json with the detail of each value, for a given EMS device -// for other types like sensors, scheduler, custom entities it will process single commands like 'info', 'values', 'commands', 'entities'... +// builds json with the detail of each value, for a given device type +// device type can be EMS devices looking for entities, or a sensor/scheduler/custom entity etc looking for values extracted from the info command 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; @@ -794,7 +794,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), "no %s in %s", msg, cmd); output["message"] = error; return false; } @@ -1312,7 +1312,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const default_name = "Cascaded Controller"; device_type = DeviceType::CONTROLLER; } else { - LOG_WARNING("Unrecognized EMS device (device ID 0x%02X, no product ID). Please report on GitHub.", device_id); + LOG_WARNING("Unrecognized EMS device (deviceID 0x%02X, no productID). Please report on GitHub.", device_id); return false; } } @@ -1363,13 +1363,9 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const device_type, F_(values), [device_type](const char * value, const int8_t id, JsonObject output) { - return EMSdevice::export_values(device_type, - output, - id, - EMSdevice::OUTPUT_TARGET::API_SHORTNAMES); // HIDDEN command showing short names, used in e.g. /api/boiler + return EMSdevice::export_values(device_type, output, id, EMSdevice::OUTPUT_TARGET::API_SHORTNAMES); }, - nullptr, - CommandFlag::HIDDEN); // this command is hidden + FL_(values_cmd)); Command::add( device_type, F_(commands), diff --git a/src/locale_common.h b/src/locale_common.h index 0426b05fe..0f28f5a17 100644 --- a/src/locale_common.h +++ b/src/locale_common.h @@ -151,7 +151,7 @@ MAKE_WORD_CUSTOM(nvs_optional, "[nvs]") MAKE_WORD_CUSTOM(offset_optional, "[offset]") MAKE_WORD_CUSTOM(length_optional, "[length]") MAKE_WORD_CUSTOM(typeid_mandatory, "") -MAKE_WORD_CUSTOM(deviceid_mandatory, "") +MAKE_WORD_CUSTOM(deviceid_mandatory, "") MAKE_WORD_CUSTOM(device_type_optional, "[device]") MAKE_WORD_CUSTOM(invalid_log_level, "Invalid log level") MAKE_WORD_CUSTOM(log_level_optional, "[level]") diff --git a/src/locale_translations.h b/src/locale_translations.h index 024c5b0cf..baf5c6100 100644 --- a/src/locale_translations.h +++ b/src/locale_translations.h @@ -59,7 +59,7 @@ 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, "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(info_cmd, "list all values (verbose)", "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 @@ -74,8 +74,9 @@ MAKE_WORD_TRANSLATION(schedule_cmd, "enable schedule item", "Aktiviere Zeitplan" MAKE_WORD_TRANSLATION(entity_cmd, "set custom value on ems", "Sende eigene Entitäten zu EMS", "verstuur custom waarde naar EMS", "", "wyślij własną wartość na EMS", "", "", "emp üzerinde özel değer ayarla", "imposta valori personalizzati su EMS", "nastaviť vlastnú hodnotu na ems") // TODO translate MAKE_WORD_TRANSLATION(commands_response, "get response", "Hole Antwort", "Verzoek om antwoord", "", "uzyskaj odpowiedź", "", "", "gelen cevap", "", "získať odpoveď") // TODO translate MAKE_WORD_TRANSLATION(coldshot_cmd, "send a cold shot of water", "", "", "", "uruchom tryśnięcie zimnej wody", "", "", "soğuk su gönder", "", "pošlite studenú dávku vody") // TODO translate -MAKE_WORD_TRANSLATION(allvalues_cmd, "output all values", "", "", "", "wyświetl wszystkie wartości", "", "", "", "", "vypísať všetky hodnoty") // TODO translate +MAKE_WORD_TRANSLATION(allvalues_cmd, "output all values in system", "", "", "", "wyświetl wszystkie wartości", "", "", "", "", "vypísať všetky hodnoty") // TODO translate MAKE_WORD_TRANSLATION(message_cmd, "send a message", "", "", "", "", "", "", "", "", "") // TODO translate +MAKE_WORD_TRANSLATION(values_cmd, "list all values", "", "", "", "", "", "", "", "", "") // TODO translate // tags MAKE_WORD_TRANSLATION(tag_hc1, "hc1", "HK1", "hc1", "VK1", "OG1", "hc1", "hc1", "ID1", "hc1", "hc1") diff --git a/src/system.cpp b/src/system.cpp index 25ffdc458..98f5e8555 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -144,24 +144,20 @@ bool System::command_fetch(const char * value, const int8_t id) { if (value_s == "all") { LOG_INFO("Requesting data from EMS devices"); EMSESP::fetch_device_values(); - return true; } else if (value_s == (F_(boiler))) { EMSESP::fetch_device_values_type(EMSdevice::DeviceType::BOILER); - return true; } else if (value_s == (F_(thermostat))) { EMSESP::fetch_device_values_type(EMSdevice::DeviceType::THERMOSTAT); - return true; } else if (value_s == (F_(solar))) { EMSESP::fetch_device_values_type(EMSdevice::DeviceType::SOLAR); - return true; } else if (value_s == (F_(mixer))) { EMSESP::fetch_device_values_type(EMSdevice::DeviceType::MIXER); - return true; } + } else { + EMSESP::fetch_device_values(); // default if no name or id is given } - EMSESP::fetch_device_values(); // default if no name or id is given - return true; + return true; // always true } // mqtt publish @@ -857,7 +853,6 @@ void System::commands_init() { Command::add(EMSdevice::DeviceType::SYSTEM, F("response"), System::command_response, FL_(commands_response)); Command::add(EMSdevice::DeviceType::SYSTEM, F("allvalues"), System::command_allvalues, FL_(allvalues_cmd)); - // MQTT subscribe "ems-esp/system/#" Mqtt::subscribe(EMSdevice::DeviceType::SYSTEM, "system/#", nullptr); // use empty function callback } @@ -1295,14 +1290,25 @@ bool System::saveSettings(const char * filename, const char * section, JsonObjec return false; // not found } +// return back a system value bool System::get_value_info(JsonObject root, const char * command) { if (command == nullptr || strlen(command) == 0) { LOG_ERROR("empty system command"); return false; } + // cmd is lower case of the command char cmd[COMMAND_MAX_LENGTH]; strlcpy(cmd, Helpers::toLower(command).c_str(), sizeof(cmd)); + + // fetch all the data from the system + (void)command_info("", 0, root); + + // check for hardcoded "info" + if (!strcmp(cmd, F_(info))) { + return true; + } + char * val = strstr(cmd, "/value"); if (val) { val[0] = '\0'; @@ -1314,14 +1320,6 @@ bool System::get_value_info(JsonObject root, const char * command) { slash++; } - // fetch all the data from the system - (void)command_info("", 0, root); - - // check for hardcoded "info" - if (Helpers::toLower(cmd) == F_(info)) { - return true; - } - std::string s; // Loop through all the key-value pairs in root to find the key, case independent if (slash) { // search the top level first @@ -1344,8 +1342,9 @@ bool System::get_value_info(JsonObject root, const char * command) { } } + root.clear(); // clear json, we only one a single value + if (!s.empty()) { - root.clear(); if (val) { root["api_data"] = s; } else { @@ -1354,7 +1353,7 @@ bool System::get_value_info(JsonObject root, const char * command) { return true; // found } - return EMSESP::return_not_found(root, "data", command); // not found + return false; // not found } // export status information including the device information @@ -1623,7 +1622,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output #if defined(EMSESP_TEST) // run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler bool System::command_test(const char * value, const int8_t id) { - return Test::test(value, id); + if (value) { + return Test::test(value, id); + } else { + return false; + } } #endif diff --git a/src/telegram.cpp b/src/telegram.cpp index 45d99d2c6..07c5f406c 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -554,7 +554,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt // send a Tx telegram to request data from an EMS device void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length, const bool front) { - LOG_DEBUG("Tx read request to device 0x%02X for type ID 0x%02X", dest, type_id); + LOG_DEBUG("Tx read request to deviceID 0x%02X for typeID 0x%02X", dest, type_id); uint8_t message_data = (type_id > 0xFF) ? (EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 2) : EMS_MAX_TELEGRAM_MESSAGE_LENGTH; if (length > 0 && length < message_data) { diff --git a/src/telegram.h b/src/telegram.h index 72c4ff0de..f9e4bd966 100644 --- a/src/telegram.h +++ b/src/telegram.h @@ -32,8 +32,13 @@ #include "helpers.h" +#if defined(EMSESP_STANDALONE) +#define MAX_RX_TELEGRAMS 100 // size of Rx queue +#define MAX_TX_TELEGRAMS 200 // size of Tx queue +#else #define MAX_RX_TELEGRAMS 10 // size of Rx queue #define MAX_TX_TELEGRAMS 100 // size of Tx queue +#endif // default values for null values static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean diff --git a/src/temperaturesensor.cpp b/src/temperaturesensor.cpp index 6919cc054..f27111c51 100644 --- a/src/temperaturesensor.cpp +++ b/src/temperaturesensor.cpp @@ -402,13 +402,13 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons output["api_data"] = data; // always as string return true; } - return EMSESP::return_not_found(output, "attribute", sensor_name); // not found + return EMSESP::return_not_found(output, attribute_s, sensor_name); // not found } return true; // found a match, exit } } - return EMSESP::return_not_found(output, cmd, F_(temperaturesensor)); // not found + return false; // not found } void TemperatureSensor::addSensorJson(JsonObject output, const Sensor & sensor) { diff --git a/src/test/test.cpp b/src/test/test.cpp index c79aea1a5..2c5e4ed73 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -963,27 +963,38 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // load devices test("boiler"); - // test("thermostat"); - test("2thermostats"); + 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/thermostat/commands"); + shell.invoke_command("call system fetch"); + request.url("/api/system/fetch"); + EMSESP::webAPIService.webAPIService(&request); + + // request.url("/api/system"); + // EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/system/system/version"); + // EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/system/bad"); // EMSESP::webAPIService.webAPIService(&request); - request.url("/api/thermostat/hc1/mode2"); - EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/boiler"); + // EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/boiler/bad"); + // EMSESP::webAPIService.webAPIService(&request); - request.url("/api/thermostat/hc2/mode"); - EMSESP::webAPIService.webAPIService(&request); - - request.url("/api/thermostat/hc1/mode"); - EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/custom"); + // EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/custom/seltemp"); + // EMSESP::webAPIService.webAPIService(&request); + // request.url("/api/custom/bad"); + // EMSESP::webAPIService.webAPIService(&request); } else { EMSESP::webCustomEntityService.test(); // custom entities @@ -991,6 +1002,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const EMSESP::temperaturesensor_.test(); // add temperature sensors EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions + request.method(HTTP_GET); + // boiler request.url("/api/boiler"); EMSESP::webAPIService.webAPIService(&request); @@ -1018,22 +1031,34 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // custom request.url("/api/custom"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/custom/info"); + EMSESP::webAPIService.webAPIService(&request); request.url("/api/custom/seltemp"); EMSESP::webAPIService.webAPIService(&request); // system request.url("/api/system"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/system/info"); + EMSESP::webAPIService.webAPIService(&request); request.url("/api/system/settings/locale"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/system/fetch"); + EMSESP::webAPIService.webAPIService(&request); // scheduler + request.url("/api/scheduler"); + EMSESP::webAPIService.webAPIService(&request); request.url("/api/scheduler/info"); EMSESP::webAPIService.webAPIService(&request); request.url("/api/scheduler/test_scheduler"); EMSESP::webAPIService.webAPIService(&request); // temperaturesensor + request.url("/api/temperaturesensor"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/temperaturesensor/info"); + EMSESP::webAPIService.webAPIService(&request); request.url("/api/temperaturesensor/test_sensor2"); EMSESP::webAPIService.webAPIService(&request); request.url("/api/temperaturesensor/0B_0C0D_0E0F_1011"); @@ -1044,11 +1069,40 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // analogsensor request.url("/api/analogsensor"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/analogsensor/info"); + EMSESP::webAPIService.webAPIService(&request); request.url("/api/analogsensor/test_analog1"); EMSESP::webAPIService.webAPIService(&request); request.url("/api/analogsensor/test_analog1/offset"); EMSESP::webAPIService.webAPIService(&request); + // system calls with POST + request.method(HTTP_POST); + + // these next 3 should return empty JSON in their response + // but there will be a log message + char data[] = "{\"cmd\":\"send\",\"data\":\"0B 90 FF 13 01 01 B9 01\"}"; + deserializeJson(doc, data); + json = doc.as(); + request.url("/api/system"); + EMSESP::webAPIService.webAPIService(&request, json); + + char data2[] = "{\"device\":\"system\", \"cmd\":\"send\",\"value\":\"0B 90 FF 13 01 01 B9 02\"}"; + deserializeJson(doc, data2); + json = doc.as(); + request.url("/api"); + EMSESP::webAPIService.webAPIService(&request, json); + + char data4[] = "{\"value\":\"0B 90 FF 13 01 01 B9 03\"}"; + deserializeJson(doc, data4); + json = doc.as(); + request.url("/api/system/send"); + EMSESP::webAPIService.webAPIService(&request, json); + + // console commands + shell.invoke_command("call system fetch"); + shell.invoke_command("call system send \"0B 90 FF 13 01 01 B9\""); + // // This next batch should all fail // @@ -1070,14 +1124,24 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // system request.url("/api/system/settings/locale2"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/system/settings2"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/system/settings2/locale2"); + EMSESP::webAPIService.webAPIService(&request); // scheduler request.url("/api/scheduler/test_scheduler2"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/scheduler/test_scheduler/val"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/scheduler/test_scheduler2/val2"); + EMSESP::webAPIService.webAPIService(&request); // custom request.url("/api/custom/seltemp2"); EMSESP::webAPIService.webAPIService(&request); + request.url("/api/custom/seltemp/val"); + EMSESP::webAPIService.webAPIService(&request); // temperaturesensor request.url("/api/temperaturesensor/test_sensor20"); @@ -1092,7 +1156,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const EMSESP::webAPIService.webAPIService(&request); request.url("/api/analogsensor/test_analog10"); EMSESP::webAPIService.webAPIService(&request); - request.url("/api/analogsensor/test_analog10/bad"); + request.url("/api/analogsensor/test_analog10/bad2"); EMSESP::webAPIService.webAPIService(&request); } } diff --git a/src/version.h b/src/version.h index 7ba5e93f1..9f72e133d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.0-dev.26" +#define EMSESP_APP_VERSION "3.7.0-dev.27" diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index af835df54..5880f4c98 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -55,8 +55,9 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant j // HTTP GET JsonDocument input_doc; // has no body JSON so create dummy as empty input object input = input_doc.to(); + } else { - // HTTP_POST | HTTP_PUT | HTTP_PATCH + // HTTP_POST input = json.as(); // extract values from the json. these will be used as default values } parse(request, input); @@ -134,7 +135,6 @@ 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); @@ -158,7 +158,6 @@ 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); diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 612f86b64..e1706ff9a 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -256,10 +256,12 @@ void WebCustomEntityService::show_values(JsonObject output) { // process json output for info/commands and value_info bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) { - // if it's commands... + // check of it a 'commands' command if (Helpers::toLower(cmd) == F_(commands)) { output[F_(info)] = Helpers::translated_word(FL_(info_cmd)); output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd)); + output[F_(values)] = Helpers::translated_word(FL_(values_cmd)); + for (const auto & entity : *customEntityItems_) { if (entity.writeable) { output[entity.name] = "custom entity"; @@ -325,7 +327,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) output["api_data"] = data; // always as string return true; } - return EMSESP::return_not_found(output, "attribute", command_s); // not found + return EMSESP::return_not_found(output, attribute_s, command_s); // not found } } @@ -334,7 +336,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) } } - return EMSESP::return_not_found(output, cmd, F_(custom)); // not found + return false; // not found } // publish single value diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index f016a0f5e..86239f466 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -239,8 +239,6 @@ void WebCustomizationService::writeDeviceName(AsyncWebServerRequest * request, J uint8_t device_id = emsdevice->device_id(); auto custom_name = json["name"].as(); - // emsesp::EMSESP::logger().info("Found Device ID: %d, Product ID: %d", device_id, product_id); - // updates current record or creates a new one bool entry_exists = false; update([&](WebCustomization & settings) { diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 7cfb6b2f0..a423e5d0b 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -245,7 +245,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar return_code = Command::call(device_type, cmd, data.as() ? "true" : "false", true, id, output); } - // write debug + // write log if (return_code != CommandRet::OK) { EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code)); } else { diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index c52613cf5..ad3fc6cb7 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -136,6 +136,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { if (Helpers::toLower(cmd) == F_(commands)) { output[F_(info)] = Helpers::translated_word(FL_(info_cmd)); output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd)); + output[F_(values)] = Helpers::translated_word(FL_(values_cmd)); for (const ScheduleItem & scheduleItem : *scheduleItems_) { if (!scheduleItem.name.empty()) { @@ -196,21 +197,25 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { output["readable"] = true; output["writeable"] = true; output["visible"] = true; + break; } } - if (attribute_s && output.containsKey(attribute_s)) { - std::string data = output[attribute_s].as(); - output.clear(); - output["api_data"] = data; // always as a string - return true; + if (attribute_s) { + if (output.containsKey(attribute_s)) { + std::string data = output[attribute_s].as(); + output.clear(); + output["api_data"] = data; // always as a string + return true; + } + return EMSESP::return_not_found(output, attribute_s, command_s); // not found } if (output.size()) { return true; } - return EMSESP::return_not_found(output, cmd, F_(scheduler)); // not found + return false; // not found } // publish single value @@ -604,7 +609,6 @@ void WebSchedulerService::test() { test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5"; command("test10", test_cmd.c_str(), compute(test_value).c_str()); - // TODO add some more HTTP/URI tests test_cmd = "{\"method\":\"POST\",\"url\":\"http://192.168.1.42:8123/api/services/script/test_notify2\", \"header\":{\"authorization\":\"Bearer " "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMmNlYWI5NDgzMmI0ODE2YWQ2NzU4MjkzZDE2YWMxZSIsImlhdCI6MTcyMTM5MTI0NCwiZXhwIjoyMDM2NzUxMjQ0fQ." "S5sago1tEI6lNhrDCO0dM_WsVQHkD_laAjcks8tWAqo\"}}";