From 84248ec9b65e18a224a77d02d273bc0fb73548f0 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sun, 17 Dec 2023 14:28:07 +0100 Subject: [PATCH] Feature: Add DTU to Home Assistant Auto Discovery This is based on PR 1365 from @CFenner with several fixes and optimizations --- include/MqttHandleHass.h | 13 +++- src/MqttHandleHass.cpp | 158 ++++++++++++++++++++++++++++++++++----- 2 files changed, 152 insertions(+), 19 deletions(-) diff --git a/include/MqttHandleHass.h b/include/MqttHandleHass.h index 02ff8bd9a..41f7bf8c3 100644 --- a/include/MqttHandleHass.h +++ b/include/MqttHandleHass.h @@ -58,11 +58,20 @@ class MqttHandleHassClass { private: void loop(); void publish(const String& subtopic, const String& payload); - void publishField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false); + void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic); + void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = ""); + void publishInverterField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false); void publishInverterButton(std::shared_ptr inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload); void publishInverterNumber(std::shared_ptr inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100); void publishInverterBinarySensor(std::shared_ptr inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off); - void createDeviceInfo(JsonObject& object, std::shared_ptr inv); + + static void createInverterInfo(DynamicJsonDocument& doc, std::shared_ptr inv); + static void createDtuInfo(DynamicJsonDocument& doc); + + static void createDeviceInfo(DynamicJsonDocument& 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; diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index cb10230bc..88553e15e 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -6,6 +6,7 @@ #include "MqttHandleInverter.h" #include "MqttSettings.h" #include "NetworkSettings.h" +#include "Utils.h" #include "defaults.h" MqttHandleHassClass MqttHandleHass; @@ -52,6 +53,14 @@ void MqttHandleHassClass::publishConfig() const CONFIG_T& config = Configuration.get(); + // publish DTU sensors + publishDtuSensor("IP", "", "diagnostic", "mdi:network-outline", "", ""); + publishDtuSensor("WiFi Signal", "signal_strength", "diagnostic", "", "dBm", "rssi"); + publishDtuSensor("Uptime", "duration", "diagnostic", "", "s", ""); + publishDtuBinarySensor("Status", "connectivity", "diagnostic", config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, config.Mqtt.Lwt.Topic); + + yield(); + // Loop all inverters for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { auto inv = Hoymiles.getInverterByPos(i); @@ -77,7 +86,7 @@ void MqttHandleHassClass::publishConfig() if (t == TYPE_DC && !config.Mqtt.Hass.IndividualPanels) { clear = true; } - publishField(inv, t, c, deviceFieldAssignment[f], clear); + publishInverterField(inv, t, c, deviceFieldAssignment[f], clear); } } } @@ -86,7 +95,7 @@ void MqttHandleHassClass::publishConfig() } } -void MqttHandleHassClass::publishField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear) +void MqttHandleHassClass::publishInverterField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear) { if (!inv->Statistics()->hasChannelFieldValue(type, channel, fieldType.fieldId)) { return; @@ -135,8 +144,7 @@ void MqttHandleHassClass::publishField(std::shared_ptr inv, co root["unit_of_meas"] = unit_of_measure; } - JsonObject deviceObj = root.createNestedObject("dev"); - createDeviceInfo(deviceObj, inv); + createInverterInfo(root, inv); if (Configuration.get().Mqtt.Hass.Expire) { root["exp_aft"] = Hoymiles.getNumInverters() * max(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold(); @@ -183,8 +191,7 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr inv) +void MqttHandleHassClass::createInverterInfo(DynamicJsonDocument& root, std::shared_ptr inv) +{ + createDeviceInfo( + root, + inv->name(), + inv->serialString(), + getDtuUrl(), + "OpenDTU", + inv->typeName(), + AUTO_GIT_HASH, + getDtuUniqueId()); +} + +void MqttHandleHassClass::createDtuInfo(DynamicJsonDocument& root) +{ + createDeviceInfo( + root, + NetworkSettings.getHostname(), + getDtuUniqueId(), + getDtuUrl(), + "OpenDTU", + "OpenDTU", + AUTO_GIT_HASH); +} + +void MqttHandleHassClass::createDeviceInfo( + DynamicJsonDocument& root, + const String& name, const String& identifiers, const String& configuration_url, + const String& manufacturer, const String& model, const String& sw_version, + const String& via_device) +{ + auto object = root.createNestedObject("dev"); + + object["name"] = name; + object["ids"] = identifiers; + object["cu"] = configuration_url; + object["mf"] = manufacturer; + object["mdl"] = model; + object["sw"] = sw_version; + + if (via_device != "") { + object["via_device"] = via_device; + } +} + +String MqttHandleHassClass::getDtuUniqueId() +{ + return NetworkSettings.getHostname() + "_" + Utils::getChipId(); +} + +String MqttHandleHassClass::getDtuUrl() { - object["name"] = inv->name(); - object["ids"] = inv->serialString(); - object["cu"] = String("http://") + NetworkSettings.localIP().toString(); - object["mf"] = "OpenDTU"; - object["mdl"] = inv->typeName(); - object["sw"] = AUTO_GIT_HASH; + return String("http://") + NetworkSettings.localIP().toString(); } void MqttHandleHassClass::publish(const String& subtopic, const String& payload)