Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions code/components/jomjol_mqtt/server_mqtt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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/ */
Expand Down
1 change: 1 addition & 0 deletions code/components/jomjol_mqtt/server_mqtt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 22 additions & 0 deletions code/test/components/jomjol_mqtt/test_server_mqtt.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
3 changes: 2 additions & 1 deletion code/test/test_suite_flowcontroll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -167,6 +167,7 @@ extern "C" void app_main()

// getReadoutRawString test
RUN_TEST(test_getReadoutRawString);
RUN_TEST(test_mqtt);

UNITY_END();
}
2 changes: 2 additions & 0 deletions param-docs/parameter-pages/MQTT/MainTopic.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.