diff --git a/interface/package.json b/interface/package.json index d458f87d5..c2841fec4 100644 --- a/interface/package.json +++ b/interface/package.json @@ -65,7 +65,7 @@ "typescript-eslint": "^8.50.0", "vite": "^7.3.0", "vite-plugin-imagemin": "^0.6.1", - "vite-tsconfig-paths": "^6.0.2" + "vite-tsconfig-paths": "^6.0.3" }, "packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402" } diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index d66e262a2..1e2b41d03 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -127,8 +127,8 @@ importers: specifier: ^0.6.1 version: 0.6.1(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) vite-tsconfig-paths: - specifier: ^6.0.2 - version: 6.0.2(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) + specifier: ^6.0.3 + version: 6.0.3(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) packages: @@ -1027,8 +1027,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.9: - resolution: {integrity: sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg==} + baseline-browser-mapping@2.9.10: + resolution: {integrity: sha512-2VIKvDx8Z1a9rTB2eCkdPE5nSe28XnA+qivGnWHoB40hMMt/h1hSz0960Zqsn6ZyxWXUie0EBdElKv8may20AA==} hasBin: true bin-build@3.0.0: @@ -2988,8 +2988,8 @@ packages: peerDependencies: vite: 5.x || 6.x || 7.x - vite-tsconfig-paths@6.0.2: - resolution: {integrity: sha512-c06LOO8fWB5RuEPpEIHXU9t7Dt4DoiPIljnKws9UygIaQo6PoFKawTftz5/QVcO+6pOs/HHWycnq4UrZkWVYnQ==} + vite-tsconfig-paths@6.0.3: + resolution: {integrity: sha512-7bL7FPX/DSviaZGYUKowWF1AiDVWjMjxNbE8lyaVGDezkedWqfGhlnQ4BZXre0ZN5P4kAgIJfAlgFDVyjrCIyg==} peerDependencies: vite: '*' peerDependenciesMeta: @@ -3958,7 +3958,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.9.9: {} + baseline-browser-mapping@2.9.10: {} bin-build@3.0.0: dependencies: @@ -4015,7 +4015,7 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.9 + baseline-browser-mapping: 2.9.10 caniuse-lite: 1.0.30001760 electron-to-chromium: 1.5.267 node-releases: 2.0.27 @@ -6002,7 +6002,7 @@ snapshots: stack-trace: 1.0.0-pre2 vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1) - vite-tsconfig-paths@6.0.2(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)): + vite-tsconfig-paths@6.0.3(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index 5a4722ea5..2b2c5668b 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -646,6 +646,8 @@ void EMSESP::reset_mqtt_ha() { // force the re-creating of the temperature and analog sensor topics (for HA) temperaturesensor_.reload(); analogsensor_.reload(); + + // rebuild MQTT HA config topics for shower, custom entities and scheduler shower_.ha_reset(); webSchedulerService.ha_reset(); webCustomEntityService.ha_reset(); diff --git a/src/core/mqtt.cpp b/src/core/mqtt.cpp index a201fdc54..d4ca0eb5b 100644 --- a/src/core/mqtt.cpp +++ b/src/core/mqtt.cpp @@ -1448,27 +1448,31 @@ void Mqtt::add_ha_dev_section(JsonObject doc, const char * name, const char * mo return; } - // create dev section - JsonObject dev_json = doc["dev"].to(); + JsonObject dev_json = doc["dev"].to(); // create dev section - // add ids and name - capitalize first letter of the name + // add ids - capitalize first letter of the name if there is one JsonArray ids = dev_json["ids"].to(); // ids, it is an array with a single element if (name != nullptr) { - // for ids, replace all spaces with - + // for ids, replace all spaces with - and add to the basename std::string lower_name_str(name); std::replace(lower_name_str.begin(), lower_name_str.end(), ' ', '-'); ids.add(Mqtt::basename() + "-" + Helpers::toLower(lower_name_str)); - - auto cap_name = strdup(name); - Helpers::CharToUpperUTF8(cap_name); // capitalize first letter - dev_json["name"] = Mqtt::basename() + " " + cap_name; - free(cap_name); } else { ids.add(Mqtt::basename()); // no name, assign it to the main EMS-ESP device in HA } - // create the model, manufacturer and version + // create the name, model, manufacturer and version if (create_model) { + if (name != nullptr) { + auto cap_name = strdup(name); + Helpers::CharToUpperUTF8(cap_name); // capitalize first letter + dev_json["name"] = Mqtt::basename() + " " + cap_name; + free(cap_name); + } else { + dev_json["name"] = Mqtt::basename(); + } + + // add mf, mdl, sw and via_device dev_json["mf"] = brand != nullptr ? brand : "EMS-ESP"; if (model != nullptr) { dev_json["mdl"] = model; diff --git a/src/web/WebCustomEntityService.cpp b/src/web/WebCustomEntityService.cpp index d04d40fff..9f6fce797 100644 --- a/src/web/WebCustomEntityService.cpp +++ b/src/web/WebCustomEntityService.cpp @@ -378,19 +378,12 @@ void WebCustomEntityService::publish_single(CustomEntityItem & entity) { } // publish to Mqtt -void WebCustomEntityService::publish(const bool force) { - if (force) { - ha_registered_ = false; - } - - if (!Mqtt::enabled()) { +void WebCustomEntityService::publish() { + if (!Mqtt::enabled() || customEntityItems_->empty()) { return; } - if (customEntityItems_->empty()) { - return; - } - if (Mqtt::publish_single() && force) { + if (Mqtt::publish_single()) { for (CustomEntityItem & entityItem : *customEntityItems_) { publish_single(entityItem); } @@ -398,7 +391,7 @@ void WebCustomEntityService::publish(const bool force) { JsonDocument doc; JsonObject output = doc.to(); - bool ha_created = ha_registered_; + bool ha_created = ha_configdone_; for (CustomEntityItem & entityItem : *customEntityItems_) { if (entityItem.hide) { @@ -406,7 +399,7 @@ void WebCustomEntityService::publish(const bool force) { } render_value(output, entityItem); // create HA config - if (Mqtt::ha_enabled() && !ha_registered_) { + if (!ha_configdone_) { JsonDocument config; config["~"] = Mqtt::base(); @@ -464,7 +457,6 @@ void WebCustomEntityService::publish(const bool force) { config["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_s; Mqtt::add_ha_classes(config.as(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom); - Mqtt::add_ha_dev_section(config.as(), "Custom Entities", nullptr, "EMS-ESP", EMSESP_APP_VERSION, !ha_created); Mqtt::add_ha_avty_section(config.as(), stat_t, val_cond); @@ -472,7 +464,8 @@ void WebCustomEntityService::publish(const bool force) { } } - ha_registered_ = ha_created; + ha_configdone_ = ha_created; + if (output.size() > 0) { char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; snprintf(topic, sizeof(topic), "%s_data", F_(custom)); diff --git a/src/web/WebCustomEntityService.h b/src/web/WebCustomEntityService.h index d6149d31e..517d19c88 100644 --- a/src/web/WebCustomEntityService.h +++ b/src/web/WebCustomEntityService.h @@ -58,7 +58,7 @@ class WebCustomEntityService : public StatefulService { void begin(); void publish_single(CustomEntityItem & entity); - void publish(const bool force = false); + void publish(); bool command_setvalue(const char * value, const int8_t id, const char * name); bool get_value_info(JsonObject output, const char * cmd); void get_value_json(JsonObject output, CustomEntityItem const & entity); @@ -70,7 +70,7 @@ class WebCustomEntityService : public StatefulService { uint8_t count_entities(); void ha_reset() { - ha_registered_ = false; + ha_configdone_ = false; } #if defined(EMSESP_TEST) @@ -85,7 +85,7 @@ class WebCustomEntityService : public StatefulService { std::list> * customEntityItems_; // pointer to the list of entity items - bool ha_registered_ = false; + bool ha_configdone_ = false; }; } // namespace emsesp diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index b72b60556..fcc313bbe 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -106,7 +106,8 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu } } - EMSESP::webSchedulerService.publish(true); + EMSESP::webSchedulerService.ha_reset(); + EMSESP::webSchedulerService.publish(); return StateUpdateResult::CHANGED; } @@ -215,20 +216,12 @@ void WebSchedulerService::publish_single(const char * name, const bool state) { } // publish to Mqtt -void WebSchedulerService::publish(const bool force) { - if (force) { - ha_registered_ = false; - } - - if (!Mqtt::enabled()) { +void WebSchedulerService::publish() { + if (!Mqtt::enabled() || scheduleItems_->empty()) { return; } - if (scheduleItems_->empty()) { - return; - } - - if (Mqtt::publish_single() && force) { + if (Mqtt::publish_single()) { for (const ScheduleItem & scheduleItem : *scheduleItems_) { publish_single(scheduleItem.name, scheduleItem.active); } @@ -236,13 +229,13 @@ void WebSchedulerService::publish(const bool force) { JsonDocument doc; JsonObject output = doc.to(); - bool ha_created = ha_registered_; + bool ha_created = ha_configdone_; for (const ScheduleItem & scheduleItem : *scheduleItems_) { if (scheduleItem.name[0] != '\0' && !output[scheduleItem.name].is()) { Mqtt::add_value_bool(output, (const char *)scheduleItem.name, scheduleItem.active); // create HA config - if (Mqtt::ha_enabled() && !ha_registered_) { + if (!ha_configdone_) { JsonDocument config; config["~"] = Mqtt::base(); @@ -284,7 +277,7 @@ void WebSchedulerService::publish(const bool force) { } } - ha_registered_ = ha_created; + ha_configdone_ = ha_created; if (!doc.isNull()) { char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; diff --git a/src/web/WebSchedulerService.h b/src/web/WebSchedulerService.h index 4a485e35d..25b561bcc 100644 --- a/src/web/WebSchedulerService.h +++ b/src/web/WebSchedulerService.h @@ -78,12 +78,12 @@ class WebSchedulerService : public StatefulService { void begin(); void loop(); void publish_single(const char * name, const bool state); - void publish(const bool force = false); + void publish(); bool command_setvalue(const char * value, const int8_t id, const char * name); bool get_value_info(JsonObject output, const char * cmd); void get_value_json(JsonObject output, const ScheduleItem & scheduleItem); void ha_reset() { - ha_registered_ = false; + ha_configdone_ = false; } uint8_t count_entities(bool cmd_only = false); bool onChange(const char * cmd); @@ -106,7 +106,7 @@ class WebSchedulerService : public StatefulService { HttpEndpoint _httpEndpoint; FSPersistence _fsPersistence; - bool ha_registered_ = false; + bool ha_configdone_ = false; std::list> * scheduleItems_; // pointer to the list of schedule events std::list> cmd_changed_; // pointer to commands in list that are triggered by change