From cb5f707b2d5e7e27374bf7e599efae9ca3c2bcfb Mon Sep 17 00:00:00 2001 From: Proddy Date: Wed, 3 Jan 2024 23:00:07 +0100 Subject: [PATCH] use add_ha_sections_to_doc(), add via_device, add check for domoticz --- src/analogsensor.cpp | 8 +-- src/mqtt.cpp | 109 +++++++++++++++++------------ src/mqtt.h | 9 ++- src/shower.cpp | 21 +----- src/temperaturesensor.cpp | 17 ++--- src/web/WebCustomEntityService.cpp | 7 +- src/web/WebSchedulerService.cpp | 7 +- 7 files changed, 85 insertions(+), 93 deletions(-) diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index 61f6cca7f..045dbbdae 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -615,13 +615,7 @@ 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"); - - // add "availability" section - Mqtt::add_avty_to_doc(stat_t, config.as(), val_cond); + Mqtt::add_ha_sections_to_doc("analog", stat_t, config.as(), true, val_cond); sensor.ha_registered = Mqtt::queue_ha(topic, config.as()); } diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 3b0745405..0a96f0cea 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -542,7 +542,7 @@ void Mqtt::ha_status() { JsonObject dev = doc.createNestedObject("dev"); 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()); @@ -739,10 +739,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 @@ -1136,8 +1137,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(), val_cond); // add "availability" section + doc["dev"] = dev_json; // add the dev json object to the end + add_ha_sections_to_doc("", stat_t, doc.as(), false, val_cond); // no name, since the "dev" has already been added return queue_ha(topic, doc.as()); } @@ -1245,18 +1246,8 @@ bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, 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(), seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); + add_ha_sections_to_doc("thermostat", topic_t, doc.as(), false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); return queue_ha(topic, doc.as()); // publish the config payload with retain flag } @@ -1280,42 +1271,68 @@ 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) { - const char * tpl_draft = "{{'online' if %s else 'offline'}}"; - char tpl[150]; - JsonArray avty = doc.createNestedArray("avty"); - - StaticJsonDocument<512> avty_json; - - 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); - - 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); +// adds availability, dev, ids to the config section to HA Discovery config +void Mqtt::add_ha_sections_to_doc(const std::string & name, + const char * state_t, + const JsonObject & config, + const bool is_first, + const char * cond1, + const char * cond2, + const char * negcond) { + // adds dev section to HA Discovery config + if (!name.empty()) { + JsonObject dev = config.createNestedObject("dev"); + auto cap_name = name; + cap_name[0] = toupper(name[0]); // capitalize first letter + dev["name"] = 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.createNestedArray("ids"); + ids.add(Mqtt::basename() + "-" + Helpers::toLower(name)); } - if (negcond != nullptr) { - avty_json.clear(); - avty_json["t"] = state_t; - snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond); + // adds "availability" section to HA Discovery config + // but not for Domoticz + if (discovery_type() == discoveryType::HOMEASSISTANT) { + const char * tpl_draft = "{{'online' if %s else 'offline'}}"; + char tpl[150]; + JsonArray avty = config.createNestedArray("avty"); + + StaticJsonDocument<512> avty_json; + + 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); - } - doc["avty_mode"] = "all"; + 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); + } + + 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); + } + + config["avty_mode"] = "all"; + } } } // namespace emsesp \ No newline at end of file diff --git a/src/mqtt.h b/src/mqtt.h index 53f4b631f..6854cc334 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -217,8 +217,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 std::string & name, + const char * state_t, + const JsonObject & doc, + const bool is_first = false, + const char * cond1 = nullptr, + const char * cond2 = nullptr, + const char * negcond = nullptr); private: static uuid::log::Logger logger_; diff --git a/src/shower.cpp b/src/shower.cpp index 3da18df2d..aba136de1 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -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()); // add "availability" section + Mqtt::add_ha_sections_to_doc("shower", stat_t, doc.as(), true); snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str()); ha_configdone_ = Mqtt::queue_ha(topic, doc.as()); // 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(), "value_json.duration is defined"); // add "availability" section + Mqtt::add_ha_sections_to_doc("shower", stat_t, doc.as(), "value_json.duration is defined"); snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str()); Mqtt::queue_ha(topic, doc.as()); // 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(), "value_json.timestamp is defined"); // add "availability" section + Mqtt::add_ha_sections_to_doc("shower", stat_t, doc.as(), "value_json.timestamp is defined"); snprintf(topic, sizeof(topic), "sensor/%s/shower_timestamp/config", Mqtt::basename().c_str()); Mqtt::queue_ha(topic, doc.as()); // publish the config payload with retain flag diff --git a/src/temperaturesensor.cpp b/src/temperaturesensor.cpp index 31c50599b..504853eb3 100644 --- a/src/temperaturesensor.cpp +++ b/src/temperaturesensor.cpp @@ -527,7 +527,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::DOMOTICZ) { + config["val_tpl"] = (std::string) "{{" + val_obj + "}}"; + } else { + config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else -55}}"; + } char uniq_s[70]; if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) { @@ -543,13 +550,7 @@ 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"); - - // add "availability" section - Mqtt::add_avty_to_doc(stat_t, config.as(), val_cond); + Mqtt::add_ha_sections_to_doc("temperature", stat_t, config.as(), true, val_cond); char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; // use '_' as HA doesn't like '-' in the topic name diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index 5400fcf6a..4f318a07f 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -426,13 +426,8 @@ 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"); + Mqtt::add_ha_sections_to_doc("custom", stat_t, config.as(), true, val_cond); - // add "availability" section - Mqtt::add_avty_to_doc(stat_t, config.as(), val_cond); ha_created |= Mqtt::queue_ha(topic, config.as()); } } diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index bb2b91c52..2036fde97 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -287,13 +287,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.as(), true, val_cond); - // add "availability" section - Mqtt::add_avty_to_doc(stat_t, config.as(), val_cond); ha_created |= Mqtt::queue_ha(topic, config.as()); } }