This commit is contained in:
MichaelDvP
2023-01-30 10:41:29 +01:00
6 changed files with 90 additions and 70 deletions

View File

@@ -12,7 +12,7 @@
"@emotion/styled": "^11.10.5", "@emotion/styled": "^11.10.5",
"@msgpack/msgpack": "^2.8.0", "@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.11.0", "@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.5", "@mui/material": "^5.11.6",
"@table-library/react-table-library": "4.0.23", "@table-library/react-table-library": "4.0.23",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
@@ -20,8 +20,7 @@
"@types/react-dom": "^18.0.10", "@types/react-dom": "^18.0.10",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"axios": "^1.2.3", "axios": "^1.2.6",
"http-proxy-middleware": "^2.0.6",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"notistack": "^2.0.8", "notistack": "^2.0.8",
@@ -30,13 +29,14 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"react-router-dom": "^6.7.0", "react-router-dom": "^6.8.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"typesafe-i18n": "^5.20.0", "typesafe-i18n": "^5.20.0",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"devDependencies": { "devDependencies": {
"http-proxy-middleware": "^2.0.6",
"nodemon": "^2.0.20", "nodemon": "^2.0.20",
"npm-run-all": "^4.1.5" "npm-run-all": "^4.1.5"
} }
@@ -3082,9 +3082,9 @@
} }
}, },
"node_modules/@mui/base": { "node_modules/@mui/base": {
"version": "5.0.0-alpha.114", "version": "5.0.0-alpha.115",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.114.tgz", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.115.tgz",
"integrity": "sha512-ZpsG2I+zTOAnVTj3Un7TxD2zKRA2OhEPGMcWs/9ylPlS6VuGQSXowPooZiqarjT7TZ0+1bOe8titk/t8dLFiGw==", "integrity": "sha512-OGQ84whT/yNYd6xKCGGS6MxqEfjVjk5esXM7HP6bB2Rim7QICUapxZt4nm8q39fpT08rNDkv3xPVqDDwRdRg1g==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.7", "@babel/runtime": "^7.20.7",
"@emotion/is-prop-valid": "^1.2.0", "@emotion/is-prop-valid": "^1.2.0",
@@ -3114,9 +3114,9 @@
} }
}, },
"node_modules/@mui/core-downloads-tracker": { "node_modules/@mui/core-downloads-tracker": {
"version": "5.11.5", "version": "5.11.6",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.5.tgz", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.6.tgz",
"integrity": "sha512-MIuWGjitOsugpRhp64CQY3ZEVMIu9M/L9ioql6QLSkz73+bGIlC9FEhfi670/GZ8pQIIGmtiGGwofYzlwEWjig==", "integrity": "sha512-lbD3qdafBOf2dlqKhOcVRxaPAujX+9UlPC6v8iMugMeAXe0TCgU3QbGXY3zrJsu6ex64WYDpH4y1+WOOBmWMuA==",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/mui" "url": "https://opencollective.com/mui"
@@ -3148,13 +3148,13 @@
} }
}, },
"node_modules/@mui/material": { "node_modules/@mui/material": {
"version": "5.11.5", "version": "5.11.6",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.5.tgz", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.6.tgz",
"integrity": "sha512-5fzjBbRYaB5MoEpvA32oalAWltOZ3/kSyuovuVmPc6UF6AG42lTtbdMLpdCygurFSGUMZYTg4Cjij52fKlDDgg==", "integrity": "sha512-MzkkL5KC2PCkFiv8cLpkzgLUPXSrAtnvJBR0emV7mLVWbkwV3n5832vjBx154B6R032fHjFTziTh7YEb50nK6Q==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.7", "@babel/runtime": "^7.20.7",
"@mui/base": "5.0.0-alpha.114", "@mui/base": "5.0.0-alpha.115",
"@mui/core-downloads-tracker": "^5.11.5", "@mui/core-downloads-tracker": "^5.11.6",
"@mui/system": "^5.11.5", "@mui/system": "^5.11.5",
"@mui/types": "^7.2.3", "@mui/types": "^7.2.3",
"@mui/utils": "^5.11.2", "@mui/utils": "^5.11.2",
@@ -3449,9 +3449,9 @@
} }
}, },
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.3.0", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.1.tgz",
"integrity": "sha512-nwQoYb3m4DDpHTeOwpJEuDt8lWVcujhYYSFGLluC+9es2PyLjm+jjq3IeRBQbwBtPLJE/lkuHuGHr8uQLgmJRA==", "integrity": "sha512-+eun1Wtf72RNRSqgU7qM2AMX/oHp+dnx7BHk1qhK5ZHzdHTUU4LA1mGG1vT+jMc8sbhG3orvsfOmryjzx2PzQw==",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
} }
@@ -5076,9 +5076,9 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.2.3", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.3.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz",
"integrity": "sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==", "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@@ -14884,11 +14884,11 @@
} }
}, },
"node_modules/react-router": { "node_modules/react-router": {
"version": "6.7.0", "version": "6.8.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.7.0.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.0.tgz",
"integrity": "sha512-KNWlG622ddq29MAM159uUsNMdbX8USruoKnwMMQcs/QWZgFUayICSn2oB7reHce1zPj6CG18kfkZIunSSRyGHg==", "integrity": "sha512-760bk7y3QwabduExtudhWbd88IBbuD1YfwzpuDUAlJUJ7laIIcqhMvdhSVh1Fur1PE8cGl84L0dxhR3/gvHF7A==",
"dependencies": { "dependencies": {
"@remix-run/router": "1.3.0" "@remix-run/router": "1.3.1"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=14"
@@ -14898,12 +14898,12 @@
} }
}, },
"node_modules/react-router-dom": { "node_modules/react-router-dom": {
"version": "6.7.0", "version": "6.8.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.7.0.tgz", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.0.tgz",
"integrity": "sha512-jQtXUJyhso3kFw430+0SPCbmCmY1/kJv8iRffGHwHy3CkoomGxeYzMkmeSPYo6Egzh3FKJZRAL22yg5p2tXtfg==", "integrity": "sha512-hQouduSTywGJndE86CXJ2h7YEy4HYC6C/uh19etM+79FfQ6cFFFHnHyDlzO4Pq0eBUI96E4qVE5yUjA00yJZGQ==",
"dependencies": { "dependencies": {
"@remix-run/router": "1.3.0", "@remix-run/router": "1.3.1",
"react-router": "6.7.0" "react-router": "6.8.0"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=14"

View File

@@ -8,7 +8,7 @@
"@emotion/styled": "^11.10.5", "@emotion/styled": "^11.10.5",
"@msgpack/msgpack": "^2.8.0", "@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.11.0", "@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.5", "@mui/material": "^5.11.6",
"@table-library/react-table-library": "4.0.23", "@table-library/react-table-library": "4.0.23",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
@@ -16,8 +16,7 @@
"@types/react-dom": "^18.0.10", "@types/react-dom": "^18.0.10",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"axios": "^1.2.3", "axios": "^1.2.6",
"http-proxy-middleware": "^2.0.6",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"notistack": "^2.0.8", "notistack": "^2.0.8",
@@ -26,7 +25,7 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"react-router-dom": "^6.7.0", "react-router-dom": "^6.8.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"typesafe-i18n": "^5.20.0", "typesafe-i18n": "^5.20.0",
@@ -99,6 +98,7 @@
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^2.0.20", "nodemon": "^2.0.20",
"npm-run-all": "^4.1.5" "npm-run-all": "^4.1.5",
"http-proxy-middleware": "^2.0.6"
} }
} }

View File

@@ -1130,7 +1130,7 @@ void EMSdevice::dump_value_info() {
if (dv.fullname != nullptr) { if (dv.fullname != nullptr) {
Serial.print(name_); Serial.print(name_);
Serial.print(','); Serial.print(',');
Serial.print(device_type_name().c_str()); Serial.print(device_type_name());
Serial.print(','); Serial.print(',');
Serial.print(product_id_); Serial.print(product_id_);
@@ -1546,10 +1546,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
if (dv.type == DeviceValueType::BOOL && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { if (dv.type == DeviceValueType::BOOL && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
// see how to render the value depending on the setting // see how to render the value depending on the setting
auto value_b = (bool)*(uint8_t *)(dv.value_p); auto value_b = (bool)*(uint8_t *)(dv.value_p);
if (Mqtt::ha_enabled() && (output_target == OUTPUT_TARGET::MQTT)) { if (output_target == OUTPUT_TARGET::CONSOLE) {
char s[12];
json[name] = Helpers::render_boolean(s, value_b); // for HA always render as string
} else if (output_target == OUTPUT_TARGET::CONSOLE) {
char s[12]; char s[12];
json[name] = Helpers::render_boolean(s, value_b, true); // console use web settings json[name] = Helpers::render_boolean(s, value_b, true); // console use web settings
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {

View File

@@ -610,15 +610,15 @@ void Mqtt::ha_status() {
strcpy(uniq, "system_status"); strcpy(uniq, "system_status");
} }
doc["uniq_id"] = uniq; doc["uniq_id"] = uniq;
doc["object_id"] = uniq; doc["obj_id"] = uniq;
doc["stat_t"] = mqtt_base_ + "/status"; doc["stat_t"] = mqtt_base_ + "/status";
doc["name"] = "EMS-ESP status"; doc["name"] = "EMS-ESP status";
doc["payload_on"] = "online"; doc["pl_on"] = "online";
doc["payload_off"] = "offline"; doc["pl_off"] = "offline";
doc["state_class"] = "measurement"; doc["stat_cla"] = "measurement";
doc["device_class"] = "connectivity"; doc["dev_cla"] = "connectivity";
// doc["avty_t"] = "~/status"; // commented out, as it causes errors in HA sometimes // doc["avty_t"] = "~/status"; // commented out, as it causes errors in HA sometimes
// doc["json_attr_t"] = "~/heartbeat"; // store also as HA attributes // doc["json_attr_t"] = "~/heartbeat"; // store also as HA attributes
@@ -643,7 +643,7 @@ void Mqtt::ha_status() {
} }
publish_system_ha_sensor_config(DeviceValueType::STRING, "EMS Bus", "bus_status", DeviceValueUOM::NONE); publish_system_ha_sensor_config(DeviceValueType::STRING, "EMS Bus", "bus_status", DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, "Uptime", "uptime", DeviceValueUOM::NONE); publish_system_ha_sensor_config(DeviceValueType::STRING, "Uptime", "uptime", DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT, "Uptime (sec)", "uptime_sec", DeviceValueUOM::SECONDS); publish_system_ha_sensor_config(DeviceValueType::INT, "Uptime (sec)", "uptime_sec", DeviceValueUOM::SECONDS);
publish_system_ha_sensor_config(DeviceValueType::BOOL, "NTP status", "ntp_status", DeviceValueUOM::CONNECTIVITY); publish_system_ha_sensor_config(DeviceValueType::BOOL, "NTP status", "ntp_status", DeviceValueUOM::CONNECTIVITY);
publish_system_ha_sensor_config(DeviceValueType::INT, "Free memory", "freemem", DeviceValueUOM::KB); publish_system_ha_sensor_config(DeviceValueType::INT, "Free memory", "freemem", DeviceValueUOM::KB);
@@ -1062,8 +1062,8 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
// build the payload // build the payload
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG); DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
doc["uniq_id"] = uniq_id; doc["uniq_id"] = uniq_id;
doc["object_id"] = uniq_id; // same as unique_id doc["obj_id"] = uniq_id; // same as unique_id
const char * ic_ha = "ic"; // icon - only set this if there is no device class const char * ic_ha = "ic"; // icon - only set this if there is no device class
const char * sc_ha = "state_class"; // state class const char * sc_ha = "state_class"; // state class
@@ -1080,7 +1080,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
} else { } else {
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", mqtt_basename_.c_str(), device_name, entity); snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", mqtt_basename_.c_str(), device_name, entity);
} }
doc["command_topic"] = command_topic; doc["cmd_t"] = command_topic;
// for enums, add options // for enums, add options
if (type == DeviceValueType::ENUM) { if (type == DeviceValueType::ENUM) {
@@ -1088,7 +1088,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
for (uint8_t i = 0; i < options_size; i++) { for (uint8_t i = 0; i < options_size; i++) {
option_list.add(Helpers::translated_word(options[i])); option_list.add(Helpers::translated_word(options[i]));
} }
} else if (type != DeviceValueType::STRING) { } else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) {
// Must be Numeric.... // Must be Numeric....
doc["mode"] = "box"; // auto, slider or box doc["mode"] = "box"; // auto, slider or box
if (num_op > 0) { if (num_op > 0) {
@@ -1155,13 +1155,20 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
// special case to handle booleans // special case to handle booleans
// applies to both Binary Sensor (read only) and a Switch (for a command) // applies to both Binary Sensor (read only) and a Switch (for a command)
// always render boolean as strings true & false // has no unit of measure or icon
// and has no unit of measure or icon
if (type == DeviceValueType::BOOL) { if (type == DeviceValueType::BOOL) {
char result[12]; if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
doc["payload_on"] = Helpers::render_boolean(result, true); doc["pl_on"] = true;
doc["payload_off"] = Helpers::render_boolean(result, false); doc["pl_off"] = false;
doc[sc_ha] = F_(measurement); } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
doc["pl_on"] = 1;
doc["pl_off"] = 0;
} else {
char result[12];
doc["pl_on"] = Helpers::render_boolean(result, true);
doc["pl_off"] = Helpers::render_boolean(result, false);
}
doc[sc_ha] = F_(measurement); //do we want this???
} else { } else {
// always set the uom, using the standards except for hours/minutes/seconds // always set the uom, using the standards except for hours/minutes/seconds
// using HA specific codes from https://github.com/home-assistant/core/blob/dev/homeassistant/const.py // using HA specific codes from https://github.com/home-assistant/core/blob/dev/homeassistant/const.py
@@ -1333,7 +1340,7 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
doc["~"] = mqtt_base_; doc["~"] = mqtt_base_;
doc["uniq_id"] = uniq_id_s; doc["uniq_id"] = uniq_id_s;
doc["object_id"] = uniq_id_s; // same as uniq_id doc["obj_id"] = uniq_id_s; // same as uniq_id
doc["name"] = name_s; doc["name"] = name_s;
doc["mode_stat_t"] = topic_t; doc["mode_stat_t"] = topic_t;
doc["mode_stat_tpl"] = mode_str_tpl; doc["mode_stat_tpl"] = mode_str_tpl;

View File

@@ -167,10 +167,17 @@ void Shower::set_shower_state(bool state, bool force) {
snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str()); // use base path snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str()); // use base path
doc["stat_t"] = stat_t; doc["stat_t"] = stat_t;
// always render boolean as strings for HA if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
char result[12]; doc["pl_on"] = true;
doc[("payload_on")] = Helpers::render_boolean(result, true); doc["pl_off"] = false;
doc[("payload_off")] = Helpers::render_boolean(result, false); } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
doc["pl_on"] = 1;
doc["pl_off"] = 0;
} else {
char result[12];
doc["pl_on"] = Helpers::render_boolean(result, true);
doc["pl_off"] = Helpers::render_boolean(result, false);
}
JsonObject dev = doc.createNestedObject("dev"); JsonObject dev = doc.createNestedObject("dev");
JsonArray ids = dev.createNestedArray("ids"); JsonArray ids = dev.createNestedArray("ids");

View File

@@ -249,6 +249,21 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
} }
#endif #endif
//
// these may need mqtt restart to rebuild HA discovery topics
//
prev = settings.bool_format;
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
EMSESP::system_.bool_format(settings.bool_format);
if (Mqtt::ha_enabled())
check_flag(prev, settings.bool_format, ChangeFlags::MQTT);
prev = settings.enum_format;
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
EMSESP::system_.enum_format(settings.enum_format);
if (Mqtt::ha_enabled())
check_flag(prev, settings.enum_format, ChangeFlags::MQTT);
// //
// without checks or necessary restarts... // without checks or necessary restarts...
// //
@@ -264,15 +279,9 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
settings.readonly_mode = root["readonly_mode"] | false; settings.readonly_mode = root["readonly_mode"] | false;
EMSESP::system_.readonly_mode(settings.readonly_mode); EMSESP::system_.readonly_mode(settings.readonly_mode);
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
EMSESP::system_.bool_format(settings.bool_format);
settings.bool_dashboard = root["bool_dashboard"] | EMSESP_DEFAULT_BOOL_FORMAT; settings.bool_dashboard = root["bool_dashboard"] | EMSESP_DEFAULT_BOOL_FORMAT;
EMSESP::system_.bool_dashboard(settings.bool_dashboard); EMSESP::system_.bool_dashboard(settings.bool_dashboard);
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
EMSESP::system_.enum_format(settings.enum_format);
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL; settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER; settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT; settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;