Moved MQTT connection into handler

This commit is contained in:
Gunnar Skjold
2023-10-14 08:07:56 +02:00
parent f9b4680b9c
commit 7b025ddb01
17 changed files with 461 additions and 428 deletions

View File

@@ -1,37 +0,0 @@
#ifndef _AMSMQTTHANDLER_H
#define _AMSMQTTHANDLER_H
#include "Arduino.h"
#include <MQTT.h>
#include "AmsData.h"
#include "AmsConfiguration.h"
#include "EnergyAccounting.h"
#include "HwTools.h"
#include "EntsoeApi.h"
#if defined(ESP32)
#include <esp_task_wdt.h>
#endif
class AmsMqttHandler {
public:
AmsMqttHandler(MQTTClient* mqtt, char* buf) {
this->mqtt = mqtt;
this->json = buf;
};
virtual ~AmsMqttHandler() {};
virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi);
virtual bool publishTemperatures(AmsConfiguration*, HwTools*);
virtual bool publishPrices(EntsoeApi* eapi);
virtual bool publishSystem(HwTools*, EntsoeApi*, EnergyAccounting*);
protected:
MQTTClient* mqtt;
char* json;
uint16_t BufferSize = 2048;
bool loop();
};
#endif

View File

@@ -0,0 +1,56 @@
#ifndef _AMSMQTTHANDLER_H
#define _AMSMQTTHANDLER_H
#include "Arduino.h"
#include <MQTT.h>
#include "AmsData.h"
#include "AmsConfiguration.h"
#include "EnergyAccounting.h"
#include "HwTools.h"
#include "EntsoeApi.h"
#if defined(ESP32)
#include <esp_task_wdt.h>
#endif
class AmsMqttHandler {
public:
AmsMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf) {
this->mqttConfig = mqttConfig;
this->debugger = debugger;
this->json = buf;
mqtt.dropOverflow(true);
};
bool connect();
void disconnect();
lwmqtt_err_t lastError();
bool connected();
bool loop();
virtual uint8_t getFormat() { return 0; };
virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi) { return false; };
virtual bool publishTemperatures(AmsConfiguration*, HwTools*) { return false; };
virtual bool publishPrices(EntsoeApi* eapi) { return false; };
virtual bool publishSystem(HwTools*, EntsoeApi*, EnergyAccounting*) { return false; };
virtual bool publishRaw(String data) { return false; };
virtual ~AmsMqttHandler() {
if(mqttClient != NULL) {
mqttClient->stop();
delete mqttClient;
}
};
protected:
RemoteDebug* debugger;
MqttConfig mqttConfig;
MQTTClient mqtt = MQTTClient(128);
WiFiClient *mqttClient = NULL;
WiFiClientSecure *mqttSecureClient = NULL;
char* json;
uint16_t BufferSize = 2048;
};
#endif

View File

@@ -0,0 +1,152 @@
#include "AmsMqttHandler.h"
#include "FirmwareVersion.h"
#include "AmsStorage.h"
#include "LittleFS.h"
bool AmsMqttHandler::connect() {
time_t epoch = time(nullptr);
if(mqttConfig.ssl) {
if(epoch < FirmwareVersion::BuildEpoch) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("NTP not ready for MQTT SSL"));
return false;
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("MQTT SSL is configured (%dkb free heap)"), ESP.getFreeHeap());
if(mqttSecureClient == NULL) {
mqttSecureClient = new WiFiClientSecure();
#if defined(ESP8266)
mqttSecureClient->setBufferSizes(512, 512);
debugD_P(PSTR("ESP8266 firmware does not have enough memory..."));
return;
#endif
if(LittleFS.begin()) {
File file;
if(LittleFS.exists(FILE_MQTT_CA)) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT CA file (%dkb free heap)"), ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_CA, (char*) "r");
#if defined(ESP8266)
BearSSL::X509List *serverTrustedCA = new BearSSL::X509List(file);
mqttSecureClient->setTrustAnchors(serverTrustedCA);
#elif defined(ESP32)
if(mqttSecureClient->loadCACert(file, file.size())) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("CA accepted"));
} else {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf_P(PSTR("CA was rejected"));
delete mqttSecureClient;
mqttSecureClient = NULL;
return false;
}
#endif
file.close();
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
#if defined(ESP8266)
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT certificate file (%dkb free heap)"), ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
BearSSL::X509List *serverCertList = new BearSSL::X509List(file);
file.close();
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT key file (%dkb free heap)"), ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
file.close();
debugD_P(PSTR("Setting client certificates (%dkb free heap)"), ESP.getFreeHeap());
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
#elif defined(ESP32)
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT certificate file (%dkb free heap)"), ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
mqttSecureClient->loadCertificate(file, file.size());
file.close();
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Found MQTT key file (%dkb free heap)"), ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
mqttSecureClient->loadPrivateKey(file, file.size());
file.close();
#endif
}
} else {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("No CA, disabling certificate validation"));
mqttSecureClient->setInsecure();
}
mqttClient = mqttSecureClient;
LittleFS.end();
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("MQTT SSL setup complete (%dkb free heap)"), ESP.getFreeHeap());
}
}
}
if(mqttClient == NULL) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("No SSL, using client without SSL support"));
mqttClient = new WiFiClient();
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Connecting to MQTT %s:%d"), mqttConfig.host, mqttConfig.port);
mqtt.begin(mqttConfig.host, mqttConfig.port, *mqttClient);
#if defined(ESP8266)
if(mqttSecureClient) {
time_t epoch = time(nullptr);
debugD_P(PSTR("Setting NTP time %lu for secure MQTT connection"), epoch);
mqttSecureClient->setX509Time(epoch);
}
#endif
// Connect to a unsecure or secure MQTT server
if ((strlen(mqttConfig.username) == 0 && mqtt.connect(mqttConfig.clientId)) ||
(strlen(mqttConfig.username) > 0 && mqtt.connect(mqttConfig.clientId, mqttConfig.username, mqttConfig.password))) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Successfully connected to MQTT!"));
return true;
} else {
if (debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf_P(PSTR("Failed to connect to MQTT: %d"), mqtt.lastError());
#if defined(ESP8266)
if(mqttSecureClient) {
mqttSecureClient->getLastSSLError((char*) commonBuffer, BUF_SIZE_COMMON);
Debug.println((char*) commonBuffer);
}
#endif
}
return false;
}
}
void AmsMqttHandler::disconnect() {
mqtt.disconnect();
mqtt.loop();
delay(10);
yield();
if(mqttClient != NULL) {
mqttClient->stop();
delete mqttClient;
mqttClient = NULL;
if(mqttSecureClient != NULL) {
mqttSecureClient = NULL;
}
}
}
lwmqtt_err_t AmsMqttHandler::lastError() {
return mqtt.lastError();
}
bool AmsMqttHandler::connected() {
return mqtt.connected();
}
bool AmsMqttHandler::loop() {
bool ret = mqtt.loop();
delay(10);
yield();
#if defined(ESP32)
esp_task_wdt_reset();
#elif defined(ESP8266)
ESP.wdtFeed();
#endif
return ret;
}

View File

@@ -6,13 +6,16 @@
class DomoticzMqttHandler : public AmsMqttHandler {
public:
DomoticzMqttHandler(MQTTClient* mqtt, char* buf, DomoticzConfig config) : AmsMqttHandler(mqtt, buf) {
DomoticzMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, DomoticzConfig config) : AmsMqttHandler(mqttConfig, debugger, buf) {
this->config = config;
};
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi);
bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
bool publishRaw(String data);
uint8_t getFormat();
private:
DomoticzConfig config;

View File

@@ -14,7 +14,7 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
config.elidx,
val
);
ret = mqtt->publish(F("domoticz/in"), json);
ret = mqtt.publish(F("domoticz/in"), json);
}
}
@@ -28,7 +28,7 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
config.vl1idx,
val
);
ret |= mqtt->publish(F("domoticz/in"), json);
ret |= mqtt.publish(F("domoticz/in"), json);
}
if (config.vl2idx > 0){
@@ -38,7 +38,7 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
config.vl2idx,
val
);
ret |= mqtt->publish(F("domoticz/in"), json);
ret |= mqtt.publish(F("domoticz/in"), json);
}
if (config.vl3idx > 0){
@@ -48,7 +48,7 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
config.vl3idx,
val
);
ret |= mqtt->publish(F("domoticz/in"), json);
ret |= mqtt.publish(F("domoticz/in"), json);
}
if (config.cl1idx > 0){
@@ -58,7 +58,7 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
config.cl1idx,
val
);
ret |= mqtt->publish(F("domoticz/in"), json);
ret |= mqtt.publish(F("domoticz/in"), json);
}
return ret;
}
@@ -74,3 +74,11 @@ bool DomoticzMqttHandler::publishPrices(EntsoeApi* eapi) {
bool DomoticzMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
return false;
}
uint8_t DomoticzMqttHandler::getFormat() {
return 3;
}
bool DomoticzMqttHandler::publishRaw(String data) {
return false;
}

View File

@@ -7,12 +7,12 @@
class HomeAssistantMqttHandler : public AmsMqttHandler {
public:
HomeAssistantMqttHandler(MQTTClient* mqtt, char* buf, const char* clientId, const char* topic, uint8_t boardType, HomeAssistantConfig config, HwTools* hw) : AmsMqttHandler(mqtt, buf) {
this->clientId = clientId;
this->topic = String(topic);
HomeAssistantMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, uint8_t boardType, HomeAssistantConfig config, HwTools* hw) : AmsMqttHandler(mqttConfig, debugger, buf) {
this->hw = hw;
l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = false;
topic = String(mqttConfig.publishTopic);
if(strlen(config.discoveryNameTag) > 0) {
snprintf_P(buf, 128, PSTR("AMS reader (%s)"), config.discoveryNameTag);
deviceName = String(buf);
@@ -56,11 +56,13 @@ public:
bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
bool publishRaw(String data);
protected:
bool loop();
uint8_t getFormat();
private:
String topic;
String deviceName;
String deviceModel;
String deviceUid;
@@ -74,8 +76,6 @@ private:
bool tInit[32] = {false};
bool prInit[38] = {false};
String clientId;
String topic;
HwTools* hw;
bool publishList1(AmsData* data, EnergyAccounting* ea);

View File

@@ -16,7 +16,7 @@
#endif
bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi) {
if(topic.isEmpty() || !mqtt->connected())
if(topic.isEmpty() || !mqtt.connected())
return false;
if(data->getListType() >= 3) { // publish energy counts
@@ -45,7 +45,7 @@ bool HomeAssistantMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea)
snprintf_P(json, BufferSize, HA1_JSON,
data->getActiveImportPower()
);
return mqtt->publish(topic + "/power", json);
return mqtt.publish(topic + "/power", json);
}
bool HomeAssistantMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
@@ -66,7 +66,7 @@ bool HomeAssistantMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea)
data->getL2Voltage(),
data->getL3Voltage()
);
return mqtt->publish(topic + "/power", json);
return mqtt.publish(topic + "/power", json);
}
bool HomeAssistantMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
@@ -79,7 +79,7 @@ bool HomeAssistantMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea)
data->getReactiveExportCounter(),
data->getMeterTimestamp()
);
return mqtt->publish(topic + "/energy", json);
return mqtt.publish(topic + "/energy", json);
}
bool HomeAssistantMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
@@ -110,7 +110,7 @@ bool HomeAssistantMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea)
data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor()
);
return mqtt->publish(topic + "/power", json);
return mqtt.publish(topic + "/power", json);
}
String HomeAssistantMqttHandler::getMeterModel(AmsData* data) {
@@ -146,7 +146,7 @@ bool HomeAssistantMqttHandler::publishRealtime(AmsData* data, EnergyAccounting*
ea->getProducedThisMonth(),
ea->getIncomeThisMonth()
);
return mqtt->publish(topic + "/realtime", json);
return mqtt.publish(topic + "/realtime", json);
}
@@ -174,13 +174,13 @@ bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwT
}
char* pos = buf+strlen(buf);
snprintf_P(count == 0 ? pos : pos-1, 8, PSTR("}}"));
bool ret = mqtt->publish(topic + "/temperatures", buf);
bool ret = mqtt.publish(topic + "/temperatures", buf);
loop();
return ret;
}
bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
if(topic.isEmpty() || !mqtt->connected())
if(topic.isEmpty() || !mqtt.connected())
return false;
if(eapi->getValueForHour(0) == ENTSOE_NO_VALUE)
return false;
@@ -310,13 +310,13 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
ts3hr,
ts6hr
);
bool ret = mqtt->publish(topic + "/prices", json, true, 0);
bool ret = mqtt.publish(topic + "/prices", json, true, 0);
loop();
return ret;
}
bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
if(topic.isEmpty() || !mqtt->connected())
if(topic.isEmpty() || !mqtt.connected())
return false;
publishSystemSensors();
@@ -324,14 +324,14 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, Energ
snprintf_P(json, BufferSize, JSONSYS_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
FirmwareVersion::VersionString
);
bool ret = mqtt->publish(topic + "/state", json);
bool ret = mqtt.publish(topic + "/state", json);
loop();
return ret;
}
@@ -345,7 +345,7 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor)
snprintf_P(json, BufferSize, HADISCOVER_JSON,
sensorNamePrefix.c_str(),
sensor.name,
topic.c_str(), sensor.topic,
mqttConfig.publishTopic, sensor.topic,
deviceUid.c_str(), uid.c_str(),
deviceUid.c_str(), uid.c_str(),
sensor.uom,
@@ -363,7 +363,7 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor)
strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : "",
strlen_P(sensor.stacl) > 0 ? "\"" : ""
);
mqtt->publish(discoveryTopic + deviceUid + "_" + uid.c_str() + "/config", json, true, 0);
mqtt.publish(discoveryTopic + deviceUid + "_" + uid.c_str() + "/config", json, true, 0);
loop();
}
@@ -540,14 +540,10 @@ void HomeAssistantMqttHandler::publishSystemSensors() {
sInit = true;
}
bool HomeAssistantMqttHandler::loop() {
bool ret = mqtt->loop();
delay(10);
yield();
#if defined(ESP32)
esp_task_wdt_reset();
#elif defined(ESP8266)
ESP.wdtFeed();
#endif
return ret;
uint8_t HomeAssistantMqttHandler::getFormat() {
return 4;
}
bool HomeAssistantMqttHandler::publishRaw(String data) {
return false;
}

View File

@@ -5,22 +5,18 @@
class JsonMqttHandler : public AmsMqttHandler {
public:
JsonMqttHandler(MQTTClient* mqtt, char* buf, const char* clientId, const char* topic, HwTools* hw) : AmsMqttHandler(mqtt, buf) {
this->clientId = clientId;
this->topic = String(topic);
JsonMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, HwTools* hw) : AmsMqttHandler(mqttConfig, debugger, buf) {
this->hw = hw;
};
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi);
bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
bool publishRaw(String data);
protected:
bool loop();
uint8_t getFormat();
private:
String clientId;
String topic;
HwTools* hw;
bool publishList1(AmsData* data, EnergyAccounting* ea);

View File

@@ -10,7 +10,7 @@
#include "json/jsonprices_json.h"
bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi) {
if(topic.isEmpty() || !mqtt->connected())
if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
bool ret = false;
@@ -31,7 +31,7 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
bool JsonMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON1_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
@@ -45,13 +45,13 @@ bool JsonMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
return mqtt.publish(mqttConfig.publishTopic, json);
}
bool JsonMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON2_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
@@ -77,13 +77,13 @@ bool JsonMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
return mqtt.publish(mqttConfig.publishTopic, json);
}
bool JsonMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON3_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
@@ -114,13 +114,13 @@ bool JsonMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
return mqtt.publish(mqttConfig.publishTopic, json);
}
bool JsonMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON4_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
@@ -161,7 +161,7 @@ bool JsonMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
return mqtt.publish(mqttConfig.publishTopic, json);
}
String JsonMqttHandler::getMeterModel(AmsData* data) {
@@ -191,13 +191,13 @@ bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw)
}
char* pos = json+strlen(json);
snprintf_P(count == 0 ? pos : pos-1, 8, PSTR("}}"));
bool ret = mqtt->publish(topic, json);
bool ret = mqtt.publish(mqttConfig.publishTopic, json);
loop();
return ret;
}
bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
if(topic.isEmpty() || !mqtt->connected())
if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
if(eapi->getValueForHour(0) == ENTSOE_NO_VALUE)
return false;
@@ -325,37 +325,33 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
ts3hr,
ts6hr
);
bool ret = mqtt->publish(topic, json);
bool ret = mqtt.publish(mqttConfig.publishTopic, json);
loop();
return ret;
}
bool JsonMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
if(topic.isEmpty() || !mqtt->connected())
if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
snprintf_P(json, BufferSize, JSONSYS_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
FirmwareVersion::VersionString
);
bool ret = mqtt->publish(topic, json);
bool ret = mqtt.publish(mqttConfig.publishTopic, json);
loop();
return ret;
}
bool JsonMqttHandler::loop() {
bool ret = mqtt->loop();
delay(10);
yield();
#if defined(ESP32)
esp_task_wdt_reset();
#elif defined(ESP8266)
ESP.wdtFeed();
#endif
return ret;
uint8_t JsonMqttHandler::getFormat() {
return 0;
}
bool JsonMqttHandler::publishRaw(String data) {
return false;
}

View File

@@ -5,21 +5,21 @@
class RawMqttHandler : public AmsMqttHandler {
public:
RawMqttHandler(MQTTClient* mqtt, char* buf, const char* topic, bool full) : AmsMqttHandler(mqtt, buf) {
this->topic = String(topic);
this->full = full;
RawMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf) : AmsMqttHandler(mqttConfig, debugger, buf) {
full = mqttConfig.payloadFormat == 2;
topic = String(mqttConfig.publishTopic);
};
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi);
bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
bool publishRaw(String data);
protected:
bool loop();
uint8_t getFormat();
private:
String topic;
bool full;
String topic;
bool publishList1(AmsData* data, AmsData* meterState);
bool publishList2(AmsData* data, AmsData* meterState);

View File

@@ -3,11 +3,11 @@
#include "Uptime.h"
bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccounting* ea, EntsoeApi* eapi) {
if(topic.isEmpty() || !mqtt->connected())
if(topic.isEmpty() || !mqtt.connected())
return false;
if(data->getPackageTimestamp() > 0) {
mqtt->publish(topic + "/meter/dlms/timestamp", String(data->getPackageTimestamp()));
mqtt.publish(topic + "/meter/dlms/timestamp", String(data->getPackageTimestamp()));
}
switch(data->getListType()) {
case 4:
@@ -32,7 +32,7 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
bool RawMqttHandler::publishList1(AmsData* data, AmsData* meterState) {
if(full || meterState->getActiveImportPower() != data->getActiveImportPower()) {
mqtt->publish(topic + "/meter/import/active", String(data->getActiveImportPower()));
mqtt.publish(topic + "/meter/import/active", String(data->getActiveImportPower()));
}
return true;
}
@@ -40,101 +40,101 @@ bool RawMqttHandler::publishList1(AmsData* data, AmsData* meterState) {
bool RawMqttHandler::publishList2(AmsData* data, AmsData* meterState) {
// Only send data if changed. ID and Type is sent on the 10s interval only if changed
if(full || meterState->getMeterId() != data->getMeterId()) {
mqtt->publish(topic + "/meter/id", data->getMeterId());
mqtt.publish(topic + "/meter/id", data->getMeterId());
}
if(full || meterState->getMeterModel() != data->getMeterModel()) {
mqtt->publish(topic + "/meter/type", data->getMeterModel());
mqtt.publish(topic + "/meter/type", data->getMeterModel());
}
if(full || meterState->getL1Current() != data->getL1Current()) {
mqtt->publish(topic + "/meter/l1/current", String(data->getL1Current(), 2));
mqtt.publish(topic + "/meter/l1/current", String(data->getL1Current(), 2));
}
if(full || meterState->getL1Voltage() != data->getL1Voltage()) {
mqtt->publish(topic + "/meter/l1/voltage", String(data->getL1Voltage(), 2));
mqtt.publish(topic + "/meter/l1/voltage", String(data->getL1Voltage(), 2));
}
if(full || meterState->getL2Current() != data->getL2Current()) {
mqtt->publish(topic + "/meter/l2/current", String(data->getL2Current(), 2));
mqtt.publish(topic + "/meter/l2/current", String(data->getL2Current(), 2));
}
if(full || meterState->getL2Voltage() != data->getL2Voltage()) {
mqtt->publish(topic + "/meter/l2/voltage", String(data->getL2Voltage(), 2));
mqtt.publish(topic + "/meter/l2/voltage", String(data->getL2Voltage(), 2));
}
if(full || meterState->getL3Current() != data->getL3Current()) {
mqtt->publish(topic + "/meter/l3/current", String(data->getL3Current(), 2));
mqtt.publish(topic + "/meter/l3/current", String(data->getL3Current(), 2));
}
if(full || meterState->getL3Voltage() != data->getL3Voltage()) {
mqtt->publish(topic + "/meter/l3/voltage", String(data->getL3Voltage(), 2));
mqtt.publish(topic + "/meter/l3/voltage", String(data->getL3Voltage(), 2));
}
if(full || meterState->getReactiveExportPower() != data->getReactiveExportPower()) {
mqtt->publish(topic + "/meter/export/reactive", String(data->getReactiveExportPower()));
mqtt.publish(topic + "/meter/export/reactive", String(data->getReactiveExportPower()));
}
if(full || meterState->getActiveExportPower() != data->getActiveExportPower()) {
mqtt->publish(topic + "/meter/export/active", String(data->getActiveExportPower()));
mqtt.publish(topic + "/meter/export/active", String(data->getActiveExportPower()));
}
if(full || meterState->getReactiveImportPower() != data->getReactiveImportPower()) {
mqtt->publish(topic + "/meter/import/reactive", String(data->getReactiveImportPower()));
mqtt.publish(topic + "/meter/import/reactive", String(data->getReactiveImportPower()));
}
return true;
}
bool RawMqttHandler::publishList3(AmsData* data, AmsData* meterState) {
// ID and type belongs to List 2, but I see no need to send that every 10s
mqtt->publish(topic + "/meter/id", data->getMeterId(), true, 0);
mqtt->publish(topic + "/meter/type", data->getMeterModel(), true, 0);
mqtt->publish(topic + "/meter/clock", String(data->getMeterTimestamp()));
mqtt->publish(topic + "/meter/import/reactive/accumulated", String(data->getReactiveImportCounter(), 3), true, 0);
mqtt->publish(topic + "/meter/import/active/accumulated", String(data->getActiveImportCounter(), 3), true, 0);
mqtt->publish(topic + "/meter/export/reactive/accumulated", String(data->getReactiveExportCounter(), 3), true, 0);
mqtt->publish(topic + "/meter/export/active/accumulated", String(data->getActiveExportCounter(), 3), true, 0);
mqtt.publish(topic + "/meter/id", data->getMeterId(), true, 0);
mqtt.publish(topic + "/meter/type", data->getMeterModel(), true, 0);
mqtt.publish(topic + "/meter/clock", String(data->getMeterTimestamp()));
mqtt.publish(topic + "/meter/import/reactive/accumulated", String(data->getReactiveImportCounter(), 3), true, 0);
mqtt.publish(topic + "/meter/import/active/accumulated", String(data->getActiveImportCounter(), 3), true, 0);
mqtt.publish(topic + "/meter/export/reactive/accumulated", String(data->getReactiveExportCounter(), 3), true, 0);
mqtt.publish(topic + "/meter/export/active/accumulated", String(data->getActiveExportCounter(), 3), true, 0);
return true;
}
bool RawMqttHandler::publishList4(AmsData* data, AmsData* meterState) {
if(full || meterState->getL1ActiveImportPower() != data->getL1ActiveImportPower()) {
mqtt->publish(topic + "/meter/import/l1", String(data->getL1ActiveImportPower(), 2));
mqtt.publish(topic + "/meter/import/l1", String(data->getL1ActiveImportPower(), 2));
}
if(full || meterState->getL2ActiveImportPower() != data->getL2ActiveImportPower()) {
mqtt->publish(topic + "/meter/import/l2", String(data->getL2ActiveImportPower(), 2));
mqtt.publish(topic + "/meter/import/l2", String(data->getL2ActiveImportPower(), 2));
}
if(full || meterState->getL3ActiveImportPower() != data->getL3ActiveImportPower()) {
mqtt->publish(topic + "/meter/import/l3", String(data->getL3ActiveImportPower(), 2));
mqtt.publish(topic + "/meter/import/l3", String(data->getL3ActiveImportPower(), 2));
}
if(full || meterState->getL1ActiveExportPower() != data->getL1ActiveExportPower()) {
mqtt->publish(topic + "/meter/export/l1", String(data->getL1ActiveExportPower(), 2));
mqtt.publish(topic + "/meter/export/l1", String(data->getL1ActiveExportPower(), 2));
}
if(full || meterState->getL2ActiveExportPower() != data->getL2ActiveExportPower()) {
mqtt->publish(topic + "/meter/export/l2", String(data->getL2ActiveExportPower(), 2));
mqtt.publish(topic + "/meter/export/l2", String(data->getL2ActiveExportPower(), 2));
}
if(full || meterState->getL3ActiveExportPower() != data->getL3ActiveExportPower()) {
mqtt->publish(topic + "/meter/export/l3", String(data->getL3ActiveExportPower(), 2));
mqtt.publish(topic + "/meter/export/l3", String(data->getL3ActiveExportPower(), 2));
}
if(full || meterState->getPowerFactor() != data->getPowerFactor()) {
mqtt->publish(topic + "/meter/powerfactor", String(data->getPowerFactor(), 2));
mqtt.publish(topic + "/meter/powerfactor", String(data->getPowerFactor(), 2));
}
if(full || meterState->getL1PowerFactor() != data->getL1PowerFactor()) {
mqtt->publish(topic + "/meter/l1/powerfactor", String(data->getL1PowerFactor(), 2));
mqtt.publish(topic + "/meter/l1/powerfactor", String(data->getL1PowerFactor(), 2));
}
if(full || meterState->getL2PowerFactor() != data->getL2PowerFactor()) {
mqtt->publish(topic + "/meter/l2/powerfactor", String(data->getL2PowerFactor(), 2));
mqtt.publish(topic + "/meter/l2/powerfactor", String(data->getL2PowerFactor(), 2));
}
if(full || meterState->getL3PowerFactor() != data->getL3PowerFactor()) {
mqtt->publish(topic + "/meter/l3/powerfactor", String(data->getL3PowerFactor(), 2));
mqtt.publish(topic + "/meter/l3/powerfactor", String(data->getL3PowerFactor(), 2));
}
return true;
}
bool RawMqttHandler::publishRealtime(EnergyAccounting* ea) {
mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3));
mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
mqtt->publish(topic + "/realtime/import/month", String(ea->getUseThisMonth(), 1));
mqtt.publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3));
mqtt.publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
mqtt.publish(topic + "/realtime/import/month", String(ea->getUseThisMonth(), 1));
uint8_t peakCount = ea->getConfig()->hours;
if(peakCount > 5) peakCount = 5;
for(uint8_t i = 1; i <= peakCount; i++) {
mqtt->publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i).value / 100.0, 10), true, 0);
mqtt.publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i).value / 100.0, 10), true, 0);
}
mqtt->publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0);
mqtt->publish(topic + "/realtime/import/monthmax", String(ea->getMonthMax(), 3), true, 0);
mqtt->publish(topic + "/realtime/export/hour", String(ea->getProducedThisHour(), 3));
mqtt->publish(topic + "/realtime/export/day", String(ea->getProducedToday(), 2));
mqtt->publish(topic + "/realtime/export/month", String(ea->getProducedThisMonth(), 1));
mqtt.publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0);
mqtt.publish(topic + "/realtime/import/monthmax", String(ea->getMonthMax(), 3), true, 0);
mqtt.publish(topic + "/realtime/export/hour", String(ea->getProducedThisHour(), 3));
mqtt.publish(topic + "/realtime/export/day", String(ea->getProducedToday(), 2));
mqtt.publish(topic + "/realtime/export/month", String(ea->getProducedThisMonth(), 1));
return true;
}
@@ -144,7 +144,7 @@ bool RawMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw)
TempSensorData* data = hw->getTempSensorData(i);
if(data != NULL && data->lastValidRead > -85) {
if(data->changed || full) {
mqtt->publish(topic + "/temperature/" + toHex(data->address), String(data->lastValidRead, 2));
mqtt.publish(topic + "/temperature/" + toHex(data->address), String(data->lastValidRead, 2));
data->changed = false;
}
}
@@ -153,7 +153,7 @@ bool RawMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw)
}
bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
if(topic.isEmpty() || !mqtt->connected())
if(topic.isEmpty() || !mqtt.connected())
return false;
if(eapi->getValueForHour(0) == ENTSOE_NO_VALUE)
return false;
@@ -236,58 +236,54 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
for(int i = 0; i < 34; i++) {
float val = values[i];
if(val == ENTSOE_NO_VALUE) {
mqtt->publish(topic + "/price/" + String(i), "", true, 0);
mqtt.publish(topic + "/price/" + String(i), "", true, 0);
} else {
mqtt->publish(topic + "/price/" + String(i), String(val, 4), true, 0);
mqtt.publish(topic + "/price/" + String(i), String(val, 4), true, 0);
}
mqtt->loop();
mqtt.loop();
delay(10);
}
if(min != INT16_MAX) {
mqtt->publish(topic + "/price/min", String(min, 4), true, 0);
mqtt.publish(topic + "/price/min", String(min, 4), true, 0);
}
if(max != INT16_MIN) {
mqtt->publish(topic + "/price/max", String(max, 4), true, 0);
mqtt.publish(topic + "/price/max", String(max, 4), true, 0);
}
if(min1hrIdx != -1) {
mqtt->publish(topic + "/price/cheapest/1hr", String(ts1hr), true, 0);
mqtt.publish(topic + "/price/cheapest/1hr", String(ts1hr), true, 0);
}
if(min3hrIdx != -1) {
mqtt->publish(topic + "/price/cheapest/3hr", String(ts3hr), true, 0);
mqtt.publish(topic + "/price/cheapest/3hr", String(ts3hr), true, 0);
}
if(min6hrIdx != -1) {
mqtt->publish(topic + "/price/cheapest/6hr", String(ts6hr), true, 0);
mqtt.publish(topic + "/price/cheapest/6hr", String(ts6hr), true, 0);
}
return true;
}
bool RawMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
if(topic.isEmpty() || !mqtt->connected())
if(topic.isEmpty() || !mqtt.connected())
return false;
mqtt->publish(topic + "/id", WiFi.macAddress(), true, 0);
mqtt->publish(topic + "/uptime", String((uint32_t) (millis64()/1000)));
mqtt.publish(topic + "/id", WiFi.macAddress(), true, 0);
mqtt.publish(topic + "/uptime", String((uint32_t) (millis64()/1000)));
float vcc = hw->getVcc();
if(vcc > 0) {
mqtt->publish(topic + "/vcc", String(vcc, 2));
mqtt.publish(topic + "/vcc", String(vcc, 2));
}
mqtt->publish(topic + "/mem", String(ESP.getFreeHeap()));
mqtt->publish(topic + "/rssi", String(hw->getWifiRssi()));
mqtt.publish(topic + "/mem", String(ESP.getFreeHeap()));
mqtt.publish(topic + "/rssi", String(hw->getWifiRssi()));
if(hw->getTemperature() > -85) {
mqtt->publish(topic + "/temperature", String(hw->getTemperature(), 2));
mqtt.publish(topic + "/temperature", String(hw->getTemperature(), 2));
}
return true;
}
bool RawMqttHandler::loop() {
bool ret = mqtt->loop();
delay(10);
yield();
#if defined(ESP32)
esp_task_wdt_reset();
#elif defined(ESP8266)
ESP.wdtFeed();
#endif
return ret;
uint8_t RawMqttHandler::getFormat() {
return full ? 3 : 2;
}
bool RawMqttHandler::publishRaw(String data) {
return false;
}

View File

@@ -2,7 +2,7 @@
#define _AMSWEBSERVER_h
#include "Arduino.h"
#include <MQTT.h>
#include "AmsMqttHandler.h"
#include "AmsConfiguration.h"
#include "HwTools.h"
#include "AmsData.h"
@@ -39,6 +39,7 @@ public:
void setMqttEnabled(bool);
void setEntsoeApi(EntsoeApi* eapi);
void setPriceSettings(String region, String currency);
void setMqttHandler(AmsMqttHandler* mqttHandler);
private:
RemoteDebug* debugger;
@@ -54,7 +55,7 @@ private:
AmsData* meterState;
AmsDataStorage* ds;
EnergyAccounting* ea = NULL;
MQTTClient* mqtt = NULL;
AmsMqttHandler* mqttHandler = NULL;
bool uploading = false;
File file;
bool performRestart = false;

View File

@@ -35,7 +35,7 @@
#if defined(ESP32)
#include <esp_task_wdt.h>
#include <esp_wifi.h>
#include <esp_clk.h>
#include <esp32/clk.h>
#endif
@@ -124,11 +124,6 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
mqttEnabled = strlen(mqttConfig.host) > 0;
}
void AmsWebServer::setMqtt(MQTTClient* mqtt) {
this->mqtt = mqtt;
}
void AmsWebServer::setTimezone(Timezone* tz) {
this->tz = tz;
}
@@ -136,6 +131,9 @@ void AmsWebServer::setTimezone(Timezone* tz) {
void AmsWebServer::setMqttEnabled(bool enabled) {
mqttEnabled = enabled;
}
void AmsWebServer::setMqttHandler(AmsMqttHandler* mqttHandler) {
this->mqttHandler = mqttHandler;
}
void AmsWebServer::setEntsoeApi(EntsoeApi* eapi) {
this->eapi = eapi;
@@ -418,9 +416,9 @@ void AmsWebServer::dataJson() {
uint8_t mqttStatus;
if(!mqttEnabled) {
mqttStatus = 0;
} else if(mqtt != NULL && mqtt->connected()) {
} else if(mqttHandler != NULL && mqttHandler->connected()) {
mqttStatus = 1;
} else if(mqtt != NULL && mqtt->lastError() == 0) {
} else if(mqttHandler != NULL && mqttHandler->lastError() == 0) {
mqttStatus = 2;
} else {
mqttStatus = 3;
@@ -467,7 +465,7 @@ void AmsWebServer::dataJson() {
hanStatus,
wifiStatus,
mqttStatus,
mqtt == NULL ? 0 : (int) mqtt->lastError(),
mqttHandler == NULL ? 0 : (int) mqttHandler->lastError(),
price == ENTSOE_NO_VALUE ? "null" : String(price, 2).c_str(),
meterState->getMeterType(),
meterConfig->distributionSystem,