From 70c992f005fd7e23580e50149333a7b938d350a3 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 13:10:12 +1200 Subject: [PATCH 01/27] Changing WebUI to include display device name, and change it to select drop down rather than checkbox --- main/TheengsCommon.h | 1 + main/config_WebContent.h | 2 +- main/config_WebUI.h | 4 ++++ main/config_mqttDiscovery.h | 4 ---- main/gatewayBT.cpp | 15 ++++++++++----- main/mqttDiscovery.cpp | 2 +- main/webUI.cpp | 23 +++++++++++++++++++---- 7 files changed, 36 insertions(+), 15 deletions(-) diff --git a/main/TheengsCommon.h b/main/TheengsCommon.h index 17a0fe8469..73c9930d6c 100644 --- a/main/TheengsCommon.h +++ b/main/TheengsCommon.h @@ -62,6 +62,7 @@ extern bool ready_to_sleep; extern char mqtt_topic[]; extern char gateway_name[]; extern unsigned long lastDiscovery; // Time of the last discovery to trigger automaticaly to off after DiscoveryAutoOffTimer +extern bool displayDeviceName; extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc, int timeout); extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc); diff --git a/main/config_WebContent.h b/main/config_WebContent.h index 9215d0d9e7..4544d3f394 100644 --- a/main/config_WebContent.h +++ b/main/config_WebContent.h @@ -112,7 +112,7 @@ const char config_gateway_body[] = body_header "
OpenMQTTGateway Logging

Log Level


" body_footer_config_menu; -const char config_webui_body[] = body_header "
Configure WebUI

Display Metric

Secure WebUI


" body_footer_config_menu; +const char config_webui_body[] = body_header "
Configure WebUI

Display temperature

Device naming

Secure WebUI


" body_footer_config_menu; const char config_rf_body[] = body_header "
" diff --git a/main/config_WebUI.h b/main/config_WebUI.h index 5ba02a4261..5f1cef6edc 100644 --- a/main/config_WebUI.h +++ b/main/config_WebUI.h @@ -42,6 +42,10 @@ # define DISPLAY_METRIC true // Units used for display of sensor data #endif +#ifndef DISPLAY_DEVICE_NAME +# define DISPLAY_DEVICE_NAME false // Set to true to force the device name to be from the name of the device and not the model +#endif + #ifndef DISPLAY_WEBUI_INTERVAL # define DISPLAY_WEBUI_INTERVAL 3 // Number of seconds between json message displays #endif diff --git a/main/config_mqttDiscovery.h b/main/config_mqttDiscovery.h index 7cb89e22cd..20d4ee8e95 100644 --- a/main/config_mqttDiscovery.h +++ b/main/config_mqttDiscovery.h @@ -127,10 +127,6 @@ extern char discovery_prefix[]; # define GATEWAY_MANUFACTURER "OMG_community" #endif -#ifndef ForceDeviceName -# define ForceDeviceName false // Set to true to force the device name to be from the name of the device and not the model -#endif - /*-------------- Auto discovery macros-----------------*/ // Home assistant autodiscovery value key definition #define jsonBatt "{{ value_json.batt | is_defined }}" diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index b3cd3328ad..dfa05c8807 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -951,11 +951,11 @@ void launchBTDiscovery(bool overrideDiscovery) { Log.trace(F("properties: %s" CR), properties.c_str()); std::string brand = decoder.getTheengAttribute(p->sensorModel_id, "brand"); std::string model = decoder.getTheengAttribute(p->sensorModel_id, "model"); -# if ForceDeviceName - if (p->name[0] != '\0') { - model = p->name; + if (displayDeviceName) { + if (p->name[0] != '\0') { + model = p->name; + } } -# endif std::string model_id = decoder.getTheengAttribute(p->sensorModel_id, "model_id"); // Check for tracker status @@ -1010,7 +1010,12 @@ void launchBTDiscovery(bool overrideDiscovery) { Log.trace(F("Key: %s"), prop.key().c_str()); Log.trace(F("Unit: %s"), prop.value()["unit"].as()); Log.trace(F("Name: %s"), prop.value()["name"].as()); - String entity_name = String(model_id.c_str()) + "-" + String(prop.key().c_str()); + String entity_name = ""; + if (displayDeviceName) { + entity_name = String(model.c_str()) + "-" + String(prop.key().c_str()); + } else { + entity_name = String(model_id.c_str()) + "-" + String(prop.key().c_str()); + } String unique_id = macWOdots + "-" + String(prop.key().c_str()); String value_template = "{{ value_json." + String(prop.key().c_str()) + " | is_defined }}"; if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::SBS1 && strcmp(prop.key().c_str(), "state") == 0) { diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 2f064cf3a9..343ea5dace 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -586,7 +586,7 @@ void createDiscovery(const char* sensor_type, // generate unique device name by adding the second half of the device_id only if device_name and device_id are different and we don't want to use the BLE name if (device_name[0]) { - if (strcmp(device_id, device_name) != 0 && device_id[0] && !ForceDeviceName) { + if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { device["name"] = device_name + String("-") + String(device_id + 6); } else { device["name"] = device_name; diff --git a/main/webUI.cpp b/main/webUI.cpp index ea033876d6..bbbfa3415d 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -85,6 +85,7 @@ const char* www_username = WEBUI_LOGIN; String authFailResponse = "Authentication Failed"; bool webUISecure = WEBUI_AUTH; boolean displayMetric = DISPLAY_METRIC; +boolean displayDeviceName = DISPLAY_DEVICE_NAME; /*********************************************************************************************\ * ESP32 AutoMutex @@ -502,7 +503,8 @@ void handleCN() { /** * @brief /WU - Configuration Page * T: handleWU: uri: /wu, args: 3, method: 1 - * T: handleWU Arg: 0, dm=on - displayMetric + * T: handleWU Arg: 0, dm=1 - displayMetric + * T: handleWU Arg: 0, dn=1 - displayDeviceName * T: handleWU Arg: 1, sw=on - webUISecure * T: handleWU Arg: 2, save= */ @@ -515,10 +517,19 @@ void handleWU() { } bool update = false; - if (displayMetric != server.hasArg("dm")) { + if (server.hasArg("dm") && server.arg("dm").toInt() != displayMetric) { + WEBUI_TRACE_LOG(F("handleWU Update displayMetric from: %d" CR), displayMetric); + displayMetric = server.arg("dm").toInt(); + WEBUI_TRACE_LOG(F("handleWU Update displayMetric to: %d" CR), displayMetric); + update = true; + } + + if (server.hasArg("dn") && server.arg("dn").toInt() != displayDeviceName) { + WEBUI_TRACE_LOG(F("handleWU Update displayDeviceName from: %d" CR), displayDeviceName); + displayDeviceName = server.arg("dn").toInt(); + WEBUI_TRACE_LOG(F("handleWU Update displayDeviceName from: %d" CR), displayDeviceName); update = true; } - displayMetric = server.hasArg("dm"); if (webUISecure != server.hasArg("sw")) { update = true; @@ -540,7 +551,7 @@ void handleWU() { response += String(script); response += String(style); int logLevel = Log.getLevel(); - snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "checked" : ""), (webUISecure ? "checked" : "")); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "selected" : ""), (!displayMetric ? "selected" : ""), (!displayDeviceName ? "selected" : ""), (displayDeviceName ? "selected" : ""), (webUISecure ? "checked" : "")); response += String(buffer); snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION); response += String(buffer); @@ -1770,6 +1781,7 @@ String stateWebUIStatus() { StaticJsonDocument WebUIdataBuffer; JsonObject WebUIdata = WebUIdataBuffer.to(); WebUIdata["displayMetric"] = (bool)displayMetric; + WebUIdata["displayDeviceName"] = (bool)displayDeviceName; WebUIdata["webUISecure"] = (bool)webUISecure; WebUIdata["displayQueue"] = uxQueueMessagesWaiting(webUIQueue); @@ -1786,6 +1798,7 @@ bool WebUIConfig_save() { StaticJsonDocument jsonBuffer; JsonObject jo = jsonBuffer.to(); jo["displayMetric"] = (bool)displayMetric; + jo["displayDeviceName"] = (bool)displayDeviceName; jo["webUISecure"] = (bool)webUISecure; // Save config into NVS (non-volatile storage) String conf = ""; @@ -1799,6 +1812,7 @@ bool WebUIConfig_save() { void WebUIConfig_init() { displayMetric = DISPLAY_METRIC; + displayDeviceName = DISPLAY_DEVICE_NAME; webUISecure = WEBUI_AUTH; Log.notice(F("WebUI config initialised" CR)); } @@ -1819,6 +1833,7 @@ bool WebUIConfig_load() { } JsonObject jo = jsonBuffer.as(); displayMetric = jo["displayMetric"].as(); + displayDeviceName = jo["displayDeviceName"].as(); webUISecure = jo["webUISecure"].as(); return true; } else { From 5f20c72f0e60411692ccba08d3c33b2c7494b330 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 14:17:28 +1200 Subject: [PATCH 02/27] Fix mqttDiscovery to require WebUI and ESP32 for displayDeviceName --- main/mqttDiscovery.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 343ea5dace..4c7b52ef13 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -586,7 +586,11 @@ void createDiscovery(const char* sensor_type, // generate unique device name by adding the second half of the device_id only if device_name and device_id are different and we don't want to use the BLE name if (device_name[0]) { + #if defined(ZwebUI) && defined(ESP32) // displayDeviceName only applies when running with the WebUI and ESP32 if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { + #else + if (strcmp(device_id, device_name) != 0 && device_id[0]) { + #endif device["name"] = device_name + String("-") + String(device_id + 6); } else { device["name"] = device_name; From df1d711245745ab52031739987b10fa49cbcc137 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 14:27:36 +1200 Subject: [PATCH 03/27] Fix mqttDiscovery to require WebUI and ESP32 and ESP8266 for displayDeviceName and ForceDeviceName --- main/config_mqttDiscovery.h | 4 ++++ main/gatewayBT.cpp | 4 ++-- main/mqttDiscovery.cpp | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/main/config_mqttDiscovery.h b/main/config_mqttDiscovery.h index 20d4ee8e95..7cb89e22cd 100644 --- a/main/config_mqttDiscovery.h +++ b/main/config_mqttDiscovery.h @@ -127,6 +127,10 @@ extern char discovery_prefix[]; # define GATEWAY_MANUFACTURER "OMG_community" #endif +#ifndef ForceDeviceName +# define ForceDeviceName false // Set to true to force the device name to be from the name of the device and not the model +#endif + /*-------------- Auto discovery macros-----------------*/ // Home assistant autodiscovery value key definition #define jsonBatt "{{ value_json.batt | is_defined }}" diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index dfa05c8807..fbe5958fa8 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -951,7 +951,7 @@ void launchBTDiscovery(bool overrideDiscovery) { Log.trace(F("properties: %s" CR), properties.c_str()); std::string brand = decoder.getTheengAttribute(p->sensorModel_id, "brand"); std::string model = decoder.getTheengAttribute(p->sensorModel_id, "model"); - if (displayDeviceName) { + if (displayDeviceName || ForceDeviceName) { if (p->name[0] != '\0') { model = p->name; } @@ -1011,7 +1011,7 @@ void launchBTDiscovery(bool overrideDiscovery) { Log.trace(F("Unit: %s"), prop.value()["unit"].as()); Log.trace(F("Name: %s"), prop.value()["name"].as()); String entity_name = ""; - if (displayDeviceName) { + if (displayDeviceName || ForceDeviceName) { entity_name = String(model.c_str()) + "-" + String(prop.key().c_str()); } else { entity_name = String(model_id.c_str()) + "-" + String(prop.key().c_str()); diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 4c7b52ef13..64fcea91f8 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -587,9 +587,11 @@ void createDiscovery(const char* sensor_type, // generate unique device name by adding the second half of the device_id only if device_name and device_id are different and we don't want to use the BLE name if (device_name[0]) { #if defined(ZwebUI) && defined(ESP32) // displayDeviceName only applies when running with the WebUI and ESP32 - if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { + if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { + #elif !ForceDeviceName // Support ForceDeviceName for esp8266's + if (strcmp(device_id, device_name) != 0 && device_id[0] && !ForceDeviceName) { #else - if (strcmp(device_id, device_name) != 0 && device_id[0]) { + if (strcmp(device_id, device_name) != 0 && device_id[0]) { #endif device["name"] = device_name + String("-") + String(device_id + 6); } else { From 90f13606576281d754cf02d2603b191d4108304f Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 14:31:54 +1200 Subject: [PATCH 04/27] Changing WebUI to include display device name, and change it to select drop down rather than checkbox --- main/webUI.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main/webUI.cpp b/main/webUI.cpp index bbbfa3415d..b0a4ab2e60 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -518,16 +518,12 @@ void handleWU() { bool update = false; if (server.hasArg("dm") && server.arg("dm").toInt() != displayMetric) { - WEBUI_TRACE_LOG(F("handleWU Update displayMetric from: %d" CR), displayMetric); displayMetric = server.arg("dm").toInt(); - WEBUI_TRACE_LOG(F("handleWU Update displayMetric to: %d" CR), displayMetric); update = true; } if (server.hasArg("dn") && server.arg("dn").toInt() != displayDeviceName) { - WEBUI_TRACE_LOG(F("handleWU Update displayDeviceName from: %d" CR), displayDeviceName); displayDeviceName = server.arg("dn").toInt(); - WEBUI_TRACE_LOG(F("handleWU Update displayDeviceName from: %d" CR), displayDeviceName); update = true; } From 8fb8f4141ee895dc895a510ff7d5b1a6d3a48213 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 15:21:25 +1200 Subject: [PATCH 05/27] Fixes for WebUI and BT for supporting custom setting Display name --- main/config_BT.h | 4 ++++ main/config_WebUI.h | 4 ---- main/gatewayBT.cpp | 1 + main/mqttDiscovery.cpp | 4 +--- main/webUI.cpp | 2 -- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/main/config_BT.h b/main/config_BT.h index 5105110139..67edbcdca4 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -150,6 +150,10 @@ extern unsigned long scanCount; # define enableMultiGTWSync true // //define true to use tracker and closest control devices sync across OpenMQTTGateway and Theengs Gateway gateways #endif +#ifndef DISPLAY_DEVICE_NAME +# define DISPLAY_DEVICE_NAME false // Set to true to force the device name to be from the name of the device and not the model +#endif + /*--------------HOME ASSISTANT ROOM PRESENCE--------------*/ #define subjectHomePresence "presence/" // will send Home Assistant room presence message to this topic (first part is same for all rooms, second is room name) diff --git a/main/config_WebUI.h b/main/config_WebUI.h index 5f1cef6edc..5ba02a4261 100644 --- a/main/config_WebUI.h +++ b/main/config_WebUI.h @@ -42,10 +42,6 @@ # define DISPLAY_METRIC true // Units used for display of sensor data #endif -#ifndef DISPLAY_DEVICE_NAME -# define DISPLAY_DEVICE_NAME false // Set to true to force the device name to be from the name of the device and not the model -#endif - #ifndef DISPLAY_WEBUI_INTERVAL # define DISPLAY_WEBUI_INTERVAL 3 // Number of seconds between json message displays #endif diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index fbe5958fa8..2560dbb3dc 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -88,6 +88,7 @@ static bool oneWhite = false; extern bool BTProcessLock; extern int queueLength; +boolean displayDeviceName = DISPLAY_DEVICE_NAME; void setupBTTasksAndBLE(); bool checkIfIsTracker(char ch); diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 64fcea91f8..fb75a9e302 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -588,10 +588,8 @@ void createDiscovery(const char* sensor_type, if (device_name[0]) { #if defined(ZwebUI) && defined(ESP32) // displayDeviceName only applies when running with the WebUI and ESP32 if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { - #elif !ForceDeviceName // Support ForceDeviceName for esp8266's + #else !ForceDeviceName // Support ForceDeviceName for esp8266's if (strcmp(device_id, device_name) != 0 && device_id[0] && !ForceDeviceName) { - #else - if (strcmp(device_id, device_name) != 0 && device_id[0]) { #endif device["name"] = device_name + String("-") + String(device_id + 6); } else { diff --git a/main/webUI.cpp b/main/webUI.cpp index b0a4ab2e60..0eb68cd8e4 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -85,7 +85,6 @@ const char* www_username = WEBUI_LOGIN; String authFailResponse = "Authentication Failed"; bool webUISecure = WEBUI_AUTH; boolean displayMetric = DISPLAY_METRIC; -boolean displayDeviceName = DISPLAY_DEVICE_NAME; /*********************************************************************************************\ * ESP32 AutoMutex @@ -1808,7 +1807,6 @@ bool WebUIConfig_save() { void WebUIConfig_init() { displayMetric = DISPLAY_METRIC; - displayDeviceName = DISPLAY_DEVICE_NAME; webUISecure = WEBUI_AUTH; Log.notice(F("WebUI config initialised" CR)); } From 98174615547f590ac7fd91f0096fac22c4390819 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 15:30:10 +1200 Subject: [PATCH 06/27] Fixes for WebUI and BT for supporting custom setting Display name --- main/mqttDiscovery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index fb75a9e302..cd2835b392 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -586,7 +586,7 @@ void createDiscovery(const char* sensor_type, // generate unique device name by adding the second half of the device_id only if device_name and device_id are different and we don't want to use the BLE name if (device_name[0]) { - #if defined(ZwebUI) && defined(ESP32) // displayDeviceName only applies when running with the WebUI and ESP32 + #if defined(ZgatewayBT) // displayDeviceName only applies when running with the WebUI and ESP32 if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { #else !ForceDeviceName // Support ForceDeviceName for esp8266's if (strcmp(device_id, device_name) != 0 && device_id[0] && !ForceDeviceName) { From 966c2344fa8d133a6f6050cc0b02369442f1c840 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 15:38:14 +1200 Subject: [PATCH 07/27] Move DISPLAY_DEVICE_NAME to User_config.h --- main/User_config.h | 5 +++++ main/config_BT.h | 4 ---- main/gatewayBT.cpp | 1 - main/main.cpp | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/main/User_config.h b/main/User_config.h index ea5fcbf4bd..16e07c01fd 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -604,6 +604,11 @@ extern ss_cnt_parameters cnt_parameters_array[]; # define LOG_LEVEL LOG_LEVEL_NOTICE #endif +/*-------------------DEFINE DISPLAY NAME-------------------*/ +#ifndef DISPLAY_DEVICE_NAME +# define DISPLAY_DEVICE_NAME false // Set to true to force the device name to be from the name of the device and not the model +#endif + /*-------------------ESP Wifi band and tx power ---------------------*/ //Certain sensors are sensitive to Wifi which can cause interference with their normal operation //For example it can cause false triggers on a PIR HC-SR501 diff --git a/main/config_BT.h b/main/config_BT.h index 67edbcdca4..5105110139 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -150,10 +150,6 @@ extern unsigned long scanCount; # define enableMultiGTWSync true // //define true to use tracker and closest control devices sync across OpenMQTTGateway and Theengs Gateway gateways #endif -#ifndef DISPLAY_DEVICE_NAME -# define DISPLAY_DEVICE_NAME false // Set to true to force the device name to be from the name of the device and not the model -#endif - /*--------------HOME ASSISTANT ROOM PRESENCE--------------*/ #define subjectHomePresence "presence/" // will send Home Assistant room presence message to this topic (first part is same for all rooms, second is room name) diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index 2560dbb3dc..fbe5958fa8 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -88,7 +88,6 @@ static bool oneWhite = false; extern bool BTProcessLock; extern int queueLength; -boolean displayDeviceName = DISPLAY_DEVICE_NAME; void setupBTTasksAndBLE(); bool checkIfIsTracker(char ch); diff --git a/main/main.cpp b/main/main.cpp index bd2b379f45..d4341a58eb 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -286,6 +286,7 @@ bool failSafeMode = false; bool ProcessLock = true; // Process lock when we want to use a critical function like OTA for example bool mqttSetupPending = true; static int cnt_index = CNT_DEFAULT_INDEX; +boolean displayDeviceName = DISPLAY_DEVICE_NAME; #ifdef ESP32 # include From 56f1bb2e48a4d33c7a3fe3f754c1f6c9eb6d4ea6 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 15:58:24 +1200 Subject: [PATCH 08/27] Update docs to include change for Display temperature --- docs/use/displays.md | 6 +++--- docs/use/webui.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/use/displays.md b/docs/use/displays.md index 572a56523d..34e46ec850 100644 --- a/docs/use/displays.md +++ b/docs/use/displays.md @@ -45,10 +45,10 @@ or with the runtime command `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306/config -m {"brightness":50}` -### Metric or Imperial property units -To have applicable device properties displayed in Imperial units, e.g. °F for temperature. +### Celsius or Fahrenheit property units +To have applicable device temperature properties displayed in °C Celsius or °F Fahrenheit. -This can be set with the compiler directive `-DDISPLAY_METRIC=false`. +This can be set with the compiler directive `-DDISPLAY_METRIC=false` for Fahrenheit or via the Configure WebUI Property `Display temperature` As the display Metric setting is being defined in the WebUI part of OpenMQTTGateway changes need to be sent there with the runtime command diff --git a/docs/use/webui.md b/docs/use/webui.md index 24198c0484..d763f9e2fb 100644 --- a/docs/use/webui.md +++ b/docs/use/webui.md @@ -28,7 +28,7 @@ Ability to change the mqtt settings, if the change is unsuccessful it will rever ## WebUI -Ability to change the display of sensor to Metric or Imperial, and disable the WebUI Authentication +Ability to change the display of temperature sensors to Celsius or Fahrenheit, display the advertised device name or the device model_id and disable the WebUI Authentication ## Logging From f9df09063acd962774bb2096b2f7d30089f1a1ed Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 16:07:41 +1200 Subject: [PATCH 09/27] Update docs to include change for Display temperature --- docs/use/displays.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/use/displays.md b/docs/use/displays.md index 34e46ec850..99cc66e70e 100644 --- a/docs/use/displays.md +++ b/docs/use/displays.md @@ -54,6 +54,16 @@ As the display Metric setting is being defined in the WebUI part of OpenMQTTGate `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoWebUI/config -m {"displayMetric":false}` +### Display name as Bluetooth Name or `model_id` +There is a build property of ForceDeviceName which forces devices when they are added in Home Assistant auto-discovery to be created with their Bluetooth advertised name isntead of their `model_id`. The default naming is `model_id` with `{"displayDeviceName":true}`. + +This can also be adjusted in the WebUI by switching the Configure WebUI Device naming between `Model ID` (false) or `Device name` (true) + +This can also be changed with the runtime command. + +`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoWebUI/config -m {"displayDeviceName":true}` + + ### Rotating the display by 180 degrees This can be set with the compiler directive `-DDISPLAY_FLIP=false`. From 6ff9d61ccfa537994ae4e9699a31692b0b94463f Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 17:34:06 +1200 Subject: [PATCH 10/27] Fix minor cosmetic bug where devices were not linking in HA to the gateway using via_device as it should be the gateway mac address not name --- main/mqttDiscovery.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index cd2835b392..0cf09aa212 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -43,6 +43,7 @@ extern bool ethConnected; extern JsonArray modules; +String gateway_mac; char discovery_prefix[parameters_size + 1] = discovery_Prefix; // From https://github.com/home-assistant/core/blob/d7ac4bd65379e11461c7ce0893d3533d8d8b8cbf/homeassistant/const.py#L225 @@ -564,6 +565,7 @@ void createDiscovery(const char* sensor_type, device["sw"] = OMG_VERSION; identifiers.add(String(getMacAddress())); + gateway_mac = getMacAddress(); } else { //The Connections if (device_id[0]) { @@ -597,7 +599,7 @@ void createDiscovery(const char* sensor_type, } } - device["via_device"] = String(gateway_name); //device name of the board + device["via_device"] = String(gateway_mac); //mac address of the gateway so that the devices link to the gateway } sensor["device"] = device; From 678e983a78e765c622c503ae3ceaa3b497bcf827 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 19:49:36 +1200 Subject: [PATCH 11/27] Add support for decrypting BTHome v2 frames --- main/TheengsCommon.h | 2 + main/User_config.h | 16 ++++- main/config_BT.h | 3 - main/config_WebContent.h | 10 ++- main/gatewayBT.cpp | 146 ++++++++++++++++++++++++++++++++++++--- main/main.cpp | 8 +++ main/webUI.cpp | 60 ++++++++++++++++ 7 files changed, 231 insertions(+), 14 deletions(-) diff --git a/main/TheengsCommon.h b/main/TheengsCommon.h index 73c9930d6c..bb0277bfb3 100644 --- a/main/TheengsCommon.h +++ b/main/TheengsCommon.h @@ -63,6 +63,8 @@ extern char mqtt_topic[]; extern char gateway_name[]; extern unsigned long lastDiscovery; // Time of the last discovery to trigger automaticaly to off after DiscoveryAutoOffTimer extern bool displayDeviceName; +extern boolean displayMetric; +extern char ble_aes[]; extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc, int timeout); extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc); diff --git a/main/User_config.h b/main/User_config.h index 16e07c01fd..121c56658e 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -293,8 +293,8 @@ struct ss_cnt_parameters { # define CNT_PARAMS_ARR \ { \ {ss_server_cert, ss_client_cert, ss_client_key, OTAserver_cert, MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, \ - {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, \ - { "", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false } \ + {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, \ + {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false} \ } # define cnt_parameters_array_size 3 @@ -657,6 +657,18 @@ bool isAduplicateSignal(uint64_t); void storeSignalValue(uint64_t); #endif +#ifdef ZgatewayBT +# ifndef BLEDecoder +# define BLEDecoder true //true if we use the Theengs decoder +# ifndef BLEDecryptor +# define BLEDecryptor true //true if decrypt encrypted PVVX or BTHome v2 service data +# endif +# endif +#endif +#ifndef BLE_AES +# define BLE_AES "00112233445566778899001122334455" +#endif + #define convertTemp_CtoF(c) ((c * 1.8) + 32) #define convertTemp_FtoC(f) ((f - 32) * 5 / 9) diff --git a/main/config_BT.h b/main/config_BT.h index 5105110139..d72814a97f 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -122,9 +122,6 @@ extern String stateBTMeasures(bool); # define EnableBT true #endif -#ifndef BLEDecoder -# define BLEDecoder true //true if we use the Theengs decoder -#endif #if !BLEDecoder # define UNKWNON_MODEL -1 diff --git a/main/config_WebContent.h b/main/config_WebContent.h index 4544d3f394..667c9690ba 100644 --- a/main/config_WebContent.h +++ b/main/config_WebContent.h @@ -62,7 +62,11 @@ #else # define configure_6 #endif -#define configure_7 +#ifdef BLEDecryptor +# define configure_7 "

" +#else +# define configure_7 +#endif #define configure_8 /*------------------- ----------------------*/ @@ -219,6 +223,10 @@ const char config_lora_body[] = body_header "" "
" body_footer_config_menu; +#ifdef BLEDecryptor +const char config_ble_body[] = body_header "
Configure BLE

BLE AES Key (32 Char Hex)


" body_footer_config_menu; +#endif + const char footer[] = "

%s
"; // Source file - https://github.com/1technophile/OpenMQTTGateway/blob/54decb4b65c7894b926ac3a89de0c6b2a3021506/docs/.vuepress/public/favicon-16x16.png diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index fbe5958fa8..203e67ba9d 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -57,6 +57,9 @@ BTConfig_s BTConfig; # if BLEDecoder # include +# if BLEDecryptor +# include "mbedtls/ccm.h" +# endif TheengsDecoder decoder; # endif @@ -518,18 +521,28 @@ void BM2Discovery(const char* mac, const char* sensorModel_id) { createDiscoveryFromList(mac, BM2sensor, BM2parametersCount, "BM2", "Generic", sensorModel_id); } -void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) { +void LYWSD03MMCDiscovery(const char* mac, const char* device_name, const char* sensorModel) { # define LYWSD03MMCparametersCount 4 Log.trace(F("LYWSD03MMCDiscovery" CR)); + const char* jsonTemp; + const char* tempChar; + if (displayMetric) { + jsonTemp = jsonTempc; + tempChar = "°C"; + } else { + jsonTemp = jsonTempf; + tempChar = "°F"; + } + const char* LYWSD03MMCsensor[LYWSD03MMCparametersCount][9] = { - {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, - {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, - {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} + {"sensor", "Battery", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, + {"sensor", "Voltage", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, + {"sensor", "Temperature", mac, "temperature", jsonTemp, "", "", tempChar, stateClassMeasurement}, + {"sensor", "Humidity", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; - createDiscoveryFromList(mac, LYWSD03MMCsensor, LYWSD03MMCparametersCount, "LYWSD03MMC", "Xiaomi", sensorModel); + createDiscoveryFromList(mac, LYWSD03MMCsensor, LYWSD03MMCparametersCount, device_name, "Xiaomi", sensorModel); } void MHO_C401Discovery(const char* mac, const char* sensorModel) { @@ -576,7 +589,7 @@ void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel) { } # else -void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) {} +void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel, const char* device_name) {} void MHO_C401Discovery(const char* mac, const char* sensorModel) {} void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) {} void DT24Discovery(const char* mac, const char* sensorModel_id) {} @@ -1133,7 +1146,7 @@ void launchBTDiscovery(bool overrideDiscovery) { stateClassNone); } if (p->sensorModel_id == BLEconectable::id::LYWSD03MMC) { - LYWSD03MMCDiscovery(macWOdots.c_str(), "LYWSD03MMC"); + LYWSD03MMCDiscovery(macWOdots.c_str(), model.c_str(), "LYWSD03MMC"); } if (p->sensorModel_id == BLEconectable::id::MHO_C401) { MHO_C401Discovery(macWOdots.c_str(), "MHO-C401"); @@ -1159,6 +1172,18 @@ void launchBTDiscovery(bool overrideDiscovery) { void launchBTDiscovery(bool overrideDiscovery) {} # endif +# if BLEDecryptor +// ** TODO - Hex string to bytes, there is probably a function for this already just need to find it +int hexToBytes(String hex, uint8_t *out, size_t maxLen) { + int len = hex.length(); + if (len % 2 || len / 2 > maxLen) return -1; + for (int i = 0, j = 0; i < len; i += 2, j++) { + out[j] = (uint8_t) strtol(hex.substring(i, i + 2).c_str(), nullptr, 16); + } + return len / 2; +} +# endif + # if BLEDecoder void process_bledata(JsonObject& BLEdata) { yield(); // Necessary to let the loop run in case of connectivity issues @@ -1171,6 +1196,110 @@ void process_bledata(JsonObject& BLEdata) { int model_id = BTConfig.extDecoderEnable ? -1 : decoder.decodeBLEJson(BLEdata); int mac_type = BLEdata["mac_type"].as(); +# if BLEDecryptor + if (BLEdata["encr"]) { + // Decrypting BTHome v2 payload + + // Set BLE AES Key string from UI + unsigned char bleaeskey[16]; + int keylen = hexToBytes(ble_aes, bleaeskey, 16); + if (keylen != 16) { + Log.error(F("[BTHomeDecrypt] Invalid key length %d" CR), keylen); + return; + } + mbedtls_ccm_context ctx; + mbedtls_ccm_init(&ctx); + if (mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, bleaeskey, 128) != 0) { + Log.error(F("[BTHomeDecrypt] Failed to set AES key" CR)); + return; + } + + uint8_t nonce[13]; // Build nonce + String macWOdots = BLEdata["id"].as(); // Mac Address without dots + macWOdots.replace(":", ""); + unsigned char macAddress[6]; + int maclen = hexToBytes(macWOdots, macAddress, 6); + if (maclen != 6) { + Log.error(F("[BTHomeDecrypt] Invalid MAC Address length %d" CR), maclen); + return; + } + memcpy(nonce, macAddress, 6); + nonce[6] = 0xD2; // UUID + nonce[7] = 0xFC; + nonce[8] = 0x41; // BTHome Device Data encrypted payload byte + unsigned char ctr[4]; // Counter + int ctrlen = hexToBytes(BLEdata["ctr"].as(), ctr, 4); + if (ctrlen != 4) { + Log.error(F("[BTHomeDecrypt] Invalid counter length %d" CR), ctrlen); + return; + } + memcpy(&nonce[9], ctr, 4); + + // Ciphertext + int cipherlen = sizeof(BLEdata["cipher"].as()); + unsigned char ciphertext[cipherlen]; + int ciphertextlen = hexToBytes(BLEdata["cipher"].as(), ciphertext, cipherlen); + + // Decrypted payload + unsigned char decrypted[ciphertextlen]; + + // Message Integrity Check (MIC) + unsigned char mic[4]; + int miclen = hexToBytes(BLEdata["mic"].as(), mic, 4); + if (miclen != 4) { + Log.error(F("[BTHomeDecrypt] Invalid MIC length %d" CR), miclen); + return; + } + + // Decrypt ciphertext + int ret = mbedtls_ccm_auth_decrypt( + &ctx, // AES Key + ciphertextlen, // length of ciphertext + nonce, sizeof(nonce), // Nonce + nullptr, 0, // No AAD + ciphertext, // input ciphertext + decrypted, // output plaintext + mic, sizeof(mic) // Message Integrity Check + ); + + if (ret == 0) { + Log.notice(F("[BTHomeDecrypt] BTHome v2 decryption successful" CR)); + } else if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED) { + Log.error(F("[BTHomeDecrypt] Authentication failed." CR)); + return; + } else { + Log.error(F("[BTHomeDecrypt] Decryption failed with error: -0x%04x" CR), -ret); + return; + } + + // Build new servicedata + uint8_t newservicedata[3 + ciphertextlen]; + newservicedata[0] = 0x40; // Decrypted BTHome + newservicedata[1] = 0x00; // Packet counter which the PVVX BTHome non-encrypted has but the encrypted does not + newservicedata[2] = 0x00; // **TODO Convert the ctr to the packet counter or just stick with 0? + memcpy(&newservicedata[3], decrypted, ciphertextlen); + + // Replace service data and call the decoder again + BLEdata["servicedata"] = NimBLEUtils::dataToHexString(newservicedata, 3 + ciphertextlen); + model_id = BTConfig.extDecoderEnable ? -1 : decoder.decodeBLEJson(BLEdata); + Log.trace(F("[BTHomeDecrypt] Decrypted model_id %d" CR), model_id); + + // Remove the cipher fields from BLEdata + BLEdata.remove("encr"); + BLEdata.remove("cipher"); + BLEdata.remove("ctr"); + BLEdata.remove("mic"); + + } +# endif + // Update model_id for LYWSD03MMC devices + if (model_id >= 0) { + std::string model_id_str = BLEdata["model_id"].as(); + if (model_id_str.compare("LYWSD03MMC") >= 0) { + model_id = BLEconectable::id::LYWSD03MMC; + } + } + // Convert prmacs to RMACS until or if OMG gets Identity MAC/IRK decoding if (BLEdata["prmac"]) { BLEdata.remove("prmac"); @@ -1213,6 +1342,7 @@ void process_bledata(JsonObject& BLEdata) { } else { if (BLEdata.containsKey("name")) { // Connectable only devices std::string name = BLEdata["name"]; + Log.trace(F("Device LYWSD03MMC found: %d" CR), name.compare("LYWSD03MMC")); if (name.compare("LYWSD03MMC") == 0) model_id = BLEconectable::id::LYWSD03MMC; else if (name.compare("DT24-BLE") == 0) diff --git a/main/main.cpp b/main/main.cpp index d4341a58eb..fef940cd1a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -93,6 +93,7 @@ JsonArray modules = modulesBuffer.to(); bool ethConnected = false; char mqtt_topic[parameters_size + 1] = Base_Topic; char gateway_name[parameters_size + 1] = Gateway_Name; +char ble_aes[parameters_size] = BLE_AES; unsigned long lastDiscovery = 0; #if !MQTT_BROKER_MODE ss_cnt_parameters cnt_parameters_array[cnt_parameters_array_size] = CNT_PARAMS_ARR; @@ -2023,6 +2024,7 @@ void saveConfig() { # endif json["gateway_name"] = gateway_name; json["ota_pass"] = ota_pass; + json["ble_aes"] = ble_aes; File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { @@ -2170,6 +2172,9 @@ bool loadConfigFromFlash() { } # endif } + if (json.containsKey("ble_aes")) { + strcpy(ble_aes, json["ble_aes"]); + } result = true; } else { Log.warning(F("failed to load json config" CR)); @@ -3432,6 +3437,9 @@ void XtoSYS(const char* topicOri, JsonObject& SYSdata) { // json object decoding if (SYSdata.containsKey("gateway_name")) { strncpy(gateway_name, SYSdata["gateway_name"], parameters_size); } + if (SYSdata.containsKey("ble_aes")) { + strncpy(ble_aes, SYSdata["ble_aes"], parameters_size); + } if (SYSdata.containsKey("gw_pass")) { strncpy(ota_pass, SYSdata["gw_pass"], parameters_size); restartESP = true; diff --git a/main/webUI.cpp b/main/webUI.cpp index 0eb68cd8e4..247689376b 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -960,6 +960,62 @@ void handleLO() { server.send(200, "text/html", response); } +# ifdef BLEDecryptor +/** + * @brief /BL - Config BLE + * T: handleBL: uri: /bl, args: 3, method: 1 + * T: handleBL Arg: 0, dn=on - Force Device Name + * T: handleBL Arg: 1, bk=on - BLE AES Key + * T: handleBL Arg: 2, save= + */ +void handleBL() { + WEBUI_TRACE_LOG(F("handleBL: uri: %s, args: %d, method: %d" CR), server.uri(), server.args(), server.method()); + WEBUI_SECURE + StaticJsonDocument jsonBuffer; + JsonObject WEBtoSYS = jsonBuffer.to(); + + if (server.args()) { + for (uint8_t i = 0; i < server.args(); i++) { + WEBUI_TRACE_LOG(F("handleBL Arg: %d, %s=%s" CR), i, server.argName(i).c_str(), server.arg(i).c_str()); + } + bool update = false; + + if (server.hasArg("save")) { + if (server.hasArg("bk")) { + WEBtoSYS["ble_aes"] = server.arg("bk"); + update = true; + } + + if (update) { + Log.warning(F("[BLE] Save Config" CR)); + String topic = String(mqtt_topic) + String(gateway_name) + String(subjectMQTTtoSYSset); + XtoSYS((char*)topic.c_str(), WEBtoSYS); + // forceDeviceName = WEBtoSYS["force_device_name"].as(); + strcpy(ble_aes, WEBtoSYS["ble_aes"]); + } else { + Log.warning(F("[BLE] No changes" CR)); + } + } + } + + char jsonChar[100]; + serializeJson(modules, jsonChar, measureJson(modules) + 1); + + char buffer[WEB_TEMPLATE_BUFFER_MAX_SIZE]; + + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, header_html, (String(gateway_name) + " - Configure BLE").c_str()); + String response = String(buffer); + response += String(script); + response += String(style); + int logLevel = Log.getLevel(); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_ble_body, jsonChar, gateway_name, ble_aes); + response += String(buffer); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION); + response += String(buffer); + server.send(200, "text/html", response); +} +# endif + # ifdef ZgatewayLORA # include "config_LORA.h" extern void LORAConfig_fromJson(JsonObject& LORAdata); @@ -1694,6 +1750,10 @@ void WebUISetup() { # endif server.on("/lo", handleLO); // Configure Logging +# ifdef BLEDecryptor + server.on("/bl", handleBL); // Configure BLE +# endif + server.on("/rt", handleRT); // Reset configuration ( Erase and Restart ) server.on("/favicon.ico", handleFavicon); // Information server.begin(); From 3166c6f77047b5e5406caca66545789d74a8dee2 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 20:12:33 +1200 Subject: [PATCH 12/27] Add support for decrypting BTHome v2 frames --- main/User_config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/User_config.h b/main/User_config.h index 121c56658e..264ce14b39 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -293,8 +293,8 @@ struct ss_cnt_parameters { # define CNT_PARAMS_ARR \ { \ {ss_server_cert, ss_client_cert, ss_client_key, OTAserver_cert, MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, \ - {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, \ - {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false} \ + {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, \ + { "", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false } \ } # define cnt_parameters_array_size 3 From 4865234c154f82ccc21a7c67671e909a9d8cbe8f Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 20:15:11 +1200 Subject: [PATCH 13/27] Add support for decrypting BTHome v2 frames --- main/config_BT.h | 1 - 1 file changed, 1 deletion(-) diff --git a/main/config_BT.h b/main/config_BT.h index d72814a97f..c6d03ebb10 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -122,7 +122,6 @@ extern String stateBTMeasures(bool); # define EnableBT true #endif - #if !BLEDecoder # define UNKWNON_MODEL -1 #else From 913b95e0c9a22bb56919023b972be6966adad755 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 21:03:41 +1200 Subject: [PATCH 14/27] BTHome fix issue with theengs-plug --- main/User_config.h | 4 ++++ main/config_WebUI.h | 4 ---- main/gatewayBT.cpp | 2 ++ main/webUI.cpp | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/main/User_config.h b/main/User_config.h index 264ce14b39..56227f0d84 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -657,6 +657,10 @@ bool isAduplicateSignal(uint64_t); void storeSignalValue(uint64_t); #endif +#ifndef DISPLAY_METRIC +# define DISPLAY_METRIC true // Units used for display of sensor data +#endif + #ifdef ZgatewayBT # ifndef BLEDecoder # define BLEDecoder true //true if we use the Theengs decoder diff --git a/main/config_WebUI.h b/main/config_WebUI.h index 5ba02a4261..567e67d169 100644 --- a/main/config_WebUI.h +++ b/main/config_WebUI.h @@ -38,10 +38,6 @@ # define WEB_TEMPLATE_BUFFER_MAX_SIZE 3000 // Max size of the template buffer #endif -#ifndef DISPLAY_METRIC -# define DISPLAY_METRIC true // Units used for display of sensor data -#endif - #ifndef DISPLAY_WEBUI_INTERVAL # define DISPLAY_WEBUI_INTERVAL 3 // Number of seconds between json message displays #endif diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index 203e67ba9d..e13977e215 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -77,6 +77,8 @@ vector BLEactions; vector devices; int newDevices = 0; +boolean displayMetric = DISPLAY_METRIC; + static BLEdevice NO_BT_DEVICE_FOUND = { {0}, 0, diff --git a/main/webUI.cpp b/main/webUI.cpp index 247689376b..39c9655233 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -84,7 +84,6 @@ bool reset_web_log_flag = false; // Reset web console log const char* www_username = WEBUI_LOGIN; String authFailResponse = "Authentication Failed"; bool webUISecure = WEBUI_AUTH; -boolean displayMetric = DISPLAY_METRIC; /*********************************************************************************************\ * ESP32 AutoMutex From cb2fed08e13ca82088de622419bf0df15b30ab39 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Sun, 3 Aug 2025 21:55:56 +1200 Subject: [PATCH 15/27] BTHome fix issue with theengs-plug --- main/gatewayBT.cpp | 2 ++ main/webUI.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index e13977e215..cdbd4276f9 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -77,7 +77,9 @@ vector BLEactions; vector devices; int newDevices = 0; +#if !defined(ZwebUI) boolean displayMetric = DISPLAY_METRIC; +#endif static BLEdevice NO_BT_DEVICE_FOUND = { {0}, diff --git a/main/webUI.cpp b/main/webUI.cpp index 39c9655233..247689376b 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -84,6 +84,7 @@ bool reset_web_log_flag = false; // Reset web console log const char* www_username = WEBUI_LOGIN; String authFailResponse = "Authentication Failed"; bool webUISecure = WEBUI_AUTH; +boolean displayMetric = DISPLAY_METRIC; /*********************************************************************************************\ * ESP32 AutoMutex From 5231f72b6398e4ee21d7e6adb4a38b0c5ff1534b Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Tue, 5 Aug 2025 19:56:38 +1200 Subject: [PATCH 16/27] Adding support for all BLE encrypted methods, support in UI and gatewayBT for specific MACAddress AES Keys --- main/TheengsCommon.h | 6 +- main/User_config.h | 15 +- main/config_WebContent.h | 5 +- main/gatewayBT.cpp | 315 +++++++++++++++++++++++++++++---------- main/main.cpp | 37 ++++- main/webUI.cpp | 58 ++++++- 6 files changed, 342 insertions(+), 94 deletions(-) diff --git a/main/TheengsCommon.h b/main/TheengsCommon.h index bb0277bfb3..7e05918acd 100644 --- a/main/TheengsCommon.h +++ b/main/TheengsCommon.h @@ -64,7 +64,11 @@ extern char gateway_name[]; extern unsigned long lastDiscovery; // Time of the last discovery to trigger automaticaly to off after DiscoveryAutoOffTimer extern bool displayDeviceName; extern boolean displayMetric; -extern char ble_aes[]; + +# if BLEDecryptor + extern char ble_aes[]; + extern StaticJsonDocument ble_aes_keys; +#endif extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc, int timeout); extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc); diff --git a/main/User_config.h b/main/User_config.h index 56227f0d84..1cf6af0cb4 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -664,13 +664,16 @@ void storeSignalValue(uint64_t); #ifdef ZgatewayBT # ifndef BLEDecoder # define BLEDecoder true //true if we use the Theengs decoder -# ifndef BLEDecryptor -# define BLEDecryptor true //true if decrypt encrypted PVVX or BTHome v2 service data -# endif # endif -#endif -#ifndef BLE_AES -# define BLE_AES "00112233445566778899001122334455" +# ifndef BLEDecryptor +# define BLEDecryptor true //true if decrypt encrypted PVVX or BTHome v2 service data +# endif +# ifndef JSON_BLE_AES_CUSTOM_KEYS +# define JSON_BLE_AES_CUSTOM_KEYS 42 * 6 // 42 byte BLE Custom Key * 6 +# endif +# ifndef BLE_AES +# define BLE_AES "00112233445566778899001122334455" +# endif #endif #define convertTemp_CtoF(c) ((c * 1.8) + 32) diff --git a/main/config_WebContent.h b/main/config_WebContent.h index 667c9690ba..53968c2d6a 100644 --- a/main/config_WebContent.h +++ b/main/config_WebContent.h @@ -116,7 +116,7 @@ const char config_gateway_body[] = body_header "
OpenMQTTGateway Logging

Log Level


" body_footer_config_menu; -const char config_webui_body[] = body_header "
Configure WebUI

Display temperature

Device naming

Secure WebUI


" body_footer_config_menu; +const char config_webui_body[] = body_header "
Configure WebUI

Units of measurement displayed

Device naming

Secure WebUI


" body_footer_config_menu; const char config_rf_body[] = body_header "
" @@ -224,7 +224,8 @@ const char config_lora_body[] = body_header "
" body_footer_config_menu; #ifdef BLEDecryptor -const char config_ble_body[] = body_header "
Configure BLE

BLE AES Key (32 Char Hex)


" body_footer_config_menu; +const char config_ble_body[] = body_header "
Configure BLE

BLE AES Default Key (32 char hex)


BLE Key Pairs

MacAddress:AESKey with space separator


" body_footer_config_menu; +const char ble_script[] = "var bkok,kpok;function bkv(){let e=document.getElementById('bk'),t=document.getElementById('bke'),l=e.value.trim();0===l.length||/^[A-Fa-f0-9]{32}$/.test(l)?(t.textContent='',e.style.color='',bkok=!0,console.log('bkok'),kpok&&(console.log('both'),document.getElementById('s').disabled=!1)):(t.textContent='Invalid format',e.style.color='red',document.getElementById('s').disabled=!0,console.log('bknok'),bkok=!1)}function kpv(){let e=document.getElementById('kp'),t=document.getElementById('kpe'),l=e.value.split(/ /),o=/^[A-Fa-f0-9]{12}:[A-Fa-f0-9]{32}$/,n=l.map((e,t)=>({line:e,index:t})).filter(e=>!o.test(e.line));if(0===e.value.trim().length||0===n.length)t.textContent='',e.style.color='',console.log('kpok'),kpok=!0,bkok&&(console.log('both'),document.getElementById('s').disabled=!1);else{let k=n.map(e=>e.index+1).join(', ');t.textContent=`Invalid format on line(s): ${k}`,e.style.color='red',document.getElementById('s').disabled=!0,console.log('kpok'),kpeok=!1}}"; #endif const char footer[] = ""; diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index cdbd4276f9..58745a4271 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -59,6 +59,7 @@ BTConfig_s BTConfig; # include # if BLEDecryptor # include "mbedtls/ccm.h" +# include "mbedtls/aes.h" # endif TheengsDecoder decoder; # endif @@ -499,13 +500,23 @@ void strupp(char* beg) { void DT24Discovery(const char* mac, const char* sensorModel_id) { # define DT24parametersCount 7 Log.trace(F("DT24Discovery" CR)); + const char* temperatureJson; + const char* temperatureCharacter; + if (displayMetric) { + temperatureJson = jsonTempc; + temperatureCharacter = "°C"; + } else { + temperatureJson = jsonTempf; + temperatureCharacter = "°F"; + } + const char* DT24sensor[DT24parametersCount][9] = { {"sensor", "volt", mac, "voltage", jsonVolt, "", "", "V", stateClassMeasurement}, {"sensor", "amp", mac, "current", jsonCurrent, "", "", "A", stateClassMeasurement}, {"sensor", "watt", mac, "power", jsonPower, "", "", "W", stateClassMeasurement}, {"sensor", "watt-hour", mac, "power", jsonEnergy, "", "", "kWh", stateClassMeasurement}, {"sensor", "price", mac, "", jsonMsg, "", "", "", stateClassNone}, - {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, {"binary_sensor", "inUse", mac, "power", jsonInuse, "", "", "", stateClassNone} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -528,21 +539,21 @@ void BM2Discovery(const char* mac, const char* sensorModel_id) { void LYWSD03MMCDiscovery(const char* mac, const char* device_name, const char* sensorModel) { # define LYWSD03MMCparametersCount 4 Log.trace(F("LYWSD03MMCDiscovery" CR)); - const char* jsonTemp; - const char* tempChar; + const char* temperatureJson; + const char* temperatureCharacter; if (displayMetric) { - jsonTemp = jsonTempc; - tempChar = "°C"; + temperatureJson = jsonTempc; + temperatureCharacter = "°C"; } else { - jsonTemp = jsonTempf; - tempChar = "°F"; + temperatureJson = jsonTempf; + temperatureCharacter = "°F"; } const char* LYWSD03MMCsensor[LYWSD03MMCparametersCount][9] = { - {"sensor", "Battery", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, - {"sensor", "Voltage", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "Temperature", mac, "temperature", jsonTemp, "", "", tempChar, stateClassMeasurement}, - {"sensor", "Humidity", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} + {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, + {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, + {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -552,10 +563,20 @@ void LYWSD03MMCDiscovery(const char* mac, const char* device_name, const char* s void MHO_C401Discovery(const char* mac, const char* sensorModel) { # define MHO_C401parametersCount 4 Log.trace(F("MHO_C401Discovery" CR)); + const char* temperatureJson; + const char* temperatureCharacter; + if (displayMetric) { + temperatureJson = jsonTempc; + temperatureCharacter = "°C"; + } else { + temperatureJson = jsonTempf; + temperatureCharacter = "°F"; + } + const char* MHO_C401sensor[MHO_C401parametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -566,9 +587,19 @@ void MHO_C401Discovery(const char* mac, const char* sensorModel) { void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) { # define HHCCJCY01HHCCparametersCount 5 Log.trace(F("HHCCJCY01HHCCDiscovery" CR)); + const char* temperatureJson; + const char* temperatureCharacter; + if (displayMetric) { + temperatureJson = jsonTempc; + temperatureCharacter = "°C"; + } else { + temperatureJson = jsonTempf; + temperatureCharacter = "°F"; + } + const char* HHCCJCY01HHCCsensor[HHCCJCY01HHCCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, {"sensor", "lux", mac, "illuminance", jsonLux, "", "", "lx", stateClassMeasurement}, {"sensor", "fer", mac, "", jsonFer, "", "", "µS/cm", stateClassMeasurement}, {"sensor", "moi", mac, "", jsonMoi, "", "", "%", stateClassMeasurement} @@ -581,10 +612,20 @@ void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) { void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel) { # define XMWSDJ04MMCparametersCount 4 Log.trace(F("XMWSDJ04MMCDiscovery" CR)); + const char* temperatureJson; + const char* temperatureCharacter; + if (displayMetric) { + temperatureJson = jsonTempc; + temperatureCharacter = "°C"; + } else { + temperatureJson = jsonTempf; + temperatureCharacter = "°F"; + } + const char* XMWSDJ04MMCsensor[XMWSDJ04MMCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -1186,6 +1227,15 @@ int hexToBytes(String hex, uint8_t *out, size_t maxLen) { } return len / 2; } +// Reverse bytes +void reverseBytes(uint8_t *data, size_t length) { + size_t i; + for (i = 0; i < length / 2; i++) { + uint8_t temp = data[i]; + data[i] = data[length - 1 - i]; + data[length - 1 - i] = temp; + } +} # endif # if BLEDecoder @@ -1201,92 +1251,203 @@ void process_bledata(JsonObject& BLEdata) { int mac_type = BLEdata["mac_type"].as(); # if BLEDecryptor - if (BLEdata["encr"]) { - // Decrypting BTHome v2 payload - - // Set BLE AES Key string from UI - unsigned char bleaeskey[16]; - int keylen = hexToBytes(ble_aes, bleaeskey, 16); - if (keylen != 16) { - Log.error(F("[BTHomeDecrypt] Invalid key length %d" CR), keylen); - return; - } - mbedtls_ccm_context ctx; - mbedtls_ccm_init(&ctx); - if (mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, bleaeskey, 128) != 0) { - Log.error(F("[BTHomeDecrypt] Failed to set AES key" CR)); - return; - } + if (BLEdata["encr"] && (BLEdata["encr"].as() >0 && BLEdata["encr"].as() <=2)) { + // Decrypting Encrypted BLE Data PVVX, BTHome or Victron + Log.trace(F("[BLEDecryptor] Decrypt ENCR:%d ModelID:%s Payload:%s" CR), BLEdata["encr"].as(), BLEdata["model_id"].as(), BLEdata["cipher"].as()); - uint8_t nonce[13]; // Build nonce + // MAC address String macWOdots = BLEdata["id"].as(); // Mac Address without dots macWOdots.replace(":", ""); unsigned char macAddress[6]; int maclen = hexToBytes(macWOdots, macAddress, 6); if (maclen != 6) { - Log.error(F("[BTHomeDecrypt] Invalid MAC Address length %d" CR), maclen); + Log.error(F("[BLEDecryptor] Invalid MAC Address length %d" CR), maclen); return; } - memcpy(nonce, macAddress, 6); - nonce[6] = 0xD2; // UUID - nonce[7] = 0xFC; - nonce[8] = 0x41; // BTHome Device Data encrypted payload byte - unsigned char ctr[4]; // Counter - int ctrlen = hexToBytes(BLEdata["ctr"].as(), ctr, 4); - if (ctrlen != 4) { - Log.error(F("[BTHomeDecrypt] Invalid counter length %d" CR), ctrlen); + + // AES decryption key + unsigned char bleaeskey[16]; + int bleaeskeylength = 0; + if (ble_aes_keys.containsKey(macWOdots)){ + Log.trace(F("[BLEDecryptor] Custom AES key %s" CR), ble_aes_keys[macWOdots].as()); + bleaeskeylength = hexToBytes(ble_aes_keys[macWOdots], bleaeskey, 16); + } else { + Log.trace(F("[BLEDecryptor] Default AES key" CR)); + bleaeskeylength = hexToBytes(ble_aes, bleaeskey, 16); + } + // Check AES Key + if (bleaeskeylength != 16) { + Log.error(F("[BLEDecryptor] Invalid key length %d" CR), bleaeskeylength); return; } - memcpy(&nonce[9], ctr, 4); - // Ciphertext + // Build nonce and aad + uint8_t nonce[13]; + int noncelength = 0; + unsigned char aad[1]; + int aadLength; + + if (BLEdata["encr"].as() == 1){ // PVVX Encrypted + noncelength = 11; // 11 bytes + reverseBytes(macAddress, 6); // 6 bytes: device address in reverse + memcpy(nonce, macAddress, 6); + int maclen = hexToBytes(macWOdots, macAddress, 6); + + unsigned char servicedata[16]; + int servicedatalen = hexToBytes(BLEdata["servicedata"].as(), servicedata, 16); + nonce[6] = servicedatalen + 3; // 1 byte : length of (service data + type and UUID) + nonce[7] = 0x16; // 1 byte : "16" -> AD type for "Service Data - 16-bit UUID" + nonce[8] = 0x1A; // 2 bytes: "1a18" -> UUID 181a in little-endian + nonce[9] = 0x18; // + unsigned char ctr[1]; // 1 byte : counter + int ctrlen = hexToBytes(BLEdata["ctr"].as(), ctr, 1); + if (ctrlen != 1) { + Log.error(F("[BLEDecryptor] Invalid counter length %d" CR), ctrlen); + return; + } + nonce[10] = ctr[0]; + aad[0] = 0x11; + aadLength = 1; + Log.trace(F("[BLEDecryptor] PVVX nonce %s" CR), NimBLEUtils::dataToHexString(nonce, noncelength).c_str()); + + } else if (BLEdata["encr"].as() == 2){ // BTHome V2 Encrypted + noncelength = 13; // 13 bytes + memcpy(nonce, macAddress, 6); + nonce[6] = 0xD2; // UUID + nonce[7] = 0xFC; + nonce[8] = 0x41; // BTHome Device Data encrypted payload byte + unsigned char ctr[4]; // Counter + int ctrlen = hexToBytes(BLEdata["ctr"].as(), ctr, 4); + if (ctrlen != 4) { + Log.error(F("[BLEDecryptor] Invalid counter length %d" CR), ctrlen); + return; + } + memcpy(&nonce[9], ctr, 4); + aad[0] = 0x00; + aadLength = 0; + Log.trace(F("[BLEDecryptor] BTHomeV2 nonce %s" CR), NimBLEUtils::dataToHexString(nonce, noncelength).c_str()); + + } else if (BLEdata["encr"].as() == 3){ + noncelength = 8; // Victron has a 8 byte nonce + memset(nonce, 0, 8); + unsigned char ctr[2]; + int ctrlen = hexToBytes(BLEdata["ctr"].as(), ctr, 2); + if (ctrlen != 2) { + Log.error(F("[BLEDecryptor] Invalid counter length %d" CR), ctrlen); + return; + } + memcpy(nonce, ctr, 2); + Log.trace(F("[BLEDecryptor] Victron nonce %s" CR), NimBLEUtils::dataToHexString(nonce, noncelength).c_str()); + } else { + return; // No match + } + + // Ciphertext to bytes int cipherlen = sizeof(BLEdata["cipher"].as()); unsigned char ciphertext[cipherlen]; int ciphertextlen = hexToBytes(BLEdata["cipher"].as(), ciphertext, cipherlen); + unsigned char decrypted[ciphertextlen]; // Decrypted payload - // Decrypted payload - unsigned char decrypted[ciphertextlen]; + // Decrypt ciphertext + if (BLEdata["encr"].as() == 1 || BLEdata["encr"].as() == 2) { + // Decrypt PVVX and BTHome V2 ciphertext using AES CCM + mbedtls_ccm_context ctx; + mbedtls_ccm_init(&ctx); + if (mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, bleaeskey, 128) != 0) { + Log.error(F("[BLEDecryptor] Failed to set AES key to mbedtls" CR)); + return; + } + + // Message Integrity Check (MIC) + unsigned char mic[4]; + int miclen = hexToBytes(BLEdata["mic"].as(), mic, 4); + if (miclen != 4) { + Log.error(F("[BLEDecryptor] Invalid MIC length %d" CR), miclen); + return; + } - // Message Integrity Check (MIC) - unsigned char mic[4]; - int miclen = hexToBytes(BLEdata["mic"].as(), mic, 4); - if (miclen != 4) { - Log.error(F("[BTHomeDecrypt] Invalid MIC length %d" CR), miclen); - return; - } + int ret = mbedtls_ccm_auth_decrypt( + &ctx, // AES Key + ciphertextlen, // length of ciphertext + nonce, noncelength, // Nonce + aad, aadLength, // AAD + ciphertext, // input ciphertext + decrypted, // output plaintext + mic, sizeof(mic) // Message Integrity Check + ); + + if (ret == 0) { + Log.notice(F("[BLEDecryptor] Decryption successful" CR)); + } else if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED) { + Log.error(F("[BLEDecryptor] Authentication failed." CR)); + return; + } else { + Log.error(F("[BLEDecryptor] Decryption failed with error: %X" CR), ret); + return; + } - // Decrypt ciphertext - int ret = mbedtls_ccm_auth_decrypt( + // Build new servicedata + if (BLEdata["encr"].as() == 1){ // PVVX + BLEdata["servicedata"] = NimBLEUtils::dataToHexString(decrypted, ciphertextlen); + } else if (BLEdata["encr"].as() == 2) { // BTHomeV2 + // Build new servicedata + uint8_t newservicedata[3 + ciphertextlen]; + newservicedata[0] = 0x40; // Decrypted BTHomeV2 Packet Type + newservicedata[1] = 0x00; // Packet counter which the PVVX BTHome non-encrypted has but the encrypted does not + newservicedata[2] = 0x00; // **TODO Convert the ctr to the packet counter or just stick with 0? + memcpy(&newservicedata[3], decrypted, ciphertextlen); + BLEdata["servicedata"] = NimBLEUtils::dataToHexString(newservicedata, ciphertextlen + 3); + } else { + return; + } + Log.trace(F("[BLEDecryptor] Decrypted servicedata %s" CR), BLEdata["servicedata"].as()); + + } else if (BLEdata["encr"].as() == 3) { + // Decrypt Victron Energy encrypted advertisements. + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_enc(&ctx, bleaeskey, 128); + + size_t nc_off = 0; + unsigned char stream_block[16]; // Must be 16 bytes (AES block size) + memset(stream_block, 0, 16); + + int ret = mbedtls_aes_crypt_ctr( &ctx, // AES Key ciphertextlen, // length of ciphertext - nonce, sizeof(nonce), // Nonce - nullptr, 0, // No AAD + &nc_off, + nonce, + stream_block, ciphertext, // input ciphertext - decrypted, // output plaintext - mic, sizeof(mic) // Message Integrity Check - ); - - if (ret == 0) { - Log.notice(F("[BTHomeDecrypt] BTHome v2 decryption successful" CR)); - } else if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED) { - Log.error(F("[BTHomeDecrypt] Authentication failed." CR)); - return; - } else { - Log.error(F("[BTHomeDecrypt] Decryption failed with error: -0x%04x" CR), -ret); - return; - } + decrypted // output plaintext + ); + + if (ret == 0) { + Log.notice(F("[BLEDecryptor] Victron Decryption successful" CR)); + } else if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED) { + Log.error(F("[BLEDecryptor] Victron Authentication failed." CR)); + return; + } else { + Log.error(F("[BLEDecryptor] Victron decryption failed with error: %X" CR), ret); + return; + } - // Build new servicedata - uint8_t newservicedata[3 + ciphertextlen]; - newservicedata[0] = 0x40; // Decrypted BTHome - newservicedata[1] = 0x00; // Packet counter which the PVVX BTHome non-encrypted has but the encrypted does not - newservicedata[2] = 0x00; // **TODO Convert the ctr to the packet counter or just stick with 0? - memcpy(&newservicedata[3], decrypted, ciphertextlen); + // Build new manufacturerdata + unsigned char manufacturerdata[10 + ciphertextlen]; + int manufacturerdatalen = hexToBytes(BLEdata["manufacturerdata"].as(), manufacturerdata, 10); + manufacturerdata[2] = 0x11; // Replace byte 2 with "11" indicate decrypted data + manufacturerdata[7] = 0xff; // Replace byte 7 with "ff" to indicate decrypted data + manufacturerdata[8] = 0xff; // Replace byte 8 with "ff" to indicate decrypted data + memcpy(&manufacturerdata[8], decrypted, ciphertextlen); // Append the decrypted payload to the manufacturer data + BLEdata["manufacturerdata"] = NimBLEUtils::dataToHexString(manufacturerdata, 10 + ciphertextlen); // Rebuild manufacturerdata + Log.trace(F("[BLEDecryptor] Victron decrypted manufacturerdata %s" CR), BLEdata["manufacturerdata"].as()); + } - // Replace service data and call the decoder again - BLEdata["servicedata"] = NimBLEUtils::dataToHexString(newservicedata, 3 + ciphertextlen); + // Print before and after decoder post decryption + // serializeJsonPretty(BLEdata, Serial); model_id = BTConfig.extDecoderEnable ? -1 : decoder.decodeBLEJson(BLEdata); - Log.trace(F("[BTHomeDecrypt] Decrypted model_id %d" CR), model_id); + // serializeJsonPretty(BLEdata, Serial); + Log.trace(F("[BLEDecryptor] Decrypted model_id %d" CR), model_id); // Remove the cipher fields from BLEdata BLEdata.remove("encr"); diff --git a/main/main.cpp b/main/main.cpp index fef940cd1a..3d619188b9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -93,8 +93,13 @@ JsonArray modules = modulesBuffer.to(); bool ethConnected = false; char mqtt_topic[parameters_size + 1] = Base_Topic; char gateway_name[parameters_size + 1] = Gateway_Name; -char ble_aes[parameters_size] = BLE_AES; unsigned long lastDiscovery = 0; + +#ifdef BLEDecryptor + char ble_aes[parameters_size] = BLE_AES; + StaticJsonDocument ble_aes_keys; +#endif + #if !MQTT_BROKER_MODE ss_cnt_parameters cnt_parameters_array[cnt_parameters_array_size] = CNT_PARAMS_ARR; #endif @@ -2024,7 +2029,10 @@ void saveConfig() { # endif json["gateway_name"] = gateway_name; json["ota_pass"] = ota_pass; +# ifdef BLEDecryptor json["ble_aes"] = ble_aes; + json["ble_aes_keys"] = ble_aes_keys; +# endif File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { @@ -2172,9 +2180,16 @@ bool loadConfigFromFlash() { } # endif } +# ifdef BLEDecryptor if (json.containsKey("ble_aes")) { strcpy(ble_aes, json["ble_aes"]); + Log.trace(F("loaded default BLE AES key %s" CR), ble_aes); + } + if (json.containsKey("ble_aes_keys")) { + ble_aes_keys = json["ble_aes_keys"]; + Log.trace(F("loaded %d custom BLE AES keys" CR), ble_aes_keys.size()); } +# endif result = true; } else { Log.warning(F("failed to load json config" CR)); @@ -3437,9 +3452,6 @@ void XtoSYS(const char* topicOri, JsonObject& SYSdata) { // json object decoding if (SYSdata.containsKey("gateway_name")) { strncpy(gateway_name, SYSdata["gateway_name"], parameters_size); } - if (SYSdata.containsKey("ble_aes")) { - strncpy(ble_aes, SYSdata["ble_aes"], parameters_size); - } if (SYSdata.containsKey("gw_pass")) { strncpy(ota_pass, SYSdata["gw_pass"], parameters_size); restartESP = true; @@ -3450,6 +3462,23 @@ void XtoSYS(const char* topicOri, JsonObject& SYSdata) { // json object decoding mqttSetupPending = true; // trigger reconnect in loop using the new topic/name } +#ifdef BLEDecryptor + if (SYSdata.containsKey("ble_aes") || SYSdata.containsKey("ble_aes_keys")) { + if (SYSdata.containsKey("ble_aes")) { + strncpy(ble_aes, SYSdata["ble_aes"], parameters_size); + } + if (SYSdata.containsKey("ble_aes_keys")) { + Log.warning(F("Contains ble_aes_keys" CR)); + ble_aes_keys = SYSdata["ble_aes_keys"]; + } else { + // If no keys are passed clear the object. + StaticJsonDocument jsonBLEBuffer; + ble_aes_keys = jsonBLEBuffer.to(); + } + saveConfig(); + } +#endif + #if !MQTT_BROKER_MODE # ifdef MQTTsetMQTT diff --git a/main/webUI.cpp b/main/webUI.cpp index 247689376b..9efb3beb9e 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -27,6 +27,7 @@ # include // Docs for this are here - https://github.com/espressif/arduino-esp32/tree/master/libraries/WebServer # include +# include "TheengsCommon.h" # include "config_WebContent.h" # include "config_WebUI.h" @@ -961,6 +962,23 @@ void handleLO() { } # ifdef BLEDecryptor +size_t count_tokens( const char *s1, const char *s2 ) +{ + size_t n = 0; + while (*s1) + { + s1 += strspn( s1, s2 ); + if (*s1) + { + ++n; + s1 += strcspn( s1, s2 ); + } + } + + return n; +} + + /** * @brief /BL - Config BLE * T: handleBL: uri: /bl, args: 3, method: 1 @@ -981,23 +999,54 @@ void handleBL() { bool update = false; if (server.hasArg("save")) { + + // Default BLE AES Key if (server.hasArg("bk")) { WEBtoSYS["ble_aes"] = server.arg("bk"); update = true; } + // Split Custom BLE AES key pair string add to config + if (server.hasArg("kp")) { + String kp = server.arg("kp"); + while (kp.length() > 0) { + int kpindex = kp.indexOf(' '); + if (kpindex == -1) { + if (kp.indexOf(':') == 12) { + WEBtoSYS["ble_aes_keys"][kp.substring(0, 12)] = kp.substring(13, 45); + } + break; + } else { + if (kp.indexOf(':') == 12) { + WEBtoSYS["ble_aes_keys"][kp.substring(0, 12)] = kp.substring(13, 45); + } + kp = kp.substring(kpindex+1); + } + } + update = true; + } + if (update) { Log.warning(F("[BLE] Save Config" CR)); String topic = String(mqtt_topic) + String(gateway_name) + String(subjectMQTTtoSYSset); XtoSYS((char*)topic.c_str(), WEBtoSYS); - // forceDeviceName = WEBtoSYS["force_device_name"].as(); - strcpy(ble_aes, WEBtoSYS["ble_aes"]); } else { Log.warning(F("[BLE] No changes" CR)); } } } + // Build BLE Key Pair string + std::string aeskeysstring; + JsonObject root = ble_aes_keys.as(); + for (JsonPair kv : root) { + aeskeysstring += kv.key().c_str(); + aeskeysstring += ":"; + aeskeysstring += kv.value().as(); + aeskeysstring += " "; + } + aeskeysstring.pop_back(); + char jsonChar[100]; serializeJson(modules, jsonChar, measureJson(modules) + 1); @@ -1005,10 +1054,11 @@ void handleBL() { snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, header_html, (String(gateway_name) + " - Configure BLE").c_str()); String response = String(buffer); + response += String(ble_script); response += String(script); response += String(style); int logLevel = Log.getLevel(); - snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_ble_body, jsonChar, gateway_name, ble_aes); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_ble_body, jsonChar, gateway_name, ble_aes, aeskeysstring.c_str()); response += String(buffer); snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION); response += String(buffer); @@ -1751,7 +1801,7 @@ void WebUISetup() { server.on("/lo", handleLO); // Configure Logging # ifdef BLEDecryptor - server.on("/bl", handleBL); // Configure BLE + server.on("/bl", HTTP_POST, handleBL); // Configure BLE # endif server.on("/rt", handleRT); // Reset configuration ( Erase and Restart ) From 222e37818be9aed257b86047c6fd1fbaedddc417 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Tue, 5 Aug 2025 20:01:13 +1200 Subject: [PATCH 17/27] Fix lint --- main/TheengsCommon.h | 6 +++--- main/User_config.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main/TheengsCommon.h b/main/TheengsCommon.h index 7e05918acd..cccd4b6de5 100644 --- a/main/TheengsCommon.h +++ b/main/TheengsCommon.h @@ -65,9 +65,9 @@ extern unsigned long lastDiscovery; // Time of the last discovery to trigger aut extern bool displayDeviceName; extern boolean displayMetric; -# if BLEDecryptor - extern char ble_aes[]; - extern StaticJsonDocument ble_aes_keys; +#if BLEDecryptor +extern char ble_aes[]; +extern StaticJsonDocument ble_aes_keys; #endif extern bool enqueueJsonObject(const StaticJsonDocument& jsonDoc, int timeout); diff --git a/main/User_config.h b/main/User_config.h index 1cf6af0cb4..eef2769a37 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -669,7 +669,7 @@ void storeSignalValue(uint64_t); # define BLEDecryptor true //true if decrypt encrypted PVVX or BTHome v2 service data # endif # ifndef JSON_BLE_AES_CUSTOM_KEYS -# define JSON_BLE_AES_CUSTOM_KEYS 42 * 6 // 42 byte BLE Custom Key * 6 +# define JSON_BLE_AES_CUSTOM_KEYS 256 // 42 byte BLE Custom Key * 6 rounded up to 256. # endif # ifndef BLE_AES # define BLE_AES "00112233445566778899001122334455" From 74734fb57cd388b938a470fd431b83dcec80f8a2 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Tue, 5 Aug 2025 20:37:00 +1200 Subject: [PATCH 18/27] Fix build issue with theengs-bridge-v11 and esp32dev-all-test and revert the documentation to Units of measurement displayed --- docs/use/displays.md | 5 ++--- docs/use/webui.md | 6 +++++- main/config_WebContent.h | 3 ++- main/main.cpp | 2 ++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/use/displays.md b/docs/use/displays.md index 99cc66e70e..f130ced257 100644 --- a/docs/use/displays.md +++ b/docs/use/displays.md @@ -45,10 +45,9 @@ or with the runtime command `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306/config -m {"brightness":50}` -### Celsius or Fahrenheit property units -To have applicable device temperature properties displayed in °C Celsius or °F Fahrenheit. +### Units of measurement displayed either Metric or Imperial -This can be set with the compiler directive `-DDISPLAY_METRIC=false` for Fahrenheit or via the Configure WebUI Property `Display temperature` +This can be set with the compiler directive `-DDISPLAY_METRIC=false`. As the display Metric setting is being defined in the WebUI part of OpenMQTTGateway changes need to be sent there with the runtime command diff --git a/docs/use/webui.md b/docs/use/webui.md index d763f9e2fb..db9495422e 100644 --- a/docs/use/webui.md +++ b/docs/use/webui.md @@ -28,7 +28,11 @@ Ability to change the mqtt settings, if the change is unsuccessful it will rever ## WebUI -Ability to change the display of temperature sensors to Celsius or Fahrenheit, display the advertised device name or the device model_id and disable the WebUI Authentication +Ability to change the units of measurement displayed either Metric or Imperial, display the advertised device name or the device model_id and disable the WebUI Authentication + +## Bluetooth Low Energy - BLE + +Ability to add the default AES BLE decryption key, and multiple per-device `macaddress:aeskey` ## Logging diff --git a/main/config_WebContent.h b/main/config_WebContent.h index 53968c2d6a..79ff9501ac 100644 --- a/main/config_WebContent.h +++ b/main/config_WebContent.h @@ -225,7 +225,8 @@ const char config_lora_body[] = body_header #ifdef BLEDecryptor const char config_ble_body[] = body_header "
Configure BLE

BLE AES Default Key (32 char hex)


BLE Key Pairs

MacAddress:AESKey with space separator


" body_footer_config_menu; -const char ble_script[] = "var bkok,kpok;function bkv(){let e=document.getElementById('bk'),t=document.getElementById('bke'),l=e.value.trim();0===l.length||/^[A-Fa-f0-9]{32}$/.test(l)?(t.textContent='',e.style.color='',bkok=!0,console.log('bkok'),kpok&&(console.log('both'),document.getElementById('s').disabled=!1)):(t.textContent='Invalid format',e.style.color='red',document.getElementById('s').disabled=!0,console.log('bknok'),bkok=!1)}function kpv(){let e=document.getElementById('kp'),t=document.getElementById('kpe'),l=e.value.split(/ /),o=/^[A-Fa-f0-9]{12}:[A-Fa-f0-9]{32}$/,n=l.map((e,t)=>({line:e,index:t})).filter(e=>!o.test(e.line));if(0===e.value.trim().length||0===n.length)t.textContent='',e.style.color='',console.log('kpok'),kpok=!0,bkok&&(console.log('both'),document.getElementById('s').disabled=!1);else{let k=n.map(e=>e.index+1).join(', ');t.textContent=`Invalid format on line(s): ${k}`,e.style.color='red',document.getElementById('s').disabled=!0,console.log('kpok'),kpeok=!1}}"; +//const char ble_script[] = "var bkok,kpok;function bkv(){let e=document.getElementById('bk'),t=document.getElementById('bke'),l=e.value.trim();0===l.length||/^[A-Fa-f0-9]{32}$/.test(l)?(t.textContent='',e.style.color='',bkok=!0,console.log('bkok'),kpok&&(console.log('both'),document.getElementById('s').disabled=!1)):(t.textContent='Invalid format',e.style.color='red',document.getElementById('s').disabled=!0,console.log('bknok'),bkok=!1)}function kpv(){let e=document.getElementById('kp'),t=document.getElementById('kpe'),l=e.value.split(/ /),o=/^[A-Fa-f0-9]{12}:[A-Fa-f0-9]{32}$/,n=l.map((e,t)=>({line:e,index:t})).filter(e=>!o.test(e.line));if(0===e.value.trim().length||0===n.length)t.textContent='',e.style.color='',console.log('kpok'),kpok=!0,bkok&&(console.log('both'),document.getElementById('s').disabled=!1);else{let k=n.map(e=>e.index+1).join(', ');t.textContent=`Invalid format on line(s): ${k}`,e.style.color='red',document.getElementById('s').disabled=!0,console.log('kpok'),kpeok=!1}}"; +const char ble_script[] = ""; #endif const char footer[] = ""; diff --git a/main/main.cpp b/main/main.cpp index 3d619188b9..f5d0a678b4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -3475,7 +3475,9 @@ void XtoSYS(const char* topicOri, JsonObject& SYSdata) { // json object decoding StaticJsonDocument jsonBLEBuffer; ble_aes_keys = jsonBLEBuffer.to(); } +# ifndef ESPWifiManualSetup saveConfig(); +# endif } #endif From 507be3fa3ef96710891ebdcd96bfd60d95cf319a Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Tue, 5 Aug 2025 20:51:40 +1200 Subject: [PATCH 19/27] Revert docs --- docs/use/displays.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/use/displays.md b/docs/use/displays.md index f130ced257..f482167bae 100644 --- a/docs/use/displays.md +++ b/docs/use/displays.md @@ -46,6 +46,7 @@ or with the runtime command `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306/config -m {"brightness":50}` ### Units of measurement displayed either Metric or Imperial +To have applicable device properties displayed in Imperial units, e.g. °F for temperature. This can be set with the compiler directive `-DDISPLAY_METRIC=false`. From a8b13f450be1939325088ae0ac3ce31336c38299 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 07:35:00 +1200 Subject: [PATCH 20/27] Revert displayDeviceName and Units of measurement --- docs/use/displays.md | 12 +------ docs/use/webui.md | 6 ++-- main/TheengsCommon.h | 2 -- main/config_WebContent.h | 2 +- main/gatewayBT.cpp | 78 +++++++--------------------------------- main/main.cpp | 1 - main/mqttDiscovery.cpp | 6 +--- main/webUI.cpp | 14 ++------ 8 files changed, 21 insertions(+), 100 deletions(-) diff --git a/docs/use/displays.md b/docs/use/displays.md index f482167bae..572a56523d 100644 --- a/docs/use/displays.md +++ b/docs/use/displays.md @@ -45,7 +45,7 @@ or with the runtime command `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoSSD1306/config -m {"brightness":50}` -### Units of measurement displayed either Metric or Imperial +### Metric or Imperial property units To have applicable device properties displayed in Imperial units, e.g. °F for temperature. This can be set with the compiler directive `-DDISPLAY_METRIC=false`. @@ -54,16 +54,6 @@ As the display Metric setting is being defined in the WebUI part of OpenMQTTGate `mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoWebUI/config -m {"displayMetric":false}` -### Display name as Bluetooth Name or `model_id` -There is a build property of ForceDeviceName which forces devices when they are added in Home Assistant auto-discovery to be created with their Bluetooth advertised name isntead of their `model_id`. The default naming is `model_id` with `{"displayDeviceName":true}`. - -This can also be adjusted in the WebUI by switching the Configure WebUI Device naming between `Model ID` (false) or `Device name` (true) - -This can also be changed with the runtime command. - -`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoWebUI/config -m {"displayDeviceName":true}` - - ### Rotating the display by 180 degrees This can be set with the compiler directive `-DDISPLAY_FLIP=false`. diff --git a/docs/use/webui.md b/docs/use/webui.md index db9495422e..7ef756317c 100644 --- a/docs/use/webui.md +++ b/docs/use/webui.md @@ -28,11 +28,11 @@ Ability to change the mqtt settings, if the change is unsuccessful it will rever ## WebUI -Ability to change the units of measurement displayed either Metric or Imperial, display the advertised device name or the device model_id and disable the WebUI Authentication +Ability to change the display of sensor to Metric or Imperial, and disable the WebUI Authentication ## Bluetooth Low Energy - BLE -Ability to add the default AES BLE decryption key, and multiple per-device `macaddress:aeskey` +Ability to add the default AES BLE decryption key, and multiple per-device `macaddress:aeskey` allowing for devices that cannot have their AES key changed. ## Logging @@ -54,4 +54,4 @@ Ability to inject commands to OpenMQTTGateway for processing. The commands acce `commands/MQTTtoSYS/config {"cmd":"restart"}` -This works for all modules in your environment. +This works for all modules in your environment. \ No newline at end of file diff --git a/main/TheengsCommon.h b/main/TheengsCommon.h index cccd4b6de5..f52cea64cb 100644 --- a/main/TheengsCommon.h +++ b/main/TheengsCommon.h @@ -62,8 +62,6 @@ extern bool ready_to_sleep; extern char mqtt_topic[]; extern char gateway_name[]; extern unsigned long lastDiscovery; // Time of the last discovery to trigger automaticaly to off after DiscoveryAutoOffTimer -extern bool displayDeviceName; -extern boolean displayMetric; #if BLEDecryptor extern char ble_aes[]; diff --git a/main/config_WebContent.h b/main/config_WebContent.h index 79ff9501ac..9937e69359 100644 --- a/main/config_WebContent.h +++ b/main/config_WebContent.h @@ -116,7 +116,7 @@ const char config_gateway_body[] = body_header "
OpenMQTTGateway Logging

Log Level


" body_footer_config_menu; -const char config_webui_body[] = body_header "
Configure WebUI

Units of measurement displayed

Device naming

Secure WebUI


" body_footer_config_menu; +const char config_webui_body[] = body_header "
Configure WebUI

Display Metric

Secure WebUI


" body_footer_config_menu; const char config_rf_body[] = body_header "
" diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index 58745a4271..0a1fbf9676 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -78,10 +78,6 @@ vector BLEactions; vector devices; int newDevices = 0; -#if !defined(ZwebUI) -boolean displayMetric = DISPLAY_METRIC; -#endif - static BLEdevice NO_BT_DEVICE_FOUND = { {0}, 0, @@ -502,13 +498,6 @@ void DT24Discovery(const char* mac, const char* sensorModel_id) { Log.trace(F("DT24Discovery" CR)); const char* temperatureJson; const char* temperatureCharacter; - if (displayMetric) { - temperatureJson = jsonTempc; - temperatureCharacter = "°C"; - } else { - temperatureJson = jsonTempf; - temperatureCharacter = "°F"; - } const char* DT24sensor[DT24parametersCount][9] = { {"sensor", "volt", mac, "voltage", jsonVolt, "", "", "V", stateClassMeasurement}, @@ -516,7 +505,7 @@ void DT24Discovery(const char* mac, const char* sensorModel_id) { {"sensor", "watt", mac, "power", jsonPower, "", "", "W", stateClassMeasurement}, {"sensor", "watt-hour", mac, "power", jsonEnergy, "", "", "kWh", stateClassMeasurement}, {"sensor", "price", mac, "", jsonMsg, "", "", "", stateClassNone}, - {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, {"binary_sensor", "inUse", mac, "power", jsonInuse, "", "", "", stateClassNone} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -536,28 +525,18 @@ void BM2Discovery(const char* mac, const char* sensorModel_id) { createDiscoveryFromList(mac, BM2sensor, BM2parametersCount, "BM2", "Generic", sensorModel_id); } -void LYWSD03MMCDiscovery(const char* mac, const char* device_name, const char* sensorModel) { +void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) { # define LYWSD03MMCparametersCount 4 Log.trace(F("LYWSD03MMCDiscovery" CR)); - const char* temperatureJson; - const char* temperatureCharacter; - if (displayMetric) { - temperatureJson = jsonTempc; - temperatureCharacter = "°C"; - } else { - temperatureJson = jsonTempf; - temperatureCharacter = "°F"; - } - const char* LYWSD03MMCsensor[LYWSD03MMCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; - createDiscoveryFromList(mac, LYWSD03MMCsensor, LYWSD03MMCparametersCount, device_name, "Xiaomi", sensorModel); + createDiscoveryFromList(mac, LYWSD03MMCsensor, LYWSD03MMCparametersCount, "LYWSD03MMC", "Xiaomi", sensorModel); } void MHO_C401Discovery(const char* mac, const char* sensorModel) { @@ -565,18 +544,10 @@ void MHO_C401Discovery(const char* mac, const char* sensorModel) { Log.trace(F("MHO_C401Discovery" CR)); const char* temperatureJson; const char* temperatureCharacter; - if (displayMetric) { - temperatureJson = jsonTempc; - temperatureCharacter = "°C"; - } else { - temperatureJson = jsonTempf; - temperatureCharacter = "°F"; - } - const char* MHO_C401sensor[MHO_C401parametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -589,17 +560,9 @@ void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) { Log.trace(F("HHCCJCY01HHCCDiscovery" CR)); const char* temperatureJson; const char* temperatureCharacter; - if (displayMetric) { - temperatureJson = jsonTempc; - temperatureCharacter = "°C"; - } else { - temperatureJson = jsonTempf; - temperatureCharacter = "°F"; - } - const char* HHCCJCY01HHCCsensor[HHCCJCY01HHCCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, {"sensor", "lux", mac, "illuminance", jsonLux, "", "", "lx", stateClassMeasurement}, {"sensor", "fer", mac, "", jsonFer, "", "", "µS/cm", stateClassMeasurement}, {"sensor", "moi", mac, "", jsonMoi, "", "", "%", stateClassMeasurement} @@ -614,18 +577,10 @@ void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel) { Log.trace(F("XMWSDJ04MMCDiscovery" CR)); const char* temperatureJson; const char* temperatureCharacter; - if (displayMetric) { - temperatureJson = jsonTempc; - temperatureCharacter = "°C"; - } else { - temperatureJson = jsonTempf; - temperatureCharacter = "°F"; - } - const char* XMWSDJ04MMCsensor[XMWSDJ04MMCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, - {"sensor", "temp", mac, "temperature", temperatureJson, "", "", temperatureCharacter, stateClassMeasurement}, + {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement} //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement }; @@ -1009,11 +964,10 @@ void launchBTDiscovery(bool overrideDiscovery) { Log.trace(F("properties: %s" CR), properties.c_str()); std::string brand = decoder.getTheengAttribute(p->sensorModel_id, "brand"); std::string model = decoder.getTheengAttribute(p->sensorModel_id, "model"); - if (displayDeviceName || ForceDeviceName) { - if (p->name[0] != '\0') { - model = p->name; - } - } +# if ForceDeviceName + if (p->name[0] != '\0') { + model = p->name; +# endif std::string model_id = decoder.getTheengAttribute(p->sensorModel_id, "model_id"); // Check for tracker status @@ -1068,12 +1022,7 @@ void launchBTDiscovery(bool overrideDiscovery) { Log.trace(F("Key: %s"), prop.key().c_str()); Log.trace(F("Unit: %s"), prop.value()["unit"].as()); Log.trace(F("Name: %s"), prop.value()["name"].as()); - String entity_name = ""; - if (displayDeviceName || ForceDeviceName) { - entity_name = String(model.c_str()) + "-" + String(prop.key().c_str()); - } else { - entity_name = String(model_id.c_str()) + "-" + String(prop.key().c_str()); - } + String entity_name = String(model_id.c_str()) + "-" + String(prop.key().c_str()); String unique_id = macWOdots + "-" + String(prop.key().c_str()); String value_template = "{{ value_json." + String(prop.key().c_str()) + " | is_defined }}"; if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::SBS1 && strcmp(prop.key().c_str(), "state") == 0) { @@ -1191,7 +1140,7 @@ void launchBTDiscovery(bool overrideDiscovery) { stateClassNone); } if (p->sensorModel_id == BLEconectable::id::LYWSD03MMC) { - LYWSD03MMCDiscovery(macWOdots.c_str(), model.c_str(), "LYWSD03MMC"); + LYWSD03MMCDiscovery(macWOdots.c_str(), "LYWSD03MMC"); } if (p->sensorModel_id == BLEconectable::id::MHO_C401) { MHO_C401Discovery(macWOdots.c_str(), "MHO-C401"); @@ -1507,7 +1456,6 @@ void process_bledata(JsonObject& BLEdata) { } else { if (BLEdata.containsKey("name")) { // Connectable only devices std::string name = BLEdata["name"]; - Log.trace(F("Device LYWSD03MMC found: %d" CR), name.compare("LYWSD03MMC")); if (name.compare("LYWSD03MMC") == 0) model_id = BLEconectable::id::LYWSD03MMC; else if (name.compare("DT24-BLE") == 0) diff --git a/main/main.cpp b/main/main.cpp index f5d0a678b4..8f75b30bac 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -292,7 +292,6 @@ bool failSafeMode = false; bool ProcessLock = true; // Process lock when we want to use a critical function like OTA for example bool mqttSetupPending = true; static int cnt_index = CNT_DEFAULT_INDEX; -boolean displayDeviceName = DISPLAY_DEVICE_NAME; #ifdef ESP32 # include diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 0cf09aa212..65550c4e53 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -588,11 +588,7 @@ void createDiscovery(const char* sensor_type, // generate unique device name by adding the second half of the device_id only if device_name and device_id are different and we don't want to use the BLE name if (device_name[0]) { - #if defined(ZgatewayBT) // displayDeviceName only applies when running with the WebUI and ESP32 - if (strcmp(device_id, device_name) != 0 && device_id[0] && !displayDeviceName) { - #else !ForceDeviceName // Support ForceDeviceName for esp8266's - if (strcmp(device_id, device_name) != 0 && device_id[0] && !ForceDeviceName) { - #endif + if (strcmp(device_id, device_name) != 0 && device_id[0] && !ForceDeviceName) { device["name"] = device_name + String("-") + String(device_id + 6); } else { device["name"] = device_name; diff --git a/main/webUI.cpp b/main/webUI.cpp index 9efb3beb9e..de9c257f26 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -504,7 +504,6 @@ void handleCN() { * @brief /WU - Configuration Page * T: handleWU: uri: /wu, args: 3, method: 1 * T: handleWU Arg: 0, dm=1 - displayMetric - * T: handleWU Arg: 0, dn=1 - displayDeviceName * T: handleWU Arg: 1, sw=on - webUISecure * T: handleWU Arg: 2, save= */ @@ -517,13 +516,7 @@ void handleWU() { } bool update = false; - if (server.hasArg("dm") && server.arg("dm").toInt() != displayMetric) { - displayMetric = server.arg("dm").toInt(); - update = true; - } - - if (server.hasArg("dn") && server.arg("dn").toInt() != displayDeviceName) { - displayDeviceName = server.arg("dn").toInt(); + if (displayMetric != server.hasArg("dm")) { update = true; } @@ -547,7 +540,7 @@ void handleWU() { response += String(script); response += String(style); int logLevel = Log.getLevel(); - snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "selected" : ""), (!displayMetric ? "selected" : ""), (!displayDeviceName ? "selected" : ""), (displayDeviceName ? "selected" : ""), (webUISecure ? "checked" : "")); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "checked" : ""), (webUISecure ? "checked" : "")); response += String(buffer); snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION); response += String(buffer); @@ -1886,7 +1879,6 @@ String stateWebUIStatus() { StaticJsonDocument WebUIdataBuffer; JsonObject WebUIdata = WebUIdataBuffer.to(); WebUIdata["displayMetric"] = (bool)displayMetric; - WebUIdata["displayDeviceName"] = (bool)displayDeviceName; WebUIdata["webUISecure"] = (bool)webUISecure; WebUIdata["displayQueue"] = uxQueueMessagesWaiting(webUIQueue); @@ -1903,7 +1895,6 @@ bool WebUIConfig_save() { StaticJsonDocument jsonBuffer; JsonObject jo = jsonBuffer.to(); jo["displayMetric"] = (bool)displayMetric; - jo["displayDeviceName"] = (bool)displayDeviceName; jo["webUISecure"] = (bool)webUISecure; // Save config into NVS (non-volatile storage) String conf = ""; @@ -1937,7 +1928,6 @@ bool WebUIConfig_load() { } JsonObject jo = jsonBuffer.as(); displayMetric = jo["displayMetric"].as(); - displayDeviceName = jo["displayDeviceName"].as(); webUISecure = jo["webUISecure"].as(); return true; } else { From 761060525175c18a9352a6c7e2ae0ac0b7b43d5b Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 07:49:46 +1200 Subject: [PATCH 21/27] Revert displayDeviceName and Units of measurement --- docs/use/webui.md | 2 +- main/User_config.h | 12 ------------ main/config_BT.h | 4 ++++ main/config_WebUI.h | 4 ++++ main/gatewayBT.cpp | 12 ++---------- main/mqttDiscovery.cpp | 3 --- main/webUI.cpp | 18 +----------------- 7 files changed, 12 insertions(+), 43 deletions(-) diff --git a/docs/use/webui.md b/docs/use/webui.md index 7ef756317c..b3004a91af 100644 --- a/docs/use/webui.md +++ b/docs/use/webui.md @@ -54,4 +54,4 @@ Ability to inject commands to OpenMQTTGateway for processing. The commands acce `commands/MQTTtoSYS/config {"cmd":"restart"}` -This works for all modules in your environment. \ No newline at end of file +This works for all modules in your environment. diff --git a/main/User_config.h b/main/User_config.h index eef2769a37..e11e3a2e98 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -604,11 +604,6 @@ extern ss_cnt_parameters cnt_parameters_array[]; # define LOG_LEVEL LOG_LEVEL_NOTICE #endif -/*-------------------DEFINE DISPLAY NAME-------------------*/ -#ifndef DISPLAY_DEVICE_NAME -# define DISPLAY_DEVICE_NAME false // Set to true to force the device name to be from the name of the device and not the model -#endif - /*-------------------ESP Wifi band and tx power ---------------------*/ //Certain sensors are sensitive to Wifi which can cause interference with their normal operation //For example it can cause false triggers on a PIR HC-SR501 @@ -657,14 +652,7 @@ bool isAduplicateSignal(uint64_t); void storeSignalValue(uint64_t); #endif -#ifndef DISPLAY_METRIC -# define DISPLAY_METRIC true // Units used for display of sensor data -#endif - #ifdef ZgatewayBT -# ifndef BLEDecoder -# define BLEDecoder true //true if we use the Theengs decoder -# endif # ifndef BLEDecryptor # define BLEDecryptor true //true if decrypt encrypted PVVX or BTHome v2 service data # endif diff --git a/main/config_BT.h b/main/config_BT.h index c6d03ebb10..9f48004523 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -122,6 +122,10 @@ extern String stateBTMeasures(bool); # define EnableBT true #endif +# ifndef BLEDecoder +# define BLEDecoder true //true if we use the Theengs decoder +# endif + #if !BLEDecoder # define UNKWNON_MODEL -1 #else diff --git a/main/config_WebUI.h b/main/config_WebUI.h index 567e67d169..0b1c723ee5 100644 --- a/main/config_WebUI.h +++ b/main/config_WebUI.h @@ -42,6 +42,10 @@ # define DISPLAY_WEBUI_INTERVAL 3 // Number of seconds between json message displays #endif +#ifndef DISPLAY_METRIC +# define DISPLAY_METRIC true // Units used for display of sensor data +#endif + #ifdef WEBUI_DEVELOPMENT # pragma message("[WebUI] Usage of SPIFFS for missing WebUI content enabled") # define FILESYSTEM SPIFFS diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index 0a1fbf9676..e668d16557 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -496,9 +496,7 @@ void strupp(char* beg) { void DT24Discovery(const char* mac, const char* sensorModel_id) { # define DT24parametersCount 7 Log.trace(F("DT24Discovery" CR)); - const char* temperatureJson; - const char* temperatureCharacter; - + const char* DT24sensor[DT24parametersCount][9] = { {"sensor", "volt", mac, "voltage", jsonVolt, "", "", "V", stateClassMeasurement}, {"sensor", "amp", mac, "current", jsonCurrent, "", "", "A", stateClassMeasurement}, @@ -542,8 +540,6 @@ void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) { void MHO_C401Discovery(const char* mac, const char* sensorModel) { # define MHO_C401parametersCount 4 Log.trace(F("MHO_C401Discovery" CR)); - const char* temperatureJson; - const char* temperatureCharacter; const char* MHO_C401sensor[MHO_C401parametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, @@ -558,8 +554,6 @@ void MHO_C401Discovery(const char* mac, const char* sensorModel) { void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) { # define HHCCJCY01HHCCparametersCount 5 Log.trace(F("HHCCJCY01HHCCDiscovery" CR)); - const char* temperatureJson; - const char* temperatureCharacter; const char* HHCCJCY01HHCCsensor[HHCCJCY01HHCCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}, @@ -575,8 +569,6 @@ void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) { void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel) { # define XMWSDJ04MMCparametersCount 4 Log.trace(F("XMWSDJ04MMCDiscovery" CR)); - const char* temperatureJson; - const char* temperatureCharacter; const char* XMWSDJ04MMCsensor[XMWSDJ04MMCparametersCount][9] = { {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement}, {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement}, @@ -589,7 +581,7 @@ void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel) { } # else -void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel, const char* device_name) {} +void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) {} void MHO_C401Discovery(const char* mac, const char* sensorModel) {} void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) {} void DT24Discovery(const char* mac, const char* sensorModel_id) {} diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index 65550c4e53..f939b63028 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -43,7 +43,6 @@ extern bool ethConnected; extern JsonArray modules; -String gateway_mac; char discovery_prefix[parameters_size + 1] = discovery_Prefix; // From https://github.com/home-assistant/core/blob/d7ac4bd65379e11461c7ce0893d3533d8d8b8cbf/homeassistant/const.py#L225 @@ -565,7 +564,6 @@ void createDiscovery(const char* sensor_type, device["sw"] = OMG_VERSION; identifiers.add(String(getMacAddress())); - gateway_mac = getMacAddress(); } else { //The Connections if (device_id[0]) { @@ -595,7 +593,6 @@ void createDiscovery(const char* sensor_type, } } - device["via_device"] = String(gateway_mac); //mac address of the gateway so that the devices link to the gateway } sensor["device"] = device; diff --git a/main/webUI.cpp b/main/webUI.cpp index de9c257f26..f000a23e40 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -519,6 +519,7 @@ void handleWU() { if (displayMetric != server.hasArg("dm")) { update = true; } + displayMetric = server.hasArg("dm"); if (webUISecure != server.hasArg("sw")) { update = true; @@ -955,23 +956,6 @@ void handleLO() { } # ifdef BLEDecryptor -size_t count_tokens( const char *s1, const char *s2 ) -{ - size_t n = 0; - while (*s1) - { - s1 += strspn( s1, s2 ); - if (*s1) - { - ++n; - s1 += strcspn( s1, s2 ); - } - } - - return n; -} - - /** * @brief /BL - Config BLE * T: handleBL: uri: /bl, args: 3, method: 1 From c1a16de715c693230911f3e5373c51668f8e9a34 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 07:52:41 +1200 Subject: [PATCH 22/27] Revert displayDeviceName and Units of measurement --- main/config_BT.h | 6 +++--- main/config_WebUI.h | 8 ++++---- main/gatewayBT.cpp | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/main/config_BT.h b/main/config_BT.h index 9f48004523..5105110139 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -122,9 +122,9 @@ extern String stateBTMeasures(bool); # define EnableBT true #endif -# ifndef BLEDecoder -# define BLEDecoder true //true if we use the Theengs decoder -# endif +#ifndef BLEDecoder +# define BLEDecoder true //true if we use the Theengs decoder +#endif #if !BLEDecoder # define UNKWNON_MODEL -1 diff --git a/main/config_WebUI.h b/main/config_WebUI.h index 0b1c723ee5..5ba02a4261 100644 --- a/main/config_WebUI.h +++ b/main/config_WebUI.h @@ -38,14 +38,14 @@ # define WEB_TEMPLATE_BUFFER_MAX_SIZE 3000 // Max size of the template buffer #endif -#ifndef DISPLAY_WEBUI_INTERVAL -# define DISPLAY_WEBUI_INTERVAL 3 // Number of seconds between json message displays -#endif - #ifndef DISPLAY_METRIC # define DISPLAY_METRIC true // Units used for display of sensor data #endif +#ifndef DISPLAY_WEBUI_INTERVAL +# define DISPLAY_WEBUI_INTERVAL 3 // Number of seconds between json message displays +#endif + #ifdef WEBUI_DEVELOPMENT # pragma message("[WebUI] Usage of SPIFFS for missing WebUI content enabled") # define FILESYSTEM SPIFFS diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index e668d16557..ae270b9838 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -496,7 +496,6 @@ void strupp(char* beg) { void DT24Discovery(const char* mac, const char* sensorModel_id) { # define DT24parametersCount 7 Log.trace(F("DT24Discovery" CR)); - const char* DT24sensor[DT24parametersCount][9] = { {"sensor", "volt", mac, "voltage", jsonVolt, "", "", "V", stateClassMeasurement}, {"sensor", "amp", mac, "current", jsonCurrent, "", "", "A", stateClassMeasurement}, From f993aee0dce64f17d720f76fa046c2289a8912c2 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 09:17:28 +1200 Subject: [PATCH 23/27] Revert minor typo --- main/gatewayBT.cpp | 8 +------- main/mqttDiscovery.cpp | 1 + main/webUI.cpp | 4 ++-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index ae270b9838..37edbadf01 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -958,6 +958,7 @@ void launchBTDiscovery(bool overrideDiscovery) { # if ForceDeviceName if (p->name[0] != '\0') { model = p->name; + } # endif std::string model_id = decoder.getTheengAttribute(p->sensorModel_id, "model_id"); @@ -1397,13 +1398,6 @@ void process_bledata(JsonObject& BLEdata) { } # endif - // Update model_id for LYWSD03MMC devices - if (model_id >= 0) { - std::string model_id_str = BLEdata["model_id"].as(); - if (model_id_str.compare("LYWSD03MMC") >= 0) { - model_id = BLEconectable::id::LYWSD03MMC; - } - } // Convert prmacs to RMACS until or if OMG gets Identity MAC/IRK decoding if (BLEdata["prmac"]) { diff --git a/main/mqttDiscovery.cpp b/main/mqttDiscovery.cpp index f939b63028..2f064cf3a9 100644 --- a/main/mqttDiscovery.cpp +++ b/main/mqttDiscovery.cpp @@ -593,6 +593,7 @@ void createDiscovery(const char* sensor_type, } } + device["via_device"] = String(gateway_name); //device name of the board } sensor["device"] = device; diff --git a/main/webUI.cpp b/main/webUI.cpp index f000a23e40..91ffe80aa9 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -504,7 +504,7 @@ void handleCN() { * @brief /WU - Configuration Page * T: handleWU: uri: /wu, args: 3, method: 1 * T: handleWU Arg: 0, dm=1 - displayMetric - * T: handleWU Arg: 1, sw=on - webUISecure + * T: handleWU Arg: 1, sw=1 - webUISecure * T: handleWU Arg: 2, save= */ void handleWU() { @@ -541,7 +541,7 @@ void handleWU() { response += String(script); response += String(style); int logLevel = Log.getLevel(); - snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "checked" : ""), (webUISecure ? "checked" : "")); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_webui_body, jsonChar, gateway_name, (displayMetric ? "checked" : ""), (webUISecure ? "checked" : "")); response += String(buffer); snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION); response += String(buffer); From d5369a874899d8e67f454c4863cc3e293a5b9cab Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 10:42:36 +1200 Subject: [PATCH 24/27] Revert minor typo --- main/gatewayBT.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index 37edbadf01..906332f884 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -959,7 +959,7 @@ void launchBTDiscovery(bool overrideDiscovery) { if (p->name[0] != '\0') { model = p->name; } -# endif +# endif std::string model_id = decoder.getTheengAttribute(p->sensorModel_id, "model_id"); // Check for tracker status From 68aafe97578c1879478b800b0ebe8ee231137eae Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 15:59:14 +1200 Subject: [PATCH 25/27] Revert minor typo --- main/webUI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/webUI.cpp b/main/webUI.cpp index 91ffe80aa9..f6be12f40f 100644 --- a/main/webUI.cpp +++ b/main/webUI.cpp @@ -503,8 +503,8 @@ void handleCN() { /** * @brief /WU - Configuration Page * T: handleWU: uri: /wu, args: 3, method: 1 - * T: handleWU Arg: 0, dm=1 - displayMetric - * T: handleWU Arg: 1, sw=1 - webUISecure + * T: handleWU Arg: 0, dm=on - displayMetric + * T: handleWU Arg: 1, sw=on - webUISecure * T: handleWU Arg: 2, save= */ void handleWU() { From 7317dde787914dd7231e7e4247eb8f86f263606f Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 21:41:50 +1200 Subject: [PATCH 26/27] Bug in Victron as nonce should be 16 bytes --- main/gatewayBT.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/main/gatewayBT.cpp b/main/gatewayBT.cpp index 906332f884..77f98539d0 100644 --- a/main/gatewayBT.cpp +++ b/main/gatewayBT.cpp @@ -1223,7 +1223,7 @@ void process_bledata(JsonObject& BLEdata) { } // Build nonce and aad - uint8_t nonce[13]; + uint8_t nonce[16]; int noncelength = 0; unsigned char aad[1]; int aadLength; @@ -1269,16 +1269,15 @@ void process_bledata(JsonObject& BLEdata) { Log.trace(F("[BLEDecryptor] BTHomeV2 nonce %s" CR), NimBLEUtils::dataToHexString(nonce, noncelength).c_str()); } else if (BLEdata["encr"].as() == 3){ - noncelength = 8; // Victron has a 8 byte nonce - memset(nonce, 0, 8); - unsigned char ctr[2]; - int ctrlen = hexToBytes(BLEdata["ctr"].as(), ctr, 2); - if (ctrlen != 2) { - Log.error(F("[BLEDecryptor] Invalid counter length %d" CR), ctrlen); + nonce[16] = {0}; // Victron has a 16 byte zero padded nonce with IV bytes 6,7 + unsigned char iv[2]; + int ivlen = hexToBytes(BLEdata["ctr"].as(), iv, 2); + if (ivlen != 2) { + Log.error(F("[BLEDecryptor] Invalid iv length %d" CR), ivlen); return; } - memcpy(nonce, ctr, 2); - Log.trace(F("[BLEDecryptor] Victron nonce %s" CR), NimBLEUtils::dataToHexString(nonce, noncelength).c_str()); + memcpy(nonce, iv, 2); + Log.trace(F("[BLEDecryptor] Victron nonce %s" CR), NimBLEUtils::dataToHexString(nonce, 16).c_str()); } else { return; // No match } @@ -1316,6 +1315,7 @@ void process_bledata(JsonObject& BLEdata) { decrypted, // output plaintext mic, sizeof(mic) // Message Integrity Check ); + mbedtls_ccm_free(&ctx); if (ret == 0) { Log.notice(F("[BLEDecryptor] Decryption successful" CR)); @@ -1345,23 +1345,22 @@ void process_bledata(JsonObject& BLEdata) { } else if (BLEdata["encr"].as() == 3) { // Decrypt Victron Energy encrypted advertisements. + size_t nc_off = 0; + uint8_t stream_block[16] = {0}; + mbedtls_aes_context ctx; mbedtls_aes_init(&ctx); mbedtls_aes_setkey_enc(&ctx, bleaeskey, 128); - - size_t nc_off = 0; - unsigned char stream_block[16]; // Must be 16 bytes (AES block size) - memset(stream_block, 0, 16); - int ret = mbedtls_aes_crypt_ctr( &ctx, // AES Key ciphertextlen, // length of ciphertext &nc_off, - nonce, + nonce, // 16 byte nonce with 2 bytes iv stream_block, ciphertext, // input ciphertext decrypted // output plaintext ); + mbedtls_aes_free(&ctx); if (ret == 0) { Log.notice(F("[BLEDecryptor] Victron Decryption successful" CR)); From dbbdd47e50445e4f103323d84d478aa840ef4122 Mon Sep 17 00:00:00 2001 From: Peter Lambrechtsen Date: Wed, 6 Aug 2025 22:06:06 +1200 Subject: [PATCH 27/27] Shortened the client side javascript for BLE key validation that is commented out due to image constrains on theengs-bridge-v11 --- main/config_WebContent.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main/config_WebContent.h b/main/config_WebContent.h index 9937e69359..eccf82f740 100644 --- a/main/config_WebContent.h +++ b/main/config_WebContent.h @@ -225,7 +225,10 @@ const char config_lora_body[] = body_header #ifdef BLEDecryptor const char config_ble_body[] = body_header "
Configure BLE

BLE AES Default Key (32 char hex)


BLE Key Pairs

MacAddress:AESKey with space separator


" body_footer_config_menu; -//const char ble_script[] = "var bkok,kpok;function bkv(){let e=document.getElementById('bk'),t=document.getElementById('bke'),l=e.value.trim();0===l.length||/^[A-Fa-f0-9]{32}$/.test(l)?(t.textContent='',e.style.color='',bkok=!0,console.log('bkok'),kpok&&(console.log('both'),document.getElementById('s').disabled=!1)):(t.textContent='Invalid format',e.style.color='red',document.getElementById('s').disabled=!0,console.log('bknok'),bkok=!1)}function kpv(){let e=document.getElementById('kp'),t=document.getElementById('kpe'),l=e.value.split(/ /),o=/^[A-Fa-f0-9]{12}:[A-Fa-f0-9]{32}$/,n=l.map((e,t)=>({line:e,index:t})).filter(e=>!o.test(e.line));if(0===e.value.trim().length||0===n.length)t.textContent='',e.style.color='',console.log('kpok'),kpok=!0,bkok&&(console.log('both'),document.getElementById('s').disabled=!1);else{let k=n.map(e=>e.index+1).join(', ');t.textContent=`Invalid format on line(s): ${k}`,e.style.color='red',document.getElementById('s').disabled=!0,console.log('kpok'),kpeok=!1}}"; + +// Client side javascript validation of the Default AES Key is hex, and the custom keys are in the correct format. This pushes the theengs-bridge-v11 way of the file size, so reudcing it now and leaving it commented out +//const char ble_script[] = "function bkv(){let e=document.getElementById('bk'),t=document.getElementById('bke'),l=e.value.trim();0===l.length||/^[A-Fa-f0-9]{32}$/.test(l)?(t.textContent='',e.style.color=''):(t.textContent='Invalid key',e.style.color='red')}function kpv(){let e=document.getElementById('kp'),t=document.getElementById('kpe'),l=e.value.split(/ /),n=/^[A-Fa-f0-9]{12}:[A-Fa-f0-9]{32}$/,o=l.map((e,t)=>({line:e,index:t})).filter(e=>!n.test(e.line));if(0===e.value.trim().length||0===o.length)t.textContent='',e.style.color='';else{let i=o.map(e=>e.index+1).join(', ');t.textContent=`Invalid format on line(s): ${i}`,e.style.color='red'}}"; + const char ble_script[] = ""; #endif