Compare commits

..

5 Commits

Author SHA1 Message Date
Gunnar Skjold
9803f62a4d Merge branch 'main' into fix/tariff_peaks_wrong_day 2026-03-15 10:10:34 +01:00
Gunnar Skjold
9e24395681 Stricter time restrictions when updating history 2026-03-15 09:33:14 +01:00
Gunnar Skjold
3d128f5e20 Fix issue for ex DLMS where accumulated is always included 2026-03-15 09:29:40 +01:00
Gunnar Skjold
dda61db9ef Merge branch 'main' into fix/tariff_peaks_wrong_day 2026-03-06 10:48:24 +01:00
Gunnar Skjold
e81ef7a93b Trying to fix tariff on wrong date. Also some code cleanup 2026-03-05 13:14:48 +01:00
42 changed files with 995 additions and 1408 deletions

View File

@@ -9,14 +9,8 @@
#include <EEPROM.h>
#include "Arduino.h"
#if defined(ESP8266)
#define BUF_SIZE_COMMON 2048
#else
#define BUF_SIZE_COMMON 4096
#endif
#define EEPROM_SIZE 1024*3
#define EEPROM_EXPECTED_VERSION 104 // Used to check if config is stored. Change if structure changes
#define EEPROM_CHECK_SUM 104 // Used to check if config is stored. Change if structure changes
#define EEPROM_CLEARED_INDICATOR 0xFC
#define EEPROM_CONFIG_ADDRESS 0
@@ -289,12 +283,11 @@ struct ZmartChargeConfig {
class AmsConfiguration {
public:
bool load();
bool save();
bool hasConfig();
int getConfigVersion();
bool save();
bool getSystemConfig(SystemConfig&);
bool setSystemConfig(SystemConfig&);
bool isSystemConfigChanged();

View File

@@ -11,14 +11,16 @@
#endif
bool AmsConfiguration::getSystemConfig(SystemConfig& config) {
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
EEPROM.get(CONFIG_SYSTEM_START, config);
EEPROM.end();
if(config.firmwareChannel > 3) {
config.firmwareChannel = 0;
}
if(configVersion == EEPROM_EXPECTED_VERSION) {
if(configVersion == EEPROM_CHECK_SUM) {
return true;
} else {
if(configVersion == EEPROM_CLEARED_INDICATOR && config.boardType > 0 && config.boardType < 250) {
@@ -50,9 +52,12 @@ bool AmsConfiguration::setSystemConfig(SystemConfig& config) {
} else {
sysChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
stripNonAscii((uint8_t*) config.country, 2);
EEPROM.put(CONFIG_SYSTEM_START, config);
return sysChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::isSystemConfigChanged() {
@@ -65,8 +70,10 @@ void AmsConfiguration::ackSystemConfigChanged() {
bool AmsConfiguration::getNetworkConfig(NetworkConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_NETWORK_START, config);
if(config.sleep > 2) config.sleep = 1;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_NETWORK_START, config);
EEPROM.end();
if(config.sleep > 2) config.sleep = 1;
return true;
} else {
clearNetworkConfig(config);
@@ -106,8 +113,11 @@ bool AmsConfiguration::setNetworkConfig(NetworkConfig& config) {
stripNonAscii((uint8_t*) config.dns2, 16);
stripNonAscii((uint8_t*) config.hostname, 32);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_NETWORK_START, config);
return networkChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearNetworkConfig(NetworkConfig& config) {
@@ -148,8 +158,10 @@ void AmsConfiguration::ackNetworkConfigChange() {
bool AmsConfiguration::getMqttConfig(MqttConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_MQTT_START, config);
if(config.magic != 0xA5) { // New magic for 2.4.11
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_MQTT_START, config);
EEPROM.end();
if(config.magic != 0xA5) { // New magic for 2.4.11
if(config.magic != 0x9C) {
if(config.magic != 0x7B) {
config.stateUpdate = false;
@@ -201,8 +213,11 @@ bool AmsConfiguration::setMqttConfig(MqttConfig& config) {
if(config.keepalive > 240) config.keepalive = 60;
if(config.rebootMinutes > 240) config.rebootMinutes = 0;
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_MQTT_START, config);
return mqttChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearMqtt(MqttConfig& config) {
@@ -238,8 +253,10 @@ void AmsConfiguration::ackMqttChange() {
bool AmsConfiguration::getWebConfig(WebConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_WEB_START, config);
return true;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_WEB_START, config);
EEPROM.end();
return true;
} else {
clearWebConfig(config);
return false;
@@ -260,8 +277,11 @@ bool AmsConfiguration::setWebConfig(WebConfig& config) {
stripNonAscii((uint8_t*) config.password, 37, false, false);
stripNonAscii((uint8_t*) config.context, 37);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_WEB_START, config);
return webChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearWebConfig(WebConfig& config) {
@@ -280,10 +300,12 @@ void AmsConfiguration::ackWebChange() {
}
bool AmsConfiguration::getMeterConfig(MeterConfig& config) {
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
if(configVersion == EEPROM_EXPECTED_VERSION || configVersion == EEPROM_CLEARED_INDICATOR) {
if(configVersion == EEPROM_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) {
EEPROM.get(CONFIG_METER_START, config);
if(config.bufferSize < 1 || config.bufferSize > 64) {
EEPROM.end();
if(config.bufferSize < 1 || config.bufferSize > 64) {
#if defined(ESP32)
config.bufferSize = 2;
#else
@@ -324,8 +346,11 @@ bool AmsConfiguration::setMeterConfig(MeterConfig& config) {
} else {
meterChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_METER_START, config);
return meterChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearMeter(MeterConfig& config) {
@@ -363,8 +388,10 @@ void AmsConfiguration::setMeterChanged() {
bool AmsConfiguration::getDebugConfig(DebugConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_DEBUG_START, config);
return true;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_DEBUG_START, config);
EEPROM.end();
return true;
} else {
clearDebug(config);
return false;
@@ -374,8 +401,11 @@ bool AmsConfiguration::getDebugConfig(DebugConfig& config) {
bool AmsConfiguration::setDebugConfig(DebugConfig& config) {
if(!config.serial && !config.telnet)
config.level = 4; // Force warning level when debug is disabled
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_DEBUG_START, config);
return true;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearDebug(DebugConfig& config) {
@@ -386,8 +416,10 @@ void AmsConfiguration::clearDebug(DebugConfig& config) {
bool AmsConfiguration::getDomoticzConfig(DomoticzConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_DOMOTICZ_START, config);
return true;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_DOMOTICZ_START, config);
EEPROM.end();
return true;
} else {
clearDomo(config);
return false;
@@ -405,8 +437,11 @@ bool AmsConfiguration::setDomoticzConfig(DomoticzConfig& config) {
} else {
mqttChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_DOMOTICZ_START, config);
return mqttChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearDomo(DomoticzConfig& config) {
@@ -419,8 +454,10 @@ void AmsConfiguration::clearDomo(DomoticzConfig& config) {
bool AmsConfiguration::getHomeAssistantConfig(HomeAssistantConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_HA_START, config);
if(stripNonAscii((uint8_t*) config.discoveryPrefix, 64) || stripNonAscii((uint8_t*) config.discoveryHostname, 64) || stripNonAscii((uint8_t*) config.discoveryNameTag, 16)) {
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 false;
}
@@ -445,8 +482,11 @@ bool AmsConfiguration::setHomeAssistantConfig(HomeAssistantConfig& config) {
stripNonAscii((uint8_t*) config.discoveryHostname, 64);
stripNonAscii((uint8_t*) config.discoveryNameTag, 16);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_HA_START, config);
return mqttChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearHomeAssistantConfig(HomeAssistantConfig& config) {
@@ -472,10 +512,12 @@ bool AmsConfiguration::pinUsed(uint8_t pin, GpioConfig& config) {
}
bool AmsConfiguration::getGpioConfig(GpioConfig& config) {
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
if(configVersion == EEPROM_EXPECTED_VERSION || configVersion == EEPROM_CLEARED_INDICATOR) {
if(configVersion == EEPROM_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) {
EEPROM.get(CONFIG_GPIO_START, config);
if(config.powersaving > 4) config.powersaving = 0;
EEPROM.end();
if(config.powersaving > 4) config.powersaving = 0;
return true;
} else {
clearGpio(config);
@@ -530,8 +572,11 @@ bool AmsConfiguration::setGpioConfig(GpioConfig& config) {
if(config.apPin >= 0)
pinMode(config.apPin, INPUT_PULLUP);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_GPIO_START, config);
return true;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearGpio(GpioConfig& config, bool all) {
@@ -560,8 +605,10 @@ void AmsConfiguration::clearGpio(GpioConfig& config, bool all) {
bool AmsConfiguration::getNtpConfig(NtpConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_NTP_START, config);
return true;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_NTP_START, config);
EEPROM.end();
return true;
} else {
clearNtp(config);
return false;
@@ -588,8 +635,11 @@ bool AmsConfiguration::setNtpConfig(NtpConfig& config) {
stripNonAscii((uint8_t*) config.server, 64);
stripNonAscii((uint8_t*) config.timezone, 32);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_NTP_START, config);
return ntpChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::isNtpChanged() {
@@ -609,8 +659,10 @@ void AmsConfiguration::clearNtp(NtpConfig& config) {
bool AmsConfiguration::getPriceServiceConfig(PriceServiceConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_PRICE_START, config);
if(strlen(config.entsoeToken) != 0 && strlen(config.entsoeToken) != 36) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_PRICE_START, config);
EEPROM.end();
if(strlen(config.entsoeToken) != 0 && strlen(config.entsoeToken) != 36) {
clearPriceServiceConfig(config);
return false;
}
@@ -640,8 +692,11 @@ bool AmsConfiguration::setPriceServiceConfig(PriceServiceConfig& config) {
stripNonAscii((uint8_t*) config.area, 17);
stripNonAscii((uint8_t*) config.currency, 4);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_PRICE_START, config);
return priceChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearPriceServiceConfig(PriceServiceConfig& config) {
@@ -663,8 +718,10 @@ void AmsConfiguration::ackPriceServiceChange() {
bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_ENERGYACCOUNTING_START, config);
if(config.thresholds[9] != 0xFFFF) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_ENERGYACCOUNTING_START, config);
EEPROM.end();
if(config.thresholds[9] != 0xFFFF) {
clearEnergyAccountingConfig(config);
return false;
}
@@ -690,8 +747,11 @@ bool AmsConfiguration::setEnergyAccountingConfig(EnergyAccountingConfig& config)
} else {
energyAccountingChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_ENERGYACCOUNTING_START, config);
return energyAccountingChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearEnergyAccountingConfig(EnergyAccountingConfig& config) {
@@ -718,9 +778,11 @@ void AmsConfiguration::ackEnergyAccountingChange() {
bool AmsConfiguration::getUiConfig(UiConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_UI_START, config);
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_UI_START, config);
if(config.showImport > 2) clearUiConfig(config); // Must be wrong
return true;
EEPROM.end();
return true;
} else {
clearUiConfig(config);
return false;
@@ -734,8 +796,11 @@ bool AmsConfiguration::setUiConfig(UiConfig& config) {
} else {
uiLanguageChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_UI_START, config);
return uiLanguageChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearUiConfig(UiConfig& config) {
@@ -770,14 +835,19 @@ bool AmsConfiguration::setUpgradeInformation(UpgradeInformation& upinfo) {
stripNonAscii((uint8_t*) upinfo.fromVersion, 16);
stripNonAscii((uint8_t*) upinfo.toVersion, 16);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_UPGRADE_INFO_START, upinfo);
return true;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::getUpgradeInformation(UpgradeInformation& upinfo) {
if(hasConfig()) {
EEPROM.get(CONFIG_UPGRADE_INFO_START, upinfo);
if(stripNonAscii((uint8_t*) upinfo.fromVersion, 16) || stripNonAscii((uint8_t*) upinfo.toVersion, 16)) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_UPGRADE_INFO_START, upinfo);
EEPROM.end();
if(stripNonAscii((uint8_t*) upinfo.fromVersion, 16) || stripNonAscii((uint8_t*) upinfo.toVersion, 16)) {
clearUpgradeInformation(upinfo);
return false;
}
@@ -800,8 +870,10 @@ void AmsConfiguration::clearUpgradeInformation(UpgradeInformation& upinfo) {
bool AmsConfiguration::getCloudConfig(CloudConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_CLOUD_START, config);
if(config.proto > 2) config.proto = 0;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_CLOUD_START, config);
EEPROM.end();
if(config.proto > 2) config.proto = 0;
return true;
} else {
clearCloudConfig(config);
@@ -824,8 +896,11 @@ bool AmsConfiguration::setCloudConfig(CloudConfig& config) {
stripNonAscii((uint8_t*) config.hostname, 64);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_CLOUD_START, config);
return cloudChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearCloudConfig(CloudConfig& config) {
@@ -847,9 +922,11 @@ void AmsConfiguration::ackCloudConfig() {
bool AmsConfiguration::getZmartChargeConfig(ZmartChargeConfig& config) {
if(hasConfig()) {
EEPROM.get(CONFIG_ZC_START, config);
stripNonAscii((uint8_t*) config.token, 21);
stripNonAscii((uint8_t*) config.baseUrl, 64);
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_ZC_START, config);
EEPROM.end();
stripNonAscii((uint8_t*) config.token, 21);
stripNonAscii((uint8_t*) config.baseUrl, 64);
if(strlen(config.token) != 20 || !config.enabled) {
config.enabled = false;
memset(config.token, 0, 64);
@@ -882,8 +959,11 @@ bool AmsConfiguration::setZmartChargeConfig(ZmartChargeConfig& config) {
memset(config.baseUrl, 0, 64);
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_ZC_START, config);
return zcChanged;
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearZmartChargeConfig(ZmartChargeConfig& config) {
@@ -904,6 +984,8 @@ void AmsConfiguration::setUiLanguageChanged() {
}
void AmsConfiguration::clear() {
EEPROM.begin(EEPROM_SIZE);
SystemConfig sys;
EEPROM.get(CONFIG_SYSTEM_START, sys);
sys.userConfigured = false;
@@ -971,16 +1053,18 @@ void AmsConfiguration::clear() {
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CLEARED_INDICATOR);
EEPROM.commit();
EEPROM.end();
}
bool AmsConfiguration::load() {
EEPROM.begin(EEPROM_SIZE);
bool AmsConfiguration::hasConfig() {
if(configVersion == 0) {
EEPROM.begin(EEPROM_SIZE);
configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
EEPROM.end();
}
if(configVersion > EEPROM_EXPECTED_VERSION) {
if(loadFromFs(EEPROM_EXPECTED_VERSION)) {
configVersion = EEPROM_EXPECTED_VERSION;
if(configVersion > EEPROM_CHECK_SUM) {
if(loadFromFs(EEPROM_CHECK_SUM)) {
configVersion = EEPROM_CHECK_SUM;
} else {
configVersion = 0;
}
@@ -994,18 +1078,14 @@ bool AmsConfiguration::load() {
configVersion = 0;
return false;
}
case EEPROM_EXPECTED_VERSION:
case EEPROM_CHECK_SUM:
return true;
default:
configVersion = 0;
return false;
}
}
return configVersion == EEPROM_EXPECTED_VERSION;
}
bool AmsConfiguration::hasConfig() {
return configVersion == EEPROM_EXPECTED_VERSION;
return configVersion == EEPROM_CHECK_SUM;
}
int AmsConfiguration::getConfigVersion() {
@@ -1013,6 +1093,8 @@ int AmsConfiguration::getConfigVersion() {
}
bool AmsConfiguration::relocateConfig103() {
EEPROM.begin(EEPROM_SIZE);
MeterConfig meter;
UpgradeInformation upinfo;
UiConfig ui;
@@ -1109,14 +1191,19 @@ bool AmsConfiguration::relocateConfig103() {
EEPROM.put(CONFIG_ZC_START, zcc);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 104);
return EEPROM.commit();
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::save() {
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_EXPECTED_VERSION);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);
bool success = EEPROM.commit();
configVersion = EEPROM_EXPECTED_VERSION;
EEPROM.end();
configVersion = EEPROM_CHECK_SUM;
return success;
}

View File

@@ -7,8 +7,7 @@
#ifndef _AMSDATA_H
#define _AMSDATA_H
#include "Arduino.h"
#include <Timezone.h>
#include <WString.h>
#include "OBIScodes.h"
enum AmsType {
@@ -28,7 +27,7 @@ public:
AmsData();
void apply(AmsData& other);
void apply(const OBIS_code_t obis, double value);
void apply(const OBIS_code_t obis, double value, uint64_t millis64);
uint64_t getLastUpdateMillis();
@@ -95,9 +94,7 @@ protected:
uint64_t lastList2 = 0;
uint8_t listType = 0, meterType = AmsTypeUnknown;
time_t packageTimestamp = 0;
char listId[32] = {};
char meterId[32] = {};
char meterModel[65] = {};
String listId = "", meterId = "", meterModel = "";
time_t meterTimestamp = 0;
uint32_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;

View File

@@ -5,6 +5,7 @@
*/
#include "AmsData.h"
#include <algorithm>
AmsData::AmsData() {}
@@ -17,7 +18,6 @@ void AmsData::apply(AmsData& other) {
uint32_t power = (activeImportPower + other.getActiveImportPower()) / 2;
float add = power * (((float) ms) / 3600000.0);
activeImportCounter += add / 1000.0;
//Serial.printf("%dW, %dms, %.6fkWh added\n", other.getActiveImportPower(), ms, add);
}
if(other.getListType() > 1) {
@@ -88,10 +88,10 @@ void AmsData::apply(AmsData& other) {
}
this->counterEstimated = false;
case 2:
strncpy(this->listId, other.listId, sizeof(this->listId) - 1);
strncpy(this->meterId, other.meterId, sizeof(this->meterId) - 1);
this->listId = other.getListId();
this->meterId = other.getMeterId();
this->meterType = other.getMeterType();
strncpy(this->meterModel, other.meterModel, sizeof(this->meterModel) - 1);
this->meterModel = other.getMeterModel();
this->reactiveImportPower = other.getReactiveImportPower();
this->reactiveExportPower = other.getReactiveExportPower();
this->l1current = other.getL1Current();
@@ -112,14 +112,14 @@ void AmsData::apply(AmsData& other) {
this->activeExportPower = other.getActiveExportPower();
}
void AmsData::apply(OBIS_code_t obis, double value) {
void AmsData::apply(OBIS_code_t obis, double value, uint64_t millis64) {
if(obis.sensor == 0 && obis.gr == 0 && obis.tariff == 0) {
meterType = value;
}
if(obis.gr == 1) {
if(obis.sensor == 96) {
if(obis.tariff == 0) {
snprintf(meterId, sizeof(meterId), "%ld", (long) value);
meterId = String((long) value, 10);
return;
} else if(obis.tariff == 1) {
return;
@@ -127,138 +127,137 @@ void AmsData::apply(OBIS_code_t obis, double value) {
}
}
if(obis.tariff != 0) {
Serial.println("Tariff not implemented");
return;
}
if(obis.gr == 7) { // Instant values
switch(obis.sensor) {
case 1:
activeImportPower = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 2:
activeExportPower = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 3:
reactiveImportPower = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 4:
reactiveExportPower = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 13:
powerFactor = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 21:
l1activeImportPower = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 22:
l1activeExportPower = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 31:
l1current = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 32:
l1voltage = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 33:
l1PowerFactor = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 41:
l2activeImportPower = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 42:
l2activeExportPower = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 51:
l2current = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 52:
l2voltage = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 53:
l2PowerFactor = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 61:
l3activeImportPower = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 62:
l3activeExportPower = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 71:
l3current = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 72:
l3voltage = value;
listType = max(listType, (uint8_t) 2);
listType = std::max(listType, (uint8_t) 2);
break;
case 73:
l3PowerFactor = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
}
} else if(obis.gr == 8) { // Accumulated values
switch(obis.sensor) {
case 1:
activeImportCounter = value;
listType = max(listType, (uint8_t) 3);
listType = std::max(listType, (uint8_t) 3);
break;
case 2:
activeExportCounter = value;
listType = max(listType, (uint8_t) 3);
listType = std::max(listType, (uint8_t) 3);
break;
case 3:
reactiveImportCounter = value;
listType = max(listType, (uint8_t) 3);
listType = std::max(listType, (uint8_t) 3);
break;
case 4:
reactiveExportCounter = value;
listType = max(listType, (uint8_t) 3);
listType = std::max(listType, (uint8_t) 3);
break;
case 21:
l1activeImportCounter = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 22:
l1activeExportCounter = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 41:
l2activeImportCounter = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 42:
l2activeExportCounter = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 61:
l3activeImportCounter = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
case 62:
l3activeExportCounter = value;
listType = max(listType, (uint8_t) 4);
listType = std::max(listType, (uint8_t) 4);
break;
}
}
if(listType > 0)
lastUpdateMillis = millis();
lastUpdateMillis = millis64;
threePhase = l1voltage > 0 && l2voltage > 0 && l3voltage > 0;
if(!threePhase)
@@ -278,11 +277,11 @@ uint8_t AmsData::getListType() {
}
String AmsData::getListId() {
return String(this->listId);
return this->listId;
}
String AmsData::getMeterId() {
return String(this->meterId);
return this->meterId;
}
uint8_t AmsData::getMeterType() {
@@ -290,7 +289,7 @@ uint8_t AmsData::getMeterType() {
}
String AmsData::getMeterModel() {
return String(this->meterModel);
return this->meterModel;
}
time_t AmsData::getMeterTimestamp() {

View File

@@ -639,25 +639,22 @@ bool AmsDataStorage::isDayHappy(time_t now) {
return false;
}
if(now < FirmwareVersion::BuildEpoch) return false;
if(now < day.lastMeterReadTime) {
// If the timestamp is before the firware was built, there is something seriously wrong..
if(now < FirmwareVersion::BuildEpoch) {
return false;
}
// There are cases where the meter reports before the hour. The update method will then receive the meter timestamp as reference, thus there will not be 3600s between.
// Leaving a 100s buffer for these cases
if(now-day.lastMeterReadTime > 3500) {
// If the timestamp is before the last time we updated, there is also something wrong..
if(now < day.lastMeterReadTime) {
return false;
}
tmElements_t tm, last;
breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(day.lastMeterReadTime), last);
if(tm.Hour != last.Hour) {
return false;
}
return true;
// If the timestamp is at the same day and hour as last update, we are happy
return tm.Day == last.Day && tm.Hour == last.Hour;
}
bool AmsDataStorage::isMonthHappy(time_t now) {

View File

@@ -1,11 +1,9 @@
#pragma once
#include "AmsDataStorage.h"
#include "AmsConfiguration.h"
class AmsJsonGenerator {
public:
static void generateDayPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize);
static void generateMonthPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize);
static void generateConfigurationJson(AmsConfiguration* config, char* buf, size_t bufSize);
};

View File

@@ -1,8 +1,4 @@
#include "AmsJsonGenerator.h"
#include "hexutils.h"
#include "AmsStorage.h"
#include "LittleFS.h"
#include "FirmwareVersion.h"
void AmsJsonGenerator::generateDayPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize) {
uint16_t pos = snprintf_P(buf, bufSize, PSTR("{\"unit\":\"kwh\""));
@@ -19,266 +15,3 @@ void AmsJsonGenerator::generateMonthPlotJson(AmsDataStorage* ds, char* buf, size
}
snprintf_P(buf+pos, bufSize-pos, PSTR("}"));
}
void AmsJsonGenerator::generateConfigurationJson(AmsConfiguration* config, char* buf, size_t bufSize) {
uint16_t pos = snprintf_P(buf, bufSize, PSTR("{\"version\":\"%s\""), FirmwareVersion::VersionString);
SystemConfig sysConfig;
config->getSystemConfig(sysConfig);
WebConfig webConfig;
config->getWebConfig(webConfig);
MeterConfig meterConfig;
config->getMeterConfig(meterConfig);
NetworkConfig networkConfig;
config->getNetworkConfig(networkConfig);
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
MqttConfig mqttConfig;
config->getMqttConfig(mqttConfig);
PriceServiceConfig price;
config->getPriceServiceConfig(price);
DebugConfig debugConfig;
config->getDebugConfig(debugConfig);
GpioConfig gpioConfig;
config->getGpioConfig(gpioConfig);
UiConfig ui;
config->getUiConfig(ui);
DomoticzConfig domo;
config->getDomoticzConfig(domo);
HomeAssistantConfig haconf;
config->getHomeAssistantConfig(haconf);
CloudConfig cloud;
config->getCloudConfig(cloud);
ZmartChargeConfig zcc;
config->getZmartChargeConfig(zcc);
// General
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"g\":{\"t\":\"%s\",\"h\":\"%s\",\"s\":%d,\"u\":\"%s\",\"p\":\"%s\",\"c\":\"%s\"}"),
ntpConfig.timezone,
networkConfig.hostname,
webConfig.security,
webConfig.username,
strlen(webConfig.password) > 0 ? "***" : "",
webConfig.context
);
// Meter
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"m\":{\"o\":%d,\"a\":%d,\"b\":%d,\"p\":%d,\"i\":%s,\"s\":%d,\"d\":%d,\"f\":%d,\"r\":%d"),
meterConfig.source,
meterConfig.parser,
meterConfig.baud,
meterConfig.parity,
meterConfig.invert ? "true" : "false",
meterConfig.bufferSize * 64,
meterConfig.distributionSystem,
meterConfig.mainFuse,
meterConfig.productionCapacity
);
bool encen = false;
for(uint8_t i = 0; i < 16; i++) {
if(meterConfig.encryptionKey[i] > 0) {
encen = true;
}
}
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"e\":{\"e\":%s,\"k\":\"%s\",\"a\":\"%s\"}"),
encen ? "true" : "false",
toHex(meterConfig.encryptionKey, 16).c_str(),
toHex(meterConfig.authenticationKey, 16).c_str()
);
bool multEnable = false;
if(meterConfig.wattageMultiplier != 1.0 && meterConfig.wattageMultiplier != 0.0)
multEnable = true;
if(meterConfig.voltageMultiplier != 1.0 && meterConfig.voltageMultiplier != 0.0)
multEnable = true;
if(meterConfig.amperageMultiplier != 1.0 && meterConfig.amperageMultiplier != 0.0)
multEnable = true;
if(meterConfig.accumulatedMultiplier != 1.0 && meterConfig.accumulatedMultiplier != 0.0)
multEnable = true;
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"m\":{\"e\":%s,\"w\":%.3f,\"v\":%.3f,\"a\":%.3f,\"c\":%.3f}"),
multEnable ? "true" : "false",
meterConfig.wattageMultiplier == 0.0 ? 1.0 : meterConfig.wattageMultiplier / 1000.0,
meterConfig.voltageMultiplier == 0.0 ? 1.0 : meterConfig.voltageMultiplier / 1000.0,
meterConfig.amperageMultiplier == 0.0 ? 1.0 : meterConfig.amperageMultiplier / 1000.0,
meterConfig.accumulatedMultiplier == 0.0 ? 1.0 : meterConfig.accumulatedMultiplier / 1000.0
);
pos += snprintf_P(buf+pos, bufSize-pos, PSTR("}")); // End of meter
// Thresholds
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"t\":{\"t\":[%d,%d,%d,%d,%d,%d,%d,%d,%d,%d],\"h\":%d}"),
eac.thresholds[0],
eac.thresholds[1],
eac.thresholds[2],
eac.thresholds[3],
eac.thresholds[4],
eac.thresholds[5],
eac.thresholds[6],
eac.thresholds[7],
eac.thresholds[8],
eac.thresholds[9],
eac.hours
);
// WiFi
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"w\":{\"s\":\"%s\",\"p\":\"%s\",\"w\":%.1f,\"z\":%d,\"b\":%s}"),
networkConfig.ssid,
strlen(networkConfig.psk) > 0 ? "***" : "",
networkConfig.power / 10.0,
networkConfig.sleep,
networkConfig.use11b ? "true" : "false"
);
// Network
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"n\":{\"c\":%d,\"m\":\"%s\",\"i\":\"%s\",\"s\":\"%s\",\"g\":\"%s\",\"d1\":\"%s\",\"d2\":\"%s\",\"d\":%s,\"n1\":\"%s\",\"h\":%s,\"x\":%s}"),
networkConfig.mode,
strlen(networkConfig.ip) > 0 ? "static" : "dhcp",
networkConfig.ip,
networkConfig.subnet,
networkConfig.gateway,
networkConfig.dns1,
networkConfig.dns2,
networkConfig.mdns ? "true" : "false",
ntpConfig.server,
ntpConfig.dhcp ? "true" : "false",
networkConfig.ipv6 ? "true" : "false"
);
// MQTT
bool qsc = false;
bool qsr = false;
bool qsk = false;
if(LittleFS.begin()) {
qsc = LittleFS.exists(FILE_MQTT_CA);
qsr = LittleFS.exists(FILE_MQTT_CERT);
qsk = LittleFS.exists(FILE_MQTT_KEY);
}
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"q\":{\"h\":\"%s\",\"p\":%d,\"u\":\"%s\",\"a\":\"%s\",\"c\":\"%s\",\"b\":\"%s\",\"r\":\"%s\",\"m\":%d,\"s\":{\"e\":%s,\"c\":%s,\"r\":%s,\"k\":%s},\"t\":%d,\"d\":%d,\"i\":%d,\"k\":%d,\"e\":%s}"),
mqttConfig.host,
mqttConfig.port,
mqttConfig.username,
strlen(mqttConfig.password) > 0 ? "***" : "",
mqttConfig.clientId,
mqttConfig.publishTopic,
mqttConfig.subscribeTopic,
mqttConfig.payloadFormat,
mqttConfig.ssl ? "true" : "false",
qsc ? "true" : "false",
qsr ? "true" : "false",
qsk ? "true" : "false",
mqttConfig.stateUpdate,
mqttConfig.stateUpdateInterval,
mqttConfig.timeout,
mqttConfig.keepalive,
mqttConfig.rebootMinutes == 0 ? "null" : String(mqttConfig.rebootMinutes, 10).c_str()
);
// Price
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"p\":{\"e\":%s,\"t\":\"%s\",\"r\":\"%s\",\"c\":\"%s\",\"m\":%d}"),
price.enabled ? "true" : "false",
price.entsoeToken,
price.area,
price.currency,
price.resolutionInMinutes
);
// Debug
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"d\":{\"s\":%s,\"t\":%s,\"l\":%d}"),
debugConfig.serial ? "true" : "false",
debugConfig.telnet ? "true" : "false",
debugConfig.level
);
// GPIO
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"i\":{\"h\":{\"p\":%s,\"u\":%s,\"t\":%s},\"a\":%s,\"l\":{\"p\":%s,\"i\":%s},\"r\":{\"r\":%s,\"g\":%s,\"b\":%s,\"i\":%s},\"d\":{\"d\":%s,\"b\":%d},\"t\":{\"d\":%s,\"a\":%s},\"v\":{\"p\":%s,\"o\":%.2f,\"m\":%.3f,\"d\":{\"v\":%d,\"g\":%d},\"b\":%.1f},\"p\":%d}"),
meterConfig.rxPin == 0xff ? "null" : String(meterConfig.rxPin, 10).c_str(),
meterConfig.rxPinPullup ? "true" : "false",
meterConfig.txPin == 0xff ? "null" : String(meterConfig.txPin, 10).c_str(),
gpioConfig.apPin == 0xff ? "null" : String(gpioConfig.apPin, 10).c_str(),
gpioConfig.ledPin == 0xff ? "null" : String(gpioConfig.ledPin, 10).c_str(),
gpioConfig.ledInverted ? "true" : "false",
gpioConfig.ledPinRed == 0xff ? "null" : String(gpioConfig.ledPinRed, 10).c_str(),
gpioConfig.ledPinGreen == 0xff ? "null" : String(gpioConfig.ledPinGreen, 10).c_str(),
gpioConfig.ledPinBlue == 0xff ? "null" : String(gpioConfig.ledPinBlue, 10).c_str(),
gpioConfig.ledRgbInverted ? "true" : "false",
gpioConfig.ledDisablePin == 0xff ? "null" : String(gpioConfig.ledDisablePin, 10).c_str(),
gpioConfig.ledBehaviour,
gpioConfig.tempSensorPin == 0xff ? "null" : String(gpioConfig.tempSensorPin, 10).c_str(),
gpioConfig.tempAnalogSensorPin == 0xff ? "null" : String(gpioConfig.tempAnalogSensorPin, 10).c_str(),
gpioConfig.vccPin == 0xff ? "null" : String(gpioConfig.vccPin, 10).c_str(),
gpioConfig.vccOffset / 100.0,
gpioConfig.vccMultiplier / 1000.0,
gpioConfig.vccResistorVcc,
gpioConfig.vccResistorGnd,
gpioConfig.vccBootLimit / 10.0,
gpioConfig.powersaving
);
// UI
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"u\":{\"i\":%d,\"e\":%d,\"v\":%d,\"a\":%d,\"r\":%d,\"c\":%d,\"t\":%d,\"p\":%d,\"d\":%d,\"m\":%d,\"s\":%d,\"l\":%d,\"h\":%d,\"f\":%d,\"k\":%d,\"lang\":\"%s\"}"),
ui.showImport,
ui.showExport,
ui.showVoltage,
ui.showAmperage,
ui.showReactive,
ui.showRealtime,
ui.showPeaks,
ui.showPricePlot,
ui.showDayPlot,
ui.showMonthPlot,
ui.showTemperaturePlot,
ui.showRealtimePlot,
ui.showPerPhasePower,
ui.showPowerFactor,
ui.darkMode,
ui.language
);
// Domoticz
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"o\":{\"e\":%d,\"c\":%d,\"u1\":%d,\"u2\":%d,\"u3\":%d}"),
domo.elidx,
domo.cl1idx,
domo.vl1idx,
domo.vl2idx,
domo.vl3idx
);
// Home-Assistant
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"h\":{\"t\":\"%s\",\"h\":\"%s\",\"n\":\"%s\"}"),
haconf.discoveryPrefix,
haconf.discoveryHostname,
haconf.discoveryNameTag
);
// Cloud
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"c\":{\"e\":%s,\"p\":%d,\"es\":%s,\"ze\":%s,\"zt\":\"%s\"}"),
cloud.enabled ? "true" : "false",
cloud.proto,
#if defined(ESP32) && defined(ENERGY_SPEEDOMETER_PASS)
sysConfig.energyspeedometer == 7 ? "true" : "false",
#else
"null",
#endif
zcc.enabled ? "true" : "false",
zcc.token
);
pos += snprintf_P(buf+pos, bufSize-pos, PSTR("}")); // End of config
}

View File

@@ -23,23 +23,21 @@
class AmsMqttHandler {
public:
#if defined(AMS_REMOTE_DEBUG)
AmsMqttHandler(AmsConfiguration* config, RemoteDebug* debugger, char* buf, AmsFirmwareUpdater* updater) {
AmsMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, AmsFirmwareUpdater* updater) {
#else
AmsMqttHandler(AmsConfiguration* config, Stream* debugger, char* buf, AmsFirmwareUpdater* updater) {
AmsMqttHandler(MqttConfig& mqttConfig, Stream* debugger, char* buf, AmsFirmwareUpdater* updater) {
#endif
this->config = config;
config->getMqttConfig(mqttConfig);
mqttConfigChanged = true;
init(debugger, buf, updater);
this->mqttConfig = mqttConfig;
this->mqttConfigChanged = true;
this->debugger = debugger;
this->json = buf;
this->updater = updater;
mqtt.dropOverflow(true);
pubTopic = String(mqttConfig.publishTopic);
subTopic = String(mqttConfig.subscribeTopic);
if(subTopic.isEmpty()) subTopic = pubTopic+"/command";
};
#if defined(AMS_REMOTE_DEBUG)
AmsMqttHandler(MqttConfig& MqttConfig, RemoteDebug* debugger, char* buf, AmsFirmwareUpdater* updater) {
#else
AmsMqttHandler(MqttConfig& MqttConfig, Stream* debugger, char* buf, AmsFirmwareUpdater* updater) {
#endif
this->mqttConfig = MqttConfig;
init(debugger, buf, updater);
}
void setCaVerification(bool);
void setConfig(MqttConfig& mqttConfig);
@@ -81,49 +79,24 @@ protected:
#else
Stream* debugger;
#endif
AmsConfiguration* config;
MqttConfig mqttConfig;
bool mqttConfigChanged = true;
#if defined(ESP32)
MQTTClient mqtt = MQTTClient(2048);
#else
MQTTClient mqtt = MQTTClient(256);
#endif
unsigned long lastMqttRetry = -10000;
bool caVerification = true;
WiFiClient *mqttClient = NULL;
WiFiClientSecure *mqttSecureClient = NULL;
boolean _connected = false;
char* json;
uint16_t BufferSize = 2048;
uint64_t lastStateUpdate = 0;
uint64_t lastSuccessfulLoop = 0;
char pubTopic[64];
char subTopic[64];
String pubTopic;
String subTopic;
AmsFirmwareUpdater* updater = NULL;
bool rebootSuggested = false;
private:
#if defined(AMS_REMOTE_DEBUG)
void init(RemoteDebug* debugger, char* buf, AmsFirmwareUpdater* updater) {
#else
void init(Stream* debugger, char* buf, AmsFirmwareUpdater* updater) {
#endif
this->debugger = debugger;
this->json = buf;
this->updater = updater;
mqtt.dropOverflow(true);
strncpy(pubTopic, mqttConfig.publishTopic, sizeof(pubTopic) - 1);
pubTopic[sizeof(pubTopic) - 1] = '\0';
if(strlen(mqttConfig.subscribeTopic) > 0) {
strncpy(subTopic, mqttConfig.subscribeTopic, sizeof(subTopic) - 1);
subTopic[sizeof(subTopic) - 1] = '\0';
} else {
snprintf(subTopic, sizeof(subTopic), "%s/command", mqttConfig.publishTopic);
}
}
};
#endif

View File

@@ -118,9 +118,8 @@ bool AmsMqttHandler::connect() {
mqtt.setTimeout(mqttConfig.timeout);
mqtt.setKeepAlive(mqttConfig.keepalive);
mqtt.begin(mqttConfig.host, mqttConfig.port, *actualClient);
char statusTopic[72];
snprintf(statusTopic, sizeof(statusTopic), "%s/status", mqttConfig.publishTopic);
mqtt.setWill(statusTopic, "offline", true, 0);
String statusTopic = String(mqttConfig.publishTopic) + "/status";
mqtt.setWill(statusTopic.c_str(), "offline", true, 0);
#if defined(ESP8266)
if(mqttSecureClient) {
@@ -150,7 +149,7 @@ bool AmsMqttHandler::connect() {
debugger->printf_P(PSTR("Failed to connect to MQTT: %d\n"), mqtt.lastError());
#if defined(ESP8266)
if(mqttSecureClient) {
mqttSecureClient->getLastSSLError((char*) json, BUF_SIZE_COMMON);
mqttSecureClient->getLastSSLError((char*) json, BufferSize);
debugger->println((char*) json);
}
#endif
@@ -161,17 +160,17 @@ bool AmsMqttHandler::connect() {
bool AmsMqttHandler::defaultSubscribe() {
bool ret = true;
if(subTopic[0] != '\0') {
if(!subTopic.isEmpty()) {
if(mqtt.subscribe(subTopic)) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR(" Subscribed to [%s]\n"), subTopic);
debugger->printf_P(PSTR(" Subscribed to [%s]\n"), subTopic.c_str());
} else {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR(" Unable to subscribe to [%s]\n"), subTopic);
debugger->printf_P(PSTR(" Unable to subscribe to [%s]\n"), subTopic.c_str());
ret = false;
}
}

View File

@@ -29,7 +29,7 @@ bool DomoticzMqttHandler::publish(AmsData* update, AmsData* previousState, Energ
if(energy > 0.0) {
char val[16];
snprintf_P(val, 16, PSTR("%.1f;%.1f"), (data.getActiveImportPower()/1.0), energy*1000.0);
snprintf_P(json, BUF_SIZE_COMMON, DOMOTICZ_JSON,
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.elidx,
val
);
@@ -44,7 +44,7 @@ bool DomoticzMqttHandler::publish(AmsData* update, AmsData* previousState, Energ
if (config.vl1idx > 0){
char val[16];
snprintf_P(val, 16, PSTR("%.2f"), data.getL1Voltage());
snprintf_P(json, BUF_SIZE_COMMON, DOMOTICZ_JSON,
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.vl1idx,
val
);
@@ -55,7 +55,7 @@ bool DomoticzMqttHandler::publish(AmsData* update, AmsData* previousState, Energ
if (config.vl2idx > 0){
char val[16];
snprintf_P(val, 16, PSTR("%.2f"), data.getL2Voltage());
snprintf_P(json, BUF_SIZE_COMMON, DOMOTICZ_JSON,
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.vl2idx,
val
);
@@ -66,7 +66,7 @@ bool DomoticzMqttHandler::publish(AmsData* update, AmsData* previousState, Energ
if (config.vl3idx > 0){
char val[16];
snprintf(val, 16, "%.2f", data.getL3Voltage());
snprintf_P(json, BUF_SIZE_COMMON, DOMOTICZ_JSON,
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.vl3idx,
val
);
@@ -77,7 +77,7 @@ bool DomoticzMqttHandler::publish(AmsData* update, AmsData* previousState, Energ
if (config.cl1idx > 0){
char val[16];
snprintf(val, 16, "%.1f;%.1f;%.1f", data.getL1Current(), data.getL2Current(), data.getL3Current());
snprintf_P(json, BUF_SIZE_COMMON, DOMOTICZ_JSON,
snprintf_P(json, BufferSize, DOMOTICZ_JSON,
config.cl1idx,
val
);

View File

@@ -7,8 +7,6 @@
#ifndef _ENERGYACCOUNTING_H
#define _ENERGYACCOUNTING_H
#include "Arduino.h"
#include "AmsData.h"
#include "AmsDataStorage.h"
#include "PriceService.h"
@@ -83,7 +81,7 @@ public:
void setPriceService(PriceService *ps);
void setTimezone(Timezone*);
EnergyAccountingConfig* getConfig();
bool update(AmsData* amsData);
bool update(time_t now, uint64_t lastUpdatedMillis, uint8_t listType, uint32_t activeImportPower, uint32_t activeExportPower);
bool load();
bool save();
bool isInitialized();

View File

@@ -54,9 +54,8 @@ bool EnergyAccounting::isInitialized() {
return this->init;
}
bool EnergyAccounting::update(AmsData* amsData) {
bool EnergyAccounting::update(time_t now, uint64_t lastUpdatedMillis, uint8_t listType, uint32_t activeImportPower, uint32_t activeExportPower) {
if(config == NULL) return false;
time_t now = time(nullptr);
if(now < FirmwareVersion::BuildEpoch) return false;
if(tz == NULL) {
return false;
@@ -90,7 +89,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
calcDayCost();
}
if(local.Hour != realtimeData->currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) {
if(local.Hour != realtimeData->currentHour && (listType >= 3 || local.Minute == 1)) {
tmElements_t oneHrAgo, oneHrAgoLocal;
breakTime(now-3600, oneHrAgo);
uint16_t val = round(ds->getHourImport(oneHrAgo.Hour) / 10.0);
@@ -156,9 +155,9 @@ bool EnergyAccounting::update(AmsData* amsData) {
}
}
if(realtimeData->lastImportUpdateMillis < amsData->getLastUpdateMillis()) {
unsigned long ms = amsData->getLastUpdateMillis() - realtimeData->lastImportUpdateMillis;
float kwhi = (amsData->getActiveImportPower() * (((float) ms) / 3600000.0)) / 1000.0;
if(realtimeData->lastImportUpdateMillis < lastUpdatedMillis) {
unsigned long ms = lastUpdatedMillis - realtimeData->lastImportUpdateMillis;
float kwhi = (activeImportPower * (((float) ms) / 3600000.0)) / 1000.0;
if(kwhi > 0) {
realtimeData->use += kwhi;
float importPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_IMPORT);
@@ -168,12 +167,12 @@ bool EnergyAccounting::update(AmsData* amsData) {
realtimeData->costDay += cost;
}
}
realtimeData->lastImportUpdateMillis = amsData->getLastUpdateMillis();
realtimeData->lastImportUpdateMillis = lastUpdatedMillis;
}
if(amsData->getListType() > 1 && realtimeData->lastExportUpdateMillis < amsData->getLastUpdateMillis()) {
unsigned long ms = amsData->getLastUpdateMillis() - realtimeData->lastExportUpdateMillis;
float kwhe = (amsData->getActiveExportPower() * (((float) ms) / 3600000.0)) / 1000.0;
if(listType > 1 && realtimeData->lastExportUpdateMillis < lastUpdatedMillis) {
unsigned long ms = lastUpdatedMillis - realtimeData->lastExportUpdateMillis;
float kwhe = (activeExportPower * (((float) ms) / 3600000.0)) / 1000.0;
if(kwhe > 0) {
realtimeData->produce += kwhe;
float exportPrice = ps == NULL ? PRICE_NO_VALUE : ps->getCurrentPrice(PRICE_DIRECTION_EXPORT);
@@ -183,7 +182,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
realtimeData->incomeDay += income;
}
}
realtimeData->lastExportUpdateMillis = amsData->getLastUpdateMillis();
realtimeData->lastExportUpdateMillis = lastUpdatedMillis;
}
if(config != NULL) {

View File

@@ -88,7 +88,7 @@ bool HomeAssistantMqttHandler::postConnect() {
}
bool HomeAssistantMqttHandler::publish(AmsData* update, AmsData* previousState, EnergyAccounting* ea, PriceService* ps) {
if(pubTopic[0] == 'pubTopic.isEmpty()' || !connected())
if(pubTopic.isEmpty() || !connected())
return false;
if(time(nullptr) < FirmwareVersion::BuildEpoch)
@@ -135,9 +135,8 @@ bool HomeAssistantMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea)
char pt[24];
toJsonIsoTimestamp(data->getPackageTimestamp(), pt, sizeof(pt));
snprintf_P(json, BUF_SIZE_COMMON, HA1_JSON, data->getActiveImportPower(), pt);
{ char _t[72]; snprintf(_t, sizeof(_t), "%s/power", pubTopic);
return mqtt.publish(_t, json); }
snprintf_P(json, BufferSize, HA1_JSON, data->getActiveImportPower(), pt);
return mqtt.publish(pubTopic + "/power", json);
}
bool HomeAssistantMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
@@ -147,7 +146,7 @@ bool HomeAssistantMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea)
char pt[24];
toJsonIsoTimestamp(data->getPackageTimestamp(), pt, sizeof(pt));
snprintf_P(json, BUF_SIZE_COMMON, HA3_JSON,
snprintf_P(json, BufferSize, HA3_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
@@ -163,8 +162,7 @@ bool HomeAssistantMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea)
data->getL3Voltage(),
pt
);
{ char _t[72]; snprintf(_t, sizeof(_t), "%s/power", pubTopic);
return mqtt.publish(_t, json); }
return mqtt.publish(pubTopic + "/power", json);
}
bool HomeAssistantMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
@@ -178,7 +176,7 @@ bool HomeAssistantMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea)
memset(pt, 0, 24);
toJsonIsoTimestamp(data->getPackageTimestamp(), pt, sizeof(pt));
snprintf_P(json, BUF_SIZE_COMMON, HA2_JSON,
snprintf_P(json, BufferSize, HA2_JSON,
data->getActiveImportCounter(),
data->getActiveExportCounter(),
data->getReactiveImportCounter(),
@@ -186,8 +184,7 @@ bool HomeAssistantMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea)
mt,
pt
);
{ char _t[72]; snprintf(_t, sizeof(_t), "%s/energy", pubTopic);
return mqtt.publish(_t, json); }
return mqtt.publish(pubTopic + "/energy", json);
}
bool HomeAssistantMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
@@ -197,7 +194,7 @@ bool HomeAssistantMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea)
char pt[24];
toJsonIsoTimestamp(data->getPackageTimestamp(), pt, sizeof(pt));
snprintf_P(json, BUF_SIZE_COMMON, HA4_JSON,
snprintf_P(json, BufferSize, HA4_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
@@ -229,8 +226,7 @@ bool HomeAssistantMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea)
data->getL3ActiveExportCounter(),
pt
);
{ char _t[72]; snprintf(_t, sizeof(_t), "%s/power", pubTopic);
return mqtt.publish(_t, json); }
return mqtt.publish(pubTopic + "/power", json);
}
String HomeAssistantMqttHandler::getMeterModel(AmsData* data) {
@@ -250,7 +246,7 @@ bool HomeAssistantMqttHandler::publishRealtime(AmsData* data, EnergyAccounting*
if(!peaks.isEmpty()) peaks += ",";
peaks += String(ea->getPeak(i).value / 100.0, 2);
}
uint16_t pos = snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"max\":%.1f,\"peaks\":[%s],\"threshold\":%d,\"hour\":{\"use\":%.2f,\"cost\":%.2f,\"produced\":%.2f,\"income\":%.2f},\"day\":{\"use\":%.2f,\"cost\":%.2f,\"produced\":%.2f,\"income\":%.2f},\"month\":{\"use\":%.2f,\"cost\":%.2f,\"produced\":%.2f,\"income\":%.2f}"),
uint16_t pos = snprintf_P(json, BufferSize, PSTR("{\"max\":%.1f,\"peaks\":[%s],\"threshold\":%d,\"hour\":{\"use\":%.2f,\"cost\":%.2f,\"produced\":%.2f,\"income\":%.2f},\"day\":{\"use\":%.2f,\"cost\":%.2f,\"produced\":%.2f,\"income\":%.2f},\"month\":{\"use\":%.2f,\"cost\":%.2f,\"produced\":%.2f,\"income\":%.2f}"),
ea->getMonthMax(),
peaks.c_str(),
ea->getCurrentThreshold(),
@@ -270,7 +266,7 @@ bool HomeAssistantMqttHandler::publishRealtime(AmsData* data, EnergyAccounting*
uint32_t ms = millis();
if(lastThresholdPublish == 0 || ms-lastThresholdPublish > 3600000) {
EnergyAccountingConfig* conf = ea->getConfig();
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR(",\"thresholds\": [%d,%d,%d,%d,%d,%d,%d,%d,%d]"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR(",\"thresholds\": [%d,%d,%d,%d,%d,%d,%d,%d,%d]"),
conf->thresholds[0],
conf->thresholds[1],
conf->thresholds[2],
@@ -287,13 +283,12 @@ bool HomeAssistantMqttHandler::publishRealtime(AmsData* data, EnergyAccounting*
time_t now = time(nullptr);
char pt[24];
toJsonIsoTimestamp(now, pt, sizeof(pt));
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR(",\"t\":%s"), pt);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR(",\"t\":%s"), pt);
json[pos++] = '}';
json[pos] = '\0';
{ char _t[72]; snprintf(_t, sizeof(_t), "%s/realtime", pubTopic);
return mqtt.publish(_t, json); }
return mqtt.publish(pubTopic + "/realtime", json);
}
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
@@ -306,7 +301,7 @@ bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwT
TempSensorData* data = hw->getTempSensorData(i);
if(data != NULL) {
String id = toHex(data->address, 8);
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"%s\":%.2f,"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"%s\":%.2f,"),
id.c_str(),
data->lastRead
);
@@ -314,22 +309,21 @@ bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwT
publishTemperatureSensor(i+1, id);
}
}
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("}"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("}"));
time_t now = time(nullptr);
char pt[24];
toJsonIsoTimestamp(now, pt, sizeof(pt));
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR(",\"t\":%s"), pt);
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("}"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR(",\"t\":%s"), pt);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("}"));
char _t[72]; snprintf(_t, sizeof(_t), "%s/temperatures", pubTopic);
bool ret = mqtt.publish(_t, json);
bool ret = mqtt.publish(pubTopic + "/temperatures", json);
loop();
return ret;
}
bool HomeAssistantMqttHandler::publishPrices(PriceService* ps) {
if(pubTopic[0] == 'pubTopic.isEmpty()' || !connected())
if(pubTopic.isEmpty() || !connected())
return false;
if(!ps->hasPrice())
return false;
@@ -422,33 +416,33 @@ bool HomeAssistantMqttHandler::publishPrices(PriceService* ps) {
toJsonIsoTimestamp(ts, ts6hr, sizeof(ts6hr));
}
uint16_t pos = snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"id\":\"%s\",\"prices\":{\"import\":["), WiFi.macAddress().c_str());
uint16_t pos = snprintf_P(json, BufferSize, PSTR("{\"id\":\"%s\",\"prices\":{\"import\":["), WiFi.macAddress().c_str());
uint8_t currentPricePointIndex = ps->getCurrentPricePointIndex();
uint8_t numberOfPoints = ps->getNumberOfPointsAvailable();
for(int i = currentPricePointIndex; i < numberOfPoints; i++) {
float val = ps->getPricePoint(PRICE_DIRECTION_IMPORT, i);
if(val == PRICE_NO_VALUE) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("null,"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("null,"));
} else {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("%.4f,"), val);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("%.4f,"), val);
}
}
if(rteInit && ps->isExportPricesDifferentFromImport()) {
pos--;
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("],\"export\":["));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("],\"export\":["));
for(int i = currentPricePointIndex; i < numberOfPoints; i++) {
float val = ps->getPricePoint(PRICE_DIRECTION_EXPORT, i);
if(val == PRICE_NO_VALUE) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("null,"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("null,"));
} else {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("%.4f,"), val);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("%.4f,"), val);
}
}
}
pos--;
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("],\"min\":%.4f,\"max\":%.4f,\"cheapest1hr\":%s,\"cheapest3hr\":%s,\"cheapest6hr\":%s}"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("],\"min\":%.4f,\"max\":%.4f,\"cheapest1hr\":%s,\"cheapest3hr\":%s,\"cheapest6hr\":%s}"),
min == INT16_MAX ? 0.0 : min,
max == INT16_MIN ? 0.0 : max,
ts1hr,
@@ -458,19 +452,18 @@ bool HomeAssistantMqttHandler::publishPrices(PriceService* ps) {
char pt[24];
toJsonIsoTimestamp(now, pt, sizeof(pt));
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR(",\"t\":%s"), pt);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR(",\"t\":%s"), pt);
json[pos++] = '}';
json[pos] = '\0';
char _t[72]; snprintf(_t, sizeof(_t), "%s/prices", pubTopic);
bool ret = mqtt.publish(_t, json, true, 0);
bool ret = mqtt.publish(pubTopic + "/prices", json, true, 0);
loop();
return ret;
}
bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea) {
if(pubTopic[0] == 'pubTopic.isEmpty()' || !connected())
if(pubTopic.isEmpty() || !connected())
return false;
publishSystemSensors();
@@ -480,7 +473,7 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, PriceService* ps, Ener
char pt[24];
toJsonIsoTimestamp(now, pt, sizeof(pt));
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"id\":\"%s\",\"name\":\"%s\",\"up\":%d,\"vcc\":%.3f,\"rssi\":%d,\"temp\":%.2f,\"version\":\"%s\",\"t\":%s}"),
snprintf_P(json, BufferSize, PSTR("{\"id\":\"%s\",\"name\":\"%s\",\"up\":%d,\"vcc\":%.3f,\"rssi\":%d,\"temp\":%.2f,\"version\":\"%s\",\"t\":%s}"),
WiFi.macAddress().c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
@@ -490,8 +483,7 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, PriceService* ps, Ener
FirmwareVersion::VersionString,
pt
);
char _t[72]; snprintf(_t, sizeof(_t), "%s/state", pubTopic);
bool ret = mqtt.publish(_t, json);
bool ret = mqtt.publish(pubTopic + "/state", json);
loop();
return ret;
}
@@ -507,7 +499,7 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor sensor) {
uid.replace("]", "");
uid.replace("'", "");
}
snprintf_P(json, BUF_SIZE_COMMON, HADISCOVER_JSON,
snprintf_P(json, BufferSize, HADISCOVER_JSON,
sensorNamePrefix.c_str(),
sensor.name,
mqttConfig.publishTopic, sensor.topic,
@@ -813,7 +805,7 @@ bool HomeAssistantMqttHandler::publishRaw(uint8_t* raw, size_t length) {
if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
if(length <= 0 || length > BUF_SIZE_COMMON) return false;
if(length <= 0 || length > BufferSize) return false;
if(!dInit) {
// Not sure how this sensor should be defined in HA, so skipping for now
@@ -823,7 +815,7 @@ bool HomeAssistantMqttHandler::publishRaw(uint8_t* raw, size_t length) {
String str = toHex(raw, length);
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"data\":\"%s\"}"), str.c_str());
snprintf_P(json, BufferSize, PSTR("{\"data\":\"%s\"}"), str.c_str());
char topic[192];
snprintf_P(topic, 192, PSTR("%s/data"), mqttConfig.publishTopic);
bool ret = mqtt.publish(topic, json);
@@ -833,24 +825,23 @@ bool HomeAssistantMqttHandler::publishRaw(uint8_t* raw, size_t length) {
bool HomeAssistantMqttHandler::publishFirmware() {
if(!fInit) {
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"name\":\"%sFirmware\",\"stat_t\":\"%s/firmware\",\"uniq_id\":\"%s_fwupgrade\",\"dev_cla\":\"firmware\",\"cmd_t\":\"%s\",\"pl_inst\":\"fwupgrade\"}"),
snprintf_P(json, BufferSize, PSTR("{\"name\":\"%sFirmware\",\"stat_t\":\"%s/firmware\",\"uniq_id\":\"%s_fwupgrade\",\"dev_cla\":\"firmware\",\"cmd_t\":\"%s\",\"pl_inst\":\"fwupgrade\"}"),
sensorNamePrefix.c_str(),
pubTopic,
pubTopic.c_str(),
deviceUid.c_str(),
subTopic
subTopic.c_str()
);
fInit = mqtt.publish(updateTopic + "/" + deviceUid + "/config", json, true, 0);
loop();
return fInit;
}
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"installed_version\":\"%s\",\"latest_version\":\"%s\",\"title\":\"amsreader firmware\",\"release_url\":\"https://github.com/UtilitechAS/amsreader-firmware/releases\",\"release_summary\":\"New version %s is available\",\"update_percentage\":%s}"),
snprintf_P(json, BufferSize, PSTR("{\"installed_version\":\"%s\",\"latest_version\":\"%s\",\"title\":\"amsreader firmware\",\"release_url\":\"https://github.com/UtilitechAS/amsreader-firmware/releases\",\"release_summary\":\"New version %s is available\",\"update_percentage\":%s}"),
FirmwareVersion::VersionString,
strlen(updater->getNextVersion()) == 0 ? FirmwareVersion::VersionString : updater->getNextVersion(),
strlen(updater->getNextVersion()) == 0 ? FirmwareVersion::VersionString : updater->getNextVersion(),
updater->getProgress() < 0 ? "null" : String(updater->getProgress(), 0)
);
char _t[72]; snprintf(_t, sizeof(_t), "%s/firmware", pubTopic);
bool ret = mqtt.publish(_t, json);
bool ret = mqtt.publish(pubTopic + "/firmware", json);
loop();
return ret;
}
@@ -867,7 +858,7 @@ void HomeAssistantMqttHandler::onMessage(String &topic, String &payload) {
priceImportInit = 0;
priceExportInit = 0;
}
} else if(topic == subTopic) {
} else if(topic.equals(subTopic)) {
if(payload.equals("fwupgrade")) {
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
updater->setTargetVersion(updater->getNextVersion());

View File

@@ -8,18 +8,9 @@
#define _JSONMQTTHANDLER_H
#include "AmsMqttHandler.h"
#include "ArduinoJson.h"
class JsonMqttHandler : public AmsMqttHandler {
public:
#if defined(AMS_REMOTE_DEBUG)
JsonMqttHandler(AmsConfiguration* config, RemoteDebug* debugger, char* buf, HwTools* hw, AmsDataStorage* ds, AmsFirmwareUpdater* updater) : AmsMqttHandler(config, debugger, buf, updater) {
#else
JsonMqttHandler(AmsConfiguration* config, Stream* debugger, char* buf, HwTools* hw, AmsDataStorage* ds, AmsFirmwareUpdater* updater) : AmsMqttHandler(config, debugger, buf, updater) {
#endif
this->hw = hw;
this->ds = ds;
};
#if defined(AMS_REMOTE_DEBUG)
JsonMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, HwTools* hw, AmsDataStorage* ds, AmsFirmwareUpdater* updater) : AmsMqttHandler(mqttConfig, debugger, buf, updater) {
#else
@@ -52,6 +43,5 @@ private:
bool publishList4(AmsData* data, EnergyAccounting* ea);
String getMeterModel(AmsData* data);
void toJsonIsoTimestamp(time_t t, char* buf, size_t buflen);
void handleConfigMessage(JsonObject& configObj);
};
#endif

View File

@@ -19,7 +19,7 @@ bool JsonMqttHandler::publish(AmsData* update, AmsData* previousState, EnergyAcc
}
bool ret = false;
memset(json, 0, BUF_SIZE_COMMON);
memset(json, 0, BufferSize);
AmsData data;
if(mqttConfig.stateUpdate) {
@@ -59,7 +59,7 @@ bool JsonMqttHandler::publish(AmsData* update, AmsData* previousState, EnergyAcc
}
uint16_t JsonMqttHandler::appendJsonHeader(AmsData* data) {
return snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"id\":\"%s\",\"name\":\"%s\",\"up\":%u,\"t\":%lu,\"vcc\":%.3f,\"rssi\":%d,\"temp\":%.2f,"),
return snprintf_P(json, BufferSize, PSTR("{\"id\":\"%s\",\"name\":\"%s\",\"up\":%u,\"t\":%lu,\"vcc\":%.3f,\"rssi\":%d,\"temp\":%.2f,"),
WiFi.macAddress().c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
@@ -86,7 +86,7 @@ uint16_t JsonMqttHandler::appendJsonFooter(EnergyAccounting* ea, uint16_t pos) {
peaks += String(ea->getPeak(i).value / 100.0, 2);
}
return snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("%s\"%sh\":%.3f,\"%sd\":%.2f,\"%sm\":%.1f,\"%st\":%d,\"%sx\":%.2f,\"%she\":%.3f,\"%sde\":%.2f,\"%sme\":%.1f,\"peaks\":[%s]%s"),
return snprintf_P(json+pos, BufferSize-pos, PSTR("%s\"%sh\":%.3f,\"%sd\":%.2f,\"%sm\":%.1f,\"%st\":%d,\"%sx\":%.2f,\"%she\":%.3f,\"%sde\":%.2f,\"%sme\":%.1f,\"peaks\":[%s]%s"),
strlen(pf) == 0 ? "},\"realtime\":{" : ",",
pf,
ea->getUseThisHour(),
@@ -112,9 +112,9 @@ uint16_t JsonMqttHandler::appendJsonFooter(EnergyAccounting* ea, uint16_t pos) {
bool JsonMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
uint16_t pos = appendJsonHeader(data);
if(mqttConfig.payloadFormat != 6) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"data\":{"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"data\":{"));
}
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"P\":%d"), data->getActiveImportPower());
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"P\":%d"), data->getActiveImportPower());
pos += appendJsonFooter(ea, pos);
json[pos++] = '}';
json[pos] = '\0';
@@ -130,9 +130,9 @@ bool JsonMqttHandler::publishList1(AmsData* data, EnergyAccounting* ea) {
bool JsonMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
uint16_t pos = appendJsonHeader(data);
if(mqttConfig.payloadFormat != 6) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"data\":{"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"data\":{"));
}
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"lv\":\"%s\",\"meterId\":\"%s\",\"type\":\"%s\",\"P\":%d,\"Q\":%d,\"PO\":%d,\"QO\":%d,\"I1\":%.2f,\"I2\":%.2f,\"I3\":%.2f,\"U1\":%.2f,\"U2\":%.2f,\"U3\":%.2f"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"lv\":\"%s\",\"meterId\":\"%s\",\"type\":\"%s\",\"P\":%d,\"Q\":%d,\"PO\":%d,\"QO\":%d,\"I1\":%.2f,\"I2\":%.2f,\"I3\":%.2f,\"U1\":%.2f,\"U2\":%.2f,\"U3\":%.2f"),
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
@@ -162,9 +162,9 @@ bool JsonMqttHandler::publishList2(AmsData* data, EnergyAccounting* ea) {
bool JsonMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
uint16_t pos = appendJsonHeader(data);
if(mqttConfig.payloadFormat != 6) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"data\":{"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"data\":{"));
}
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"lv\":\"%s\",\"meterId\":\"%s\",\"type\":\"%s\",\"P\":%d,\"Q\":%d,\"PO\":%d,\"QO\":%d,\"I1\":%.2f,\"I2\":%.2f,\"I3\":%.2f,\"U1\":%.2f,\"U2\":%.2f,\"U3\":%.2f,\"tPI\":%.3f,\"tPO\":%.3f,\"tQI\":%.3f,\"tQO\":%.3f,\"rtc\":%lu"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"lv\":\"%s\",\"meterId\":\"%s\",\"type\":\"%s\",\"P\":%d,\"Q\":%d,\"PO\":%d,\"QO\":%d,\"I1\":%.2f,\"I2\":%.2f,\"I3\":%.2f,\"U1\":%.2f,\"U2\":%.2f,\"U3\":%.2f,\"tPI\":%.3f,\"tPO\":%.3f,\"tQI\":%.3f,\"tQO\":%.3f,\"rtc\":%lu"),
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
@@ -199,9 +199,9 @@ bool JsonMqttHandler::publishList3(AmsData* data, EnergyAccounting* ea) {
bool JsonMqttHandler::publishList4(AmsData* data, EnergyAccounting* ea) {
uint16_t pos = appendJsonHeader(data);
if(mqttConfig.payloadFormat != 6) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"data\":{"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"data\":{"));
}
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"lv\":\"%s\",\"meterId\":\"%s\",\"type\":\"%s\",\"P\":%d,\"P1\":%d,\"P2\":%d,\"P3\":%d,\"Q\":%d,\"PO\":%d,\"PO1\":%d,\"PO2\":%d,\"PO3\":%d,\"QO\":%d,\"I1\":%.2f,\"I2\":%.2f,\"I3\":%.2f,\"U1\":%.2f,\"U2\":%.2f,\"U3\":%.2f,\"PF\":%.2f,\"PF1\":%.2f,\"PF2\":%.2f,\"PF3\":%.2f,\"tPI\":%.3f,\"tPO\":%.3f,\"tQI\":%.3f,\"tQO\":%.3f,\"tPI1\":%.3f,\"tPI2\":%.3f,\"tPI3\":%.3f,\"tPO1\":%.3f,\"tPO2\":%.3f,\"tPO3\":%.3f,\"rtc\":%lu"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"lv\":\"%s\",\"meterId\":\"%s\",\"type\":\"%s\",\"P\":%d,\"P1\":%d,\"P2\":%d,\"P3\":%d,\"Q\":%d,\"PO\":%d,\"PO1\":%d,\"PO2\":%d,\"PO3\":%d,\"QO\":%d,\"I1\":%.2f,\"I2\":%.2f,\"I3\":%.2f,\"U1\":%.2f,\"U2\":%.2f,\"U3\":%.2f,\"PF\":%.2f,\"PF1\":%.2f,\"PF2\":%.2f,\"PF3\":%.2f,\"tPI\":%.3f,\"tPO\":%.3f,\"tQI\":%.3f,\"tQO\":%.3f,\"tPI1\":%.3f,\"tPI2\":%.3f,\"tPI3\":%.3f,\"tPO1\":%.3f,\"tPO2\":%.3f,\"tPO3\":%.3f,\"rtc\":%lu"),
data->getListId().c_str(),
data->getMeterId().c_str(),
getMeterModel(data).c_str(),
@@ -387,17 +387,17 @@ bool JsonMqttHandler::publishPrices(PriceService* ps) {
}
if(mqttConfig.payloadFormat == 6) {
uint16_t pos = snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"id\":\"%s\","), WiFi.macAddress().c_str());
uint16_t pos = snprintf_P(json, BufferSize, PSTR("{\"id\":\"%s\","), WiFi.macAddress().c_str());
for(uint8_t i = 0;i < 38; i++) {
if(values[i] == PRICE_NO_VALUE) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"pr_%d\":null,"), i);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"pr_%d\":null,"), i);
} else {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"pr_%d\":%.4f,"), i, values[i]);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"pr_%d\":%.4f,"), i, values[i]);
}
}
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("\"pr_min\":%.4f,\"pr_max\":%.4f,\"pr_cheapest1hr\":%s,\"pr_cheapest3hr\":%s,\"pr_cheapest6hr\":%s}"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("\"pr_min\":%.4f,\"pr_max\":%.4f,\"pr_cheapest1hr\":%s,\"pr_cheapest3hr\":%s,\"pr_cheapest6hr\":%s}"),
min == INT16_MAX ? 0.0 : min,
max == INT16_MIN ? 0.0 : max,
ts1hr,
@@ -405,33 +405,33 @@ bool JsonMqttHandler::publishPrices(PriceService* ps) {
ts6hr
);
} else {
uint16_t pos = snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"id\":\"%s\",\"prices\":{\"import\":["), WiFi.macAddress().c_str());
uint16_t pos = snprintf_P(json, BufferSize, PSTR("{\"id\":\"%s\",\"prices\":{\"import\":["), WiFi.macAddress().c_str());
uint8_t currentPricePointIndex = ps->getCurrentPricePointIndex();
uint8_t numberOfPoints = ps->getNumberOfPointsAvailable();
for(int i = currentPricePointIndex; i < numberOfPoints; i++) {
float val = ps->getPricePoint(PRICE_DIRECTION_IMPORT, i);
if(val == PRICE_NO_VALUE) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("null,"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("null,"));
} else {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("%.4f,"), val);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("%.4f,"), val);
}
}
if(hasExport && ps->isExportPricesDifferentFromImport()) {
pos--;
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("],\"export\":["));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("],\"export\":["));
for(int i = currentPricePointIndex; i < numberOfPoints; i++) {
float val = ps->getPricePoint(PRICE_DIRECTION_EXPORT, i);
if(val == PRICE_NO_VALUE) {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("null,"));
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("null,"));
} else {
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("%.4f,"), val);
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("%.4f,"), val);
}
}
}
pos--;
pos += snprintf_P(json+pos, BUF_SIZE_COMMON-pos, PSTR("],\"min\":%.4f,\"max\":%.4f,\"cheapest1hr\":%s,\"cheapest3hr\":%s,\"cheapest6hr\":%s}}"),
pos += snprintf_P(json+pos, BufferSize-pos, PSTR("],\"min\":%.4f,\"max\":%.4f,\"cheapest1hr\":%s,\"cheapest3hr\":%s,\"cheapest6hr\":%s}}"),
min == INT16_MAX ? 0.0 : min,
max == INT16_MIN ? 0.0 : max,
ts1hr,
@@ -456,7 +456,7 @@ bool JsonMqttHandler::publishSystem(HwTools* hw, PriceService* ps, EnergyAccount
if(strlen(mqttConfig.publishTopic) == 0 || !connected())
return false;
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"id\":\"%s\",\"name\":\"%s\",\"up\":%d,\"vcc\":%.3f,\"rssi\":%d,\"temp\":%.2f,\"version\":\"%s\"}"),
snprintf_P(json, BufferSize, PSTR("{\"id\":\"%s\",\"name\":\"%s\",\"up\":%d,\"vcc\":%.3f,\"rssi\":%d,\"temp\":%.2f,\"version\":\"%s\"}"),
WiFi.macAddress().c_str(),
mqttConfig.clientId,
(uint32_t) (millis64()/1000),
@@ -485,11 +485,11 @@ bool JsonMqttHandler::publishRaw(uint8_t* raw, size_t length) {
if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
if(length <= 0 || length > BUF_SIZE_COMMON) return false;
if(length <= 0 || length > BufferSize) return false;
String str = toHex(raw, length);
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"data\":\"%s\"}"), str.c_str());
snprintf_P(json, BufferSize, PSTR("{\"data\":\"%s\"}"), str.c_str());
char topic[192];
snprintf_P(topic, 192, PSTR("%s/data"), mqttConfig.publishTopic);
bool ret = mqtt.publish(topic, json);
@@ -498,7 +498,7 @@ bool JsonMqttHandler::publishRaw(uint8_t* raw, size_t length) {
}
bool JsonMqttHandler::publishFirmware() {
snprintf_P(json, BUF_SIZE_COMMON, PSTR("{\"installed_version\":\"%s\",\"latest_version\":\"%s\",\"title\":\"amsreader firmware\",\"release_url\":\"https://github.com/UtilitechAS/amsreader-firmware/releases\",\"release_summary\":\"New version %s is available\",\"update_percentage\":%s}"),
snprintf_P(json, BufferSize, PSTR("{\"installed_version\":\"%s\",\"latest_version\":\"%s\",\"title\":\"amsreader firmware\",\"release_url\":\"https://github.com/UtilitechAS/amsreader-firmware/releases\",\"release_summary\":\"New version %s is available\",\"update_percentage\":%s}"),
FirmwareVersion::VersionString,
strlen(updater->getNextVersion()) == 0 ? FirmwareVersion::VersionString : updater->getNextVersion(),
strlen(updater->getNextVersion()) == 0 ? FirmwareVersion::VersionString : updater->getNextVersion(),
@@ -520,624 +520,31 @@ void JsonMqttHandler::onMessage(String &topic, String &payload) {
#endif
debugger->printf_P(PSTR("Received command [%s] to [%s]\n"), payload.c_str(), topic.c_str());
if(topic == subTopic) {
if(topic.equals(subTopic)) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR(" - this is our subscribed topic\n"));
if(payload.startsWith("{")) {
DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, payload);
if(error) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR(" - failed to parse JSON: %s\n"), error.c_str());
return;
} else {
JsonObject obj = doc.as<JsonObject>();
if(obj.containsKey(F("action"))) {
const char* action = obj[F("action")];
if(strcmp_P(action, PSTR("fwupgrade")) == 0) {
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
updater->setTargetVersion(updater->getNextVersion());
}
} else if(strcmp_P(action, PSTR("dayplot")) == 0) {
char pubTopic[192];
snprintf_P(pubTopic, 192, PSTR("%s/dayplot"), mqttConfig.publishTopic);
AmsJsonGenerator::generateDayPlotJson(ds, json, BUF_SIZE_COMMON);
bool ret = mqtt.publish(pubTopic, json);
loop();
} else if(strcmp_P(action, PSTR("monthplot")) == 0) {
char pubTopic[192];
snprintf_P(pubTopic, 192, PSTR("%s/monthplot"), mqttConfig.publishTopic);
AmsJsonGenerator::generateMonthPlotJson(ds, json, BUF_SIZE_COMMON);
bool ret = mqtt.publish(pubTopic, json);
loop();
#if defined(ESP32)
} else if(strcmp_P(action, PSTR("getconfig")) == 0) {
char pubTopic[192];
snprintf_P(pubTopic, 192, PSTR("%s/config"), mqttConfig.publishTopic);
AmsJsonGenerator::generateConfigurationJson(config, json, BUF_SIZE_COMMON);
bool ret = mqtt.publish(pubTopic, json);
loop();
} else if(strcmp_P(action, PSTR("setconfig")) == 0 && obj.containsKey(F("config"))) {
JsonObject configObj = obj[F("config")];
handleConfigMessage(configObj);
#endif
}
}
}
} else if(payload.equals(F("fwupgrade"))) {
if(payload.equals("fwupgrade")) {
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
updater->setTargetVersion(updater->getNextVersion());
}
} else if(payload.equals(F("dayplot"))) {
} else if(payload.equals("dayplot")) {
char pubTopic[192];
snprintf_P(pubTopic, 192, PSTR("%s/dayplot"), mqttConfig.publishTopic);
AmsJsonGenerator::generateDayPlotJson(ds, json, BUF_SIZE_COMMON);
AmsJsonGenerator::generateDayPlotJson(ds, json, BufferSize);
bool ret = mqtt.publish(pubTopic, json);
loop();
} else if(payload.equals(F("monthplot"))) {
} else if(payload.equals("monthplot")) {
char pubTopic[192];
snprintf_P(pubTopic, 192, PSTR("%s/monthplot"), mqttConfig.publishTopic);
AmsJsonGenerator::generateMonthPlotJson(ds, json, BUF_SIZE_COMMON);
AmsJsonGenerator::generateMonthPlotJson(ds, json, BufferSize);
bool ret = mqtt.publish(pubTopic, json);
loop();
}
}
}
#if defined(ESP32)
void JsonMqttHandler::handleConfigMessage(JsonObject& configObj) {
// General
if(configObj.containsKey(F("g"))) {
JsonObject generalObj = configObj[F("g")];
if(generalObj.containsKey(F("t"))) {
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
strlcpy(ntpConfig.timezone, generalObj[F("t")], sizeof(ntpConfig.timezone));
config->setNtpConfig(ntpConfig);
}
if(generalObj.containsKey(F("h"))) {
NetworkConfig networkConfig;
config->getNetworkConfig(networkConfig);
strlcpy(networkConfig.hostname, generalObj[F("h")], sizeof(networkConfig.hostname));
config->setNetworkConfig(networkConfig);
}
WebConfig webConfig;
config->getWebConfig(webConfig);
if(generalObj.containsKey(F("s"))) {
webConfig.security = generalObj[F("s")];
}
if(webConfig.security > 0) {
if(generalObj.containsKey(F("u"))) {
strlcpy(webConfig.username, generalObj[F("u")], sizeof(webConfig.username));
}
// Check if password is provided and that it is not empty and is not equal *** (which is used in the UI to indicate that the password is set but not shown)
if(generalObj.containsKey(F("p")) && strlen(generalObj[F("p")]) > 0 && strcmp(generalObj[F("p")], "***") != 0) {
strlcpy(webConfig.password, generalObj[F("p")], sizeof(webConfig.password));
}
}
if(generalObj.containsKey(F("c"))) {
strlcpy(webConfig.context, generalObj[F("c")], sizeof(webConfig.context));
}
config->setWebConfig(webConfig);
}
// Meter
if(configObj.containsKey(F("m"))) {
JsonObject meterObj = configObj[F("m")];
MeterConfig newConfig;
config->getMeterConfig(newConfig);
if(meterObj.containsKey(F("o"))) {
newConfig.source = meterObj[F("o")];
}
if(meterObj.containsKey(F("a"))) {
newConfig.parser = meterObj[F("a")];
}
if(meterObj.containsKey(F("b"))) {
newConfig.baud = meterObj[F("b")];
}
if(meterObj.containsKey(F("p"))) {
newConfig.parity = meterObj[F("p")];
}
if(meterObj.containsKey(F("i"))) {
newConfig.invert = meterObj[F("i")];
}
if(meterObj.containsKey(F("s"))) {
newConfig.bufferSize = (int) meterObj[F("s")] / 64; // convert from bytes to 64 byte blocks
}
if(meterObj.containsKey(F("d"))) {
newConfig.distributionSystem = meterObj[F("d")];
}
if(meterObj.containsKey(F("f"))) {
newConfig.mainFuse = meterObj[F("f")];
}
if(meterObj.containsKey(F("r"))) {
newConfig.productionCapacity = meterObj[F("r")];
}
if(meterObj.containsKey(F("e"))) {
JsonObject encryptionObj = meterObj[F("e")];
if(encryptionObj.containsKey(F("e"))) {
bool enabled = encryptionObj[F("e")];
if(enabled) {
if(encryptionObj.containsKey(F("k"))) {
String encryptionKeyHex = encryptionObj[F("k")];
if(encryptionKeyHex.length() == 16) {
fromHex(newConfig.encryptionKey, encryptionKeyHex, 16);
}
}
if(encryptionObj.containsKey(F("a"))) {
String authenticationKeyHex = encryptionObj[F("a")];
if(authenticationKeyHex.length() == 16) {
fromHex(newConfig.authenticationKey, authenticationKeyHex, 16);
}
}
} else {
memset(newConfig.encryptionKey, 0, sizeof(newConfig.encryptionKey));
memset(newConfig.authenticationKey, 0, sizeof(newConfig.authenticationKey));
}
}
}
if(meterObj.containsKey(F("m"))) {
JsonObject multipliersObj = meterObj[F("m")];
bool enabled = multipliersObj[F("e")];
if(enabled) {
if(multipliersObj.containsKey(F("w"))) {
newConfig.wattageMultiplier = multipliersObj[F("w")];
}
if(multipliersObj.containsKey(F("v"))) {
newConfig.voltageMultiplier = multipliersObj[F("v")];
}
if(multipliersObj.containsKey(F("a"))) {
newConfig.amperageMultiplier = multipliersObj[F("a")];
}
if(multipliersObj.containsKey(F("c"))) {
newConfig.accumulatedMultiplier = multipliersObj[F("c")];
}
} else {
newConfig.wattageMultiplier = 1.0;
newConfig.voltageMultiplier = 1.0;
newConfig.amperageMultiplier = 1.0;
newConfig.accumulatedMultiplier = 1.0;
}
}
config->setMeterConfig(newConfig);
}
// Network
if(configObj.containsKey(F("n"))) {
NetworkConfig newConfig;
config->getNetworkConfig(newConfig);
JsonObject networkObj = configObj[F("n")];
if(networkObj.containsKey(F("c"))) {
newConfig.mode = networkObj[F("c")];
}
if(networkObj.containsKey(F("m"))) {
if(strcmp_P(networkObj[F("m")], PSTR("dhcp")) == 0) {
newConfig.mode = 1;
} else if(strcmp_P(networkObj[F("m")], PSTR("static")) == 0) {
newConfig.mode = 2;
}
}
if(networkObj.containsKey(F("i"))) {
strlcpy(newConfig.ip, networkObj[F("i")], sizeof(newConfig.ip));
}
if(networkObj.containsKey(F("s"))) {
strlcpy(newConfig.subnet, networkObj[F("s")], sizeof(newConfig.subnet));
}
if(networkObj.containsKey(F("g"))) {
strlcpy(newConfig.gateway, networkObj[F("g")], sizeof(newConfig.gateway));
}
if(networkObj.containsKey(F("d1"))) {
strlcpy(newConfig.dns1, networkObj[F("d1")], sizeof(newConfig.dns1));
}
if(networkObj.containsKey(F("d2"))) {
strlcpy(newConfig.dns2, networkObj[F("d2")], sizeof(newConfig.dns2));
}
if(networkObj.containsKey(F("d"))) {
newConfig.mdns = networkObj[F("d")];
}
if(networkObj.containsKey(F("x"))) {
newConfig.ipv6 = networkObj[F("x")];
}
config->setNetworkConfig(newConfig);
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
if(networkObj.containsKey(F("n1"))) {
strlcpy(ntpConfig.server, networkObj[F("n1")], sizeof(ntpConfig.server));
config->setNtpConfig(ntpConfig);
}
if(networkObj.containsKey(F("h"))) {
ntpConfig.dhcp = networkObj[F("h")];
config->setNtpConfig(ntpConfig);
}
config->getNtpConfig(ntpConfig);
}
// WiFi
if(configObj.containsKey(F("w"))) {
NetworkConfig newConfig;
config->getNetworkConfig(newConfig);
if(newConfig.mode == 1 || newConfig.mode == 2) {
JsonObject wifiObj = configObj[F("w")];
if(wifiObj.containsKey(F("s"))) {
strlcpy(newConfig.ssid, wifiObj[F("s")], sizeof(newConfig.ssid));
}
// Check if PSK is provided and that it is not empty and is not equal *** (which is used in the UI to indicate that the password is set but not shown)
if(wifiObj.containsKey(F("p")) && strlen(wifiObj[F("p")]) > 0 && strcmp(wifiObj[F("p")], "***") != 0) {
strlcpy(newConfig.psk, wifiObj[F("p")], sizeof(newConfig.psk));
}
if(wifiObj.containsKey(F("w"))) {
newConfig.power = wifiObj[F("w")];
}
if(wifiObj.containsKey(F("z"))) {
newConfig.sleep = wifiObj[F("z")];
}
if(wifiObj.containsKey(F("b"))) {
newConfig.use11b = wifiObj[F("b")];
}
config->setNetworkConfig(newConfig);
}
}
// MQTT
if(configObj.containsKey(F("q"))) {
JsonObject mqttObj = configObj[F("q")];
MqttConfig newConfig;
config->getMqttConfig(newConfig);
if(mqttObj.containsKey(F("p"))) {
newConfig.port = mqttObj[F("p")];
}
if(mqttObj.containsKey(F("u"))) {
strlcpy(newConfig.username, mqttObj[F("u")], sizeof(newConfig.username));
}
// Check if password is provided and that it is not empty and is not equal *** (which is used in the UI to indicate that the password is set but not shown)
if(mqttObj.containsKey(F("a")) && strlen(mqttObj[F("a")]) > 0 && strcmp(mqttObj[F("a")], "***") != 0) {
strlcpy(newConfig.password, mqttObj[F("a")], sizeof(newConfig.password));
}
if(mqttObj.containsKey(F("c"))) {
strlcpy(newConfig.clientId, mqttObj[F("c")], sizeof(newConfig.clientId));
}
if(mqttObj.containsKey(F("b"))) {
strlcpy(newConfig.publishTopic, mqttObj[F("b")], sizeof(newConfig.publishTopic));
}
if(mqttObj.containsKey(F("r"))) {
strlcpy(newConfig.subscribeTopic, mqttObj[F("r")], sizeof(newConfig.subscribeTopic));
}
if(mqttObj.containsKey(F("m"))) {
newConfig.payloadFormat = mqttObj[F("m")];
}
if(mqttObj.containsKey(F("t"))) {
newConfig.stateUpdate = mqttObj[F("s")];
}
if(mqttObj.containsKey(F("d"))) {
newConfig.stateUpdateInterval = mqttObj[F("d")];
}
if(mqttObj.containsKey(F("i"))) {
newConfig.timeout = mqttObj[F("i")];
}
if(mqttObj.containsKey(F("k"))) {
newConfig.keepalive = mqttObj[F("k")];
}
if(mqttObj.containsKey(F("e"))) {
newConfig.rebootMinutes = mqttObj[F("e")];
}
config->setMqttConfig(newConfig);
if(newConfig.payloadFormat == 3) { // Domiticz
if(configObj.containsKey(F("o"))) {
JsonObject domoticzObj = configObj[F("o")];
DomoticzConfig domoticzConfig;
config->getDomoticzConfig(domoticzConfig);
if(domoticzObj.containsKey(F("e"))) {
domoticzConfig.elidx = domoticzObj[F("e")];
}
if(domoticzObj.containsKey(F("c"))) {
domoticzConfig.cl1idx = domoticzObj[F("c")];
}
if(domoticzObj.containsKey(F("u1"))) {
domoticzConfig.vl1idx = domoticzObj[F("u1")];
}
if(domoticzObj.containsKey(F("u2"))) {
domoticzConfig.vl2idx = domoticzObj[F("u2")];
}
if(domoticzObj.containsKey(F("u3"))) {
domoticzConfig.vl3idx = domoticzObj[F("u3")];
}
config->setDomoticzConfig(domoticzConfig);
}
} else if(newConfig.payloadFormat == 4) { // Home Assistant
if(configObj.containsKey(F("h"))) {
JsonObject haObj = configObj[F("h")];
HomeAssistantConfig haConfig;
config->getHomeAssistantConfig(haConfig);
if(haObj.containsKey(F("t"))) {
strlcpy(haConfig.discoveryPrefix, haObj[F("t")], sizeof(haConfig.discoveryPrefix));
}
if(haObj.containsKey(F("h"))) {
strlcpy(haConfig.discoveryHostname, haObj[F("h")], sizeof(haConfig.discoveryHostname));
}
if(haObj.containsKey(F("n"))) {
strlcpy(haConfig.discoveryNameTag, haObj[F("n")], sizeof(haConfig.discoveryNameTag));
}
config->setHomeAssistantConfig(haConfig);
}
}
}
// Price service
if(configObj.containsKey(F("p"))) {
PriceServiceConfig newConfig;
config->getPriceServiceConfig(newConfig);
JsonObject priceServiceObj = configObj[F("p")];
if(priceServiceObj.containsKey(F("e"))) {
newConfig.enabled = priceServiceObj[F("e")];
}
if(priceServiceObj.containsKey(F("r"))) {
strlcpy(newConfig.area, priceServiceObj[F("r")], sizeof(newConfig.area));
}
if(priceServiceObj.containsKey(F("c"))) {
strlcpy(newConfig.currency, priceServiceObj[F("c")], sizeof(newConfig.currency));
}
if(priceServiceObj.containsKey(F("m"))) {
newConfig.resolutionInMinutes = priceServiceObj[F("m")];
}
config->setPriceServiceConfig(newConfig);
}
// Thresholds
if(configObj.containsKey(F("t"))) {
EnergyAccountingConfig newConfig;
config->getEnergyAccountingConfig(newConfig);
JsonObject thresholdObj = configObj[F("t")];
if(thresholdObj.containsKey(F("h"))) {
newConfig.hours = thresholdObj[F("h")];
}
if(thresholdObj.containsKey(F("t"))) {
JsonArray thresholdsArray = thresholdObj[F("t")].as<JsonArray>();
for(size_t i = 0; i < thresholdsArray.size(); i++) {
newConfig.thresholds[i] = thresholdsArray[i];
}
}
config->setEnergyAccountingConfig(newConfig);
}
// Debug
if(configObj.containsKey(F("d"))) {
DebugConfig newConfig;
config->getDebugConfig(newConfig);
JsonObject debugObj = configObj[F("d")];
if(debugObj.containsKey(F("s"))) {
newConfig.serial = debugObj[F("s")];
}
if(debugObj.containsKey(F("t"))) {
newConfig.telnet = debugObj[F("t")];
}
if(debugObj.containsKey(F("l"))) {
newConfig.level = debugObj[F("l")];
}
config->setDebugConfig(newConfig);
}
// Cloud
if(configObj.containsKey(F("c"))) {
CloudConfig newConfig;
config->getCloudConfig(newConfig);
JsonObject cloudObj = configObj[F("c")];
if(cloudObj.containsKey(F("e"))) {
newConfig.enabled = cloudObj[F("e")];
}
if(cloudObj.containsKey(F("p"))) {
newConfig.proto = cloudObj[F("p")];
}
config->setCloudConfig(newConfig);
if(cloudObj.containsKey(F("es"))) {
SystemConfig sysConfig;
config->getSystemConfig(sysConfig);
sysConfig.energyspeedometer = cloudObj[F("es")];
config->setSystemConfig(sysConfig);
}
if(cloudObj.containsKey(F("ze"))) {
ZmartChargeConfig zmartConfig;
config->getZmartChargeConfig(zmartConfig);
zmartConfig.enabled = cloudObj[F("ze")];
if(cloudObj.containsKey(F("zt"))) {
strlcpy(zmartConfig.token, cloudObj[F("zt")], sizeof(zmartConfig.token));
}
if(cloudObj.containsKey(F("zu"))) {
strlcpy(zmartConfig.baseUrl, cloudObj[F("zu")], sizeof(zmartConfig.baseUrl));
}
config->setZmartChargeConfig(zmartConfig);
}
}
// UI
if(configObj.containsKey(F("u"))) {
UiConfig newConfig;
config->getUiConfig(newConfig);
JsonObject uiObj = configObj[F("u")];
if(uiObj.containsKey(F("i"))) {
newConfig.showImport = uiObj[F("i")];
}
if(uiObj.containsKey(F("e"))) {
newConfig.showExport = uiObj[F("e")];
}
if(uiObj.containsKey(F("v"))) {
newConfig.showVoltage = uiObj[F("v")];
}
if(uiObj.containsKey(F("a"))) {
newConfig.showAmperage = uiObj[F("a")];
}
if(uiObj.containsKey(F("r"))) {
newConfig.showReactive = uiObj[F("r")];
}
if(uiObj.containsKey(F("c"))) {
newConfig.showRealtime = uiObj[F("c")];
}
if(uiObj.containsKey(F("t"))) {
newConfig.showPeaks = uiObj[F("t")];
}
if(uiObj.containsKey(F("p"))) {
newConfig.showPricePlot = uiObj[F("p")];
}
if(uiObj.containsKey(F("d"))) {
newConfig.showDayPlot = uiObj[F("d")];
}
if(uiObj.containsKey(F("m"))) {
newConfig.showMonthPlot = uiObj[F("m")];
}
if(uiObj.containsKey(F("s"))) {
newConfig.showTemperaturePlot = uiObj[F("s")];
}
if(uiObj.containsKey(F("l"))) {
newConfig.showRealtimePlot = uiObj[F("l")];
}
if(uiObj.containsKey(F("h"))) {
newConfig.showPerPhasePower = uiObj[F("h")];
}
if(uiObj.containsKey(F("f"))) {
newConfig.showPowerFactor = uiObj[F("f")];
}
if(uiObj.containsKey(F("k"))) {
newConfig.darkMode = uiObj[F("k")];
}
if(uiObj.containsKey(F("lang"))) {
strlcpy(newConfig.language, uiObj[F("lang")], sizeof(newConfig.language));
}
config->setUiConfig(newConfig);
}
// System
if(configObj.containsKey(F("s"))) {
SystemConfig sysConfig;
config->getSystemConfig(sysConfig);
JsonObject sysObj = configObj[F("s")];
if(sysObj.containsKey(F("b"))) {
sysConfig.boardType = sysObj[F("b")];
}
if(sysObj.containsKey(F("v"))) {
sysConfig.vendorConfigured = sysObj[F("v")];
}
if(sysObj.containsKey(F("u"))) {
sysConfig.userConfigured = sysObj[F("u")];
}
if(sysObj.containsKey(F("d"))) {
sysConfig.dataCollectionConsent = sysObj[F("d")];
}
if(sysObj.containsKey(F("o"))) {
strlcpy(sysConfig.country, sysObj[F("o")], sizeof(sysConfig.country));
}
if(sysObj.containsKey(F("c"))) {
sysConfig.firmwareChannel = sysObj[F("c")];
}
config->setSystemConfig(sysConfig);
}
// GPIO
if(configObj.containsKey(F("i"))) {
JsonObject gpioObj = configObj[F("i")];
GpioConfig newConfig;
config->getGpioConfig(newConfig);
if(gpioObj.containsKey(F("a"))) {
newConfig.apPin = gpioObj[F("a")];
}
if(gpioObj.containsKey(F("l"))) {
JsonObject ledObj = gpioObj[F("l")];
if(ledObj.containsKey(F("p"))) {
newConfig.ledPin = ledObj[F("p")];
}
if(ledObj.containsKey(F("i"))) {
newConfig.ledInverted = ledObj[F("i")];
}
}
if(gpioObj.containsKey(F("r"))) {
JsonObject rgbLedObj = gpioObj[F("r")];
if(rgbLedObj.containsKey(F("r"))) {
newConfig.ledPinRed = rgbLedObj[F("r")];
}
if(rgbLedObj.containsKey(F("g"))) {
newConfig.ledPinGreen = rgbLedObj[F("g")];
}
if(rgbLedObj.containsKey(F("b"))) {
newConfig.ledPinBlue = rgbLedObj[F("b")];
}
if(rgbLedObj.containsKey(F("i"))) {
newConfig.ledRgbInverted = rgbLedObj[F("i")];
}
}
if(gpioObj.containsKey(F("d"))) {
JsonObject ledDisableObj = gpioObj[F("d")];
if(ledDisableObj.containsKey(F("d"))) {
newConfig.ledDisablePin = ledDisableObj[F("d")];
}
if(ledDisableObj.containsKey(F("b"))) {
newConfig.ledBehaviour = ledDisableObj[F("b")];
}
}
if(gpioObj.containsKey(F("t"))) {
JsonObject tempSensorObj = gpioObj[F("t")];
if(tempSensorObj.containsKey(F("d"))) {
newConfig.tempSensorPin = tempSensorObj[F("d")];
}
if(tempSensorObj.containsKey(F("a"))) {
newConfig.tempAnalogSensorPin = tempSensorObj[F("a")];
}
}
if(gpioObj.containsKey(F("v"))) {
JsonObject vccObj = gpioObj[F("v")];
if(vccObj.containsKey(F("p"))) {
newConfig.vccPin = vccObj[F("p")];
}
if(vccObj.containsKey(F("o"))) {
newConfig.vccOffset = vccObj[F("o")];
}
if(vccObj.containsKey(F("m"))) {
newConfig.vccMultiplier = vccObj[F("m")];
}
if(vccObj.containsKey(F("d"))) {
JsonObject vccDividerObj = vccObj[F("d")];
if(vccDividerObj.containsKey(F("v"))) {
newConfig.vccResistorVcc = vccDividerObj[F("v")];
}
if(vccDividerObj.containsKey(F("g"))) {
newConfig.vccResistorGnd = vccDividerObj[F("g")];
}
}
}
config->setGpioConfig(newConfig);
if(gpioObj.containsKey(F("h"))) {
JsonObject hwObj = gpioObj[F("h")];
MeterConfig meterConfig;
config->getMeterConfig(meterConfig);
if(hwObj.containsKey(F("p"))) {
meterConfig.rxPin = hwObj[F("p")];
}
if(hwObj.containsKey(F("u"))) {
meterConfig.rxPinPullup = hwObj[F("u")];
}
if(hwObj.containsKey(F("t"))) {
meterConfig.txPin = hwObj[F("t")];
}
config->setMeterConfig(meterConfig);
}
}
}
#endif
void JsonMqttHandler::toJsonIsoTimestamp(time_t t, char* buf, size_t buflen) {
memset(buf, 0, buflen);
if(t > 0) {

View File

@@ -11,6 +11,7 @@
#include "AmsConfiguration.h"
#include "DataParser.h"
#include "Cosem.h"
#include "Timezone.h"
#if defined(AMS_REMOTE_DEBUG)
#include "RemoteDebug.h"
#endif

View File

@@ -16,53 +16,50 @@ IEC6205621::IEC6205621(const char* p, Timezone* tz, MeterConfig* meterConfig) {
String payload(p+1);
lastUpdateMillis = millis64();
String listIdStr = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n"));
listId = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n"));
if(listIdStr.startsWith(F("ADN"))) {
if(listId.startsWith(F("ADN"))) {
meterType = AmsTypeAidon;
listIdStr = listIdStr.substring(0,4);
} else if(listIdStr.startsWith(F("KFM"))) {
listId = listId.substring(0,4);
} else if(listId.startsWith(F("KFM"))) {
meterType = AmsTypeKaifa;
listIdStr = listIdStr.substring(0,4);
} else if(listIdStr.startsWith(F("KMP"))) {
listId = listId.substring(0,4);
} else if(listId.startsWith(F("KMP"))) {
meterType = AmsTypeKamstrup;
listIdStr = listIdStr.substring(0,4);
} else if(listIdStr.startsWith(F("KAM"))) {
listId = listId.substring(0,4);
} else if(listId.startsWith(F("KAM"))) {
meterType = AmsTypeKamstrup;
listIdStr = listIdStr.substring(0,4);
} else if(listIdStr.startsWith(F("ISk"))) {
listId = listId.substring(0,4);
} else if(listId.startsWith(F("ISk"))) {
meterType = AmsTypeIskra;
listIdStr = listIdStr.substring(0,5);
} else if(listIdStr.startsWith(F("XMX"))) {
listId = listId.substring(0,5);
} else if(listId.startsWith(F("XMX"))) {
meterType = AmsTypeLandisGyr;
listIdStr = listIdStr.substring(0,6);
} else if(listIdStr.startsWith(F("Ene")) || listIdStr.startsWith(F("EST"))) {
listId = listId.substring(0,6);
} else if(listId.startsWith(F("Ene")) || listId.startsWith(F("EST"))) {
meterType = AmsTypeSagemcom;
listIdStr = listIdStr.substring(0,4);
} else if(listIdStr.startsWith(F("LGF"))) {
listId = listId.substring(0,4);
} else if(listId.startsWith(F("LGF"))) {
meterType = AmsTypeLandisGyr;
listIdStr = listIdStr.substring(0,4);
listId = listId.substring(0,4);
} else {
meterType = AmsTypeUnknown;
listIdStr = listIdStr.substring(0,4);
listId = listId.substring(0,4);
}
strncpy(listId, listIdStr.c_str(), sizeof(listId) - 1);
String meterIdStr = extract(payload, F("96.1.0"));
if(meterIdStr.isEmpty()) {
meterIdStr = extract(payload, F("0.0.5"));
meterId = extract(payload, F("96.1.0"));
if(meterId.isEmpty()) {
meterId = extract(payload, F("0.0.5"));
}
strncpy(meterId, meterIdStr.c_str(), sizeof(meterId) - 1);
String meterModelStr = extract(payload, F("96.1.1"));
if(meterModelStr.isEmpty()) {
meterModelStr = extract(payload, F("96.1.7"));
if(meterModelStr.isEmpty()) {
meterModelStr = payload.substring(payload.indexOf(listIdStr) + listIdStr.length(), payload.indexOf(F("\n")));
meterModelStr.trim();
meterModel = extract(payload, F("96.1.1"));
if(meterModel.isEmpty()) {
meterModel = extract(payload, F("96.1.7"));
if(meterModel.isEmpty()) {
meterModel = payload.substring(payload.indexOf(listId) + listId.length(), payload.indexOf(F("\n")));
meterModel.trim();
}
}
strncpy(meterModel, meterModelStr.c_str(), sizeof(meterModel) - 1);
tmElements_t tm { 0, 0, 0, 0, 0, 0, 0 };
String timestamp = extract(payload, F("1.0.0"));

View File

@@ -213,7 +213,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
uint8_t str_len = 0;
str_len = getString(AMS_OBIS_UNKNOWN_1, sizeof(AMS_OBIS_UNKNOWN_1), ((char *) (d)), str);
if(str_len > 0) {
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
}
listType = 4;
@@ -227,8 +227,9 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
} else if(data->base.type == CosemTypeOctetString) { // Assuming first string is a list identifier
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
if(strncmp(str, "KFM_001", 7) == 0) {
strncpy(this->listId, str, sizeof(this->listId) - 1);
String listId = String(str);
if(listId.startsWith(F("KFM_001"))) {
this->listId = listId;
meterType = AmsTypeKaifa;
uint8_t idx = 0;
@@ -240,12 +241,12 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterModel, str, sizeof(meterModel) - 1);
meterModel = String(str);
data = getCosemDataAt(idx++, ((char *) (d)));
activeImportPower = ntohl(data->dlu.data);
@@ -276,12 +277,12 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterModel, str, sizeof(meterModel) - 1);
meterModel = String(str);
data = getCosemDataAt(idx++, ((char *) (d)));
activeImportPower = ntohl(data->dlu.data);
@@ -300,7 +301,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
l1voltage = ntohl(data->dlu.data) / 10.0;
}
if(listType >= 2 && memcmp(meterModel, "MA304T3", 7) == 0) {
if(listType >= 2 && memcmp(meterModel.c_str(), "MA304T3", 7) == 0) {
l2voltage = sqrt(pow(l1voltage - l3voltage * cos(60 * (PI/180)), 2) + pow(l3voltage * sin(60 * (PI/180)),2));
l2currentMissing = true;
}
@@ -329,8 +330,8 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
}
lastUpdateMillis = millis64();
} else if(strncmp(listId, "ISK", 3) == 0) { // Iskra special case
strncpy(this->listId, str, sizeof(this->listId) - 1);
} else if(listId.startsWith("ISK")) { // Iskra special case
this->listId = listId;
meterType = AmsTypeIskra;
uint8_t idx = 0;
@@ -346,7 +347,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 1.7.0
data = getCosemDataAt(idx++, ((char *) (d)));
@@ -426,7 +427,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 96.3.10 Disconnect control
// 96.14.0 Currently acrive energy tariff
@@ -468,7 +469,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 32.7.0
data = getCosemDataAt(idx++, ((char *) (d)));
@@ -525,7 +526,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 1.7.0
data = getCosemDataAt(idx++, ((char *) (d)));
@@ -571,7 +572,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 1.8.1
data = getCosemDataAt(idx++, ((char *) (d)));
@@ -632,7 +633,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 1.7.0
data = getCosemDataAt(idx++, ((char *) (d)));
@@ -706,7 +707,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
// 32.7.0
data = getCosemDataAt(idx++, ((char *) (d)));
@@ -777,7 +778,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
str_len = getString(AMS_OBIS_UNKNOWN_1, sizeof(AMS_OBIS_UNKNOWN_1), ((char *) (d)), str);
if(str_len > 0) {
meterType = AmsTypeIskra;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
lastUpdateMillis = millis64();
listType = 3;
}
@@ -809,7 +810,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
uint8_t str_len = 0;
str_len = getString(AMS_OBIS_VERSION, sizeof(AMS_OBIS_VERSION), ((char *) (d)), str);
if(str_len > 0) {
strncpy(listId, str, sizeof(listId) - 1);
listId = String(str);
}
val = getNumber(AMS_OBIS_ACTIVE_EXPORT, sizeof(AMS_OBIS_ACTIVE_EXPORT), ((char *) (d)));
@@ -884,21 +885,21 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
str_len = getString(AMS_OBIS_METER_MODEL, sizeof(AMS_OBIS_METER_MODEL), ((char *) (d)), str);
if(str_len > 0) {
strncpy(meterModel, str, sizeof(meterModel) - 1);
meterModel = String(str);
} else {
str_len = getString(AMS_OBIS_METER_MODEL_2, sizeof(AMS_OBIS_METER_MODEL_2), ((char *) (d)), str);
if(str_len > 0) {
strncpy(meterModel, str, sizeof(meterModel) - 1);
meterModel = String(str);
}
}
str_len = getString(AMS_OBIS_METER_ID, sizeof(AMS_OBIS_METER_ID), ((char *) (d)), str);
if(str_len > 0) {
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
} else {
str_len = getString(AMS_OBIS_METER_ID_2, sizeof(AMS_OBIS_METER_ID_2), ((char *) (d)), str);
if(str_len > 0) {
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
}
}
@@ -1034,12 +1035,12 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
case CosemTypeString:
memcpy(str, mid->oct.data, mid->oct.length);
str[mid->oct.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
break;
case CosemTypeOctetString:
memcpy(str, mid->str.data, mid->str.length);
str[mid->str.length] = 0x00;
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
break;
}
}
@@ -1058,9 +1059,9 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
meterType = AmsTypeIskra;
}
if(meterId[0] == '\0' && meterType != AmsTypeUnknown) {
if(meterId.isEmpty() && meterType != AmsTypeUnknown) {
stripNonAscii((uint8_t*) ctx.system_title, 8);
strncpy(meterId, (const char*)ctx.system_title, sizeof(meterId) - 1);
meterId = String((const char*)ctx.system_title);
}
}
@@ -1104,13 +1105,7 @@ IEC6205675::IEC6205675(const char* d, Timezone* tz, uint8_t useMeterType, MeterC
threePhase = true;
}
}
// Trim trailing whitespace from meterId
{
int len = strlen(meterId);
while(len > 0 && (meterId[len-1] == ' ' || meterId[len-1] == '\t' || meterId[len-1] == '\r' || meterId[len-1] == '\n')) {
meterId[--len] = '\0';
}
}
meterId.trim();
}
CosemData* IEC6205675::getCosemDataAt(uint8_t index, const char* ptr) {

View File

@@ -24,8 +24,6 @@ void KmpCommunicator::configure(MeterConfig& meterConfig) {
}
bool KmpCommunicator::loop() {
uint64_t now = millis64();
bool ret = talker->loop();
int lastError = getLastError();
if(ret) {
@@ -58,35 +56,36 @@ AmsData* KmpCommunicator::getData(AmsData& meterState) {
if(talker == NULL) return NULL;
KmpDataHolder kmpData;
talker->getData(kmpData);
uint64_t now = millis64();
AmsData* data = new AmsData();
data->apply(OBIS_ACTIVE_IMPORT_COUNT, kmpData.activeImportCounter);
data->apply(OBIS_ACTIVE_EXPORT_COUNT, kmpData.activeExportCounter);
data->apply(OBIS_REACTIVE_IMPORT_COUNT, kmpData.reactiveImportCounter);
data->apply(OBIS_REACTIVE_EXPORT_COUNT, kmpData.reactiveExportCounter);
data->apply(OBIS_ACTIVE_IMPORT, kmpData.activeImportPower);
data->apply(OBIS_ACTIVE_EXPORT, kmpData.activeExportPower);
data->apply(OBIS_REACTIVE_IMPORT, kmpData.reactiveImportPower);
data->apply(OBIS_REACTIVE_EXPORT, kmpData.reactiveExportPower);
data->apply(OBIS_VOLTAGE_L1, kmpData.l1voltage);
data->apply(OBIS_VOLTAGE_L2, kmpData.l2voltage);
data->apply(OBIS_VOLTAGE_L3, kmpData.l3voltage);
data->apply(OBIS_CURRENT_L1, kmpData.l1current);
data->apply(OBIS_CURRENT_L2, kmpData.l2current);
data->apply(OBIS_CURRENT_L3, kmpData.l3current);
data->apply(OBIS_POWER_FACTOR_L1, kmpData.l1PowerFactor);
data->apply(OBIS_POWER_FACTOR_L2, kmpData.l2PowerFactor);
data->apply(OBIS_POWER_FACTOR_L3, kmpData.l3PowerFactor);
data->apply(OBIS_POWER_FACTOR, kmpData.powerFactor);
data->apply(OBIS_ACTIVE_IMPORT_L1, kmpData.l1activeImportPower);
data->apply(OBIS_ACTIVE_IMPORT_L2, kmpData.l2activeImportPower);
data->apply(OBIS_ACTIVE_IMPORT_L3, kmpData.l3activeImportPower);
data->apply(OBIS_ACTIVE_EXPORT_L1, kmpData.l1activeExportPower);
data->apply(OBIS_ACTIVE_EXPORT_L2, kmpData.l2activeExportPower);
data->apply(OBIS_ACTIVE_EXPORT_L3, kmpData.l3activeExportPower);
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L1, kmpData.l1activeImportCounter);
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L2, kmpData.l2activeImportCounter);
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L3, kmpData.l3activeImportCounter);
data->apply(OBIS_METER_ID, kmpData.meterId);
data->apply(OBIS_NULL, AmsTypeKamstrup);
data->apply(OBIS_ACTIVE_IMPORT_COUNT, kmpData.activeImportCounter, now);
data->apply(OBIS_ACTIVE_EXPORT_COUNT, kmpData.activeExportCounter, now);
data->apply(OBIS_REACTIVE_IMPORT_COUNT, kmpData.reactiveImportCounter, now);
data->apply(OBIS_REACTIVE_EXPORT_COUNT, kmpData.reactiveExportCounter, now);
data->apply(OBIS_ACTIVE_IMPORT, kmpData.activeImportPower, now);
data->apply(OBIS_ACTIVE_EXPORT, kmpData.activeExportPower, now);
data->apply(OBIS_REACTIVE_IMPORT, kmpData.reactiveImportPower, now);
data->apply(OBIS_REACTIVE_EXPORT, kmpData.reactiveExportPower, now);
data->apply(OBIS_VOLTAGE_L1, kmpData.l1voltage, now);
data->apply(OBIS_VOLTAGE_L2, kmpData.l2voltage, now);
data->apply(OBIS_VOLTAGE_L3, kmpData.l3voltage, now);
data->apply(OBIS_CURRENT_L1, kmpData.l1current, now);
data->apply(OBIS_CURRENT_L2, kmpData.l2current, now);
data->apply(OBIS_CURRENT_L3, kmpData.l3current, now);
data->apply(OBIS_POWER_FACTOR_L1, kmpData.l1PowerFactor, now);
data->apply(OBIS_POWER_FACTOR_L2, kmpData.l2PowerFactor, now);
data->apply(OBIS_POWER_FACTOR_L3, kmpData.l3PowerFactor, now);
data->apply(OBIS_POWER_FACTOR, kmpData.powerFactor, now);
data->apply(OBIS_ACTIVE_IMPORT_L1, kmpData.l1activeImportPower, now);
data->apply(OBIS_ACTIVE_IMPORT_L2, kmpData.l2activeImportPower, now);
data->apply(OBIS_ACTIVE_IMPORT_L3, kmpData.l3activeImportPower, now);
data->apply(OBIS_ACTIVE_EXPORT_L1, kmpData.l1activeExportPower, now);
data->apply(OBIS_ACTIVE_EXPORT_L2, kmpData.l2activeExportPower, now);
data->apply(OBIS_ACTIVE_EXPORT_L3, kmpData.l3activeExportPower, now);
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L1, kmpData.l1activeImportCounter, now);
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L2, kmpData.l2activeImportCounter, now);
data->apply(OBIS_ACTIVE_IMPORT_COUNT_L3, kmpData.l3activeImportCounter, now);
data->apply(OBIS_METER_ID, kmpData.meterId, now);
data->apply(OBIS_NULL, AmsTypeKamstrup, now);
return data;
}

View File

@@ -210,13 +210,13 @@ LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterCo
char str[item->oct.length+1];
memcpy(str, item->oct.data, item->oct.length);
str[item->oct.length] = '\0';
strncpy(meterId, str, sizeof(meterId) - 1);
meterId = String(str);
listType = listType >= 2 ? listType : 2;
} 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';
strncpy(meterModel, str, sizeof(meterModel) - 1);
meterModel = String(str);
listType = listType >= 2 ? listType : 2;
}
}

View File

@@ -31,7 +31,7 @@ LNG2::LNG2(AmsData& meterState, const char* payload, uint8_t useMeterType, Meter
char str[64];
uint8_t str_len = getString((CosemData*) &d->meterId, str);
if(str_len > 0) {
strncpy(this->meterId, str, sizeof(this->meterId) - 1);
this->meterId = String(str);
}
listType = 3;
lastUpdateMillis = millis64();
@@ -57,7 +57,7 @@ LNG2::LNG2(AmsData& meterState, const char* payload, uint8_t useMeterType, Meter
char str[64];
uint8_t str_len = getString((CosemData*) &d->meterId, str);
if(str_len > 0) {
strncpy(this->meterId, str, sizeof(this->meterId) - 1);
this->meterId = String(str);
}
listType = 3;
lastUpdateMillis = millis64();

View File

@@ -400,7 +400,7 @@ bool RawMqttHandler::publishRaw(uint8_t* raw, size_t length) {
if(topic.isEmpty() || !connected())
return false;
if(length <= 0 || length > BUF_SIZE_COMMON) return false;
if(length <= 0 || length > BufferSize) return false;
String str = toHex(raw, length);
bool ret = mqtt.publish(topic + "/data", str);
@@ -409,7 +409,7 @@ bool RawMqttHandler::publishRaw(uint8_t* raw, size_t length) {
}
void RawMqttHandler::onMessage(String &topic, String &payload) {
if(topic == subTopic) {
if(topic.equals(subTopic)) {
if(payload.equals("fwupgrade")) {
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
updater->setTargetVersion(updater->getNextVersion());

View File

@@ -7,7 +7,6 @@
#ifndef _REALTIMEPLOT_H
#define _REALTIMEPLOT_H
#include <stdint.h>
#include "AmsData.h"
#define REALTIME_SAMPLE 10000
@@ -21,8 +20,8 @@ public:
int16_t getSize();
private:
int8_t values[REALTIME_SIZE];
uint8_t scaling[REALTIME_SIZE];
int8_t* values;
uint8_t* scaling;
unsigned long lastMillis = 0;
double lastReading = 0;

View File

@@ -4,9 +4,13 @@
*
*/
#include "Arduino.h"
#include "RealtimePlot.h"
#include <stdlib.h>
RealtimePlot::RealtimePlot() {
values = (int8_t*) malloc(REALTIME_SIZE);
scaling = (uint8_t*) malloc(REALTIME_SIZE);
memset(values, 0, REALTIME_SIZE);
memset(scaling, 0, REALTIME_SIZE);
}

View File

@@ -96,15 +96,16 @@ private:
bool uploading = false;
File file;
bool performRestart = false;
char priceRegion[8] = {};
char priceCurrency[4] = {};
String priceRegion = "";
String priceCurrency = "";
#if defined(AMS2MQTT_FIRMWARE_URL)
char customFirmwareUrl[128] = AMS2MQTT_FIRMWARE_URL;
String customFirmwareUrl = AMS2MQTT_FIRMWARE_URL;
#else
char customFirmwareUrl[128] = {};
String customFirmwareUrl;
#endif
char* buf;
static const uint16_t BufferSize = 2048;
char* buf;
#if defined(ESP8266)
ESP8266WebServer server;

View File

@@ -0,0 +1,7 @@
"c": {
"e" : %s,
"p" : %d,
"es": %s,
"ze": %s,
"zt" : "%s"
}

View File

@@ -0,0 +1,5 @@
"d": {
"s": %s,
"t": %s,
"l": %d
},

View File

@@ -0,0 +1,7 @@
"o": {
"e" : %d,
"c" : %d,
"u1" : %d,
"u2" : %d,
"u3" : %d
},

View File

@@ -0,0 +1,8 @@
"g": {
"t": "%s",
"h": "%s",
"s": %d,
"u": "%s",
"p": "%s",
"c": "%s"
},

View File

@@ -0,0 +1,37 @@
"i": {
"h": {
"p": %s,
"u": %s,
"t": %s
},
"a": %s,
"l": {
"p": %s,
"i": %s
},
"r": {
"r": %s,
"g": %s,
"b": %s,
"i": %s
},
"d": {
"d": %s,
"b": %d
},
"t": {
"d": %s,
"a": %s
},
"v": {
"p": %s,
"o": %.2f,
"m": %.3f,
"d": {
"v": %d,
"g": %d
},
"b": %.1f
},
"p": %d
},

View File

@@ -0,0 +1,5 @@
"h": {
"t" : "%s",
"h" : "%s",
"n" : "%s"
},

View File

@@ -0,0 +1,23 @@
"m": {
"o": %d,
"a": %d,
"b": %d,
"p": %d,
"i": %s,
"s": %d,
"d": %d,
"f": %d,
"r": %d,
"e": {
"e": %s,
"k": "%s",
"a": "%s"
},
"m": {
"e": %s,
"w": %.3f,
"v": %.3f,
"a": %.3f,
"c": %.3f
}
},

View File

@@ -0,0 +1,21 @@
"q": {
"h": "%s",
"p": %d,
"u": "%s",
"a": "%s",
"c": "%s",
"b": "%s",
"r": "%s",
"m": %d,
"s": {
"e": %s,
"c": %s,
"r": %s,
"k": %s
},
"t": %d,
"d": %d,
"i": %d,
"k": %d,
"e": %s
},

View File

@@ -0,0 +1,13 @@
"n": {
"c": %d,
"m": "%s",
"i": "%s",
"s": "%s",
"g": "%s",
"d1": "%s",
"d2": "%s",
"d": %s,
"n1": "%s",
"h": %s,
"x": %s
},

View File

@@ -0,0 +1,7 @@
"p": {
"e": %s,
"t": "%s",
"r": "%s",
"c": "%s",
"m": %d
},

View File

@@ -0,0 +1,10 @@
{
"t" : %d,
"n" : "%s",
"d" : %d,
"a" : [%s],
"h" : [%s],
"v" : %.4f,
"s" : { "m":%d,"d":%d},
"e" : { "m":%d,"d":%d}
}%s

View File

@@ -0,0 +1,15 @@
"t": {
"t": [
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d,
%d
],
"h": %d
},

View File

@@ -0,0 +1,18 @@
"u": {
"i": %d,
"e": %d,
"v": %d,
"a": %d,
"r": %d,
"c": %d,
"t": %d,
"p": %d,
"d": %d,
"m": %d,
"s": %d,
"l": %d,
"h": %d,
"f": %d,
"k": %d,
"lang" : "%s"
},

View File

@@ -0,0 +1,7 @@
"w": {
"s": "%s",
"p": "%s",
"w": %.1f,
"z": %d,
"b": %s
},

View File

@@ -19,6 +19,20 @@
#include "html/response_json.h"
#include "html/sysinfo_json.h"
#include "html/tariff_json.h"
#include "html/conf_general_json.h"
#include "html/conf_meter_json.h"
#include "html/conf_wifi_json.h"
#include "html/conf_net_json.h"
#include "html/conf_mqtt_json.h"
#include "html/conf_price_json.h"
#include "html/conf_price_row_json.h"
#include "html/conf_thresholds_json.h"
#include "html/conf_debug_json.h"
#include "html/conf_gpio_json.h"
#include "html/conf_domoticz_json.h"
#include "html/conf_ha_json.h"
#include "html/conf_ui_json.h"
#include "html/conf_cloud_json.h"
#include "html/firmware_html.h"
#if defined(ESP32)
@@ -392,7 +406,7 @@ void AmsWebServer::sysinfoJson() {
features += "\"zc\"";
#endif
int size = snprintf_P(buf, BUF_SIZE_COMMON, SYSINFO_JSON,
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
FirmwareVersion::VersionString,
#if defined(CONFIG_IDF_TARGET_ESP32S2)
"esp32s2",
@@ -584,7 +598,7 @@ void AmsWebServer::dataJson() {
time_t now = time(nullptr);
snprintf_P(buf, BUF_SIZE_COMMON, DATA_JSON,
snprintf_P(buf, BufferSize, DATA_JSON,
maxPwr == 0 ? meterState->isThreePhase() ? 20000 : 10000 : maxPwr,
productionCapacity,
mainFuse == 0 ? 40 : mainFuse,
@@ -648,8 +662,8 @@ void AmsWebServer::dataJson() {
ea->getProducedThisMonth(),
ea->getIncomeThisMonth(),
price == PRICE_NO_VALUE ? "false" : "true",
priceRegion,
priceCurrency,
priceRegion.c_str(),
priceCurrency.c_str(),
meterState->getLastError(),
ps == NULL ? 0 : ps->getLastError(),
(uint32_t) now,
@@ -672,7 +686,7 @@ void AmsWebServer::dayplotJson() {
if(ds == NULL) {
notFound();
} else {
AmsJsonGenerator::generateDayPlotJson(ds, buf, BUF_SIZE_COMMON);
AmsJsonGenerator::generateDayPlotJson(ds, buf, BufferSize);
addConditionalCloudHeaders();
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
@@ -690,7 +704,7 @@ void AmsWebServer::monthplotJson() {
if(ds == NULL) {
notFound();
} else {
AmsJsonGenerator::generateMonthPlotJson(ds, buf, BUF_SIZE_COMMON);
AmsJsonGenerator::generateMonthPlotJson(ds, buf, BufferSize);
addConditionalCloudHeaders();
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
@@ -716,19 +730,19 @@ void AmsWebServer::energyPriceJson() {
prices[i] = ps->getPriceForRelativeHour(PRICE_DIRECTION_IMPORT, i);
}
uint16_t pos = snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"currency\":\"%s\",\"source\":\"%s\""),
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"currency\":\"%s\",\"source\":\"%s\""),
ps->getCurrency(),
ps->getSource()
);
for(uint8_t i = 0;i < 36; i++) {
if(prices[i] == PRICE_NO_VALUE) {
pos += snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR(",\"%02d\":null"), i);
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"%02d\":null"), i);
} else {
pos += snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR(",\"%02d\":%.4f"), i, prices[i]);
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"%02d\":%.4f"), i, prices[i]);
}
}
snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR("}"));
snprintf_P(buf+pos, BufferSize-pos, PSTR("}"));
addConditionalCloudHeaders();
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
@@ -763,7 +777,7 @@ void AmsWebServer::priceJson(uint8_t direction) {
prices[i] = ps->getPricePoint(direction, i);
}
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"currency\":\"%s\",\"source\":\"%s\",\"resolution\":%d,\"direction\":\"%s\",\"cursor\":%d,\"importExportPriceDifferent\":%s,\"prices\":["),
snprintf_P(buf, BufferSize, PSTR("{\"currency\":\"%s\",\"source\":\"%s\",\"resolution\":%d,\"direction\":\"%s\",\"cursor\":%d,\"importExportPriceDifferent\":%s,\"prices\":["),
ps->getCurrency(),
ps->getSource(),
ps->getResolutionInMinutes(),
@@ -782,10 +796,10 @@ void AmsWebServer::priceJson(uint8_t direction) {
for(uint8_t i = 0;i < numberOfPoints; i++) {
if(prices[i] == PRICE_NO_VALUE) {
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("%snull"), i == 0 ? "" : ",");
snprintf_P(buf, BufferSize, PSTR("%snull"), i == 0 ? "" : ",");
server.sendContent(buf);
} else {
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("%s%.4f"), i == 0 ? "" : ",", prices[i]);
snprintf_P(buf, BufferSize, PSTR("%s%.4f"), i == 0 ? "" : ",", prices[i]);
server.sendContent(buf);
}
}
@@ -878,14 +892,244 @@ void AmsWebServer::configurationJson() {
if(!checkSecurity(1))
return;
MeterConfig meterConfig;
config->getMeterConfig(meterConfig);
bool multEnable = false;
if(meterConfig.wattageMultiplier != 1.0 && meterConfig.wattageMultiplier != 0.0)
multEnable = true;
if(meterConfig.voltageMultiplier != 1.0 && meterConfig.voltageMultiplier != 0.0)
multEnable = true;
if(meterConfig.amperageMultiplier != 1.0 && meterConfig.amperageMultiplier != 0.0)
multEnable = true;
if(meterConfig.accumulatedMultiplier != 1.0 && meterConfig.accumulatedMultiplier != 0.0)
multEnable = true;
SystemConfig sysConfig;
config->getSystemConfig(sysConfig);
NtpConfig ntpConfig;
config->getNtpConfig(ntpConfig);
NetworkConfig networkConfig;
config->getNetworkConfig(networkConfig);
bool encen = false;
for(uint8_t i = 0; i < 16; i++) {
if(meterConfig.encryptionKey[i] > 0) {
encen = true;
}
}
EnergyAccountingConfig* eac = ea->getConfig();
MqttConfig mqttConfig;
config->getMqttConfig(mqttConfig);
PriceServiceConfig price;
config->getPriceServiceConfig(price);
DebugConfig debugConfig;
config->getDebugConfig(debugConfig);
DomoticzConfig domo;
config->getDomoticzConfig(domo);
UiConfig ui;
config->getUiConfig(ui);
HomeAssistantConfig haconf;
config->getHomeAssistantConfig(haconf);
CloudConfig cloud;
config->getCloudConfig(cloud);
ZmartChargeConfig zcc;
config->getZmartChargeConfig(zcc);
stripNonAscii((uint8_t*) zcc.token, 21);
bool qsc = false;
bool qsr = false;
bool qsk = false;
if(LittleFS.begin()) {
qsc = LittleFS.exists(FILE_MQTT_CA);
qsr = LittleFS.exists(FILE_MQTT_CERT);
qsk = LittleFS.exists(FILE_MQTT_KEY);
}
addConditionalCloudHeaders();
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
AmsJsonGenerator::generateConfigurationJson(config, buf, BUF_SIZE_COMMON);
server.send(200, MIME_JSON, buf);
server.send_P(200, MIME_JSON, PSTR("{\"version\":\""));
server.sendContent_P(FirmwareVersion::VersionString);
server.sendContent_P(PSTR("\","));
snprintf_P(buf, BufferSize, CONF_GENERAL_JSON,
ntpConfig.timezone,
networkConfig.hostname,
webConfig.security,
webConfig.username,
strlen(webConfig.password) > 0 ? "***" : "",
webConfig.context
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_METER_JSON,
meterConfig.source,
meterConfig.parser,
meterConfig.baud,
meterConfig.parity,
meterConfig.invert ? "true" : "false",
meterConfig.bufferSize * 64,
meterConfig.distributionSystem,
meterConfig.mainFuse,
meterConfig.productionCapacity,
encen ? "true" : "false",
toHex(meterConfig.encryptionKey, 16).c_str(),
toHex(meterConfig.authenticationKey, 16).c_str(),
multEnable ? "true" : "false",
meterConfig.wattageMultiplier == 0.0 ? 1.0 : meterConfig.wattageMultiplier / 1000.0,
meterConfig.voltageMultiplier == 0.0 ? 1.0 : meterConfig.voltageMultiplier / 1000.0,
meterConfig.amperageMultiplier == 0.0 ? 1.0 : meterConfig.amperageMultiplier / 1000.0,
meterConfig.accumulatedMultiplier == 0.0 ? 1.0 : meterConfig.accumulatedMultiplier / 1000.0
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_THRESHOLDS_JSON,
eac->thresholds[0],
eac->thresholds[1],
eac->thresholds[2],
eac->thresholds[3],
eac->thresholds[4],
eac->thresholds[5],
eac->thresholds[6],
eac->thresholds[7],
eac->thresholds[8],
eac->thresholds[9],
eac->hours
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_WIFI_JSON,
networkConfig.ssid,
strlen(networkConfig.psk) > 0 ? "***" : "",
networkConfig.power / 10.0,
networkConfig.sleep,
networkConfig.use11b ? "true" : "false"
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_NET_JSON,
networkConfig.mode,
strlen(networkConfig.ip) > 0 ? "static" : "dhcp",
networkConfig.ip,
networkConfig.subnet,
networkConfig.gateway,
networkConfig.dns1,
networkConfig.dns2,
networkConfig.mdns ? "true" : "false",
ntpConfig.server,
ntpConfig.dhcp ? "true" : "false",
networkConfig.ipv6 ? "true" : "false"
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_MQTT_JSON,
mqttConfig.host,
mqttConfig.port,
mqttConfig.username,
strlen(mqttConfig.password) > 0 ? "***" : "",
mqttConfig.clientId,
mqttConfig.publishTopic,
mqttConfig.subscribeTopic,
mqttConfig.payloadFormat,
mqttConfig.ssl ? "true" : "false",
qsc ? "true" : "false",
qsr ? "true" : "false",
qsk ? "true" : "false",
mqttConfig.stateUpdate,
mqttConfig.stateUpdateInterval,
mqttConfig.timeout,
mqttConfig.keepalive,
mqttConfig.rebootMinutes == 0 ? "null" : String(mqttConfig.rebootMinutes, 10).c_str()
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_PRICE_JSON,
price.enabled ? "true" : "false",
price.entsoeToken,
price.area,
price.currency,
price.resolutionInMinutes
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_DEBUG_JSON,
debugConfig.serial ? "true" : "false",
debugConfig.telnet ? "true" : "false",
debugConfig.level
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_GPIO_JSON,
meterConfig.rxPin == 0xff ? "null" : String(meterConfig.rxPin, 10).c_str(),
meterConfig.rxPinPullup ? "true" : "false",
meterConfig.txPin == 0xff ? "null" : String(meterConfig.txPin, 10).c_str(),
gpioConfig->apPin == 0xff ? "null" : String(gpioConfig->apPin, 10).c_str(),
gpioConfig->ledPin == 0xff ? "null" : String(gpioConfig->ledPin, 10).c_str(),
gpioConfig->ledInverted ? "true" : "false",
gpioConfig->ledPinRed == 0xff ? "null" : String(gpioConfig->ledPinRed, 10).c_str(),
gpioConfig->ledPinGreen == 0xff ? "null" : String(gpioConfig->ledPinGreen, 10).c_str(),
gpioConfig->ledPinBlue == 0xff ? "null" : String(gpioConfig->ledPinBlue, 10).c_str(),
gpioConfig->ledRgbInverted ? "true" : "false",
gpioConfig->ledDisablePin == 0xff ? "null" : String(gpioConfig->ledDisablePin, 10).c_str(),
gpioConfig->ledBehaviour,
gpioConfig->tempSensorPin == 0xff ? "null" : String(gpioConfig->tempSensorPin, 10).c_str(),
gpioConfig->tempAnalogSensorPin == 0xff ? "null" : String(gpioConfig->tempAnalogSensorPin, 10).c_str(),
gpioConfig->vccPin == 0xff ? "null" : String(gpioConfig->vccPin, 10).c_str(),
gpioConfig->vccOffset / 100.0,
gpioConfig->vccMultiplier / 1000.0,
gpioConfig->vccResistorVcc,
gpioConfig->vccResistorGnd,
gpioConfig->vccBootLimit / 10.0,
gpioConfig->powersaving
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_UI_JSON,
ui.showImport,
ui.showExport,
ui.showVoltage,
ui.showAmperage,
ui.showReactive,
ui.showRealtime,
ui.showPeaks,
ui.showPricePlot,
ui.showDayPlot,
ui.showMonthPlot,
ui.showTemperaturePlot,
ui.showRealtimePlot,
ui.showPerPhasePower,
ui.showPowerFactor,
ui.darkMode,
ui.language
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_DOMOTICZ_JSON,
domo.elidx,
domo.cl1idx,
domo.vl1idx,
domo.vl2idx,
domo.vl3idx
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_HA_JSON,
haconf.discoveryPrefix,
haconf.discoveryHostname,
haconf.discoveryNameTag
);
server.sendContent(buf);
snprintf_P(buf, BufferSize, CONF_CLOUD_JSON,
cloud.enabled ? "true" : "false",
cloud.proto,
#if defined(ESP32) && defined(ENERGY_SPEEDOMETER_PASS)
sysConfig.energyspeedometer == 7 ? "true" : "false",
#else
"null",
#endif
zcc.enabled ? "true" : "false",
zcc.token
);
server.sendContent(buf);
server.sendContent_P(PSTR("}"));
}
void AmsWebServer::priceConfigJson() {
@@ -921,7 +1165,7 @@ void AmsWebServer::priceConfigJson() {
}
hours = hours.substring(0, hours.length()-1);
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"t\":%d,\"n\":\"%s\",\"d\":%d,\"a\":[%s],\"h\":[%s],\"v\":%.4f,\"s\":{\"m\":%d,\"d\":%d},\"e\":{\"m\":%d,\"d\":%d}}%s"),
snprintf_P(buf, BufferSize, CONF_PRICE_ROW_JSON,
p.type,
p.name,
p.direction,
@@ -938,7 +1182,7 @@ void AmsWebServer::priceConfigJson() {
}
}
}
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("]}"));
snprintf_P(buf, BufferSize, PSTR("]}"));
server.sendContent(buf);
}
@@ -956,7 +1200,7 @@ void AmsWebServer::translationsJson() {
}
}
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("/translations-%s.json"), lang.c_str());
snprintf_P(buf, BufferSize, PSTR("/translations-%s.json"), lang.c_str());
if(!LittleFS.exists(buf)) {
notFound();
return;
@@ -973,7 +1217,7 @@ void AmsWebServer::translationsJson() {
server.send(200, MIME_JSON);
while(file.available() > 0) {
int len = file.readBytes(buf, BUF_SIZE_COMMON);
int len = file.readBytes(buf, BufferSize);
server.sendContent(buf, len);
}
file.close();
@@ -988,7 +1232,7 @@ void AmsWebServer::cloudkeyJson() {
String seed = cloud->generateSeed();
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"seed\":\"%s\"}"), seed.c_str());
snprintf_P(buf, BufferSize, PSTR("{\"seed\":\"%s\"}"), seed.c_str());
server.setContentLength(strlen(buf));
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
@@ -1385,12 +1629,12 @@ void AmsWebServer::handleSave() {
}
if(server.hasArg(F("p")) && server.arg(F("p")) == F("true")) {
strncpy(priceRegion, server.arg(F("pr")).c_str(), sizeof(priceRegion) - 1);
priceRegion = server.arg(F("pr"));
PriceServiceConfig price;
price.enabled = server.hasArg(F("pe")) && server.arg(F("pe")) == F("true");
strcpy(price.entsoeToken, server.arg(F("pt")).c_str());
strcpy(price.area, priceRegion);
strcpy(price.area, priceRegion.c_str());
strcpy(price.currency, server.arg(F("pc")).c_str());
price.resolutionInMinutes = server.arg(F("pm")).toInt();
config->setPriceServiceConfig(price);
@@ -1437,19 +1681,19 @@ void AmsWebServer::handleSave() {
uint8_t count = server.arg(F("rc")).toInt();
for(uint8_t i = 0; i < count; i++) {
PriceConfig pc;
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rt%d"), i);
snprintf_P(buf, BufferSize, PSTR("rt%d"), i);
pc.type = server.arg(buf).toInt();
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rd%d"), i);
snprintf_P(buf, BufferSize, PSTR("rd%d"), i);
pc.direction = server.arg(buf).toInt();
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rv%d"), i);
snprintf_P(buf, BufferSize, PSTR("rv%d"), i);
pc.value = server.arg(buf).toDouble() * 10000.0;
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rn%d"), i);
snprintf_P(buf, BufferSize, PSTR("rn%d"), i);
String name = server.arg(buf);
strcpy(pc.name, name.c_str());
int d = 0;
pc.days = 0x00;
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ra%d"), i);
snprintf_P(buf, BufferSize, PSTR("ra%d"), i);
String days = server.arg(buf);
char * pch = strtok ((char*) days.c_str(),",");
while (pch != NULL && d < 7) {
@@ -1461,7 +1705,7 @@ void AmsWebServer::handleSave() {
int h = 0;
pc.hours = 0x00000000;
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rh%d"), i);
snprintf_P(buf, BufferSize, PSTR("rh%d"), i);
String hours = server.arg(buf);
pch = strtok ((char*) hours.c_str(),",");
while (pch != NULL && h < 24) {
@@ -1471,16 +1715,16 @@ void AmsWebServer::handleSave() {
h++;
}
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rsm%d"), i);
snprintf_P(buf, BufferSize, PSTR("rsm%d"), i);
pc.start_month = server.arg(buf).toInt();
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rsd%d"), i);
snprintf_P(buf, BufferSize, PSTR("rsd%d"), i);
pc.start_dayofmonth = server.arg(buf).toInt();
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("rem%d"), i);
snprintf_P(buf, BufferSize, PSTR("rem%d"), i);
pc.end_month = server.arg(buf).toInt();
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("red%d"), i);
snprintf_P(buf, BufferSize, PSTR("red%d"), i);
pc.end_dayofmonth = server.arg(buf).toInt();
ps->setPriceConfig(i, pc);
@@ -1522,7 +1766,7 @@ void AmsWebServer::handleSave() {
success = false;
}
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
success ? "true" : "false",
"",
performRestart ? "true" : "false"
@@ -1574,7 +1818,7 @@ void AmsWebServer::upgrade() {
SystemConfig sys;
config->getSystemConfig(sys);
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
sys.dataCollectionConsent == 1 ? "true" : "false",
"",
sys.dataCollectionConsent == 1 ? "true" : "false"
@@ -1637,7 +1881,7 @@ void AmsWebServer::firmwareUpload() {
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Invalid file extension\n"));
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Invalid file extension",
"false"
@@ -1651,7 +1895,7 @@ void AmsWebServer::firmwareUpload() {
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("An error has occurred while starting firmware upload\n"));
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Unable to start firmware upgrade",
"false"
@@ -1678,7 +1922,7 @@ void AmsWebServer::firmwareUpload() {
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("An error has occurred while writing firmware to flash\n"));
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Unable to write to flash",
"false"
@@ -1704,7 +1948,7 @@ void AmsWebServer::firmwareUpload() {
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("An error has occurred while activating new firmware\n"));
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Unable to activate new firmware",
"false"
@@ -1752,7 +1996,7 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("An Error has occurred while writing file\n"));
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"File size does not match",
"false"
@@ -1767,7 +2011,7 @@ HTTPUpload& AmsWebServer::uploadFile(const char* path) {
file.close();
} else {
debugger->printf_P(PSTR("File was not valid in the end... Write error: %d, \n"), file.getWriteError());
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"false",
"Upload ended, but file is missing",
"false"
@@ -1807,7 +2051,7 @@ void AmsWebServer::factoryResetPost() {
success = true;
}
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
success ? "true" : "false",
"",
"true"
@@ -1947,7 +2191,7 @@ void AmsWebServer::tariffJson() {
String peaks;
for(uint8_t x = 0;x < min((uint8_t) 5, eac->hours); x++) {
EnergyAccountingPeak peak = ea->getPeak(x+1);
int len = snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"d\":%d,\"h\":%d,\"v\":%.2f}"),
int len = snprintf_P(buf, BufferSize, PSTR("{\"d\":%d,\"h\":%d,\"v\":%.2f}"),
peak.day,
peak.hour,
peak.value / 100.0
@@ -1957,7 +2201,7 @@ void AmsWebServer::tariffJson() {
peaks += String(buf);
}
snprintf_P(buf, BUF_SIZE_COMMON, TARIFF_JSON,
snprintf_P(buf, BufferSize, TARIFF_JSON,
eac->thresholds[0],
eac->thresholds[1],
eac->thresholds[2],
@@ -2010,20 +2254,20 @@ void AmsWebServer::realtimeJson() {
offset = rtp->getSize();
}
uint16_t pos = snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"offset\":%d,\"size\":%d,\"total\":%d,\"data\":["), offset, size, rtp->getSize());
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"offset\":%d,\"size\":%d,\"total\":%d,\"data\":["), offset, size, rtp->getSize());
bool first = true;
for(uint16_t i = 0; i < size; i++) {
pos += snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR("%s%d"), first ? "" : ",", rtp->getValue(offset+i));
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR("%s%d"), first ? "" : ",", rtp->getValue(offset+i));
first = false;
delay(1);
}
pos += snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR("]}"));
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR("]}"));
server.send(200, MIME_JSON, buf);
}
void AmsWebServer::setPriceSettings(String region, String currency) {
strncpy(this->priceRegion, region.c_str(), sizeof(this->priceRegion) - 1);
strncpy(this->priceCurrency, currency.c_str(), sizeof(this->priceCurrency) - 1);
this->priceRegion = region;
this->priceCurrency = currency;
}
void AmsWebServer::configFileDownload() {
@@ -2050,59 +2294,59 @@ void AmsWebServer::configFileDownload() {
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send_P(200, MIME_PLAIN, PSTR("amsconfig\n"));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("version %s\n"), FirmwareVersion::VersionString));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("boardType %d\n"), sys.boardType));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("version %s\n"), FirmwareVersion::VersionString));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("boardType %d\n"), sys.boardType));
if(includeWifi) {
NetworkConfig network;
config->getNetworkConfig(network);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("netmode %d\n"), network.mode));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("hostname %s\n"), network.hostname));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ssid %s\n"), network.ssid));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("psk %s\n"), network.psk));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("netmode %d\n"), network.mode));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("hostname %s\n"), network.hostname));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ssid %s\n"), network.ssid));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("psk %s\n"), network.psk));
if(strlen(network.ip) > 0) {
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ip %s\n"), network.ip));
if(strlen(network.gateway) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gateway %s\n"), network.gateway));
if(strlen(network.subnet) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("subnet %s\n"), network.subnet));
if(strlen(network.dns1) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("dns1 %s\n"), network.dns1));
if(strlen(network.dns2) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("dns2 %s\n"), network.dns2));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ip %s\n"), network.ip));
if(strlen(network.gateway) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gateway %s\n"), network.gateway));
if(strlen(network.subnet) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("subnet %s\n"), network.subnet));
if(strlen(network.dns1) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dns1 %s\n"), network.dns1));
if(strlen(network.dns2) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dns2 %s\n"), network.dns2));
}
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mdns %d\n"), network.mdns ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("use11b %d\n"), network.use11b ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mdns %d\n"), network.mdns ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("use11b %d\n"), network.use11b ? 1 : 0));
}
if(includeMqtt) {
MqttConfig mqtt;
config->getMqttConfig(mqtt);
if(strlen(mqtt.host) > 0) {
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttHost %s\n"), mqtt.host));
if(mqtt.port > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttPort %d\n"), mqtt.port));
if(strlen(mqtt.clientId) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttClientId %s\n"), mqtt.clientId));
if(strlen(mqtt.publishTopic) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttPublishTopic %s\n"), mqtt.publishTopic));
if(strlen(mqtt.subscribeTopic) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttSubscribeTopic %s\n"), mqtt.subscribeTopic));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttUsername %s\n"), mqtt.username));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttPassword %s\n"), mqtt.password));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttPayloadFormat %d\n"), mqtt.payloadFormat));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttSsl %d\n"), mqtt.ssl ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttHost %s\n"), mqtt.host));
if(mqtt.port > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPort %d\n"), mqtt.port));
if(strlen(mqtt.clientId) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttClientId %s\n"), mqtt.clientId));
if(strlen(mqtt.publishTopic) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPublishTopic %s\n"), mqtt.publishTopic));
if(strlen(mqtt.subscribeTopic) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttSubscribeTopic %s\n"), mqtt.subscribeTopic));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttUsername %s\n"), mqtt.username));
if(includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPassword %s\n"), mqtt.password));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttPayloadFormat %d\n"), mqtt.payloadFormat));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttSsl %d\n"), mqtt.ssl ? 1 : 0));
if(mqtt.timeout > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttTimeout %d\n"), mqtt.timeout));
if(mqtt.keepalive > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttKeepalive %d\n"), mqtt.keepalive));
if(mqtt.rebootMinutes > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("mqttRebootMinutes %d\n"), mqtt.rebootMinutes));
if(mqtt.timeout > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttTimeout %d\n"), mqtt.timeout));
if(mqtt.keepalive > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttKeepalive %d\n"), mqtt.keepalive));
if(mqtt.rebootMinutes > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("mqttRebootMinutes %d\n"), mqtt.rebootMinutes));
if(mqtt.payloadFormat == 3) {
DomoticzConfig domo;
config->getDomoticzConfig(domo);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("domoticzElidx %d\n"), domo.elidx));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("domoticzVl1idx %d\n"), domo.vl1idx));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("domoticzVl2idx %d\n"), domo.vl2idx));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("domoticzVl3idx %d\n"), domo.vl3idx));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("domoticzCl1idx %d\n"), domo.cl1idx));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzElidx %d\n"), domo.elidx));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzVl1idx %d\n"), domo.vl1idx));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzVl2idx %d\n"), domo.vl2idx));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzVl3idx %d\n"), domo.vl3idx));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("domoticzCl1idx %d\n"), domo.cl1idx));
} else if(mqtt.payloadFormat == 4) {
HomeAssistantConfig haconf;
config->getHomeAssistantConfig(haconf);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("homeAssistantDiscoveryPrefix %s\n"), haconf.discoveryPrefix));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("homeAssistantDiscoveryHostname %s\n"), haconf.discoveryHostname));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("homeAssistantDiscoveryNameTag %s\n"), haconf.discoveryNameTag));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("homeAssistantDiscoveryPrefix %s\n"), haconf.discoveryPrefix));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("homeAssistantDiscoveryHostname %s\n"), haconf.discoveryHostname));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("homeAssistantDiscoveryNameTag %s\n"), haconf.discoveryNameTag));
}
}
}
@@ -2110,17 +2354,17 @@ void AmsWebServer::configFileDownload() {
if(includeWeb && includeSecrets) {
WebConfig web;
config->getWebConfig(web);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("webSecurity %d\n"), web.security));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("webSecurity %d\n"), web.security));
if(web.security > 0) {
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("webUsername %s\n"), web.username));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("webPassword %s\n"), web.password));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("webUsername %s\n"), web.username));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("webPassword %s\n"), web.password));
}
}
if(includeMeter) {
MeterConfig meter;
config->getMeterConfig(meter);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterBaud %d\n"), meter.baud));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterBaud %d\n"), meter.baud));
char parity[4] = "";
switch(meter.parity) {
case 2:
@@ -2139,23 +2383,23 @@ void AmsWebServer::configFileDownload() {
strcpy_P(parity, PSTR("8E1"));
break;
}
if(strlen(parity) > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterParity %s\n"), parity));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterInvert %d\n"), meter.invert ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterDistributionSystem %d\n"), meter.distributionSystem));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterMainFuse %d\n"), meter.mainFuse));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterProductionCapacity %d\n"), meter.productionCapacity));
if(strlen(parity) > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterParity %s\n"), parity));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterInvert %d\n"), meter.invert ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterDistributionSystem %d\n"), meter.distributionSystem));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterMainFuse %d\n"), meter.mainFuse));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterProductionCapacity %d\n"), meter.productionCapacity));
if(includeSecrets) {
if(meter.encryptionKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterEncryptionKey %s\n"), toHex(meter.encryptionKey, 16).c_str()));
if(meter.authenticationKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterAuthenticationKey %s\n"), toHex(meter.authenticationKey, 16).c_str()));
if(meter.encryptionKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterEncryptionKey %s\n"), toHex(meter.encryptionKey, 16).c_str()));
if(meter.authenticationKey[0] != 0x00) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAuthenticationKey %s\n"), toHex(meter.authenticationKey, 16).c_str()));
}
if(meter.wattageMultiplier != 1.0 && meter.wattageMultiplier != 0.0)
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterWattageMultiplier %.3f\n"), meter.wattageMultiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterWattageMultiplier %.3f\n"), meter.wattageMultiplier / 1000.0));
if(meter.voltageMultiplier != 1.0 && meter.voltageMultiplier != 0.0)
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterVoltageMultiplier %.3f\n"), meter.voltageMultiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterVoltageMultiplier %.3f\n"), meter.voltageMultiplier / 1000.0));
if(meter.amperageMultiplier != 1.0 && meter.amperageMultiplier != 0.0)
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterAmperageMultiplier %.3f\n"), meter.amperageMultiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAmperageMultiplier %.3f\n"), meter.amperageMultiplier / 1000.0));
if(meter.accumulatedMultiplier != 1.0 && meter.accumulatedMultiplier != 0.0)
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("meterAccumulatedMultiplier %.3f\n"), meter.accumulatedMultiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("meterAccumulatedMultiplier %.3f\n"), meter.accumulatedMultiplier / 1000.0));
}
if(includeGpio) {
@@ -2163,41 +2407,41 @@ void AmsWebServer::configFileDownload() {
config->getMeterConfig(meter);
GpioConfig gpio;
config->getGpioConfig(gpio);
if(meter.rxPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioHanPin %d\n"), meter.rxPin));
if(meter.rxPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioHanPinPullup %d\n"), meter.rxPinPullup ? 1 : 0));
if(gpio.apPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioApPin %d\n"), gpio.apPin));
if(gpio.ledPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioLedPin %d\n"), gpio.ledPin));
if(gpio.ledPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioLedInverted %d\n"), gpio.ledInverted ? 1 : 0));
if(gpio.ledPinRed != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioLedPinRed %d\n"), gpio.ledPinRed));
if(gpio.ledPinGreen != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioLedPinGreen %d\n"), gpio.ledPinGreen));
if(gpio.ledPinBlue != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioLedPinBlue %d\n"), gpio.ledPinBlue));
if(gpio.ledPinRed != 0xFF || gpio.ledPinGreen != 0xFF || gpio.ledPinBlue != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioLedRgbInverted %d\n"), gpio.ledRgbInverted ? 1 : 0));
if(gpio.tempSensorPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioTempSensorPin %d\n"), gpio.tempSensorPin));
if(gpio.tempAnalogSensorPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioTempAnalogSensorPin %d\n"), gpio.tempAnalogSensorPin));
if(gpio.vccPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioVccPin %d\n"), gpio.vccPin));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioVccOffset %.2f\n"), gpio.vccOffset / 100.0));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioVccMultiplier %.3f\n"), gpio.vccMultiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioVccBootLimit %.1f\n"), gpio.vccBootLimit / 10.0));
if(gpio.vccPin != 0xFF && gpio.vccResistorGnd != 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioVccResistorGnd %d\n"), gpio.vccResistorGnd));
if(gpio.vccPin != 0xFF && gpio.vccResistorVcc != 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("gpioVccResistorVcc %d\n"), gpio.vccResistorVcc));
if(meter.rxPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioHanPin %d\n"), meter.rxPin));
if(meter.rxPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioHanPinPullup %d\n"), meter.rxPinPullup ? 1 : 0));
if(gpio.apPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioApPin %d\n"), gpio.apPin));
if(gpio.ledPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPin %d\n"), gpio.ledPin));
if(gpio.ledPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedInverted %d\n"), gpio.ledInverted ? 1 : 0));
if(gpio.ledPinRed != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPinRed %d\n"), gpio.ledPinRed));
if(gpio.ledPinGreen != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPinGreen %d\n"), gpio.ledPinGreen));
if(gpio.ledPinBlue != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedPinBlue %d\n"), gpio.ledPinBlue));
if(gpio.ledPinRed != 0xFF || gpio.ledPinGreen != 0xFF || gpio.ledPinBlue != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioLedRgbInverted %d\n"), gpio.ledRgbInverted ? 1 : 0));
if(gpio.tempSensorPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioTempSensorPin %d\n"), gpio.tempSensorPin));
if(gpio.tempAnalogSensorPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioTempAnalogSensorPin %d\n"), gpio.tempAnalogSensorPin));
if(gpio.vccPin != 0xFF) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccPin %d\n"), gpio.vccPin));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccOffset %.2f\n"), gpio.vccOffset / 100.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccMultiplier %.3f\n"), gpio.vccMultiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccBootLimit %.1f\n"), gpio.vccBootLimit / 10.0));
if(gpio.vccPin != 0xFF && gpio.vccResistorGnd != 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccResistorGnd %d\n"), gpio.vccResistorGnd));
if(gpio.vccPin != 0xFF && gpio.vccResistorVcc != 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("gpioVccResistorVcc %d\n"), gpio.vccResistorVcc));
}
if(includeNtp) {
NtpConfig ntp;
config->getNtpConfig(ntp);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ntpEnable %d\n"), ntp.enable ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ntpDhcp %d\n"), ntp.dhcp ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ntpTimezone %s\n"), ntp.timezone));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("ntpServer %s\n"), ntp.server));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpEnable %d\n"), ntp.enable ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpDhcp %d\n"), ntp.dhcp ? 1 : 0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpTimezone %s\n"), ntp.timezone));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("ntpServer %s\n"), ntp.server));
}
if(includePrice) {
PriceServiceConfig price;
config->getPriceServiceConfig(price);
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("priceEnabled %d\n"), price.enabled ? 1 : 0));
if(strlen(price.entsoeToken) == 36 && includeSecrets) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("priceEntsoeToken %s\n"), price.entsoeToken));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("priceArea %s\n"), price.area));
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("priceCurrency %s\n"), price.currency));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceEnabled %d\n"), price.enabled ? 1 : 0));
if(strlen(price.entsoeToken) == 36 && includeSecrets) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceEntsoeToken %s\n"), price.entsoeToken));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceArea %s\n"), price.area));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceCurrency %s\n"), price.currency));
if(ps != NULL) {
uint8_t i = 0;
std::vector<PriceConfig> pc = ps->getPriceConfig();
@@ -2263,7 +2507,7 @@ void AmsWebServer::configFileDownload() {
if(strlen(hours) > 0) hours[strlen(hours)-1] = '\0';
}
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("priceModifier %i \"%s\" %s %s %.4f %s %s %02d-%02d %02d-%02d\n"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("priceModifier %i \"%s\" %s %s %.4f %s %s %02d-%02d %02d-%02d\n"),
i,
p.name,
direction,
@@ -2285,7 +2529,7 @@ void AmsWebServer::configFileDownload() {
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
if(eac.thresholds[9] > 0) server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("thresholds %d %d %d %d %d %d %d %d %d %d %d\n"),
if(eac.thresholds[9] > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("thresholds %d %d %d %d %d %d %d %d %d %d %d\n"),
eac.thresholds[0],
eac.thresholds[1],
eac.thresholds[2],
@@ -2303,7 +2547,7 @@ void AmsWebServer::configFileDownload() {
if(ds != NULL) {
DayDataPoints day = ds->getDayData();
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("dayplot %d %lu %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dayplot %d %lu %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
day.version,
(int32_t) day.lastMeterReadTime,
day.activeImport / 1000.0,
@@ -2334,7 +2578,7 @@ void AmsWebServer::configFileDownload() {
ds->getHourImport(23)
));
if(day.activeExport > 0) {
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR(" %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR(" %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"),
day.activeExport / 1000.0,
ds->getHourExport(0),
ds->getHourExport(1),
@@ -2366,7 +2610,7 @@ void AmsWebServer::configFileDownload() {
}
MonthDataPoints month = ds->getMonthData();
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("monthplot %d %lu %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("monthplot %d %lu %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
month.version,
(int32_t) month.lastMeterReadTime,
month.activeImport / 1000.0,
@@ -2404,7 +2648,7 @@ void AmsWebServer::configFileDownload() {
ds->getDayImport(31)
));
if(month.activeExport > 0) {
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR(" %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR(" %.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"),
month.activeExport / 1000.0,
ds->getDayExport(1),
ds->getDayExport(2),
@@ -2447,7 +2691,7 @@ void AmsWebServer::configFileDownload() {
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
EnergyAccountingData ead = ea->getData();
server.sendContent(buf, snprintf_P(buf, BUF_SIZE_COMMON, PSTR("energyaccounting %d %d %.2f %.2f %.2f %.2f %.2f %.2f %d %d %.2f %d %d %.2f %d %d %.2f %d %d %.2f %d %d %.2f %.2f %.2f"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("energyaccounting %d %d %.2f %.2f %.2f %.2f %.2f %.2f %d %d %.2f %d %d %.2f %d %d %.2f %d %d %.2f %d %d %.2f %.2f %.2f"),
ead.version,
ead.month,
ea->getCostYesterday(),
@@ -2479,7 +2723,7 @@ void AmsWebServer::configFileDownload() {
}
void AmsWebServer::configFilePost() {
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"true",
"",
performRestart ? "true" : "false"
@@ -2495,7 +2739,7 @@ void AmsWebServer::configFileUpload() {
HTTPUpload& upload = uploadFile(FILE_CFG);
if(upload.status == UPLOAD_FILE_END) {
performRestart = true;
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"true",
"",
performRestart ? "true" : "false"
@@ -2529,18 +2773,18 @@ void AmsWebServer::modifyDayPlot() {
return;
for(uint8_t i = 0; i < 24; i++) {
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("i%02d"), i);
snprintf_P(buf, BufferSize, PSTR("i%02d"), i);
if(server.hasArg(buf)) {
ds->setHourImport(i, server.arg(buf).toDouble() * 1000);
}
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("e%02d"), i);
snprintf_P(buf, BufferSize, PSTR("e%02d"), i);
if(server.hasArg(buf)) {
ds->setHourExport(i, server.arg(buf).toDouble() * 1000);
}
}
bool ret = ds->save();
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"true",
"",
ret ? "true" : "false"
@@ -2554,18 +2798,18 @@ void AmsWebServer::modifyMonthPlot() {
return;
for(uint8_t i = 1; i <= 31; i++) {
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("i%02d"), i);
snprintf_P(buf, BufferSize, PSTR("i%02d"), i);
if(server.hasArg(buf)) {
ds->setDayImport(i, server.arg(buf).toDouble() * 1000);
}
snprintf_P(buf, BUF_SIZE_COMMON, PSTR("e%02d"), i);
snprintf_P(buf, BufferSize, PSTR("e%02d"), i);
if(server.hasArg(buf)) {
ds->setDayExport(i, server.arg(buf).toDouble() * 1000);
}
}
bool ret = ds->save();
snprintf_P(buf, BUF_SIZE_COMMON, RESPONSE_JSON,
snprintf_P(buf, BufferSize, RESPONSE_JSON,
"true",
"",
ret ? "true" : "false"
@@ -2637,7 +2881,7 @@ void AmsWebServer::wifiScan() {
return;
int16_t count = WiFi.scanNetworks();
int pos = snprintf_P(buf, BUF_SIZE_COMMON, PSTR("{\"c\":%d,\"n\":["), count);
int pos = snprintf_P(buf, BufferSize, PSTR("{\"c\":%d,\"n\":["), count);
count = min(count, (int16_t) 25); // Max 25 so that we don't overflow the buffer size
for (int16_t i = 0; i < count; i++) {
uint8_t* bssid = WiFi.BSSID(i);
@@ -2673,9 +2917,9 @@ void AmsWebServer::wifiScan() {
char bssidStr[18] = { 0 };
sprintf(bssidStr, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
pos += snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR("{\"b\":\"%s\",\"s\":\"%s\",\"r\":%d,\"c\":%d,\"e\":\"%s\"}%s"), bssidStr, ssid.c_str(), rssi, chan, encStr, i == count-1 ? "" : ",");
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR("{\"b\":\"%s\",\"s\":\"%s\",\"r\":%d,\"c\":%d,\"e\":\"%s\"}%s"), bssidStr, ssid.c_str(), rssi, chan, encStr, i == count-1 ? "" : ",");
}
pos += snprintf_P(buf+pos, BUF_SIZE_COMMON-pos, PSTR("]}"));
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR("]}"));
WiFi.scanDelete();
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);

View File

@@ -2,7 +2,7 @@
extra_configs = platformio-user.ini
[common]
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.2, OneWireNg@0.13.3, DallasTemperature@4.0.4, https://github.com/gskjold/RemoteDebug.git, PaulStoffregen/Time@1.6.1, JChristensen/Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, PriceService, EnergyAccounting, AmsFirmwareUpdater, AmsJsonGenerator, bblanchon/ArduinoJson@7.0.4, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, PassthroughMqttHandler, RealtimePlot, ConnectionHandler, MeterCommunicators
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.2, OneWireNg@0.13.3, DallasTemperature@4.0.4, https://github.com/gskjold/RemoteDebug.git, PaulStoffregen/Time@1.6.1, JChristensen/Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, PriceService, EnergyAccounting, AmsFirmwareUpdater, AmsJsonGenerator, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, PassthroughMqttHandler, RealtimePlot, ConnectionHandler, MeterCommunicators
lib_ignore = OneWire
extra_scripts =
pre:scripts/addversion.py
@@ -19,7 +19,7 @@ build_flags =
-fexceptions
[esp32]
lib_deps = WiFi, Ethernet, ESPmDNS, WiFiClientSecure, HTTPClient, FS, WebServer, ESP32 Async UDP, ESP32SSDP, mulmer89/ESPRandom@1.5.0, ${common.lib_deps}, CloudConnector, ZmartCharge, SvelteUi
lib_deps = WiFi, Ethernet, ESPmDNS, WiFiClientSecure, HTTPClient, FS, WebServer, ESP32 Async UDP, ESP32SSDP, mulmer89/ESPRandom@1.5.0, ${common.lib_deps}, bblanchon/ArduinoJson@7.0.4, CloudConnector, ZmartCharge, SvelteUi
[env:esp8266]
platform = espressif8266@4.2.1

View File

@@ -115,6 +115,8 @@ RemoteDebug Debug;
HardwareSerial Debug = Serial;
#endif
#define BUF_SIZE_COMMON (2048)
#include "Timezones.h"
#include "AmsFirmwareUpdater.h"
@@ -358,7 +360,7 @@ void resetBootCycleCounter(bool deepSleep) {
void setup() {
Serial.begin(115200);
config.load(); // Need to run this to make sure all configuration have been migrated before we load GPIO config
config.hasConfig(); // Need to run this to make sure all configuration have been migrated before we load GPIO config
if(!config.getGpioConfig(gpioConfig)) {
config.clearGpio(gpioConfig);
@@ -1575,6 +1577,7 @@ void handleDataSuccess(AmsData* data) {
time_t dataUpdateTime = now;
if(abs(now - meterTime) < 300) {
// If the meter timestamp is close to our internal clock, use meter timestamp, because that is best for data tracking
dataUpdateTime = meterTime;
}
@@ -1587,14 +1590,14 @@ void handleDataSuccess(AmsData* data) {
debugD_P(PSTR("READY to update (internal clock %02d:%02d:%02d UTC, meter clock: %02d:%02d:%02d, list type %d, est: %d, using clock: %d)"), tm.Hour, tm.Minute, tm.Second, mtm.Hour, mtm.Minute, mtm.Second, data->getListType(), wasCounterEstimated, dataUpdateTime == now);
tmElements_t dtm;
breakTime(dataUpdateTime, dtm);
if(dtm.Minute < 2 && data->getListType() >= 3) {
if(dtm.Minute < 1 && data->getListType() >= 3) {
debugD_P(PSTR("Updating data storage using actual data"));
saveData = ds.update(data, dataUpdateTime);
#if defined(_CLOUDCONNECTOR_H)
if(saveData && cloud != NULL) cloud->forceUpdate();
#endif
} else if(dtm.Minute == 2) {
} else if(dtm.Minute == 1) {
debugW_P(PSTR("Did not receive necessary data for previous hour, clearing"));
AmsData nullData;
saveData = ds.update(&nullData, dataUpdateTime);
@@ -1609,7 +1612,7 @@ void handleDataSuccess(AmsData* data) {
debugD_P(PSTR("NOT Ready to update (internal clock %02d:%02d:%02d UTC, meter clock: %02d:%02d:%02d, list type %d, est: %d)"), tm.Hour, tm.Minute, tm.Second, mtm.Hour, mtm.Minute, mtm.Second, data->getListType(), wasCounterEstimated);
}
if(ea.update(data)) {
if(ea.update(dataUpdateTime, data->getLastUpdateMillis(), data->getListType(), data->getActiveImportPower(), data->getActiveExportPower())) {
debugI_P(PSTR("Saving energy accounting"));
if(!ea.save()) {
debugW_P(PSTR("Unable to save energy accounting"));
@@ -1749,7 +1752,7 @@ void MQTT_connect() {
case 0:
case 5:
case 6:
mqttHandler = new JsonMqttHandler(&config, &Debug, (char*) commonBuffer, &hw, &ds, &updater);
mqttHandler = new JsonMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &hw, &ds, &updater);
break;
case 1:
case 2:
@@ -1850,8 +1853,8 @@ void configFileParse() {
size_t size;
char* buf = (char*) commonBuffer;
memset(buf, 0, BUF_SIZE_COMMON);
while((size = file.readBytesUntil('\n', buf, BUF_SIZE_COMMON)) > 0) {
memset(buf, 0, 1024);
while((size = file.readBytesUntil('\n', buf, 1024)) > 0) {
for(uint16_t i = 0; i < size; i++) {
if(buf[i] < 32 || buf[i] > 126) {
memset(buf+i, 0, size-i);