Merge pull request #1234 from jomjol/add-homeassistant-discovery2

Add homeassistant discovery
This commit is contained in:
jomjol
2022-10-30 10:58:54 +01:00
committed by GitHub
10 changed files with 334 additions and 183 deletions

View File

@@ -110,7 +110,7 @@ void GpioPin::init()
// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt. // if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt.
if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) { if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) {
//hook isr handler for specific gpio pin //hook isr handler for specific gpio pin
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::init add isr handler for GPIO %d\r\n", _gpio); ESP_LOGD(TAG_SERVERGPIO, "GpioPin::init add isr handler for GPIO %d", _gpio);
gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio); gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio);
} }
@@ -131,7 +131,7 @@ bool GpioPin::getValue(std::string* errorText)
void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* errorText) void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* errorText)
{ {
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::setValue %d\r\n", value); ESP_LOGD(TAG_SERVERGPIO, "GpioPin::setValue %d", value);
if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_OUTPUT_PWM) && (_mode != GPIO_PIN_MODE_BUILT_IN_FLASH_LED)) { if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_OUTPUT_PWM) && (_mode != GPIO_PIN_MODE_BUILT_IN_FLASH_LED)) {
(*errorText) = "GPIO is not in output mode"; (*errorText) = "GPIO is not in output mode";
@@ -154,7 +154,7 @@ void GpioPin::publishState() {
} }
bool GpioPin::handleMQTT(std::string, char* data, int data_len) { bool GpioPin::handleMQTT(std::string, char* data, int data_len) {
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::handleMQTT data %.*s\r\n", data_len, data); ESP_LOGD(TAG_SERVERGPIO, "GpioPin::handleMQTT data %.*s", data_len, data);
std::string dataStr(data, data_len); std::string dataStr(data, data_len);
dataStr = toLower(dataStr); dataStr = toLower(dataStr);
@@ -321,7 +321,7 @@ bool GpioHandler::readConfig()
if (mainTopicMQTT.length() > 0) if (mainTopicMQTT.length() > 0)
{ {
mainTopicMQTT = mainTopicMQTT + "/GPIO"; mainTopicMQTT = mainTopicMQTT + "/GPIO";
ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n"); ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found");
} }
bool registerISR = false; bool registerISR = false;
@@ -333,9 +333,9 @@ bool GpioHandler::readConfig()
// if (std::regex_match(zerlegt[0], pieces_match, pieces_regex) && (pieces_match.size() == 2)) // if (std::regex_match(zerlegt[0], pieces_match, pieces_regex) && (pieces_match.size() == 2))
// { // {
// std::string gpioStr = pieces_match[1]; // std::string gpioStr = pieces_match[1];
ESP_LOGD(TAG_SERVERGPIO, "conf param %s\r\n", toUpper(zerlegt[0]).c_str()); ESP_LOGD(TAG_SERVERGPIO, "conf param %s", toUpper(zerlegt[0]).c_str());
if (toUpper(zerlegt[0]) == "MAINTOPICMQTT") { if (toUpper(zerlegt[0]) == "MAINTOPICMQTT") {
// ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n"); // ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found");
// mainTopicMQTT = zerlegt[1]; // mainTopicMQTT = zerlegt[1];
} else if ((zerlegt[0].rfind("IO", 0) == 0) && (zerlegt.size() >= 6)) } else if ((zerlegt[0].rfind("IO", 0) == 0) && (zerlegt.size() >= 6))
{ {
@@ -420,7 +420,7 @@ bool GpioHandler::readConfig()
void GpioHandler::clear() void GpioHandler::clear()
{ {
ESP_LOGD(TAG_SERVERGPIO, "GpioHandler::clear\r\n"); ESP_LOGD(TAG_SERVERGPIO, "GpioHandler::clear");
if (gpioMap != NULL) { if (gpioMap != NULL) {
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
@@ -541,7 +541,7 @@ esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req)
void GpioHandler::flashLightEnable(bool value) void GpioHandler::flashLightEnable(bool value)
{ {
ESP_LOGD(TAG_SERVERGPIO, "GpioHandler::flashLightEnable %s\r\n", value ? "true" : "false"); ESP_LOGD(TAG_SERVERGPIO, "GpioHandler::flashLightEnable %s", value ? "true" : "false");
if (gpioMap != NULL) { if (gpioMap != NULL) {
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it)
@@ -552,9 +552,9 @@ void GpioHandler::flashLightEnable(bool value)
it->second->setValue(value, GPIO_SET_SOURCE_INTERNAL, &resp_str); it->second->setValue(value, GPIO_SET_SOURCE_INTERNAL, &resp_str);
if (resp_str == "") { if (resp_str == "") {
ESP_LOGD(TAG_SERVERGPIO, "Flash light pin GPIO %d switched to %s\r\n", (int)it->first, (value ? "on" : "off")); ESP_LOGD(TAG_SERVERGPIO, "Flash light pin GPIO %d switched to %s", (int)it->first, (value ? "on" : "off"));
} else { } else {
ESP_LOGE(TAG_SERVERGPIO, "Can't set flash light pin GPIO %d. Error: %s\r\n", (int)it->first, resp_str.c_str()); ESP_LOGE(TAG_SERVERGPIO, "Can't set flash light pin GPIO %d. Error: %s", (int)it->first, resp_str.c_str());
} }
} else } else
{ {

View File

@@ -131,7 +131,6 @@ string ClassFlowControll::GetMQTTMainTopic()
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0)
return ((ClassFlowMQTT*) (FlowControll[i]))->GetMQTTMainTopic(); return ((ClassFlowMQTT*) (FlowControll[i]))->GetMQTTMainTopic();
return ""; return "";
} }
@@ -148,7 +147,6 @@ void ClassFlowControll::SetInitialParameter(void)
disabled = false; disabled = false;
aktRunNr = 0; aktRunNr = 0;
aktstatus = "Booting ..."; aktstatus = "Booting ...";
} }
bool ClassFlowControll::isAutoStart(long &_intervall) bool ClassFlowControll::isAutoStart(long &_intervall)
@@ -157,6 +155,11 @@ bool ClassFlowControll::isAutoStart(long &_intervall)
return AutoStart; return AutoStart;
} }
int ClassFlowControll::getAutoInterval()
{
return AutoIntervall * 60; // AutoIntervall: Minuten -> seconds
}
ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type) ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
{ {
ClassFlow* cfc = NULL; ClassFlow* cfc = NULL;

View File

@@ -63,6 +63,7 @@ public:
std::string doSingleStep(std::string _stepname, std::string _host); std::string doSingleStep(std::string _stepname, std::string _host);
bool isAutoStart(long &_intervall); bool isAutoStart(long &_intervall);
int getAutoInterval();
std::string* getActStatus(); std::string* getActStatus();

View File

@@ -2,11 +2,12 @@
#include "ClassFlowMQTT.h" #include "ClassFlowMQTT.h"
#include "Helper.h" #include "Helper.h"
#include "connect_wlan.h" #include "connect_wlan.h"
#include "ClassLogFile.h"
#include "time_sntp.h" #include "time_sntp.h"
#include "interface_mqtt.h" #include "interface_mqtt.h"
#include "ClassFlowPostProcessing.h" #include "ClassFlowPostProcessing.h"
#include "ClassLogFile.h" #include "ClassFlowControll.h"
#include <time.h> #include <time.h>
@@ -14,6 +15,156 @@
static const char *TAG = "class_flow_MQTT"; static const char *TAG = "class_flow_MQTT";
#define LWT_TOPIC "connection"
#define LWT_CONNECTED "connected"
#define LWT_DISCONNECTED "connection lost"
extern const char* libfive_git_version(void);
extern const char* libfive_git_revision(void);
extern const char* libfive_git_branch(void);
std::vector<NumberPost*>* NUMBERS;
bool HomeassistantDiscovery = false;
void sendHomeAssistantDiscoveryTopic(std::string maintopic, std::string group, std::string field,
std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass) {
std::string version = std::string(libfive_git_version());
if (version == "") {
version = std::string(libfive_git_branch()) + " (" + std::string(libfive_git_revision()) + ")";
}
std::string topic;
std::string topicFull;
std::string topicT;
std::string payload;
std::string nl = "\n";
if (group == "") {
topic = field;
topicT = field;
}
else {
topic = group + "/" + field;
topicT = group + "_" + field;
}
if (group != "") { // Prepend the group to the name
name = group + " " + name;
}
topicFull = "homeassistant/sensor/" + maintopic + "/" + topicT + "/config";
/* See https://www.home-assistant.io/docs/mqtt/discovery/ */
payload = "{" + nl +
"\"~\": \"" + maintopic + "\"," + nl +
"\"unique_id\": \"" + maintopic + "-" + topicT + "\"," + nl +
"\"object_id\": \"" + maintopic + "_" + topicT + "\"," + nl + // This used to generate the Entity ID
"\"name\": \"" + name + "\"," + nl +
"\"icon\": \"mdi:" + icon + "\"," + nl;
if (group != "") {
if (field == "problem") { // Special binary sensor which is based on error topic
payload += "\"state_topic\": \"~/" + group + "/error\"," + nl;
payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\"," + nl;
}
else {
payload += "\"state_topic\": \"~/" + group + "/" + field + "\"," + nl;
}
}
else {
payload += "\"state_topic\": \"~/" + field + "\"," + nl;
}
if (unit != "") {
payload += "\"unit_of_meas\": \"" + unit + "\"," + nl;
}
if (deviceClass != "") {
payload += "\"device_class\": \"" + deviceClass + "\"," + nl;
/* if (deviceClass == "problem") {
payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\"," + nl;
}*/
}
if (stateClass != "") {
payload += "\"state_class\": \"" + stateClass + "\"," + nl;
}
payload +=
"\"availability_topic\": \"~/" + std::string(LWT_TOPIC) + "\"," + nl +
"\"payload_available\": \"" + LWT_CONNECTED + "\"," + nl +
"\"payload_not_available\": \"" + LWT_DISCONNECTED + "\"," + nl;
payload +=
"\"device\": {" + nl +
"\"identifiers\": [\"" + maintopic + "\"]," + nl +
"\"name\": \"" + maintopic + "\"," + nl +
"\"model\": \"Meter Digitizer\"," + nl +
"\"manufacturer\": \"AI on the Edge Device\"," + nl +
"\"sw_version\": \"" + version + "\"," + nl +
"\"configuration_url\": \"http://" + *getIPAddress() + "\"" + nl +
"}" + nl +
"}" + nl;
MQTTPublish(topicFull, payload, true);
}
void MQTThomeassistantDiscovery(std::string maintopic) {
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - Sending Homeassistant Discovery Topics...");
// maintopic group field User Friendly Name icon unit Device Class State Class
sendHomeAssistantDiscoveryTopic(maintopic, "", "uptime", "Uptime", "clock-time-eight-outline", "s", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, "", "IP", "IP", "network-outline", "", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, "", "MAC", "MAC Address", "network-outline", "", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, "", "hostname", "Hostname", "network-outline", "", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, "", "freeMem", "Free Memory", "memory", "B", "", "measurement");
sendHomeAssistantDiscoveryTopic(maintopic, "", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "");
sendHomeAssistantDiscoveryTopic(maintopic, "", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement");
for (int i = 0; i < (*NUMBERS).size(); ++i) {
// maintopic group field User Friendly Name icon unit Device Class State Class
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "value", "Value", "gauge", "", "", "total_increasing");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "error", "Error", "alert-circle-outline", "", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "rate", "Rate", "swap-vertical", "", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "changeabsolut", "Absolute Change", "arrow-expand-vertical", "", "", "measurement");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "raw", "Raw Value", "raw", "", "", "total_increasing");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "json", "JSON", "code-json", "", "", "");
sendHomeAssistantDiscoveryTopic(maintopic, (*NUMBERS)[i]->name, "problem", "Problem", "alert-outline", "", "", ""); // Special binary sensor which is based on error topic
}
}
void publishRuntimeData(std::string maintopic, int SetRetainFlag) {
char tmp_char[50];
sprintf(tmp_char, "%ld", (long)getUpTime());
MQTTPublish(maintopic + "/" + "uptime", std::string(tmp_char), SetRetainFlag);
sprintf(tmp_char, "%zu", esp_get_free_heap_size());
MQTTPublish(maintopic + "/" + "freeMem", std::string(tmp_char), SetRetainFlag);
sprintf(tmp_char, "%d", get_WIFI_RSSI());
MQTTPublish(maintopic + "/" + "wifiRSSI", std::string(tmp_char), SetRetainFlag);
sprintf(tmp_char, "%d", (int)temperatureRead());
MQTTPublish(maintopic + "/" + "CPUtemp", std::string(tmp_char), SetRetainFlag);
}
void GotConnected(std::string maintopic, int SetRetainFlag) {
if (HomeassistantDiscovery) {
MQTThomeassistantDiscovery(maintopic);
}
MQTTPublish(maintopic + "/" + "MAC", getMac(), SetRetainFlag);
MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), SetRetainFlag);
MQTTPublish(maintopic + "/" + "hostname", hostname, SetRetainFlag);
publishRuntimeData(maintopic, SetRetainFlag);
}
void ClassFlowMQTT::SetInitialParameter(void) void ClassFlowMQTT::SetInitialParameter(void)
{ {
uri = ""; uri = "";
@@ -21,22 +172,22 @@ void ClassFlowMQTT::SetInitialParameter(void)
topicError = ""; topicError = "";
topicRate = ""; topicRate = "";
topicTimeStamp = ""; topicTimeStamp = "";
maintopic = ""; maintopic = hostname;
mainerrortopic = "";
topicUptime = ""; topicUptime = "";
topicFreeMem = ""; topicFreeMem = "";
clientname = "watermeter";
clientname = "AIOTED-" + getMac();
OldValue = ""; OldValue = "";
flowpostprocessing = NULL; flowpostprocessing = NULL;
user = ""; user = "";
password = ""; password = "";
SetRetainFlag = 0; SetRetainFlag = 0;
previousElement = NULL; previousElement = NULL;
ListFlowControll = NULL; ListFlowControll = NULL;
disabled = false; disabled = false;
MQTTenable = false; keepAlive = 25*60;
keepAlive = 600; // TODO This must be greater than the Flow Interval!
} }
ClassFlowMQTT::ClassFlowMQTT() ClassFlowMQTT::ClassFlowMQTT()
@@ -51,11 +202,23 @@ ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc)
ListFlowControll = lfc; ListFlowControll = lfc;
for (int i = 0; i < ListFlowControll->size(); ++i) for (int i = 0; i < ListFlowControll->size(); ++i)
{ {
// ESP_LOGW(TAG, "LCF: %s", ((*ListFlowControll)[i])->name().c_str());
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
{ {
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
} }
// TODO this does not work since ClassFlowControll is not in the list!
/* if (((*ListFlowControll)[i])->name().compare("ClassFlowControll") == 0)
{
ClassFlowControll *cfc = (ClassFlowControll*) (*ListFlowControll)[i];
this->keepAlive = cfc->getAutoInterval()* 2.5; // Allow at least than 2 failed rounds before we are threated as disconnected
ESP_LOGW(TAG, "KEEPALIVE: %d", this->keepAlive);
}*/
} }
NUMBERS = flowpostprocessing->GetNumbers();
} }
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev) ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
@@ -108,7 +271,11 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
if (toUpper(zerlegt[1]) == "TRUE") if (toUpper(zerlegt[1]) == "TRUE")
SetRetainFlag = 1; SetRetainFlag = 1;
} }
if ((toUpper(zerlegt[0]) == "HOMEASSISTANTDISCOVERY") && (zerlegt.size() > 1))
{
if (toUpper(zerlegt[1]) == "TRUE")
HomeassistantDiscovery = true;
}
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1)) if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
{ {
@@ -121,65 +288,13 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
} }
} }
#ifdef __HIDE_PASSWORD MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED, LWT_DISCONNECTED, keepAlive, SetRetainFlag, (void *)&GotConnected);
ESP_LOGD(TAG, "Init Read with uri: %s, clientname: %s, user: %s, password: XXXXXX, maintopic: %s", uri.c_str(), clientname.c_str(), user.c_str(), mainerrortopic.c_str());
#else
ESP_LOGD(TAG, "Init Read with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
#endif
if (!MQTTisConnected() && (uri.length() > 0) && (maintopic.length() > 0)) if (!MQTT_Init()) {
{ if (!MQTT_Init()) { // Retry
ESP_LOGD(TAG, "InitMQTTInit"); return false;
mainerrortopic = maintopic + "/connection";
#ifdef __HIDE_PASSWORD
ESP_LOGD(TAG, "Init MQTT with uri: %s, clientname: %s, user: %s, password: XXXXXXXX, maintopic: %s", uri.c_str(), clientname.c_str(), user.c_str(), mainerrortopic.c_str());
#else
ESP_LOGD(TAG, "Init MQTT with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
#endif
if (!MQTTInit(uri, clientname, user, password, mainerrortopic, keepAlive))
{ // Failed
MQTTenable = false;
return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
} }
} }
// Try sending mainerrortopic. If it fails, re-run init
if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
{ // Failed
LogFile.WriteToFile(ESP_LOG_WARN, "MQTT - Re-running init...!");
if (!MQTTInit(this->uri, this->clientname, this->user, this->password, this->mainerrortopic, keepAlive))
{ // Failed
MQTTenable = false;
return false;
}
}
// Try again and quit if it fails
if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
{ // Failed
MQTTenable = false;
return false;
}
/* if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
{ // Failed
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not publish connection status!");
MQTTenable = false;
return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
}*/
/* if(!MQTTPublish(_LWTContext, "", 1))
{
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not publish LWT!");
MQTTenable = false;
return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
}*/
MQTTenable = true;
return true; return true;
} }
@@ -192,24 +307,6 @@ string ClassFlowMQTT::GetMQTTMainTopic()
bool ClassFlowMQTT::doFlow(string zwtime) bool ClassFlowMQTT::doFlow(string zwtime)
{ {
// Try sending mainerrortopic. If it fails, re-run init
if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
{ // Failed
LogFile.WriteToFile(ESP_LOG_WARN, "MQTT - Re-running init...!");
if (!MQTTInit(this->uri, this->clientname, this->user, this->password, this->mainerrortopic, keepAlive))
{ // Failed
MQTTenable = false;
return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
}
}
// Try again and quit if it fails
if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag))
{ // Failed
MQTTenable = false;
return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
}
std::string result; std::string result;
std::string resulterror = ""; std::string resulterror = "";
std::string resultraw = ""; std::string resultraw = "";
@@ -219,32 +316,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
string zw = ""; string zw = "";
string namenumber = ""; string namenumber = "";
// if (!MQTTPublish(mainerrortopic, "connected", SetRetainFlag)) publishRuntimeData(maintopic, SetRetainFlag);
//{ // Failed, skip other topics
// return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
//}
zw = maintopic + "/" + "uptime";
char uptimeStr[11];
sprintf(uptimeStr, "%ld", (long)getUpTime());
MQTTPublish(zw, uptimeStr, SetRetainFlag);
zw = maintopic + "/" + "freeMem";
char freeheapmem[11];
sprintf(freeheapmem, "%zu", esp_get_free_heap_size());
if (!MQTTPublish(zw, freeheapmem, SetRetainFlag))
{ // Failed, skip other topics
return true; // We need to return true despite we failed, else it will retry 5x and then reboot!
}
zw = maintopic + "/" + "wifiRSSI";
char rssi[11];
sprintf(rssi, "%d", get_WIFI_RSSI());
MQTTPublish(zw, rssi, SetRetainFlag);
zw = maintopic + "/" + "CPUtemp";
std::string cputemp = std::to_string(temperatureRead());
MQTTPublish(zw, cputemp, SetRetainFlag);
if (flowpostprocessing) if (flowpostprocessing)
{ {
@@ -265,29 +337,23 @@ bool ClassFlowMQTT::doFlow(string zwtime)
else else
namenumber = maintopic + "/" + namenumber + "/"; namenumber = maintopic + "/" + namenumber + "/";
zw = namenumber + "value";
if (result.length() > 0) if (result.length() > 0)
MQTTPublish(zw, result, SetRetainFlag); MQTTPublish(namenumber + "value", result, SetRetainFlag);
zw = namenumber + "error";
if (resulterror.length() > 0) if (resulterror.length() > 0)
MQTTPublish(zw, resulterror, SetRetainFlag); MQTTPublish(namenumber + "error", resulterror, SetRetainFlag);
zw = namenumber + "rate";
if (resultrate.length() > 0) if (resultrate.length() > 0)
MQTTPublish(zw, resultrate, SetRetainFlag); MQTTPublish(namenumber + "rate", resultrate, SetRetainFlag);
zw = namenumber + "changeabsolut";
if (resultchangabs.length() > 0) if (resultchangabs.length() > 0)
MQTTPublish(zw, resultchangabs, SetRetainFlag); MQTTPublish(namenumber + "changeabsolut", resultchangabs, SetRetainFlag);
zw = namenumber + "raw";
if (resultraw.length() > 0) if (resultraw.length() > 0)
MQTTPublish(zw, resultraw, SetRetainFlag); MQTTPublish(namenumber + "raw", resultraw, SetRetainFlag);
zw = namenumber + "timestamp";
if (resulttimestamp.length() > 0) if (resulttimestamp.length() > 0)
MQTTPublish(zw, resulttimestamp, SetRetainFlag); MQTTPublish(namenumber + "timestamp", resulttimestamp, SetRetainFlag);
std::string json = ""; std::string json = "";
@@ -305,8 +371,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
json += "\",\"rate\":\"\""; json += "\",\"rate\":\"\"";
json += ",\"timestamp\":\""+resulttimestamp+"\"}"; json += ",\"timestamp\":\""+resulttimestamp+"\"}";
zw = namenumber + "json"; MQTTPublish(namenumber + "json", json, SetRetainFlag);
MQTTPublish(zw, json, SetRetainFlag);
} }
} }
else else

View File

@@ -14,10 +14,9 @@ protected:
ClassFlowPostProcessing* flowpostprocessing; ClassFlowPostProcessing* flowpostprocessing;
std::string user, password; std::string user, password;
int SetRetainFlag; int SetRetainFlag;
bool MQTTenable;
int keepAlive; int keepAlive;
std::string maintopic, mainerrortopic; std::string maintopic;
void SetInitialParameter(void); void SetInitialParameter(void);
public: public:

View File

@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <algorithm>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -206,6 +207,8 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string info, bool _ti
strftime(buffer, 30, logfile.c_str(), timeinfo); strftime(buffer, 30, logfile.c_str(), timeinfo);
std::string logpath = logroot + "/" + buffer; std::string logpath = logroot + "/" + buffer;
std::replace(info.begin(), info.end(), '\n', ' '); // Replace all newline characters
WriteToDedicatedFile(logpath, level, info, _time); WriteToDedicatedFile(logpath, level, info, _time);
ESP_LOG_LEVEL(level, TAG, "%s", info.c_str()); ESP_LOG_LEVEL(level, TAG, "%s", info.c_str());
} }

View File

@@ -12,25 +12,51 @@ static const char *TAG_INTERFACEMQTT = "interface_mqtt";
std::map<std::string, std::function<void()>>* connectFunktionMap = NULL; std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL; std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL;
// #define CONFIG_BROKER_URL "mqtt://192.168.178.43:1883"
esp_mqtt_event_id_t esp_mmqtt_ID = MQTT_EVENT_ANY; esp_mqtt_event_id_t esp_mmqtt_ID = MQTT_EVENT_ANY;
// ESP_EVENT_ANY_ID // ESP_EVENT_ANY_ID
bool mqtt_connected = false; bool mqtt_connected = false;
esp_mqtt_client_handle_t client = NULL; esp_mqtt_client_handle_t client = NULL;
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic;
int keepalive, SetRetainFlag;
void (*callbackOnConnected)(std::string, int) = NULL;
bool MQTTPublish(std::string _key, std::string _content, int retained_flag){
bool MQTTPublish(std::string _key, std::string _content, int retained_flag) {
int msg_id; int msg_id;
std::string zw; std::string zw;
if (!mqtt_connected) {
LogFile.WriteToFile(ESP_LOG_WARN, "MQTT - Not connected, trying to re-connect...");
if (!MQTT_Init()) {
if (!MQTT_Init()) { // Retry
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Failed to init!");
return false;
}
}
}
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag); msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
if (msg_id < 0) { if (msg_id < 0) {
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Failed to publish '" + _key + "'!"); LogFile.WriteToFile(ESP_LOG_WARN, "MQTT - Failed to publish topic '" + _key + "', re-trying...");
return false;
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, 1, retained_flag);
if (msg_id < 0) {
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Failed to publish topic '" + _key + "'!");
mqtt_connected = false; // Force re-init on next call
return false;
}
} }
zw = "MQTT - sent publish successful in MQTTPublish, msg_id=" + std::to_string(msg_id) + ", " + _key + ", " + _content;
LogFile.WriteToFile(ESP_LOG_INFO, zw); if (_content.length() > 80) { // Truncate message if too long
_content.resize(80);
_content.append("..");
}
zw = "MQTT - Published topic: " + _key + ", content: " + _content + " (msg_id=" + std::to_string(msg_id) + ")";
LogFile.WriteToFile(ESP_LOG_DEBUG, zw);
return true; return true;
} }
@@ -50,6 +76,7 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
break; break;
case MQTT_EVENT_DISCONNECTED: case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_DISCONNECTED"); ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_DISCONNECTED");
mqtt_connected = false; // Force re-init on next call
esp_mqtt_client_reconnect(client); esp_mqtt_client_reconnect(client);
break; break;
case MQTT_EVENT_SUBSCRIBED: case MQTT_EVENT_SUBSCRIBED:
@@ -79,6 +106,7 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
break; break;
case MQTT_EVENT_ERROR: case MQTT_EVENT_ERROR:
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_ERROR"); ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_ERROR");
mqtt_connected = false; // Force re-init on next call
break; break;
default: default:
ESP_LOGI(TAG_INTERFACEMQTT, "Other event id:%d", event->event_id); ESP_LOGI(TAG_INTERFACEMQTT, "Other event id:%d", event->event_id);
@@ -93,58 +121,81 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
} }
bool MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive){ void MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
std::string _zwmessage = "connection lost"; std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
int _keepalive, int _SetRetainFlag, void *_callbackOnConnected){
#ifdef __HIDE_PASSWORD
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT Configuration: uri: " + _mqttURI + ", clientname: " + _clientid +
", user: " + _user + ", password: XXXXXXXX, maintopic: " + _maintopic + ", last-will-topic: " + _maintopic + "/" + _lwt + ", keepAlive: " + std::to_string(_keepalive));
#else
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT Configuration: uri: " + _mqttURI + ", clientname: " + _clientid +
", user: " + _user + ", password: " + _password + ", maintopic: " + _maintopic + ", last-will-topic: " + _maintopic + "/" + _lwt + ", keepAlive: " + std::to_string(_keepalive));
#endif
int _lzw = _zwmessage.length(); uri = _mqttURI;
client_id = _clientid;
esp_mqtt_client_config_t mqtt_cfg = { lwt_topic = _maintopic + "/" + _lwt;
.uri = _mqttURI.c_str(), lwt_connected = _lwt_connected;
.client_id = _clientid.c_str(), lwt_disconnected = _lwt_disconnected;
.lwt_topic = _LWTContext.c_str(), keepalive = _keepalive;
.lwt_msg = _zwmessage.c_str(), SetRetainFlag = _SetRetainFlag;
.lwt_retain = 1, maintopic = _maintopic;
.lwt_msg_len = _lzw, callbackOnConnected = ( void (*)(std::string, int) )(_callbackOnConnected);
.keepalive = _keepalive
};
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - Init");
if (_user.length() && _password.length()){ if (_user.length() && _password.length()){
mqtt_cfg.username = _user.c_str(); user = _user;
mqtt_cfg.password = _password.c_str(); password = _password;
}
}
#ifdef __HIDE_PASSWORD bool MQTT_Init() {
ESP_LOGI(TAG_INTERFACEMQTT, "Connect to MQTT: %s, XXXXXXXX", mqtt_cfg.username); esp_err_t ret;
#else LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - Init");
ESP_LOGI(TAG_INTERFACEMQTT, "Connect to MQTT: %s, %s", mqtt_cfg.username, mqtt_cfg.password);
#endif MQTTdestroy_client();
std::string lw = lwt_disconnected;
esp_mqtt_client_config_t mqtt_cfg = {
.uri = uri.c_str(),
.client_id = client_id.c_str(),
.lwt_topic = lwt_topic.c_str(),
.lwt_msg = lw.c_str(),
.lwt_retain = 1,
.lwt_msg_len = (int)(lw.length()),
.keepalive = keepalive
};
if (user.length() && password.length()){
mqtt_cfg.username = user.c_str();
mqtt_cfg.password = password.c_str();
}; };
MQTTdestroy();
client = esp_mqtt_client_init(&mqtt_cfg); client = esp_mqtt_client_init(&mqtt_cfg);
if (client) if (client)
{ {
if (esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client) != ESP_OK) ret = esp_mqtt_client_register_event(client, esp_mmqtt_ID, mqtt_event_handler, client);
if (ret != ESP_OK)
{ {
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not register event!"); LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not register event (ret=" + std::to_string(ret) + ")!");
return false;
}
if (esp_mqtt_client_start(client) != ESP_OK)
{
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not start client!");
return false; return false;
} }
/* if(!MQTTPublish(_LWTContext, "", 1)) ret = esp_mqtt_client_start(client);
if (ret != ESP_OK)
{ {
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not publish LWT!"); LogFile.WriteToFile(ESP_LOG_WARN, "MQTT - Could not start client (ret=" + std::to_string(ret) + "), retrying...");
return false; ret = esp_mqtt_client_start(client);
}*/ if (ret != ESP_OK)
{
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not start client (ret=" + std::to_string(ret) + ")!");
return false;
}
}
} }
else else
{ {
LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not Init client!"); LogFile.WriteToFile(ESP_LOG_ERROR, "MQTT - Could not init client!");
return false; return false;
} }
@@ -153,7 +204,7 @@ bool MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, st
} }
void MQTTdestroy() { void MQTTdestroy_client() {
if (client != NULL) { if (client != NULL) {
esp_mqtt_client_stop(client); esp_mqtt_client_stop(client);
esp_mqtt_client_destroy(client); esp_mqtt_client_destroy(client);
@@ -211,6 +262,9 @@ void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::st
void MQTTconnected(){ void MQTTconnected(){
if (mqtt_connected) { if (mqtt_connected) {
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - Connected"); LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - Connected");
MQTTPublish(lwt_topic, lwt_connected, true);
if (connectFunktionMap != NULL) { if (connectFunktionMap != NULL) {
for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) { for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
it->second(); it->second();
@@ -224,6 +278,10 @@ void MQTTconnected(){
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - topic " + it->first + " subscribe successful, msg_id=" + std::to_string(msg_id)); LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - topic " + it->first + " subscribe successful, msg_id=" + std::to_string(msg_id));
} }
} }
if (callbackOnConnected) {
callbackOnConnected(maintopic, SetRetainFlag);
}
} }
} }
@@ -240,4 +298,4 @@ void MQTTdestroySubscribeFunction(){
delete subscribeFunktionMap; delete subscribeFunktionMap;
subscribeFunktionMap = NULL; subscribeFunktionMap = NULL;
} }
} }

View File

@@ -5,8 +5,11 @@
#include <map> #include <map>
#include <functional> #include <functional>
bool MQTTInit(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, std::string _LWTContext, int _keepalive); void MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
void MQTTdestroy(); std::string _maintopic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
int _keepalive, int SetRetainFlag, void *callbackOnConnected);
bool MQTT_Init();
void MQTTdestroy_client();
bool MQTTPublish(std::string _key, std::string _content, int retained_flag = 1); // retained Flag as Standart bool MQTTPublish(std::string _key, std::string _content, int retained_flag = 1); // retained Flag as Standart

View File

@@ -625,7 +625,23 @@ textarea {
Enable or disable the retain flag for all MQTT entries Enable or disable the retain flag for all MQTT entries
</td> </td>
</tr> </tr>
<tr>
<td class="indent1">
<input type="checkbox" id="MQTT_HomeassistantDiscovery_enabled" value="1" onclick = 'InvertEnableItem("MQTT", "HomeassistantDiscovery")' unchecked >
<label for=MQTT_HomeassistantDiscovery_enabled><class id="MQTT_HomeassistantDiscovery_text" style="color:black;">Enable Homeassistant Discovery</class></label>
</td>
<td>
<select id="MQTT_HomeassistantDiscovery_value1">
<option value="true" selected>true</option>
<option value="false" >false</option>
</select>
</td>
<td style="font-size: 80%;">
Enable or disable the <a href=https://www.home-assistant.io/docs/mqtt/discovery/ target=_blank>Homeassistand Discovery</a>
</td>
</tr>
<tr> <tr>
<td colspan="3" style="padding-left: 20px;"> <td colspan="3" style="padding-left: 20px;">
<h4> <h4>
@@ -1715,6 +1731,7 @@ function UpdateInput() {
WriteParameter(param, category, "MQTT", "user", true); WriteParameter(param, category, "MQTT", "user", true);
WriteParameter(param, category, "MQTT", "password", true); WriteParameter(param, category, "MQTT", "password", true);
WriteParameter(param, category, "MQTT", "SetRetainFlag", true); WriteParameter(param, category, "MQTT", "SetRetainFlag", true);
WriteParameter(param, category, "MQTT", "HomeassistantDiscovery", true);
WriteParameter(param, category, "InfluxDB", "Uri", true); WriteParameter(param, category, "InfluxDB", "Uri", true);
WriteParameter(param, category, "InfluxDB", "Database", true); WriteParameter(param, category, "InfluxDB", "Database", true);
@@ -1831,6 +1848,7 @@ function ReadParameterAll()
ReadParameter(param, "MQTT", "user", true); ReadParameter(param, "MQTT", "user", true);
ReadParameter(param, "MQTT", "password", true); ReadParameter(param, "MQTT", "password", true);
ReadParameter(param, "MQTT", "SetRetainFlag", true); ReadParameter(param, "MQTT", "SetRetainFlag", true);
ReadParameter(param, "MQTT", "HomeassistantDiscovery", true);
ReadParameter(param, "InfluxDB", "Uri", true); ReadParameter(param, "InfluxDB", "Uri", true);
ReadParameter(param, "InfluxDB", "Database", true); ReadParameter(param, "InfluxDB", "Database", true);

View File

@@ -187,6 +187,7 @@ function ParseConfig() {
ParamAddValue(param, catname, "user"); ParamAddValue(param, catname, "user");
ParamAddValue(param, catname, "password"); ParamAddValue(param, catname, "password");
ParamAddValue(param, catname, "SetRetainFlag"); ParamAddValue(param, catname, "SetRetainFlag");
ParamAddValue(param, catname, "HomeassistantDiscovery");
var catname = "InfluxDB"; var catname = "InfluxDB";
category[catname] = new Object(); category[catname] = new Object();