mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 01:09:51 +03:00
Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev2
This commit is contained in:
@@ -462,13 +462,13 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(120 * num_sensors);
|
||||
JsonDocument doc;
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() != AnalogType::NOTUSED) {
|
||||
if (Mqtt::is_nested()) {
|
||||
char s[10];
|
||||
JsonObject dataSensor = doc.createNestedObject(Helpers::smallitoa(s, sensor.gpio()));
|
||||
JsonObject dataSensor = doc[Helpers::smallitoa(s, sensor.gpio())].to<JsonObject>();
|
||||
dataSensor["name"] = sensor.name();
|
||||
switch (sensor.type()) {
|
||||
case AnalogType::COUNTER:
|
||||
@@ -508,11 +508,11 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
doc[sensor.name()] = serialized(Helpers::render_value(s, sensor.value(), 2));
|
||||
}
|
||||
|
||||
// create HA config
|
||||
// create HA config if hasn't already been done
|
||||
if (Mqtt::ha_enabled() && (!sensor.ha_registered || force)) {
|
||||
LOG_DEBUG("Recreating HA config for analog sensor GPIO %02d", sensor.gpio());
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
JsonDocument config;
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s_data", Mqtt::base().c_str(), F_(analogsensor)); // use base path
|
||||
@@ -615,13 +615,16 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
config["stat_cla"] = "measurement";
|
||||
}
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
dev["name"] = Mqtt::basename() + " Analog";
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename() + "-analog");
|
||||
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
|
||||
bool is_ha_device_created = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.ha_registered) {
|
||||
is_ha_device_created = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
Mqtt::add_ha_sections_to_doc("analog", stat_t, config, !is_ha_device_created, val_cond);
|
||||
|
||||
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
@@ -703,7 +706,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (id == -1) { // show number and id for info command
|
||||
JsonObject dataSensor = output.createNestedObject(sensor.name());
|
||||
JsonObject dataSensor = output[sensor.name()].to<JsonObject>();
|
||||
dataSensor["gpio"] = sensor.gpio();
|
||||
dataSensor["type"] = F_(number);
|
||||
dataSensor["value"] = sensor.value();
|
||||
|
||||
@@ -278,8 +278,8 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
// calls a command directly
|
||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value) {
|
||||
// create a temporary buffer
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> output_doc;
|
||||
JsonObject output = output_doc.to<JsonObject>();
|
||||
JsonDocument output_doc;
|
||||
JsonObject output = output_doc.to<JsonObject>();
|
||||
|
||||
// authenticated is always true and ID is the default value
|
||||
return call(device_type, cmd, value, true, -1, output);
|
||||
|
||||
@@ -237,7 +237,7 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
networkSettings.password = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||
shell.println("WiFi password updated");
|
||||
} else {
|
||||
shell.println("Passwords do not match");
|
||||
}
|
||||
@@ -272,7 +272,7 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
networkSettings.ssid = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||
shell.println("WiFi ssid updated");
|
||||
});
|
||||
|
||||
|
||||
@@ -496,11 +496,11 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE);
|
||||
int8_t id = -1;
|
||||
const char * cmd = Command::parse_command_string(arguments[1].c_str(), id);
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
JsonDocument doc;
|
||||
int8_t id = -1;
|
||||
const char * cmd = Command::parse_command_string(arguments[1].c_str(), id);
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
if (cmd == nullptr) {
|
||||
cmd = device_type == EMSdevice::DeviceType::SYSTEM ? F_(info) : F_(values);
|
||||
|
||||
@@ -261,7 +261,7 @@ enum {
|
||||
#else
|
||||
#define ARDUINO_VERSION_STR(major, minor, patch) "ESP32 Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
||||
#endif
|
||||
#define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
||||
#define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2933,8 +2933,8 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
|
||||
uint8_t time = 0x91; // invalid
|
||||
|
||||
if (value[0] == '{') {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
DeserializationError error = deserializeJson(doc, value);
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, value);
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
snprintf(key, sizeof(key), "%s", dv.short_name);
|
||||
}
|
||||
|
||||
JsonArray details = output.createNestedArray(key);
|
||||
JsonArray details = output[key].to<JsonArray>();
|
||||
|
||||
// add the full name description
|
||||
details.add(fullname);
|
||||
@@ -868,7 +868,7 @@ bool EMSdevice::export_values(uint8_t device_type, JsonObject & output, const in
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
if (!nest_created && emsdevice->has_tags(tag)) {
|
||||
output_hc = output.createNestedObject(EMSdevice::tag_to_mqtt(tag));
|
||||
output_hc = output[EMSdevice::tag_to_mqtt(tag)].to<JsonObject>();
|
||||
nest_created = true;
|
||||
}
|
||||
has_value |= emsdevice->generate_values(output_hc, tag, true, output_target); // use nested for id -1 and 0
|
||||
@@ -885,7 +885,7 @@ bool EMSdevice::export_values(uint8_t device_type, JsonObject & output, const in
|
||||
void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
// output["label"] = to_string_short();
|
||||
// output["label"] = name_;
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
JsonArray data = output["data"].to<JsonArray>();
|
||||
|
||||
for (auto & dv : devicevalues_) {
|
||||
auto fullname = dv.get_fullname();
|
||||
@@ -895,7 +895,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
// 2. it must have a valid value, if it is not a command like 'reset'
|
||||
// 3. show favorites first
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && !fullname.empty() && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) {
|
||||
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
|
||||
JsonObject obj = data.add<JsonObject>(); // create the object, we know there is a value
|
||||
uint8_t fahrenheit = 0;
|
||||
|
||||
// handle Booleans (true, false), output as strings according to the user settings
|
||||
@@ -962,7 +962,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
|
||||
// add the Command options
|
||||
if (dv.type == DeviceValueType::ENUM || (dv.type == DeviceValueType::CMD && dv.options_size > 1)) {
|
||||
JsonArray l = obj.createNestedArray("l");
|
||||
JsonArray l = obj["l"].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
auto enum_str = Helpers::translated_word(dv.options[i]);
|
||||
if (enum_str) {
|
||||
@@ -970,7 +970,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
}
|
||||
}
|
||||
} else if (dv.type == DeviceValueType::BOOL) {
|
||||
JsonArray l = obj.createNestedArray("l");
|
||||
JsonArray l = obj["l"].to<JsonArray>();
|
||||
char result[12];
|
||||
l.add(Helpers::render_boolean(result, false, true));
|
||||
l.add(Helpers::render_boolean(result, true, true));
|
||||
@@ -1004,7 +1004,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
// also show commands and entities that have an empty full name
|
||||
JsonObject obj = output.createNestedObject();
|
||||
JsonObject obj = output.add<JsonObject>();
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
|
||||
// create the value
|
||||
@@ -1096,7 +1096,7 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||
if (mask & 0x80) {
|
||||
JsonObject obj = output.createNestedObject();
|
||||
JsonObject obj = output.add<JsonObject>();
|
||||
obj["id"] = DeviceValue::get_name(entity_id);
|
||||
obj["m"] = mask;
|
||||
obj["w"] = false;
|
||||
@@ -1433,7 +1433,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
}
|
||||
}
|
||||
json[type] = F_(enum);
|
||||
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||
JsonArray enum_ = json[F_(enum)].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
enum_.add(Helpers::translated_word(dv.options[i]));
|
||||
}
|
||||
@@ -1507,7 +1507,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
case DeviceValueType::CMD:
|
||||
json[type] = F_(command);
|
||||
if (dv.options_size > 1) {
|
||||
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||
JsonArray enum_ = json[F_(enum)].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
enum_.add(Helpers::translated_word(dv.options[i]));
|
||||
}
|
||||
@@ -1628,15 +1628,19 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
if (dv.tag != old_tag) {
|
||||
old_tag = dv.tag;
|
||||
if (nested && have_tag && dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
json = output.createNestedObject(tag_to_mqtt(dv.tag));
|
||||
json = output[tag_to_mqtt(dv.tag)].to<JsonObject>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do not overwrite
|
||||
if (json.containsKey(name)) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
EMSESP::logger().debug("double json key: %s", name);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle Booleans
|
||||
if (dv.type == DeviceValueType::BOOL && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
||||
// see how to render the value depending on the setting
|
||||
|
||||
@@ -380,8 +380,8 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
// print header, with device type translated
|
||||
shell.printfln("%s: %s (%d)", emsdevice->device_type_2_device_name_translated(), emsdevice->to_string().c_str(), emsdevice->count_entities());
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE); // use max size
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
JsonDocument doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::CONSOLE);
|
||||
|
||||
@@ -405,8 +405,8 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
// show any custom entities
|
||||
if (webCustomEntityService.count_entities() > 0) {
|
||||
shell.printfln("Custom entities:");
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> custom_doc; // use max size
|
||||
JsonObject custom_output = custom_doc.to<JsonObject>();
|
||||
JsonDocument custom_doc; // use max size
|
||||
JsonObject custom_output = custom_doc.to<JsonObject>();
|
||||
webCustomEntityService.show_values(custom_output);
|
||||
for (JsonPair p : custom_output) {
|
||||
shell.printfln(" %s: %s%s%s", p.key().c_str(), COLOR_BRIGHT_GREEN, p.value().as<std::string>().c_str(), COLOR_RESET);
|
||||
@@ -557,22 +557,22 @@ void EMSESP::reset_mqtt_ha() {
|
||||
// this will also create the HA /config topic for each device value
|
||||
// generate_values_json is called to build the device value (dv) object array
|
||||
void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool need_publish = false;
|
||||
bool nested = (Mqtt::is_nested());
|
||||
JsonDocument doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool need_publish = false;
|
||||
bool nested = (Mqtt::is_nested());
|
||||
|
||||
// group by device type
|
||||
for (uint8_t tag = DeviceValueTAG::TAG_BOILER_DATA_WW; tag <= DeviceValueTAG::TAG_HS16; tag++) {
|
||||
JsonObject json_hc = json;
|
||||
JsonObject json_tag = json;
|
||||
bool nest_created = false;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
if (nested && !nest_created && emsdevice->has_tags(tag)) {
|
||||
json_hc = doc.createNestedObject(EMSdevice::tag_to_mqtt(tag));
|
||||
json_tag = doc[EMSdevice::tag_to_mqtt(tag)].to<JsonObject>();
|
||||
nest_created = true;
|
||||
}
|
||||
need_publish |= emsdevice->generate_values(json_hc, tag, false, EMSdevice::OUTPUT_TARGET::MQTT);
|
||||
need_publish |= emsdevice->generate_values(json_tag, tag, false, EMSdevice::OUTPUT_TARGET::MQTT);
|
||||
}
|
||||
}
|
||||
if (need_publish && ((!nested && tag >= DeviceValueTAG::TAG_DEVICE_DATA_WW) || (tag == DeviceValueTAG::TAG_BOILER_DATA_WW))) {
|
||||
@@ -645,8 +645,8 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
|
||||
strlcat(buffer, " ", 768);
|
||||
return;
|
||||
}
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_LARGE);
|
||||
char s[10];
|
||||
JsonDocument doc;
|
||||
char s[10];
|
||||
doc["src"] = Helpers::hextoa(s, telegram->src);
|
||||
doc["dest"] = Helpers::hextoa(s, telegram->dest);
|
||||
doc["type"] = Helpers::hextoa(s, telegram->type_id);
|
||||
|
||||
11
src/emsesp.h
11
src/emsesp.h
@@ -66,17 +66,6 @@
|
||||
|
||||
#define WATCH_ID_NONE 0 // no watch id set
|
||||
|
||||
// uses StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_SMALL 256
|
||||
#define EMSESP_JSON_SIZE_MEDIUM 768
|
||||
#define EMSESP_JSON_SIZE_LARGE 1024 // used in forming HA config payloads, also in *AsyncJsonResponse
|
||||
|
||||
// used in larger buffers like DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_XLARGE 2048
|
||||
#define EMSESP_JSON_SIZE_XXLARGE 4096
|
||||
#define EMSESP_JSON_SIZE_XXXLARGE 16384
|
||||
#define EMSESP_JSON_SIZE_XXXXLARGE 20480 // web output
|
||||
|
||||
// helpers for callback functions
|
||||
#define MAKE_PF_CB(__f) [&](std::shared_ptr<const Telegram> t) { __f(t); } // for Process Function callbacks to EMSDevice::process_function_p
|
||||
#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id) { return __f(value, id); } // for Command Function callbacks Command::cmd_function_p
|
||||
|
||||
138
src/mqtt.cpp
138
src/mqtt.cpp
@@ -263,9 +263,9 @@ void Mqtt::on_message(const char * topic, const uint8_t * payload, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
|
||||
DynamicJsonDocument output_doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject input, output;
|
||||
JsonDocument input_doc;
|
||||
JsonDocument output_doc;
|
||||
JsonObject input, output;
|
||||
|
||||
// convert payload into a json doc
|
||||
// if the payload doesn't not contain the key 'value' or 'data', treat the whole payload as the 'value'
|
||||
@@ -515,8 +515,7 @@ void Mqtt::on_connect() {
|
||||
}
|
||||
|
||||
// send initial MQTT messages for some of our services
|
||||
EMSESP::shower_.set_shower_state(false, true); // Send shower_activated as false
|
||||
EMSESP::system_.send_heartbeat(); // send heartbeat
|
||||
EMSESP::system_.send_heartbeat(); // send heartbeat
|
||||
|
||||
// re-subscribe to all custom registered MQTT topics
|
||||
resubscribe();
|
||||
@@ -531,7 +530,7 @@ void Mqtt::on_connect() {
|
||||
// e.g. homeassistant/sensor/ems-esp/status/config
|
||||
// all the values from the heartbeat payload will be added as attributes to the entity state
|
||||
void Mqtt::ha_status() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
JsonDocument doc;
|
||||
|
||||
char uniq[70];
|
||||
if (Mqtt::entity_format() == entityFormat::MULTI_SHORT) {
|
||||
@@ -554,15 +553,15 @@ void Mqtt::ha_status() {
|
||||
// doc["avty_t"] = "~/status"; // commented out, as it causes errors in HA sometimes
|
||||
// doc["json_attr_t"] = "~/heartbeat"; // store also as HA attributes
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
JsonObject dev = doc["dev"].to<JsonObject>();
|
||||
dev["name"] = Mqtt::basename();
|
||||
dev["sw"] = "v" + std::string(EMSESP_APP_VERSION);
|
||||
dev["mf"] = "proddy";
|
||||
dev["mf"] = "EMS-ESP";
|
||||
dev["mdl"] = "EMS-ESP";
|
||||
#ifndef EMSESP_STANDALONE
|
||||
dev["cu"] = "http://" + (EMSESP::system_.ethernet_connected() ? ETH.localIP().toString() : WiFi.localIP().toString());
|
||||
#endif
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
JsonArray ids = dev["ids"].to<JsonArray>();
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||
@@ -738,12 +737,12 @@ bool Mqtt::queue_ha(const char * topic, const JsonObject & payload) {
|
||||
// create's a ha sensor config topic from a device value object
|
||||
// and also takes a flag (create_device_config) used to also create the main HA device config. This is only needed for one entity
|
||||
bool Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> dev_json;
|
||||
JsonDocument dev_json;
|
||||
|
||||
// always create the ids (discovery indentifiers)
|
||||
// with the name always
|
||||
// and the manufacturer and model if we're creating the device config for the first entity
|
||||
JsonArray ids = dev_json.createNestedArray("ids");
|
||||
JsonArray ids = dev_json["ids"].to<JsonArray>();
|
||||
char ha_device[40];
|
||||
auto device_type_name = EMSdevice::device_type_2_device_name(dv.device_type);
|
||||
snprintf(ha_device, sizeof(ha_device), "%s-%s", Mqtt::basename().c_str(), device_type_name);
|
||||
@@ -754,10 +753,11 @@ bool Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const
|
||||
dev_json["name"] = Mqtt::basename() + " " + cap_name;
|
||||
free(cap_name);
|
||||
|
||||
// create only once per category
|
||||
if (create_device_config) {
|
||||
dev_json["mf"] = brand;
|
||||
dev_json["mdl"] = model;
|
||||
dev_json["via_device"] = "ems-esp";
|
||||
dev_json["via_device"] = Mqtt::basename();
|
||||
}
|
||||
|
||||
// calculate the min and max
|
||||
@@ -789,11 +789,11 @@ bool Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const
|
||||
|
||||
// publish HA sensor for System using the heartbeat tag
|
||||
bool Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
JsonObject dev_json = doc.createNestedObject("dev");
|
||||
JsonDocument doc;
|
||||
JsonObject dev_json = doc["dev"].to<JsonObject>();
|
||||
|
||||
dev_json["name"] = Mqtt::basename();
|
||||
JsonArray ids = dev_json.createNestedArray("ids");
|
||||
JsonArray ids = dev_json["ids"].to<JsonArray>();
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
return publish_ha_sensor_config(
|
||||
@@ -877,8 +877,8 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
case DeviceValueType::USHORT:
|
||||
case DeviceValueType::ULONG:
|
||||
// number - https://www.home-assistant.io/integrations/number.mqtt
|
||||
// Domoticz does not support number, use sensor
|
||||
if (discovery_type() == discoveryType::HOMEASSISTANT) {
|
||||
// older Domoticz does not support number, use sensor
|
||||
if (discovery_type() == discoveryType::HOMEASSISTANT || discovery_type() == discoveryType::DOMOTICZ_LATEST) {
|
||||
snprintf(topic, sizeof(topic), "number/%s", config_topic);
|
||||
readonly_sensors = false;
|
||||
} else {
|
||||
@@ -920,7 +920,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
}
|
||||
|
||||
// build the payload
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
JsonDocument doc;
|
||||
doc["uniq_id"] = uniq_id;
|
||||
doc["obj_id"] = uniq_id; // same as unique_id
|
||||
|
||||
@@ -945,7 +945,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
|
||||
// extend for enums, add options
|
||||
if (type == DeviceValueType::ENUM) {
|
||||
JsonArray option_list = doc.createNestedArray("ops"); // options
|
||||
JsonArray option_list = doc["ops"].to<JsonArray>();
|
||||
if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
|
||||
// use index numbers
|
||||
for (uint8_t i = 0; i < options_size; i++) {
|
||||
@@ -1151,8 +1151,8 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
}
|
||||
}
|
||||
|
||||
doc["dev"] = dev_json; // add the dev json object to the end
|
||||
add_avty_to_doc(stat_t, doc.as<JsonObject>(), val_cond); // add "availability" section
|
||||
doc["dev"] = dev_json; // add the dev json object to the end
|
||||
add_ha_sections_to_doc(nullptr, stat_t, doc, false, val_cond); // no name, since the "dev" has already been added
|
||||
|
||||
return queue_ha(topic, doc.as<JsonObject>());
|
||||
}
|
||||
@@ -1231,7 +1231,7 @@ bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/seltemp", hc_num);
|
||||
snprintf(mode_cmd_s, sizeof(mode_cmd_s), "~/thermostat/hc%d/mode", hc_num);
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_XLARGE> doc; // 1024 is not enough
|
||||
JsonDocument doc;
|
||||
|
||||
doc["~"] = Mqtt::base();
|
||||
doc["uniq_id"] = uniq_id_s;
|
||||
@@ -1254,24 +1254,14 @@ bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
|
||||
doc["mode_cmd_t"] = mode_cmd_s;
|
||||
|
||||
// the HA climate component only responds to auto, heat and off
|
||||
JsonArray modes = doc.createNestedArray("modes");
|
||||
JsonArray modes = doc["modes"].to<JsonArray>();
|
||||
|
||||
modes.add("auto");
|
||||
modes.add("heat");
|
||||
modes.add("off");
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
|
||||
char ha_device[40];
|
||||
snprintf(ha_device, sizeof(ha_device), "%s-thermostat", Mqtt::basename().c_str());
|
||||
ids.add(ha_device);
|
||||
|
||||
// device name must be different to the entity name, take the ids value we just created
|
||||
dev["name"] = ha_device;
|
||||
|
||||
// add "availability" section
|
||||
add_avty_to_doc(topic_t, doc.as<JsonObject>(), seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond);
|
||||
add_ha_sections_to_doc("thermostat", topic_t, doc, false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond);
|
||||
|
||||
return queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
}
|
||||
@@ -1295,42 +1285,74 @@ std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
|
||||
}
|
||||
}
|
||||
|
||||
// adds "availability" section to HA Discovery config
|
||||
void Mqtt::add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1, const char * cond2, const char * negcond) {
|
||||
// adds availability, dev, ids to the config section to HA Discovery config
|
||||
void Mqtt::add_ha_sections_to_doc(const char * name,
|
||||
const char * state_t,
|
||||
JsonDocument & config,
|
||||
const bool is_first,
|
||||
const char * cond1,
|
||||
const char * cond2,
|
||||
const char * negcond) {
|
||||
// adds dev section to HA Discovery config
|
||||
if (name != nullptr) {
|
||||
JsonObject dev = config["dev"].to<JsonObject>();
|
||||
char * cap_name = strdup(name);
|
||||
cap_name[0] = toupper(name[0]); // capitalize first letter
|
||||
dev["name"] = std::string(Mqtt::basename()) + " " + cap_name;
|
||||
// if it's the first in the category, attach the group to the main HA device
|
||||
if (is_first) {
|
||||
dev["mf"] = "EMS-ESP";
|
||||
dev["mdl"] = cap_name;
|
||||
dev["via_device"] = Mqtt::basename();
|
||||
}
|
||||
JsonArray ids = dev["ids"].to<JsonArray>();
|
||||
ids.add(Mqtt::basename() + "-" + Helpers::toLower(name));
|
||||
free(cap_name);
|
||||
}
|
||||
|
||||
// adds "availability" section to HA Discovery config
|
||||
JsonArray avty = config["avty"].to<JsonArray>();
|
||||
JsonDocument avty_json;
|
||||
|
||||
const char * tpl_draft = "{{'online' if %s else 'offline'}}";
|
||||
char tpl[150];
|
||||
JsonArray avty = doc.createNestedArray("avty");
|
||||
|
||||
StaticJsonDocument<512> avty_json;
|
||||
|
||||
// EMS-ESP status check
|
||||
char tpl[150];
|
||||
snprintf(tpl, sizeof(tpl), "%s/status", Mqtt::base().c_str());
|
||||
avty_json["t"] = tpl;
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, "value == 'online'");
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
avty_json.clear();
|
||||
avty_json["t"] = state_t;
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, cond1 == nullptr ? "value is defined" : cond1);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
avty.add(avty_json); // returns 0 if no mem
|
||||
|
||||
if (cond2 != nullptr) {
|
||||
// skip conditional Jinja2 templates if not home assistant
|
||||
if (discovery_type() == discoveryType::HOMEASSISTANT) {
|
||||
// condition 1
|
||||
avty_json.clear();
|
||||
avty_json["t"] = state_t;
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, cond2);
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, cond1 == nullptr ? "value is defined" : cond1);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
}
|
||||
avty.add(avty_json); // returns 0 if no mem
|
||||
|
||||
if (negcond != nullptr) {
|
||||
avty_json.clear();
|
||||
avty_json["t"] = state_t;
|
||||
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json);
|
||||
}
|
||||
// condition 2
|
||||
if (cond2 != nullptr) {
|
||||
avty_json.clear();
|
||||
avty_json["t"] = state_t;
|
||||
snprintf(tpl, sizeof(tpl), tpl_draft, cond2);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json); // returns 0 if no mem
|
||||
}
|
||||
|
||||
doc["avty_mode"] = "all";
|
||||
// negative condition
|
||||
if (negcond != nullptr) {
|
||||
avty_json.clear();
|
||||
avty_json["t"] = state_t;
|
||||
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
|
||||
avty_json["val_tpl"] = tpl;
|
||||
avty.add(avty_json); // returns 0 if no mem
|
||||
}
|
||||
|
||||
config["avty_mode"] = "all";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
11
src/mqtt.h
11
src/mqtt.h
@@ -35,7 +35,7 @@ using mqtt_sub_function_p = std::function<bool(const char * message)>;
|
||||
|
||||
class Mqtt {
|
||||
public:
|
||||
enum discoveryType : uint8_t { HOMEASSISTANT, DOMOTICZ };
|
||||
enum discoveryType : uint8_t { HOMEASSISTANT, DOMOTICZ, DOMOTICZ_LATEST };
|
||||
enum entityFormat : uint8_t { SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT };
|
||||
|
||||
void loop();
|
||||
@@ -218,8 +218,13 @@ class Mqtt {
|
||||
|
||||
static std::string tag_to_topic(uint8_t device_type, uint8_t tag);
|
||||
|
||||
static void
|
||||
add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1 = nullptr, const char * cond2 = nullptr, const char * negcond = nullptr);
|
||||
static void add_ha_sections_to_doc(const char * name,
|
||||
const char * state_t,
|
||||
JsonDocument & config,
|
||||
const bool is_first = false,
|
||||
const char * cond1 = nullptr,
|
||||
const char * cond2 = nullptr,
|
||||
const char * negcond = nullptr);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
@@ -99,7 +99,7 @@ void Shower::loop() {
|
||||
if ((timer_pause_ - timer_start_) > SHOWER_OFFSET_TIME) {
|
||||
duration_ = (timer_pause_ - timer_start_ - SHOWER_OFFSET_TIME);
|
||||
if (duration_ > SHOWER_MIN_DURATION) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
JsonDocument doc;
|
||||
|
||||
// duration in seconds
|
||||
doc["duration"] = (duration_ / 1000UL); // seconds
|
||||
@@ -177,10 +177,10 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
|
||||
// send out HA MQTT Discovery config topic
|
||||
if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char str[70];
|
||||
char stat_t[50];
|
||||
JsonDocument doc;
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char str[70];
|
||||
char stat_t[50];
|
||||
|
||||
//
|
||||
// shower active
|
||||
@@ -210,12 +210,7 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
doc["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = "EMS-ESP Shower";
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename() + "-shower");
|
||||
|
||||
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>()); // add "availability" section
|
||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, true); // create first dev & ids
|
||||
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
|
||||
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
@@ -240,12 +235,7 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
doc["dev_cla"] = "duration";
|
||||
// doc["ent_cat"] = "diagnostic";
|
||||
|
||||
JsonObject dev2 = doc.createNestedObject("dev");
|
||||
dev2["name"] = "EMS-ESP Shower";
|
||||
JsonArray ids2 = dev2.createNestedArray("ids");
|
||||
ids2.add(Mqtt::basename() + "-shower");
|
||||
|
||||
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>(), "value_json.duration is defined"); // add "availability" section
|
||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, false, "value_json.duration is defined");
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str());
|
||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
@@ -267,12 +257,7 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
doc["val_tpl"] = "{{value_json.timestamp if value_json.timestamp is defined else 0}}";
|
||||
// doc["ent_cat"] = "diagnostic";
|
||||
|
||||
JsonObject dev3 = doc.createNestedObject("dev");
|
||||
dev3["name"] = "EMS-ESP Shower";
|
||||
JsonArray ids3 = dev3.createNestedArray("ids");
|
||||
ids3.add(Mqtt::basename() + "-shower");
|
||||
|
||||
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>(), "value_json.timestamp is defined"); // add "availability" section
|
||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, false, "value_json.timestamp is defined");
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/shower_timestamp/config", Mqtt::basename().c_str());
|
||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
@@ -96,7 +96,7 @@ bool System::command_send(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
bool System::command_response(const char * value, const int8_t id, JsonObject & output) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_LARGE);
|
||||
JsonDocument doc;
|
||||
if (DeserializationError::Ok == deserializeJson(doc, Mqtt::get_response())) {
|
||||
for (JsonPair p : doc.as<JsonObject>()) {
|
||||
output[p.key()] = p.value();
|
||||
@@ -110,23 +110,23 @@ bool System::command_response(const char * value, const int8_t id, JsonObject &
|
||||
// output all the EMS devices and their values, plus the sensors and any custom entities
|
||||
// not scheduler as these are records with no output data
|
||||
bool System::command_allvalues(const char * value, const int8_t id, JsonObject & output) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE);
|
||||
JsonObject device_output;
|
||||
JsonDocument doc;
|
||||
JsonObject device_output;
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
std::string title = emsdevice->device_type_2_device_name_translated() + std::string(" ") + emsdevice->to_string();
|
||||
device_output = output.createNestedObject(title);
|
||||
device_output = output[title].to<JsonObject>();
|
||||
emsdevice->generate_values(device_output, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::API_VERBOSE); // use nested for id -1 and 0
|
||||
}
|
||||
|
||||
// Custom entities
|
||||
device_output = output.createNestedObject("Custom Entities");
|
||||
device_output = output["Custom Entities"].to<JsonObject>();
|
||||
EMSESP::webCustomEntityService.get_value_info(device_output, "");
|
||||
|
||||
// Sensors
|
||||
device_output = output.createNestedObject("Analog Sensors");
|
||||
device_output = output["Analog Sensors"].to<JsonObject>();
|
||||
EMSESP::analogsensor_.command_info(nullptr, 0, device_output);
|
||||
device_output = output.createNestedObject("Temperature Sensors");
|
||||
device_output = output["Temperature Sensors"].to<JsonObject>();
|
||||
EMSESP::temperaturesensor_.command_info(nullptr, 0, device_output);
|
||||
|
||||
return true;
|
||||
@@ -586,7 +586,7 @@ void System::send_info_mqtt() {
|
||||
return;
|
||||
}
|
||||
_connection = connection;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
|
||||
JsonDocument doc;
|
||||
doc["event"] = "connected";
|
||||
doc["version"] = EMSESP_APP_VERSION;
|
||||
|
||||
@@ -697,8 +697,8 @@ void System::send_heartbeat() {
|
||||
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
JsonDocument doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
if (heartbeat_json(json)) {
|
||||
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
@@ -1051,8 +1051,8 @@ bool System::check_restore() {
|
||||
// see if we have a temp file, if so try and read it
|
||||
File new_file = LittleFS.open(TEMP_FILENAME_PATH);
|
||||
if (new_file) {
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(FS_BUFFER_SIZE);
|
||||
DeserializationError error = deserializeJson(jsonDocument, new_file);
|
||||
JsonDocument jsonDocument;
|
||||
DeserializationError error = deserializeJson(jsonDocument, new_file);
|
||||
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
|
||||
JsonObject input = jsonDocument.as<JsonObject>();
|
||||
// see what type of file it is, either settings or customization. anything else is ignored
|
||||
@@ -1177,11 +1177,11 @@ void System::extractSettings(const char * filename, const char * section, JsonOb
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File settingsFile = LittleFS.open(filename);
|
||||
if (settingsFile) {
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(FS_BUFFER_SIZE);
|
||||
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
|
||||
JsonDocument jsonDocument;
|
||||
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
|
||||
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
|
||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||
JsonObject node = output.createNestedObject(section);
|
||||
JsonObject node = output[section].to<JsonObject>();
|
||||
for (JsonPair kvp : jsonObject) {
|
||||
node[kvp.key()] = kvp.value();
|
||||
}
|
||||
@@ -1214,7 +1214,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
JsonObject node;
|
||||
|
||||
// System
|
||||
node = output.createNestedObject("System Info");
|
||||
node = output["System Info"].to<JsonObject>();
|
||||
node["version"] = EMSESP_APP_VERSION;
|
||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
node["uptime (seconds)"] = uuid::get_uptime_sec();
|
||||
@@ -1231,7 +1231,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// Network Status
|
||||
node = output.createNestedObject("Network Info");
|
||||
node = output["Network Info"].to<JsonObject>();
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
node["network"] = "Ethernet";
|
||||
node["hostname"] = ETH.getHostname();
|
||||
@@ -1279,7 +1279,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
#endif
|
||||
|
||||
// NTP status
|
||||
node = output.createNestedObject("NTP Info");
|
||||
node = output["NTP Info"].to<JsonObject>();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["NTP status"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected";
|
||||
EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) {
|
||||
@@ -1290,7 +1290,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
});
|
||||
|
||||
// OTA status
|
||||
node = output.createNestedObject("OTA Info");
|
||||
node = output["OTA Info"].to<JsonObject>();
|
||||
EMSESP::esp8266React.getOTASettingsService()->read([&](OTASettings & settings) {
|
||||
node["enabled"] = settings.enabled;
|
||||
node["port"] = settings.port;
|
||||
@@ -1298,7 +1298,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
#endif
|
||||
|
||||
// MQTT Status
|
||||
node = output.createNestedObject("MQTT Info");
|
||||
node = output["MQTT Info"].to<JsonObject>();
|
||||
node["MQTT status"] = Mqtt::connected() ? F_(connected) : F_(disconnected);
|
||||
if (Mqtt::enabled()) {
|
||||
node["MQTT publishes"] = Mqtt::publish_count();
|
||||
@@ -1333,7 +1333,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
});
|
||||
|
||||
// Syslog Status
|
||||
node = output.createNestedObject("Syslog Info");
|
||||
node = output["Syslog Info"].to<JsonObject>();
|
||||
node["enabled"] = EMSESP::system_.syslog_enabled_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (EMSESP::system_.syslog_enabled_) {
|
||||
@@ -1345,7 +1345,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
#endif
|
||||
|
||||
// Sensor Status
|
||||
node = output.createNestedObject("Sensor Info");
|
||||
node = output["Sensor Info"].to<JsonObject>();
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
node["temperature sensors"] = EMSESP::temperaturesensor_.no_sensors();
|
||||
node["temperature sensor reads"] = EMSESP::temperaturesensor_.reads();
|
||||
@@ -1358,12 +1358,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
}
|
||||
|
||||
// API Status
|
||||
node = output.createNestedObject("API Info");
|
||||
node = output["API Info"].to<JsonObject>();
|
||||
node["API calls"] = WebAPIService::api_count();
|
||||
node["API fails"] = WebAPIService::api_fails();
|
||||
|
||||
// EMS Bus Status
|
||||
node = output.createNestedObject("Bus Info");
|
||||
node = output["Bus Info"].to<JsonObject>();
|
||||
switch (EMSESP::bus_status()) {
|
||||
case EMSESP::BUS_STATUS_OFFLINE:
|
||||
node["bus status"] = "disconnected";
|
||||
@@ -1391,7 +1391,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
}
|
||||
|
||||
// Settings
|
||||
node = output.createNestedObject("Settings");
|
||||
node = output["Settings"].to<JsonObject>();
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
node["board profile"] = settings.board_profile;
|
||||
node["locale"] = settings.locale;
|
||||
@@ -1432,11 +1432,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
|
||||
// Devices - show EMS devices if we have any
|
||||
if (!EMSESP::emsdevices.empty()) {
|
||||
JsonArray devices = output.createNestedArray("Devices");
|
||||
JsonArray devices = output["Devices"].to<JsonArray>();
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_class.first)) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["type"] = emsdevice->device_type_name(); // non translated name
|
||||
obj["name"] = emsdevice->name();
|
||||
obj["device id"] = Helpers::hextoa(emsdevice->device_id());
|
||||
|
||||
@@ -108,11 +108,14 @@ void TemperatureSensor::loop() {
|
||||
if (++scanretry_ > SCAN_MAX) { // every 30 sec
|
||||
scanretry_ = 0;
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_ERROR("Bus reset failed");
|
||||
LOG_DEBUG("Error: Bus reset failed");
|
||||
#endif
|
||||
#ifndef EMSESP_TEST
|
||||
// don't reset if running in test mode where we simulate sensors
|
||||
for (auto & sensor : sensors_) {
|
||||
sensor.temperature_c = EMS_VALUE_SHORT_NOTSET;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +205,7 @@ void TemperatureSensor::loop() {
|
||||
bus_.depower();
|
||||
}
|
||||
// check for missing sensors after some samples
|
||||
// but don't do this if running in test mode where we simulate sensors
|
||||
if (++scancnt_ > SCAN_MAX) {
|
||||
for (auto & sensor : sensors_) {
|
||||
if (!sensor.read) {
|
||||
@@ -309,6 +313,7 @@ bool TemperatureSensor::update(const std::string & id, const std::string & name,
|
||||
// if HA is enabled then delete the old record
|
||||
if (Mqtt::ha_enabled()) {
|
||||
remove_ha_topic(id);
|
||||
sensor.ha_registered = false; // force HA configs to be re-created
|
||||
}
|
||||
|
||||
sensor.set_name(name);
|
||||
@@ -371,7 +376,7 @@ bool TemperatureSensor::command_info(const char * value, const int8_t id, JsonOb
|
||||
for (const auto & sensor : sensors_) {
|
||||
char val[10];
|
||||
if (id == -1) { // show number and id, info command
|
||||
JsonObject dataSensor = output.createNestedObject(sensor.name());
|
||||
JsonObject dataSensor = output[sensor.name()].to<JsonObject>();
|
||||
dataSensor["id"] = sensor.id();
|
||||
dataSensor["uom"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
dataSensor["type"] = F_(number);
|
||||
@@ -458,6 +463,7 @@ void TemperatureSensor::remove_ha_topic(const std::string & id) {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Removing HA config for temperature sensor ID %s", id.c_str());
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = id;
|
||||
@@ -474,7 +480,6 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
}
|
||||
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
|
||||
if (num_sensors == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -485,19 +490,19 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(120 * num_sensors);
|
||||
JsonDocument doc;
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
bool has_value = Helpers::hasValue(sensor.temperature_c);
|
||||
char val[10];
|
||||
if (Mqtt::is_nested()) {
|
||||
JsonObject dataSensor = doc.createNestedObject(sensor.id());
|
||||
dataSensor["name"] = sensor.name();
|
||||
if (has_value) {
|
||||
dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
if (has_value) {
|
||||
char val[10];
|
||||
if (Mqtt::is_nested()) {
|
||||
JsonObject dataSensor = doc[sensor.id()].to<JsonObject>();
|
||||
dataSensor["name"] = sensor.name();
|
||||
dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
} else {
|
||||
doc[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
} else if (has_value) {
|
||||
doc[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
|
||||
// create the HA MQTT config
|
||||
@@ -509,7 +514,7 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
} else if (!sensor.ha_registered || force) {
|
||||
LOG_DEBUG("Recreating HA config for sensor ID %s", sensor.id().c_str());
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
JsonDocument config; // this needs to be large because of all the copying in add_ha_sections_to_doc()
|
||||
config["dev_cla"] = "temperature";
|
||||
|
||||
char stat_t[50];
|
||||
@@ -518,8 +523,8 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
|
||||
config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[95];
|
||||
char val_obj[70];
|
||||
char val_cond[170];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s'].temp", sensor.id().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined and %s is defined", sensor.id().c_str(), val_obj);
|
||||
@@ -527,7 +532,14 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else -55}}";
|
||||
|
||||
// for the value template, there's a problem still with Domoticz probably due to the special characters.
|
||||
// See https://github.com/emsesp/EMS-ESP32/issues/1360
|
||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else -55}}";
|
||||
} else {
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + "}}"; // ommit value conditional Jinja2 template code
|
||||
}
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
@@ -543,13 +555,16 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
snprintf(name, sizeof(name), "%s", sensor.name().c_str());
|
||||
config["name"] = name;
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
dev["name"] = Mqtt::basename() + " Temperature";
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename() + "-temperature");
|
||||
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
|
||||
bool is_ha_device_created = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.ha_registered) {
|
||||
is_ha_device_created = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
Mqtt::add_ha_sections_to_doc("temperature", stat_t, config, !is_ha_device_created, val_cond);
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
@@ -568,7 +583,6 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
Mqtt::queue_publish(topic, doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
|
||||
// skip crc from id
|
||||
TemperatureSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
: internal_id_(((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
@@ -628,15 +642,17 @@ void TemperatureSensor::test() {
|
||||
// add 2 temperature sensors
|
||||
uint8_t addr[ADDR_LEN] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
sensors_.emplace_back(addr);
|
||||
// sensors_.back().apply_customization();
|
||||
sensors_.back().temperature_c = 123;
|
||||
sensors_.back().read = true;
|
||||
sensors_.back().apply_customization();
|
||||
publish_sensor(sensors_.back()); // call publish single
|
||||
|
||||
uint8_t addr2[ADDR_LEN] = {11, 12, 13, 14, 15, 16, 17, 18};
|
||||
sensors_.emplace_back(addr2);
|
||||
// sensors_.back().apply_customization();
|
||||
sensors_.back().temperature_c = 456;
|
||||
sensors_.back().read = true;
|
||||
sensors_.back().apply_customization();
|
||||
publish_sensor(sensors_.back()); // call publish single
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ class TemperatureSensor {
|
||||
|
||||
bool apply_customization();
|
||||
|
||||
// initial values
|
||||
int16_t temperature_c = EMS_VALUE_SHORT_NOTSET;
|
||||
bool read = false;
|
||||
bool ha_registered = false;
|
||||
|
||||
@@ -286,7 +286,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
emsesp::EMSESP::temperaturesensor_.test();
|
||||
|
||||
// shell.invoke_command("show devices");
|
||||
shell.invoke_command("show values");
|
||||
// shell.invoke_command("show values");
|
||||
shell.invoke_command("call system allvalues");
|
||||
// shell.invoke_command("call system publish");
|
||||
// shell.invoke_command("show mqtt");
|
||||
@@ -545,7 +545,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
DynamicJsonDocument doc(8000); // some absurd high number
|
||||
JsonDocument doc; // some absurd high number
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
doc.clear();
|
||||
@@ -570,8 +570,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
serializeJson(doc, Serial);
|
||||
Serial.print(COLOR_RESET);
|
||||
Serial.println();
|
||||
Serial.print("** memoryUsage=");
|
||||
Serial.print(doc.memoryUsage());
|
||||
Serial.print(" measureMsgPack=");
|
||||
Serial.print(measureMsgPack(doc));
|
||||
Serial.print(" measureJson=");
|
||||
@@ -705,6 +703,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
if (command == "temperature") {
|
||||
shell.printfln("Testing adding Temperature sensor");
|
||||
emsesp::EMSESP::temperaturesensor_.test();
|
||||
shell.invoke_command("show values");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -847,7 +846,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("thermostat");
|
||||
|
||||
AsyncWebServerRequest request;
|
||||
DynamicJsonDocument doc(2000);
|
||||
JsonDocument doc;
|
||||
JsonVariant json;
|
||||
request.method(HTTP_GET);
|
||||
|
||||
@@ -892,8 +891,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
AsyncWebServerRequest request;
|
||||
request.method(HTTP_POST);
|
||||
DynamicJsonDocument doc(2000);
|
||||
JsonVariant json;
|
||||
JsonDocument doc;
|
||||
JsonVariant json;
|
||||
|
||||
char data[] = "{\"value\":\"off\"}";
|
||||
deserializeJson(doc, data);
|
||||
@@ -916,7 +915,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("thermostat");
|
||||
|
||||
AsyncWebServerRequest requestX;
|
||||
DynamicJsonDocument docX(2000);
|
||||
JsonDocument docX;
|
||||
JsonVariant jsonX;
|
||||
|
||||
requestX.method(HTTP_GET);
|
||||
@@ -1107,8 +1106,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// POST tests
|
||||
request.method(HTTP_POST);
|
||||
DynamicJsonDocument doc(2000);
|
||||
JsonVariant json;
|
||||
JsonDocument doc;
|
||||
JsonVariant json;
|
||||
|
||||
// 1
|
||||
char data1[] = "{\"entity\":\"seltemp\",\"value\":11}";
|
||||
@@ -1535,7 +1534,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
if (command == "mqtt2") {
|
||||
shell.printfln("Testing MQTT large payloads...");
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE);
|
||||
JsonDocument doc;
|
||||
|
||||
char key[8];
|
||||
char value[8];
|
||||
@@ -1548,7 +1547,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
}
|
||||
doc.shrinkToFit();
|
||||
JsonObject jo = doc.as<JsonObject>();
|
||||
shell.printfln("Size of JSON payload = %d", jo.memoryUsage());
|
||||
shell.printfln("Length of JSON payload = %d", measureJson(jo));
|
||||
|
||||
Mqtt::queue_publish("test", jo);
|
||||
|
||||
@@ -58,7 +58,6 @@ namespace emsesp {
|
||||
// #define EMSESP_DEBUG_DEFAULT "custom_entities"
|
||||
// #define EMSESP_DEBUG_DEFAULT "heat_exchange"
|
||||
|
||||
|
||||
class Test {
|
||||
public:
|
||||
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & data = "");
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.6.5-test.7"
|
||||
#define EMSESP_APP_VERSION "3.6.5-test.8"
|
||||
|
||||
@@ -27,8 +27,8 @@ uint16_t WebAPIService::api_fails_ = 0;
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS, must use 'Content-Type: application/json' in header
|
||||
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS
|
||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2)) { // for POSTS, must use 'Content-Type: application/json' in header
|
||||
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS
|
||||
server->addHandler(&_apiHandler);
|
||||
|
||||
// for settings
|
||||
@@ -45,8 +45,8 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security
|
||||
// GET /{device}/{entity}
|
||||
void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
|
||||
// has no body JSON so create dummy as empty input object
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
|
||||
JsonObject input = input_doc.to<JsonObject>();
|
||||
JsonDocument input_doc;
|
||||
JsonObject input = input_doc.to<JsonObject>();
|
||||
parse(request, input);
|
||||
}
|
||||
|
||||
@@ -106,13 +106,15 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
emsesp::EMSESP::system_.refreshHeapMem();
|
||||
|
||||
// output json buffer
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE;
|
||||
auto * response = new PrettyAsyncJsonResponse(false, buffer);
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
buffer -= 1024;
|
||||
response = new PrettyAsyncJsonResponse(false, buffer);
|
||||
}
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
|
||||
// add more mem if needed - won't be needed in ArduinoJson 7
|
||||
// while (!response->getSize()) {
|
||||
// delete response;
|
||||
// buffer -= 1024;
|
||||
// response = new AsyncJsonResponse(false, buffer);
|
||||
// }
|
||||
|
||||
JsonObject output = response->getRoot();
|
||||
|
||||
// call command
|
||||
@@ -161,12 +163,12 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
}
|
||||
|
||||
void WebAPIService::getSettings(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, FS_BUFFER_SIZE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["type"] = "settings";
|
||||
|
||||
JsonObject node = root.createNestedObject("System");
|
||||
JsonObject node = root["System"].to<JsonObject>();
|
||||
node["version"] = EMSESP_APP_VERSION;
|
||||
|
||||
System::extractSettings(NETWORK_SETTINGS_FILE, "Network", root);
|
||||
@@ -182,7 +184,7 @@ void WebAPIService::getSettings(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
void WebAPIService::getCustomizations(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, FS_BUFFER_SIZE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["type"] = "customizations";
|
||||
@@ -194,7 +196,7 @@ void WebAPIService::getCustomizations(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
void WebAPIService::getSchedule(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, FS_BUFFER_SIZE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["type"] = "schedule";
|
||||
@@ -206,7 +208,7 @@ void WebAPIService::getSchedule(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
void WebAPIService::getEntities(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, FS_BUFFER_SIZE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["type"] = "entities";
|
||||
|
||||
@@ -30,7 +30,7 @@ WebCustomEntityService::WebCustomEntityService(AsyncWebServer * server, FS * fs,
|
||||
EMSESP_CUSTOMENTITY_SERVICE_PATH,
|
||||
securityManager,
|
||||
AuthenticationPredicates::IS_AUTHENTICATED)
|
||||
, _fsPersistence(WebCustomEntity::read, WebCustomEntity::update, this, fs, EMSESP_CUSTOMENTITY_FILE, FS_BUFFER_SIZE) {
|
||||
, _fsPersistence(WebCustomEntity::read, WebCustomEntity::update, this, fs, EMSESP_CUSTOMENTITY_FILE) {
|
||||
}
|
||||
|
||||
// load the settings when the service starts
|
||||
@@ -43,10 +43,10 @@ void WebCustomEntityService::begin() {
|
||||
// this creates the entity file, saving it to the FS
|
||||
// and also calls when the Entity web page is refreshed
|
||||
void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject & root) {
|
||||
JsonArray entity = root.createNestedArray("entities");
|
||||
JsonArray entity = root["entities"].to<JsonArray>();
|
||||
uint8_t counter = 0;
|
||||
for (const CustomEntityItem & entityItem : webEntity.customEntityItems) {
|
||||
JsonObject ei = entity.createNestedObject();
|
||||
JsonObject ei = entity.add<JsonObject>();
|
||||
ei["id"] = counter++; // id is only used to render the table and must be unique
|
||||
ei["device_id"] = entityItem.device_id;
|
||||
ei["type_id"] = entityItem.type_id;
|
||||
@@ -70,7 +70,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject & root, WebCustomEntity & w
|
||||
const char * json =
|
||||
"{\"entities\": [{\"id\":0,\"device_id\":8,\"type_id\":24,\"offset\":0,\"factor\":1,\"name\":\"boiler_flowtemp\",\"uom\":1,\"value_type\":1,\"writeable\":true}]}";
|
||||
// clang-format on
|
||||
StaticJsonDocument<500> doc;
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, json);
|
||||
root = doc.as<JsonObject>();
|
||||
Serial.println(COLOR_BRIGHT_MAGENTA);
|
||||
@@ -342,8 +342,8 @@ void WebCustomEntityService::publish_single(const CustomEntityItem & entity) {
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", "custom_data", entity.name.c_str());
|
||||
}
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
JsonDocument doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
render_value(output, entity, true);
|
||||
Mqtt::queue_publish(topic, output["value"].as<std::string>());
|
||||
}
|
||||
@@ -353,9 +353,11 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
if (force) {
|
||||
ha_registered_ = false;
|
||||
}
|
||||
|
||||
if (!Mqtt::enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
|
||||
if (customEntityItems->size() == 0) {
|
||||
return;
|
||||
@@ -366,15 +368,16 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
bool ha_created = ha_registered_;
|
||||
JsonDocument doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
bool ha_created = ha_registered_;
|
||||
|
||||
for (const CustomEntityItem & entityItem : *customEntityItems) {
|
||||
render_value(output, entityItem);
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && !ha_registered_) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
char stat_t[50];
|
||||
JsonDocument config;
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/custom_data", Mqtt::base().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
@@ -392,12 +395,13 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
config["name"] = entityItem.name.c_str();
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
if (entityItem.writeable) {
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
snprintf(topic, sizeof(topic), "switch/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else if (entityItem.value_type == DeviceValueType::STRING) {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||
} else if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT || Mqtt::discovery_type() == Mqtt::discoveryType::DOMOTICZ_LATEST) {
|
||||
snprintf(topic, sizeof(topic), "number/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
@@ -412,6 +416,7 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
@@ -426,16 +431,13 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
}
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
dev["name"] = Mqtt::basename() + " Custom";
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename() + "-custom");
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
Mqtt::add_ha_sections_to_doc("custom", stat_t, config, !ha_created, val_cond);
|
||||
|
||||
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
|
||||
ha_registered_ = ha_created;
|
||||
if (output.size() > 0) {
|
||||
Mqtt::queue_publish("custom_data", output);
|
||||
@@ -450,9 +452,9 @@ uint8_t WebCustomEntityService::count_entities() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
uint8_t count = 0;
|
||||
JsonDocument doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
uint8_t count = 0;
|
||||
for (const CustomEntityItem & entity : *customEntityItems) {
|
||||
render_value(output, entity);
|
||||
count += (output.containsKey(entity.name) || entity.writeable) ? 1 : 0;
|
||||
@@ -474,10 +476,10 @@ void WebCustomEntityService::generate_value_web(JsonObject & output) {
|
||||
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
|
||||
|
||||
output["label"] = (std::string) "Custom Entities";
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
JsonArray data = output["data"].to<JsonArray>();
|
||||
uint8_t index = 0;
|
||||
for (const CustomEntityItem & entity : *customEntityItems) {
|
||||
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
|
||||
JsonObject obj = data.add<JsonObject>(); // create the object, we know there is a value
|
||||
obj["id"] = "00" + entity.name;
|
||||
obj["u"] = entity.uom;
|
||||
if (entity.writeable) {
|
||||
@@ -492,7 +494,7 @@ void WebCustomEntityService::generate_value_web(JsonObject & output) {
|
||||
case DeviceValueType::BOOL: {
|
||||
char s[12];
|
||||
obj["v"] = Helpers::render_boolean(s, (uint8_t)entity.value, true);
|
||||
JsonArray l = obj.createNestedArray("l");
|
||||
JsonArray l = obj["l"].to<JsonArray>();
|
||||
l.add(Helpers::render_boolean(s, false, true));
|
||||
l.add(Helpers::render_boolean(s, true, true));
|
||||
break;
|
||||
|
||||
@@ -51,25 +51,25 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f
|
||||
|
||||
_masked_entities_handler.setMethod(HTTP_POST);
|
||||
_masked_entities_handler.setMaxContentLength(2048);
|
||||
_masked_entities_handler.setMaxJsonBufferSize(2048);
|
||||
server->addHandler(&_masked_entities_handler);
|
||||
}
|
||||
|
||||
// this creates the customization file, saving it to the FS
|
||||
void WebCustomization::read(WebCustomization & customizations, JsonObject & root) {
|
||||
// Temperature Sensor customization
|
||||
JsonArray sensorsJson = root.createNestedArray("ts");
|
||||
JsonArray sensorsJson = root["ts"].to<JsonArray>();
|
||||
|
||||
for (const SensorCustomization & sensor : customizations.sensorCustomizations) {
|
||||
JsonObject sensorJson = sensorsJson.createNestedObject();
|
||||
JsonObject sensorJson = sensorsJson.add<JsonObject>();
|
||||
sensorJson["id"] = sensor.id; // ID of chip
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
}
|
||||
|
||||
// Analog Sensor customization
|
||||
JsonArray analogJson = root.createNestedArray("as");
|
||||
JsonArray analogJson = root["as"].to<JsonArray>();
|
||||
for (const AnalogCustomization & sensor : customizations.analogCustomizations) {
|
||||
JsonObject sensorJson = analogJson.createNestedObject();
|
||||
JsonObject sensorJson = analogJson.add<JsonObject>();
|
||||
sensorJson["gpio"] = sensor.gpio; // g
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
@@ -79,14 +79,14 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject & root
|
||||
}
|
||||
|
||||
// Masked entities customization
|
||||
JsonArray masked_entitiesJson = root.createNestedArray("masked_entities");
|
||||
JsonArray masked_entitiesJson = root["masked_entities"].to<JsonArray>();
|
||||
for (const EntityCustomization & entityCustomization : customizations.entityCustomizations) {
|
||||
JsonObject entityJson = masked_entitiesJson.createNestedObject();
|
||||
JsonObject entityJson = masked_entitiesJson.add<JsonObject>();
|
||||
entityJson["product_id"] = entityCustomization.product_id;
|
||||
entityJson["device_id"] = entityCustomization.device_id;
|
||||
|
||||
// entries are in the form <XX><shortname>[|optional customname] e.g "08heatingactive|heating is on"
|
||||
JsonArray masked_entityJson = entityJson.createNestedArray("entity_ids");
|
||||
JsonArray masked_entityJson = entityJson["entity_ids"].to<JsonArray>();
|
||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||
masked_entityJson.add(entity_id);
|
||||
}
|
||||
@@ -98,9 +98,9 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject & root
|
||||
StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & customizations) {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// invoke some fake data for testing
|
||||
const char * json = "{\"ts\":[],\"as\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom "
|
||||
"name for heating active (HS1)\",\"08tapwateractive\"]}]}";
|
||||
StaticJsonDocument<500> doc;
|
||||
const char * json = "{\"ts\":[],\"as\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom "
|
||||
"name for heating active (HS1)\",\"08tapwateractive\"]}]}";
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, json);
|
||||
root = doc.as<JsonObject>();
|
||||
Serial.println(COLOR_BRIGHT_MAGENTA);
|
||||
@@ -179,15 +179,15 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques
|
||||
|
||||
// send back a list of devices used in the customization web page
|
||||
void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
// controller is ignored since it doesn't have any associated entities
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
JsonArray devices = root["devices"].to<JsonArray>();
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->has_entities()) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["i"] = emsdevice->unique_id(); // its unique id
|
||||
obj["s"] = std::string(emsdevice->device_type_2_device_name_translated()) + " (" + emsdevice->name() + ")"; // shortname, is device type translated
|
||||
obj["tn"] = emsdevice->device_type_name(); // non-translated, lower-case
|
||||
@@ -205,14 +205,14 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam(F_(id))) {
|
||||
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
||||
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
|
||||
auto * response = new MsgpackAsyncJsonResponse(true, buffer);
|
||||
auto * response = new MsgpackAsyncJsonResponse(true);
|
||||
|
||||
// while (!response) {
|
||||
// delete response;
|
||||
// buffer -= 1024;
|
||||
// response = new MsgpackAsyncJsonResponse(true, buffer);
|
||||
// }
|
||||
|
||||
while (!response) {
|
||||
delete response;
|
||||
buffer -= 1024;
|
||||
response = new MsgpackAsyncJsonResponse(true, buffer);
|
||||
}
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->unique_id() == id) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
@@ -71,15 +71,15 @@ void WebDataService::scan_devices(AsyncWebServerRequest * request) {
|
||||
// this is used in the dashboard and contains all ems device information
|
||||
// /coreData endpoint
|
||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
JsonArray devices = root["devices"].to<JsonArray>();
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
// ignore controller
|
||||
if (emsdevice && (emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER || emsdevice->count_entities() > 0)) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["id"] = emsdevice->unique_id(); // a unique id
|
||||
obj["tn"] = emsdevice->device_type_2_device_name_translated(); // translated device type name
|
||||
obj["t"] = emsdevice->device_type(); // device type number
|
||||
@@ -95,7 +95,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
// add any custom entities
|
||||
uint8_t customEntities = EMSESP::webCustomEntityService.count_entities();
|
||||
if (customEntities) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["id"] = 99; // the last unique id
|
||||
obj["tn"] = Helpers::translated_word(FL_(custom_device)); // translated device type name
|
||||
obj["t"] = EMSdevice::DeviceType::CUSTOM; // device type number
|
||||
@@ -116,14 +116,14 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
// sensor data - sends back to web
|
||||
// /sensorData endpoint
|
||||
void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// temperature sensors
|
||||
JsonArray sensors = root.createNestedArray("ts");
|
||||
JsonArray sensors = root["ts"].to<JsonArray>();
|
||||
if (EMSESP::temperaturesensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::temperaturesensor_.sensors()) {
|
||||
JsonObject obj = sensors.createNestedObject();
|
||||
JsonObject obj = sensors.add<JsonObject>();
|
||||
obj["id"] = sensor.id(); // id as string
|
||||
obj["n"] = sensor.name(); // name
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
@@ -143,11 +143,11 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
// analog sensors
|
||||
JsonArray analogs = root.createNestedArray("as");
|
||||
JsonArray analogs = root["as"].to<JsonArray>();
|
||||
if (EMSESP::analog_enabled() && EMSESP::analogsensor_.have_sensors()) {
|
||||
uint8_t count = 0;
|
||||
for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
|
||||
JsonObject obj = analogs.createNestedObject();
|
||||
JsonObject obj = analogs.add<JsonObject>();
|
||||
obj["id"] = ++count; // needed for sorting table
|
||||
obj["g"] = sensor.gpio();
|
||||
obj["n"] = sensor.name();
|
||||
@@ -178,15 +178,14 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam(F_(id))) {
|
||||
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
||||
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
|
||||
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
auto * response = new MsgpackAsyncJsonResponse(false);
|
||||
|
||||
// check size
|
||||
while (!response) {
|
||||
delete response;
|
||||
buffer -= 1024;
|
||||
response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
}
|
||||
// while (!response) {
|
||||
// delete response;
|
||||
// buffer -= 1024;
|
||||
// response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
// }
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->unique_id() == id) {
|
||||
@@ -249,7 +248,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
||||
cmd = Command::parse_command_string(cmd, id); // extract hc or wwc
|
||||
|
||||
// create JSON for output
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject output = response->getRoot();
|
||||
|
||||
// the data could be in any format, but we need string
|
||||
@@ -289,7 +288,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
||||
// parse the command as it could have a hc or wwc prefixed, e.g. hc2/seltemp
|
||||
int8_t id = -1;
|
||||
cmd = Command::parse_command_string(cmd, id);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject output = response->getRoot();
|
||||
uint8_t return_code = CommandRet::NOT_FOUND;
|
||||
uint8_t device_type = EMSdevice::DeviceType::CUSTOM;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace emsesp {
|
||||
|
||||
WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: events_(EVENT_SOURCE_LOG_PATH)
|
||||
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2), 256) {
|
||||
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2)) {
|
||||
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getValues, this, _1)); // get settings
|
||||
@@ -183,10 +183,8 @@ char * WebLogService::messagetime(char * out, const uint64_t t, const size_t buf
|
||||
|
||||
// send to web eventsource
|
||||
void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
DynamicJsonDocument jsonDocument(EMSESP_JSON_SIZE_LARGE);
|
||||
if (jsonDocument.capacity() == 0) {
|
||||
return;
|
||||
}
|
||||
JsonDocument jsonDocument;
|
||||
|
||||
JsonObject logEvent = jsonDocument.to<JsonObject>();
|
||||
char time_string[25];
|
||||
|
||||
@@ -234,7 +232,7 @@ void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & jso
|
||||
|
||||
// return the current value settings after a GET
|
||||
void WebLogService::getValues(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["level"] = log_level();
|
||||
root["max_messages"] = maximum_log_messages();
|
||||
|
||||
@@ -37,10 +37,10 @@ void WebSchedulerService::begin() {
|
||||
// this creates the scheduler file, saving it to the FS
|
||||
// and also calls when the Scheduler web page is refreshed
|
||||
void WebScheduler::read(WebScheduler & webScheduler, JsonObject & root) {
|
||||
JsonArray schedule = root.createNestedArray("schedule");
|
||||
JsonArray schedule = root["schedule"].to<JsonArray>();
|
||||
uint8_t counter = 0;
|
||||
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||
JsonObject si = schedule.createNestedObject();
|
||||
JsonObject si = schedule.add<JsonObject>();
|
||||
si["id"] = counter++; // id is only used to render the table and must be unique
|
||||
si["active"] = scheduleItem.active;
|
||||
si["flags"] = scheduleItem.flags;
|
||||
@@ -59,7 +59,7 @@ StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webSche
|
||||
const char * json =
|
||||
"{\"schedule\": [{\"id\":1,\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1mode\",\"value\": \"day\",\"name\": \"turn on "
|
||||
"central heating\"}]}";
|
||||
StaticJsonDocument<500> doc;
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, json);
|
||||
root = doc.as<JsonObject>();
|
||||
Serial.println(COLOR_BRIGHT_MAGENTA);
|
||||
@@ -113,6 +113,7 @@ bool WebSchedulerService::command_setvalue(const char * value, const std::string
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.name == name) {
|
||||
@@ -143,9 +144,11 @@ bool WebSchedulerService::get_value_info(JsonObject & output, const char * cmd)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (scheduleItems->size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen(cmd) == 0 || Helpers::toLower(cmd) == F_(values) || Helpers::toLower(cmd) == F_(info)) {
|
||||
// list all names
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
@@ -162,6 +165,7 @@ bool WebSchedulerService::get_value_info(JsonObject & output, const char * cmd)
|
||||
}
|
||||
return (output.size() > 0);
|
||||
}
|
||||
|
||||
char command_s[30];
|
||||
strlcpy(command_s, cmd, sizeof(command_s));
|
||||
char * attribute_s = nullptr;
|
||||
@@ -172,6 +176,7 @@ bool WebSchedulerService::get_value_info(JsonObject & output, const char * cmd)
|
||||
*breakp = '\0';
|
||||
attribute_s = breakp + 1;
|
||||
}
|
||||
|
||||
JsonVariant data;
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (Helpers::toLower(scheduleItem.name) == Helpers::toLower(command_s)) {
|
||||
@@ -192,14 +197,17 @@ bool WebSchedulerService::get_value_info(JsonObject & output, const char * cmd)
|
||||
output["visible"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute_s && output.containsKey(attribute_s)) {
|
||||
data = output[attribute_s];
|
||||
output.clear();
|
||||
output["api_data"] = data;
|
||||
}
|
||||
|
||||
if (output.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
output["message"] = "unknown command";
|
||||
return false;
|
||||
}
|
||||
@@ -209,12 +217,14 @@ void WebSchedulerService::publish_single(const char * name, const bool state) {
|
||||
if (!Mqtt::publish_single() || name == nullptr || name[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", F_(scheduler), name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(scheduler), "_data", name);
|
||||
}
|
||||
|
||||
char payload[12];
|
||||
Mqtt::queue_publish(topic, Helpers::render_boolean(payload, state));
|
||||
}
|
||||
@@ -224,21 +234,24 @@ void WebSchedulerService::publish(const bool force) {
|
||||
if (force) {
|
||||
ha_registered_ = false;
|
||||
}
|
||||
|
||||
if (!Mqtt::enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mqtt::publish_single() && force) {
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
publish_single(scheduleItem.name.c_str(), scheduleItem.active);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
bool ha_created = ha_registered_;
|
||||
JsonDocument doc;
|
||||
bool ha_created = ha_registered_;
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.name.empty() && !doc.containsKey(scheduleItem.name)) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
@@ -252,8 +265,8 @@ void WebSchedulerService::publish(const bool force) {
|
||||
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && !ha_registered_) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
char stat_t[50];
|
||||
JsonDocument config;
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/scheduler_data", Mqtt::base().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
@@ -272,9 +285,11 @@ void WebSchedulerService::publish(const bool force) {
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
snprintf(topic, sizeof(topic), "switch/%s/scheduler_%s/config", Mqtt::basename().c_str(), scheduleItem.name.c_str());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/scheduler/%s", Mqtt::base().c_str(), scheduleItem.name.c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
config["pl_on"] = true;
|
||||
config["pl_off"] = false;
|
||||
@@ -287,13 +302,8 @@ void WebSchedulerService::publish(const bool force) {
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
dev["name"] = Mqtt::basename() + " Scheduler";
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename() + "-scheduler");
|
||||
Mqtt::add_ha_sections_to_doc("scheduler", stat_t, config, !ha_created, val_cond);
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
@@ -309,6 +319,7 @@ bool WebSchedulerService::has_commands() {
|
||||
if (scheduleItems->size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.name.empty()) {
|
||||
return true;
|
||||
@@ -319,14 +330,14 @@ bool WebSchedulerService::has_commands() {
|
||||
|
||||
// execute scheduled command
|
||||
bool WebSchedulerService::command(const char * cmd, const char * data) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_input;
|
||||
JsonObject input = doc_input.to<JsonObject>();
|
||||
JsonDocument doc_input;
|
||||
JsonObject input = doc_input.to<JsonObject>();
|
||||
if (strlen(data)) { // empty data queries a value
|
||||
input["data"] = data;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_output; // only for commands without output
|
||||
JsonObject output = doc_output.to<JsonObject>();
|
||||
JsonDocument doc_output; // only for commands without output
|
||||
JsonObject output = doc_output.to<JsonObject>();
|
||||
|
||||
// prefix "api/" to command string
|
||||
char command_str[100];
|
||||
@@ -352,6 +363,7 @@ bool WebSchedulerService::command(const char * cmd, const char * data) {
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Scheduled command %s failed with error code (%s)", cmd, Command::return_code_string(return_code).c_str());
|
||||
}
|
||||
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ void WebSettingsService::board_profile(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam("boardProfile")) {
|
||||
std::string board_profile = request->getParam("boardProfile")->value().c_str();
|
||||
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
|
||||
@@ -115,7 +115,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
}
|
||||
|
||||
void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
@@ -125,29 +125,29 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
root["num_sensors"] = EMSESP::temperaturesensor_.no_sensors();
|
||||
root["num_analogs"] = EMSESP::analogsensor_.no_sensors();
|
||||
|
||||
JsonArray statsJson = root.createNestedArray("stats");
|
||||
JsonArray statsJson = root["stats"].to<JsonArray>();
|
||||
JsonObject statJson;
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 0;
|
||||
statJson["s"] = EMSESP::rxservice_.telegram_count();
|
||||
statJson["f"] = EMSESP::rxservice_.telegram_error_count();
|
||||
statJson["q"] = EMSESP::rxservice_.quality();
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 1;
|
||||
statJson["s"] = EMSESP::txservice_.telegram_read_count();
|
||||
statJson["f"] = EMSESP::txservice_.telegram_read_fail_count();
|
||||
statJson["q"] = EMSESP::txservice_.read_quality();
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 2;
|
||||
statJson["s"] = EMSESP::txservice_.telegram_write_count();
|
||||
statJson["f"] = EMSESP::txservice_.telegram_write_fail_count();
|
||||
statJson["q"] = EMSESP::txservice_.write_quality();
|
||||
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 3;
|
||||
statJson["s"] = EMSESP::temperaturesensor_.reads() - EMSESP::temperaturesensor_.fails();
|
||||
statJson["f"] = EMSESP::temperaturesensor_.fails();
|
||||
@@ -155,21 +155,21 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
EMSESP::temperaturesensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::temperaturesensor_.fails()) / EMSESP::temperaturesensor_.reads());
|
||||
}
|
||||
if (EMSESP::analog_enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 4;
|
||||
statJson["s"] = EMSESP::analogsensor_.reads() - EMSESP::analogsensor_.fails();
|
||||
statJson["f"] = EMSESP::analogsensor_.fails();
|
||||
statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads());
|
||||
}
|
||||
if (Mqtt::enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 5;
|
||||
statJson["s"] = Mqtt::publish_count() - Mqtt::publish_fails();
|
||||
statJson["f"] = Mqtt::publish_fails();
|
||||
statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (uint8_t)((100 * Mqtt::publish_fails()) / Mqtt::publish_count());
|
||||
}
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 6;
|
||||
statJson["s"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
|
||||
statJson["f"] = WebAPIService::api_fails();
|
||||
@@ -179,7 +179,7 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (EMSESP::system_.syslog_enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 7;
|
||||
statJson["s"] = EMSESP::system_.syslog_count();
|
||||
statJson["f"] = EMSESP::system_.syslog_fails();
|
||||
|
||||
Reference in New Issue
Block a user