Skip to content

Commit

Permalink
BREAKING CHANGE: allow multiple OpenDTU-OnBattery instances at same HASS
Browse files Browse the repository at this point in the history
This breaks existing HASS automation, as entity names and MQTT topics change!

* include dtu-unique-id in DPL MQTT HASS topics to allow multiple DTUs to be controlled
* fix filename "src/MqttHandlVedirectHass.cpp"
* refactor: use values from 'MqttHandleHass', add 'via_device' to all HASS devices
* set step size for power limiter voltage values
  • Loading branch information
AndreasBoehm authored Jul 23, 2024
1 parent 6ee6eaf commit e3f9da7
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 26 deletions.
6 changes: 3 additions & 3 deletions include/MqttHandleHass.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class MqttHandleHassClass {
void publishConfig();
void forceUpdate();

static String getDtuUniqueId();
static String getDtuUrl();

private:
void loop();
void publish(const String& subtopic, const String& payload);
Expand All @@ -71,9 +74,6 @@ class MqttHandleHassClass {

static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");

static String getDtuUniqueId();
static String getDtuUrl();

Task _loopTask;

bool _wasConnected = false;
Expand Down
4 changes: 2 additions & 2 deletions include/MqttHandlePowerLimiterHass.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ class MqttHandlePowerLimiterHassClass {
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishNumber(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min, const int16_t max);
void publishNumber(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min, const int16_t max, const float step);
void publishSelect(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic);
void createDeviceInfo(JsonObject& object);
void createDeviceInfo(JsonDocument& root);

Task _loopTask;

Expand Down
4 changes: 3 additions & 1 deletion src/MqttHandleBatteryHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "MqttHandleBatteryHass.h"
#include "Configuration.h"
#include "MqttSettings.h"
#include "MqttHandleHass.h"
#include "Utils.h"
#include "__compiled_constants.h"

Expand Down Expand Up @@ -294,10 +295,11 @@ void MqttHandleBatteryHassClass::createDeviceInfo(JsonObject& object)
}

object["ids"] = serial;
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
object["cu"] = MqttHandleHass.getDtuUrl();
object["mf"] = "OpenDTU";
object["mdl"] = Battery.getStats()->getManufacturer();
object["sw"] = __COMPILED_GIT_HASH__;
object["via_device"] = MqttHandleHass.getDtuUniqueId();
}

void MqttHandleBatteryHassClass::publish(const String& subtopic, const String& payload)
Expand Down
40 changes: 21 additions & 19 deletions src/MqttHandlePowerLimiterHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Copyright (C) 2022 Thomas Basler and others
*/
#include "MqttHandlePowerLimiterHass.h"
#include "MqttHandleHass.h"
#include "Configuration.h"
#include "MqttSettings.h"
#include "NetworkSettings.h"
Expand Down Expand Up @@ -69,32 +70,32 @@ void MqttHandlePowerLimiterHassClass::publishConfig()

// as this project revolves around Hoymiles inverters, 16 - 60 V is a reasonable voltage range
publishNumber("DPL battery voltage start threshold", "mdi:battery-charging",
"config", "threshold/voltage/start", "threshold/voltage/start", "V", 16, 60);
"config", "threshold/voltage/start", "threshold/voltage/start", "V", 16, 60, 0.1);
publishNumber("DPL battery voltage stop threshold", "mdi:battery-charging",
"config", "threshold/voltage/stop", "threshold/voltage/stop", "V", 16, 60);
"config", "threshold/voltage/stop", "threshold/voltage/stop", "V", 16, 60, 0.1);

if (config.Vedirect.Enabled) {
publishNumber("DPL full solar passthrough start voltage",
"mdi:transmission-tower-import", "config",
"threshold/voltage/full_solar_passthrough_start",
"threshold/voltage/full_solar_passthrough_start", "V", 16, 60);
"threshold/voltage/full_solar_passthrough_start", "V", 16, 60, 0.1);
publishNumber("DPL full solar passthrough stop voltage",
"mdi:transmission-tower-import", "config",
"threshold/voltage/full_solar_passthrough_stop",
"threshold/voltage/full_solar_passthrough_stop", "V", 16, 60);
"threshold/voltage/full_solar_passthrough_stop", "V", 16, 60, 0.1);
}

if (config.Battery.Enabled && !config.PowerLimiter.IgnoreSoc) {
publishNumber("DPL battery SoC start threshold", "mdi:battery-charging",
"config", "threshold/soc/start", "threshold/soc/start", "%", 0, 100);
"config", "threshold/soc/start", "threshold/soc/start", "%", 0, 100, 1.0);
publishNumber("DPL battery SoC stop threshold", "mdi:battery-charging",
"config", "threshold/soc/stop", "threshold/soc/stop", "%", 0, 100);
"config", "threshold/soc/stop", "threshold/soc/stop", "%", 0, 100, 1.0);

if (config.Vedirect.Enabled) {
publishNumber("DPL full solar passthrough SoC",
"mdi:transmission-tower-import", "config",
"threshold/soc/full_solar_passthrough",
"threshold/soc/full_solar_passthrough", "%", 0, 100);
"threshold/soc/full_solar_passthrough", "%", 0, 100, 1.0);
}
}
}
Expand All @@ -108,15 +109,15 @@ void MqttHandlePowerLimiterHassClass::publishSelect(
selectId.replace(" ", "_");
selectId.toLowerCase();

const String configTopic = "select/powerlimiter/" + selectId + "/config";
const String configTopic = "select/" + MqttHandleHass.getDtuUniqueId() + "/" + selectId + "/config";

const String cmdTopic = MqttSettings.getPrefix() + "powerlimiter/cmd/" + commandTopic;
const String statTopic = MqttSettings.getPrefix() + "powerlimiter/status/" + stateTopic;

JsonDocument root;

root["name"] = caption;
root["uniq_id"] = selectId;
root["uniq_id"] = MqttHandleHass.getDtuUniqueId() + "_" + selectId;
if (strcmp(icon, "")) {
root["ic"] = icon;
}
Expand All @@ -128,8 +129,7 @@ void MqttHandlePowerLimiterHassClass::publishSelect(
options.add("1");
options.add("2");

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

if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
Expand All @@ -143,22 +143,22 @@ void MqttHandlePowerLimiterHassClass::publishSelect(
void MqttHandlePowerLimiterHassClass::publishNumber(
const char* caption, const char* icon, const char* category,
const char* commandTopic, const char* stateTopic, const char* unitOfMeasure,
const int16_t min, const int16_t max)
const int16_t min, const int16_t max, const float step)
{

String numberId = caption;
numberId.replace(" ", "_");
numberId.toLowerCase();

const String configTopic = "number/powerlimiter/" + numberId + "/config";
const String configTopic = "number/" + MqttHandleHass.getDtuUniqueId() + "/" + numberId + "/config";

const String cmdTopic = MqttSettings.getPrefix() + "powerlimiter/cmd/" + commandTopic;
const String statTopic = MqttSettings.getPrefix() + "powerlimiter/status/" + stateTopic;

JsonDocument root;

root["name"] = caption;
root["uniq_id"] = numberId;
root["uniq_id"] = MqttHandleHass.getDtuUniqueId() + "_" + numberId;
if (strcmp(icon, "")) {
root["ic"] = icon;
}
Expand All @@ -168,15 +168,15 @@ void MqttHandlePowerLimiterHassClass::publishNumber(
root["unit_of_meas"] = unitOfMeasure;
root["min"] = min;
root["max"] = max;
root["step"] = step;
root["mode"] = "box";

auto const& config = Configuration.get();
if (config.Mqtt.Hass.Expire) {
root["exp_aft"] = config.Mqtt.PublishInterval * 3;
}

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

if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
return;
Expand All @@ -187,14 +187,16 @@ void MqttHandlePowerLimiterHassClass::publishNumber(
publish(configTopic, buffer);
}

void MqttHandlePowerLimiterHassClass::createDeviceInfo(JsonObject& object)
void MqttHandlePowerLimiterHassClass::createDeviceInfo(JsonDocument& root)
{
JsonObject object = root["dev"].to<JsonObject>();
object["name"] = "Dynamic Power Limiter";
object["ids"] = "0002";
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
object["ids"] = MqttHandleHass.getDtuUniqueId() + "_DPL";
object["cu"] = MqttHandleHass.getDtuUrl();
object["mf"] = "OpenDTU";
object["mdl"] = "Dynamic Power Limiter";
object["sw"] = __COMPILED_GIT_HASH__;
object["via_device"] = MqttHandleHass.getDtuUniqueId();
}

void MqttHandlePowerLimiterHassClass::publish(const String& subtopic, const String& payload)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "MqttHandleVedirectHass.h"
#include "Configuration.h"
#include "MqttSettings.h"
#include "MqttHandleHass.h"
#include "NetworkSettings.h"
#include "MessageOutput.h"
#include "VictronMppt.h"
Expand Down Expand Up @@ -212,10 +213,11 @@ void MqttHandleVedirectHassClass::createDeviceInfo(JsonObject &object,
String serial = mpptData.serialNr_SER;
object["name"] = "Victron(" + serial + ")";
object["ids"] = serial;
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
object["cu"] = MqttHandleHass.getDtuUrl();
object["mf"] = "OpenDTU";
object["mdl"] = mpptData.getPidAsString();
object["sw"] = __COMPILED_GIT_HASH__;
object["via_device"] = MqttHandleHass.getDtuUniqueId();
}

void MqttHandleVedirectHassClass::publish(const String& subtopic, const String& payload)
Expand Down

0 comments on commit e3f9da7

Please sign in to comment.