From 510a378a5ec19fa72150661590c08ee46f30e865 Mon Sep 17 00:00:00 2001 From: Nicu Hodos Date: Wed, 29 May 2024 16:12:17 +0200 Subject: [PATCH] add Number, refactor StateConfig and add support for restoring state at startup --- gateway/include/devices.h | 14 +++--- gateway/include/ha.h | 91 +++++++++++++++++++++++++++++---------- gateway/include/mqtt.h | 9 +++- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/gateway/include/devices.h b/gateway/include/devices.h index e38b2cf..589f7fb 100644 --- a/gateway/include/devices.h +++ b/gateway/include/devices.h @@ -67,6 +67,7 @@ struct PollinSwitch : Switch { return uId; }()), group(group), channel(channel) { mainDevice = &DeviceConfig::create(id).withName(name).withManufacturer("Pollin").withArea(area).withParent(gatewayDevice); + withStateTopic(); deviceClass = "outlet"; p1Switches.insert({ string(id), this }); } @@ -87,6 +88,7 @@ struct EasyHomeSwitch : Switch { memcpy(&this->on[4], on, 4 * sizeof(unsigned long)); memcpy(&this->off[4], off, 4 * sizeof(unsigned long)); mainDevice = &DeviceConfig::create(id).withName(name).withManufacturer("Intertek").withModel("Easy Home").withArea(area).withParent(gatewayDevice); + withStateTopic(); deviceClass = "outlet"; for (int i = 0; i < 8; i++) { onSwitches.insert({ this->on[i], this }); @@ -120,12 +122,12 @@ Command* commands[] = { .withParent(gatewayDevice) ) .build(), - (new EasyHomeSwitch{"FritzBox", "easy_home_a", (unsigned long[4]) { 4483136, 4626800, 4661552, 4819632 }, (unsigned long[4]) { 4326544, 4537104, 4767520, 4972704 }, "Basement"})->withStateTopic(), - (new EasyHomeSwitch{"Outside", "easy_home_b", (unsigned long[4]) { 4483140, 4626804, 4661556, 4819636 }, (unsigned long[4]) { 4326548, 4537108, 4767524, 4972708 }, "Basement"})->withStateTopic(), - (new PollinSwitch{"Meeting sensor", "00001", 1, "Dining room"})->withStateTopic(), - (new PollinSwitch{"Fire Tv", "00001", 2, "Living room"})->withStateTopic(), - (new PollinSwitch{"Diningroom player", "00001", 3, "Dining room"})->withStateTopic(), - (new PollinSwitch{"Train", "11111", 4, "Playroom"})->withStateTopic() + new EasyHomeSwitch{"FritzBox", "easy_home_a", (unsigned long[4]) { 4483136, 4626800, 4661552, 4819632 }, (unsigned long[4]) { 4326544, 4537104, 4767520, 4972704 }, "Basement"}, + new EasyHomeSwitch{"Outside", "easy_home_b", (unsigned long[4]) { 4483140, 4626804, 4661556, 4819636 }, (unsigned long[4]) { 4326548, 4537108, 4767524, 4972708 }, "Basement"}, + new PollinSwitch{"Meeting sensor", "00001", 1, "Dining room"}, + new PollinSwitch{"Fire Tv", "00001", 2, "Living room"}, + new PollinSwitch{"Diningroom player", "00001", 3, "Dining room"}, + new PollinSwitch{"Train", "11111", 4, "Playroom"} }; Sensor* sensors[] = { diff --git a/gateway/include/ha.h b/gateway/include/ha.h index 4a16eb8..714a3f8 100644 --- a/gateway/include/ha.h +++ b/gateway/include/ha.h @@ -3,6 +3,8 @@ #include #include "utils.h" +using namespace std; + #define JSON_SIZE 512 #define TOPIC_SIZE 255 @@ -180,11 +182,20 @@ namespace Ha { return *this; } + Builder& withStateTopic() { + cmp->withStateTopic(); + return *this; + } + Builder& withRetain(bool retain = true) { cmp->retain = retain; return *this; } + Builder& restoreFromState() { + cmp->restoreFromState(); + return *this; + } }; struct Command : Component { @@ -216,37 +227,76 @@ namespace Ha { }; - template struct StateConfig { char stateTopic[TOPIC_SIZE]; + static unordered_map mapStateTopics; + Component* cmp; - T* withStateTopic() { - sprintf(stateTopic, "homeassistant/%s/%s/%s/state", ((T*)this)->type, MAIN_DEVICE_ID, ((T*)this)->id); - return static_cast(this); + StateConfig(Component* cmp) : cmp(cmp) {} + + void withStateTopic() { + sprintf(stateTopic, "homeassistant/%s/%s/%s/state", cmp->type, MAIN_DEVICE_ID, cmp->id); } - }; - struct Switch : Command, StateConfig { - - Switch(const char* name, const char* id, onMessage f = nullptr) : Command(name, id, "switch", f) {} - - void buildConfig(JsonDocument& jsonDoc) override { - Command::buildConfig(jsonDoc); - // jsonDoc["retain"] = true; + void buildConfig(JsonDocument& jsonDoc) { if (stateTopic[0]) jsonDoc["state_topic"] = stateTopic; } - void updateState(bool state) { - publisher(stateTopic, state ? "ON" : "OFF"); + void updateState(const char* message) { + if (stateTopic[0]) publisher(stateTopic, message); } }; - struct Sensor : Component, StateConfig { + struct Switch : Command, StateConfig { + + Switch(const char* name, const char* id, onMessage f = nullptr) : Command(name, id, "switch", f), StateConfig(this) {} + + void buildConfig(JsonDocument& jsonDoc) override { + Command::buildConfig(jsonDoc); + StateConfig::buildConfig(jsonDoc); + } + + void updateState(bool state) { + StateConfig::updateState(state ? "ON" : "OFF"); + } + + void restoreFromState() { + mapStateTopics.insert({stateTopic, this}); + } + }; + + struct Number : Command, StateConfig { + unsigned int min, max, step; + + Number(const char* name, const char* id, unsigned int min, unsigned int max, unsigned int step, onMessage f) + : Command(name, id, "number", f), StateConfig(this), min(min), max(max), step(step) {} + + void buildConfig(JsonDocument& jsonDoc) override { + Command::buildConfig(jsonDoc); + StateConfig::buildConfig(jsonDoc); + jsonDoc["min"] = min; + jsonDoc["max"] = max; + jsonDoc["step"] = step; + } + + void updateState(unsigned int value) { + char message[32]; + sprintf(message, "%u", value); + StateConfig::updateState(message); + } + + void restoreFromState() { + mapStateTopics.insert({stateTopic, this}); + } + + }; + + struct Sensor : Component, StateConfig { const char* unitMeasure = nullptr; const char* valueTemplate = nullptr; static unordered_map mapSensors; - Sensor(const char* name, const char* id) : Component(name, id, "sensor") { + Sensor(const char* name, const char* id) : Component(name, id, "sensor"), StateConfig(this) { withStateTopic(); mapSensors.insert({ id, this }); } @@ -269,15 +319,11 @@ namespace Ha { void buildConfig(JsonDocument& jsonDoc) override { Component::buildConfig(jsonDoc); + StateConfig::buildConfig(jsonDoc); if (unitMeasure) jsonDoc["unit_of_measurement"] = unitMeasure; if (valueTemplate) jsonDoc["value_template"] = valueTemplate; - jsonDoc["state_topic"] = stateTopic; jsonDoc["suggested_display_precision"] = 2; } - - void updateState(const char* message) { - publisher(stateTopic, message); - } }; struct TemperatureSensor : Sensor { @@ -307,7 +353,6 @@ namespace Ha { HumiditySensor(const char* id, const char* name = "Humidity") : Sensor(name, id) { deviceClass = "humidity"; unitMeasure = "%"; - // valueTemplate = "{{ value_json.sensor.humidity }}"; } }; @@ -315,7 +360,6 @@ namespace Ha { PressureSensor(const char* id, const char* name = "Pressure") : Sensor(name, id) { deviceClass = "pressure"; unitMeasure = "hPa"; - // valueTemplate = "{{ value_json.sensor.pressure }}"; } }; @@ -323,4 +367,5 @@ namespace Ha { List AbstractBuilder::builders; unordered_map Command::mapCommands; unordered_map Sensor::mapSensors; + unordered_map StateConfig::mapStateTopics; } diff --git a/gateway/include/mqtt.h b/gateway/include/mqtt.h index 93df1c9..66e8d46 100644 --- a/gateway/include/mqtt.h +++ b/gateway/include/mqtt.h @@ -42,8 +42,15 @@ namespace Mqtt { char msg[len + 1]; memcpy(msg, payload, len); msg[len] = 0; - Command* cmd = Command::mapCommands[string{ topic }]; + auto strTopic = string{ topic }; + auto cmd = Command::mapCommands[strTopic]; if (cmd) cmd->onCommand(msg); + + cmd = StateConfig::mapStateTopics[strTopic]; + if (cmd) { + cmd->onCommand(msg); + StateConfig::mapStateTopics.erase(strTopic); + } } void setup(Scheduler* ts = nullptr, void(*onConnected)() = nullptr, void(*onDisconnected)() = nullptr) {