Feature: Add DTU to Home Assistant Auto Discovery
This is based on PR 1365 from @CFenner with several fixes and optimizations
This commit is contained in:
parent
e0cc1559d0
commit
84248ec9b6
@ -58,11 +58,20 @@ public:
|
|||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publish(const String& subtopic, const String& payload);
|
void publish(const String& subtopic, const String& payload);
|
||||||
void publishField(std::shared_ptr<InverterAbstract> 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<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
||||||
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
|
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
|
||||||
void publishInverterNumber(std::shared_ptr<InverterAbstract> 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 publishInverterNumber(std::shared_ptr<InverterAbstract> 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<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
|
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
|
||||||
void createDeviceInfo(JsonObject& object, std::shared_ptr<InverterAbstract> inv);
|
|
||||||
|
static void createInverterInfo(DynamicJsonDocument& doc, std::shared_ptr<InverterAbstract> 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;
|
Task _loopTask;
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "MqttHandleInverter.h"
|
#include "MqttHandleInverter.h"
|
||||||
#include "MqttSettings.h"
|
#include "MqttSettings.h"
|
||||||
#include "NetworkSettings.h"
|
#include "NetworkSettings.h"
|
||||||
|
#include "Utils.h"
|
||||||
#include "defaults.h"
|
#include "defaults.h"
|
||||||
|
|
||||||
MqttHandleHassClass MqttHandleHass;
|
MqttHandleHassClass MqttHandleHass;
|
||||||
@ -52,6 +53,14 @@ void MqttHandleHassClass::publishConfig()
|
|||||||
|
|
||||||
const CONFIG_T& config = Configuration.get();
|
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
|
// Loop all inverters
|
||||||
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||||
auto inv = Hoymiles.getInverterByPos(i);
|
auto inv = Hoymiles.getInverterByPos(i);
|
||||||
@ -77,7 +86,7 @@ void MqttHandleHassClass::publishConfig()
|
|||||||
if (t == TYPE_DC && !config.Mqtt.Hass.IndividualPanels) {
|
if (t == TYPE_DC && !config.Mqtt.Hass.IndividualPanels) {
|
||||||
clear = true;
|
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<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear)
|
void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract> 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)) {
|
if (!inv->Statistics()->hasChannelFieldValue(type, channel, fieldType.fieldId)) {
|
||||||
return;
|
return;
|
||||||
@ -135,8 +144,7 @@ void MqttHandleHassClass::publishField(std::shared_ptr<InverterAbstract> inv, co
|
|||||||
root["unit_of_meas"] = unit_of_measure;
|
root["unit_of_meas"] = unit_of_measure;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject deviceObj = root.createNestedObject("dev");
|
createInverterInfo(root, inv);
|
||||||
createDeviceInfo(deviceObj, inv);
|
|
||||||
|
|
||||||
if (Configuration.get().Mqtt.Hass.Expire) {
|
if (Configuration.get().Mqtt.Hass.Expire) {
|
||||||
root["exp_aft"] = Hoymiles.getNumInverters() * max<uint32_t>(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold();
|
root["exp_aft"] = Hoymiles.getNumInverters() * max<uint32_t>(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold();
|
||||||
@ -183,8 +191,7 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
|
|||||||
root["cmd_t"] = cmdTopic;
|
root["cmd_t"] = cmdTopic;
|
||||||
root["payload_press"] = payload;
|
root["payload_press"] = payload;
|
||||||
|
|
||||||
JsonObject deviceObj = root.createNestedObject("dev");
|
createInverterInfo(root, inv);
|
||||||
createDeviceInfo(deviceObj, inv);
|
|
||||||
|
|
||||||
String buffer;
|
String buffer;
|
||||||
serializeJson(root, buffer);
|
serializeJson(root, buffer);
|
||||||
@ -222,8 +229,7 @@ void MqttHandleHassClass::publishInverterNumber(
|
|||||||
root["min"] = min;
|
root["min"] = min;
|
||||||
root["max"] = max;
|
root["max"] = max;
|
||||||
|
|
||||||
JsonObject deviceObj = root.createNestedObject("dev");
|
createInverterInfo(root, inv);
|
||||||
createDeviceInfo(deviceObj, inv);
|
|
||||||
|
|
||||||
String buffer;
|
String buffer;
|
||||||
serializeJson(root, buffer);
|
serializeJson(root, buffer);
|
||||||
@ -251,22 +257,140 @@ void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAb
|
|||||||
root["pl_on"] = payload_on;
|
root["pl_on"] = payload_on;
|
||||||
root["pl_off"] = payload_off;
|
root["pl_off"] = payload_off;
|
||||||
|
|
||||||
JsonObject deviceObj = root.createNestedObject("dev");
|
createInverterInfo(root, inv);
|
||||||
createDeviceInfo(deviceObj, inv);
|
|
||||||
|
|
||||||
String buffer;
|
String buffer;
|
||||||
serializeJson(root, buffer);
|
serializeJson(root, buffer);
|
||||||
publish(configTopic, buffer);
|
publish(configTopic, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::createDeviceInfo(JsonObject& object, std::shared_ptr<InverterAbstract> inv)
|
void MqttHandleHassClass::publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic)
|
||||||
{
|
{
|
||||||
object["name"] = inv->name();
|
String id = name;
|
||||||
object["ids"] = inv->serialString();
|
id.toLowerCase();
|
||||||
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
|
id.replace(" ", "_");
|
||||||
object["mf"] = "OpenDTU";
|
String topic = subTopic;
|
||||||
object["mdl"] = inv->typeName();
|
if (topic == "") {
|
||||||
object["sw"] = AUTO_GIT_HASH;
|
topic = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument root(1024);
|
||||||
|
root["name"] = name;
|
||||||
|
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
||||||
|
if (strcmp(device_class, "")) {
|
||||||
|
root["dev_cla"] = device_class;
|
||||||
|
}
|
||||||
|
if (strcmp(category, "")) {
|
||||||
|
root["ent_cat"] = category;
|
||||||
|
}
|
||||||
|
if (strcmp(icon, "")) {
|
||||||
|
root["ic"] = icon;
|
||||||
|
}
|
||||||
|
if (strcmp(unit_of_measure, "")) {
|
||||||
|
root["unit_of_meas"] = unit_of_measure;
|
||||||
|
}
|
||||||
|
root["stat_t"] = MqttSettings.getPrefix() + "dtu" + "/" + topic;
|
||||||
|
|
||||||
|
root["avty_t"] = MqttSettings.getPrefix() + Configuration.get().Mqtt.Lwt.Topic;
|
||||||
|
|
||||||
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
root["pl_avail"] = config.Mqtt.Lwt.Value_Online;
|
||||||
|
root["pl_not_avail"] = config.Mqtt.Lwt.Value_Offline;
|
||||||
|
|
||||||
|
createDtuInfo(root);
|
||||||
|
|
||||||
|
String buffer;
|
||||||
|
const String configTopic = "sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
||||||
|
serializeJson(root, buffer);
|
||||||
|
publish(configTopic, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic)
|
||||||
|
{
|
||||||
|
String id = name;
|
||||||
|
id.toLowerCase();
|
||||||
|
id.replace(" ", "_");
|
||||||
|
|
||||||
|
String topic = subTopic;
|
||||||
|
if (!strcmp(subTopic, "")) {
|
||||||
|
topic = String("dtu/") + "/" + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicJsonDocument root(1024);
|
||||||
|
root["name"] = name;
|
||||||
|
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
||||||
|
root["stat_t"] = MqttSettings.getPrefix() + topic;
|
||||||
|
root["pl_on"] = payload_on;
|
||||||
|
root["pl_off"] = payload_off;
|
||||||
|
|
||||||
|
if (strcmp(device_class, "")) {
|
||||||
|
root["dev_cla"] = device_class;
|
||||||
|
}
|
||||||
|
if (strcmp(category, "")) {
|
||||||
|
root["ent_cat"] = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
createDtuInfo(root);
|
||||||
|
|
||||||
|
String buffer;
|
||||||
|
const String configTopic = "binary_sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
||||||
|
serializeJson(root, buffer);
|
||||||
|
publish(configTopic, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::createInverterInfo(DynamicJsonDocument& root, std::shared_ptr<InverterAbstract> 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()
|
||||||
|
{
|
||||||
|
return String("http://") + NetworkSettings.localIP().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::publish(const String& subtopic, const String& payload)
|
void MqttHandleHassClass::publish(const String& subtopic, const String& payload)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user