refactoring: combine composition and inheritance for building the configuration

This commit is contained in:
Nicu Hodos 2024-10-28 11:49:56 +01:00
parent 2a5fb84d83
commit 1787f20ddb
2 changed files with 63 additions and 52 deletions

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json",
"name": "ha-mqtt", "name": "ha-mqtt",
"version": "1.3.1", "version": "1.4.0",
"description": "Home Assistant classes for integration with MQTT auto discovery", "description": "Home Assistant classes for integration with MQTT auto discovery",
"repository": { "repository": {
"type": "git", "type": "git",

113
src/ha.h
View File

@ -13,7 +13,11 @@ namespace Ha {
uint16_t(*publisher)(const char* topic, const char* message); uint16_t(*publisher)(const char* topic, const char* message);
typedef void (*onMessage)(const char* msg); 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* id = nullptr;
const char* name = nullptr; const char* name = nullptr;
const char* model = nullptr; const char* model = nullptr;
@ -50,7 +54,7 @@ namespace Ha {
return *this; return *this;
} }
void buildConfig(JsonDocument& jsonDoc) { void buildConfig(JsonDocument& jsonDoc) override {
JsonObject device = jsonDoc.createNestedObject("device"); JsonObject device = jsonDoc.createNestedObject("device");
if (name) device["name"] = name; if (name) device["name"] = name;
if (model) device["model"] = model; if (model) device["model"] = model;
@ -60,9 +64,12 @@ namespace Ha {
JsonArray identifiers = device.createNestedArray("identifiers"); JsonArray identifiers = device.createNestedArray("identifiers");
identifiers.add(id); identifiers.add(id);
} }
private:
DeviceConfig(const char* id) : id(id) {}
}; };
struct Component { struct Component : Config {
template <typename V> template <typename V>
struct JsonPairs { struct JsonPairs {
@ -121,9 +128,7 @@ namespace Ha {
snprintf(configTopic, sizeof(configTopic), BASE_TOPIC"/config", type, MAIN_DEVICE_ID, id); snprintf(configTopic, sizeof(configTopic), BASE_TOPIC"/config", type, MAIN_DEVICE_ID, id);
} }
virtual void buildComponentConfig(JsonDocument& jsonDoc) = 0; void buildConfig(JsonDocument& jsonDoc) override {
void buildConfig(JsonDocument& jsonDoc) {
if (mainDevice) mainDevice->buildConfig(jsonDoc); if (mainDevice) mainDevice->buildConfig(jsonDoc);
if (entityCategory) jsonDoc["entity_category"] = entityCategory; if (entityCategory) jsonDoc["entity_category"] = entityCategory;
if (deviceClass) jsonDoc["device_class"] = deviceClass; if (deviceClass) jsonDoc["device_class"] = deviceClass;
@ -132,8 +137,6 @@ namespace Ha {
jsonDoc["unique_id"] = (const char*)uniqueId; jsonDoc["unique_id"] = (const char*)uniqueId;
buildConfigTopic(); buildConfigTopic();
buildComponentConfig(jsonDoc);
jsonBooleans.addToJson(jsonDoc); jsonBooleans.addToJson(jsonDoc);
jsonNumbers.addToJson(jsonDoc); jsonNumbers.addToJson(jsonDoc);
jsonStrings.addToJson(jsonDoc); jsonStrings.addToJson(jsonDoc);
@ -267,12 +270,12 @@ namespace Ha {
} }
}; };
struct Command : Component { struct Command : Config {
bool retain = false; bool retain = false;
static unordered_map<string, Command*> mapCommandTopics; static unordered_map<string, Command*> mapCommandTopics;
Command(const char* name, const char* id, const char* type, onMessage f) : Component(name, id, type), f(f) { Command(Component* cmp, onMessage f) : f(f), cmp(cmp) {
snprintf(commandTopic, sizeof(commandTopic), BASE_TOPIC"/set", type, MAIN_DEVICE_ID, id); snprintf(commandTopic, sizeof(commandTopic), BASE_TOPIC"/set", cmp->type, MAIN_DEVICE_ID, cmp->id);
mapCommandTopics.insert({ string(commandTopic), this }); mapCommandTopics.insert({ string(commandTopic), this });
} }
@ -283,28 +286,29 @@ namespace Ha {
protected: protected:
char commandTopic[TOPIC_SIZE]; char commandTopic[TOPIC_SIZE];
onMessage f; onMessage f;
virtual void buildCommandConfig(JsonDocument& jsonDoc) = 0;
void buildComponentConfig(JsonDocument& jsonDoc) override { void buildConfig(JsonDocument& jsonDoc) override {
jsonDoc["command_topic"] = (const char*)commandTopic; jsonDoc["command_topic"] = (const char*)commandTopic;
if (retain) jsonDoc["retain"] = true; 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 { struct State : Config {
Button(const char* name, const char* id, onMessage f = nullptr) : Command(name, id, "button", f) {}
protected:
void buildCommandConfig(JsonDocument& jsonDoc) override {}
};
struct StateConfig {
char stateTopic[TOPIC_SIZE]; char stateTopic[TOPIC_SIZE];
StateConfig(Component* cmp) : cmp(cmp) {} State(Component* cmp) : cmp(cmp) {}
void withStateTopic() { void withStateTopic() {
snprintf(stateTopic, sizeof(stateTopic), BASE_TOPIC"/state", cmp->type, MAIN_DEVICE_ID, cmp->id); snprintf(stateTopic, sizeof(stateTopic), BASE_TOPIC"/state", cmp->type, MAIN_DEVICE_ID, cmp->id);
@ -315,66 +319,80 @@ namespace Ha {
} }
protected: protected:
Component* cmp; void buildConfig(JsonDocument& jsonDoc) override {
void buildConfig(JsonDocument& jsonDoc) {
if (stateTopic[0]) jsonDoc["state_topic"] = (const char*)stateTopic; if (stateTopic[0]) jsonDoc["state_topic"] = (const char*)stateTopic;
} }
private:
Component* cmp;
}; };
struct StatefulCommand : Command, StateConfig { struct StatefulCommand : Command, State {
static unordered_map<string, Command*> mapRestoreStateTopics; static unordered_map<string, Command*> mapRestoreStateTopics;
StatefulCommand(const char* name, const char* id, const char* type, onMessage f) StatefulCommand(Component* cmp, onMessage f) : Command(cmp, f), State(cmp) {}
: Command(name, id, type, f), StateConfig(this) {}
void restoreFromState() { void restoreFromState() {
mapRestoreStateTopics.insert({stateTopic, this}); 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) { void updateState(bool state) {
StateConfig::updateState(state ? "ON" : "OFF"); State::updateState(state ? "ON" : "OFF");
} }
protected: void buildConfig(JsonDocument& jsonDoc) override {
void buildCommandConfig(JsonDocument& jsonDoc) override { Component::buildConfig(jsonDoc);
StateConfig::buildConfig(jsonDoc); StatefulCommand::buildConfig(jsonDoc);
} }
}; };
struct Number : StatefulCommand { struct Number : Component, StatefulCommand {
unsigned int min, max, step; 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) { void updateState(unsigned int value) {
StateConfig::updateState(to_string(value).c_str()); State::updateState(to_string(value).c_str());
} }
protected: void buildConfig(JsonDocument& jsonDoc) override {
void buildCommandConfig(JsonDocument& jsonDoc) override { Component::buildConfig(jsonDoc);
StateConfig::buildConfig(jsonDoc); StatefulCommand::buildConfig(jsonDoc);
jsonDoc["min"] = min; jsonDoc["min"] = min;
jsonDoc["max"] = max; jsonDoc["max"] = max;
jsonDoc["step"] = step; jsonDoc["step"] = step;
} }
}; };
struct Sensor : Component, StateConfig { struct Sensor : Component, State {
const char* unitMeasure = nullptr; const char* unitMeasure = nullptr;
const char* valueTemplate = nullptr; const char* valueTemplate = nullptr;
unsigned int precision = 2; unsigned int precision = 2;
static unordered_map<string, Sensor*> mapSensors; static unordered_map<string, Sensor*> 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(); withStateTopic();
mapSensors.insert({ string(id), this }); 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: protected:
void buildUniqueId() override { void buildUniqueId() override {
if (multiValueComponent && deviceClass) { 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: private:
bool isNumericSensor() { bool isNumericSensor() {
return deviceClass || unitMeasure; return deviceClass || unitMeasure;