diff --git a/library.json b/library.json index 04fd2d0..8e8ed86 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "name": "ha-mqtt", - "version": "1.3.1", + "version": "1.4.0", "description": "Home Assistant classes for integration with MQTT auto discovery", "repository": { "type": "git", diff --git a/src/ha.h b/src/ha.h index ee64123..ff1e640 100644 --- a/src/ha.h +++ b/src/ha.h @@ -13,7 +13,11 @@ namespace Ha { uint16_t(*publisher)(const char* topic, const char* message); typedef void (*onMessage)(const char* msg); - struct DeviceConfig { + struct Config { + virtual void buildConfig(JsonDocument& jsonDoc) = 0; + }; + + struct DeviceConfig : Config { const char* id = nullptr; const char* name = nullptr; const char* model = nullptr; @@ -50,7 +54,7 @@ namespace Ha { return *this; } - void buildConfig(JsonDocument& jsonDoc) { + void buildConfig(JsonDocument& jsonDoc) override { JsonObject device = jsonDoc.createNestedObject("device"); if (name) device["name"] = name; if (model) device["model"] = model; @@ -60,9 +64,12 @@ namespace Ha { JsonArray identifiers = device.createNestedArray("identifiers"); identifiers.add(id); } + + private: + DeviceConfig(const char* id) : id(id) {} }; - struct Component { + struct Component : Config { template struct JsonPairs { @@ -121,9 +128,7 @@ namespace Ha { snprintf(configTopic, sizeof(configTopic), BASE_TOPIC"/config", type, MAIN_DEVICE_ID, id); } - virtual void buildComponentConfig(JsonDocument& jsonDoc) = 0; - - void buildConfig(JsonDocument& jsonDoc) { + void buildConfig(JsonDocument& jsonDoc) override { if (mainDevice) mainDevice->buildConfig(jsonDoc); if (entityCategory) jsonDoc["entity_category"] = entityCategory; if (deviceClass) jsonDoc["device_class"] = deviceClass; @@ -132,8 +137,6 @@ namespace Ha { jsonDoc["unique_id"] = (const char*)uniqueId; buildConfigTopic(); - buildComponentConfig(jsonDoc); - jsonBooleans.addToJson(jsonDoc); jsonNumbers.addToJson(jsonDoc); jsonStrings.addToJson(jsonDoc); @@ -267,12 +270,12 @@ namespace Ha { } }; - struct Command : Component { + struct Command : Config { bool retain = false; static unordered_map mapCommandTopics; - Command(const char* name, const char* id, const char* type, onMessage f) : Component(name, id, type), f(f) { - snprintf(commandTopic, sizeof(commandTopic), BASE_TOPIC"/set", type, MAIN_DEVICE_ID, id); + Command(Component* cmp, onMessage f) : f(f), cmp(cmp) { + snprintf(commandTopic, sizeof(commandTopic), BASE_TOPIC"/set", cmp->type, MAIN_DEVICE_ID, cmp->id); mapCommandTopics.insert({ string(commandTopic), this }); } @@ -283,28 +286,29 @@ namespace Ha { protected: char commandTopic[TOPIC_SIZE]; onMessage f; - virtual void buildCommandConfig(JsonDocument& jsonDoc) = 0; - void buildComponentConfig(JsonDocument& jsonDoc) override { + void buildConfig(JsonDocument& jsonDoc) override { jsonDoc["command_topic"] = (const char*)commandTopic; if (retain) jsonDoc["retain"] = true; - buildCommandConfig(jsonDoc); + } + private: + Component* cmp; + }; + + struct Button : Component, Command { + + Button(const char* name, const char* id, onMessage f = nullptr) : Component(name, id, "button"), Command(this, f) {} + + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + Command::buildConfig(jsonDoc); } }; - struct Button : Command { - - Button(const char* name, const char* id, onMessage f = nullptr) : Command(name, id, "button", f) {} - - protected: - void buildCommandConfig(JsonDocument& jsonDoc) override {} - - }; - - struct StateConfig { + struct State : Config { char stateTopic[TOPIC_SIZE]; - StateConfig(Component* cmp) : cmp(cmp) {} + State(Component* cmp) : cmp(cmp) {} void withStateTopic() { snprintf(stateTopic, sizeof(stateTopic), BASE_TOPIC"/state", cmp->type, MAIN_DEVICE_ID, cmp->id); @@ -315,66 +319,80 @@ namespace Ha { } protected: - Component* cmp; - void buildConfig(JsonDocument& jsonDoc) { + void buildConfig(JsonDocument& jsonDoc) override { if (stateTopic[0]) jsonDoc["state_topic"] = (const char*)stateTopic; } + private: + Component* cmp; }; - struct StatefulCommand : Command, StateConfig { + struct StatefulCommand : Command, State { static unordered_map mapRestoreStateTopics; - StatefulCommand(const char* name, const char* id, const char* type, onMessage f) - : Command(name, id, type, f), StateConfig(this) {} + StatefulCommand(Component* cmp, onMessage f) : Command(cmp, f), State(cmp) {} void restoreFromState() { mapRestoreStateTopics.insert({stateTopic, this}); } + + protected: + void buildConfig(JsonDocument& jsonDoc) override { + Command::buildConfig(jsonDoc); + State::buildConfig(jsonDoc); + } }; - struct Switch : StatefulCommand { + struct Switch : Component, StatefulCommand { - Switch(const char* name, const char* id, onMessage f = nullptr) : StatefulCommand(name, id, "switch", f) {} + Switch(const char* name, const char* id, onMessage f = nullptr) : Component(name, id, "switch"), StatefulCommand(this, f) {} void updateState(bool state) { - StateConfig::updateState(state ? "ON" : "OFF"); + State::updateState(state ? "ON" : "OFF"); } - protected: - void buildCommandConfig(JsonDocument& jsonDoc) override { - StateConfig::buildConfig(jsonDoc); + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + StatefulCommand::buildConfig(jsonDoc); } }; - struct Number : StatefulCommand { + struct Number : Component, StatefulCommand { unsigned int min, max, step; - Number(const char* name, const char* id, onMessage f) : StatefulCommand(name, id, "number", f) {} + Number(const char* name, const char* id, onMessage f) : Component(name, id, "number"), StatefulCommand(this, f) {} void updateState(unsigned int value) { - StateConfig::updateState(to_string(value).c_str()); + State::updateState(to_string(value).c_str()); } - protected: - void buildCommandConfig(JsonDocument& jsonDoc) override { - StateConfig::buildConfig(jsonDoc); + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + StatefulCommand::buildConfig(jsonDoc); jsonDoc["min"] = min; jsonDoc["max"] = max; jsonDoc["step"] = step; } }; - struct Sensor : Component, StateConfig { + struct Sensor : Component, State { const char* unitMeasure = nullptr; const char* valueTemplate = nullptr; unsigned int precision = 2; static unordered_map mapSensors; - Sensor(const char* name, const char* id) : Component(name, id, "sensor"), StateConfig(this) { + Sensor(const char* name, const char* id) : Component(name, id, "sensor"), State(this) { withStateTopic(); mapSensors.insert({ string(id), this }); } + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + State::buildConfig(jsonDoc); + if (unitMeasure) jsonDoc["unit_of_measurement"] = unitMeasure; + if (valueTemplate) jsonDoc["value_template"] = valueTemplate; + if (isNumericSensor()) jsonDoc["suggested_display_precision"] = precision; + } + protected: void buildUniqueId() override { if (multiValueComponent && deviceClass) { @@ -392,13 +410,6 @@ namespace Ha { } } - void buildComponentConfig(JsonDocument& jsonDoc) override { - StateConfig::buildConfig(jsonDoc); - if (unitMeasure) jsonDoc["unit_of_measurement"] = unitMeasure; - if (valueTemplate) jsonDoc["value_template"] = valueTemplate; - if (isNumericSensor()) jsonDoc["suggested_display_precision"] = precision; - } - private: bool isNumericSensor() { return deviceClass || unitMeasure;