diff --git a/code/components/jomjol_mqtt/server_mqtt.cpp b/code/components/jomjol_mqtt/server_mqtt.cpp index c95affd2..7c7591d0 100644 --- a/code/components/jomjol_mqtt/server_mqtt.cpp +++ b/code/components/jomjol_mqtt/server_mqtt.cpp @@ -49,6 +49,15 @@ void mqttServer_setMeterType(std::string _meterType, std::string _valueUnit, std rateUnit = _rateUnit; } +/** + * Takes any multi-level MQTT-topic and returns the last topic level as nodeId + * see https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/ for details about MQTT topics +*/ +std::string createNodeId(std::string &topic) { + auto splitPos = topic.find_last_of('/'); + return (splitPos == std::string::npos) ? topic : topic.substr(splitPos + 1); +} + bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field, std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory, int qos) { @@ -69,11 +78,18 @@ bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field, name = group + " " + name; } + /** + * homeassistant needs the MQTT discovery topic according to the following structure: + * //[/]/config + * if the main topic is embedded in a nested structure, we just use the last part as node_id + * This means a maintopic "home/test/watermeter" is transformed to the discovery topic "homeassistant/sensor/watermeter/..." + */ + std::string node_id = createNodeId(maintopic); if (field == "problem") { // Special binary sensor which is based on error topic - topicFull = "homeassistant/binary_sensor/" + maintopic + "/" + configTopic + "/config"; + topicFull = "homeassistant/binary_sensor/" + node_id + "/" + configTopic + "/config"; } else { - topicFull = "homeassistant/sensor/" + maintopic + "/" + configTopic + "/config"; + topicFull = "homeassistant/sensor/" + node_id + "/" + configTopic + "/config"; } /* See https://www.home-assistant.io/docs/mqtt/discovery/ */ diff --git a/code/components/jomjol_mqtt/server_mqtt.h b/code/components/jomjol_mqtt/server_mqtt.h index 925358e3..41c359c9 100644 --- a/code/components/jomjol_mqtt/server_mqtt.h +++ b/code/components/jomjol_mqtt/server_mqtt.h @@ -22,6 +22,7 @@ std::string getTimeUnit(void); void GotConnected(std::string maintopic, bool SetRetainFlag); esp_err_t sendDiscovery_and_static_Topics(void); +std::string createNodeId(std::string &topic); #endif //SERVERMQTT_H #endif //ENABLE_MQTT \ No newline at end of file diff --git a/code/test/components/jomjol_mqtt/test_server_mqtt.cpp b/code/test/components/jomjol_mqtt/test_server_mqtt.cpp new file mode 100644 index 00000000..e59b1225 --- /dev/null +++ b/code/test/components/jomjol_mqtt/test_server_mqtt.cpp @@ -0,0 +1,22 @@ +#include +#include + +void test_createNodeId() +{ + std::string topic = "watermeter"; + TEST_ASSERT_EQUAL_STRING("watermeter", createNodeId(topic).c_str()); + + topic = "/watermeter"; + TEST_ASSERT_EQUAL_STRING("watermeter", createNodeId(topic).c_str()); + + topic = "home/test/watermeter"; + TEST_ASSERT_EQUAL_STRING("watermeter", createNodeId(topic).c_str()); + + topic = "home/test/subtopic/something/test/watermeter"; + TEST_ASSERT_EQUAL_STRING("watermeter", createNodeId(topic).c_str()); +} + +void test_mqtt() +{ + test_createNodeId(); +} \ No newline at end of file diff --git a/code/test/test_suite_flowcontroll.cpp b/code/test/test_suite_flowcontroll.cpp index b1643c26..c59c7e96 100644 --- a/code/test/test_suite_flowcontroll.cpp +++ b/code/test/test_suite_flowcontroll.cpp @@ -20,7 +20,7 @@ #include "components/jomjol-flowcontroll/test_PointerEvalAnalogToDigitNew.cpp" #include "components/jomjol-flowcontroll/test_getReadoutRawString.cpp" #include "components/jomjol-flowcontroll/test_cnnflowcontroll.cpp" - +#include "components/jomjol_mqtt/test_server_mqtt.cpp" bool Init_NVS_SDCard() { @@ -167,6 +167,7 @@ extern "C" void app_main() // getReadoutRawString test RUN_TEST(test_getReadoutRawString); + RUN_TEST(test_mqtt); UNITY_END(); } diff --git a/param-docs/parameter-pages/MQTT/MainTopic.md b/param-docs/parameter-pages/MQTT/MainTopic.md index 3cf12ec2..837b42ee 100644 --- a/param-docs/parameter-pages/MQTT/MainTopic.md +++ b/param-docs/parameter-pages/MQTT/MainTopic.md @@ -16,3 +16,5 @@ See [MQTT Result Topics](../MQTT-API#result) for a full list of topics. !!! Note The main topic is allowed to contain `/` which can be used to split it into multiple levels, eg. `/basement/meters/watermeter/1/` if you have multiple water meters in your basement. + +The nodeId for the Home Assistant MQTT Service Discovery must follow the schema `//[/]/config`. The node_id is not configurable but derived from the `MainTopic` by stripping any but the last topic level. A `MainTopic` with the value `home/basement/watermeter` is transformed into the node_id `watermeter`, resulting in the discovery topic `homeassistant/sensor/watermeter/value/config` for the current value.