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 && (
-
- }
- disabled={saving}
- variant="outlined"
- color="primary"
- type="submit"
- onClick={validateAndSubmit}
- >
- {LL.SAVE()}
-
-
+
+ }
+ disabled={saving}
+ variant="outlined"
+ color="primary"
+ type="submit"
+ onClick={validateAndSubmit}
+ >
+ {LL.SAVE()}
+
+
)}
>
);
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
}
}