Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: add base class for hass discovery #1137

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/BatteryStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
11 changes: 7 additions & 4 deletions include/MqttHandleBatteryHass.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@
#pragma once

#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h>
#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;

Expand Down
24 changes: 24 additions & 0 deletions include/MqttHassPublisher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 Thomas Basler and others
*/
#pragma once

#include <ArduinoJson.h>

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();
};
98 changes: 52 additions & 46 deletions src/MqttHandleBatteryHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "MqttHandleBatteryHass.h"
#include "Configuration.h"
#include "MqttSettings.h"
#include "MqttHandleHass.h"
#include "Utils.h"
#include "__compiled_constants.h"

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -218,8 +219,7 @@ void MqttHandleBatteryHassClass::publishSensor(const char* caption, const char*
root["unit_of_meas"] = unitOfMeasurement;
}

JsonObject deviceObj = root["dev"].to<JsonObject>();
createDeviceInfo(deviceObj);
root["dev"] = createDeviceInfo();

if (Configuration.get().Mqtt.Hass.Expire) {
root["exp_aft"] = Battery.getStats()->getMqttFullPublishIntervalMs() / 1000 * 3;
Expand All @@ -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)
Expand All @@ -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<JsonObject>();
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
);
}
80 changes: 80 additions & 0 deletions src/MqttHassPublisher.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
Loading