diff --git a/src/ha.h b/src/ha.h index 111ebc1..471604d 100644 --- a/src/ha.h +++ b/src/ha.h @@ -89,7 +89,7 @@ namespace Ha { StaticJsonDocument jsonDoc; buildConfig(jsonDoc); - char message[JSON_SIZE]; + char message[JSON_SIZE] = {}; serializeJson(jsonDoc, message); publisher(configTopic, message); @@ -110,7 +110,7 @@ namespace Ha { buildConfigTopic(); } private: - char configTopic[TOPIC_SIZE]; + char configTopic[TOPIC_SIZE] = {}; void buildUniqueId(JsonDocument& jsonDoc) { char uniqueId[50]; @@ -131,6 +131,181 @@ namespace Ha { } }; + struct Command : Config { + bool retain = false; + static unordered_map mapCommandTopics; + + 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 }); + } + + virtual void onCommand(const char* msg) { + if (f) f(msg); + } + + protected: + char commandTopic[TOPIC_SIZE] = {}; + onMessage f; + + void buildConfig(JsonDocument& jsonDoc) override { + jsonDoc["command_topic"] = (const char*)commandTopic; + if (retain) jsonDoc["retain"] = true; + } + private: + Component* cmp; + }; + + struct State : Config { + char stateTopic[TOPIC_SIZE] = {}; + const char* jsonAttributesTemplate = nullptr; + + State(Component* cmp) : cmp(cmp) {} + + void withStateTopic() { + snprintf(stateTopic, sizeof(stateTopic), BASE_TOPIC"/state", cmp->type, MAIN_DEVICE_ID, cmp->id); + } + + void updateState(const char* message) { + if (stateTopic[0]) publisher(stateTopic, message); + } + + protected: + void buildConfig(JsonDocument& jsonDoc) override { + if (stateTopic[0]) { + jsonDoc["state_topic"] = (const char*)stateTopic; + if (jsonAttributesTemplate) { + jsonDoc["json_attributes_template"] = jsonAttributesTemplate; + jsonDoc["json_attributes_topic"] = (const char*)stateTopic; + } + } + } + private: + Component* cmp; + }; + + struct StatefulCommand : Command, State { + static unordered_map mapRestoreStateTopics; + + 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 Button : Component, Command { + + Button(const char* name, const char* id, onMessage f = nullptr) : Component(id, name, "button"), Command(this, f) {} + + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + Command::buildConfig(jsonDoc); + } + }; + + struct Switch : Component, StatefulCommand { + + Switch(const char* name, const char* id, onMessage f = nullptr) : Component(id, name, "switch"), StatefulCommand(this, f) {} + + void updateState(bool state) { + State::updateState(state ? "ON" : "OFF"); + } + + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + StatefulCommand::buildConfig(jsonDoc); + } + }; + + struct Number : Component, StatefulCommand { + unsigned int min = 1; + unsigned int max = 100; + unsigned int step = 1; + + Number(const char* name, const char* id, onMessage f) : Component(id, name, "number"), StatefulCommand(this, f) {} + + void updateState(unsigned int value) { + State::updateState(to_string(value).c_str()); + } + + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + StatefulCommand::buildConfig(jsonDoc); + jsonDoc["min"] = min; + jsonDoc["max"] = max; + jsonDoc["step"] = step; + } + }; + + 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(id, name, "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; + } + + private: + bool isNumericSensor() { + return deviceClass || unitMeasure; + } + }; + + struct TemperatureSensor : Sensor { + TemperatureSensor(const char* id, const char* name = "Temperature") : Sensor(name, id) { + deviceClass = "temperature"; + unitMeasure = "°C"; + } + }; + + struct HumiditySensor : Sensor { + HumiditySensor(const char* id, const char* name = "Humidity") : Sensor(name, id) { + deviceClass = "humidity"; + unitMeasure = "%"; + } + }; + + struct PressureSensor : Sensor { + PressureSensor(const char* id, const char* name = "Pressure") : Sensor(name, id) { + deviceClass = "pressure"; + unitMeasure = "hPa"; + } + }; + + struct VoltageSensor : Sensor { + VoltageSensor(const char* id, const char* name, const char* valueTemplate) : Sensor(name, id) { + this->valueTemplate = valueTemplate; + deviceClass = "voltage"; + unitMeasure = "V"; + } + }; + + struct BatterySensor : Sensor { + BatterySensor(const char* id, const char* name, const char* valueTemplate) : Sensor(name, id) { + this->valueTemplate = valueTemplate; + deviceClass = "battery"; + unitMeasure = "%"; + } + }; + struct AbstractBuilder { static List builders; @@ -248,179 +423,6 @@ namespace Ha { } }; - struct Command : Config { - bool retain = false; - static unordered_map mapCommandTopics; - - 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 }); - } - - virtual void onCommand(const char* msg) { - if (f) f(msg); - } - - protected: - char commandTopic[TOPIC_SIZE]; - onMessage f; - - void buildConfig(JsonDocument& jsonDoc) override { - jsonDoc["command_topic"] = (const char*)commandTopic; - if (retain) jsonDoc["retain"] = true; - } - private: - Component* cmp; - }; - - struct Button : Component, Command { - - Button(const char* name, const char* id, onMessage f = nullptr) : Component(id, name, "button"), Command(this, f) {} - - void buildConfig(JsonDocument& jsonDoc) override { - Component::buildConfig(jsonDoc); - Command::buildConfig(jsonDoc); - } - }; - - struct State : Config { - char stateTopic[TOPIC_SIZE]; - const char* jsonAttributesTemplate = nullptr; - - State(Component* cmp) : cmp(cmp) {} - - void withStateTopic() { - snprintf(stateTopic, sizeof(stateTopic), BASE_TOPIC"/state", cmp->type, MAIN_DEVICE_ID, cmp->id); - } - - void updateState(const char* message) { - if (stateTopic[0]) publisher(stateTopic, message); - } - - protected: - void buildConfig(JsonDocument& jsonDoc) override { - if (stateTopic[0]) { - jsonDoc["state_topic"] = (const char*)stateTopic; - if (jsonAttributesTemplate) { - jsonDoc["json_attributes_template"] = jsonAttributesTemplate; - jsonDoc["json_attributes_topic"] = (const char*)stateTopic; - } - } - } - private: - Component* cmp; - }; - - struct StatefulCommand : Command, State { - static unordered_map mapRestoreStateTopics; - - 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 : Component, StatefulCommand { - - Switch(const char* name, const char* id, onMessage f = nullptr) : Component(id, name, "switch"), StatefulCommand(this, f) {} - - void updateState(bool state) { - State::updateState(state ? "ON" : "OFF"); - } - - void buildConfig(JsonDocument& jsonDoc) override { - Component::buildConfig(jsonDoc); - StatefulCommand::buildConfig(jsonDoc); - } - }; - - struct Number : Component, StatefulCommand { - unsigned int min, max, step; - - Number(const char* name, const char* id, onMessage f) : Component(id, name, "number"), StatefulCommand(this, f) {} - - void updateState(unsigned int value) { - State::updateState(to_string(value).c_str()); - } - - void buildConfig(JsonDocument& jsonDoc) override { - Component::buildConfig(jsonDoc); - StatefulCommand::buildConfig(jsonDoc); - jsonDoc["min"] = min; - jsonDoc["max"] = max; - jsonDoc["step"] = step; - } - }; - - 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(id, name, "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; - } - - private: - bool isNumericSensor() { - return deviceClass || unitMeasure; - } - }; - - struct TemperatureSensor : Sensor { - TemperatureSensor(const char* id, const char* name = "Temperature") : Sensor(name, id) { - deviceClass = "temperature"; - unitMeasure = "°C"; - } - }; - - struct VoltageSensor : Sensor { - VoltageSensor(const char* id, const char* name, const char* valueTemplate) : Sensor(name, id) { - this->valueTemplate = valueTemplate; - deviceClass = "voltage"; - unitMeasure = "V"; - } - }; - - struct BatterySensor : Sensor { - BatterySensor(const char* id, const char* name, const char* valueTemplate) : Sensor(name, id) { - this->valueTemplate = valueTemplate; - deviceClass = "battery"; - unitMeasure = "%"; - } - }; - - struct HumiditySensor : Sensor { - HumiditySensor(const char* id, const char* name = "Humidity") : Sensor(name, id) { - deviceClass = "humidity"; - unitMeasure = "%"; - } - }; - - struct PressureSensor : Sensor { - PressureSensor(const char* id, const char* name = "Pressure") : Sensor(name, id) { - deviceClass = "pressure"; - unitMeasure = "hPa"; - } - }; - List Component::components; List AbstractBuilder::builders; unordered_map Command::mapCommandTopics;