5 Commits

Author SHA1 Message Date
Proddy
d84d52df4b Merge pull request #2827 from proddy/dev
fix HA custom entities
2025-12-18 21:53:22 +01:00
proddy
11d4109915 package update 2025-12-18 21:40:53 +01:00
proddy
0eddbac150 remove force in HA MQTT 2025-12-18 21:40:41 +01:00
proddy
99afeb221a changes to HA 2025-12-18 21:40:08 +01:00
proddy
39d18b78a1 formatting 2025-12-18 21:39:43 +01:00
8 changed files with 47 additions and 55 deletions

View File

@@ -65,7 +65,7 @@
"typescript-eslint": "^8.50.0", "typescript-eslint": "^8.50.0",
"vite": "^7.3.0", "vite": "^7.3.0",
"vite-plugin-imagemin": "^0.6.1", "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" "packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402"
} }

View File

@@ -127,8 +127,8 @@ importers:
specifier: ^0.6.1 specifier: ^0.6.1
version: 0.6.1(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) version: 0.6.1(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: ^6.0.2 specifier: ^6.0.3
version: 6.0.2(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1)) version: 6.0.3(typescript@5.9.3)(vite@7.3.0(@types/node@25.0.3)(terser@5.44.1))
packages: packages:
@@ -1027,8 +1027,8 @@ packages:
base64-js@1.5.1: base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.9.9: baseline-browser-mapping@2.9.10:
resolution: {integrity: sha512-V8fbOCSeOFvlDj7LLChUcqbZrdKD9RU/VR260piF1790vT0mfLSwGc/Qzxv3IqiTukOpNtItePa0HBpMAj7MDg==} resolution: {integrity: sha512-2VIKvDx8Z1a9rTB2eCkdPE5nSe28XnA+qivGnWHoB40hMMt/h1hSz0960Zqsn6ZyxWXUie0EBdElKv8may20AA==}
hasBin: true hasBin: true
bin-build@3.0.0: bin-build@3.0.0:
@@ -2988,8 +2988,8 @@ packages:
peerDependencies: peerDependencies:
vite: 5.x || 6.x || 7.x vite: 5.x || 6.x || 7.x
vite-tsconfig-paths@6.0.2: vite-tsconfig-paths@6.0.3:
resolution: {integrity: sha512-c06LOO8fWB5RuEPpEIHXU9t7Dt4DoiPIljnKws9UygIaQo6PoFKawTftz5/QVcO+6pOs/HHWycnq4UrZkWVYnQ==} resolution: {integrity: sha512-7bL7FPX/DSviaZGYUKowWF1AiDVWjMjxNbE8lyaVGDezkedWqfGhlnQ4BZXre0ZN5P4kAgIJfAlgFDVyjrCIyg==}
peerDependencies: peerDependencies:
vite: '*' vite: '*'
peerDependenciesMeta: peerDependenciesMeta:
@@ -3958,7 +3958,7 @@ snapshots:
base64-js@1.5.1: {} base64-js@1.5.1: {}
baseline-browser-mapping@2.9.9: {} baseline-browser-mapping@2.9.10: {}
bin-build@3.0.0: bin-build@3.0.0:
dependencies: dependencies:
@@ -4015,7 +4015,7 @@ snapshots:
browserslist@4.28.1: browserslist@4.28.1:
dependencies: dependencies:
baseline-browser-mapping: 2.9.9 baseline-browser-mapping: 2.9.10
caniuse-lite: 1.0.30001760 caniuse-lite: 1.0.30001760
electron-to-chromium: 1.5.267 electron-to-chromium: 1.5.267
node-releases: 2.0.27 node-releases: 2.0.27
@@ -6002,7 +6002,7 @@ snapshots:
stack-trace: 1.0.0-pre2 stack-trace: 1.0.0-pre2
vite: 7.3.0(@types/node@25.0.3)(terser@5.44.1) 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: dependencies:
debug: 4.4.3 debug: 4.4.3
globrex: 0.1.2 globrex: 0.1.2

View File

@@ -646,6 +646,8 @@ void EMSESP::reset_mqtt_ha() {
// force the re-creating of the temperature and analog sensor topics (for HA) // force the re-creating of the temperature and analog sensor topics (for HA)
temperaturesensor_.reload(); temperaturesensor_.reload();
analogsensor_.reload(); analogsensor_.reload();
// rebuild MQTT HA config topics for shower, custom entities and scheduler
shower_.ha_reset(); shower_.ha_reset();
webSchedulerService.ha_reset(); webSchedulerService.ha_reset();
webCustomEntityService.ha_reset(); webCustomEntityService.ha_reset();

View File

@@ -1448,27 +1448,31 @@ void Mqtt::add_ha_dev_section(JsonObject doc, const char * name, const char * mo
return; return;
} }
// create dev section JsonObject dev_json = doc["dev"].to<JsonObject>(); // create dev section
JsonObject dev_json = doc["dev"].to<JsonObject>();
// 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<JsonArray>(); // ids, it is an array with a single element JsonArray ids = dev_json["ids"].to<JsonArray>(); // ids, it is an array with a single element
if (name != nullptr) { 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::string lower_name_str(name);
std::replace(lower_name_str.begin(), lower_name_str.end(), ' ', '-'); std::replace(lower_name_str.begin(), lower_name_str.end(), ' ', '-');
ids.add(Mqtt::basename() + "-" + Helpers::toLower(lower_name_str)); 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 { } else {
ids.add(Mqtt::basename()); // no name, assign it to the main EMS-ESP device in HA 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 (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"; dev_json["mf"] = brand != nullptr ? brand : "EMS-ESP";
if (model != nullptr) { if (model != nullptr) {
dev_json["mdl"] = model; dev_json["mdl"] = model;

View File

@@ -378,19 +378,12 @@ void WebCustomEntityService::publish_single(CustomEntityItem & entity) {
} }
// publish to Mqtt // publish to Mqtt
void WebCustomEntityService::publish(const bool force) { void WebCustomEntityService::publish() {
if (force) { if (!Mqtt::enabled() || customEntityItems_->empty()) {
ha_registered_ = false;
}
if (!Mqtt::enabled()) {
return; return;
} }
if (customEntityItems_->empty()) { if (Mqtt::publish_single()) {
return;
}
if (Mqtt::publish_single() && force) {
for (CustomEntityItem & entityItem : *customEntityItems_) { for (CustomEntityItem & entityItem : *customEntityItems_) {
publish_single(entityItem); publish_single(entityItem);
} }
@@ -398,7 +391,7 @@ void WebCustomEntityService::publish(const bool force) {
JsonDocument doc; JsonDocument doc;
JsonObject output = doc.to<JsonObject>(); JsonObject output = doc.to<JsonObject>();
bool ha_created = ha_registered_; bool ha_created = ha_configdone_;
for (CustomEntityItem & entityItem : *customEntityItems_) { for (CustomEntityItem & entityItem : *customEntityItems_) {
if (entityItem.hide) { if (entityItem.hide) {
@@ -406,7 +399,7 @@ void WebCustomEntityService::publish(const bool force) {
} }
render_value(output, entityItem); render_value(output, entityItem);
// create HA config // create HA config
if (Mqtt::ha_enabled() && !ha_registered_) { if (!ha_configdone_) {
JsonDocument config; JsonDocument config;
config["~"] = Mqtt::base(); 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; config["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_s;
Mqtt::add_ha_classes(config.as<JsonObject>(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom); Mqtt::add_ha_classes(config.as<JsonObject>(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom);
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Custom Entities", nullptr, "EMS-ESP", EMSESP_APP_VERSION, !ha_created); Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Custom Entities", nullptr, "EMS-ESP", EMSESP_APP_VERSION, !ha_created);
Mqtt::add_ha_avty_section(config.as<JsonObject>(), stat_t, val_cond); Mqtt::add_ha_avty_section(config.as<JsonObject>(), 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) { if (output.size() > 0) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "%s_data", F_(custom)); snprintf(topic, sizeof(topic), "%s_data", F_(custom));

View File

@@ -58,7 +58,7 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
void begin(); void begin();
void publish_single(CustomEntityItem & entity); 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 command_setvalue(const char * value, const int8_t id, const char * name);
bool get_value_info(JsonObject output, const char * cmd); bool get_value_info(JsonObject output, const char * cmd);
void get_value_json(JsonObject output, CustomEntityItem const & entity); void get_value_json(JsonObject output, CustomEntityItem const & entity);
@@ -70,7 +70,7 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
uint8_t count_entities(); uint8_t count_entities();
void ha_reset() { void ha_reset() {
ha_registered_ = false; ha_configdone_ = false;
} }
#if defined(EMSESP_TEST) #if defined(EMSESP_TEST)
@@ -85,7 +85,7 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
std::list<CustomEntityItem, AllocatorPSRAM<CustomEntityItem>> * customEntityItems_; // pointer to the list of entity items std::list<CustomEntityItem, AllocatorPSRAM<CustomEntityItem>> * customEntityItems_; // pointer to the list of entity items
bool ha_registered_ = false; bool ha_configdone_ = false;
}; };
} // namespace emsesp } // namespace emsesp

View File

@@ -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; return StateUpdateResult::CHANGED;
} }
@@ -215,20 +216,12 @@ void WebSchedulerService::publish_single(const char * name, const bool state) {
} }
// publish to Mqtt // publish to Mqtt
void WebSchedulerService::publish(const bool force) { void WebSchedulerService::publish() {
if (force) { if (!Mqtt::enabled() || scheduleItems_->empty()) {
ha_registered_ = false;
}
if (!Mqtt::enabled()) {
return; return;
} }
if (scheduleItems_->empty()) { if (Mqtt::publish_single()) {
return;
}
if (Mqtt::publish_single() && force) {
for (const ScheduleItem & scheduleItem : *scheduleItems_) { for (const ScheduleItem & scheduleItem : *scheduleItems_) {
publish_single(scheduleItem.name, scheduleItem.active); publish_single(scheduleItem.name, scheduleItem.active);
} }
@@ -236,13 +229,13 @@ void WebSchedulerService::publish(const bool force) {
JsonDocument doc; JsonDocument doc;
JsonObject output = doc.to<JsonObject>(); JsonObject output = doc.to<JsonObject>();
bool ha_created = ha_registered_; bool ha_created = ha_configdone_;
for (const ScheduleItem & scheduleItem : *scheduleItems_) { for (const ScheduleItem & scheduleItem : *scheduleItems_) {
if (scheduleItem.name[0] != '\0' && !output[scheduleItem.name].is<JsonVariantConst>()) { if (scheduleItem.name[0] != '\0' && !output[scheduleItem.name].is<JsonVariantConst>()) {
Mqtt::add_value_bool(output, (const char *)scheduleItem.name, scheduleItem.active); Mqtt::add_value_bool(output, (const char *)scheduleItem.name, scheduleItem.active);
// create HA config // create HA config
if (Mqtt::ha_enabled() && !ha_registered_) { if (!ha_configdone_) {
JsonDocument config; JsonDocument config;
config["~"] = Mqtt::base(); config["~"] = Mqtt::base();
@@ -284,7 +277,7 @@ void WebSchedulerService::publish(const bool force) {
} }
} }
ha_registered_ = ha_created; ha_configdone_ = ha_created;
if (!doc.isNull()) { if (!doc.isNull()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];

View File

@@ -78,12 +78,12 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
void begin(); void begin();
void loop(); void loop();
void publish_single(const char * name, const bool state); 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 command_setvalue(const char * value, const int8_t id, const char * name);
bool get_value_info(JsonObject output, const char * cmd); bool get_value_info(JsonObject output, const char * cmd);
void get_value_json(JsonObject output, const ScheduleItem & scheduleItem); void get_value_json(JsonObject output, const ScheduleItem & scheduleItem);
void ha_reset() { void ha_reset() {
ha_registered_ = false; ha_configdone_ = false;
} }
uint8_t count_entities(bool cmd_only = false); uint8_t count_entities(bool cmd_only = false);
bool onChange(const char * cmd); bool onChange(const char * cmd);
@@ -106,7 +106,7 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
HttpEndpoint<WebScheduler> _httpEndpoint; HttpEndpoint<WebScheduler> _httpEndpoint;
FSPersistence<WebScheduler> _fsPersistence; FSPersistence<WebScheduler> _fsPersistence;
bool ha_registered_ = false; bool ha_configdone_ = false;
std::list<ScheduleItem, AllocatorPSRAM<ScheduleItem>> * scheduleItems_; // pointer to the list of schedule events std::list<ScheduleItem, AllocatorPSRAM<ScheduleItem>> * scheduleItems_; // pointer to the list of schedule events
std::list<ScheduleItem *, AllocatorPSRAM<ScheduleItem *>> cmd_changed_; // pointer to commands in list that are triggered by change std::list<ScheduleItem *, AllocatorPSRAM<ScheduleItem *>> cmd_changed_; // pointer to commands in list that are triggered by change