Эх сурвалжийг харах

homeassistant service discovery: derive node_id when using nested topics (#3088)

* derive correct node_id for homeassistant service discovery in nested topics (fixes #1792)

* explicit use of std::string

* move nodeId creation to separate function
add unit-tests

* add documentation about node_id generation for Home Assistant MQTT Service Discovery
Henry Thasler 1 жил өмнө
parent
commit
1300242d4a

+ 18 - 2
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:
+     *      <discovery_prefix>/<component>/[<node_id>/]<object_id>/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/ */

+ 1 - 0
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

+ 22 - 0
code/test/components/jomjol_mqtt/test_server_mqtt.cpp

@@ -0,0 +1,22 @@
+#include <unity.h>
+#include <server_mqtt.h>
+
+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();
+}

+ 2 - 1
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();
 }

+ 2 - 0
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 `<discovery_prefix>/<component>/[<node_id>/]<object_id>/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.