From 80aa1e65b7b3c4c2262216c6ae3033edcb55e651 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Tue, 30 Jul 2024 15:05:11 +0200 Subject: [PATCH] refactor common commands #1897 --- CHANGELOG_LATEST.md | 1 + src/analogsensor.cpp | 65 ++---- src/analogsensor.h | 1 + src/command.cpp | 65 ++++-- src/command.h | 2 + src/emsdevice.cpp | 341 ++++++++++++++--------------- src/emsdevice.h | 6 +- src/emsesp.cpp | 47 +--- src/emsesp.h | 2 - src/system.cpp | 103 +++++---- src/system.h | 2 +- src/temperaturesensor.cpp | 64 ++---- src/temperaturesensor.h | 2 +- src/version.h | 2 +- src/web/WebCustomEntityService.cpp | 97 ++++---- src/web/WebCustomEntityService.h | 1 + src/web/WebSchedulerService.cpp | 108 ++++----- src/web/WebSchedulerService.h | 1 + 18 files changed, 397 insertions(+), 513 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 41c203aa4..3caf2ce47 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -33,6 +33,7 @@ - scheduler can use web get/post for values and commands [#1806](https://github.com/emsesp/EMS-ESP32/issues/1806) - RT800 remote emulation [#1867](https://github.com/emsesp/EMS-ESP32/issues/1867) - RC310 cooling parameters [#1857](https://github.com/emsesp/EMS-ESP32/issues/1857) +- command `api/device/entities` [#1897](https://github.com/emsesp/EMS-ESP32/issues/1897) ## Fixed diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index c15d66e29..d317c2844 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -648,78 +648,39 @@ void AnalogSensor::publish_values(const bool force) { // called from emsesp.cpp for commands // searches sensor by name bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int8_t id) { - // check of it a 'commands' command - if (Helpers::toLower(cmd) == F_(commands)) { - return Command::list(EMSdevice::DeviceType::ANALOGSENSOR, output); - } - if (sensors_.empty()) { return true; // no sensors, return true } - uint8_t show_all = 0; - if (Helpers::hasValue(cmd)) { - show_all = (strncmp(cmd, F_(info), 4) == 0) ? 1 : (strncmp(cmd, F_(values), 6) == 0) ? 2 : 0; + if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) { + for (const auto & sensor : sensors_) { + output[sensor.name()] = sensor.value(); + } + return true; } - // see if we're showing all sensors - if (show_all) { + if (!strcmp(cmd, F_(entities))) { for (const auto & sensor : sensors_) { - if (show_all == 1) { - // info - JsonObject dataSensor = output[sensor.name()].to(); - addSensorJson(dataSensor, sensor); - } else { - // values, shortname version. Also used in 'system allvalues' - output[sensor.name()] = sensor.value(); - } + get_value_json(output[sensor.name()].to(), sensor); } return true; } // this is for a specific sensor - // make a copy of the string command for parsing, and lowercase it - char sensor_name[COMMAND_MAX_LENGTH] = {'\0'}; - char * attribute_s = nullptr; - strlcpy(sensor_name, Helpers::toLower(cmd).c_str(), sizeof(sensor_name)); - - // check specific attribute to fetch instead of the complete record - char * breakp = strchr(sensor_name, '/'); - if (breakp) { - *breakp = '\0'; - attribute_s = breakp + 1; - } + const char * attribute_s = Command::get_attribute(cmd); for (const auto & sensor : sensors_) { - if (sensor_name == Helpers::toLower(sensor.name()) || Helpers::atoint(sensor_name) == sensor.gpio()) { - // add the details - addSensorJson(output, sensor); - - /* - // if someone wants gpio numbers - char gpio_str[9]; - snprintf(gpio_str, sizeof(gpio_str), "gpio_%02d", sensor.gpio()); - output[gpio_str] = sensor.value(); - */ - - // if we're filtering on an attribute, go find it - 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, sensor_name); // not found - } - return true; // found a match, exit + // match custom name or sensor GPIO + if (cmd == Helpers::toLower(sensor.name()) || Helpers::atoint(cmd) == sensor.gpio()) { + get_value_json(output, sensor); + return Command::set_attirbute(output, cmd, attribute_s); } } return false; // not found } -void AnalogSensor::addSensorJson(JsonObject output, const Sensor & sensor) { +void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) { output["gpio"] = sensor.gpio(); output["type"] = F_(number); output["analog"] = FL_(list_sensortype)[sensor.type()]; diff --git a/src/analogsensor.h b/src/analogsensor.h index 31c65b6c7..0401a5a44 100644 --- a/src/analogsensor.h +++ b/src/analogsensor.h @@ -171,6 +171,7 @@ class AnalogSensor { bool command_setvalue(const char * value, const int8_t gpio); void measure(); void addSensorJson(JsonObject output, const Sensor & sensor); + void get_value_json(JsonObject output, const Sensor & sensor); std::vector sensors_; // our list of sensors diff --git a/src/command.cpp b/src/command.cpp index c8e230964..4b667b51b 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -284,6 +284,29 @@ const char * Command::parse_command_string(const char * command, int8_t & id) { return command; } +// check if command contains an attribute +const char * Command::get_attribute(const char * cmd) { + char * breakp = strchr(cmd, '/'); + if (breakp) { + *breakp = '\0'; + return breakp + 1; + } + return nullptr; +} + +bool Command::set_attirbute(JsonObject output, const char * cmd, const char * attribute) { + if (attribute == nullptr) { + return true; + } + if (output.containsKey(attribute)) { + std::string data = output[attribute].as(); + output.clear(); + output["api_data"] = data; // always as a string + return true; + } + return EMSESP::return_not_found(output, attribute, cmd); // not found +} + // calls a command directly uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) { // create a temporary buffer @@ -297,10 +320,12 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // calls a command. Takes a json object for output. // id may be used to represent a heating circuit for example // returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed -uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject output) { - if (cmd == nullptr) { +uint8_t Command::call(const uint8_t device_type, const char * command, const char * value, const bool is_admin, const int8_t id, JsonObject output) { + if (command == nullptr) { return CommandRet::NOT_FOUND; } + char cmd[COMMAND_MAX_LENGTH]; + strcpy(cmd, Helpers::toLower(command).c_str()); auto dname = EMSdevice::device_type_2_device_name(device_type); // device name, not translated @@ -309,6 +334,9 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // or a special command like 'info', 'values', 'commands', 'entities' etc bool single_command = (!value || !strlen(value)); if (single_command) { + if (!strcmp(cmd, F_(commands))) { + return Command::list(device_type, output); + } if (EMSESP::get_device_value_info(output, cmd, id, device_type)) { // entity = cmd LOG_DEBUG("Fetched device entity/attributes for %s/%s", dname, cmd); return CommandRet::OK; @@ -319,17 +347,14 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // determine flags based on id (which is the tag) uint8_t flag = CommandFlag::CMD_FLAG_DEFAULT; - // info and values works with all tags, keep it default - if (std::string(cmd) != F_(values) && std::string(cmd) != F_(info)) { - if (id >= DeviceValueTAG::TAG_HC1 && id <= DeviceValueTAG::TAG_HC8) { - flag = CommandFlag::CMD_FLAG_HC; - } else if (id >= DeviceValueTAG::TAG_DHW1 && id <= DeviceValueTAG::TAG_DHW10) { - flag = CommandFlag::CMD_FLAG_DHW; - } else if (id >= DeviceValueTAG::TAG_HS1 && id <= DeviceValueTAG::TAG_HS16) { - flag = CommandFlag::CMD_FLAG_HS; - } else if (id >= DeviceValueTAG::TAG_AHS1 && id <= DeviceValueTAG::TAG_AHS1) { - flag = CommandFlag::CMD_FLAG_AHS; - } + if (id >= DeviceValueTAG::TAG_HC1 && id <= DeviceValueTAG::TAG_HC8) { + flag = CommandFlag::CMD_FLAG_HC; + } else if (id >= DeviceValueTAG::TAG_DHW1 && id <= DeviceValueTAG::TAG_DHW10) { + flag = CommandFlag::CMD_FLAG_DHW; + } else if (id >= DeviceValueTAG::TAG_HS1 && id <= DeviceValueTAG::TAG_HS16) { + flag = CommandFlag::CMD_FLAG_HS; + } else if (id >= DeviceValueTAG::TAG_AHS1 && id <= DeviceValueTAG::TAG_AHS1) { + flag = CommandFlag::CMD_FLAG_AHS; } // see if there is a command registered and it's valid @@ -500,15 +525,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) { - // 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; - } + // common commands + output[F_(info)] = Helpers::translated_word(FL_(info_cmd)); + output[F_(values)] = Helpers::translated_word(FL_(values_cmd)); + output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd)); + output[F_(entities)] = Helpers::translated_word(FL_(entities_cmd)); // create a list of commands we have registered, and sort them std::list sorted_cmds; diff --git a/src/command.h b/src/command.h index 38a54b405..c74e01c36 100644 --- a/src/command.h +++ b/src/command.h @@ -135,6 +135,8 @@ class Command { static uint8_t process(const char * path, const bool is_admin, const JsonObject input, JsonObject output); static const char * parse_command_string(const char * command, int8_t & id); + static const char * get_attribute(const char * cmd); + static bool set_attirbute(JsonObject output, const char * cmd, const char * attirbute); static const char * return_code_string(const uint8_t return_code); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 1f96767ed..7d654cd97 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -389,6 +389,7 @@ bool EMSdevice::has_cmd(const char * cmd, const int8_t id) const { // list of registered device entries // called from the command 'entities' +/* void EMSdevice::list_device_entries(JsonObject output) const { for (const auto & dv : devicevalues_) { auto fullname = dv.get_fullname(); @@ -414,6 +415,7 @@ void EMSdevice::list_device_entries(JsonObject output) const { } } } +*/ // list all the telegram type IDs for this device void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const { @@ -1435,187 +1437,182 @@ void EMSdevice::dump_telegram_info(std::vector & telegram_ // cmd is the endpoint or name of the device entity // returns false if failed, otherwise true bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t tag) { - JsonObject json = output; - - // make a copy of the string command for parsing - char command_s[30]; - strlcpy(command_s, cmd, sizeof(command_s)); - char * attribute_s = nullptr; - - // check specific attribute to fetch instead of the complete record - char * breakp = strchr(command_s, '/'); - if (breakp) { - *breakp = '\0'; - attribute_s = breakp + 1; + if (!strcmp(cmd, F_(info))) { + return export_values(device_type(), output, tag, EMSdevice::OUTPUT_TARGET::API_VERBOSE); + } + if (!strcmp(cmd, F_(values))) { + return export_values(device_type(), output, tag, EMSdevice::OUTPUT_TARGET::API_SHORTNAMES); + } + if (!strcmp(cmd, F_(entities))) { + char name[30]; + for (auto & dv : devicevalues_) { + if (tag < 0 || tag == dv.tag) { + strlcpy(name, tag < 0 ? tag_to_mqtt(dv.tag) : "", sizeof(name)); + strlcat(name, tag < 0 && dv.tag > 0 ? "." : "", sizeof(name)); + strlcat(name, dv.short_name, sizeof(name)); + get_value_json(output[name].to(), dv); + } + } + return true; } // search device value with this tag + const char * attribute_s = Command::get_attribute(cmd); for (auto & dv : devicevalues_) { - if (Helpers::toLower(command_s) == Helpers::toLower(dv.short_name) && (tag <= 0 || tag == dv.tag)) { - uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0; - - const char * type = "type"; - const char * value = "value"; - - json["name"] = dv.short_name; - - auto fullname = dv.get_fullname(); - if (!fullname.empty()) { - json["fullname"] = dv.has_tag() ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag - - // TAG https://github.com/emsesp/EMS-ESP32/issues/1338 - json["fullname"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname.c_str() : fullname; // prefix tag - } - - if (dv.tag != DeviceValueTAG::TAG_NONE) { - json["circuit"] = tag_to_mqtt(dv.tag); - } - - char val[10]; - switch (dv.type) { - case DeviceValueType::ENUM: { - if (*(uint8_t *)(dv.value_p) < dv.options_size) { - if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) { - json[value] = (uint8_t)(*(uint8_t *)(dv.value_p)); - } else { - json[value] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]); // text - } - } - json[type] = F_(enum); - JsonArray enum_ = json[F_(enum)].to(); - for (uint8_t i = 0; i < dv.options_size; i++) { - enum_.add(Helpers::translated_word(dv.options[i])); - } - break; - } - - case DeviceValueType::UINT16: - if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) { - json[value] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); - } - json[type] = F_(number); - break; - - case DeviceValueType::UINT8: - if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) { - json[value] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); - } - json[type] = F_(number); - break; - - case DeviceValueType::INT16: - if (Helpers::hasValue(*(int16_t *)(dv.value_p))) { - json[value] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); - } - json[type] = F_(number); - break; - - case DeviceValueType::INT8: - if (Helpers::hasValue(*(int8_t *)(dv.value_p))) { - json[value] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); - } - json[type] = F_(number); - break; - - case DeviceValueType::UINT24: - case DeviceValueType::UINT32: - if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) { - json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator)); - } - json[type] = F_(number); - break; - - case DeviceValueType::BOOL: - if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { - auto value_b = (bool)*(uint8_t *)(dv.value_p); - if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { - json[value] = value_b; - } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { - json[value] = value_b ? 1 : 0; - } else { - char s[12]; - json[value] = Helpers::render_boolean(s, value_b); - } - } - json[type] = ("boolean"); - break; - - case DeviceValueType::TIME: - if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) { - json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator)); - } - json[type] = F_(number); - break; - - case DeviceValueType::STRING: - if (Helpers::hasValue((char *)(dv.value_p))) { - json[value] = (char *)(dv.value_p); - } - json[type] = ("string"); - break; - - case DeviceValueType::CMD: - if (dv.options_size > 1) { - json[type] = F_(enum); - JsonArray enum_ = json[F_(enum)].to(); - for (uint8_t i = 0; i < dv.options_size; i++) { - enum_.add(Helpers::translated_word(dv.options[i])); - } - } else if (dv.uom != DeviceValueUOM::NONE) { - json[type] = F_(number); - } else { - json[type] = F_(command); - } - break; - - default: - json[type] = Helpers::translated_word(FL_(unknown)); - break; - } - - // set the min and max only for commands - if (dv.has_cmd) { - int16_t dv_set_min; - uint32_t dv_set_max; - if (dv.get_min_max(dv_set_min, dv_set_max)) { - json["min"] = dv_set_min; - json["max"] = dv_set_max; - } - } - - // add uom if it's not a " " (single space) - if (dv.uom != DeviceValueUOM::NONE) { - json["uom"] = uom_to_string(dv.uom); - } - - json["readable"] = !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE); - json["writeable"] = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY); - json["visible"] = !dv.has_state(DeviceValueState::DV_WEB_EXCLUDE); - - // TODO refactor to remove containsKey as it's costly and not advisable to use it - // https://arduinojson.org/v7/api/jsonobject/containskey/#avoid - - // commented out, leads to issues if type is set to number - // if there is no value, mention it - // if (!json.containsKey(value)) { - // json[value] = "not set"; - // } - + if (cmd == Helpers::toLower(dv.short_name) && (tag <= 0 || tag == dv.tag)) { + get_value_json(output, dv); // if we're filtering on an attribute, go find it - if (attribute_s) { - 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_s, command_s); // not found + return Command::set_attirbute(output, cmd, attribute_s); + } + } + return false; // not found, but don't return a message error yet +} + +// build the json for a specific entity +void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) { + uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0; + + const char * type = "type"; + const char * value = "value"; + + json["name"] = dv.short_name; + + auto fullname = dv.get_fullname(); + if (!fullname.empty()) { + json["fullname"] = dv.has_tag() ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag + + // TAG https://github.com/emsesp/EMS-ESP32/issues/1338 + json["fullname"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname.c_str() : fullname; // prefix tag + } + + if (dv.tag != DeviceValueTAG::TAG_NONE) { + json["circuit"] = tag_to_mqtt(dv.tag); + } + + char val[10]; + switch (dv.type) { + case DeviceValueType::ENUM: { + if (*(uint8_t *)(dv.value_p) < dv.options_size) { + json["index"] = (uint8_t)(*(uint8_t *)(dv.value_p)); + json["enum"] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]); // text + if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) { + json[value] = (uint8_t)(*(uint8_t *)(dv.value_p)); + } else { + json[value] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]); // text } - return true; + } + json[type] = F_(enum); + JsonArray enum_ = json[F_(enum)].to(); + for (uint8_t i = 0; i < dv.options_size; i++) { + enum_.add(Helpers::translated_word(dv.options[i])); + } + break; + } + + case DeviceValueType::UINT16: + if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) { + json[value] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); + } + json[type] = F_(number); + break; + + case DeviceValueType::UINT8: + if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) { + json[value] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); + } + json[type] = F_(number); + break; + + case DeviceValueType::INT16: + if (Helpers::hasValue(*(int16_t *)(dv.value_p))) { + json[value] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); + } + json[type] = F_(number); + break; + + case DeviceValueType::INT8: + if (Helpers::hasValue(*(int8_t *)(dv.value_p))) { + json[value] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit)); + } + json[type] = F_(number); + break; + + case DeviceValueType::UINT24: + case DeviceValueType::UINT32: + if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) { + json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator)); + } + json[type] = F_(number); + break; + + case DeviceValueType::BOOL: + if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { + auto value_b = (bool)*(uint8_t *)(dv.value_p); + json["bool"] = value_b; + json["index"] = value_b ? 1 : 0; + if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { + json[value] = value_b; + } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { + json[value] = value_b ? 1 : 0; + } else { + char s[12]; + json[value] = Helpers::render_boolean(s, value_b); + } + } + json[type] = ("boolean"); + break; + + case DeviceValueType::TIME: + if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) { + json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator)); + } + json[type] = F_(number); + break; + + case DeviceValueType::STRING: + if (Helpers::hasValue((char *)(dv.value_p))) { + json[value] = (char *)(dv.value_p); + } + json[type] = ("string"); + break; + + case DeviceValueType::CMD: + if (dv.options_size > 1) { + json[type] = F_(enum); + JsonArray enum_ = json[F_(enum)].to(); + for (uint8_t i = 0; i < dv.options_size; i++) { + enum_.add(Helpers::translated_word(dv.options[i])); + } + } else if (dv.uom != DeviceValueUOM::NONE) { + json[type] = F_(number); + } else { + json[type] = F_(command); + } + break; + + default: + json[type] = Helpers::translated_word(FL_(unknown)); + break; + } + + // set the min and max only for commands + if (dv.has_cmd) { + int16_t dv_set_min; + uint32_t dv_set_max; + if (dv.get_min_max(dv_set_min, dv_set_max)) { + json["min"] = dv_set_min; + json["max"] = dv_set_max; } } - return false; // not found, but don't return a message error yet + // add uom if it's not a " " (single space) + if (dv.uom != DeviceValueUOM::NONE) { + json["uom"] = uom_to_string(dv.uom); + } + + json["readable"] = !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE); + json["writeable"] = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY); + json["visible"] = !dv.has_state(DeviceValueState::DV_WEB_EXCLUDE); } // mqtt publish all single values from one device (used for time schedule) diff --git a/src/emsdevice.h b/src/emsdevice.h index cb07683fd..057edab70 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -214,7 +214,7 @@ class EMSdevice { void show_telegram_handlers(uuid::console::Shell & shell) const; char * show_telegram_handlers(char * result, const size_t len, const uint8_t handlers); void show_mqtt_handlers(uuid::console::Shell & shell) const; - void list_device_entries(JsonObject output) const; + // void list_device_entries(JsonObject output) const; void add_handlers_ignored(const uint16_t handler); void set_climate_minmax(int8_t tag, int16_t min, uint32_t max); @@ -227,6 +227,7 @@ class EMSdevice { std::string get_value_uom(const std::string & shortname) const; bool get_value_info(JsonObject root, const char * cmd, const int8_t id); + void get_value_json(JsonObject output, DeviceValue & dv); void get_dv_info(JsonObject json); enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE }; @@ -353,8 +354,7 @@ class EMSdevice { ANALOGSENSOR, // for internal analog sensors SCHEDULER, // for internal schedule CUSTOM, // for user defined entities - - BOILER, + BOILER, // frome here on enum the ems-devices THERMOSTAT, MIXER, SOLAR, diff --git a/src/emsesp.cpp b/src/emsesp.cpp index ea7e43ba7..37849ed60 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -1343,38 +1343,12 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const // Print to LOG showing we've added a new device LOG_INFO("Recognized new %s with deviceID 0x%02X", EMSdevice::device_type_2_device_name(device_type), device_id); - // add commands 'info', 'commands', 'values', 'entities' for all EMS devices - // and register the MQTT subscribe topic for this device + // register the MQTT subscribe topic for this device // except for connect, controller and gateway if ((device_type == DeviceType::CONNECT) || (device_type == DeviceType::CONTROLLER) || (device_type == DeviceType::GATEWAY)) { return true; } - Command::add( - device_type, - F_(info), - [device_type](const char * value, const int8_t id, JsonObject output) { - return EMSdevice::export_values(device_type, output, id, EMSdevice::OUTPUT_TARGET::API_VERBOSE); - }, - FL_(info_cmd)); - Command::add( - 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); - }, - FL_(values_cmd)); - Command::add( - device_type, - F_(commands), - [device_type](const char * value, const int8_t id, JsonObject output) { return command_commands(device_type, output, id); }, - FL_(commands_cmd)); - Command::add( - device_type, - F_(entities), - [device_type](const char * value, const int8_t id, JsonObject output) { return command_entities(device_type, output, id); }, - FL_(entities_cmd)); - // MQTT subscribe to the device e.g. "ems-esp/boiler/#" auto topic = std::string(EMSdevice::device_type_2_device_name(device_type)) + "/#"; Mqtt::subscribe(device_type, topic, nullptr); @@ -1382,25 +1356,6 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const return true; } -// list device entities -bool EMSESP::command_entities(uint8_t device_type, JsonObject output, const int8_t id) { - JsonObject node; - - for (const auto & emsdevice : emsdevices) { - if (emsdevice && (emsdevice->device_type() == device_type)) { - emsdevice->list_device_entries(output); - return true; - } - } - - return false; -} - -// list all available commands, return as json -bool EMSESP::command_commands(uint8_t device_type, JsonObject output, const int8_t id) { - return Command::list(device_type, output); -} - // send a read request, passing it into to the Tx Service, with optional offset and length void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length, const bool front) { txservice_.read_request(type_id, dest, offset, length, front); diff --git a/src/emsesp.h b/src/emsesp.h index ac6592765..a779b6f73 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -251,8 +251,6 @@ class EMSESP { static void process_version(std::shared_ptr telegram); static void publish_response(std::shared_ptr telegram); static void publish_all_loop(); - static bool command_commands(uint8_t device_type, JsonObject output, const int8_t id); - static bool command_entities(uint8_t device_type, JsonObject output, const int8_t id); static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute diff --git a/src/system.cpp b/src/system.cpp index 24acc2daf..222569341 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -848,8 +848,6 @@ void System::commands_init() { #endif // these commands will return data in JSON format - Command::add(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, FL_(system_info_cmd)); - Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, FL_(commands_cmd)); 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)); @@ -1249,11 +1247,6 @@ bool System::check_upgrade(bool factory_settings) { return false; } -// list commands -bool System::command_commands(const char * value, const int8_t id, JsonObject output) { - return Command::list(EMSdevice::DeviceType::SYSTEM, output); -} - // convert settings file into json object void System::extractSettings(const char * filename, const char * section, JsonObject output) { #ifndef EMSESP_STANDALONE @@ -1291,27 +1284,41 @@ bool System::saveSettings(const char * filename, const char * section, JsonObjec } // return back a system value -bool System::get_value_info(JsonObject root, const char * command) { - if (command == nullptr || strlen(command) == 0) { +bool System::get_value_info(JsonObject output, const char * cmd) { + if (cmd == nullptr || strlen(cmd) == 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)); + // check for hardcoded "info"/"value" + if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(value))) { + return command_info("", 0, output); + } - // fetch all the data from the system + // fetch all the data from the system in a different json + JsonDocument doc; + JsonObject root = doc.to(); (void)command_info("", 0, root); - // check for hardcoded "info" - if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(value))) { + // list all entities + if (!strcmp(cmd, F_(entities))) { + for (JsonPair p : root) { + if (p.value().is()) { + // String prefix = p.key().c_str(); + for (JsonPair p1 : p.value().as()) { + JsonObject entity = output[String(p.key().c_str()) + '.' + p1.key().c_str()].to(); + get_value_json(entity, p.key().c_str(), p1.key().c_str(), p1.value()); + } + } // else { // we don't have pairs in json root object + // get_value_json(entity, "", p.key().c_str(), p.value()); + // } + } return true; } char * val = strstr(cmd, "/value"); if (val) { - val[0] = '\0'; + *val = '\0'; } char * slash = strchr(cmd, '/'); @@ -1320,40 +1327,58 @@ bool System::get_value_info(JsonObject root, const char * command) { slash++; } - std::string s; + // list values for a jsonObject in system, e.g. /api/system/network + if (!slash || !strcmp(slash, F_(info)) || !strcmp(slash, F_(values))) { + for (JsonPair p : root) { + if (Helpers::toLower(p.key().c_str()) == cmd && p.value().is()) { + for (JsonPair p1 : p.value().as()) { + output[p1.key().c_str()] = p1.value().as(); + } + return true; + } + } + return false; + } + + // value info or api_data for a single value // Loop through all the key-value pairs in root to find the key, case independent if (slash) { // search the top level first for (JsonPair p : root) { if (p.value().is() && Helpers::toLower(p.key().c_str()) == cmd) { for (JsonPair p1 : p.value().as()) { if (Helpers::toLower(p1.key().c_str()) == slash && !p1.value().is()) { - s = p1.value().as(); - break; + if (val) { + output["api_data"] = p1.value().as(); + return true; + } + get_value_json(output, p.key().c_str(), p1.key().c_str(), p1.value()); + return true; } } - } + } // else skipt, but we don't have value pairs in system root } + } + return false; +} + +void System::get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val) { + output["name"] = name; + if (circuit.length()) { + output["circuit"] = circuit; + } + output["readable"] = true; + output["writable"] = false; + output["visible"] = true; + if (val.is()) { + output["value"] = val.as(); + output["type"] = "boolean"; + } else if (val.is() || val.is() || val.is()) { + output["value"] = val.as(); + output["type"] = "string"; } else { - for (JsonPair p : root) { - if (Helpers::toLower(p.key().c_str()) == cmd && !p.value().is()) { - s = p.value().as(); - break; - } - } + output["value"] = val.as(); + output["type"] = "number"; } - - root.clear(); // clear json, we only one a single value - - if (!s.empty()) { - if (val) { - root["api_data"] = s; - } else { - root["value"] = s; - } - return true; // found - } - - return false; // not found } // export status information including the device information diff --git a/src/system.h b/src/system.h index 504a90e7a..41bab0531 100644 --- a/src/system.h +++ b/src/system.h @@ -59,10 +59,10 @@ class System { static bool command_watch(const char * value, const int8_t id); static bool command_message(const char * value, const int8_t id); static bool command_info(const char * value, const int8_t id, JsonObject output); - static bool command_commands(const char * value, const int8_t id, JsonObject output); static bool command_response(const char * value, const int8_t id, JsonObject output); static bool command_allvalues(const char * value, const int8_t id, JsonObject output); static bool get_value_info(JsonObject root, const char * cmd); + static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val); #if defined(EMSESP_TEST) static bool command_test(const char * value, const int8_t id); diff --git a/src/temperaturesensor.cpp b/src/temperaturesensor.cpp index f27111c51..9d9a2c983 100644 --- a/src/temperaturesensor.cpp +++ b/src/temperaturesensor.cpp @@ -343,75 +343,43 @@ bool TemperatureSensor::updated_values() { // called from emsesp.cpp for commands bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, const int8_t id) { - // check of it a 'commands' command - if (Helpers::toLower(cmd) == F_(commands)) { - return Command::list(EMSdevice::DeviceType::TEMPERATURESENSOR, output); - } - // return empty json if there are no sensors if (sensors_.empty()) { return true; } - uint8_t show_all = 0; - if (Helpers::hasValue(cmd)) { - show_all = (strncmp(cmd, F_(info), 4) == 0) ? 1 : (strncmp(cmd, F_(values), 6) == 0) ? 2 : 0; - } - - // see if we're showing all sensors - if (show_all) { + if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) { for (const auto & sensor : sensors_) { - if (show_all == 1) { - // info - JsonObject dataSensor = output[sensor.name()].to(); - addSensorJson(dataSensor, sensor); - } else { - // values, shortname version. Also used in 'system allvalues' - if (Helpers::hasValue(sensor.temperature_c)) { - char val[10]; - output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0)); - } + if (Helpers::hasValue(sensor.temperature_c)) { + char val[10]; + output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0)); } } return true; } - // this is for a specific sensor - // make a copy of the string command for parsing, and lowercase it - char sensor_name[COMMAND_MAX_LENGTH] = {'\0'}; - char * attribute_s = nullptr; - strlcpy(sensor_name, Helpers::toLower(cmd).c_str(), sizeof(sensor_name)); - - // check for a specific attribute to fetch instead of the complete record - char * breakp = strchr(sensor_name, '/'); - if (breakp) { - *breakp = '\0'; - attribute_s = breakp + 1; + if (!strcmp(cmd, F_(entities))) { + for (const auto & sensor : sensors_) { + get_value_json(output[sensor.name()].to(), sensor); + } + return true; } + // this is for a specific sensor + const char * attribute_s = Command::get_attribute(cmd); + for (const auto & sensor : sensors_) { // match custom name or sensor ID - if (sensor_name == Helpers::toLower(sensor.name()) || sensor_name == Helpers::toLower(sensor.id())) { - // add all the data elements - addSensorJson(output, sensor); - // if we're filtering on an attribute, go find it - if (attribute_s) { - if (output.containsKey(attribute_s)) { - std::string data = output[attribute_s].as(); - output.clear(); - output["api_data"] = data; // always as string - return true; - } - return EMSESP::return_not_found(output, attribute_s, sensor_name); // not found - } - return true; // found a match, exit + if (cmd == Helpers::toLower(sensor.name()) || cmd == Helpers::toLower(sensor.id())) { + get_value_json(output, sensor); + return Command::set_attirbute(output, cmd, attribute_s); } } return false; // not found } -void TemperatureSensor::addSensorJson(JsonObject output, const Sensor & sensor) { +void TemperatureSensor::get_value_json(JsonObject output, const Sensor & sensor) { output["id"] = sensor.id(); output["name"] = sensor.name(); if (Helpers::hasValue(sensor.temperature_c)) { diff --git a/src/temperaturesensor.h b/src/temperaturesensor.h index 24c456f14..af0f681bb 100644 --- a/src/temperaturesensor.h +++ b/src/temperaturesensor.h @@ -151,8 +151,8 @@ class TemperatureSensor { bool temperature_convert_complete(); int16_t get_temperature_c(const uint8_t addr[]); uint64_t get_id(const uint8_t addr[]); + void get_value_json(JsonObject output, const Sensor & sensor); void remove_ha_topic(const std::string & id); - void addSensorJson(JsonObject output, const Sensor & sensor); std::vector sensors_; // our list of active sensors diff --git a/src/version.h b/src/version.h index 64ea5a498..5ae1987a0 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.0-dev.28" +#define EMSESP_APP_VERSION "3.7.0-dev.29" diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index e1706ff9a..989f760da 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -256,20 +256,6 @@ 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) { - // 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"; - } - } - return true; - } - // if no custom entries, return empty json // even if we're looking for a specific entity // https://github.com/emsesp/EMS-ESP32/issues/1297 @@ -278,7 +264,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) } // if it's info or values... - if (strlen(cmd) == 0 || Helpers::toLower(cmd) == F_(values) || Helpers::toLower(cmd) == F_(info)) { + if (!strlen(cmd) || !strcmp(cmd, F_(values)) || !strcmp(cmd, F_(info))) { // list all names for (const CustomEntityItem & entity : *customEntityItems_) { render_value(output, entity); @@ -286,59 +272,50 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) return true; } - char command_s[COMMAND_MAX_LENGTH]; - strlcpy(command_s, Helpers::toLower(cmd).c_str(), sizeof(command_s)); - char * attribute_s = nullptr; - - // check specific attribute to fetch instead of the complete record - char * breakp = strchr(command_s, '/'); - if (breakp) { - *breakp = '\0'; - attribute_s = breakp + 1; + // list all entities + if (!strcmp(cmd, F_(entities))) { + for (const auto & entity : *customEntityItems_) { + auto nest = output[entity.name].to(); + get_value_json(nest, entity); + } + return true; } + // specific value info + const char * attribute_s = Command::get_attribute(cmd); for (const auto & entity : *customEntityItems_) { - if (Helpers::toLower(entity.name) == command_s) { - output["name"] = entity.name; - output["ram"] = entity.ram; - output["type"] = entity.value_type == DeviceValueType::BOOL ? "boolean" : entity.value_type == DeviceValueType::STRING ? "string" : F_(number); - if (entity.uom > 0) { - output["uom"] = EMSdevice::uom_to_string(entity.uom); - } - output["readable"] = true; - output["writeable"] = entity.writeable; - output["visible"] = true; - if (entity.ram == 0) { - output["device_id"] = Helpers::hextoa(entity.device_id); - output["type_id"] = Helpers::hextoa(entity.type_id); - output["offset"] = entity.offset; - if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) { - output["factor"] = entity.factor; - } else if (entity.value_type == DeviceValueType::STRING) { - output["bytes"] = (uint8_t)entity.factor; - } - } - render_value(output, entity, true); // create the "value" field - - if (attribute_s) { - if (output.containsKey(attribute_s)) { - std::string data = output[attribute_s].as(); - output.clear(); - output["api_data"] = data; // always as string - return true; - } - return EMSESP::return_not_found(output, attribute_s, command_s); // not found - } - } - - if (output.size()) { - return true; + if (Helpers::toLower(entity.name) == cmd) { + get_value_json(output, entity); + return Command::set_attirbute(output, cmd, attribute_s); } } - return false; // not found } +// build the json for specific entity +void WebCustomEntityService::get_value_json(JsonObject output, const CustomEntityItem & entity) { + output["name"] = entity.name; + output["storage"] = entity.ram ? "ram" : "ems"; + output["type"] = entity.value_type == DeviceValueType::BOOL ? "boolean" : entity.value_type == DeviceValueType::STRING ? "string" : F_(number); + if (entity.uom > 0) { + output["uom"] = EMSdevice::uom_to_string(entity.uom); + } + output["readable"] = true; + output["writeable"] = entity.writeable; + output["visible"] = true; + if (entity.ram == 0) { + output["device_id"] = Helpers::hextoa(entity.device_id); + output["type_id"] = Helpers::hextoa(entity.type_id); + output["offset"] = entity.offset; + if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) { + output["factor"] = entity.factor; + } else if (entity.value_type == DeviceValueType::STRING) { + output["bytes"] = (uint8_t)entity.factor; + } + } + render_value(output, entity, true); // create the "value" field +} + // publish single value void WebCustomEntityService::publish_single(const CustomEntityItem & entity) { if (!Mqtt::enabled() || !Mqtt::publish_single()) { diff --git a/src/web/WebCustomEntityService.h b/src/web/WebCustomEntityService.h index cb730f0a6..8fe70e3a3 100644 --- a/src/web/WebCustomEntityService.h +++ b/src/web/WebCustomEntityService.h @@ -58,6 +58,7 @@ class WebCustomEntityService : public StatefulService { void publish(const bool force = false); bool command_setvalue(const char * value, const int8_t id, const char * name); bool get_value_info(JsonObject output, const char * cmd); + void get_value_json(JsonObject output, const CustomEntityItem & entity); bool get_value(std::shared_ptr telegram); void fetch(); void render_value(JsonObject output, CustomEntityItem entity, const bool useVal = false, const bool web = false, const bool add_uom = false); diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index db2e6dcdf..6dcd6c7fa 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -132,26 +132,11 @@ bool WebSchedulerService::command_setvalue(const char * value, const int8_t id, // process json output for info/commands and value_info bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { - // 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 ScheduleItem & scheduleItem : *scheduleItems_) { - if (!scheduleItem.name.empty()) { - output[scheduleItem.name] = "activate schedule"; - } - } - - return true; - } - if (scheduleItems_->size() == 0) { return true; } - if (strlen(cmd) == 0 || Helpers::toLower(cmd) == F_(values) || Helpers::toLower(cmd) == F_(info)) { + if (!strlen(cmd) || !strcmp(cmd, F_(values)) || !strcmp(cmd, F_(info))) { // list all names for (const ScheduleItem & scheduleItem : *scheduleItems_) { if (!scheduleItem.name.empty()) { @@ -165,68 +150,59 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) { } } } - return true; } - char command_s[COMMAND_MAX_LENGTH]; - strlcpy(command_s, Helpers::toLower(cmd).c_str(), sizeof(command_s)); - char * attribute_s = nullptr; + const char * attribute_s = Command::get_attribute(cmd); - // check specific attribute to fetch instead of the complete record - char * breakp = strchr(command_s, '/'); - if (breakp) { - *breakp = '\0'; - attribute_s = breakp + 1; + if (!strcmp(cmd, F_(entities))) { + uint8_t i = 0; + char name[30]; + for (const ScheduleItem & scheduleItem : *scheduleItems_) { + strlcpy(name, scheduleItem.name == "" ? Helpers::smallitoa(name, i++) : scheduleItem.name.c_str(), sizeof(name)); + get_value_json(output[name].to(), scheduleItem); + } + return true; } - for (const ScheduleItem & scheduleItem : *scheduleItems_) { - if (Helpers::toLower(scheduleItem.name) == command_s) { - output["name"] = scheduleItem.name; - output["type"] = "boolean"; - if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { - output["value"] = scheduleItem.active; - } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { - output["value"] = scheduleItem.active ? 1 : 0; - } else { - char result[12]; - output["value"] = Helpers::render_boolean(result, scheduleItem.active); - } - if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_CONDITION) { - output["condition"] = scheduleItem.time; - } else if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_ONCHANGE) { - output["onchange"] = scheduleItem.time; - } else if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER) { - output["timer"] = scheduleItem.time; - } else if (scheduleItem.flags != 0) { - output["time"] = scheduleItem.time; - } - output["command"] = scheduleItem.cmd; - output["cmd_data"] = scheduleItem.value; - output["readable"] = true; - output["writeable"] = true; - output["visible"] = true; - break; + if (Helpers::toLower(scheduleItem.name) == cmd) { + get_value_json(output, scheduleItem); + return Command::set_attirbute(output, cmd, attribute_s); } } - 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 false; // not found } +// build the json for specific entity +void WebSchedulerService::get_value_json(JsonObject output, const ScheduleItem & scheduleItem) { + output["name"] = scheduleItem.name; + output["type"] = "boolean"; + if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { + output["value"] = scheduleItem.active; + } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { + output["value"] = scheduleItem.active ? 1 : 0; + } else { + char result[12]; + output["value"] = Helpers::render_boolean(result, scheduleItem.active); + } + if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_CONDITION) { + output["condition"] = scheduleItem.time; + } else if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_ONCHANGE) { + output["onchange"] = scheduleItem.time; + } else if (scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER) { + output["timer"] = scheduleItem.time; + } else if (scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) { + output["time"] = scheduleItem.time; + } + output["command"] = scheduleItem.cmd; + output["cmd_data"] = scheduleItem.value; + bool hasName = scheduleItem.name != ""; + output["readable"] = hasName; + output["writeable"] = hasName; + output["visible"] = hasName; +} + // publish single value void WebSchedulerService::publish_single(const char * name, const bool state) { if (!Mqtt::enabled() || !Mqtt::publish_single() || name == nullptr || name[0] == '\0') { diff --git a/src/web/WebSchedulerService.h b/src/web/WebSchedulerService.h index f599b90ad..30f9406c9 100644 --- a/src/web/WebSchedulerService.h +++ b/src/web/WebSchedulerService.h @@ -68,6 +68,7 @@ class WebSchedulerService : public StatefulService { bool has_commands(); bool command_setvalue(const char * value, const int8_t id, const char * name); bool get_value_info(JsonObject output, const char * cmd); + void get_value_json(JsonObject output, const ScheduleItem & scheduleItem); void ha_reset() { ha_registered_ = false; }