diff --git a/include/BatteryStats.h b/include/BatteryStats.h index 94da35d78..e606ad291 100644 --- a/include/BatteryStats.h +++ b/include/BatteryStats.h @@ -13,6 +13,7 @@ class BatteryStats { public: String const& getManufacturer() const { return _manufacturer; } + String const& getFwVersion() const { return _fwversion; } // the last time *any* data was updated uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; } diff --git a/include/MqttHandleBatteryHass.h b/include/MqttHandleBatteryHass.h index f328a6f88..72bf26472 100644 --- a/include/MqttHandleBatteryHass.h +++ b/include/MqttHandleBatteryHass.h @@ -2,19 +2,22 @@ #pragma once #include -#include +#include "MqttHassPublisher.h" -class MqttHandleBatteryHassClass { +class MqttHandleBatteryHassClass : public MqttHassPublisher { public: void init(Scheduler& scheduler); void forceUpdate() { _doPublish = true; } private: void loop(); - void publish(const String& subtopic, const String& payload); void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off); void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL); - void createDeviceInfo(JsonObject& object); + + void publishBinarySensor2(const char* caption, const char* icon, const char* sensorId, const char* subTopic); + JsonObject createDeviceInfo(); + + String configTopicPrefix(); Task _loopTask; diff --git a/include/MqttHassPublisher.h b/include/MqttHassPublisher.h new file mode 100644 index 000000000..e8e2abf39 --- /dev/null +++ b/include/MqttHassPublisher.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Thomas Basler and others + */ +#pragma once + +#include + +class MqttHassPublisher { +public: + static void publish(const String& subtopic, const JsonDocument& payload); + +protected: + static JsonObject createDeviceInfo(const String& name, const String& identifiers, const String& model, const String& sw_version, const bool& via_dtu); + static String getDtuUniqueId(); + + void publishBinarySensor(const String& unique_dentifier, const String& name, const String& icon, const String& sensorId, const String& statSubTopic); + + virtual String configTopicPrefix(); + virtual JsonObject createDeviceInfo(); + +private: + static String getDtuUrl(); +}; diff --git a/src/MqttHandleBatteryHass.cpp b/src/MqttHandleBatteryHass.cpp index 9f24abe42..43612ff89 100644 --- a/src/MqttHandleBatteryHass.cpp +++ b/src/MqttHandleBatteryHass.cpp @@ -5,7 +5,6 @@ #include "MqttHandleBatteryHass.h" #include "Configuration.h" #include "MqttSettings.h" -#include "MqttHandleHass.h" #include "Utils.h" #include "__compiled_constants.h" @@ -159,7 +158,9 @@ void MqttHandleBatteryHassClass::loop() publishSensor("Modules Blocking Charge", "mdi:counter", "modulesBlockingCharge"); publishSensor("Modules Blocking Discharge", "mdi:counter", "modulesBlockingDischarge"); - publishBinarySensor("Alarm Discharge current", "mdi:alert", "alarm/overCurrentDischarge", "1", "0"); + // publishBinarySensor("Alarm Discharge current", "mdi:alert", "alarm/overCurrentDischarge", "1", "0"); + publishBinarySensor2("Alarm Discharge current", "mdi:alert", "alarm_discharge_current", "battery/alarm/overCurrentDischarge"); + publishBinarySensor("Alarm High charge current", "mdi:alert", "alarm/overCurrentCharge", "1", "0"); publishBinarySensor("Alarm Voltage low", "mdi:alert", "alarm/underVoltage", "1", "0"); publishBinarySensor("Alarm Voltage high", "mdi:alert", "alarm/overVoltage", "1", "0"); @@ -218,8 +219,7 @@ void MqttHandleBatteryHassClass::publishSensor(const char* caption, const char* root["unit_of_meas"] = unitOfMeasurement; } - JsonObject deviceObj = root["dev"].to(); - createDeviceInfo(deviceObj); + root["dev"] = createDeviceInfo(); if (Configuration.get().Mqtt.Hass.Expire) { root["exp_aft"] = Battery.getStats()->getMqttFullPublishIntervalMs() / 1000 * 3; @@ -235,10 +235,28 @@ void MqttHandleBatteryHassClass::publishSensor(const char* caption, const char* return; } - char buffer[512]; - serializeJson(root, buffer); - publish(configTopic, buffer); + MqttHassPublisher::publish(configTopic, root); +} + +String MqttHandleBatteryHassClass::uniqueIdentifier(const char* sensorId) +{ + return serial + "_" + sensorId; +} +String MqttHandleBatteryHassClass::configTopicPrefix() +{ + return "dtu_battery_" + serial; +} + +void MqttHandleBatteryHassClass::publishBinarySensor2(const char* caption, const char* icon, const char* sensorId, const char* subTopic) +{ + MqttHassPublisher::publishBinarySensor( + uniqueIdentifier(sensorId), + caption, + icon, + sensorId, + subTopic + ); } void MqttHandleBatteryHassClass::publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off) @@ -251,60 +269,48 @@ void MqttHandleBatteryHassClass::publishBinarySensor(const char* caption, const sensorId.replace(":", ""); sensorId.toLowerCase(); - String configTopic = "binary_sensor/dtu_battery_" + serial + String configTopic = "dtu_battery_" + serial + "/" + sensorId + "/config"; - String statTopic = MqttSettings.getPrefix() + "battery/"; + String statTopic = "battery/"; // omit serial to avoid a breaking change // statTopic.concat(serial); // statTopic.concat("/"); statTopic.concat(subTopic); - JsonDocument root; - - root["name"] = caption; - root["uniq_id"] = serial + "_" + sensorId; - root["stat_t"] = statTopic; - root["pl_on"] = payload_on; - root["pl_off"] = payload_off; - - if (icon != NULL) { - root["icon"] = icon; - } - - auto deviceObj = root["dev"].to(); - createDeviceInfo(deviceObj); - - if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) { - return; - } - - char buffer[512]; - serializeJson(root, buffer); - publish(configTopic, buffer); + // MqttHassPublisher::publishBinarySensor( + // serial + "_" + sensorId, + // caption, + // icon, + // configTopic, + // statTopic, + // payload_on, + // payload_off, + // createDeviceInfo() + // ); } -void MqttHandleBatteryHassClass::createDeviceInfo(JsonObject& object) +JsonObject MqttHandleBatteryHassClass::createDeviceInfo() { - object["name"] = "Battery(" + serial + ")"; + String name = "Battery(" + serial + ")"; auto& config = Configuration.get(); if (config.Battery.Provider == 1) { - object["name"] = "JK BMS (" + Battery.getStats()->getManufacturer() + ")"; + name = "JK BMS (" + Battery.getStats()->getManufacturer() + ")"; } - object["ids"] = serial; - object["cu"] = MqttHandleHass.getDtuUrl(); - object["mf"] = "OpenDTU"; - object["mdl"] = Battery.getStats()->getManufacturer(); - object["sw"] = __COMPILED_GIT_HASH__; - object["via_device"] = MqttHandleHass.getDtuUniqueId(); -} + String firmareVersion = Battery.getStats()->getFwVersion(); -void MqttHandleBatteryHassClass::publish(const String& subtopic, const String& payload) -{ - String topic = Configuration.get().Mqtt.Hass.Topic; - topic += subtopic; - MqttSettings.publishGeneric(topic.c_str(), payload.c_str(), Configuration.get().Mqtt.Hass.Retain); + if (firmareVersion == "") { + firmareVersion = __COMPILED_GIT_HASH__; + } + + return MqttHassPublisher::createDeviceInfo( + name, + serial, + Battery.getStats()->getManufacturer(), + firmareVersion, + true + ); } diff --git a/src/MqttHassPublisher.cpp b/src/MqttHassPublisher.cpp new file mode 100644 index 000000000..95e290aeb --- /dev/null +++ b/src/MqttHassPublisher.cpp @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Thomas Basler and others + */ +#include "MqttHassPublisher.h" +#include "MqttSettings.h" +#include "NetworkSettings.h" +#include "Utils.h" +#include "Configuration.h" + +void MqttHassPublisher::publish(const String& subtopic, const JsonDocument& payload) +{ + String buffer; + serializeJson(payload, buffer); + + String topic = Configuration.get().Mqtt.Hass.Topic; + topic += subtopic; + MqttSettings.publishGeneric(topic, buffer, Configuration.get().Mqtt.Hass.Retain); +} + + +JsonObject MqttHassPublisher::createDeviceInfo( + const String& name, const String& identifiers, + const String& model, const String& sw_version, + const bool& via_dtu) +{ + JsonObject object; + + object["name"] = name; + object["ids"] = identifiers; + object["cu"] = getDtuUrl(), + object["mf"] = "OpenDTU"; + object["mdl"] = model; + object["sw"] = sw_version; + + if (via_dtu) { + object["via_device"] = getDtuUniqueId(); + } + + return object; +} + +void MqttHassPublisher::publishBinarySensor( + const String& unique_dentifier, + const String& name, const String& icon, + const String& sensorId, const String& statSubTopic) +{ + JsonDocument root; + + root["name"] = name; + root["uniq_id"] = unique_dentifier; + root["stat_t"] = MqttSettings.getPrefix() + "/" + statSubTopic; + root["pl_on"] = "1"; + root["pl_off"] = "0"; + + if (icon != nullptr) { + root["icon"] = icon; + } + + root["dev"] = createDeviceInfo(); + + if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) { + return; + } + + publish( + "binary_sensor/" + configTopicPrefix() + "/" + sensorId + "/config", + root + ); +} + +String MqttHassPublisher::getDtuUniqueId() +{ + return NetworkSettings.getHostname() + "_" + Utils::getChipId(); +} + +String MqttHassPublisher::getDtuUrl() +{ + return String("http://") + NetworkSettings.localIP().toString(); +}