Merge branch 'mqtt_changes'

This commit is contained in:
Gunnar Skjold 2023-11-16 18:20:50 +01:00
commit 0fa178a617
28 changed files with 659 additions and 444 deletions

View File

@ -33,7 +33,8 @@ struct SystemConfig {
bool userConfigured;
uint8_t dataCollectionConsent; // 0 = unknown, 1 = accepted, 2 = declined
char country[3];
}; // 7
uint8_t energyspeedometer;
}; // 8
struct WiFiConfig {
char ssid[32];
@ -231,6 +232,8 @@ public:
bool getSystemConfig(SystemConfig&);
bool setSystemConfig(SystemConfig&);
bool isSystemConfigChanged();
void ackSystemConfigChanged();
bool getWiFiConfig(WiFiConfig&);
bool setWiFiConfig(WiFiConfig&);
@ -318,7 +321,7 @@ protected:
private:
uint8_t configVersion = 0;
bool wifiChanged, mqttChanged, meterChanged = true, ntpChanged = true, entsoeChanged = false, energyAccountingChanged = true;
bool sysChanged = false, wifiChanged = false, mqttChanged = false, meterChanged = true, ntpChanged = true, entsoeChanged = false, energyAccountingChanged = true;
uint8_t tempSensorCount = 0;
TempSensorConfig** tempSensors = NULL;

View File

@ -13,12 +13,22 @@ bool AmsConfiguration::getSystemConfig(SystemConfig& config) {
config.vendorConfigured = false;
config.userConfigured = false;
config.dataCollectionConsent = 0;
config.energyspeedometer = 0;
strcpy(config.country, "");
return false;
}
}
bool AmsConfiguration::setSystemConfig(SystemConfig& config) {
SystemConfig existing;
if(getSystemConfig(existing)) {
sysChanged |= config.boardType != existing.boardType;
sysChanged |= config.vendorConfigured != existing.vendorConfigured;
sysChanged |= config.userConfigured != existing.userConfigured;
sysChanged |= config.dataCollectionConsent != existing.dataCollectionConsent;
sysChanged |= strcmp(config.country, existing.country) != 0;
sysChanged |= config.energyspeedometer != existing.energyspeedometer;
}
EEPROM.begin(EEPROM_SIZE);
stripNonAscii((uint8_t*) config.country, 2);
EEPROM.put(CONFIG_SYSTEM_START, config);
@ -27,6 +37,14 @@ bool AmsConfiguration::setSystemConfig(SystemConfig& config) {
return ret;
}
bool AmsConfiguration::isSystemConfigChanged() {
return sysChanged;
}
void AmsConfiguration::ackSystemConfigChanged() {
sysChanged = false;
}
bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
@ -85,7 +103,7 @@ void AmsConfiguration::clearWifi(WiFiConfig& config) {
uint16_t chipId;
#if defined(ESP32)
chipId = ESP.getEfuseMac();
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
config.power = 195;
#else
chipId = ESP.getChipId();
@ -738,6 +756,7 @@ void AmsConfiguration::clear() {
EEPROM.get(CONFIG_SYSTEM_START, sys);
sys.userConfigured = false;
sys.dataCollectionConsent = 0;
sys.energyspeedometer = 0;
strcpy(sys.country, "");
EEPROM.put(CONFIG_SYSTEM_START, sys);

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,60 @@
#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);
};
void setCaVerification(bool);
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(256);
unsigned long lastMqttRetry = -10000;
bool caVerification = true;
WiFiClient *mqttClient = NULL;
WiFiClientSecure *mqttSecureClient = NULL;
char* json;
uint16_t BufferSize = 2048;
};
#endif

View File

@ -0,0 +1,165 @@
#include "AmsMqttHandler.h"
#include "FirmwareVersion.h"
#include "AmsStorage.h"
#include "LittleFS.h"
void AmsMqttHandler::setCaVerification(bool caVerification) {
this->caVerification = caVerification;
}
bool AmsMqttHandler::connect() {
if(millis() - lastMqttRetry < 10000) {
yield();
return false;
}
lastMqttRetry = millis();
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\n"));
return false;
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("MQTT SSL is configured (%dkb free heap)\n"), ESP.getFreeHeap());
if(mqttSecureClient == NULL) {
mqttSecureClient = new WiFiClientSecure();
#if defined(ESP8266)
mqttSecureClient->setBufferSizes(512, 512);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("ESP8266 firmware does not have enough memory...\n"));
return false;
#endif
if(caVerification && 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)\n"), 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\n"));
} else {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf_P(PSTR("CA was rejected\n"));
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)\n"), 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)\n"), ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
file.close();
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_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)\n"), 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)\n"), 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 validation\n"));
mqttSecureClient->setInsecure();
}
LittleFS.end();
} else {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("CA verification disabled\n"));
mqttSecureClient->setInsecure();
}
mqttClient = mqttSecureClient;
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("MQTT SSL setup complete (%dkb free heap)\n"), ESP.getFreeHeap());
}
}
if(mqttClient == NULL) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("No SSL, using client without SSL support\n"));
mqttClient = new WiFiClient();
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Connecting to MQTT %s:%d\n"), mqttConfig.host, mqttConfig.port);
mqtt.begin(mqttConfig.host, mqttConfig.port, *mqttClient);
#if defined(ESP8266)
if(mqttSecureClient) {
time_t epoch = time(nullptr);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Setting NTP time %lu for secure MQTT connection\n"), 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!\n"));
return true;
} else {
if (debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf_P(PSTR("Failed to connect to MQTT: %d\n"), mqtt.lastError());
#if defined(ESP8266)
if(mqttSecureClient) {
mqttSecureClient->getLastSSLError((char*) json, BufferSize);
debugger->println((char*) json);
}
#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,11 +10,18 @@
#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) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Unable to publish data, no publish topic\n"));
return false;
}
if(!mqtt.connected()) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Unable to publish data, not connected\n"));
return false;
}
bool ret = false;
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Publishing list ID %d!\n"), data->getListType());
if(data->getListType() == 1) {
ret = publishList1(data, ea);
} else if(data->getListType() == 2) {
@ -31,7 +38,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 +52,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 +84,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 +121,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 +168,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 +198,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 +332,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;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,7 @@
"svelte": "^3.49.0",
"svelte-navigator": "^3.2.2",
"svelte-preprocess": "^4.10.7",
"svelte-qrcode": "^1.0.0",
"tailwindcss": "^3.1.5",
"vite": "^3.2.7"
}
@ -2592,6 +2593,12 @@
"sourcemap-codec": "^1.4.8"
}
},
"node_modules/svelte-qrcode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/svelte-qrcode/-/svelte-qrcode-1.0.0.tgz",
"integrity": "sha512-WrOvyyxtUzu32gVIDxcFMy0A7uUpbl/8yHaTNOsUaI8W5V4wa7AmReCjffhNY2aS42CqCLJ6qdwUoj/KxmeZzA==",
"dev": true
},
"node_modules/svelte2tsx": {
"version": "0.1.193",
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.1.193.tgz",
@ -4468,6 +4475,12 @@
}
}
},
"svelte-qrcode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/svelte-qrcode/-/svelte-qrcode-1.0.0.tgz",
"integrity": "sha512-WrOvyyxtUzu32gVIDxcFMy0A7uUpbl/8yHaTNOsUaI8W5V4wa7AmReCjffhNY2aS42CqCLJ6qdwUoj/KxmeZzA==",
"dev": true
},
"svelte2tsx": {
"version": "0.1.193",
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.1.193.tgz",

View File

@ -18,6 +18,7 @@
"svelte": "^3.49.0",
"svelte-navigator": "^3.2.2",
"svelte-preprocess": "^4.10.7",
"svelte-qrcode": "^1.0.0",
"tailwindcss": "^3.1.5",
"vite": "^3.2.7"
},

View File

@ -10,6 +10,7 @@
import { Link, navigate } from 'svelte-navigator';
import SubnetOptions from './SubnetOptions.svelte';
import TrashIcon from './TrashIcon.svelte';
import QrCode from 'svelte-qrcode';
export let sysinfo = {}
@ -98,6 +99,9 @@
},
h: {
t: '', h: '', n: ''
},
c: {
es: null
}
};
configurationStore.subscribe(update => {
@ -607,6 +611,24 @@
</div>
</div>
{/if}
{#if configuration.c.es != null}
<div class="cnt">
<input type="hidden" name="c" value="true"/>
<strong class="text-sm">Cloud connections</strong>
<div class="my-1">
<label><input type="checkbox" class="rounded mb-1" name="ces" value="true" bind:checked={configuration.c.es}/> Energy Speedometer</label>
{#if configuration.c.es}
<div class="pl-5">MAC: {sysinfo.mac}</div>
<div class="pl-5">Meter ID: {sysinfo.meter.id ? sysinfo.meter.id : "missing, required"}</div>
{#if sysinfo.mac && sysinfo.meter.id}
<div class="pl-2">
<QrCode value='{'{'}"mac":"{sysinfo.mac}","meter":"{sysinfo.meter.id}"{'}'}'/>
</div>
{/if}
{/if}
</div>
</div>
{/if}
{#if configuration.p.r.startsWith("10YNO") || configuration.p.r == '10Y1001A1001A48H'}
<div class="cnt">
<strong class="text-sm">Tariff thresholds</strong>

View File

@ -41,7 +41,7 @@ export function metertype(mt) {
case 10:
return "Sagemcom";
default:
return "";
return "Unknown";
}
}

View File

@ -126,10 +126,10 @@
Manufacturer: {metertype(sysinfo.meter.mfg)}
</div>
<div class="my-2">
Model: {sysinfo.meter.model}
Model: {sysinfo.meter.model ? sysinfo.meter.model : "unknown"}
</div>
<div class="my-2">
ID: {sysinfo.meter.id}
ID: {sysinfo.meter.id ? sysinfo.meter.id : "unknown"}
</div>
</div>
{/if}

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

@ -0,0 +1,3 @@
"c": {
"es": %s
}

View File

@ -2,4 +2,4 @@
"t" : "%s",
"h" : "%s",
"n" : "%s"
}
},

View File

@ -30,12 +30,13 @@
#include "html/conf_domoticz_json.h"
#include "html/conf_ha_json.h"
#include "html/conf_ui_json.h"
#include "html/conf_cloud_json.h"
#include "html/firmware_html.h"
#if defined(ESP32)
#include <esp_task_wdt.h>
#include <esp_wifi.h>
#include <esp_clk.h>
#include <esp32/clk.h>
#endif
@ -124,11 +125,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 +132,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 +417,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 +466,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,
@ -791,6 +790,8 @@ void AmsWebServer::configurationJson() {
if(!checkSecurity(1))
return;
SystemConfig sysConfig;
config->getSystemConfig(sysConfig);
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
WiFiConfig wifiConfig;
@ -976,6 +977,14 @@ void AmsWebServer::configurationJson() {
haconf.discoveryNameTag
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_CLOUD_JSON,
#if defined(ENERGY_SPEEDOMETER_PASS)
sysConfig.energyspeedometer == 7 ? "true" : "false"
#else
"null"
#endif
);
server.sendContent(buf);
server.sendContent_P(PSTR("}"));
}
@ -1471,6 +1480,14 @@ void AmsWebServer::handleSave() {
config->setEnergyAccountingConfig(eac);
}
if(server.hasArg(F("c")) && server.arg(F("c")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received cloud config\n"));
SystemConfig sys;
config->getSystemConfig(sys);
sys.energyspeedometer = server.hasArg(F("ces")) && server.arg(F("ces")) == F("true") ? 7 : 0;
config->setSystemConfig(sys);
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Saving configuration now...\n"));
if (config->save()) {

View File

@ -2,7 +2,7 @@
extra_configs = platformio-user.ini
[common]
lib_deps = EEPROM, LittleFS, DNSServer, https://github.com/256dpi/arduino-mqtt.git, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, EntsoePriceApi, EnergyAccounting, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, SvelteUi
lib_deps = EEPROM, LittleFS, DNSServer, https://github.com/256dpi/arduino-mqtt.git, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, EntsoePriceApi, EnergyAccounting, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, SvelteUi
lib_ignore = OneWire
extra_scripts =
pre:scripts/addversion.py

View File

@ -48,12 +48,13 @@ ADC_MODE(ADC_VCC);
#include "RawMqttHandler.h"
#include "DomoticzMqttHandler.h"
#include "HomeAssistantMqttHandler.h"
#include "PassthroughMqttHandler.h"
#include "Uptime.h"
#include "RemoteDebug.h"
#define debugV_P(x, ...) if (Debug.isActive(Debug.VERBOSE)) {Debug.printf_P(x, ##__VA_ARGS__);Debug.println();}
#define debugV_P(x, ...) if (Debug.isActive(Debug.VERBOSE)) {Debug.printf_P(x, ##__VA_ARGS__);Debug.println();}
#define debugD_P(x, ...) if (Debug.isActive(Debug.DEBUG)) {Debug.printf_P(x, ##__VA_ARGS__);Debug.println();}
#define debugI_P(x, ...) if (Debug.isActive(Debug.INFO)) {Debug.printf_P(x, ##__VA_ARGS__);Debug.println();}
#define debugW_P(x, ...) if (Debug.isActive(Debug.WARNING)) {Debug.printf_P(x, ##__VA_ARGS__);Debug.println();}
@ -89,11 +90,32 @@ Timezone* tz = NULL;
AmsWebServer ws(commonBuffer, &Debug, &hw);
MQTTClient *mqtt = NULL;
WiFiClient *mqttClient = NULL;
WiFiClientSecure *mqttSecureClient = NULL;
bool mqttEnabled = false;
AmsMqttHandler* mqttHandler = NULL;
#if defined(ESP32)
JsonMqttHandler* energySpeedometer = NULL;
MqttConfig energySpeedometerConfig = {
"mqtt.sandtime.energy",
8883,
"",
"amsleser",
"",
#if defined(ENERGY_SPEEDOMETER_USER)
ENERGY_SPEEDOMETER_USER,
#else
"",
#endif
#if defined(ENERGY_SPEEDOMETER_PASS)
ENERGY_SPEEDOMETER_PASS,
#else
"",
#endif
0,
true
};
#endif
Stream *hanSerial;
SoftwareSerial *swSerial = NULL;
HardwareSerial *hwSerial = NULL;
@ -102,8 +124,6 @@ uint8_t rxBufferErrors = 0;
SystemConfig sysConfig;
GpioConfig gpioConfig;
MeterConfig meterConfig;
bool mqttEnabled = false;
String topic = "ams";
AmsData meterState;
bool ntpEnabled = false;
@ -533,15 +553,49 @@ void loop() {
#endif
if (mqttEnabled || config.isMqttChanged()) {
if(mqtt == NULL || !mqtt->connected() || config.isMqttChanged()) {
if(mqttHandler == NULL || !mqttHandler->connected() || config.isMqttChanged()) {
MQTT_connect();
config.ackMqttChange();
}
} else if(mqtt != NULL && mqtt->connected()) {
mqttClient->stop();
mqtt->disconnect();
} else if(mqttHandler != NULL) {
mqttHandler->disconnect();
}
#if defined(ENERGY_SPEEDOMETER_PASS)
if(sysConfig.energyspeedometer == 7) {
if(!meterState.getMeterId().isEmpty()) {
if(energySpeedometer == NULL) {
uint16_t chipId;
#if defined(ESP32)
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
#else
chipId = ESP.getChipId();
#endif
strcpy(energySpeedometerConfig.clientId, (String("ams") + String(chipId, HEX)).c_str());
energySpeedometer = new JsonMqttHandler(energySpeedometerConfig, &Debug, (char*) commonBuffer, &hw);
energySpeedometer->setCaVerification(false);
}
if(!energySpeedometer->connected()) {
lwmqtt_err_t err = energySpeedometer->lastError();
if(err > 0)
debugE_P(PSTR("Energyspeedometer connector reporting error (%d)"), err);
energySpeedometer->connect();
energySpeedometer->publishSystem(&hw, eapi, &ea);
}
energySpeedometer->loop();
delay(10);
}
} else if(energySpeedometer != NULL) {
if(energySpeedometer->connected()) {
energySpeedometer->disconnect();
energySpeedometer->loop();
} else {
delete energySpeedometer;
energySpeedometer = NULL;
}
}
#endif
try {
handlePriceApi(now);
} catch(const std::exception& e) {
@ -554,9 +608,9 @@ void loop() {
debugW_P(PSTR("Used %dms to handle web"), millis()-start);
}
}
if(mqtt != NULL) {
if(mqttHandler != NULL) {
start = millis();
mqtt->loop();
mqttHandler->loop();
delay(10); // Needed to preserve power. After adding this, the voltage is super smooth on a HAN powered device
end = millis();
if(end - start > 1000) {
@ -687,11 +741,23 @@ void handleNtpChange() {
}
void handleSystem(unsigned long now) {
if(config.isSystemConfigChanged()) {
config.getSystemConfig(sysConfig);
config.ackSystemConfigChanged();
}
unsigned long start, end;
if(now - lastSysupdate > 60000) {
start = millis();
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
mqttHandler->publishSystem(&hw, eapi, &ea);
if(WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED) {
if(mqttHandler != NULL) {
mqttHandler->publishSystem(&hw, eapi, &ea);
}
#if defined(ENERGY_SPEEDOMETER_PASS)
if(energySpeedometer != NULL) {
energySpeedometer->publishSystem(&hw, eapi, &ea);
}
#endif
}
lastSysupdate = now;
end = millis();
@ -738,7 +804,7 @@ void handleTemperature(unsigned long now) {
if(hw.updateTemperatures()) {
lastTemperatureRead = now;
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
if(mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED) {
mqttHandler->publishTemperatures(&config, &hw);
}
}
@ -753,7 +819,7 @@ void handlePriceApi(unsigned long now) {
unsigned long start, end;
if(eapi != NULL && ntpEnabled) {
start = millis();
if(eapi->loop() && mqtt != NULL && mqttHandler != NULL && mqtt->connected()) {
if(eapi->loop() && mqttHandler != NULL) {
end = millis();
if(end - start > 1000) {
debugW_P(PSTR("Used %dms to update prices"), millis()-start);
@ -1078,7 +1144,7 @@ void errorBlink() {
}
break;
case 1:
if(mqttEnabled && mqtt != NULL && mqtt->lastError() != 0) {
if(mqttHandler != NULL && mqttHandler->lastError() != 0) {
debugW_P(PSTR("MQTT connection not available, double blink"));
hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice
return;
@ -1221,10 +1287,8 @@ bool readHanPort() {
meterState.setLastError(pos);
printHanReadError(pos);
len += hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
mqtt->publish(topic.c_str(), toHex(hanBuffer+pos, len));
mqtt->loop();
delay(10);
if(mqttHandler != NULL) {
mqttHandler->publishRaw(toHex(hanBuffer+pos, len));
}
while(hanSerial->available()) hanSerial->read(); // Make sure it is all empty, in case we overflowed buffer above
len = 0;
@ -1242,10 +1306,8 @@ bool readHanPort() {
if(maxDetectedPayloadSize < pos) maxDetectedPayloadSize = pos;
if(ctx.type == DATA_TAG_DLMS) {
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
mqtt->publish(topic.c_str(), toHex((byte*) payload, ctx.length));
mqtt->loop();
delay(10);
if(mqttHandler != NULL) {
mqttHandler->publishRaw(toHex((byte*) payload, ctx.length));
}
debugV_P(PSTR("Using application data:"));
@ -1300,7 +1362,7 @@ void handleDataSuccess(AmsData* data) {
if(!hw.ledBlink(LED_GREEN, 1))
hw.ledBlink(LED_INTERNAL, 1);
if(mqttEnabled && mqttHandler != NULL && mqtt != NULL) {
if(mqttHandler != NULL) {
#if defined(ESP32)
esp_task_wdt_reset();
#elif defined(ESP8266)
@ -1308,10 +1370,14 @@ void handleDataSuccess(AmsData* data) {
#endif
yield();
if(mqttHandler->publish(data, &meterState, &ea, eapi)) {
mqtt->loop();
delay(10);
}
}
#if defined(ENERGY_SPEEDOMETER_PASS)
if(energySpeedometer != NULL && energySpeedometer->publish(&meterState, &meterState, &ea, eapi)) {
delay(10);
}
#endif
time_t now = time(nullptr);
if(now < FirmwareVersion::BuildEpoch && data->getListType() >= 3) {
@ -1418,23 +1484,8 @@ unsigned long lastWifiRetry = -WIFI_CONNECTION_TIMEOUT;
void WiFi_disconnect(unsigned long timeout) {
if (Debug.isActive(RemoteDebug::INFO)) debugI_P(PSTR("Not connected to WiFi, closing resources"));
if(mqtt != NULL) {
mqtt->disconnect();
mqtt->loop();
delay(10);
yield();
delete mqtt;
mqtt = NULL;
ws.setMqtt(NULL);
}
if(mqttClient != NULL) {
mqttClient->stop();
delete mqttClient;
mqttClient = NULL;
if(mqttSecureClient != NULL) {
mqttSecureClient = NULL;
}
if(mqttHandler != NULL) {
mqttHandler->disconnect();
}
#if defined(ESP8266)
@ -1647,16 +1698,6 @@ void WiFi_post_connect() {
}
}
void mqttMessageReceived(String &topic, String &payload) {
debugI_P(PSTR("Received message for topic %s"), topic.c_str() );
//if(meterConfig.source == METER_SOURCE_MQTT) {
//DataParserContext ctx = {static_cast<uint8_t>(payload.length()/2)};
//fromHex(hanBuffer, payload, ctx.length);
//uint16_t pos = unwrapData(hanBuffer, ctx);
// TODO: Run through DLMS/DMSR parser and apply AmsData
//}
}
int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
int16_t ret = 0;
bool doRet = false;
@ -1711,19 +1752,15 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
case DATA_TAG_HDLC:
debugV_P(PSTR("HDLC frame:"));
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
mqtt->publish(topic.c_str(), toHex(buf, curLen));
mqtt->loop();
delay(10);
if(mqttHandler != NULL) {
mqttHandler->publishRaw(toHex(buf, curLen));
}
break;
case DATA_TAG_MBUS:
debugV_P(PSTR("MBUS frame:"));
// If MQTT bytestream payload is selected (mqttHandler == NULL), send the payload to MQTT
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
mqtt->publish(topic.c_str(), toHex(buf, curLen));
mqtt->loop();
delay(10);
if(mqttHandler != NULL) {
mqttHandler->publishRaw(toHex(buf, curLen));
}
break;
case DATA_TAG_GBT:
@ -1740,10 +1777,8 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
break;
case DATA_TAG_DSMR:
debugV_P(PSTR("DSMR frame:"));
if(mqttEnabled && mqtt != NULL && mqttHandler == NULL) {
mqtt->publish(topic.c_str(), (char*) buf);
mqtt->loop();
delay(10);
if(mqttHandler != NULL) {
mqttHandler->publishRaw(String((char*)buf));
}
break;
}
@ -1777,191 +1812,57 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
unsigned long lastMqttRetry = -10000;
void MQTT_connect() {
if(millis() - lastMqttRetry < (config.isMqttChanged() ? 5000 : 30000)) {
yield();
return;
}
lastMqttRetry = millis();
MqttConfig mqttConfig;
if(!config.getMqttConfig(mqttConfig) || strlen(mqttConfig.host) == 0) {
if(Debug.isActive(RemoteDebug::WARNING)) debugW_P(PSTR("No MQTT config"));
mqttEnabled = false;
ws.setMqttEnabled(false);
mqttEnabled = false;
return;
}
if(mqtt != NULL) {
if(millis() - lastMqttRetry < (mqtt->lastError() == 0 || config.isMqttChanged() ? 5000 : 30000)) {
yield();
return;
}
lastMqttRetry = millis();
if(Debug.isActive(RemoteDebug::INFO)) {
debugD_P(PSTR("Disconnecting MQTT before connecting"));
}
mqtt->disconnect();
if(config.isMqttChanged()) {
if(mqttSecureClient != NULL) {
mqttSecureClient->stop();
delete mqttSecureClient;
mqttSecureClient = NULL;
} else {
mqttClient->stop();
}
mqttClient = NULL;
}
yield();
} else {
mqtt = new MQTTClient(256);
mqtt->dropOverflow(true);
ws.setMqtt(mqtt);
}
mqttEnabled = true;
ws.setMqttEnabled(true);
topic = String(mqttConfig.publishTopic);
if(mqttHandler != NULL) {
if(mqttHandler != NULL && mqttHandler->getFormat() != mqttConfig.payloadFormat) {
delete mqttHandler;
mqttHandler = NULL;
}
switch(mqttConfig.payloadFormat) {
case 0:
mqttHandler = new JsonMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.clientId, mqttConfig.publishTopic, &hw);
break;
case 1:
case 2:
mqttHandler = new RawMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.publishTopic, mqttConfig.payloadFormat == 2);
break;
case 3:
DomoticzConfig domo;
config.getDomoticzConfig(domo);
mqttHandler = new DomoticzMqttHandler(mqtt, (char*) commonBuffer, domo);
break;
case 4:
HomeAssistantConfig haconf;
config.getHomeAssistantConfig(haconf);
mqttHandler = new HomeAssistantMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.clientId, mqttConfig.publishTopic, sysConfig.boardType, haconf, &hw);
break;
}
time_t epoch = time(nullptr);
if(mqttConfig.ssl) {
if(epoch < FirmwareVersion::BuildEpoch) {
debugI_P(PSTR("NTP not ready for MQTT SSL"));
return;
}
debugI_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)) {
debugI_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())) {
debugI_P(PSTR("CA accepted"));
} else {
debugW_P(PSTR("CA was rejected"));
delete mqttSecureClient;
mqttSecureClient = NULL;
return;
}
#endif
file.close();
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
#if defined(ESP8266)
debugI_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();
debugI_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)
debugI_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();
debugI_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 {
debugI_P(PSTR("No CA, disabling certificate validation"));
mqttSecureClient->setInsecure();
}
mqttClient = mqttSecureClient;
LittleFS.end();
debugD_P(PSTR("MQTT SSL setup complete (%dkb free heap)"), ESP.getFreeHeap());
}
if(mqttHandler == NULL) {
switch(mqttConfig.payloadFormat) {
case 0:
mqttHandler = new JsonMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &hw);
break;
case 1:
case 2:
mqttHandler = new RawMqttHandler(mqttConfig, &Debug, (char*) commonBuffer);
break;
case 3:
DomoticzConfig domo;
config.getDomoticzConfig(domo);
mqttHandler = new DomoticzMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, domo);
break;
case 4:
HomeAssistantConfig haconf;
config.getHomeAssistantConfig(haconf);
mqttHandler = new HomeAssistantMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, sysConfig.boardType, haconf, &hw);
break;
case 255:
mqttHandler = new PassthroughMqttHandler(mqttConfig, &Debug, (char*) commonBuffer);
break;
}
}
if(mqttClient == NULL) {
debugI_P(PSTR("No SSL, using client without SSL support"));
mqttClient = new WiFiClient();
ws.setMqttHandler(mqttHandler);
if(mqttHandler != NULL) {
mqttHandler->connect();
mqttHandler->publishSystem(&hw, eapi, &ea);
}
if(Debug.isActive(RemoteDebug::INFO)) {
debugI_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 (Debug.isActive(RemoteDebug::INFO)) debugI_P(PSTR("Successfully connected to MQTT!"));
if(mqttHandler != NULL) {
mqttHandler->publishSystem(&hw, eapi, &ea);
}
// Subscribe to the chosen MQTT topic, if set in configuration
if (strlen(mqttConfig.subscribeTopic) > 0) {
mqtt->onMessage(mqttMessageReceived);
mqtt->subscribe(String(mqttConfig.subscribeTopic) + "/#");
debugI_P(PSTR(" Subscribing to [%s]\n"), mqttConfig.subscribeTopic);
}
} else {
if (Debug.isActive(RemoteDebug::ERROR)) {
debugE_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
}
}
yield();
}
void configFileParse() {

View File

@ -0,0 +1,28 @@
#include "PassthroughMqttHandler.h"
bool PassthroughMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, EntsoeApi* eapi) {
return false;
}
bool PassthroughMqttHandler::publishTemperatures(AmsConfiguration*, HwTools*) {
return false;
}
bool PassthroughMqttHandler::publishPrices(EntsoeApi*) {
return false;
}
bool PassthroughMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
return false;
}
bool PassthroughMqttHandler::publishRaw(String data) {
bool ret = mqtt.publish(mqttConfig.publishTopic, data);
loop();
delay(10);
return ret;
}
uint8_t PassthroughMqttHandler::getFormat() {
return 255;
}

View File

@ -0,0 +1,17 @@
#ifndef _PASSTHROUGHMQTTHANDLER_H
#define _PASSTHROUGHMQTTHANDLER_H
#include "AmsMqttHandler.h"
class PassthroughMqttHandler : public AmsMqttHandler {
public:
PassthroughMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf) : AmsMqttHandler(mqttConfig, debugger, buf) {};
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();
};
#endif