diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 64ecba199..92d278a2b 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -5,6 +5,7 @@ ## **IMPORTANT! BREAKING CHANGES** - When upgrading to v3.5 for the first time from v3.4 on a BBQKees Gateway board you will need to use the [EMS-EPS Flasher](https://github.com/emsesp/EMS-ESP-Flasher/releases) to correctly re-partition the flash. Make sure you backup the settings and customizations from the WebUI (System->Upload/Download) and restore after the upgrade. +- Since 3.5.0b11 we have added support for multiple EMS-ESPs [#759] and also renamed the HA Entity IDs. For example what was previously `sensor.boiler_actual_boiler_temperature` is now using the shortname form `sensor.boiler_boiltemp` as opposed to the English description. Unfortunately this does means any HA dashboards, automation scripts and integrations (e.g. Grafana) need to be adjusted accordingly. ## Added diff --git a/interface/.typesafe-i18n.json b/interface/.typesafe-i18n.json index 38811b0eb..acc346bcc 100644 --- a/interface/.typesafe-i18n.json +++ b/interface/.typesafe-i18n.json @@ -1,5 +1,5 @@ { - "adapter": "react", - "baseLocale": "pl", - "$schema": "https://unpkg.com/typesafe-i18n@5.17.1/schema/typesafe-i18n.json" -} \ No newline at end of file + "adapter": "react", + "baseLocale": "pl", + "$schema": "https://unpkg.com/typesafe-i18n@5.17.1/schema/typesafe-i18n.json" +} diff --git a/interface/package-lock.json b/interface/package-lock.json index 36f95d85d..59ff55056 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -15,7 +15,7 @@ "@mui/material": "^5.10.17", "@table-library/react-table-library": "4.0.23", "@types/lodash": "^4.14.191", - "@types/node": "^18.11.11", + "@types/node": "^18.11.12", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@types/react-router-dom": "^5.3.3", @@ -31,11 +31,11 @@ "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-icons": "^4.7.1", - "react-router-dom": "^6.4.4", + "react-router-dom": "^6.4.5", "react-scripts": "5.0.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.17.1", - "typescript": "^4.9.3" + "typescript": "^4.9.4" }, "devDependencies": { "nodemon": "^2.0.20", @@ -3458,9 +3458,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.4.tgz", - "integrity": "sha512-gTL8H5USTAKOyVA4xczzDJnC3HMssdFa3tRlwBicXynx9XfiXwneHnYQogwSKpdCkjXISrEKSTtX62rLpNEVQg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.5.tgz", + "integrity": "sha512-my0Mycd+jruq/1lQuO5LBB6WTlL/e8DTCYWp44DfMTDcXz8DcTlgF0ISaLsGewt+ctHN+yA8xMq3q/N7uWJPug==", "engines": { "node": ">=14" } @@ -4017,9 +4017,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "node_modules/@types/node": { - "version": "18.11.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz", - "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==" + "version": "18.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.12.tgz", + "integrity": "sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -14561,11 +14561,11 @@ } }, "node_modules/react-router": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.4.tgz", - "integrity": "sha512-SA6tSrUCRfuLWeYsTJDuriRqfFIsrSvuH7SqAJHegx9ZgxadE119rU8oOX/rG5FYEthpdEaEljdjDlnBxvfr+Q==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.5.tgz", + "integrity": "sha512-1RQJ8bM70YEumHIlNUYc6mFfUDoWa5EgPDenK/fq0bxD8DYpQUi/S6Zoft+9DBrh2xmtg92N5HMAJgGWDhKJ5Q==", "dependencies": { - "@remix-run/router": "1.0.4" + "@remix-run/router": "1.0.5" }, "engines": { "node": ">=14" @@ -14575,12 +14575,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.4.tgz", - "integrity": "sha512-0Axverhw5d+4SBhLqLpzPhNkmv7gahUwlUVIOrRLGJ4/uwt30JVajVJXqv2Qr/LCwyvHhQc7YyK1Do8a9Jj7qA==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.5.tgz", + "integrity": "sha512-a7HsgikBR0wNfroBHcZUCd9+mLRqZS8R5U1Z1mzLWxFXEkUT3vR1XXmSIVoVpxVX8Bar0nQYYYc9Yipq8dWwAA==", "dependencies": { - "@remix-run/router": "1.0.4", - "react-router": "6.4.4" + "@remix-run/router": "1.0.5", + "react-router": "6.4.5" }, "engines": { "node": ">=14" @@ -16441,9 +16441,9 @@ } }, "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19818,9 +19818,9 @@ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, "@remix-run/router": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.4.tgz", - "integrity": "sha512-gTL8H5USTAKOyVA4xczzDJnC3HMssdFa3tRlwBicXynx9XfiXwneHnYQogwSKpdCkjXISrEKSTtX62rLpNEVQg==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.5.tgz", + "integrity": "sha512-my0Mycd+jruq/1lQuO5LBB6WTlL/e8DTCYWp44DfMTDcXz8DcTlgF0ISaLsGewt+ctHN+yA8xMq3q/N7uWJPug==" }, "@rollup/plugin-babel": { "version": "5.3.1", @@ -20232,9 +20232,9 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "@types/node": { - "version": "18.11.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz", - "integrity": "sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==" + "version": "18.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.12.tgz", + "integrity": "sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==" }, "@types/parse-json": { "version": "4.0.0", @@ -27723,20 +27723,20 @@ "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" }, "react-router": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.4.tgz", - "integrity": "sha512-SA6tSrUCRfuLWeYsTJDuriRqfFIsrSvuH7SqAJHegx9ZgxadE119rU8oOX/rG5FYEthpdEaEljdjDlnBxvfr+Q==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.5.tgz", + "integrity": "sha512-1RQJ8bM70YEumHIlNUYc6mFfUDoWa5EgPDenK/fq0bxD8DYpQUi/S6Zoft+9DBrh2xmtg92N5HMAJgGWDhKJ5Q==", "requires": { - "@remix-run/router": "1.0.4" + "@remix-run/router": "1.0.5" } }, "react-router-dom": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.4.tgz", - "integrity": "sha512-0Axverhw5d+4SBhLqLpzPhNkmv7gahUwlUVIOrRLGJ4/uwt30JVajVJXqv2Qr/LCwyvHhQc7YyK1Do8a9Jj7qA==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.5.tgz", + "integrity": "sha512-a7HsgikBR0wNfroBHcZUCd9+mLRqZS8R5U1Z1mzLWxFXEkUT3vR1XXmSIVoVpxVX8Bar0nQYYYc9Yipq8dWwAA==", "requires": { - "@remix-run/router": "1.0.4", - "react-router": "6.4.4" + "@remix-run/router": "1.0.5", + "react-router": "6.4.5" } }, "react-scripts": { @@ -29117,9 +29117,9 @@ "requires": {} }, "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/interface/package.json b/interface/package.json index 4dbd7f5e9..4238aba64 100644 --- a/interface/package.json +++ b/interface/package.json @@ -11,7 +11,7 @@ "@mui/material": "^5.10.17", "@table-library/react-table-library": "4.0.23", "@types/lodash": "^4.14.191", - "@types/node": "^18.11.11", + "@types/node": "^18.11.12", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@types/react-router-dom": "^5.3.3", @@ -27,11 +27,11 @@ "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-icons": "^4.7.1", - "react-router-dom": "^6.4.4", + "react-router-dom": "^6.4.5", "react-scripts": "5.0.1", "sockette": "^2.0.6", "typesafe-i18n": "^5.17.1", - "typescript": "^4.9.3" + "typescript": "^4.9.4" }, "scripts": { "start": "react-app-rewired start", diff --git a/interface/src/framework/mqtt/MqttSettingsForm.tsx b/interface/src/framework/mqtt/MqttSettingsForm.tsx index 013799b09..03f305508 100644 --- a/interface/src/framework/mqtt/MqttSettingsForm.tsx +++ b/interface/src/framework/mqtt/MqttSettingsForm.tsx @@ -223,7 +223,13 @@ const MqttSettingsForm: FC = () => { <> } + control={ + + } label={LL.MQTT_MULTIPLE_INSTANCES()} /> diff --git a/interface/src/framework/network/NetworkSettingsForm.tsx b/interface/src/framework/network/NetworkSettingsForm.tsx index f1e528e94..0fc9e4927 100644 --- a/interface/src/framework/network/NetworkSettingsForm.tsx +++ b/interface/src/framework/network/NetworkSettingsForm.tsx @@ -288,18 +288,18 @@ const WiFiSettingsForm: FC = () => { )} {!restartNeeded && ( - - - + + + )} ); diff --git a/interface/src/project/DeviceIcon.tsx b/interface/src/project/DeviceIcon.tsx index 347a1f1cd..ec8910487 100644 --- a/interface/src/project/DeviceIcon.tsx +++ b/interface/src/project/DeviceIcon.tsx @@ -37,9 +37,9 @@ const DeviceIcon: FC = ({ type }) => { case 'Gateway': return ; case 'Alert': - return ; + return ; case 'Pump': - return ; + return ; default: return null; } diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index 0059c29a1..ffb02bb7c 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -440,7 +440,7 @@ void AnalogSensor::publish_values(const bool force) { StaticJsonDocument config; char stat_t[50]; - snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); // use base path config["stat_t"] = stat_t; char str[50]; diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index 15b55ddad..d58f22d7c 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -505,7 +505,7 @@ void DallasSensor::publish_values(const bool force) { config["dev_cla"] = "temperature"; char stat_t[50]; - snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str()); + snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str()); // use base path config["stat_t"] = stat_t; config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES); diff --git a/src/dallassensor.h b/src/dallassensor.h index 46d62a18b..78bc9e29d 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -57,7 +57,7 @@ class DallasSensor { std::string name() const; void set_name(const std::string & name) { - name_ = name; + name_ = name; } bool apply_customization(); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 67367ed48..c29c1099d 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -443,6 +443,7 @@ void Mqtt::load_settings() { }); // create basename from base + // by taking the MQTT base path and replacing all / with underscores mqtt_basename_ = mqtt_base_; std::replace(mqtt_basename_.begin(), mqtt_basename_.end(), '/', '_'); } @@ -608,19 +609,25 @@ void Mqtt::ha_status() { StaticJsonDocument doc; char uniq[70]; - snprintf(uniq, sizeof(uniq), "%s_status", mqtt_basename_.c_str()); // always use basename + if (Mqtt::multiple_instances()) { + snprintf(uniq, sizeof(uniq), "%s_system_status", mqtt_basename_.c_str()); + } else { + strcpy(uniq, "system_status"); + } + doc["uniq_id"] = uniq; doc["object_id"] = uniq; - doc["~"] = mqtt_base_; - // doc["avty_t"] = "~/status"; // commented out, as it causes errors in HA sometimes - // doc["json_attr_t"] = "~/heartbeat"; // store also as HA attributes - doc["stat_t"] = "~/status"; + + doc["stat_t"] = mqtt_base_ + "/status"; doc["name"] = "EMS-ESP status"; doc["payload_on"] = "online"; doc["payload_off"] = "offline"; doc["state_class"] = "measurement"; doc["device_class"] = "connectivity"; + // 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"); dev["name"] = "EMS-ESP"; dev["sw"] = "v" + std::string(EMSESP_APP_VERSION); @@ -630,7 +637,7 @@ void Mqtt::ha_status() { ids.add("ems-esp"); char topic[MQTT_TOPIC_MAX_SIZE]; - snprintf(topic, sizeof(topic), "binary_sensor/%s/status/config", mqtt_basename_.c_str()); + snprintf(topic, sizeof(topic), "binary_sensor/%s/system_status/config", mqtt_basename_.c_str()); Mqtt::publish_ha(topic, doc.as()); // publish the config payload with retain flag // create the sensors - must match the MQTT payload keys @@ -982,7 +989,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // build unique identifier which will be used in the topic, also used as object_id char uniq_id[70]; - if (multiple_instances_) { + if (Mqtt::multiple_instances()) { // prefix base name to each uniq_id snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag); } else { diff --git a/src/shower.cpp b/src/shower.cpp index 01fa187cb..fa43a6f05 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -151,13 +151,17 @@ void Shower::set_shower_state(bool state, bool force) { ha_configdone_ = true; StaticJsonDocument doc; + doc["name"] = "Shower Active"; + char str[70]; snprintf(str, sizeof(str), "%s_shower_active", Mqtt::basename().c_str()); doc["uniq_id"] = str; doc["object_id"] = str; - doc["~"] = Mqtt::base(); - doc["stat_t"] = "~/shower_active"; + + char stat_t[50]; + snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str()); // use base path + doc["stat_t"] = stat_t; // always render boolean as strings for HA char result[12]; @@ -169,7 +173,7 @@ void Shower::set_shower_state(bool state, bool force) { ids.add("ems-esp"); char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::base().c_str()); + snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str()); Mqtt::publish_ha(topic, doc.as()); // publish the config payload with retain flag } }