mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-07 20:16:55 +03:00
Enhance homeassistant discovery (V5) (#1275)
* manual re-creation of #1246 * removed non-working approach to get interval * postpone the MQTT service start until the ClassFlowControll::ReadParameter() gets called * Increase the max_uri_handlers to 35 (was 30) * removed newlines in logs * added parameter to UI * Register handler to re-start MQTT Discovery * fix param
This commit is contained in:
@@ -29,7 +29,6 @@ extern "C" {
|
|||||||
|
|
||||||
static const char* TAG = "flow_controll";
|
static const char* TAG = "flow_controll";
|
||||||
|
|
||||||
float AutoIntervalShared = 10;
|
|
||||||
|
|
||||||
|
|
||||||
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host){
|
||||||
@@ -142,7 +141,7 @@ void ClassFlowControll::SetInitialParameter(void)
|
|||||||
{
|
{
|
||||||
AutoStart = false;
|
AutoStart = false;
|
||||||
SetupModeActive = false;
|
SetupModeActive = false;
|
||||||
AutoIntervall = 10;
|
AutoIntervall = 10; // Minutes
|
||||||
flowdigit = NULL;
|
flowdigit = NULL;
|
||||||
flowanalog = NULL;
|
flowanalog = NULL;
|
||||||
flowpostprocessing = NULL;
|
flowpostprocessing = NULL;
|
||||||
@@ -517,7 +516,11 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoIntervalShared = AutoIntervall;
|
/* Start the MQTT service */
|
||||||
|
for (int i = 0; i < FlowControll.size(); ++i)
|
||||||
|
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0)
|
||||||
|
return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoIntervall);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
#include "ClassFlowMQTT.h"
|
#include "ClassFlowMQTT.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
#include "connect_wlan.h"
|
#include "connect_wlan.h"
|
||||||
@@ -9,11 +10,12 @@
|
|||||||
#include "ClassFlowPostProcessing.h"
|
#include "ClassFlowPostProcessing.h"
|
||||||
#include "ClassFlowControll.h"
|
#include "ClassFlowControll.h"
|
||||||
|
|
||||||
|
#include "server_mqtt.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#define __HIDE_PASSWORD
|
|
||||||
|
|
||||||
static const char *TAG = "class_flow_MQTT";
|
#define __HIDE_PASSWORD
|
||||||
|
|
||||||
#define LWT_TOPIC "connection"
|
#define LWT_TOPIC "connection"
|
||||||
#define LWT_CONNECTED "connected"
|
#define LWT_CONNECTED "connected"
|
||||||
@@ -23,147 +25,6 @@ extern const char* libfive_git_version(void);
|
|||||||
extern const char* libfive_git_revision(void);
|
extern const char* libfive_git_revision(void);
|
||||||
extern const char* libfive_git_branch(void);
|
extern const char* libfive_git_branch(void);
|
||||||
|
|
||||||
extern float AutoIntervalShared;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -207,12 +68,6 @@ ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc)
|
|||||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NUMBERS = flowpostprocessing->GetNumbers();
|
|
||||||
keepAlive = AutoIntervalShared * 60 * 2.5; // TODO find better way to access AutoIntervall in ClassFlowControll
|
|
||||||
|
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, "Digitizer interval is " + std::to_string(AutoIntervalShared) +
|
|
||||||
" minutes => setting MQTT LWT timeout to " + std::to_string(keepAlive/60) + " minutes.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||||
@@ -262,13 +117,46 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
}
|
}
|
||||||
if ((toUpper(zerlegt[0]) == "SETRETAINFLAG") && (zerlegt.size() > 1))
|
if ((toUpper(zerlegt[0]) == "SETRETAINFLAG") && (zerlegt.size() > 1))
|
||||||
{
|
{
|
||||||
if (toUpper(zerlegt[1]) == "TRUE")
|
if (toUpper(zerlegt[1]) == "TRUE") {
|
||||||
SetRetainFlag = 1;
|
SetRetainFlag = 1;
|
||||||
|
setMqtt_Server_Retain(SetRetainFlag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((toUpper(zerlegt[0]) == "HOMEASSISTANTDISCOVERY") && (zerlegt.size() > 1))
|
if ((toUpper(zerlegt[0]) == "HOMEASSISTANTDISCOVERY") && (zerlegt.size() > 1))
|
||||||
{
|
{
|
||||||
if (toUpper(zerlegt[1]) == "TRUE")
|
if (toUpper(zerlegt[1]) == "TRUE")
|
||||||
HomeassistantDiscovery = true;
|
SetHomeassistantDiscoveryEnabled(true);
|
||||||
|
}
|
||||||
|
if ((toUpper(zerlegt[0]) == "METERTYPE") && (zerlegt.size() > 1)) {
|
||||||
|
/* Use meter type for the device class
|
||||||
|
Make sure it is a listed one on https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes */
|
||||||
|
if (toUpper(zerlegt[1]) == "WATER_M3") {
|
||||||
|
mqttServer_setMeterType("water", "m³", "h", "m³/h");
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "WATER_L") {
|
||||||
|
mqttServer_setMeterType("water", "L", "h", "L/h");
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "WATER_FT3") {
|
||||||
|
mqttServer_setMeterType("water", "ft³", "m", "ft³/m"); // Minutes
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "WATER_GAL") {
|
||||||
|
mqttServer_setMeterType("water", "gal", "h", "gal/h");
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "GAS_M3") {
|
||||||
|
mqttServer_setMeterType("gas", "m³", "h", "m³/h");
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "GAS_FT3") {
|
||||||
|
mqttServer_setMeterType("gas", "ft³", "m", "ft³/m"); // Minutes
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "ENERGY_WH") {
|
||||||
|
mqttServer_setMeterType("energy", "Wh", "h", "W");
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "ENERGY_KWH") {
|
||||||
|
mqttServer_setMeterType("energy", "kWh", "h", "kW");
|
||||||
|
}
|
||||||
|
else if (toUpper(zerlegt[1]) == "ENERGY_MWH") {
|
||||||
|
mqttServer_setMeterType("energy", "MWh", "h", "MW");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
|
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
|
||||||
@@ -279,16 +167,15 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
|||||||
if (((toUpper(zerlegt[0]) == "TOPIC") || (toUpper(zerlegt[0]) == "MAINTOPIC")) && (zerlegt.size() > 1))
|
if (((toUpper(zerlegt[0]) == "TOPIC") || (toUpper(zerlegt[0]) == "MAINTOPIC")) && (zerlegt.size() > 1))
|
||||||
{
|
{
|
||||||
maintopic = zerlegt[1];
|
maintopic = zerlegt[1];
|
||||||
|
mqttServer_setMainTopic(maintopic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED, LWT_DISCONNECTED, keepAlive, SetRetainFlag, (void *)&GotConnected);
|
/* Note:
|
||||||
|
* Originally, we started the MQTT client here.
|
||||||
|
* How ever we need the interval parameter from the ClassFlowControll, but that only gets started later.
|
||||||
|
* To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */
|
||||||
|
|
||||||
if (!MQTT_Init()) {
|
|
||||||
if (!MQTT_Init()) { // Retry
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,18 +186,44 @@ string ClassFlowMQTT::GetMQTTMainTopic()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ClassFlowMQTT::Start(float AutoIntervall) {
|
||||||
|
|
||||||
|
roundInterval = AutoIntervall; // Minutes
|
||||||
|
keepAlive = roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds!
|
||||||
|
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << std::fixed << std::setprecision(1) << "Digitizer interval is " << roundInterval <<
|
||||||
|
" minutes => setting MQTT LWT timeout to " << ((float)keepAlive/60) << " minutes.";
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, stream.str());
|
||||||
|
|
||||||
|
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
||||||
|
|
||||||
|
MQTT_Configure(uri, clientname, user, password, maintopic, LWT_TOPIC, LWT_CONNECTED, LWT_DISCONNECTED,
|
||||||
|
keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||||
|
|
||||||
|
if (!MQTT_Init()) {
|
||||||
|
if (!MQTT_Init()) { // Retry
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
std::string resulterror = "";
|
std::string resulterror = "";
|
||||||
std::string resultraw = "";
|
std::string resultraw = "";
|
||||||
std::string resultrate = "";
|
std::string resultrate = ""; // Always Unit / Minute
|
||||||
|
std::string resultRatePerTimeUnit = ""; // According to selection
|
||||||
std::string resulttimestamp = "";
|
std::string resulttimestamp = "";
|
||||||
std::string resultchangabs = "";
|
std::string resultchangabs = "";
|
||||||
string zw = "";
|
string zw = "";
|
||||||
string namenumber = "";
|
string namenumber = "";
|
||||||
|
|
||||||
publishRuntimeData(maintopic, SetRetainFlag);
|
publishSystemData();
|
||||||
|
|
||||||
if (flowpostprocessing)
|
if (flowpostprocessing)
|
||||||
{
|
{
|
||||||
@@ -321,8 +234,8 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
result = (*NUMBERS)[i]->ReturnValue;
|
result = (*NUMBERS)[i]->ReturnValue;
|
||||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
resultrate = (*NUMBERS)[i]->ReturnRateValue; // Unit per minutes
|
||||||
resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute;
|
resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute; // Units per round
|
||||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||||
|
|
||||||
namenumber = (*NUMBERS)[i]->name;
|
namenumber = (*NUMBERS)[i]->name;
|
||||||
@@ -331,17 +244,31 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
else
|
else
|
||||||
namenumber = maintopic + "/" + namenumber + "/";
|
namenumber = maintopic + "/" + namenumber + "/";
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, "Publishing MQTT topics...");
|
||||||
|
|
||||||
if (result.length() > 0)
|
if (result.length() > 0)
|
||||||
MQTTPublish(namenumber + "value", result, SetRetainFlag);
|
MQTTPublish(namenumber + "value", result, SetRetainFlag);
|
||||||
|
|
||||||
if (resulterror.length() > 0)
|
if (resulterror.length() > 0)
|
||||||
MQTTPublish(namenumber + "error", resulterror, SetRetainFlag);
|
MQTTPublish(namenumber + "error", resulterror, SetRetainFlag);
|
||||||
|
|
||||||
if (resultrate.length() > 0)
|
if (resultrate.length() > 0) {
|
||||||
MQTTPublish(namenumber + "rate", resultrate, SetRetainFlag);
|
MQTTPublish(namenumber + "rate", resultrate, SetRetainFlag);
|
||||||
|
|
||||||
|
std::string resultRatePerTimeUnit;
|
||||||
|
if (getTimeUnit() == "h") { // Need conversion to be per hour
|
||||||
|
resultRatePerTimeUnit = resultRatePerTimeUnit = to_string((*NUMBERS)[i]->FlowRateAct / 60); // per minutes => per hour
|
||||||
|
}
|
||||||
|
else { // Keep per minute
|
||||||
|
resultRatePerTimeUnit = resultrate;
|
||||||
|
}
|
||||||
|
MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, SetRetainFlag);
|
||||||
|
}
|
||||||
|
|
||||||
if (resultchangabs.length() > 0)
|
if (resultchangabs.length() > 0) {
|
||||||
MQTTPublish(namenumber + "changeabsolut", resultchangabs, SetRetainFlag);
|
MQTTPublish(namenumber + "changeabsolut", resultchangabs, SetRetainFlag); // Legacy API
|
||||||
|
MQTTPublish(namenumber + "rate_per_digitalization_round", resultchangabs, SetRetainFlag);
|
||||||
|
}
|
||||||
|
|
||||||
if (resultraw.length() > 0)
|
if (resultraw.length() > 0)
|
||||||
MQTTPublish(namenumber + "raw", resultraw, SetRetainFlag);
|
MQTTPublish(namenumber + "raw", resultraw, SetRetainFlag);
|
||||||
@@ -349,7 +276,6 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
if (resulttimestamp.length() > 0)
|
if (resulttimestamp.length() > 0)
|
||||||
MQTTPublish(namenumber + "timestamp", resulttimestamp, SetRetainFlag);
|
MQTTPublish(namenumber + "timestamp", resulttimestamp, SetRetainFlag);
|
||||||
|
|
||||||
|
|
||||||
std::string json = "";
|
std::string json = "";
|
||||||
|
|
||||||
if (result.length() > 0)
|
if (result.length() > 0)
|
||||||
@@ -359,10 +285,12 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
|||||||
|
|
||||||
json += ",\"raw\":\""+resultraw;
|
json += ",\"raw\":\""+resultraw;
|
||||||
json += "\",\"error\":\""+resulterror;
|
json += "\",\"error\":\""+resulterror;
|
||||||
|
|
||||||
if (resultrate.length() > 0)
|
if (resultrate.length() > 0)
|
||||||
json += "\",\"rate\":"+resultrate;
|
json += "\",\"rate\":"+resultrate;
|
||||||
else
|
else
|
||||||
json += "\",\"rate\":\"\"";
|
json += "\",\"rate\":\"\"";
|
||||||
|
|
||||||
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
|
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
|
||||||
|
|
||||||
MQTTPublish(namenumber + "json", json, SetRetainFlag);
|
MQTTPublish(namenumber + "json", json, SetRetainFlag);
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ protected:
|
|||||||
ClassFlowPostProcessing* flowpostprocessing;
|
ClassFlowPostProcessing* flowpostprocessing;
|
||||||
std::string user, password;
|
std::string user, password;
|
||||||
int SetRetainFlag;
|
int SetRetainFlag;
|
||||||
int keepAlive;
|
int keepAlive; // Seconds
|
||||||
|
float roundInterval; // Minutes
|
||||||
|
|
||||||
std::string maintopic;
|
std::string maintopic;
|
||||||
void SetInitialParameter(void);
|
void SetInitialParameter(void);
|
||||||
@@ -25,6 +26,7 @@ public:
|
|||||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||||
|
|
||||||
string GetMQTTMainTopic();
|
string GetMQTTMainTopic();
|
||||||
|
bool Start(float AutoIntervall);
|
||||||
|
|
||||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||||
bool doFlow(string time);
|
bool doFlow(string time);
|
||||||
|
|||||||
@@ -2,6 +2,4 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
|||||||
|
|
||||||
idf_component_register(SRCS ${app_sources}
|
idf_component_register(SRCS ${app_sources}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
REQUIRES tflite-lib mqtt jomjol_logfile)
|
REQUIRES tflite-lib mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -67,49 +67,50 @@ static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
|
|||||||
std::string topic = "";
|
std::string topic = "";
|
||||||
switch (event->event_id) {
|
switch (event->event_id) {
|
||||||
case MQTT_EVENT_BEFORE_CONNECT:
|
case MQTT_EVENT_BEFORE_CONNECT:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_BEFORE_CONNECT");
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_BEFORE_CONNECT");
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_CONNECTED:
|
case MQTT_EVENT_CONNECTED:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_CONNECTED");
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_CONNECTED");
|
||||||
mqtt_connected = true;
|
mqtt_connected = true;
|
||||||
MQTTconnected();
|
MQTTconnected();
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_DISCONNECTED:
|
case MQTT_EVENT_DISCONNECTED:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_DISCONNECTED");
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_DISCONNECTED");
|
||||||
|
LogFile.WriteToFile(ESP_LOG_WARN, "MQTT - Disconnected, going to re-connect...");
|
||||||
mqtt_connected = false; // Force re-init on next call
|
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:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
|
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "sent publish successful, msg_id=%d", msg_id);
|
ESP_LOGD(TAG_INTERFACEMQTT, "sent publish successful, msg_id=%d", msg_id);
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_UNSUBSCRIBED:
|
case MQTT_EVENT_UNSUBSCRIBED:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_PUBLISHED:
|
case MQTT_EVENT_PUBLISHED:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_DATA:
|
case MQTT_EVENT_DATA:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_DATA");
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_DATA");
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
ESP_LOGD(TAG_INTERFACEMQTT, "TOPIC=%.*s", event->topic_len, event->topic);
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "DATA=%.*s\r\n", event->data_len, event->data);
|
ESP_LOGD(TAG_INTERFACEMQTT, "DATA=%.*s", event->data_len, event->data);
|
||||||
topic.assign(event->topic, event->topic_len);
|
topic.assign(event->topic, event->topic_len);
|
||||||
if (subscribeFunktionMap != NULL) {
|
if (subscribeFunktionMap != NULL) {
|
||||||
if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end()) {
|
if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end()) {
|
||||||
ESP_LOGD(TAG_INTERFACEMQTT, "call handler function\r\n");
|
ESP_LOGD(TAG_INTERFACEMQTT, "call handler function");
|
||||||
(*subscribeFunktionMap)[topic](topic, event->data, event->data_len);
|
(*subscribeFunktionMap)[topic](topic, event->data, event->data_len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG_INTERFACEMQTT, "no handler available\r\n");
|
ESP_LOGW(TAG_INTERFACEMQTT, "no handler available");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MQTT_EVENT_ERROR:
|
case MQTT_EVENT_ERROR:
|
||||||
ESP_LOGI(TAG_INTERFACEMQTT, "MQTT_EVENT_ERROR");
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTT_EVENT_ERROR");
|
||||||
mqtt_connected = false; // Force re-init on next call
|
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_LOGD(TAG_INTERFACEMQTT, "Other event id:%d", event->event_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -126,10 +127,10 @@ void MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _us
|
|||||||
int _keepalive, int _SetRetainFlag, void *_callbackOnConnected){
|
int _keepalive, int _SetRetainFlag, void *_callbackOnConnected){
|
||||||
#ifdef __HIDE_PASSWORD
|
#ifdef __HIDE_PASSWORD
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT Configuration: uri: " + _mqttURI + ", clientname: " + _clientid +
|
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));
|
", user: " + _user + ", password: XXXXXXXX, maintopic: " + _maintopic + ", last-will-topic: " + _maintopic + "/" + _lwt + ", keepAlive: " + std::to_string(_keepalive) + " s");
|
||||||
#else
|
#else
|
||||||
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT Configuration: uri: " + _mqttURI + ", clientname: " + _clientid +
|
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));
|
", user: " + _user + ", password: " + _password + ", maintopic: " + _maintopic + ", last-will-topic: " + _maintopic + "/" + _lwt + ", keepAlive: " + std::to_string(_keepalive)+ " s");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uri = _mqttURI;
|
uri = _mqttURI;
|
||||||
@@ -216,7 +217,7 @@ bool MQTTisConnected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MQTTregisterConnectFunction(std::string name, std::function<void()> func){
|
void MQTTregisterConnectFunction(std::string name, std::function<void()> func){
|
||||||
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisteronnectFunction %s\r\n", name.c_str());
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisteronnectFunction %s", name.c_str());
|
||||||
if (connectFunktionMap == NULL) {
|
if (connectFunktionMap == NULL) {
|
||||||
connectFunktionMap = new std::map<std::string, std::function<void()>>();
|
connectFunktionMap = new std::map<std::string, std::function<void()>>();
|
||||||
}
|
}
|
||||||
@@ -234,14 +235,14 @@ void MQTTregisterConnectFunction(std::string name, std::function<void()> func){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MQTTunregisterConnectFunction(std::string name){
|
void MQTTunregisterConnectFunction(std::string name){
|
||||||
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisteronnectFunction %s\r\n", name.c_str());
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisteronnectFunction %s", name.c_str());
|
||||||
if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end())) {
|
if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end())) {
|
||||||
connectFunktionMap->erase(name);
|
connectFunktionMap->erase(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char*, int)> func){
|
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char*, int)> func){
|
||||||
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisterSubscribeFunction %s\r\n", topic.c_str());
|
ESP_LOGD(TAG_INTERFACEMQTT, "MQTTregisterSubscribeFunction %s", topic.c_str());
|
||||||
if (subscribeFunktionMap == NULL) {
|
if (subscribeFunktionMap == NULL) {
|
||||||
subscribeFunktionMap = new std::map<std::string, std::function<bool(std::string, char*, int)>>();
|
subscribeFunktionMap = new std::map<std::string, std::function<bool(std::string, char*, int)>>();
|
||||||
}
|
}
|
||||||
|
|||||||
251
code/components/jomjol_mqtt/server_mqtt.cpp
Normal file
251
code/components/jomjol_mqtt/server_mqtt.cpp
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "ClassLogFile.h"
|
||||||
|
#include "connect_wlan.h"
|
||||||
|
#include "server_mqtt.h"
|
||||||
|
#include "interface_mqtt.h"
|
||||||
|
#include "time_sntp.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static const char *TAG = "MQTT SERVER";
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
std::string meterType = "";
|
||||||
|
std::string valueUnit = "";
|
||||||
|
std::string timeUnit = "";
|
||||||
|
std::string rateUnit = "Unit/Minute";
|
||||||
|
float roundInterval; // Minutes
|
||||||
|
int keepAlive = 0; // Seconds
|
||||||
|
int retainFlag;
|
||||||
|
static std::string maintopic;
|
||||||
|
|
||||||
|
|
||||||
|
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int _keepAlive, float _roundInterval) {
|
||||||
|
NUMBERS = _NUMBERS;
|
||||||
|
keepAlive = _keepAlive;
|
||||||
|
roundInterval = _roundInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqttServer_setMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit,std::string _rateUnit) {
|
||||||
|
meterType = _meterType;
|
||||||
|
valueUnit = _valueUnit;
|
||||||
|
timeUnit = _timeUnit;
|
||||||
|
rateUnit = _rateUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||||
|
std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory) {
|
||||||
|
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 ((*NUMBERS).size() > 1) { // There is more than one meter, prepend the group so we can differentiate them
|
||||||
|
if (group != "") { // But only if the group is set
|
||||||
|
name = group + " " + name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field == "problem") { // Special binary sensor which is based on error topic
|
||||||
|
topicFull = "homeassistant/binary_sensor/" + maintopic + "/" + topicT + "/config";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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 (stateClass != "") {
|
||||||
|
payload += "\"state_class\": \"" + stateClass + "\"," + nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityCategory != "") {
|
||||||
|
payload += "\"entity_category\": \"" + entityCategory + "\"," + 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() {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, "MQTT - Sending Homeassistant Discovery Topics (Meter Type: " + meterType + ", Value Unit: " + valueUnit + " , Rate Unit: " + rateUnit + ")...");
|
||||||
|
|
||||||
|
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "clock-time-eight-outline", "s", "", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "" , "measurement", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic");
|
||||||
|
|
||||||
|
for (int i = 0; i < (*NUMBERS).size(); ++i) {
|
||||||
|
std::string group = (*NUMBERS)[i]->name;
|
||||||
|
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, "total_increasing", "");
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, "", "total_increasing", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic");
|
||||||
|
/* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_digitalization_round */
|
||||||
|
// sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", ""); // Legacy, always Unit per Minute
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, "", "", "");
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "rate_per_digitalization_round", "Change since last digitalization round", "arrow-expand-vertical", valueUnit, "", "measurement", ""); // correctly the Unit is Uint/Interval!
|
||||||
|
/* The timestamp string misses the Timezone, see PREVALUE_TIME_FORMAT_OUTPUT!
|
||||||
|
We need to know the timezone and append it! Until we do this, we simply
|
||||||
|
do not set the device class to "timestamp" to avoid errors in Homeassistant! */
|
||||||
|
// sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic");
|
||||||
|
sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "", "", ""); // Special binary sensor which is based on error topic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void publishSystemData() {
|
||||||
|
char tmp_char[50];
|
||||||
|
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, "Publishing system MQTT topics...");
|
||||||
|
|
||||||
|
sprintf(tmp_char, "%ld", (long)getUpTime());
|
||||||
|
MQTTPublish(maintopic + "/" + "uptime", std::string(tmp_char), retainFlag);
|
||||||
|
|
||||||
|
sprintf(tmp_char, "%zu", esp_get_free_heap_size());
|
||||||
|
MQTTPublish(maintopic + "/" + "freeMem", std::string(tmp_char), retainFlag);
|
||||||
|
|
||||||
|
sprintf(tmp_char, "%d", get_WIFI_RSSI());
|
||||||
|
MQTTPublish(maintopic + "/" + "wifiRSSI", std::string(tmp_char), retainFlag);
|
||||||
|
|
||||||
|
sprintf(tmp_char, "%d", (int)temperatureRead());
|
||||||
|
MQTTPublish(maintopic + "/" + "CPUtemp", std::string(tmp_char), retainFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void publishStaticData() {
|
||||||
|
LogFile.WriteToFile(ESP_LOG_INFO, "Publishing static MQTT topics...");
|
||||||
|
MQTTPublish(maintopic + "/" + "MAC", getMac(), retainFlag);
|
||||||
|
MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), retainFlag);
|
||||||
|
MQTTPublish(maintopic + "/" + "hostname", hostname, retainFlag);
|
||||||
|
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << std::fixed << std::setprecision(1) << roundInterval; // minutes
|
||||||
|
MQTTPublish(maintopic + "/" + "interval", stream.str(), retainFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t sendDiscovery_and_static_Topics(httpd_req_t *req) {
|
||||||
|
if (HomeassistantDiscovery) {
|
||||||
|
MQTThomeassistantDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
publishStaticData();
|
||||||
|
|
||||||
|
const char* resp_str = (const char*) req->user_ctx;
|
||||||
|
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GotConnected(std::string maintopic, int retainFlag) {
|
||||||
|
if (HomeassistantDiscovery) {
|
||||||
|
MQTThomeassistantDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
publishStaticData();
|
||||||
|
publishSystemData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_server_mqtt_uri(httpd_handle_t server) {
|
||||||
|
httpd_uri_t uri = { };
|
||||||
|
uri.method = HTTP_GET;
|
||||||
|
|
||||||
|
uri.uri = "/mqtt_publish_discovery";
|
||||||
|
uri.handler = sendDiscovery_and_static_Topics;
|
||||||
|
uri.user_ctx = (void*) "MQTT Discovery and Static Topics sent";
|
||||||
|
httpd_register_uri_handler(server, &uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string getTimeUnit(void) {
|
||||||
|
return timeUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SetHomeassistantDiscoveryEnabled(bool enabled) {
|
||||||
|
HomeassistantDiscovery = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setMqtt_Server_Retain(int _retainFlag) {
|
||||||
|
retainFlag = _retainFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqttServer_setMainTopic( std::string _maintopic) {
|
||||||
|
maintopic = _maintopic;
|
||||||
|
}
|
||||||
19
code/components/jomjol_mqtt/server_mqtt.h
Normal file
19
code/components/jomjol_mqtt/server_mqtt.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "ClassFlowDefineTypes.h"
|
||||||
|
|
||||||
|
#define LWT_TOPIC "connection"
|
||||||
|
#define LWT_CONNECTED "connected"
|
||||||
|
#define LWT_DISCONNECTED "connection lost"
|
||||||
|
|
||||||
|
|
||||||
|
void SetHomeassistantDiscoveryEnabled(bool enabled);
|
||||||
|
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int interval, float roundInterval);
|
||||||
|
void mqttServer_setMeterType(std::string meterType, std::string valueUnit, std::string timeUnit,std::string rateUnit);
|
||||||
|
void setMqtt_Server_Retain(int SetRetainFlag);
|
||||||
|
void mqttServer_setMainTopic( std::string maintopic);
|
||||||
|
|
||||||
|
void register_server_mqtt_uri(httpd_handle_t server);
|
||||||
|
|
||||||
|
void publishSystemData();
|
||||||
|
|
||||||
|
std::string getTimeUnit(void);
|
||||||
|
void GotConnected(std::string maintopic, int SetRetainFlag);
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "ClassControllCamera.h"
|
#include "ClassControllCamera.h"
|
||||||
#include "server_main.h"
|
#include "server_main.h"
|
||||||
#include "server_camera.h"
|
#include "server_camera.h"
|
||||||
|
#include "server_mqtt.h"
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
|
||||||
extern const char* GIT_TAG;
|
extern const char* GIT_TAG;
|
||||||
@@ -259,6 +260,7 @@ extern "C" void app_main(void)
|
|||||||
register_server_tflite_uri(server);
|
register_server_tflite_uri(server);
|
||||||
register_server_file_uri(server, "/sdcard");
|
register_server_file_uri(server, "/sdcard");
|
||||||
register_server_ota_sdcard_uri(server);
|
register_server_ota_sdcard_uri(server);
|
||||||
|
register_server_mqtt_uri(server);
|
||||||
|
|
||||||
gpio_handler_create(server);
|
gpio_handler_create(server);
|
||||||
|
|
||||||
|
|||||||
@@ -625,10 +625,15 @@ 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 colspan="3" style="padding-left: 20px;"><h4>Homeassistant Discovery (using MQTT)</h4>
|
||||||
|
<span style="font-size: 80%;">The discovery topics and the static topics (IP, MAC, Hostname, Interval, ...) only get sent on startup.
|
||||||
|
To send them again, you can call the following URL: <a href=mqtt_publish_discovery target="_blank">http://<IP>/mqtt_publish_discovery</a></span></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="indent1">
|
<td class="indent1">
|
||||||
<input type="checkbox" id="MQTT_HomeassistantDiscovery_enabled" value="1" onclick = 'InvertEnableItem("MQTT", "HomeassistantDiscovery")' unchecked >
|
<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>
|
<label for=MQTT_HomeassistantDiscovery_enabled><class id="MQTT_HomeassistantDiscovery_text" style="color:black;">Homeassistant Discovery</class></label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select id="MQTT_HomeassistantDiscovery_value1">
|
<select id="MQTT_HomeassistantDiscovery_value1">
|
||||||
@@ -637,7 +642,31 @@ textarea {
|
|||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td style="font-size: 80%;">
|
<td style="font-size: 80%;">
|
||||||
Enable or disable the <a href=https://www.home-assistant.io/docs/mqtt/discovery/ target=_blank>Homeassistand Discovery</a>
|
Enable or disable the <a href=https://www.home-assistant.io/docs/mqtt/discovery/ target=_blank>Homeassistant Discovery</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="indent1">
|
||||||
|
<input type="checkbox" id="MQTT_MeterType_enabled" value="1" onclick = 'InvertEnableItem("MQTT", "MeterType")' unchecked >
|
||||||
|
<label for=MQTT_MeterType_enabled><class id="MQTT_MeterType_text" style="color:black;">Meter Type</class></label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="MQTT_MeterType_value1"> <!-- See https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes -->
|
||||||
|
<option value="other" selected>Other (no Units)</option>
|
||||||
|
<option value="water_m3">Watermeter (Value: m³, Rate: m³/h)</option>
|
||||||
|
<option value="water_l">Watermeter (Value: l, Rate: l/h)</option>
|
||||||
|
<option value="water_gal">Watermeter (Value: gal, Rate: gal/h)</option>
|
||||||
|
<option value="water_ft3">Watermeter (Value: ft³, Rate: ft³/m)</option>
|
||||||
|
<option value="gas_m3">Gasmeter (Value: m³, Rate: m³/h)</option>
|
||||||
|
<option value="gas_ft3">Gasmeter (Value: ft³, Rate: ft³/m)</option>
|
||||||
|
<option value="energy_wh">Energymeter (Value: Wh, Rate: W)</option>
|
||||||
|
<option value="energy_kwh">Energymeter (Value: kWh, Rate: kW)</option>
|
||||||
|
<option value="energy_mwh">Energymeter (Value: MWh, Rate: MW)</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td style="font-size: 80%;">
|
||||||
|
Select the meter type so the sensors have the right units in Homeassistant.<br>
|
||||||
|
Note: For 'Watermeter' you need to have Homeassistant 2022.11 or never!
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@@ -1734,6 +1763,7 @@ function UpdateInput() {
|
|||||||
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, "MQTT", "HomeassistantDiscovery", true);
|
||||||
|
WriteParameter(param, category, "MQTT", "MeterType", 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);
|
||||||
@@ -1851,6 +1881,7 @@ function ReadParameterAll()
|
|||||||
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, "MQTT", "HomeassistantDiscovery", true);
|
||||||
|
ReadParameter(param, "MQTT", "MeterType", true);
|
||||||
|
|
||||||
ReadParameter(param, "InfluxDB", "Uri", true);
|
ReadParameter(param, "InfluxDB", "Uri", true);
|
||||||
ReadParameter(param, "InfluxDB", "Database", true);
|
ReadParameter(param, "InfluxDB", "Database", true);
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ function ParseConfig() {
|
|||||||
ParamAddValue(param, catname, "password");
|
ParamAddValue(param, catname, "password");
|
||||||
ParamAddValue(param, catname, "SetRetainFlag");
|
ParamAddValue(param, catname, "SetRetainFlag");
|
||||||
ParamAddValue(param, catname, "HomeassistantDiscovery");
|
ParamAddValue(param, catname, "HomeassistantDiscovery");
|
||||||
|
ParamAddValue(param, catname, "MeterType");
|
||||||
|
|
||||||
var catname = "InfluxDB";
|
var catname = "InfluxDB";
|
||||||
category[catname] = new Object();
|
category[catname] = new Object();
|
||||||
|
|||||||
Reference in New Issue
Block a user