Compare commits

..

24 Commits

Author SHA1 Message Date
Gunnar Skjold
39e86fa180 Show only half the labels on small screens 2023-04-21 18:55:15 +02:00
Gunnar Skjold
5d278a9d5a Splitting loop into smaller methods 2023-04-19 15:12:49 +02:00
Gunnar Skjold
61040e3e7c Crossing fingers 2023-04-18 22:06:29 +02:00
Gunnar Skjold
c4005f10a3 Some debugging changes and other optimizations 2023-04-18 11:50:44 +02:00
Gunnar Skjold
7bed5add5d Fix 2023-04-17 21:44:36 +02:00
Gunnar Skjold
18e382f21f Fix 2023-04-17 21:41:20 +02:00
Gunnar Skjold
94865b632e Some more optimization 2023-04-17 21:33:00 +02:00
Gunnar Skjold
f0461a7cdb Some adjustments 2023-04-17 11:49:22 +02:00
Gunnar Skjold
4cd6013d64 More improvements on buffer 2023-04-15 20:51:35 +02:00
Gunnar Skjold
b06dbb8d79 Buffer improvements and fixed special characters in ssid 2023-04-15 20:38:26 +02:00
Gunnar Skjold
02fe2073c2 Better error handling for price api 2023-04-15 09:30:19 +02:00
Gunnar Skjold
ec7ceafa84 General optimizations 2023-04-15 09:14:25 +02:00
Gunnar Skjold
276ac67d2e Only clear HA config if illegal characters are loaded from flash 2023-04-15 07:47:48 +02:00
Gunnar Skjold
be116d5b35 Improved firmware upgrade 2023-04-15 07:44:33 +02:00
Gunnar Skjold
9c8788225d Clear HA or domoticz config if not in use 2023-04-15 06:06:26 +02:00
Gunnar Skjold
df44b05792 Clear HA conf by default 2023-04-13 10:20:15 +02:00
Gunnar Skjold
8ee8eee6c4 Fixed laout error on realtime data 2023-04-10 14:14:20 +02:00
Gunnar Skjold
28e13b73e6 Fixed missing HA sensors 2023-04-10 14:08:41 +02:00
Gunnar Skjold
088e5645c7 Increased RX serial buff 2023-04-07 09:00:53 +02:00
Gunnar Skjold
3e3d61912d Serial debugging 2023-04-07 08:39:36 +02:00
Gunnar Skjold
b7854baa6d Some HAN serial port debugging 2023-04-07 08:36:42 +02:00
Gunnar Skjold
68bbfd6527 Load data before restoring from config file 2023-04-05 07:54:59 +02:00
Gunnar Skjold
c1ca3d0c65 Fixed month number in clock 2023-04-03 07:07:44 +02:00
Gunnar Skjold
71cac46470 Config restore loads username into password field 2023-04-03 07:06:18 +02:00
43 changed files with 1846 additions and 1261 deletions

View File

@@ -11,6 +11,7 @@
#define CONFIG_SYSTEM_START 8
#define CONFIG_METER_START 32
#define CONFIG_UPGRADE_INFO_START 216
#define CONFIG_UI_START 248
#define CONFIG_GPIO_START 266
#define CONFIG_ENTSOE_START 290
@@ -213,6 +214,13 @@ struct TempSensorConfig {
bool common;
};
struct UpgradeInformation {
char fromVersion[8];
char toVersion[8];
int16_t exitCode;
int16_t errorCode;
}; // 20
class AmsConfiguration {
public:
bool hasConfig();
@@ -244,6 +252,7 @@ public:
bool getMeterConfig(MeterConfig&);
bool setMeterConfig(MeterConfig&);
void clearMeter(MeterConfig&);
void setMeterChanged();
bool isMeterChanged();
void ackMeterChanged();
@@ -297,6 +306,10 @@ public:
bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]);
bool getUpgradeInformation(UpgradeInformation&);
bool setUpgradeInformation(int16_t exitCode, int16_t errorCode, const char* currentVersion, const char* nextVersion);
void clearUpgradeInformation(UpgradeInformation&);
void clear();
protected:

View File

@@ -7,6 +7,6 @@
String toHex(uint8_t* in);
String toHex(uint8_t* in, uint16_t size);
void fromHex(uint8_t *out, String in, uint16_t size);
void stripNonAscii(uint8_t* in, uint16_t size, bool extended = false);
bool stripNonAscii(uint8_t* in, uint16_t size, bool extended = false);
#endif

View File

@@ -266,6 +266,10 @@ void AmsConfiguration::ackMeterChanged() {
meterChanged = false;
}
void AmsConfiguration::setMeterChanged() {
meterChanged = true;
}
bool AmsConfiguration::getDebugConfig(DebugConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
@@ -337,6 +341,9 @@ bool AmsConfiguration::getHomeAssistantConfig(HomeAssistantConfig& config) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_HA_START, config);
EEPROM.end();
if(stripNonAscii((uint8_t*) config.discoveryPrefix, 64) || stripNonAscii((uint8_t*) config.discoveryHostname, 64) || stripNonAscii((uint8_t*) config.discoveryNameTag, 16)) {
clearHomeAssistantConfig(config);
}
return true;
} else {
clearHomeAssistantConfig(config);
@@ -408,39 +415,39 @@ bool AmsConfiguration::setGpioConfig(GpioConfig& config) {
}
/* This currently does not work, as it checks its own pin
if(pinUsed(config.hanPin, config)) {
Serial.println("HAN pin already used");
debugger->println(F("HAN pin already used"));
return false;
}
if(pinUsed(config.apPin, config)) {
Serial.println("AP pin already used");
debugger->println(F("AP pin already used"));
return false;
}
if(pinUsed(config.ledPin, config)) {
Serial.println("LED pin already used");
debugger->println(F("LED pin already used"));
return false;
}
if(pinUsed(config.ledPinRed, config)) {
Serial.println("LED RED pin already used");
debugger->println(F("LED RED pin already used"));
return false;
}
if(pinUsed(config.ledPinGreen, config)) {
Serial.println("LED GREEN pin already used");
debugger->println(F("LED GREEN pin already used"));
return false;
}
if(pinUsed(config.ledPinBlue, config)) {
Serial.println("LED BLUE pin already used");
debugger->println(F("LED BLUE pin already used"));
return false;
}
if(pinUsed(config.tempSensorPin, config)) {
Serial.println("Temp sensor pin already used");
debugger->println(F("Temp sensor pin already used"));
return false;
}
if(pinUsed(config.tempAnalogSensorPin, config)) {
Serial.println("Analog temp sensor pin already used");
debugger->println(F("Analog temp sensor pin already used"));
return false;
}
if(pinUsed(config.vccPin, config)) {
Serial.println("Vcc pin already used");
debugger->println(F("Vcc pin already used"));
return false;
}
*/
@@ -679,6 +686,45 @@ void AmsConfiguration::clearUiConfig(UiConfig& config) {
config.showTemperaturePlot = 2;
}
bool AmsConfiguration::setUpgradeInformation(int16_t exitCode, int16_t errorCode, const char* currentVersion, const char* nextVersion) {
UpgradeInformation upinfo;
upinfo.exitCode = exitCode;
upinfo.errorCode = errorCode;
strcpy(upinfo.fromVersion, currentVersion);
strcpy(upinfo.toVersion, nextVersion);
stripNonAscii((uint8_t*) upinfo.fromVersion, 8);
stripNonAscii((uint8_t*) upinfo.toVersion, 8);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_UPGRADE_INFO_START, upinfo);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::getUpgradeInformation(UpgradeInformation& upinfo) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_UPGRADE_INFO_START, upinfo);
EEPROM.end();
if(stripNonAscii((uint8_t*) upinfo.fromVersion, 8) || stripNonAscii((uint8_t*) upinfo.toVersion, 8)) {
clearUpgradeInformation(upinfo);
}
return true;
} else {
clearUpgradeInformation(upinfo);
return false;
}
}
void AmsConfiguration::clearUpgradeInformation(UpgradeInformation& upinfo) {
upinfo.exitCode = -1;
upinfo.errorCode = 0;
memset(upinfo.fromVersion, 0, 8);
memset(upinfo.toVersion, 0, 8);
}
void AmsConfiguration::clear() {
EEPROM.begin(EEPROM_SIZE);
@@ -710,6 +756,10 @@ void AmsConfiguration::clear() {
clearDomo(domo);
EEPROM.put(CONFIG_DOMOTICZ_START, domo);
HomeAssistantConfig haconf;
clearHomeAssistantConfig(haconf);
EEPROM.put(CONFIG_HA_START, haconf);
NtpConfig ntp;
clearNtp(ntp);
EEPROM.put(CONFIG_NTP_START, ntp);
@@ -730,6 +780,10 @@ void AmsConfiguration::clear() {
clearUiConfig(ui);
EEPROM.put(CONFIG_UI_START, ui);
UpgradeInformation upinfo;
clearUpgradeInformation(upinfo);
EEPROM.put(CONFIG_UPGRADE_INFO_START, upinfo);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CLEARED_INDICATOR);
EEPROM.commit();
EEPROM.end();
@@ -825,8 +879,6 @@ void AmsConfiguration::loadTempSensors() {
int address = EEPROM_TEMP_CONFIG_ADDRESS;
int c = 0;
int storedCount = EEPROM.read(address++);
Serial.print("Sensors: ");
Serial.println(storedCount);
if(storedCount > 0 && storedCount <= 32) {
for(int i = 0; i < storedCount; i++) {
TempSensorConfig* tsc = new TempSensorConfig();
@@ -1128,155 +1180,155 @@ void AmsConfiguration::deleteFromFs(uint8_t version) {
void AmsConfiguration::print(Print* debugger)
{
debugger->println("-----------------------------------------------");
debugger->println(F("-----------------------------------------------"));
WiFiConfig wifi;
if(getWiFiConfig(wifi)) {
debugger->println("--WiFi configuration--");
debugger->printf("SSID: '%s'\r\n", wifi.ssid);
debugger->printf("Psk: '%s'\r\n", wifi.psk);
debugger->println(F("--WiFi configuration--"));
debugger->printf_P(PSTR("SSID: '%s'\r\n"), wifi.ssid);
debugger->printf_P(PSTR("Psk: '%s'\r\n"), wifi.psk);
if(strlen(wifi.ip) > 0) {
debugger->printf("IP: '%s'\r\n", wifi.ip);
debugger->printf("Gateway: '%s'\r\n", wifi.gateway);
debugger->printf("Subnet: '%s'\r\n", wifi.subnet);
debugger->printf("DNS1: '%s'\r\n", wifi.dns1);
debugger->printf("DNS2: '%s'\r\n", wifi.dns2);
debugger->printf_P(PSTR("IP: '%s'\r\n"), wifi.ip);
debugger->printf_P(PSTR("Gateway: '%s'\r\n"), wifi.gateway);
debugger->printf_P(PSTR("Subnet: '%s'\r\n"), wifi.subnet);
debugger->printf_P(PSTR("DNS1: '%s'\r\n"), wifi.dns1);
debugger->printf_P(PSTR("DNS2: '%s'\r\n"), wifi.dns2);
}
debugger->printf("Hostname: '%s'\r\n", wifi.hostname);
debugger->printf("mDNS: '%s'\r\n", wifi.mdns ? "Yes" : "No");
debugger->println("");
debugger->printf_P(PSTR("Hostname: '%s'\r\n"), wifi.hostname);
debugger->printf_P(PSTR("mDNS: '%s'\r\n"), wifi.mdns ? "Yes" : "No");
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
MqttConfig mqtt;
if(getMqttConfig(mqtt)) {
debugger->println("--MQTT configuration--");
debugger->println(F("--MQTT configuration--"));
if(strlen(mqtt.host) > 0) {
debugger->printf("Enabled: Yes\r\n");
debugger->printf("Host: '%s'\r\n", mqtt.host);
debugger->printf("Port: %i\r\n", mqtt.port);
debugger->printf("Client ID: '%s'\r\n", mqtt.clientId);
debugger->printf("Publish topic: '%s'\r\n", mqtt.publishTopic);
debugger->printf("Subscribe topic: '%s'\r\n", mqtt.subscribeTopic);
debugger->printf_P(PSTR("Enabled: Yes\r\n"));
debugger->printf_P(PSTR("Host: '%s'\r\n"), mqtt.host);
debugger->printf_P(PSTR("Port: %i\r\n"), mqtt.port);
debugger->printf_P(PSTR("Client ID: '%s'\r\n"), mqtt.clientId);
debugger->printf_P(PSTR("Publish topic: '%s'\r\n"), mqtt.publishTopic);
debugger->printf_P(PSTR("Subscribe topic: '%s'\r\n"), mqtt.subscribeTopic);
if (strlen(mqtt.username) > 0) {
debugger->printf("Username: '%s'\r\n", mqtt.username);
debugger->printf("Password: '%s'\r\n", mqtt.password);
debugger->printf_P(PSTR("Username: '%s'\r\n"), mqtt.username);
debugger->printf_P(PSTR("Password: '%s'\r\n"), mqtt.password);
}
debugger->printf("Payload format: %i\r\n", mqtt.payloadFormat);
debugger->printf("SSL: %s\r\n", mqtt.ssl ? "Yes" : "No");
debugger->printf_P(PSTR("Payload format: %i\r\n"), mqtt.payloadFormat);
debugger->printf_P(PSTR("SSL: %s\r\n"), mqtt.ssl ? "Yes" : "No");
} else {
debugger->printf("Enabled: No\r\n");
debugger->printf_P(PSTR("Enabled: No\r\n"));
}
debugger->println("");
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
WebConfig web;
if(getWebConfig(web)) {
debugger->println("--Web configuration--");
debugger->printf("Security: %i\r\n", web.security);
debugger->println(F("--Web configuration--"));
debugger->printf_P(PSTR("Security: %i\r\n"), web.security);
if (web.security > 0) {
debugger->printf("Username: '%s'\r\n", web.username);
debugger->printf("Password: '%s'\r\n", web.password);
debugger->printf_P(PSTR("Username: '%s'\r\n"), web.username);
debugger->printf_P(PSTR("Password: '%s'\r\n"), web.password);
}
debugger->println("");
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
MeterConfig meter;
if(getMeterConfig(meter)) {
debugger->println("--Meter configuration--");
debugger->printf("Baud: %i\r\n", meter.baud);
debugger->printf("Parity: %i\r\n", meter.parity);
debugger->printf("Invert serial: %s\r\n", meter.invert ? "Yes" : "No");
debugger->printf("Distribution system: %i\r\n", meter.distributionSystem);
debugger->printf("Main fuse: %i\r\n", meter.mainFuse);
debugger->printf("Production Capacity: %i\r\n", meter.productionCapacity);
debugger->println("");
debugger->println(F("--Meter configuration--"));
debugger->printf_P(PSTR("Baud: %i\r\n"), meter.baud);
debugger->printf_P(PSTR("Parity: %i\r\n"), meter.parity);
debugger->printf_P(PSTR("Invert serial: %s\r\n"), meter.invert ? "Yes" : "No");
debugger->printf_P(PSTR("Distribution system: %i\r\n"), meter.distributionSystem);
debugger->printf_P(PSTR("Main fuse: %i\r\n"), meter.mainFuse);
debugger->printf_P(PSTR("Production Capacity: %i\r\n"), meter.productionCapacity);
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
GpioConfig gpio;
if(getGpioConfig(gpio)) {
debugger->println("--GPIO configuration--");
debugger->printf("HAN pin: %i\r\n", gpio.hanPin);
debugger->printf("HAN pin pullup %s\r\n", gpio.hanPinPullup ? "Yes" : "No");
debugger->printf("LED pin: %i\r\n", gpio.ledPin);
debugger->printf("LED inverted: %s\r\n", gpio.ledInverted ? "Yes" : "No");
debugger->printf("LED red pin: %i\r\n", gpio.ledPinRed);
debugger->printf("LED green pin: %i\r\n", gpio.ledPinGreen);
debugger->printf("LED blue pin: %i\r\n", gpio.ledPinBlue);
debugger->printf("LED inverted: %s\r\n", gpio.ledRgbInverted ? "Yes" : "No");
debugger->printf("AP pin: %i\r\n", gpio.apPin);
debugger->printf("Temperature pin: %i\r\n", gpio.tempSensorPin);
debugger->printf("Temp analog pin: %i\r\n", gpio.tempAnalogSensorPin);
debugger->printf("Vcc pin: %i\r\n", gpio.vccPin);
debugger->println(F("--GPIO configuration--"));
debugger->printf_P(PSTR("HAN pin: %i\r\n"), gpio.hanPin);
debugger->printf_P(PSTR("HAN pin pullup %s\r\n"), gpio.hanPinPullup ? "Yes" : "No");
debugger->printf_P(PSTR("LED pin: %i\r\n"), gpio.ledPin);
debugger->printf_P(PSTR("LED inverted: %s\r\n"), gpio.ledInverted ? "Yes" : "No");
debugger->printf_P(PSTR("LED red pin: %i\r\n"), gpio.ledPinRed);
debugger->printf_P(PSTR("LED green pin: %i\r\n"), gpio.ledPinGreen);
debugger->printf_P(PSTR("LED blue pin: %i\r\n"), gpio.ledPinBlue);
debugger->printf_P(PSTR("LED inverted: %s\r\n"), gpio.ledRgbInverted ? "Yes" : "No");
debugger->printf_P(PSTR("AP pin: %i\r\n"), gpio.apPin);
debugger->printf_P(PSTR("Temperature pin: %i\r\n"), gpio.tempSensorPin);
debugger->printf_P(PSTR("Temp analog pin: %i\r\n"), gpio.tempAnalogSensorPin);
debugger->printf_P(PSTR("Vcc pin: %i\r\n"), gpio.vccPin);
if(gpio.vccMultiplier > 0) {
debugger->printf("Vcc multiplier: %f\r\n", gpio.vccMultiplier / 1000.0);
debugger->printf_P(PSTR("Vcc multiplier: %f\r\n"), gpio.vccMultiplier / 1000.0);
}
if(gpio.vccOffset > 0) {
debugger->printf("Vcc offset: %f\r\n", gpio.vccOffset / 100.0);
debugger->printf_P(PSTR("Vcc offset: %f\r\n"), gpio.vccOffset / 100.0);
}
if(gpio.vccBootLimit > 0) {
debugger->printf("Vcc boot limit: %f\r\n", gpio.vccBootLimit / 10.0);
debugger->printf_P(PSTR("Vcc boot limit: %f\r\n"), gpio.vccBootLimit / 10.0);
}
debugger->printf("GND resistor: %i\r\n", gpio.vccResistorGnd);
debugger->printf("Vcc resistor: %i\r\n", gpio.vccResistorVcc);
debugger->println("");
debugger->printf_P(PSTR("GND resistor: %i\r\n"), gpio.vccResistorGnd);
debugger->printf_P(PSTR("Vcc resistor: %i\r\n"), gpio.vccResistorVcc);
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
DomoticzConfig domo;
if(getDomoticzConfig(domo)) {
debugger->println("--Domoticz configuration--");
debugger->println(F("--Domoticz configuration--"));
if(mqtt.payloadFormat == 3 && domo.elidx > 0) {
debugger->printf("Enabled: Yes\r\n");
debugger->printf("Domoticz ELIDX: %i\r\n", domo.elidx);
debugger->printf("Domoticz VL1IDX: %i\r\n", domo.vl1idx);
debugger->printf("Domoticz VL2IDX: %i\r\n", domo.vl2idx);
debugger->printf("Domoticz VL3IDX: %i\r\n", domo.vl3idx);
debugger->printf("Domoticz CL1IDX: %i\r\n", domo.cl1idx);
debugger->printf_P(PSTR("Enabled: Yes\r\n"));
debugger->printf_P(PSTR("Domoticz ELIDX: %i\r\n"), domo.elidx);
debugger->printf_P(PSTR("Domoticz VL1IDX: %i\r\n"), domo.vl1idx);
debugger->printf_P(PSTR("Domoticz VL2IDX: %i\r\n"), domo.vl2idx);
debugger->printf_P(PSTR("Domoticz VL3IDX: %i\r\n"), domo.vl3idx);
debugger->printf_P(PSTR("Domoticz CL1IDX: %i\r\n"), domo.cl1idx);
} else {
debugger->printf("Enabled: No\r\n");
debugger->printf_P(PSTR("Enabled: No\r\n"));
}
debugger->println("");
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
NtpConfig ntp;
if(getNtpConfig(ntp)) {
debugger->println("--NTP configuration--");
debugger->printf("Enabled: %s\r\n", ntp.enable ? "Yes" : "No");
debugger->println(F("--NTP configuration--"));
debugger->printf_P(PSTR("Enabled: %s\r\n"), ntp.enable ? "Yes" : "No");
if(ntp.enable) {
debugger->printf("Timezone: %s\r\n", ntp.timezone);
debugger->printf("Server: %s\r\n", ntp.server);
debugger->printf("DHCP: %s\r\n", ntp.dhcp ? "Yes" : "No");
debugger->printf_P(PSTR("Timezone: %s\r\n"), ntp.timezone);
debugger->printf_P(PSTR("Server: %s\r\n"), ntp.server);
debugger->printf_P(PSTR("DHCP: %s\r\n"), ntp.dhcp ? "Yes" : "No");
}
debugger->println("");
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
EntsoeConfig entsoe;
if(getEntsoeConfig(entsoe)) {
if(strlen(entsoe.area) > 0) {
debugger->println("--ENTSO-E configuration--");
debugger->printf("Area: %s\r\n", entsoe.area);
debugger->printf("Currency: %s\r\n", entsoe.currency);
debugger->printf("Multiplier: %f\r\n", entsoe.multiplier / 1000.0);
debugger->printf("Token: %s\r\n", entsoe.token);
debugger->println(F("--ENTSO-E configuration--"));
debugger->printf_P(PSTR("Area: %s\r\n"), entsoe.area);
debugger->printf_P(PSTR("Currency: %s\r\n"), entsoe.currency);
debugger->printf_P(PSTR("Multiplier: %f\r\n"), entsoe.multiplier / 1000.0);
debugger->printf_P(PSTR("Token: %s\r\n"), entsoe.token);
}
debugger->println("");
debugger->println(F(""));
delay(10);
Serial.flush();
debugger->flush();
}
debugger->printf("Temp sensor count: %i\r\n", this->getTempSensorCount());
debugger->printf_P(PSTR("Temp sensor count: %i\r\n"), this->getTempSensorCount());
debugger->println("-----------------------------------------------");
debugger->println(F("-----------------------------------------------"));
}

View File

@@ -22,7 +22,8 @@ void fromHex(uint8_t *out, String in, uint16_t size) {
}
}
void stripNonAscii(uint8_t* in, uint16_t size, bool extended) {
bool stripNonAscii(uint8_t* in, uint16_t size, bool extended) {
bool ret = false;
for(uint16_t i = 0; i < size; i++) {
if(in[i] == 0) { // Clear the rest with null-terminator
memset(in+i, 0, size-i);
@@ -30,9 +31,12 @@ void stripNonAscii(uint8_t* in, uint16_t size, bool extended) {
}
if(extended && (in[i] < 32 || in[i] == 127 || in[i] == 129 || in[i] == 141 || in[i] == 143 || in[i] == 144 || in[i] == 157)) {
memset(in+i, ' ', 1);
} else if(in[i] < 32 || in[i] > 126) {
ret = true;
} else if(!extended && (in[i] < 32 || in[i] > 126)) {
memset(in+i, ' ', 1);
ret = true;
}
}
memset(in+size-1, 0, 1); // Make sure the last character is null-terminator
return ret;
}

View File

@@ -9,6 +9,10 @@
#include "HwTools.h"
#include "EntsoeApi.h"
#if defined(ESP32)
#include <esp_task_wdt.h>
#endif
class AmsMqttHandler {
public:
AmsMqttHandler(MQTTClient* mqtt, char* buf) {
@@ -25,7 +29,9 @@ public:
protected:
MQTTClient* mqtt;
char* json;
uint16_t BufferSize = 1024;
uint16_t BufferSize = 2048;
bool loop();
};
#endif

View File

@@ -219,7 +219,7 @@ bool AmsData::isTwoPhase() {
}
int8_t AmsData::getLastError() {
return lastErrorCount > 3 ? lastError : 0;
return lastErrorCount > 2 ? lastError : 0;
}
void AmsData::setLastError(int8_t lastError) {

View File

@@ -18,32 +18,32 @@ void AmsDataStorage::setTimezone(Timezone* tz) {
bool AmsDataStorage::update(AmsData* data) {
if(isHappy()) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Data is up to date\n");
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Data is up to date\n"));
return false;
}
time_t now = time(nullptr);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Time is: %lu\n", (int32_t) now);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Time is: %lu\n"), (int32_t) now);
if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Timezone is missing\n");
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Timezone is missing\n"));
return false;
}
if(now < BUILD_EPOCH) {
if(data->getMeterTimestamp() > BUILD_EPOCH) {
now = data->getMeterTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using meter timestamp, which is: %lu\n", (int32_t) now);
debugger->printf_P(PSTR("(AmsDataStorage) Using meter timestamp, which is: %lu\n"), (int32_t) now);
}
} else if(data->getPackageTimestamp() > BUILD_EPOCH) {
now = data->getPackageTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using package timestamp, which is: %lu\n", (int32_t) now);
debugger->printf_P(PSTR("(AmsDataStorage) Using package timestamp, which is: %lu\n"), (int32_t) now);
}
}
}
if(now < BUILD_EPOCH) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Invalid time: %lu\n", (int32_t) now);
debugger->printf_P(PSTR("(AmsDataStorage) Invalid time: %lu\n"), (int32_t) now);
}
return false;
}
@@ -54,13 +54,13 @@ bool AmsDataStorage::update(AmsData* data) {
breakTime(now-3600, utcYesterday);
breakTime(tz->toLocal(now-3600), ltzYesterDay);
uint64_t importCounter = data->getActiveImportCounter() * 1000;
uint64_t exportCounter = data->getActiveExportCounter() * 1000;
uint32_t importCounter = data->getActiveImportCounter() * 1000;
uint32_t exportCounter = data->getActiveExportCounter() * 1000;
// Clear hours between last update and now
if(day.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for day plot, resetting\n");
debugger->printf_P(PSTR("(AmsDataStorage) Invalid future timestamp for day plot, resetting\n"));
}
day.activeImport = importCounter;
day.activeExport = exportCounter;
@@ -68,13 +68,13 @@ bool AmsDataStorage::update(AmsData* data) {
return true;
} else {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last day update: %lu\n", (int32_t) day.lastMeterReadTime);
debugger->printf_P(PSTR("(AmsDataStorage) Last day update: %lu\n"), (int32_t) day.lastMeterReadTime);
}
tmElements_t last;
breakTime(day.lastMeterReadTime, last);
for(int i = last.Hour; i < utc.Hour; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Clearing hour: %d\n", i);
debugger->printf_P(PSTR("(AmsDataStorage) Clearing hour: %d\n"), i);
}
setHourImport(i, 0);
setHourExport(i, 0);
@@ -84,20 +84,20 @@ bool AmsDataStorage::update(AmsData* data) {
// Clear days between last update and now
if(month.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for month plot, resetting\n");
debugger->printf_P(PSTR("(AmsDataStorage) Invalid future timestamp for month plot, resetting\n"));
}
month.activeImport = importCounter;
month.activeExport = exportCounter;
month.lastMeterReadTime = now;
} else {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month update: %lu\n", (int32_t) month.lastMeterReadTime);
debugger->printf_P(PSTR("(AmsDataStorage) Last month update: %lu\n"), (int32_t) month.lastMeterReadTime);
}
tmElements_t last;
breakTime(tz->toLocal(month.lastMeterReadTime), last);
for(int i = last.Day; i < ltz.Day; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Clearing day: %d\n", i);
debugger->printf_P(PSTR("(AmsDataStorage) Clearing day: %d\n"), i);
}
setDayImport(i, 0);
setDayExport(i, 0);
@@ -105,7 +105,7 @@ bool AmsDataStorage::update(AmsData* data) {
}
if(data->getListType() < 3) {
debugger->printf("(AmsDataStorage) Not enough data in list type: %d\n", data->getListType());
debugger->printf_P(PSTR("(AmsDataStorage) Not enough data in list type: %d\n"), data->getListType());
return false;
}
@@ -124,7 +124,7 @@ bool AmsDataStorage::update(AmsData* data) {
day.activeExport = exportCounter;
day.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Too long since last day update, clearing data\n");
debugger->printf_P(PSTR("(AmsDataStorage) Too long since last day update, clearing data\n"));
}
for(int i = 0; i<24; i++) {
setHourImport(i, 0);
@@ -136,7 +136,7 @@ bool AmsDataStorage::update(AmsData* data) {
setHourImport(utcYesterday.Hour, imp);
setHourExport(utcYesterday.Hour, exp);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(AmsDataStorage) Usage for hour %d: %d - %d\n", ltzYesterDay.Hour, imp, exp);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(AmsDataStorage) Usage for hour %d: %d - %d\n"), ltzYesterDay.Hour, imp, exp);
day.activeImport = importCounter;
day.activeExport = exportCounter;
day.lastMeterReadTime = now;
@@ -148,7 +148,7 @@ bool AmsDataStorage::update(AmsData* data) {
float epm = ex / mins;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Since last day update, minutes: %.1f, import: %d (%.2f/min), export: %d (%.2f/min)\n", mins, im, ipm, ex, epm);
debugger->printf_P(PSTR("(AmsDataStorage) Since last day update, minutes: %.1f, import: %d (%.2f/min), export: %d (%.2f/min)\n"), mins, im, ipm, ex, epm);
}
tmElements_t last;
@@ -167,7 +167,7 @@ bool AmsDataStorage::update(AmsData* data) {
setHourExport(last.Hour, exp);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f - %.1f (%lu)\n", last.Hour, imp, exp, (int32_t) cur);
debugger->printf_P(PSTR("(AmsDataStorage) Estimated usage for hour %u: %.1f - %.1f (%lu)\n"), last.Hour, imp, exp, (int32_t) cur);
}
day.activeImport += imp;
@@ -191,7 +191,7 @@ bool AmsDataStorage::update(AmsData* data) {
month.activeExport = exportCounter;
month.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Too long since last month update, clearing data\n");
debugger->printf_P(PSTR("(AmsDataStorage) Too long since last month update, clearing data\n"));
}
for(int i = 1; i<=31; i++) {
setDayImport(i, 0);
@@ -202,7 +202,7 @@ bool AmsDataStorage::update(AmsData* data) {
int32_t exp = exportCounter - month.activeExport;
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Usage for day %d: %d - %d\n", ltzYesterDay.Day, imp, exp);
debugger->printf_P(PSTR("(AmsDataStorage) Usage for day %d: %d - %d\n"), ltzYesterDay.Day, imp, exp);
}
setDayImport(ltzYesterDay.Day, imp);
@@ -216,7 +216,7 @@ bool AmsDataStorage::update(AmsData* data) {
breakTime(tz->toLocal(month.lastMeterReadTime), last);
month.lastMeterReadTime = month.lastMeterReadTime - (last.Hour * 3600) - (last.Minute * 60) - last.Second;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month read after resetting to midnight: %lu\n", (int32_t) month.lastMeterReadTime);
debugger->printf_P(PSTR("(AmsDataStorage) Last month read after resetting to midnight: %lu\n"), (int32_t) month.lastMeterReadTime);
}
float hrs = (now - month.lastMeterReadTime) / 3600.0;
@@ -226,7 +226,7 @@ bool AmsDataStorage::update(AmsData* data) {
float eph = ex / hrs;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Since last month update, hours: %.1f, import: %d (%.2f/hr), export: %d (%.2f/hr)\n", hrs, im, iph, ex, eph);
debugger->printf_P(PSTR("(AmsDataStorage) Since last month update, hours: %.1f, import: %d (%.2f/hr), export: %d (%.2f/hr)\n"), hrs, im, iph, ex, eph);
}
time_t stopAt = now - (ltz.Hour * 3600) - (ltz.Minute * 60) - ltz.Second;
@@ -241,7 +241,7 @@ bool AmsDataStorage::update(AmsData* data) {
setDayExport(last.Day, exp);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for day %u: %.1f - %.1f (%lu)\n", last.Day, imp, exp, (int32_t) cur);
debugger->printf_P(PSTR("(AmsDataStorage) Estimated usage for day %u: %.1f - %.1f (%lu)\n"), last.Day, imp, exp, (int32_t) cur);
}
month.activeImport += imp;
@@ -413,7 +413,7 @@ uint32_t AmsDataStorage::getDayExport(uint8_t day) {
bool AmsDataStorage::load() {
if(!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf("(AmsDataStorage) Unable to load LittleFS\n");
debugger->printf_P(PSTR("(AmsDataStorage) Unable to load LittleFS\n"));
}
return false;
}
@@ -437,15 +437,13 @@ bool AmsDataStorage::load() {
ret = ret && setMonthData(*month);
}
LittleFS.end();
return ret;
}
bool AmsDataStorage::save() {
if(!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf("(AmsDataStorage) Unable to load LittleFS\n");
debugger->printf_P(PSTR("(AmsDataStorage) Unable to load LittleFS\n"));
}
return false;
}
@@ -467,8 +465,6 @@ bool AmsDataStorage::save() {
}
file.close();
}
LittleFS.end();
return true;
}
@@ -559,17 +555,17 @@ bool AmsDataStorage::isDayHappy() {
tmElements_t tm, last;
if(now < day.lastMeterReadTime) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day data timestamp %lu < %lu\n", (int32_t) now, (int32_t) day.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Day data timestamp %lu < %lu\n"), (int32_t) now, (int32_t) day.lastMeterReadTime);
return false;
}
breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(day.lastMeterReadTime), last);
if(now-day.lastMeterReadTime > 3600) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day data timestamp age %lu - %lu > 3600\n", (int32_t) now, (int32_t) day.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Day data timestamp age %lu - %lu > 3600\n"), (int32_t) now, (int32_t) day.lastMeterReadTime);
return false;
}
if(tm.Hour > last.Hour) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day data hour of last timestamp %d > %d\n", tm.Hour, last.Hour);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Day data hour of last timestamp %d > %d\n"), tm.Hour, last.Hour);
return false;
}
@@ -578,7 +574,7 @@ bool AmsDataStorage::isDayHappy() {
bool AmsDataStorage::isMonthHappy() {
if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Timezone is missing\n");
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Timezone is missing\n"));
return false;
}
@@ -587,14 +583,14 @@ bool AmsDataStorage::isMonthHappy() {
tmElements_t tm, last;
if(now < month.lastMeterReadTime) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month data timestamp %lu < %lu\n", (int32_t) now, (int32_t) month.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Month data timestamp %lu < %lu\n"), (int32_t) now, (int32_t) month.lastMeterReadTime);
return false;
}
breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(month.lastMeterReadTime), last);
if(tm.Day > last.Day) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month data day of last timestamp %d > %d\n", tm.Day, last.Day);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Month data day of last timestamp %d > %d\n"), tm.Day, last.Day);
return false;
}

View File

@@ -0,0 +1,47 @@
#ifndef _CLOUDCONNECTOR_H
#define _CLOUDCONNECTOR_H
#include "RemoteDebug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/platform.h"
#include "mbedtls/net.h"
#include "mbedtls/esp_debug.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#include "mbedtls/rsa.h"
const unsigned char PUBLIC_KEY[] = \
"-----BEGIN PUBLIC KEY-----\n"\
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoIo0CSuuX3tAdF7KPssdlzJNX\n"\
"QryhgVV1rQIFPhHv3SxzyKtRrRM9s0CVfymcibhnEBXxxg3pxlGmwI/R6k7HHXJN\n"\
"lBsXzzDtZ/GHDVnw+xRakTfRT0Zt+xdJSH5xJNWq4EwpvJfjA22L1Nz4dKSpgWMx\n"\
"VRndAaXf0s7Q1XBz2wIDAQAB\n"\
"-----END PUBLIC KEY-----\0";
//const unsigned char PUBLIC_KEY[] = { 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xe8, 0x22, 0x8d, 0x02, 0x4a, 0xeb, 0x97, 0xde, 0xd0, 0x1d, 0x17, 0xb2, 0x8f, 0xb2, 0xc7, 0x65, 0xcc, 0x93, 0x57, 0x42, 0xbc, 0xa1, 0x81, 0x55, 0x75, 0xad, 0x02, 0x05, 0x3e, 0x11, 0xef, 0xdd, 0x2c, 0x73, 0xc8, 0xab, 0x51, 0xad, 0x13, 0x3d, 0xb3, 0x40, 0x95, 0x7f, 0x29, 0x9c, 0x89, 0xb8, 0x67, 0x10, 0x15, 0xf1, 0xc6, 0x0d, 0xe9, 0xc6, 0x51, 0xa6, 0xc0, 0x8f, 0xd1, 0xea, 0x4e, 0xc7, 0x1d, 0x72, 0x4d, 0x94, 0x1b, 0x17, 0xcf, 0x30, 0xed, 0x67, 0xf1, 0x87, 0x0d, 0x59, 0xf0, 0xfb, 0x14, 0x5a, 0x91, 0x37, 0xd1, 0x4f, 0x46, 0x6d, 0xfb, 0x17, 0x49, 0x48, 0x7e, 0x71, 0x24, 0xd5, 0xaa, 0xe0, 0x4c, 0x29, 0xbc, 0x97, 0xe3, 0x03, 0x6d, 0x8b, 0xd4, 0xdc, 0xf8, 0x74, 0xa4, 0xa9, 0x81, 0x63, 0x31, 0x55, 0x19, 0xdd, 0x01, 0xa5, 0xdf, 0xd2, 0xce, 0xd0, 0xd5, 0x70, 0x73, 0xdb, 0x02, 0x03, 0x01, 0x00, 0x01};
struct CloudData {
uint8_t type;
int16_t data;
} __attribute__((packed));
class CloudConnector {
public:
CloudConnector(RemoteDebug*);
void setup(const unsigned char * key);
void send();
private:
RemoteDebug* debugger;
unsigned char buf[4096];
mbedtls_rsa_context* rsa = nullptr;
void debugPrint(byte *buffer, int start, int length);
};
#endif

View File

@@ -0,0 +1,56 @@
#include "CloudConnector.h"
CloudConnector::CloudConnector(RemoteDebug* debugger) {
this->debugger = debugger;
mbedtls_pk_context pk;
mbedtls_pk_init(&pk);
int error_code = 0;
if((error_code = mbedtls_pk_parse_public_key(&pk, PUBLIC_KEY, sizeof(PUBLIC_KEY))) == 0){
debugger->printf("RSA public key OK\n");
rsa = mbedtls_pk_rsa(pk);
} else {
debugger->printf("RSA public key read error: ");
mbedtls_strerror(error_code, (char*) buf, 4096);
debugger->printf("%s\n", buf);
}
debugger->flush();
//send();
}
void CloudConnector::send() {
if(rsa != nullptr && mbedtls_rsa_check_pubkey(rsa) == 0) {
memset(buf, 0, 4096);
CloudData data = {65, 127};
unsigned char toEncrypt[4096] = {0};
debugger->println("RSA clear data: ");
debugPrint(toEncrypt, 0, 256);
mbedtls_rsa_rsaes_pkcs1_v15_encrypt(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, 256, toEncrypt, buf);
//byte hashResult[32];
//mbedtls_sha256(toEncrypt, strlen((char*) toEncrypt), hashResult, 0);
//int success = mbedtls_rsa_rsassa_pkcs1_v15_sign(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, strlen((char*) hashResult), hashResult, buf);
debugger->println("RSA encrypted data: ");
debugPrint(buf, 0, 256);
} else {
debugger->println("RSA key is invalid");
}
}
void CloudConnector::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print(F("0"));
debugger->print(buffer[i], HEX);
debugger->print(F(" "));
if ((i - start + 1) % 16 == 0)
debugger->println(F(""));
else if ((i - start + 1) % 4 == 0)
debugger->print(F(" "));
yield(); // Let other get some resources too
}
debugger->println(F(""));
}

View File

@@ -9,12 +9,12 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
}
if(energy > 0.0) {
char val[16];
snprintf(val, 16, "%.1f;%.1f", (data->getActiveImportPower()/1.0), energy*1000.0);
snprintf_P(val, 16, PSTR("%.1f;%.1f"), (data->getActiveImportPower()/1.0), energy*1000.0);
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.elidx,
val
);
ret = mqtt->publish("domoticz/in", json);
ret = mqtt->publish(F("domoticz/in"), json);
}
}
@@ -23,22 +23,22 @@ bool DomoticzMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyA
if (config.vl1idx > 0){
char val[16];
snprintf(val, 16, "%.2f", data->getL1Voltage());
snprintf_P(val, 16, PSTR("%.2f"), data->getL1Voltage());
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.vl1idx,
val
);
ret |= mqtt->publish("domoticz/in", json);
ret |= mqtt->publish(F("domoticz/in"), json);
}
if (config.vl2idx > 0){
char val[16];
snprintf(val, 16, "%.2f", data->getL2Voltage());
snprintf_P(val, 16, PSTR("%.2f"), data->getL2Voltage());
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.vl2idx,
val
);
ret |= mqtt->publish("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("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("domoticz/in", json);
ret |= mqtt->publish(F("domoticz/in"), json);
}
return ret;
}

View File

@@ -54,24 +54,24 @@ public:
bool save();
bool isInitialized();
double getUseThisHour();
double getUseToday();
double getUseThisMonth();
float getUseThisHour();
float getUseToday();
float getUseThisMonth();
double getProducedThisHour();
double getProducedToday();
double getProducedThisMonth();
float getProducedThisHour();
float getProducedToday();
float getProducedThisMonth();
double getCostThisHour();
double getCostToday();
double getCostYesterday();
double getCostThisMonth();
float getCostThisHour();
float getCostToday();
float getCostYesterday();
float getCostThisMonth();
uint16_t getCostLastMonth();
double getIncomeThisHour();
double getIncomeToday();
double getIncomeYesterday();
double getIncomeThisMonth();
float getIncomeThisHour();
float getIncomeToday();
float getIncomeYesterday();
float getIncomeThisMonth();
uint16_t getIncomeLastMonth();
float getMonthMax();
@@ -81,8 +81,8 @@ public:
EnergyAccountingData getData();
void setData(EnergyAccountingData&);
void setFixedPrice(double price);
double getPriceForHour(uint8_t h);
void setFixedPrice(float price);
float getPriceForHour(uint8_t h);
private:
RemoteDebug* debugger = NULL;
@@ -93,10 +93,10 @@ private:
EnergyAccountingConfig *config = NULL;
Timezone *tz = NULL;
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
double use = 0, costHour = 0, costDay = 0;
double produce = 0, incomeHour = 0, incomeDay = 0;
float use = 0, costHour = 0, costDay = 0;
float produce = 0, incomeHour = 0, incomeDay = 0;
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
double fixedPrice = 0;
float fixedPrice = 0;
void calcDayCost();
bool updateMax(uint16_t val, uint8_t day);

View File

@@ -35,7 +35,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return false;
if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Timezone is missing\n");
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Timezone is missing\n"));
return false;
}
@@ -46,9 +46,9 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(!init) {
currentHour = local.Hour;
currentDay = local.Day;
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing data at %lu\n", (int32_t) now);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EnergyAccounting) Initializing data at %lu\n"), (int32_t) now);
if(!load()) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Unable to load existing data\n");
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) Unable to load existing data\n"));
data = { 5, local.Month,
0, 0, 0, // Cost
0, 0, 0, // Income
@@ -60,21 +60,22 @@ bool EnergyAccounting::update(AmsData* amsData) {
};
} else if(debugger->isActive(RemoteDebug::DEBUG)) {
for(uint8_t i = 0; i < 5; i++) {
debugger->printf("(EnergyAccounting) Peak hour from day %d: %d\n", data.peaks[i].day, data.peaks[i].value*10);
debugger->printf_P(PSTR("(EnergyAccounting) Peak hour from day %d: %d\n"), data.peaks[i].day, data.peaks[i].value*10);
}
debugger->printf("(EnergyAccounting) Loaded cost yesterday: %.2f, this month: %d, last month: %d\n", data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth);
debugger->printf("(EnergyAccounting) Loaded income yesterday: %.2f, this month: %d, last month: %d\n", data.incomeYesterday / 10.0, data.incomeThisMonth, data.incomeLastMonth);
debugger->printf_P(PSTR("(EnergyAccounting) Loaded cost yesterday: %.2f, this month: %d, last month: %d\n"), data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth);
debugger->printf_P(PSTR("(EnergyAccounting) Loaded income yesterday: %.2f, this month: %d, last month: %d\n"), data.incomeYesterday / 10.0, data.incomeThisMonth, data.incomeLastMonth);
}
init = true;
}
if(!initPrice && eapi != NULL && getPriceForHour(0) != ENTSOE_NO_VALUE) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing prices at %lu\n", (int32_t) now);
float price = getPriceForHour(0);
if(!initPrice && price != ENTSOE_NO_VALUE) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EnergyAccounting) Initializing prices at %lu\n"), (int32_t) now);
calcDayCost();
}
if(local.Hour != currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New local hour %d\n", local.Hour);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) New local hour %d\n"), local.Hour);
tmElements_t oneHrAgo, oneHrAgoLocal;
breakTime(now-3600, oneHrAgo);
@@ -94,7 +95,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
incomeHour = 0;
if(local.Day != currentDay) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New day %d\n", local.Day);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) New day %d\n"), local.Day);
data.costYesterday = costDay * 10;
data.costThisMonth += costDay;
costDay = 0;
@@ -108,7 +109,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
}
if(local.Month != data.month) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New month %d\n", local.Month);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) New month %d\n"), local.Month);
data.costLastMonth = data.costThisMonth;
data.costThisMonth = 0;
data.incomeLastMonth = data.incomeThisMonth;
@@ -127,32 +128,30 @@ bool EnergyAccounting::update(AmsData* amsData) {
float kwhe = (amsData->getActiveExportPower() * (((float) ms) / 3600000.0)) / 1000.0;
lastUpdateMillis = amsData->getLastUpdateMillis();
if(kwhi > 0) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh import\n", kwhi);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Adding %.4f kWh import\n"), kwhi);
use += kwhi;
if(getPriceForHour(0) != ENTSOE_NO_VALUE) {
float price = getPriceForHour(0);
if(price != ENTSOE_NO_VALUE) {
float cost = price * kwhi;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", cost / 100.0, eapi->getCurrency());
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), cost / 100.0, eapi->getCurrency());
costHour += cost;
costDay += cost;
}
}
if(kwhe > 0) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Adding %.4f kWh export\n", kwhe);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Adding %.4f kWh export\n"), kwhe);
produce += kwhe;
if(getPriceForHour(0) != ENTSOE_NO_VALUE) {
float price = getPriceForHour(0);
if(price != ENTSOE_NO_VALUE) {
float income = price * kwhe;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) and %.4f %s\n", income / 100.0, eapi->getCurrency());
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), income / 100.0, eapi->getCurrency());
incomeHour += income;
incomeDay += income;
}
}
if(config != NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) calculating threshold, currently at %d\n", currentThresholdIdx);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) calculating threshold, currently at %d\n"), currentThresholdIdx);
while(getMonthMax() > config->thresholds[currentThresholdIdx] && currentThresholdIdx < 10) currentThresholdIdx++;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) new threshold %d\n", currentThresholdIdx);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) new threshold %d\n"), currentThresholdIdx);
}
return ret;
@@ -163,12 +162,12 @@ void EnergyAccounting::calcDayCost() {
tmElements_t local, utc;
breakTime(tz->toLocal(now), local);
if(eapi != NULL && getPriceForHour(0) != ENTSOE_NO_VALUE) {
if(getPriceForHour(0) != ENTSOE_NO_VALUE) {
if(initPrice) {
costDay = 0;
incomeDay = 0;
}
for(int i = 0; i < currentHour; i++) {
for(uint8_t i = 0; i < currentHour; i++) {
float price = getPriceForHour(i - local.Hour);
if(price == ENTSOE_NO_VALUE) break;
breakTime(now - ((local.Hour - i) * 3600), utc);
@@ -182,75 +181,75 @@ void EnergyAccounting::calcDayCost() {
}
}
double EnergyAccounting::getUseThisHour() {
float EnergyAccounting::getUseThisHour() {
return use;
}
double EnergyAccounting::getUseToday() {
float EnergyAccounting::getUseToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0;
if(tz == NULL) return 0.0;
tmElements_t utc, local;
breakTime(tz->toLocal(now), local);
for(int i = 0; i < currentHour; i++) {
for(uint8_t i = 0; i < currentHour; i++) {
breakTime(now - ((local.Hour - i) * 3600), utc);
ret += ds->getHourImport(utc.Hour) / 1000.0;
}
return ret + getUseThisHour();
}
double EnergyAccounting::getUseThisMonth() {
float EnergyAccounting::getUseThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0;
float ret = 0;
for(int i = 0; i < currentDay; i++) {
for(uint8_t i = 0; i < currentDay; i++) {
ret += ds->getDayImport(i) / 1000.0;
}
return ret + getUseToday();
}
double EnergyAccounting::getProducedThisHour() {
float EnergyAccounting::getProducedThisHour() {
return produce;
}
double EnergyAccounting::getProducedToday() {
float EnergyAccounting::getProducedToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0;
tmElements_t utc, local;
breakTime(tz->toLocal(now), local);
for(int i = 0; i < currentHour; i++) {
for(uint8_t i = 0; i < currentHour; i++) {
breakTime(now - ((local.Hour - i) * 3600), utc);
ret += ds->getHourExport(utc.Hour) / 1000.0;
}
return ret + getProducedThisHour();
}
double EnergyAccounting::getProducedThisMonth() {
float EnergyAccounting::getProducedThisMonth() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0;
float ret = 0;
for(int i = 0; i < currentDay; i++) {
for(uint8_t i = 0; i < currentDay; i++) {
ret += ds->getDayExport(i) / 1000.0;
}
return ret + getProducedToday();
}
double EnergyAccounting::getCostThisHour() {
float EnergyAccounting::getCostThisHour() {
return costHour;
}
double EnergyAccounting::getCostToday() {
float EnergyAccounting::getCostToday() {
return costDay;
}
double EnergyAccounting::getCostYesterday() {
float EnergyAccounting::getCostYesterday() {
return data.costYesterday / 10.0;
}
double EnergyAccounting::getCostThisMonth() {
float EnergyAccounting::getCostThisMonth() {
return data.costThisMonth + getCostToday();
}
@@ -258,19 +257,19 @@ uint16_t EnergyAccounting::getCostLastMonth() {
return data.costLastMonth;
}
double EnergyAccounting::getIncomeThisHour() {
float EnergyAccounting::getIncomeThisHour() {
return incomeHour;
}
double EnergyAccounting::getIncomeToday() {
float EnergyAccounting::getIncomeToday() {
return incomeDay;
}
double EnergyAccounting::getIncomeYesterday() {
float EnergyAccounting::getIncomeYesterday() {
return data.incomeYesterday / 10.0;
}
double EnergyAccounting::getIncomeThisMonth() {
float EnergyAccounting::getIncomeThisMonth() {
return data.incomeThisMonth + getIncomeToday();
}
@@ -353,7 +352,7 @@ EnergyAccountingPeak EnergyAccounting::getPeak(uint8_t num) {
bool EnergyAccounting::load() {
if(!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf("(EnergyAccounting) Unable to load LittleFS\n");
debugger->printf_P(PSTR("(EnergyAccounting) Unable to load LittleFS\n"));
}
return false;
}
@@ -364,7 +363,7 @@ bool EnergyAccounting::load() {
char buf[file.size()];
file.readBytes(buf, file.size());
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Data version %d\n", buf[0]);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EnergyAccounting) Data version %d\n"), buf[0]);
if(buf[0] == 5) {
EnergyAccountingData* data = (EnergyAccountingData*) buf;
memcpy(&this->data, data, sizeof(this->data));
@@ -429,25 +428,23 @@ bool EnergyAccounting::load() {
this->data.peaks[0].value = data->maxHour;
ret = true;
} else {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf("(EnergyAccounting) Unknown version\n");
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf_P(PSTR("(EnergyAccounting) Unknown version\n"));
ret = false;
}
}
file.close();
} else {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf("(EnergyAccounting) File not found\n");
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf_P(PSTR("(EnergyAccounting) File not found\n"));
}
LittleFS.end();
return ret;
}
bool EnergyAccounting::save() {
if(!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf("(EnergyAccounting) Unable to load LittleFS\n");
debugger->printf_P(PSTR("(EnergyAccounting) Unable to load LittleFS\n"));
}
return false;
}
@@ -460,8 +457,6 @@ bool EnergyAccounting::save() {
}
file.close();
}
LittleFS.end();
return true;
}
@@ -477,7 +472,7 @@ bool EnergyAccounting::updateMax(uint16_t val, uint8_t day) {
for(uint8_t i = 0; i < 5; i++) {
if(data.peaks[i].day == day || data.peaks[i].day == 0) {
if(val > data.peaks[i].value) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Adding new max %d for day %d which is larger than %d\n", val*10, day, data.peaks[i].value*10);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) Adding new max %d for day %d which is larger than %d\n"), val*10, day, data.peaks[i].value*10);
data.peaks[i].day = day;
data.peaks[i].value = val;
return true;
@@ -496,7 +491,7 @@ bool EnergyAccounting::updateMax(uint16_t val, uint8_t day) {
}
}
if(idx < 5) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Adding new max %d for day %d\n", val*10, day);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EnergyAccounting) Adding new max %d for day %d\n"), val*10, day);
data.peaks[idx].value = val;
data.peaks[idx].day = day;
return true;
@@ -504,11 +499,11 @@ bool EnergyAccounting::updateMax(uint16_t val, uint8_t day) {
return false;
}
void EnergyAccounting::setFixedPrice(double price) {
void EnergyAccounting::setFixedPrice(float price) {
this->fixedPrice = price;
}
double EnergyAccounting::getPriceForHour(uint8_t h) {
float EnergyAccounting::getPriceForHour(uint8_t h) {
if(fixedPrice > 0.0) return fixedPrice;
if(eapi == NULL) return ENTSOE_NO_VALUE;
return eapi->getValueForHour(h);

View File

@@ -38,6 +38,7 @@ private:
uint8_t currentDay = 0, currentHour = 0;
uint8_t tomorrowFetchMinute = 15; // How many minutes over 13:00 should it fetch prices
uint8_t nextFetchDelayMinutes = 15;
uint64_t lastTodayFetch = 0;
uint64_t lastTomorrowFetch = 0;
uint64_t lastCurrencyFetch = 0;
@@ -61,8 +62,6 @@ private:
bool retrieve(const char* url, Stream* doc);
float getCurrencyMultiplier(const char* from, const char* to, time_t t);
void printD(String fmt, ...);
void printE(String fmt, ...);
void debugPrint(byte *buffer, int start, int length);
};
#endif

View File

@@ -92,8 +92,8 @@ float EntsoeApi::getValueForHour(time_t ts, int8_t hour) {
if(pos >= 48)
return ENTSOE_NO_VALUE;
double value = ENTSOE_NO_VALUE;
double multiplier = config->multiplier / 1000.0;
float value = ENTSOE_NO_VALUE;
float multiplier = config->multiplier / 1000.0;
if(pos >= hoursToday) {
if(tomorrow == NULL)
return ENTSOE_NO_VALUE;
@@ -158,7 +158,7 @@ bool EntsoeApi::loop() {
}
if(currentDay != tm.Day) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lu\n", t);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EntsoeApi) Rotating price objects at %lu\n"), t);
if(today != NULL) delete today;
if(tomorrow != NULL) {
today = tomorrow;
@@ -172,14 +172,17 @@ bool EntsoeApi::loop() {
return today != NULL; // Only trigger MQTT publish if we have todays prices.
}
bool readyToFetchForTomorrow = tomorrow == NULL && (tm.Hour > 13 || (tm.Hour == 13 && tm.Minute >= tomorrowFetchMinute)) && (lastTomorrowFetch == 0 || now - lastTomorrowFetch > 900000);
bool readyToFetchForTomorrow = tomorrow == NULL && (tm.Hour > 13 || (tm.Hour == 13 && tm.Minute >= tomorrowFetchMinute)) && (lastTomorrowFetch == 0 || now - lastTomorrowFetch > (nextFetchDelayMinutes*60000));
if(today == NULL && (lastTodayFetch == 0 || now - lastTodayFetch > 60000)) {
if(today == NULL && (lastTodayFetch == 0 || now - lastTodayFetch > (nextFetchDelayMinutes*60000))) {
try {
lastTodayFetch = now;
today = fetchPrices(t);
} catch(const std::exception& e) {
if(lastError == 0) lastError = 900;
if(lastError == 0) {
lastError = 900;
nextFetchDelayMinutes = 60;
}
today = NULL;
}
return today != NULL && !readyToFetchForTomorrow; // Only trigger MQTT publish if we have todays prices and we are not immediately ready to fetch price for tomorrow.
@@ -192,7 +195,10 @@ bool EntsoeApi::loop() {
lastTomorrowFetch = now;
tomorrow = fetchPrices(t+SECS_PER_DAY);
} catch(const std::exception& e) {
if(lastError == 0) lastError = 900;
if(lastError == 0) {
lastError = 900;
nextFetchDelayMinutes = 60;
}
tomorrow = NULL;
}
return tomorrow != NULL;
@@ -204,7 +210,7 @@ bool EntsoeApi::loop() {
bool EntsoeApi::retrieve(const char* url, Stream* doc) {
#if defined(ESP32)
if(http.begin(url)) {
printD("Connection established");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Connection established\n"));
#if defined(ESP32)
esp_task_wdt_reset();
@@ -221,16 +227,24 @@ bool EntsoeApi::retrieve(const char* url, Stream* doc) {
#endif
if(status == HTTP_CODE_OK) {
printD("Receiving data");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Receiving data\n"));
http.writeToStream(doc);
http.end();
lastError = 0;
nextFetchDelayMinutes = 1;
return true;
} else {
lastError = status;
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf("(EntsoeApi) Communication error, returned status: %d\n", status);
printE(http.errorToString(status));
printD(http.getString());
if(status == 429) {
nextFetchDelayMinutes = 60;
} else if(status == 404) {
nextFetchDelayMinutes = 180;
} else {
nextFetchDelayMinutes = 30;
}
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("(EntsoeApi) Communication error, returned status: %d\n"), status);
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(http.errorToString(status).c_str());
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(http.getString().c_str());
http.end();
return false;
@@ -258,18 +272,18 @@ float EntsoeApi::getCurrencyMultiplier(const char* from, const char* to, time_t
ESP.wdtFeed();
#endif
snprintf(buf, BufferSize, "https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1", from);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Retrieving %s to NOK conversion\n", from);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", buf);
snprintf_P(buf, BufferSize, PSTR("https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1"), from);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EntsoeApi) Retrieving %s to NOK conversion\n"), from);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) url: %s\n"), buf);
if(retrieve(buf, &p)) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) got exchange rate %.4f\n", p.getValue());
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) got exchange rate %.4f\n"), p.getValue());
currencyMultiplier = p.getValue();
if(strncmp(to, "NOK", 3) != 0) {
snprintf(buf, BufferSize, "https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1", to);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Retrieving %s to NOK conversion\n", to);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", buf);
snprintf_P(buf, BufferSize, PSTR("https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1"), to);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EntsoeApi) Retrieving %s to NOK conversion\n"), to);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) url: %s\n"), buf);
if(retrieve(buf, &p)) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) got exchange rate %.4f\n", p.getValue());
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) got exchange rate %.4f\n"), p.getValue());
currencyMultiplier /= p.getValue();
} else {
return 0;
@@ -278,7 +292,7 @@ float EntsoeApi::getCurrencyMultiplier(const char* from, const char* to, time_t
} else {
return 0;
}
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) Resulting currency multiplier: %.4f\n", currencyMultiplier);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) Resulting currency multiplier: %.4f\n"), currencyMultiplier);
tmElements_t tm;
breakTime(t, tm);
lastCurrencyFetch = now + (SECS_PER_DAY * 1000) - (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000);
@@ -296,8 +310,8 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
breakTime(tz->toUTC(e1), d1); // To get day and hour for CET/CEST at UTC midnight
breakTime(tz->toUTC(e2), d2);
snprintf(buf, BufferSize, "%s?securityToken=%s&documentType=A44&periodStart=%04d%02d%02d%02d%02d&periodEnd=%04d%02d%02d%02d%02d&in_Domain=%s&out_Domain=%s",
"https://web-api.tp.entsoe.eu/api", getToken(),
snprintf_P(buf, BufferSize, PSTR("https://web-api.tp.entsoe.eu/api?securityToken=%s&documentType=A44&periodStart=%04d%02d%02d%02d%02d&periodEnd=%04d%02d%02d%02d%02d&in_Domain=%s&out_Domain=%s"),
getToken(),
d1.Year+1970, d1.Month, d1.Day, d1.Hour, 00,
d2.Year+1970, d2.Month, d2.Day, d2.Hour, 00,
config->area, config->area);
@@ -308,8 +322,8 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
ESP.wdtFeed();
#endif
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Fetching prices for %d.%d.%d\n", tm.Day, tm.Month, tm.Year+1970);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", buf);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("(EntsoeApi) Fetching prices for %d.%d.%d\n"), tm.Day, tm.Month, tm.Year+1970);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) url: %s\n"), buf);
EntsoeA44Parser a44;
if(retrieve(buf, &a44) && a44.getPoint(0) != ENTSOE_NO_VALUE) {
PricesContainer* ret = new PricesContainer();
@@ -320,8 +334,7 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
}
} else if(hub) {
String data;
snprintf(buf, BufferSize, "%s/%s/%d/%d/%d?currency=%s",
"http://hub.amsleser.no/hub/price",
snprintf_P(buf, BufferSize, PSTR("http://hub.amsleser.no/hub/price/%s/%d/%d/%d?currency=%s"),
config->area,
tm.Year+1970,
tm.Month,
@@ -344,26 +357,26 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
#endif
if(status == HTTP_CODE_OK) {
printD("Receiving data");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Receiving data\n"));
data = http.getString();
http.end();
uint8_t* content = (uint8_t*) (data.c_str());
if(debugger->isActive(RemoteDebug::DEBUG)) {
printD("Received content for prices:");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received content for prices:\n"));
debugPrint(content, 0, data.length());
}
DataParserContext ctx;
DataParserContext ctx = {0,0,0,0};
ctx.length = data.length();
GCMParser gcm(key, auth);
int8_t gcmRet = gcm.parse(content, ctx);
if(debugger->isActive(RemoteDebug::DEBUG)) {
printD("Decrypted content for prices:");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Decrypted content for prices:\n"));
debugPrint(content, 0, data.length());
}
if(gcmRet > 0) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) Price data starting at: %d\n", gcmRet);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("(EntsoeApi) Price data starting at: %d\n"), gcmRet);
PricesContainer* ret = new PricesContainer();
for(uint8_t i = 0; i < 25; i++) {
ret->points[i] = ENTSOE_NO_VALUE;
@@ -373,16 +386,25 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
ret->points[i] = ntohl(ret->points[i]);
}
lastError = 0;
nextFetchDelayMinutes = 1;
return ret;
} else {
lastError = gcmRet;
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf("(EntsoeApi) Error code while decrypting prices: %d\n", gcmRet);
nextFetchDelayMinutes = 60;
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("(EntsoeApi) Error code while decrypting prices: %d\n"), gcmRet);
}
} else {
lastError = status;
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf("(EntsoeApi) Communication error, returned status: %d\n", status);
printE(http.errorToString(status));
printD(http.getString());
if(status == 429) {
nextFetchDelayMinutes = 60;
} else if(status == 404) {
nextFetchDelayMinutes = 180;
} else {
nextFetchDelayMinutes = 30;
}
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("(EntsoeApi) Communication error, returned status: %d\n"), status);
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(http.errorToString(status).c_str());
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(http.getString().c_str());
http.end();
}
@@ -391,34 +413,20 @@ PricesContainer* EntsoeApi::fetchPrices(time_t t) {
return NULL;
}
void EntsoeApi::printD(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(String("(EntsoeApi)" + fmt + "\n").c_str(), args);
va_end(args);
}
void EntsoeApi::printE(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(String("(EntsoeApi)" + fmt + "\n").c_str(), args);
va_end(args);
}
void EntsoeApi::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print("0");
debugger->print(F("0"));
debugger->print(buffer[i], HEX);
debugger->print(" ");
debugger->print(F(" "));
if ((i - start + 1) % 16 == 0)
debugger->println("");
debugger->println(F(""));
else if ((i - start + 1) % 4 == 0)
debugger->print(" ");
debugger->print(F(" "));
yield(); // Let other get some resources too
}
debugger->println("");
debugger->println(F(""));
}
int16_t EntsoeApi::getLastError() {

View File

@@ -57,20 +57,8 @@ public:
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
void publishSensor(const HomeAssistantSensor& sensor);
void publishList1Sensors();
void publishList1ExportSensors();
void publishList2Sensors();
void publishList2ExportSensors();
void publishList3Sensors();
void publishList3ExportSensors();
void publishList4Sensors();
void publishList4ExportSensors();
void publishRealtimeSensors(EnergyAccounting* ea, EntsoeApi* eapi);
void publishRealtimeExportSensors(EnergyAccounting* ea, EntsoeApi* eapi);
void publishTemperatureSensor(uint8_t index, String id);
void publishPriceSensors(EntsoeApi* eapi);
void publishSystemSensors();
protected:
bool loop();
private:
String deviceName;
@@ -90,6 +78,27 @@ private:
String topic;
HwTools* hw;
bool publishList1(AmsData* data, EnergyAccounting* ea);
bool publishList2(AmsData* data, EnergyAccounting* ea);
bool publishList3(AmsData* data, EnergyAccounting* ea);
bool publishList4(AmsData* data, EnergyAccounting* ea);
String getMeterModel(AmsData* data);
bool publishRealtime(AmsData* data, EnergyAccounting* ea, EntsoeApi* eapi);
void publishSensor(const HomeAssistantSensor& sensor);
void publishList1Sensors();
void publishList1ExportSensors();
void publishList2Sensors();
void publishList2ExportSensors();
void publishList3Sensors();
void publishList3ExportSensors();
void publishList4Sensors();
void publishList4ExportSensors();
void publishRealtimeSensors(EnergyAccounting* ea, EntsoeApi* eapi);
void publishRealtimeExportSensors(EnergyAccounting* ea, EntsoeApi* eapi);
void publishTemperatureSensor(uint8_t index, String id);
void publishPriceSensors(EntsoeApi* eapi);
void publishSystemSensors();
String boardTypeToString(uint8_t b) {
switch(b) {
case 5:

View File

@@ -20,110 +20,136 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
return false;
if(data->getListType() >= 3) { // publish energy counts
publishList3Sensors();
if(data->getActiveExportCounter() > 0.0) publishList3ExportSensors();
snprintf_P(json, BufferSize, HA2_JSON,
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
data->getReactiveExportCounter(),
data->getMeterTimestamp()
);
mqtt->publish(topic + "/energy", json);
mqtt->loop();
delay(10);
publishList3(data, ea);
loop();
}
String meterModel = data->getMeterModel();
meterModel.replace("\\", "\\\\");
if(data->getListType() == 1) { // publish power counts
publishList1Sensors();
snprintf_P(json, BufferSize, HA1_JSON,
data->getActiveImportPower()
);
mqtt->publish(topic + "/power", json);
publishList1(data, ea);
} else if(data->getListType() <= 3) { // publish power counts and volts/amps
publishList2Sensors();
if(data->getActiveExportPower() > 0) publishList2ExportSensors();
snprintf_P(json, BufferSize, HA3_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
meterModel.c_str(),
data->getActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage()
);
mqtt->publish(topic + "/power", json);
publishList2(data, ea);
} else if(data->getListType() == 4) { // publish power counts and volts/amps/phase power and PF
publishList4Sensors();
if(data->getL1ActiveExportPower() > 0 || data->getL2ActiveExportPower() > 0 || data->getL3ActiveExportPower() > 0) publishList4ExportSensors();
snprintf_P(json, BufferSize, HA4_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
meterModel.c_str(),
data->getActiveImportPower(),
data->getL1ActiveImportPower(),
data->getL2ActiveImportPower(),
data->getL3ActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getL1ActiveExportPower(),
data->getL2ActiveExportPower(),
data->getL3ActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
data->getPowerFactor() == 0 ? 1 : data->getPowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor()
);
mqtt->publish(topic + "/power", json);
publishList4(data, ea);
}
loop();
if(ea->isInitialized()) {
publishRealtimeSensors(ea, eapi);
if(ea->getProducedThisHour() > 0.0 || ea->getProducedToday() > 0.0 || ea->getProducedThisMonth() > 0.0) publishRealtimeExportSensors(ea, eapi);
String peaks = "";
uint8_t peakCount = ea->getConfig()->hours;
if(peakCount > 5) peakCount = 5;
for(uint8_t i = 1; i <= peakCount; i++) {
if(!peaks.isEmpty()) peaks += ",";
peaks += String(ea->getPeak(i).value / 100.0, 2);
}
snprintf_P(json, BufferSize, REALTIME_JSON,
ea->getMonthMax(),
peaks.c_str(),
ea->getCurrentThreshold(),
ea->getUseThisHour(),
ea->getCostThisHour(),
ea->getProducedThisHour(),
ea->getIncomeThisHour(),
ea->getUseToday(),
ea->getCostToday(),
ea->getProducedToday(),
ea->getIncomeToday(),
ea->getUseThisMonth(),
ea->getCostThisMonth(),
ea->getProducedThisMonth(),
ea->getIncomeThisMonth()
);
mqtt->publish(topic + "/realtime", json);
publishRealtime(data, ea, eapi);
loop();
}
return true;
}
bool HomeAssistantMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
publishList1Sensors();
snprintf_P(json, BufferSize, HA1_JSON,
data->getActiveImportPower()
);
return mqtt->publish(topic + "/power", json);
}
bool HomeAssistantMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
publishList2Sensors();
if(data->getActiveExportPower() > 0) publishList2ExportSensors();
snprintf_P(json, BufferSize, HA3_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
data->getActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage()
);
return mqtt->publish(topic + "/power", json);
}
bool HomeAssistantMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
publishList3Sensors();
if(data->getActiveExportCounter() > 0.0) publishList3ExportSensors();
snprintf_P(json, BufferSize, HA2_JSON,
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
data->getReactiveExportCounter(),
data->getMeterTimestamp()
);
return mqtt->publish(topic + "/energy", json);
}
bool HomeAssistantMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
publishList4Sensors();
if(data->getL1ActiveExportPower() > 0 || data->getL2ActiveExportPower() > 0 || data->getL3ActiveExportPower() > 0) publishList4ExportSensors();
snprintf_P(json, BufferSize, HA4_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
data->getActiveImportPower(),
data->getL1ActiveImportPower(),
data->getL2ActiveImportPower(),
data->getL3ActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getL1ActiveExportPower(),
data->getL2ActiveExportPower(),
data->getL3ActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
data->getPowerFactor() == 0 ? 1 : data->getPowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor()
);
return mqtt->publish(topic + "/power", json);
}
String HomeAssistantMqttHandler::getMeterModel(AmsData* data) {
String meterModel = data->getMeterModel();
meterModel.replace("\\", "\\\\");
return meterModel;
}
bool HomeAssistantMqttHandler::publishRealtime(AmsData* data, EnergyAccounting* ea, EntsoeApi* eapi) {
publishRealtimeSensors(ea, eapi);
if(ea->getProducedThisHour() > 0.0 || ea->getProducedToday() > 0.0 || ea->getProducedThisMonth() > 0.0) publishRealtimeExportSensors(ea, eapi);
String peaks = "";
uint8_t peakCount = ea->getConfig()->hours;
if(peakCount > 5) peakCount = 5;
for(uint8_t i = 1; i <= peakCount; i++) {
if(!peaks.isEmpty()) peaks += ",";
peaks += String(ea->getPeak(i).value / 100.0, 2);
}
snprintf_P(json, BufferSize, REALTIME_JSON,
ea->getMonthMax(),
peaks.c_str(),
ea->getCurrentThreshold(),
ea->getUseThisHour(),
ea->getCostThisHour(),
ea->getProducedThisHour(),
ea->getIncomeThisHour(),
ea->getUseToday(),
ea->getCostToday(),
ea->getProducedToday(),
ea->getIncomeToday(),
ea->getUseThisMonth(),
ea->getCostThisMonth(),
ea->getProducedThisMonth(),
ea->getIncomeThisMonth()
);
return mqtt->publish(topic + "/realtime", json);
}
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
int count = hw->getTempSensorCount();
if(count < 2) return false;
@@ -138,18 +164,19 @@ bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwT
if(data != NULL) {
char* pos = buf+strlen(buf);
String id = toHex(data->address, 8);
snprintf(pos, 26, "\"%s\":%.2f,",
snprintf_P(pos, 26, PSTR("\"%s\":%.2f,"),
id.c_str(),
data->lastRead
);
data->changed = false;
publishTemperatureSensor(i+1, id);
delay(1);
}
}
char* pos = buf+strlen(buf);
snprintf_P(count == 0 ? pos : pos-1, 8, PSTR("}}"));
return mqtt->publish(topic + "/temperatures", buf);
bool ret = mqtt->publish(topic + "/temperatures", buf);
loop();
return ret;
}
bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
@@ -216,7 +243,6 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
memset(ts1hr, 0, 24);
if(min1hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf_P(ts1hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
@@ -225,7 +251,6 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
memset(ts3hr, 0, 24);
if(min3hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf_P(ts3hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
@@ -234,7 +259,6 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
memset(ts6hr, 0, 24);
if(min6hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf_P(ts6hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
@@ -286,7 +310,9 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
ts3hr,
ts6hr
);
return 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) {
@@ -305,7 +331,9 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, Energ
hw->getTemperature(),
VERSION
);
return mqtt->publish(topic + "/state", json);
bool ret = mqtt->publish(topic + "/state", json);
loop();
return ret;
}
void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor) {
@@ -333,14 +361,7 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor)
strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : ""
);
mqtt->publish(discoveryTopic + deviceUid + "_" + uid.c_str() + "/config", json, true, 0);
mqtt->loop();
delay(10);
#if defined(ESP32)
esp_task_wdt_reset();
#elif defined(ESP8266)
ESP.wdtFeed();
#endif
loop();
}
void HomeAssistantMqttHandler::publishList1Sensors() {
@@ -352,6 +373,7 @@ void HomeAssistantMqttHandler::publishList1Sensors() {
}
void HomeAssistantMqttHandler::publishList2Sensors() {
publishList1Sensors();
if(l2Init) return;
for(uint8_t i = 0; i < List2SensorCount; i++) {
publishSensor(List2Sensors[i]);
@@ -368,6 +390,7 @@ void HomeAssistantMqttHandler::publishList2ExportSensors() {
}
void HomeAssistantMqttHandler::publishList3Sensors() {
publishList2Sensors();
if(l3Init) return;
for(uint8_t i = 0; i < List3SensorCount; i++) {
publishSensor(List3Sensors[i]);
@@ -376,6 +399,7 @@ void HomeAssistantMqttHandler::publishList3Sensors() {
}
void HomeAssistantMqttHandler::publishList3ExportSensors() {
publishList2ExportSensors();
if(l3eInit) return;
for(uint8_t i = 0; i < List3ExportSensorCount; i++) {
publishSensor(List3ExportSensors[i]);
@@ -384,6 +408,7 @@ void HomeAssistantMqttHandler::publishList3ExportSensors() {
}
void HomeAssistantMqttHandler::publishList4Sensors() {
publishList3Sensors();
if(l4Init) return;
for(uint8_t i = 0; i < List4SensorCount; i++) {
publishSensor(List4Sensors[i]);
@@ -392,6 +417,7 @@ void HomeAssistantMqttHandler::publishList4Sensors() {
}
void HomeAssistantMqttHandler::publishList4ExportSensors() {
publishList3ExportSensors();
if(l4eInit) return;
for(uint8_t i = 0; i < List4ExportSensorCount; i++) {
publishSensor(List4ExportSensors[i]);
@@ -509,4 +535,16 @@ void HomeAssistantMqttHandler::publishSystemSensors() {
publishSensor(SystemSensors[i]);
}
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;
}

View File

@@ -37,13 +37,13 @@ struct AdcConfig {
class HwTools {
public:
void setup(GpioConfig*, AmsConfiguration*);
double getVcc();
float getVcc();
uint8_t getTempSensorCount();
TempSensorData* getTempSensorData(uint8_t);
bool updateTemperatures();
double getTemperature();
double getTemperatureAnalog();
double getTemperature(uint8_t address[8]);
float getTemperature();
float getTemperatureAnalog();
float getTemperature(uint8_t address[8]);
int getWifiRssi();
bool ledOn(uint8_t color);
bool ledOff(uint8_t color);

View File

@@ -213,8 +213,8 @@ void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) {
#endif
}
double HwTools::getVcc() {
double volts = 0.0;
float HwTools::getVcc() {
float volts = 0.0;
if(config->vccPin != 0xFF) {
#if defined(ESP32)
if(voltAdc.unit != 0xFF) {
@@ -257,7 +257,7 @@ double HwTools::getVcc() {
if(volts == 0.0) return 0.0;
if(config->vccResistorGnd > 0 && config->vccResistorVcc > 0) {
volts *= ((double) (config->vccResistorGnd + config->vccResistorVcc) / config->vccResistorGnd);
volts *= ((float) (config->vccResistorGnd + config->vccResistorVcc) / config->vccResistorGnd);
}
@@ -318,7 +318,7 @@ bool HwTools::updateTemperatures() {
}
tempSensors[sensorCount++] = data;
}
delay(10);
yield();
}
} else {
if(sensorCount > 0) {
@@ -348,10 +348,10 @@ bool HwTools::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) {
return true;
}
double HwTools::getTemperature() {
float HwTools::getTemperature() {
uint8_t c = 0;
double ret = 0;
double analogTemp = getTemperatureAnalog();
float ret = 0;
float analogTemp = getTemperatureAnalog();
if(analogTemp != DEVICE_DISCONNECTED_C) {
ret += analogTemp;
c++;
@@ -366,10 +366,10 @@ double HwTools::getTemperature() {
}
return c == 0 ? DEVICE_DISCONNECTED_C : ret/c;
}
double HwTools::getTemperatureAnalog() {
float HwTools::getTemperatureAnalog() {
if(config->tempAnalogSensorPin != 0xFF) {
float adcCalibrationFactor = 1.06587;
int volts = ((double) analogRead(config->tempAnalogSensorPin) / analogRange) * 3.3;
int volts = ((float) analogRead(config->tempAnalogSensorPin) / analogRange) * 3.3;
return ((volts * adcCalibrationFactor) - 0.4) / 0.0195;
}
return DEVICE_DISCONNECTED_C;

View File

@@ -15,9 +15,18 @@ public:
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
protected:
bool loop();
private:
String clientId;
String topic;
HwTools* hw;
bool publishList1(AmsData* data, EnergyAccounting* ea);
bool publishList2(AmsData* data, EnergyAccounting* ea);
bool publishList3(AmsData* data, EnergyAccounting* ea);
bool publishList4(AmsData* data, EnergyAccounting* ea);
String getMeterModel(AmsData* data);
};
#endif

View File

@@ -13,138 +13,161 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
if(topic.isEmpty() || !mqtt->connected())
return false;
bool ret = false;
if(data->getListType() == 1) {
ret = publishList1(data, ea);
} else if(data->getListType() == 2) {
ret = publishList2(data, ea);
} else if(data->getListType() == 3) {
ret = publishList3(data, ea);
} else if(data->getListType() == 4) {
ret = publishList4(data, ea);
}
loop();
return ret;
}
bool JsonMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON1_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getActiveImportPower(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
}
bool JsonMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON2_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
data->getActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
}
bool JsonMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON3_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
data->getActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
data->getReactiveExportCounter(),
data->getMeterTimestamp(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
}
bool JsonMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
snprintf_P(json, BufferSize, JSON4_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
data->getActiveImportPower(),
data->getL1ActiveImportPower(),
data->getL2ActiveImportPower(),
data->getL3ActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getL1ActiveExportPower(),
data->getL2ActiveExportPower(),
data->getL3ActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
data->getPowerFactor(),
data->getL1PowerFactor(),
data->getL2PowerFactor(),
data->getL3PowerFactor(),
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
data->getReactiveExportCounter(),
data->getMeterTimestamp(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
}
String JsonMqttHandler::getMeterModel(AmsData* data) {
String meterModel = data->getMeterModel();
meterModel.replace("\\", "\\\\");
if(data->getListType() == 1) {
snprintf_P(json, BufferSize, JSON1_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getActiveImportPower(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
} else if(data->getListType() == 2) {
snprintf_P(json, BufferSize, JSON2_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getListId().c_str(),
data->getMeterId().c_str(),
meterModel.c_str(),
data->getActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
} else if(data->getListType() == 3) {
snprintf_P(json, BufferSize, JSON3_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getListId().c_str(),
data->getMeterId().c_str(),
meterModel.c_str(),
data->getActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
data->getReactiveExportCounter(),
data->getMeterTimestamp(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
} else if(data->getListType() == 4) {
snprintf_P(json, BufferSize, JSON4_JSON,
WiFi.macAddress().c_str(),
clientId.c_str(),
(uint32_t) (millis64()/1000),
data->getPackageTimestamp(),
hw->getVcc(),
hw->getWifiRssi(),
hw->getTemperature(),
data->getListId().c_str(),
data->getMeterId().c_str(),
meterModel.c_str(),
data->getActiveImportPower(),
data->getL1ActiveImportPower(),
data->getL2ActiveImportPower(),
data->getL3ActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getL1ActiveExportPower(),
data->getL2ActiveExportPower(),
data->getL3ActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(),
data->getPowerFactor(),
data->getL1PowerFactor(),
data->getL2PowerFactor(),
data->getL3PowerFactor(),
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
data->getReactiveExportCounter(),
data->getMeterTimestamp(),
ea->getUseThisHour(),
ea->getUseToday(),
ea->getCurrentThreshold(),
ea->getMonthMax(),
ea->getProducedThisHour(),
ea->getProducedToday()
);
return mqtt->publish(topic, json);
}
return false;
return meterModel;
}
bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
@@ -153,23 +176,24 @@ bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw)
return false;
}
snprintf(json, 24, "{\"temperatures\":{");
snprintf_P(json, 24, PSTR("{\"temperatures\":{"));
for(int i = 0; i < count; i++) {
TempSensorData* data = hw->getTempSensorData(i);
if(data != NULL) {
char* pos = json+strlen(json);
snprintf(pos, 26, "\"%s\":%.2f,",
snprintf_P(pos, 26, PSTR("\"%s\":%.2f,"),
toHex(data->address, 8).c_str(),
data->lastRead
);
data->changed = false;
delay(1);
}
}
char* pos = json+strlen(json);
snprintf(count == 0 ? pos : pos-1, 8, "}}");
return mqtt->publish(topic, json);
snprintf_P(count == 0 ? pos : pos-1, 8, PSTR("}}"));
bool ret = mqtt->publish(topic, json);
loop();
return ret;
}
bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
@@ -234,28 +258,25 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
memset(ts1hr, 0, 24);
if(min1hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
sprintf_P(ts1hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
}
char ts3hr[24];
memset(ts3hr, 0, 24);
if(min3hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
sprintf_P(ts3hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
}
char ts6hr[24];
memset(ts6hr, 0, 24);
if(min6hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf(ts6hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
sprintf_P(ts6hr, PSTR("%04d-%02d-%02dT%02d:00:00Z"), tm.Year+1970, tm.Month, tm.Day, tm.Hour);
}
snprintf_P(json, BufferSize, JSONPRICES_JSON,
@@ -304,7 +325,9 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
ts3hr,
ts6hr
);
return mqtt->publish(topic, json);
bool ret = mqtt->publish(topic, json);
loop();
return ret;
}
bool JsonMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
@@ -320,5 +343,19 @@ bool JsonMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounti
hw->getTemperature(),
VERSION
);
return mqtt->publish(topic, json);
bool ret = mqtt->publish(topic, 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;
}

View File

@@ -14,8 +14,17 @@ public:
bool publishPrices(EntsoeApi*);
bool publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea);
protected:
bool loop();
private:
String topic;
bool full;
bool publishList1(AmsData* data, AmsData* meterState);
bool publishList2(AmsData* data, AmsData* meterState);
bool publishList3(AmsData* data, AmsData* meterState);
bool publishList4(AmsData* data, AmsData* meterState);
bool publishRealtime(EnergyAccounting* ea);
};
#endif

View File

@@ -11,103 +11,133 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
}
switch(data->getListType()) {
case 4:
if(full || meterState->getL1ActiveImportPower() != data->getL1ActiveImportPower()) {
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));
}
if(full || meterState->getL3ActiveImportPower() != data->getL3ActiveImportPower()) {
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));
}
if(full || meterState->getL2ActiveExportPower() != data->getL2ActiveExportPower()) {
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));
}
if(full || meterState->getPowerFactor() != data->getPowerFactor()) {
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));
}
if(full || meterState->getL2PowerFactor() != data->getL2PowerFactor()) {
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));
}
publishList4(data, meterState);
loop();
case 3:
// 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);
publishList3(data, meterState);
loop();
case 2:
// 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());
}
if(full || meterState->getMeterModel() != 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));
}
if(full || meterState->getL1Voltage() != data->getL1Voltage()) {
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));
}
if(full || meterState->getL2Voltage() != data->getL2Voltage()) {
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));
}
if(full || meterState->getL3Voltage() != data->getL3Voltage()) {
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()));
}
if(full || meterState->getActiveExportPower() != 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()));
}
publishList2(data, meterState);
loop();
case 1:
if(full || meterState->getActiveImportPower() != data->getActiveImportPower()) {
mqtt->publish(topic + "/meter/import/active", String(data->getActiveImportPower()));
}
publishList1(data, meterState);
loop();
}
if(ea->isInitialized()) {
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/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));
publishRealtime(ea);
loop();
}
return true;
}
bool RawMqttHandler::publishList1(AmsData* data, AmsData* meterState) {
if(full || meterState->getActiveImportPower() != data->getActiveImportPower()) {
mqtt->publish(topic + "/meter/import/active", String(data->getActiveImportPower()));
}
return true;
}
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());
}
if(full || meterState->getMeterModel() != 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));
}
if(full || meterState->getL1Voltage() != data->getL1Voltage()) {
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));
}
if(full || meterState->getL2Voltage() != data->getL2Voltage()) {
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));
}
if(full || meterState->getL3Voltage() != data->getL3Voltage()) {
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()));
}
if(full || meterState->getActiveExportPower() != 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()));
}
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);
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));
}
if(full || meterState->getL2ActiveImportPower() != data->getL2ActiveImportPower()) {
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));
}
if(full || meterState->getL1ActiveExportPower() != data->getL1ActiveExportPower()) {
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));
}
if(full || meterState->getL3ActiveExportPower() != data->getL3ActiveExportPower()) {
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));
}
if(full || meterState->getL1PowerFactor() != data->getL1PowerFactor()) {
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));
}
if(full || meterState->getL3PowerFactor() != data->getL3PowerFactor()) {
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));
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/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;
}
bool RawMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
uint8_t c = hw->getTempSensorCount();
for(int i = 0; i < c; i++) {
@@ -184,7 +214,6 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
char ts1hr[24];
if(min1hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
@@ -192,7 +221,6 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
char ts3hr[24];
if(min3hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
@@ -200,7 +228,6 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
char ts6hr[24];
if(min6hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
tmElements_t tm;
breakTime(ts, tm);
sprintf(ts6hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
@@ -251,4 +278,16 @@ bool RawMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccountin
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;
}

File diff suppressed because one or more lines are too long

View File

@@ -9,8 +9,8 @@
let hasCost = false;
let cols = 3
$: {
cols = currency ? 3 : 2;
hasCost = data && data.h && (data.h.c || data.d.c || data.m.c || data.h.i || data.d.i || data.m.i);
cols = hasCost ? 3 : 2;
}
</script>

View File

@@ -52,7 +52,9 @@
<g class="axis x-axis">
{#each config.x.ticks as point, i}
<g class="tick" transform="translate({xScale(i)},{heightAvailable})">
{#if barWidth > 20 || i%2 == 0}
<text x="{barWidth/2}" y="-4">{point.label}</text>
{/if}
</g>
{/each}
</g>

View File

@@ -8,5 +8,5 @@
{#if Math.abs(new Date().getTime()-timestamp.getTime()) < 300000 }
{`${zeropad(timestamp.getDate())}. ${monthnames[timestamp.getMonth()]} ${zeropad(timestamp.getHours())}:${zeropad(timestamp.getMinutes())}`}
{:else}
<span class="{fullTimeColor}">{`${zeropad(timestamp.getDate())}.${zeropad(timestamp.getMonth())}.${timestamp.getFullYear()} ${zeropad(timestamp.getHours())}:${zeropad(timestamp.getMinutes())}`}</span>
<span class="{fullTimeColor}">{`${zeropad(timestamp.getDate())}.${zeropad(timestamp.getMonth()+1)}.${timestamp.getFullYear()} ${zeropad(timestamp.getHours())}:${zeropad(timestamp.getMinutes())}`}</span>
{/if}

View File

@@ -26,6 +26,13 @@ let sysinfo = {
upgrading: false,
ui: {},
security: 0,
boot_reason: 0,
upgrade: {
x: -1,
e: 0,
f: null,
t: null
},
trying: null
};
export const sysinfoStore = writable(sysinfo);

View File

@@ -25,7 +25,7 @@
s.upgrading = true;
return s;
});
upgrade(nextVersion);
upgrade(nextVersion.tag_name);
}
}
}

View File

@@ -117,6 +117,12 @@ export function hanError(err) {
case -52: return "Decryption failed";
case -53: return "Encryption key invalid";
case 90: return "No HAN data received last 30s";
case 91: return "Serial break";
case 92: return "Serial buffer full";
case 93: return "Serial FIFO overflow";
case 94: return "Serial frame error";
case 95: return "Serial parity error";
case 96: return "RX error";
case 98: return "Exception in code, debugging necessary";
case 99: return "Autodetection failed";
}

View File

@@ -51,7 +51,7 @@
s.upgrading = true;
return s;
});
upgrade(nextVersion);
upgrade(nextVersion.tag_name);
}
}
}
@@ -109,7 +109,7 @@
{/if}
</div>
<div class="my-2">
Reason: {getResetReason(sysinfo)} ({sysinfo.boot_reason})
Reason: {getResetReason(sysinfo)} ({sysinfo.boot_reason}/{sysinfo.ex_cause})
</div>
{/if}
<div class="my-2">
@@ -155,6 +155,11 @@
<div class="my-2">
Installed version: {sysinfo.version}
</div>
{#if sysinfo.upgrade.t && sysinfo.upgrade.t != sysinfo.version}
<div class="my-2">
<div class="bd-yellow">Previous upgrade attempt ({sysinfo.upgrade.t}) does not match current version ({sysinfo.version}) [{sysinfo.upgrade.x}/{sysinfo.upgrade.e}]</div>
</div>
{/if}
{#if nextVersion}
<div class="my-2 flex">
Latest version:

View File

@@ -2,8 +2,8 @@ export function upgradeWarningText(board) {
return 'WARNING: ' + board + ' must be connected to an external power supply during firmware upgrade. Failure to do so may cause power-down during upload resulting in non-functioning unit.'
}
export async function upgrade() {
const response = await fetch('/upgrade', {
export async function upgrade(expected_version) {
const response = await fetch('/upgrade?expected_version='+expected_version, {
method: 'POST'
});
await response.json();

View File

@@ -17,18 +17,18 @@ export default defineConfig({
plugins: [svelte()],
server: {
proxy: {
"/data.json": "http://192.168.233.235",
"/energyprice.json": "http://192.168.233.235",
"/dayplot.json": "http://192.168.233.235",
"/monthplot.json": "http://192.168.233.235",
"/temperature.json": "http://192.168.233.235",
"/sysinfo.json": "http://192.168.233.235",
"/configuration.json": "http://192.168.233.235",
"/tariff.json": "http://192.168.233.235",
"/save": "http://192.168.233.235",
"/reboot": "http://192.168.233.235",
"/configfile": "http://192.168.233.235",
"/upgrade": "http://192.168.233.235"
"/data.json": "http://192.168.233.244",
"/energyprice.json": "http://192.168.233.244",
"/dayplot.json": "http://192.168.233.244",
"/monthplot.json": "http://192.168.233.244",
"/temperature.json": "http://192.168.233.244",
"/sysinfo.json": "http://192.168.233.244",
"/configuration.json": "http://192.168.233.244",
"/tariff.json": "http://192.168.233.244",
"/save": "http://192.168.233.244",
"/reboot": "http://192.168.233.244",
"/configfile": "http://192.168.233.244",
"/upgrade": "http://192.168.233.244"
}
}
})

View File

@@ -97,6 +97,7 @@ private:
void handleSave();
void reboot();
void upgrade();
void upgradeFromUrl(String url, String nextVersion);
void firmwareHtml();
void firmwarePost();
void firmwareUpload();

View File

@@ -37,5 +37,12 @@
"s": %d
},
"security": %d,
"boot_reason": %d
"boot_reason": %d,
"ex_cause": %d,
"upgrade": {
"x": %d,
"e": %d,
"f": "%s",
"t": "%s"
}
}

View File

@@ -158,11 +158,11 @@ void AmsWebServer::loop() {
bool AmsWebServer::checkSecurity(byte level, bool send401) {
bool access = WiFi.getMode() == WIFI_AP || webConfig.security < level;
if(!access && webConfig.security >= level && server.hasHeader("Authorization")) {
if(!access && webConfig.security >= level && server.hasHeader(F("Authorization"))) {
String expectedAuth = String(webConfig.username) + ":" + String(webConfig.password);
String providedPwd = server.header("Authorization");
providedPwd.replace("Basic ", "");
String providedPwd = server.header(F("Authorization"));
providedPwd.replace(F("Basic "), F(""));
#if defined(ESP8266)
String expectedBase64 = base64::encode(expectedAuth, false);
@@ -170,9 +170,6 @@ bool AmsWebServer::checkSecurity(byte level, bool send401) {
String expectedBase64 = base64::encode(expectedAuth);
#endif
debugger->printf("Expected auth: %s\n", expectedBase64.c_str());
debugger->printf("Provided auth: %s\n", providedPwd.c_str());
access = providedPwd.equals(expectedBase64);
}
@@ -192,21 +189,21 @@ void AmsWebServer::notFound() {
}
void AmsWebServer::githubSvg() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /github.svg over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /github.svg over http...\n"));
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR);
server.send_P(200, "image/svg+xml", GITHUB_SVG);
}
void AmsWebServer::faviconSvg() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /favicon.ico over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /favicon.ico over http...\n"));
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1HR);
server.send_P(200, "image/svg+xml", FAVICON_SVG);
}
void AmsWebServer::sysinfoJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /sysinfo.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /sysinfo.json over http...\n"));
SystemConfig sys;
config->getSystemConfig(sys);
@@ -254,13 +251,16 @@ void AmsWebServer::sysinfoJson() {
UiConfig ui;
config->getUiConfig(ui);
UpgradeInformation upinfo;
config->getUpgradeInformation(upinfo);
String meterModel = meterState->getMeterModel();
if(!meterModel.isEmpty())
meterModel.replace("\\", "\\\\");
meterModel.replace(F("\\"), F("\\\\"));
String meterId = meterState->getMeterId();
if(!meterId.isEmpty())
meterId.replace("\\", "\\\\");
meterId.replace(F("\\"), F("\\\\"));
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
VERSION,
@@ -314,10 +314,16 @@ void AmsWebServer::sysinfoJson() {
ui.showTemperaturePlot,
webConfig.security,
#if defined(ESP32)
rtc_get_reset_reason(0)
rtc_get_reset_reason(0),
0,
#else
ESP.getResetInfoPtr()->reason
ESP.getResetInfoPtr()->reason,
ESP.getResetInfoPtr()->exccause,
#endif
upinfo.exitCode,
upinfo.errorCode,
upinfo.fromVersion,
upinfo.toVersion
);
stripNonAscii((uint8_t*) buf, size+1);
@@ -329,26 +335,23 @@ void AmsWebServer::sysinfoJson() {
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
server.handleClient();
delay(250);
if(performRestart || rebootForUpgrade) {
server.handleClient();
delay(250);
if(ds != NULL) {
ds->save();
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Rebooting"));
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Rebooting\n"));
debugger->flush();
delay(1000);
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP32)
ESP.restart();
#endif
ESP.restart();
performRestart = false;
}
}
void AmsWebServer::dataJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /data.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /data.json over http...\n"));
uint64_t millis = millis64();
if(!checkSecurity(2, true))
@@ -491,7 +494,7 @@ void AmsWebServer::dataJson() {
}
void AmsWebServer::dayplotJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /dayplot.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /dayplot.json over http...\n"));
if(!checkSecurity(2))
return;
@@ -560,7 +563,7 @@ void AmsWebServer::dayplotJson() {
}
void AmsWebServer::monthplotJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /monthplot.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /monthplot.json over http...\n"));
if(!checkSecurity(2))
return;
@@ -643,7 +646,7 @@ void AmsWebServer::monthplotJson() {
}
void AmsWebServer::energyPriceJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /energyprice.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /energyprice.json over http...\n"));
if(!checkSecurity(2))
return;
@@ -702,13 +705,13 @@ void AmsWebServer::energyPriceJson() {
}
void AmsWebServer::temperatureJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /temperature.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /temperature.json over http...\n"));
if(!checkSecurity(2))
return;
int count = hw->getTempSensorCount();
snprintf(buf, 16, "{\"c\":%d,\"s\":[", count);
snprintf_P(buf, 16, PSTR("{\"c\":%d,\"s\":["), count);
for(int i = 0; i < count; i++) {
TempSensorData* data = hw->getTempSensorData(i);
@@ -723,10 +726,10 @@ void AmsWebServer::temperatureJson() {
conf == NULL || conf->common ? 1 : 0,
data->lastRead
);
delay(10);
yield();
}
char* pos = buf+strlen(buf);
snprintf(count == 0 ? pos : pos-1, 8, "]}");
snprintf_P(count == 0 ? pos : pos-1, 8, PSTR("]}"));
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
@@ -737,7 +740,7 @@ void AmsWebServer::temperatureJson() {
}
void AmsWebServer::indexHtml() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /index.html over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /index.html over http...\n"));
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
@@ -750,7 +753,7 @@ void AmsWebServer::indexHtml() {
}
void AmsWebServer::indexCss() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /index.css over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /index.css over http...\n"));
if(!checkSecurity(2))
return;
@@ -761,7 +764,7 @@ void AmsWebServer::indexCss() {
}
void AmsWebServer::indexJs() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /index.js over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /index.js over http...\n"));
if(!checkSecurity(2))
return;
@@ -772,7 +775,7 @@ void AmsWebServer::indexJs() {
}
void AmsWebServer::configurationJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /configuration.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /configuration.json over http...\n"));
if(!checkSecurity(1))
return;
@@ -960,11 +963,11 @@ void AmsWebServer::configurationJson() {
haconf.discoveryNameTag
);
server.sendContent(buf);
server.sendContent("}");
server.sendContent_P(PSTR("}"));
}
void AmsWebServer::handleSave() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Handling save method from http"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Handling save method from http\n"));
if(!checkSecurity(1))
return;
@@ -1176,7 +1179,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("m")) && server.arg(F("m")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received meter config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received meter config\n"));
config->getMeterConfig(*meterConfig);
meterConfig->baud = server.arg(F("mb")).toInt();
meterConfig->parity = server.arg(F("mp")).toInt();
@@ -1207,15 +1210,15 @@ void AmsWebServer::handleSave() {
memset(meterConfig->authenticationKey, 0, 16);
}
meterConfig->wattageMultiplier = server.arg(F("mmw")).toDouble() * 1000;
meterConfig->voltageMultiplier = server.arg(F("mmv")).toDouble() * 1000;
meterConfig->amperageMultiplier = server.arg(F("mma")).toDouble() * 1000;
meterConfig->accumulatedMultiplier = server.arg(F("mmc")).toDouble() * 1000;
meterConfig->wattageMultiplier = server.arg(F("mmw")).toFloat() * 1000;
meterConfig->voltageMultiplier = server.arg(F("mmv")).toFloat() * 1000;
meterConfig->amperageMultiplier = server.arg(F("mma")).toFloat() * 1000;
meterConfig->accumulatedMultiplier = server.arg(F("mmc")).toFloat() * 1000;
config->setMeterConfig(*meterConfig);
}
if(server.hasArg(F("w")) && server.arg(F("w")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received WiFi config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received WiFi config\n"));
WiFiConfig wifi;
config->getWiFiConfig(wifi);
strcpy(wifi.ssid, server.arg(F("ws")).c_str());
@@ -1257,7 +1260,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("q")) && server.arg(F("q")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received MQTT config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received MQTT config\n"));
MqttConfig mqtt;
config->getMqttConfig(mqtt);
if(server.hasArg(F("qh")) && !server.arg(F("qh")).isEmpty()) {
@@ -1288,7 +1291,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("o")) && server.arg(F("o")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received Domoticz config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received Domoticz config\n"));
DomoticzConfig domo {
static_cast<uint16_t>(server.arg(F("oe")).toInt()),
static_cast<uint16_t>(server.arg(F("ou1")).toInt()),
@@ -1300,7 +1303,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("h")) && server.arg(F("h")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received Home-Assistant config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received Home-Assistant config\n"));
HomeAssistantConfig haconf;
config->getHomeAssistantConfig(haconf);
strcpy(haconf.discoveryPrefix, server.arg(F("ht")).c_str());
@@ -1310,7 +1313,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("g")) && server.arg(F("g")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received web config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received web config\n"));
webConfig.security = server.arg(F("gs")).toInt();
if(webConfig.security > 0) {
strcpy(webConfig.username, server.arg(F("gu")).c_str());
@@ -1340,7 +1343,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("i")) && server.arg(F("i")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received GPIO config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received GPIO config\n"));
gpioConfig->hanPin = server.hasArg(F("ihp")) && !server.arg(F("ihp")).isEmpty() ? server.arg(F("ihp")).toInt() : 3;
gpioConfig->hanPinPullup = server.hasArg(F("ihu")) && server.arg(F("ihu")) == F("true");
gpioConfig->ledPin = server.hasArg(F("ilp")) && !server.arg(F("ilp")).isEmpty() ? server.arg(F("ilp")).toInt() : 0xFF;
@@ -1359,7 +1362,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("iv")) && server.arg(F("iv")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received Vcc config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received Vcc config\n"));
gpioConfig->vccOffset = server.hasArg(F("ivo")) && !server.arg(F("ivo")).isEmpty() ? server.arg(F("ivo")).toFloat() * 100 : 0;
gpioConfig->vccMultiplier = server.hasArg(F("ivm")) && !server.arg(F("ivm")).isEmpty() ? server.arg(F("ivm")).toFloat() * 1000 : 1000;
gpioConfig->vccBootLimit = server.hasArg(F("ivb")) && !server.arg(F("ivb")).isEmpty() ? server.arg(F("ivb")).toFloat() * 10 : 0;
@@ -1367,7 +1370,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("d")) && server.arg(F("d")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received Debug config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received Debug config\n"));
DebugConfig debug;
config->getDebugConfig(debug);
bool active = debug.serial || debug.telnet;
@@ -1414,7 +1417,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("p")) && server.arg(F("p")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received price API config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received price API config\n"));
priceRegion = server.arg(F("pr"));
@@ -1429,7 +1432,7 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("t")) && server.arg(F("t")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Received energy accounting config"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Received energy accounting config\n"));
EnergyAccountingConfig eac;
eac.thresholds[0] = server.arg(F("t0")).toInt();
eac.thresholds[1] = server.arg(F("t1")).toInt();
@@ -1444,10 +1447,10 @@ void AmsWebServer::handleSave() {
config->setEnergyAccountingConfig(eac);
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Saving configuration now..."));
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Saving configuration now...\n"));
if (config->save()) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Successfully saved."));
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Successfully saved.\n"));
if(config->isWifiChanged() || performRestart) {
performRestart = true;
} else {
@@ -1465,26 +1468,23 @@ void AmsWebServer::handleSave() {
server.setContentLength(strlen(buf));
server.send(200, MIME_JSON, buf);
server.handleClient();
delay(250);
if(performRestart || rebootForUpgrade) {
server.handleClient();
delay(250);
if(ds != NULL) {
ds->save();
}
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Rebooting"));
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Rebooting\n"));
debugger->flush();
delay(1000);
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP32)
ESP.restart();
#endif
ESP.restart();
performRestart = false;
}
}
void AmsWebServer::reboot() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /reboot over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /reboot over http...\n"));
if(!checkSecurity(1))
return;
@@ -1494,18 +1494,15 @@ void AmsWebServer::reboot() {
server.handleClient();
delay(250);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Rebooting"));
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Rebooting\n"));
debugger->flush();
delay(1000);
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP32)
ESP.restart();
#endif
ESP.restart();
performRestart = false;
}
void AmsWebServer::upgrade() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /upgrade over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /upgrade over http...\n"));
if(!checkSecurity(1))
return;
@@ -1534,46 +1531,59 @@ void AmsWebServer::upgrade() {
if(server.hasArg(F("version"))) {
url += "/" + server.arg(F("version"));
}
WiFiClient client;
#if defined(ESP8266)
String chipType = F("esp8266");
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
String chipType = F("esp32s2");
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
String chipType = F("esp32c3");
#elif defined(ESP32)
#if defined(CONFIG_FREERTOS_UNICORE)
String chipType = F("esp32solo");
#else
String chipType = F("esp32");
#endif
#endif
#if defined(ESP8266)
ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
#elif defined(ESP32)
HTTPUpdate httpUpdate;
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType);
#endif
switch(ret) {
case HTTP_UPDATE_FAILED:
debugger->printf(PSTR("Update failed"));
break;
case HTTP_UPDATE_NO_UPDATES:
debugger->printf(PSTR("No Update"));
break;
case HTTP_UPDATE_OK:
debugger->printf(PSTR("Update OK"));
break;
}
upgradeFromUrl(url, server.arg(F("expected_version")));
}
}
void AmsWebServer::upgradeFromUrl(String url, String nextVersion) {
config->setUpgradeInformation(0xFF, 0xFF, VERSION, nextVersion.c_str());
WiFiClient client;
#if defined(ESP8266)
String chipType = F("esp8266");
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
String chipType = F("esp32s2");
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
String chipType = F("esp32c3");
#elif defined(ESP32)
#if defined(CONFIG_FREERTOS_UNICORE)
String chipType = F("esp32solo");
#else
String chipType = F("esp32");
#endif
#endif
#if defined(ESP8266)
ESP8266HTTPUpdate httpUpdate = ESP8266HTTPUpdate(60000);
String currentVersion = VERSION;
#elif defined(ESP32)
HTTPUpdate httpUpdate = HTTPUpdate(60000);
String currentVersion = String(VERSION) + "-" + chipType;
#endif
httpUpdate.rebootOnUpdate(false);
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
HTTPUpdateResult ret = httpUpdate.update(client, url, currentVersion);
int lastError = httpUpdate.getLastError();
config->setUpgradeInformation(ret, ret == HTTP_UPDATE_OK ? 0 : lastError, VERSION, nextVersion.c_str());
switch(ret) {
case HTTP_UPDATE_FAILED:
debugger->printf_P(PSTR("Update failed\n"));
break;
case HTTP_UPDATE_NO_UPDATES:
debugger->printf_P(PSTR("No Update\n"));
break;
case HTTP_UPDATE_OK:
debugger->printf_P(PSTR("Update OK\n"));
debugger->flush();
ESP.restart();
break;
}
}
void AmsWebServer::firmwareHtml() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Serving /firmware.html over http..."));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /firmware.html over http...\n"));
if(!checkSecurity(1))
return;
@@ -1587,53 +1597,20 @@ void AmsWebServer::firmwareHtml() {
}
void AmsWebServer::firmwarePost() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Handling firmware post..."));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Handling firmware post...\n"));
if(!checkSecurity(1))
return;
if(rebootForUpgrade) {
server.send(200);
} else {
config->setUpgradeInformation(0xFF, 0xFF, VERSION, "");
if(server.hasArg(F("url"))) {
String url = server.arg(F("url"));
if(!url.isEmpty() && (url.startsWith(F("http://")) || url.startsWith(F("https://")))) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Custom firmware URL was provided"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Custom firmware URL was provided\n"));
WiFiClient client;
#if defined(ESP8266)
String chipType = F("esp8266");
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
String chipType = F("esp32s2");
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
String chipType = F("esp32c3");
#elif defined(ESP32)
#if defined(CONFIG_FREERTOS_UNICORE)
String chipType = F("esp32solo");
#else
String chipType = F("esp32");
#endif
#endif
#if defined(ESP8266)
ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
t_httpUpdate_return ret = ESPhttpUpdate.update(client, url, VERSION);
#elif defined(ESP32)
HTTPUpdate httpUpdate;
httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
HTTPUpdateResult ret = httpUpdate.update(client, url, String(VERSION) + "-" + chipType);
#endif
switch(ret) {
case HTTP_UPDATE_FAILED:
debugger->printf(PSTR("Update failed"));
break;
case HTTP_UPDATE_NO_UPDATES:
debugger->printf(PSTR("No Update"));
break;
case HTTP_UPDATE_OK:
debugger->printf(PSTR("Update OK"));
break;
}
upgradeFromUrl(url, "");
server.send(200, MIME_PLAIN, "OK");
return;
}
@@ -1652,11 +1629,11 @@ void AmsWebServer::firmwareUpload() {
if(upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
if(filename.isEmpty()) {
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("No file, falling back to post\n"));
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("No file, falling back to post\n"));
return;
}
if(!filename.endsWith(".bin")) {
server.send(500, MIME_PLAIN, "500: couldn't create file");
if(!filename.endsWith(F(".bin"))) {
server.send_P(500, MIME_PLAIN, PSTR("500: couldn't create file"));
} else {
#if defined(ESP32)
esp_task_wdt_delete(NULL);
@@ -1669,7 +1646,7 @@ void AmsWebServer::firmwareUpload() {
uploadFile(FILE_FIRMWARE);
if(upload.status == UPLOAD_FILE_END) {
rebootForUpgrade = true;
server.sendHeader("Location","/");
server.sendHeader(HEADER_LOCATION,F("/"));
server.send(302);
}
}
@@ -1678,10 +1655,10 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
if(uploading) {
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("Upload already in progress\n"));
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("Upload already in progress\n"));
server.send_P(500, MIME_HTML, PSTR("<html><body><h1>Upload already in progress!</h1></body></html>"));
} else if (!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("An Error has occurred while mounting LittleFS\n"));
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("An Error has occurred while mounting LittleFS\n"));
server.send_P(500, MIME_HTML, PSTR("<html><body><h1>Unable to mount LittleFS!</h1></body></html>"));
} else {
uploading = true;
@@ -1716,7 +1693,7 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
LittleFS.remove(path);
LittleFS.end();
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(PSTR("An Error has occurred while writing file"));
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf_P(PSTR("An Error has occurred while writing file\n"));
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"File size does not match",
@@ -1733,7 +1710,7 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
if(file) {
file.flush();
file.close();
// LittleFS.end();
LittleFS.end();
} else {
debugger->printf_P(PSTR("File was not valid in the end... Write error: %d, \n"), file.getWriteError());
snprintf_P(buf, BufferSize, RESPONSE_JSON,
@@ -1757,13 +1734,13 @@ void AmsWebServer::factoryResetPost() {
if(!checkSecurity(1))
return;
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Performing factory reset"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Performing factory reset\n"));
bool success = false;
if(server.hasArg(F("perform")) && server.arg(F("perform")) == F("true")) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Formatting LittleFS"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Formatting LittleFS\n"));
LittleFS.format();
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Clearing configuration"));
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Clearing configuration\n"));
config->clear();
success = true;
@@ -1780,13 +1757,10 @@ void AmsWebServer::factoryResetPost() {
server.handleClient();
delay(250);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(PSTR("Rebooting"));
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf_P(PSTR("Rebooting\n"));
debugger->flush();
delay(1000);
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP32)
ESP.restart();
#endif
ESP.restart();
}
void AmsWebServer::robotstxt() {
@@ -1843,7 +1817,7 @@ void AmsWebServer::mqttKeyUpload() {
}
void AmsWebServer::tariffJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /tariff.json over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /tariff.json over http...\n"));
if(!checkSecurity(2))
return;
@@ -1892,7 +1866,7 @@ void AmsWebServer::setPriceSettings(String region, String currency) {
}
void AmsWebServer::configFileDownload() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /configfile.cfg over http...\n");
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf_P(PSTR("Serving /configfile.cfg over http...\n"));
if(!checkSecurity(1))
return;
@@ -2233,7 +2207,7 @@ void AmsWebServer::configFileDownload() {
ead.peaks[4].day,
ead.peaks[4].value / 100.0
));
server.sendContent("\n");
server.sendContent_P(PSTR("\n"));
}
}
@@ -2244,12 +2218,12 @@ void AmsWebServer::configFileUpload() {
HTTPUpload& upload = uploadFile(FILE_CFG);
if(upload.status == UPLOAD_FILE_END) {
performRestart = true;
server.sendHeader("Location","/");
server.sendHeader(HEADER_LOCATION,F("/"));
server.send(303);
}
}
void AmsWebServer::redirectToMain() {
server.sendHeader("Location","/");
server.sendHeader(HEADER_LOCATION,F("/"));
server.send(302);
}

View File

@@ -2,7 +2,7 @@
extra_configs = platformio-user.ini
[common]
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.0, 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, 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, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, EntsoePriceApi, EnergyAccounting, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, SvelteUi
lib_ignore = OneWire
extra_scripts =
pre:scripts/addversion.py
@@ -20,7 +20,7 @@ build_flags =
lib_deps = WiFi, ESPmDNS, WiFiClientSecure, HTTPClient, FS, Update, HTTPUpdate, WebServer, ${common.lib_deps}
[env:esp8266]
platform = espressif8266@3.2.0
platform = espressif8266@4.2.0
framework = arduino
board = esp12e
board_build.ldscript = eagle.flash.4m2m.ld
@@ -32,7 +32,7 @@ lib_ignore = ${common.lib_ignore}
extra_scripts = ${common.extra_scripts}
[env:esp32]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip
framework = arduino
board = esp32dev
board_build.f_cpu = 160000000L
@@ -47,7 +47,7 @@ extra_scripts = ${common.extra_scripts}
# https://github.com/Jason2866/esp32-arduino-lib-builder
[env:esp32s2]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip
framework = arduino
board = esp32-s2-saola-1
board_build.mcu = esp32s2
@@ -63,7 +63,7 @@ lib_ignore = ${common.lib_ignore}
extra_scripts = ${common.extra_scripts}
[env:esp32solo]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip
framework = arduino
board = esp32-solo1
board_build.f_cpu = 160000000L
@@ -75,7 +75,7 @@ lib_ignore = ${common.lib_ignore}
extra_scripts = ${common.extra_scripts}
[env:esp32c3]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.3/platform-espressif32-2.0.5.3.zip
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip
framework = arduino
board = esp32-c3-devkitm-1
board_build.mcu = esp32c3

View File

@@ -6,12 +6,23 @@
#define INVALID_BUTTON_PIN 0xFFFFFFFF
#define MAX_PEM_SIZE 4096
#define MAX_RX_BUFFER_SIZE 1024
#define METER_SOURCE_NONE 0
#define METER_SOURCE_SERIAL 1
#define METER_SOURCE_MQTT 2
#define METER_SOURCE_ESPNOW 3
#define METER_ERROR_NO_DATA 90
#define METER_ERROR_BREAK 91
#define METER_ERROR_BUFFER 92
#define METER_ERROR_FIFO 93
#define METER_ERROR_FRAME 94
#define METER_ERROR_PARITY 95
#define METER_ERROR_RX 96
#define METER_ERROR_EXCEPTION 98
#define METER_ERROR_AUTODETECT 99
#include <SoftwareSerial.h>
#if defined(ESP8266)

File diff suppressed because it is too large Load Diff

View File

@@ -9,25 +9,25 @@ IEC6205621::IEC6205621(const char* p) {
lastUpdateMillis = millis();
listId = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n"));
if(listId.startsWith("ADN")) {
if(listId.startsWith(F("ADN"))) {
meterType = AmsTypeAidon;
listId = listId.substring(0,4);
} else if(listId.startsWith("KFM")) {
} else if(listId.startsWith(F("KFM"))) {
meterType = AmsTypeKaifa;
listId = listId.substring(0,4);
} else if(listId.startsWith("KMP")) {
} else if(listId.startsWith(F("KMP"))) {
meterType = AmsTypeKamstrup;
listId = listId.substring(0,4);
} else if(listId.startsWith("ISk")) {
} else if(listId.startsWith(F("ISk"))) {
meterType = AmsTypeIskra;
listId = listId.substring(0,5);
} else if(listId.startsWith("XMX")) {
} else if(listId.startsWith(F("XMX"))) {
meterType = AmsTypeLandisGyr;
listId = listId.substring(0,6);
} else if(listId.startsWith("Ene") || listId.startsWith("EST")) {
} else if(listId.startsWith(F("Ene")) || listId.startsWith(F("EST"))) {
meterType = AmsTypeSagemcom;
listId = listId.substring(0,4);
} else if(listId.startsWith("LGF")) {
} else if(listId.startsWith(F("LGF"))) {
meterType = AmsTypeLandisGyr;
listId = listId.substring(0,4);
} else {
@@ -35,21 +35,21 @@ IEC6205621::IEC6205621(const char* p) {
listId = listId.substring(0,4);
}
meterId = extract(payload, "96.1.0");
meterId = extract(payload, F("96.1.0"));
if(meterId.isEmpty()) {
meterId = extract(payload, "0.0.5");
meterId = extract(payload, F("0.0.5"));
}
meterModel = extract(payload, "96.1.1");
meterModel = extract(payload, F("96.1.1"));
if(meterModel.isEmpty()) {
meterModel = extract(payload, "96.1.7");
meterModel = extract(payload, F("96.1.7"));
if(meterModel.isEmpty()) {
meterModel = payload.substring(payload.indexOf(listId) + listId.length(), payload.indexOf("\n"));
meterModel = payload.substring(payload.indexOf(listId) + listId.length(), payload.indexOf(F("\n")));
meterModel.trim();
}
}
String timestamp = extract(payload, "1.0.0");
String timestamp = extract(payload, F("1.0.0"));
if(timestamp.length() > 10) {
tmElements_t tm;
tm.Year = (timestamp.substring(0,2).toInt() + 2000) - 1970;
@@ -61,36 +61,36 @@ IEC6205621::IEC6205621(const char* p) {
meterTimestamp = makeTime(tm); // TODO: Adjust for time zone
}
activeImportPower = (uint16_t) (extractDouble(payload, "1.7.0"));
activeExportPower = (uint16_t) (extractDouble(payload, "2.7.0"));
reactiveImportPower = (uint16_t) (extractDouble(payload, "3.7.0"));
reactiveExportPower = (uint16_t) (extractDouble(payload, "4.7.0"));
activeImportPower = (uint16_t) (extractDouble(payload, F("1.7.0")));
activeExportPower = (uint16_t) (extractDouble(payload, F("2.7.0")));
reactiveImportPower = (uint16_t) (extractDouble(payload, F("3.7.0")));
reactiveExportPower = (uint16_t) (extractDouble(payload, F("4.7.0")));
if(activeImportPower > 0)
listType = 1;
l1voltage = extractDouble(payload, "32.7.0");
l2voltage = extractDouble(payload, "52.7.0");
l3voltage = extractDouble(payload, "72.7.0");
l1voltage = extractFloat(payload, F("32.7.0"));
l2voltage = extractFloat(payload, F("52.7.0"));
l3voltage = extractFloat(payload, F("72.7.0"));
l1current = extractDouble(payload, "31.7.0");
l2current = extractDouble(payload, "51.7.0");
l3current = extractDouble(payload, "71.7.0");
l1current = extractFloat(payload, F("31.7.0"));
l2current = extractFloat(payload, F("51.7.0"));
l3current = extractFloat(payload, F("71.7.0"));
l1activeImportPower = extractDouble(payload, "21.7.0");
l2activeImportPower = extractDouble(payload, "41.7.0");
l3activeImportPower = extractDouble(payload, "61.7.0");
l1activeImportPower = extractFloat(payload, F("21.7.0"));
l2activeImportPower = extractFloat(payload, F("41.7.0"));
l3activeImportPower = extractFloat(payload, F("61.7.0"));
l1activeExportPower = extractDouble(payload, "22.7.0");
l2activeExportPower = extractDouble(payload, "42.7.0");
l3activeExportPower = extractDouble(payload, "62.7.0");
l1activeExportPower = extractFloat(payload, F("22.7.0"));
l2activeExportPower = extractFloat(payload, F("42.7.0"));
l3activeExportPower = extractFloat(payload, F("62.7.0"));
if(l1voltage > 0 || l2voltage > 0 || l3voltage > 0)
listType = 2;
double val = 0.0;
val = extractDouble(payload, "1.8.0");
val = extractDouble(payload, F("1.8.0"));
if(val == 0) {
for(int i = 1; i < 9; i++) {
val += extractDouble(payload, "1.8." + String(i,10));
@@ -98,7 +98,7 @@ IEC6205621::IEC6205621(const char* p) {
}
if(val > 0) activeImportCounter = val / 1000;
val = extractDouble(payload, "2.8.0");
val = extractDouble(payload, F("2.8.0"));
if(val == 0) {
for(int i = 1; i < 9; i++) {
val += extractDouble(payload, "2.8." + String(i,10));
@@ -106,7 +106,7 @@ IEC6205621::IEC6205621(const char* p) {
}
if(val > 0) activeExportCounter = val / 1000;
val = extractDouble(payload, "3.8.0");
val = extractDouble(payload, F("3.8.0"));
if(val == 0) {
for(int i = 1; i < 9; i++) {
val += extractDouble(payload, "3.8." + String(i,10));
@@ -114,7 +114,7 @@ IEC6205621::IEC6205621(const char* p) {
}
if(val > 0) reactiveImportCounter = val / 1000;
val = extractDouble(payload, "4.8.0");
val = extractDouble(payload, F("4.8.0"));
if(val == 0) {
for(int i = 1; i < 9; i++) {
val += extractDouble(payload, "4.8." + String(i,10));
@@ -135,7 +135,7 @@ IEC6205621::IEC6205621(const char* p) {
String IEC6205621::extract(String payload, String obis) {
int a = payload.indexOf(String(":" + obis + "("));
if(a > 0) {
int b = payload.indexOf(")", a);
int b = payload.indexOf(F(")"), a);
if(b > a) {
return payload.substring(a+obis.length()+2, b);
}
@@ -149,9 +149,22 @@ double IEC6205621::extractDouble(String payload, String obis) {
return 0.0;
}
int a = str.indexOf("*");
int a = str.indexOf(F("*"));
String val = str.substring(0,a);
String unit = str.substring(a+1);
return unit.startsWith("k") ? val.toDouble() * 1000 : val.toDouble();
return unit.startsWith(F("k")) ? val.toDouble() * 1000 : val.toDouble();
}
float IEC6205621::extractFloat(String payload, String obis) {
String str = extract(payload, obis);
if(str.isEmpty()) {
return 0.0;
}
int a = str.indexOf(F("*"));
String val = str.substring(0,a);
String unit = str.substring(a+1);
return unit.startsWith(F("k")) ? val.toFloat() * 1000 : val.toFloat();
}

View File

@@ -11,5 +11,6 @@ public:
private:
String extract(String payload, String obis);
double extractDouble(String payload, String obis);
float extractFloat(String payload, String obis);
};
#endif

View File

@@ -4,7 +4,7 @@
#include "ntohll.h"
IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx) {
double val;
float val;
char str[64];
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};
@@ -24,7 +24,7 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
String listId = String(str);
if(listId.startsWith("KFM_001")) {
if(listId.startsWith(F("KFM_001"))) {
this->listId = listId;
meterType = AmsTypeKaifa;
@@ -539,14 +539,14 @@ uint8_t IEC6205675::getString(uint8_t* obis, int matchlength, const char* ptr, c
return 0;
}
double IEC6205675::getNumber(uint8_t* obis, int matchlength, const char* ptr) {
float IEC6205675::getNumber(uint8_t* obis, int matchlength, const char* ptr) {
CosemData* item = findObis(obis, matchlength, ptr);
return getNumber(item);
}
double IEC6205675::getNumber(CosemData* item) {
float IEC6205675::getNumber(CosemData* item) {
if(item != NULL) {
double ret = 0.0;
float ret = 0.0;
char* pos = ((char*) item);
switch(item->base.type) {
case CosemTypeLongSigned: {

View File

@@ -21,8 +21,8 @@ private:
CosemData* getCosemDataAt(uint8_t index, const char* ptr);
CosemData* findObis(uint8_t* obis, int matchlength, const char* ptr);
uint8_t getString(uint8_t* obis, int matchlength, const char* ptr, char* target);
double getNumber(uint8_t* obis, int matchlength, const char* ptr);
double getNumber(CosemData*);
float getNumber(uint8_t* obis, int matchlength, const char* ptr);
float getNumber(CosemData*);
time_t getTimestamp(uint8_t* obis, int matchlength, const char* ptr);
uint8_t AMS_OBIS_VERSION[4] = { 0, 2, 129, 255 };

View File

@@ -19,7 +19,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
for(uint8_t x = 0; x < h->arrayLength-1; x++) {
ptr = (uint8_t*) &descriptor[1];
descriptor = (LngObisDescriptor*) ptr;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(L&G) OBIS %d.%d.%d with type 0x%02X", descriptor->obis[2], descriptor->obis[3], descriptor->obis[4], *data);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(L&G) OBIS %d.%d.%d with type 0x%02X\n"), descriptor->obis[2], descriptor->obis[3], descriptor->obis[4], *data);
CosemData* item = (CosemData*) data;
if(descriptor->obis[2] == 1) {
@@ -27,22 +27,22 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
if(descriptor->obis[4] == 0) {
o170 = getNumber(item);
listType = listType >= 1 ? listType : 1;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o170);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o170);
}
} else if(descriptor->obis[3] == 8) {
if(descriptor->obis[4] == 0) {
o180 = getNumber(item);
listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o180);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o180);
activeImportCounter = o180 / 1000.0;
} else if(descriptor->obis[4] == 1) {
o181 = getNumber(item);
listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o181);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o181);
} else if(descriptor->obis[4] == 2) {
o182 = getNumber(item);
listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o182);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o182);
}
}
} else if(descriptor->obis[2] == 2) {
@@ -50,22 +50,22 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
if(descriptor->obis[4] == 0) {
o270 = getNumber(item);
listType = listType >= 2 ? listType : 2;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o270);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o270);
}
} else if(descriptor->obis[3] == 8) {
if(descriptor->obis[4] == 0) {
o280 = getNumber(item);
listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o280);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o280);
activeExportCounter = o280 / 1000.0;
} else if(descriptor->obis[4] == 1) {
o281 = getNumber(item);
listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o281);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o281);
} else if(descriptor->obis[4] == 2) {
o282 = getNumber(item);
listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o282);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %lu\n"), o282);
}
}
} else if(descriptor->obis[2] == 96) {
@@ -76,19 +76,19 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
str[item->oct.length] = '\0';
meterId = String(str);
listType = listType >= 2 ? listType : 2;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %s (oct)", str);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %s (oct)\n"), str);
} else if(descriptor->obis[4] == 1) {
char str[item->oct.length+1];
memcpy(str, item->oct.data, item->oct.length);
str[item->oct.length] = '\0';
meterModel = String(str);
listType = listType >= 2 ? listType : 2;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %s (oct)", str);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR(" and value %s (oct)\n"), str);
}
}
}
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("\n");
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("\n"));
if(o170 > 0 || o270 > 0) {
int32_t sum = o170-o270;