diff --git a/.gitignore b/.gitignore index d7ee03c..2cb2faa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -.pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch +.pio/ +.vscode/ credentials.h diff --git a/include/ha.h b/include/ha.h index 9b628c1..3b72509 100644 --- a/include/ha.h +++ b/include/ha.h @@ -1,97 +1,124 @@ #include #define JSON_SIZE 512 +#define TOPIC_SIZE 255 namespace Ha { - struct SensorConfig { + struct Component { const char* name; - const char* uniqueId; - const char* stateTopic; + const char* id; + char configTopic[TOPIC_SIZE]; + + Component(const char* name, const char* id, const char* type) { + this->name = name; + this->id = id; + sprintf(configTopic, "homeassistant/%s/esp_clock/%s/config", type, id); + } + + virtual void buildUniqueId(char* uniqueId) = 0; + + virtual void buildConfig(JsonDocument& jsonDoc) { + buildDeviceConfig(jsonDoc); + jsonDoc["name"] = name; + char uniqueId[50]; + buildUniqueId(uniqueId); + jsonDoc["unique_id"] = uniqueId; + } + + void buildDeviceConfig(JsonDocument& jsonDoc) { + JsonObject device = jsonDoc.createNestedObject("device"); + device["name"] = "ESP Clock"; + device["suggested_area"] = "Living room"; + JsonArray connections = device.createNestedArray("connections"); + JsonArray mac = connections.createNestedArray(); + mac.add("mac"); + mac.add(WiFi.macAddress()); + } + }; + + typedef void (*onMessage)(const char* msg); + struct Command : Component { + char commandTopic[TOPIC_SIZE]; + onMessage f; + + Command(const char* name, const char* id, const char* type, onMessage f) : Component(name, id, type) { + this->f = f; + } + + void buildUniqueId(char* uniqueId) override { + sprintf(uniqueId, "esp_clock_%s", id); + } + + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + jsonDoc["command_topic"] = commandTopic; + } + + }; + + struct Button : Command { + static constexpr const char* type = "button"; + + Button(const char* name, const char* id, onMessage f) : Command(name, id, type, f) { + sprintf(commandTopic, "homeassistant/%s/esp_clock/%s", type, id); + } + + }; + + struct Switch : Command { + static constexpr const char* type = "switch"; + + Switch(const char* name, const char* id, onMessage f) : Command(name, id, type, f) { + sprintf(commandTopic, "homeassistant/%s/esp_clock/%s/set", type, id); + } + + }; + + struct Sensor : Component { const char* deviceClass; const char* unitMeasure; const char* valueTemplate; + static constexpr const char* stateTopic = "homeassistant/sensor/esp_clock/state"; + + Sensor(const char* name, const char* id) : Component(name, id, "sensor") { + } + + void buildUniqueId(char* uniqueId) override { + sprintf(uniqueId, "living_room_%s", id); + } + + void buildConfig(JsonDocument& jsonDoc) override { + Component::buildConfig(jsonDoc); + jsonDoc["device_class"] = deviceClass; + jsonDoc["unit_of_measurement"] = unitMeasure; + jsonDoc["value_template"] = valueTemplate; + jsonDoc["state_topic"] = stateTopic; + } + }; - struct TemperatureConfig : SensorConfig { - TemperatureConfig(const char* name, const char* uniqueId, const char* stateTopic) { - this->name = name; - this->uniqueId = uniqueId; - this->stateTopic = stateTopic; - this->deviceClass = "temperature"; - this->unitMeasure = "°C"; - this->valueTemplate = "{{ value_json.temperature }}"; + struct TemperatureSensor : Sensor { + TemperatureSensor(const char* name, const char* id) : Sensor(name, id) { + deviceClass = "temperature"; + unitMeasure = "°C"; + valueTemplate = "{{ value_json.temperature }}"; } }; - struct PressureConfig : SensorConfig { - PressureConfig(const char* name, const char* uniqueId, const char* stateTopic) { - this->name = name; - this->uniqueId = uniqueId; - this->stateTopic = stateTopic; - this->deviceClass = "pressure"; - this->unitMeasure = "hPa"; - this->valueTemplate = "{{ value_json.pressure }}"; + struct PressureSensor : Sensor { + PressureSensor(const char* name, const char* id) : Sensor(name, id) { + deviceClass = "pressure"; + unitMeasure = "hPa"; + valueTemplate = "{{ value_json.pressure }}"; } }; - struct AltitudeConfig : SensorConfig { - AltitudeConfig(const char* name, const char* uniqueId, const char* stateTopic) { - this->name = name; - this->uniqueId = uniqueId; - this->stateTopic = stateTopic; - this->deviceClass = "distance"; - this->unitMeasure = "m"; - this->valueTemplate = "{{ value_json.altitude }}"; + struct AltitudeSensor : Sensor { + AltitudeSensor(const char* name, const char* id) : Sensor(name, id) { + deviceClass = "distance"; + unitMeasure = "m"; + valueTemplate = "{{ value_json.altitude }}"; } }; - - void buildDeviceConfig(JsonDocument& jsonDoc) { - JsonObject device = jsonDoc.createNestedObject("device"); - device["name"] = "ESP Clock"; - device["suggested_area"] = "Living room"; - JsonArray connections = device.createNestedArray("connections"); - JsonArray mac = connections.createNestedArray(); - mac.add("mac"); - mac.add(WiFi.macAddress()); - } - - void buildSensorConfig(char(&output)[JSON_SIZE], SensorConfig config) { - StaticJsonDocument jsonDoc; - buildDeviceConfig(jsonDoc); - jsonDoc["device_class"] = config.deviceClass; - jsonDoc["name"] = config.name; - jsonDoc["unique_id"] = config.uniqueId; - jsonDoc["unit_of_measurement"] = config.unitMeasure; - jsonDoc["state_topic"] = config.stateTopic; - jsonDoc["value_template"] = config.valueTemplate; - serializeJson(jsonDoc, output); - } - - void buildRestartConfig(char(&output)[JSON_SIZE], const char* topic) { - StaticJsonDocument jsonDoc; - buildDeviceConfig(jsonDoc); - jsonDoc["name"] = "Restart"; - jsonDoc["unique_id"] = "esp_clock_restart"; - jsonDoc["command_topic"] = topic; - serializeJson(jsonDoc, output); - } - - void buildLedConfig(char(&output)[JSON_SIZE], const char* topic) { - StaticJsonDocument jsonDoc; - buildDeviceConfig(jsonDoc); - jsonDoc["name"] = "ESP Clock Led"; - jsonDoc["unique_id"] = "esp_clock_led"; - jsonDoc["command_topic"] = topic; - serializeJson(jsonDoc, output); - } - - void buildHourFormatConfig(char(&output)[JSON_SIZE], const char* topic) { - StaticJsonDocument jsonDoc; - buildDeviceConfig(jsonDoc); - jsonDoc["name"] = "ESP Clock Format 24h"; - jsonDoc["unique_id"] = "esp_clock_format_24h"; - jsonDoc["command_topic"] = topic; - serializeJson(jsonDoc, output); - } } diff --git a/include/mqtt.h b/include/mqtt.h index b0c4b9f..b8f8e4b 100644 --- a/include/mqtt.h +++ b/include/mqtt.h @@ -34,10 +34,6 @@ namespace Mqtt { } } commands; - const char* bmpTopic = "homeassistant/sensor/esp_clock/state"; - const char* restartTopic = "homeassistant/button/esp_clock/restart"; - const char* ledTopic = "homeassistant/switch/esp_clock/led/set"; - const char* hourFormatTopic = "homeassistant/switch/esp_clock/hour_format/set"; const char* espClockTopic = "homeassistant/+/esp_clock/#"; void connect() { @@ -49,49 +45,53 @@ namespace Mqtt { client.disconnect(); } - void publishTempConfig() { - char message[JSON_SIZE]; - Ha::buildSensorConfig(message, Ha::TemperatureConfig{ "Living room Temperature", "living_room_temperature", bmpTopic }); - client.publish("homeassistant/sensor/esp_clock/temperature/config", 0, true, message); - } + Ha::Sensor sensors[] = { + Ha::TemperatureSensor{"Living room Temperature", "temperature"}, + Ha::PressureSensor{"Living room Pressure", "pressure"}, + Ha::AltitudeSensor{"Living room Altitude", "altitude"} + }; - void publishPressureConfig() { - char message[JSON_SIZE]; - Ha::buildSensorConfig(message, Ha::PressureConfig{ "Living room Pressure", "living_room_pressure", bmpTopic }); - client.publish("homeassistant/sensor/esp_clock/pressure/config", 0, true, message); - } + Ha::Command switches[] = { + Ha::Button{"ESP Clock Restart", "restart", + [](const char* msg) { + if (String { "PRESS" }.equals(msg)) ESP.restart(); + } + }, + Ha::Switch{"ESP Clock Led", "led", + [](const char* msg) { + String{ "ON" }.equals(msg) ? digitalWrite(LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH); + } + }, + Ha::Switch{"ESP Clock Format 24h", "format_24h", + [](const char* msg) { + if (String{ "ON" }.equals(msg)) { + Display::hourFormat24 = true; + Display::drawTime(); + } else { + Display::hourFormat24 = false; + Display::drawTime(); + } + } + } + }; - void publishAltitudeConfig() { - char message[JSON_SIZE]; - Ha::buildSensorConfig(message, Ha::AltitudeConfig{ "Living room Altitude", "living_room_altitude", bmpTopic }); - client.publish("homeassistant/sensor/esp_clock/altitude/config", 0, true, message); - } + void publishComponentConfig(Ha::Component& component) { + StaticJsonDocument jsonDoc; + component.buildConfig(jsonDoc); - void publishRestartConfig() { char message[JSON_SIZE]; - Ha::buildRestartConfig(message, restartTopic); - client.publish("homeassistant/button/esp_clock/restart/config", 0, true, message); - } + serializeJson(jsonDoc, message); - void publishLedConfig() { - char message[JSON_SIZE]; - Ha::buildLedConfig(message, ledTopic); - client.publish("homeassistant/switch/esp_clock/led/config", 0, true, message); - } - - void publishHourFormatConfig() { - char message[JSON_SIZE]; - Ha::buildHourFormatConfig(message, hourFormatTopic); - client.publish("homeassistant/switch/esp_clock/hour_format/config", 0, true, message); + client.publish(component.configTopic, 0, true, message); } void publishConfig() { - publishTempConfig(); - publishPressureConfig(); - publishAltitudeConfig(); - publishRestartConfig(); - publishLedConfig(); - publishHourFormatConfig(); + for (Ha::Component& cmp : sensors) { + publishComponentConfig(cmp); + } + for (Ha::Component& cmp : switches) { + publishComponentConfig(cmp); + } ts.deleteTask(tPublishConfig); } @@ -103,7 +103,7 @@ namespace Mqtt { jsonDoc["altitude"] = Bmp::data.altitude; char message[255]; serializeJson(jsonDoc, message); - client.publish(bmpTopic, 0, true, message); + client.publish(Ha::Sensor::stateTopic, 0, true, message); } void publishCommand() { @@ -122,23 +122,10 @@ namespace Mqtt { char msg[len + 1]; memcpy(msg, payload, len); msg[len] = 0; - if (String{ restartTopic }.equals(topic) && String { "PRESS" }.equals(msg)) { - ESP.restart(); - } - if (String{ ledTopic }.equals(topic)) { - if (String{ "ON" }.equals(msg)) { - digitalWrite(LED_BUILTIN, LOW); - } else { - digitalWrite(LED_BUILTIN, HIGH); - } - } - if (String{ hourFormatTopic }.equals(topic)) { - if (String{ "ON" }.equals(msg)) { - Display::hourFormat24 = true; - Display::drawTime(); - } else { - Display::hourFormat24 = false; - Display::drawTime(); + for (Ha::Command cmd : switches) { + if (String{ cmd.commandTopic }.equals(topic)) { + cmd.f(msg); + return; } } }