diff --git a/src/ha.h b/src/ha.h index 8ebbc6b..0bd6609 100644 --- a/src/ha.h +++ b/src/ha.h @@ -6,7 +6,7 @@ using namespace std; -#define JSON_SIZE 512 +#define JSON_SIZE 1024 #define TOPIC_SIZE 255 #define CONFIG_TOPIC "homeassistant/%s/" MAIN_DEVICE_ID "/%s" #define BASE_TOPIC MAIN_DEVICE_ID "/%s" @@ -92,15 +92,13 @@ namespace Ha { void publishConfig() { StaticJsonDocument jsonDoc; buildConfig(jsonDoc); - - char message[JSON_SIZE] = {}; - serializeJson(jsonDoc, message); - - publisher(configTopic, message); + auto configTopic = buildConfigTopic(); + publisher(configTopic.get(), jsonDoc.as().c_str()); } void publishCleanupConfig() { - publisher(configTopic, ""); + auto configTopic = buildConfigTopic(); + publisher(configTopic.get(), ""); } void toJson(JsonDocument& jsonDoc) { @@ -119,11 +117,8 @@ namespace Ha { jsonDoc["name"] = name; buildUniqueId(jsonDoc); - buildConfigTopic(); } private: - char configTopic[TOPIC_SIZE] = {}; - void buildUniqueId(JsonDocument& jsonDoc) { char uniqueId[50]; if (multiValueComponent && deviceClass) { @@ -134,12 +129,14 @@ namespace Ha { jsonDoc["unique_id"] = uniqueId; } - void buildConfigTopic() { + unique_ptr buildConfigTopic() { + unique_ptr topic(new char[TOPIC_SIZE]); if (multiValueComponent && deviceClass) { - snprintf(configTopic, sizeof(configTopic), CONFIG_TOPIC"_%s""/config", type, deviceClass, id); + snprintf(topic.get(), TOPIC_SIZE, CONFIG_TOPIC"_%s""/config", type, deviceClass, id); } else { - snprintf(configTopic, sizeof(configTopic), CONFIG_TOPIC"/config", type, id); + snprintf(topic.get(), TOPIC_SIZE, CONFIG_TOPIC"/config", type, id); } + return topic; } }; @@ -149,8 +146,8 @@ namespace Ha { inline static unordered_map mapCommandIds; Command(Component* cmp, onMessage f) : f(f), cmp(cmp) { - snprintf(commandTopic, sizeof(commandTopic), BASE_TOPIC"/set", cmp->id); - mapCommandTopics.insert({ string(commandTopic), this }); + auto commandTopic = buildTopic(); + mapCommandTopics.insert({ string(commandTopic.get()), this }); mapCommandIds.insert({ string(cmp->id), this }); } @@ -163,39 +160,48 @@ namespace Ha { } protected: - char commandTopic[TOPIC_SIZE] = {}; onMessage f; void buildConfig(JsonDocument& jsonDoc) override { - jsonDoc["command_topic"] = (const char*)commandTopic; + auto commandTopic = buildTopic(); + jsonDoc["command_topic"] = commandTopic.get(); if (retain) jsonDoc["retain"] = true; } private: Component* cmp; + + unique_ptr buildTopic() { + unique_ptr topic(new char[TOPIC_SIZE]); + snprintf(topic.get(), TOPIC_SIZE, BASE_TOPIC "/set", cmp->id); + return topic; + } }; struct State : Config { - char stateTopic[TOPIC_SIZE] = {}; + char* topic; const char* jsonAttributesTemplate = nullptr; const char* valueTemplate = nullptr; - State(Component* cmp) : cmp(cmp) {} + State(Component* cmp) : cmp(cmp) { + auto len = snprintf(nullptr, 0, BASE_TOPIC"/state", cmp->id); + topic = new char[len + 1]; + } void withStateTopic() { - snprintf(stateTopic, sizeof(stateTopic), BASE_TOPIC"/state", cmp->id); + sprintf(topic, BASE_TOPIC"/state", cmp->id); } void updateState(const char* message) { - if (stateTopic[0]) publisher(stateTopic, message); + if (topic[0]) publisher(topic, message); } protected: void buildConfig(JsonDocument& jsonDoc) override { - if (stateTopic[0]) { - jsonDoc["state_topic"] = (const char*)stateTopic; + if (topic[0]) { + jsonDoc["state_topic"] = (const char*)topic; if (jsonAttributesTemplate) { jsonDoc["json_attributes_template"] = jsonAttributesTemplate; - jsonDoc["json_attributes_topic"] = (const char*)stateTopic; + jsonDoc["json_attributes_topic"] = (const char*)topic; } if (valueTemplate) jsonDoc["value_template"] = valueTemplate; } @@ -209,9 +215,9 @@ namespace Ha { StatefulCommand(Component* cmp, onMessage f) : Command(cmp, f), State(cmp) {} - void restoreStateFromCommand() { + void restoreStateFromTopic() { withStateTopic(); - mapRestoreStateTopics.insert({stateTopic, this}); + mapRestoreStateTopics.insert({State::topic, this}); } protected: @@ -508,12 +514,12 @@ namespace Ha { [[deprecated("Use restoreStateFromCommand() instead")]] Builder& restoreFromState() { - cmp->restoreStateFromCommand(); + cmp->restoreStateFromTopic(); return *this; } - Builder& restoreStateFromCommand() { - cmp->restoreStateFromCommand(); + Builder& restoreStateFromTopic() { + cmp->restoreStateFromTopic(); return *this; } }; diff --git a/src/webserver.h b/src/webserver.h index d16ce68..b9f0c81 100644 --- a/src/webserver.h +++ b/src/webserver.h @@ -30,10 +30,10 @@ namespace WebServer { server.on("/commands", HTTP_GET, [](AsyncWebServerRequest *request) { AsyncResponseStream *response = request->beginResponseStream("application/json"); - DynamicJsonDocument jsonResponse(JSON_SIZE*10); + DynamicJsonDocument jsonResponse(5120); JsonArray array = jsonResponse.to(); for (auto it = Command::mapCommandIds.begin(); it != Command::mapCommandIds.end(); ++it) { - StaticJsonDocument jsonDoc; + StaticJsonDocument<256> jsonDoc; it->second->toJson(jsonDoc); array.add(jsonDoc); } diff --git a/test/native/ha/test_components/main.cpp b/test/native/ha/test_components/main.cpp index aa2917e..3cacb02 100644 --- a/test/native/ha/test_components/main.cpp +++ b/test/native/ha/test_components/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #define MAIN_DEVICE_ID "test" @@ -208,6 +209,18 @@ void testBinarySensor(void) { TEST_ASSERT_NOT_NULL(GenericSensor::mapSensors["id"]); } +void testPublisher(void) { + Ha::publisher = [](const char* topic, const char* message) -> uint16_t { + TEST_ASSERT_EQUAL_STRING("{\"name\":\"a_name\",\"unique_id\":\"test_id\",\"command_topic\":\"test/id/set\"}", message); + return 0; + }; + Switch s("a_name", "id"); + + StaticJsonDocument<256> doc; + s.buildConfig(doc); + s.publishConfig(); +} + int main(int argc, char **argv) { UNITY_BEGIN(); RUN_TEST(testDevice); @@ -224,5 +237,6 @@ int main(int argc, char **argv) { RUN_TEST(testSwitchWithState); RUN_TEST(testNumber); RUN_TEST(testBinarySensor); + RUN_TEST(testPublisher); return UNITY_END(); }