2024-05-23 22:20:01 +02:00

1280 lines
36 KiB
C++

/**
* @copyright Utilitech AS 2023
* License: Fair Source
*
*/
#include "AmsConfiguration.h"
#include "hexutils.h"
#if defined(ESP32)
#include "ESPRandom.h"
#endif
bool AmsConfiguration::getSystemConfig(SystemConfig& config) {
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
if(configVersion == EEPROM_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) {
EEPROM.get(CONFIG_SYSTEM_START, config);
EEPROM.end();
return true;
} else {
config.boardType = 0xFF;
config.vendorConfigured = false;
config.userConfigured = false;
config.dataCollectionConsent = 0;
config.energyspeedometer = 0;
memset(config.country, 0, 3);
return false;
}
}
bool AmsConfiguration::setSystemConfig(SystemConfig& config) {
SystemConfig existing;
if(getSystemConfig(existing)) {
sysChanged |= config.boardType != existing.boardType;
sysChanged |= config.vendorConfigured != existing.vendorConfigured;
sysChanged |= config.userConfigured != existing.userConfigured;
sysChanged |= config.dataCollectionConsent != existing.dataCollectionConsent;
sysChanged |= strcmp(config.country, existing.country) != 0;
sysChanged |= config.energyspeedometer != existing.energyspeedometer;
}
EEPROM.begin(EEPROM_SIZE);
stripNonAscii((uint8_t*) config.country, 2);
EEPROM.put(CONFIG_SYSTEM_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::isSystemConfigChanged() {
return sysChanged;
}
void AmsConfiguration::ackSystemConfigChanged() {
sysChanged = false;
}
bool AmsConfiguration::getNetworkConfig(NetworkConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_NETWORK_START, config);
EEPROM.end();
if(config.sleep > 2) config.sleep = 1;
return true;
} else {
clearNetworkConfig(config);
return false;
}
}
bool AmsConfiguration::setNetworkConfig(NetworkConfig& config) {
NetworkConfig existing;
if(config.sleep > 2) config.sleep = 1;
if(config.mode < 1 || config.mode > 4) config.mode = 1;
if(getNetworkConfig(existing)) {
networkChanged |= strcmp(config.ssid, existing.ssid) != 0;
networkChanged |= strcmp(config.psk, existing.psk) != 0;
networkChanged |= strcmp(config.ip, existing.ip) != 0;
if(strlen(config.ip) > 0) {
networkChanged |= strcmp(config.gateway, existing.gateway) != 0;
networkChanged |= strcmp(config.subnet, existing.subnet) != 0;
networkChanged |= strcmp(config.dns1, existing.dns1) != 0;
networkChanged |= strcmp(config.dns2, existing.dns2) != 0;
}
networkChanged |= strcmp(config.hostname, existing.hostname) != 0;
networkChanged |= config.power != existing.power;
networkChanged |= config.sleep != existing.sleep;
networkChanged |= config.use11b != existing.use11b;
networkChanged |= config.mode != existing.mode;
} else {
networkChanged = true;
}
stripNonAscii((uint8_t*) config.ssid, 32, true);
stripNonAscii((uint8_t*) config.psk, 64);
stripNonAscii((uint8_t*) config.ip, 16);
stripNonAscii((uint8_t*) config.gateway, 16);
stripNonAscii((uint8_t*) config.subnet, 16);
stripNonAscii((uint8_t*) config.dns1, 16);
stripNonAscii((uint8_t*) config.dns2, 16);
stripNonAscii((uint8_t*) config.hostname, 32);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_NETWORK_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearNetworkConfig(NetworkConfig& config) {
memset(config.ssid, 0, 32);
memset(config.psk, 0, 64);
clearNetworkConfigIp(config);
uint16_t chipId;
#if defined(ESP32)
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
config.power = 195;
#else
chipId = ESP.getChipId();
config.power = 205;
#endif
strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str());
config.mdns = true;
config.sleep = 0xFF;
config.use11b = 1;
config.ipv6 = false;
}
void AmsConfiguration::clearNetworkConfigIp(NetworkConfig& config) {
memset(config.ip, 0, 16);
memset(config.gateway, 0, 16);
memset(config.subnet, 0, 16);
memset(config.dns1, 0, 16);
memset(config.dns2, 0, 16);
}
bool AmsConfiguration::isNetworkConfigChanged() {
return networkChanged;
}
void AmsConfiguration::ackNetworkConfigChange() {
networkChanged = false;
}
bool AmsConfiguration::getMqttConfig(MqttConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_MQTT_START, config);
EEPROM.end();
if(config.magic != 0x7B) {
config.stateUpdate = false;
config.stateUpdateInterval = 10;
config.magic = 0x7B;
}
return true;
} else {
clearMqtt(config);
return false;
}
}
bool AmsConfiguration::setMqttConfig(MqttConfig& config) {
MqttConfig existing;
if(getMqttConfig(existing)) {
mqttChanged |= strcmp(config.host, existing.host) != 0;
mqttChanged |= config.port != existing.port;
mqttChanged |= strcmp(config.clientId, existing.clientId) != 0;
mqttChanged |= strcmp(config.publishTopic, existing.publishTopic) != 0;
mqttChanged |= strcmp(config.subscribeTopic, existing.subscribeTopic) != 0;
mqttChanged |= strcmp(config.username, existing.username) != 0;
mqttChanged |= strcmp(config.password, existing.password) != 0;
mqttChanged |= config.payloadFormat != existing.payloadFormat;
mqttChanged |= config.ssl != existing.ssl;
mqttChanged |= config.stateUpdate != existing.stateUpdate;
mqttChanged |= config.stateUpdateInterval != existing.stateUpdateInterval;
} else {
mqttChanged = true;
}
stripNonAscii((uint8_t*) config.host, 128);
stripNonAscii((uint8_t*) config.clientId, 32);
stripNonAscii((uint8_t*) config.publishTopic, 64);
stripNonAscii((uint8_t*) config.subscribeTopic, 64);
stripNonAscii((uint8_t*) config.username, 128);
stripNonAscii((uint8_t*) config.password, 256);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_MQTT_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearMqtt(MqttConfig& config) {
memset(config.host, 0, 128);
config.port = 1883;
memset(config.clientId, 0, 32);
memset(config.publishTopic, 0, 64);
memset(config.subscribeTopic, 0, 64);
memset(config.username, 0, 128);
memset(config.password, 0, 256);
config.payloadFormat = 0;
config.ssl = false;
config.magic = 0x7B;
config.stateUpdate = false;
config.stateUpdateInterval = 10;
}
void AmsConfiguration::setMqttChanged() {
mqttChanged = true;
}
bool AmsConfiguration::isMqttChanged() {
return mqttChanged;
}
void AmsConfiguration::ackMqttChange() {
mqttChanged = false;
}
bool AmsConfiguration::getWebConfig(WebConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_WEB_START, config);
EEPROM.end();
return true;
} else {
clearWebConfig(config);
return false;
}
}
bool AmsConfiguration::setWebConfig(WebConfig& config) {
stripNonAscii((uint8_t*) config.username, 37);
stripNonAscii((uint8_t*) config.password, 37);
stripNonAscii((uint8_t*) config.context, 37);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_WEB_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearWebConfig(WebConfig& config) {
config.security = 0;
memset(config.username, 0, 37);
memset(config.password, 0, 37);
memset(config.context, 0, 37);
}
bool AmsConfiguration::getMeterConfig(MeterConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_METER_START, config);
EEPROM.end();
if(config.bufferSize < 1 || config.bufferSize > 64) {
#if defined(ESP32)
config.bufferSize = 2;
#else
config.bufferSize = 1;
#endif
}
return true;
} else {
clearMeter(config);
return false;
}
}
bool AmsConfiguration::setMeterConfig(MeterConfig& config) {
if(config.bufferSize < 1) config.bufferSize = 1;
if(config.bufferSize > 64) config.bufferSize = 64;
MeterConfig existing;
if(getMeterConfig(existing)) {
meterChanged |= config.baud != existing.baud;
meterChanged |= config.parity != existing.parity;
meterChanged |= config.invert != existing.invert;
meterChanged |= config.distributionSystem != existing.distributionSystem;
meterChanged |= config.mainFuse != existing.mainFuse;
meterChanged |= config.productionCapacity != existing.productionCapacity;
meterChanged |= strcmp((char*) config.encryptionKey, (char*) existing.encryptionKey);
meterChanged |= strcmp((char*) config.authenticationKey, (char*) existing.authenticationKey);
meterChanged |= config.bufferSize != existing.bufferSize;
meterChanged |= config.rxPin != existing.rxPin;
meterChanged |= config.rxPinPullup != existing.rxPinPullup;
meterChanged |= config.txPin != existing.txPin;
meterChanged |= config.wattageMultiplier != existing.wattageMultiplier;
meterChanged |= config.voltageMultiplier != existing.voltageMultiplier;
meterChanged |= config.amperageMultiplier != existing.amperageMultiplier;
meterChanged |= config.accumulatedMultiplier != existing.accumulatedMultiplier;
meterChanged |= config.source != existing.source;
meterChanged |= config.parser != existing.parser;
} else {
meterChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_METER_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearMeter(MeterConfig& config) {
config.rxPin = 0xFF;
config.txPin = 0xFF;
config.rxPinPullup = true;
config.baud = 0;
config.parity = 0;
config.invert = false;
config.distributionSystem = 2;
config.mainFuse = 40;
config.productionCapacity = 0;
memset(config.encryptionKey, 0, 16);
memset(config.authenticationKey, 0, 16);
config.wattageMultiplier = 0;
config.voltageMultiplier = 0;
config.amperageMultiplier = 0;
config.accumulatedMultiplier = 0;
config.source = 1; // Serial
config.parser = 0; // Auto
config.bufferSize = 1; // 64 bytes
}
bool AmsConfiguration::isMeterChanged() {
return meterChanged;
}
void AmsConfiguration::ackMeterChanged() {
meterChanged = false;
}
void AmsConfiguration::setMeterChanged() {
meterChanged = true;
}
bool AmsConfiguration::getDebugConfig(DebugConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_DEBUG_START, config);
EEPROM.end();
return true;
} else {
clearDebug(config);
return false;
}
}
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);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearDebug(DebugConfig& config) {
config.level = 5;
config.telnet = false;
config.serial = false;
}
bool AmsConfiguration::getDomoticzConfig(DomoticzConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_DOMOTICZ_START, config);
EEPROM.end();
return true;
} else {
clearDomo(config);
return false;
}
}
bool AmsConfiguration::setDomoticzConfig(DomoticzConfig& config) {
DomoticzConfig existing;
if(getDomoticzConfig(existing)) {
mqttChanged |= config.elidx != existing.elidx;
mqttChanged |= config.vl1idx != existing.vl1idx;
mqttChanged |= config.vl2idx != existing.vl2idx;
mqttChanged |= config.vl3idx != existing.vl3idx;
mqttChanged |= config.cl1idx != existing.cl1idx;
} else {
mqttChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_DOMOTICZ_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearDomo(DomoticzConfig& config) {
config.elidx = 0;
config.vl1idx = 0;
config.vl2idx = 0;
config.vl3idx = 0;
config.cl1idx = 0;
}
bool AmsConfiguration::getHomeAssistantConfig(HomeAssistantConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_HA_START, config);
EEPROM.end();
if(stripNonAscii((uint8_t*) config.discoveryPrefix, 64) || stripNonAscii((uint8_t*) config.discoveryHostname, 64) || stripNonAscii((uint8_t*) config.discoveryNameTag, 16)) {
clearHomeAssistantConfig(config);
}
return true;
} else {
clearHomeAssistantConfig(config);
return false;
}
}
bool AmsConfiguration::setHomeAssistantConfig(HomeAssistantConfig& config) {
HomeAssistantConfig existing;
if(getHomeAssistantConfig(existing)) {
mqttChanged |= strcmp(config.discoveryPrefix, existing.discoveryPrefix) != 0;
mqttChanged |= strcmp(config.discoveryHostname, existing.discoveryHostname) != 0;
mqttChanged |= strcmp(config.discoveryNameTag, existing.discoveryNameTag) != 0;
} else {
mqttChanged = true;
}
stripNonAscii((uint8_t*) config.discoveryPrefix, 64);
stripNonAscii((uint8_t*) config.discoveryHostname, 64);
stripNonAscii((uint8_t*) config.discoveryNameTag, 16);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_HA_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearHomeAssistantConfig(HomeAssistantConfig& config) {
memset(config.discoveryPrefix, 0, 64);
memset(config.discoveryHostname, 0, 64);
memset(config.discoveryNameTag, 0, 16);
}
bool AmsConfiguration::pinUsed(uint8_t pin, GpioConfig& config) {
if(pin == 0xFF)
return false;
return
pin == config.apPin ||
pin == config.ledPin ||
pin == config.ledPinRed ||
pin == config.ledPinGreen ||
pin == config.ledPinBlue ||
pin == config.tempSensorPin ||
pin == config.tempAnalogSensorPin ||
pin == config.vccPin ||
pin == config.ledDisablePin
;
}
bool AmsConfiguration::getGpioConfig(GpioConfig& config) {
EEPROM.begin(EEPROM_SIZE);
uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
if(configVersion == EEPROM_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) {
EEPROM.get(CONFIG_GPIO_START, config);
EEPROM.end();
return true;
} else {
clearGpio(config);
return false;
}
}
bool AmsConfiguration::setGpioConfig(GpioConfig& config) {
GpioConfig existing;
/* This currently does not work, as it checks its own pin
if(pinUsed(config.hanPin, config)) {
debugger->println(F("HAN pin already used"));
return false;
}
if(pinUsed(config.apPin, config)) {
debugger->println(F("AP pin already used"));
return false;
}
if(pinUsed(config.ledPin, config)) {
debugger->println(F("LED pin already used"));
return false;
}
if(pinUsed(config.ledPinRed, config)) {
debugger->println(F("LED RED pin already used"));
return false;
}
if(pinUsed(config.ledPinGreen, config)) {
debugger->println(F("LED GREEN pin already used"));
return false;
}
if(pinUsed(config.ledPinBlue, config)) {
debugger->println(F("LED BLUE pin already used"));
return false;
}
if(pinUsed(config.tempSensorPin, config)) {
debugger->println(F("Temp sensor pin already used"));
return false;
}
if(pinUsed(config.tempAnalogSensorPin, config)) {
debugger->println(F("Analog temp sensor pin already used"));
return false;
}
if(pinUsed(config.vccPin, config)) {
debugger->println(F("Vcc pin already used"));
return false;
}
if(pinUsed(config.ledDisablePin, config)) {
debugger->println(F("ledDisablePin already used"));
return false;
}
*/
if(config.apPin >= 0)
pinMode(config.apPin, INPUT_PULLUP);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_GPIO_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearGpio(GpioConfig& config, bool all) {
config.apPin = 0xFF;
config.ledPin = 0xFF;
config.ledInverted = true;
config.ledPinRed = 0xFF;
config.ledPinGreen = 0xFF;
config.ledPinBlue = 0xFF;
config.ledRgbInverted = true;
config.tempSensorPin = 0xFF;
config.tempAnalogSensorPin = 0xFF;
config.vccPin = 0xFF;
config.ledDisablePin = 0xFF;
if(all) {
config.vccOffset = 0;
config.vccMultiplier = 1000;
config.vccBootLimit = 0;
config.vccResistorGnd = 0;
config.vccResistorVcc = 0;
config.ledBehaviour = LED_BEHAVIOUR_DEFAULT;
}
}
bool AmsConfiguration::getNtpConfig(NtpConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_NTP_START, config);
EEPROM.end();
return true;
} else {
clearNtp(config);
return false;
}
}
bool AmsConfiguration::setNtpConfig(NtpConfig& config) {
NtpConfig existing;
if(getNtpConfig(existing)) {
if(config.enable != existing.enable) {
if(!existing.enable) {
networkChanged = true;
} else {
ntpChanged = true;
}
}
ntpChanged |= config.dhcp != existing.dhcp;
ntpChanged |= strcmp(config.server, existing.server) != 0;
ntpChanged |= strcmp(config.timezone, existing.timezone) != 0;
} else {
ntpChanged = true;
}
stripNonAscii((uint8_t*) config.server, 64);
stripNonAscii((uint8_t*) config.timezone, 32);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_NTP_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::isNtpChanged() {
return ntpChanged;
}
void AmsConfiguration::ackNtpChange() {
ntpChanged = false;
}
void AmsConfiguration::clearNtp(NtpConfig& config) {
config.enable = true;
config.dhcp = true;
strcpy_P(config.server, PSTR("pool.ntp.org"));
strcpy_P(config.timezone, PSTR("Europe/Oslo"));
}
bool AmsConfiguration::getPriceServiceConfig(PriceServiceConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_PRICE_START, config);
EEPROM.end();
if(strlen(config.entsoeToken) != 0 && strlen(config.entsoeToken) != 36) {
clearPriceServiceConfig(config);
}
return true;
} else {
clearPriceServiceConfig(config);
return false;
}
}
bool AmsConfiguration::setPriceServiceConfig(PriceServiceConfig& config) {
PriceServiceConfig existing;
if(getPriceServiceConfig(existing)) {
priceChanged |= strcmp(config.entsoeToken, existing.entsoeToken) != 0;
priceChanged |= strcmp(config.area, existing.area) != 0;
priceChanged |= strcmp(config.currency, existing.currency) != 0;
priceChanged |= config.enabled != existing.enabled;
} else {
priceChanged = true;
}
stripNonAscii((uint8_t*) config.entsoeToken, 37);
stripNonAscii((uint8_t*) config.area, 17);
stripNonAscii((uint8_t*) config.currency, 4);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_PRICE_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearPriceServiceConfig(PriceServiceConfig& config) {
memset(config.entsoeToken, 0, 37);
memset(config.area, 0, 17);
memset(config.currency, 0, 4);
config.unused1 = 1000;
config.enabled = false;
config.unused2 = 0;
}
bool AmsConfiguration::isPriceServiceChanged() {
return priceChanged;
}
void AmsConfiguration::ackPriceServiceChange() {
priceChanged = false;
}
bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_ENERGYACCOUNTING_START, config);
EEPROM.end();
if(config.thresholds[9] != 0xFFFF) {
clearEnergyAccountingConfig(config);
}
if(config.hours > 5) config.hours = 5;
return true;
} else {
clearEnergyAccountingConfig(config);
return false;
}
}
bool AmsConfiguration::setEnergyAccountingConfig(EnergyAccountingConfig& config) {
if(config.hours > 5) config.hours = 5;
EnergyAccountingConfig existing;
if(getEnergyAccountingConfig(existing)) {
for(int i = 0; i < 9; i++) {
if(existing.thresholds[i] != config.thresholds[i]) {
energyAccountingChanged = true;
}
}
config.thresholds[9] = 0xFFFF;
energyAccountingChanged |= config.hours != existing.hours;
} else {
energyAccountingChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_ENERGYACCOUNTING_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearEnergyAccountingConfig(EnergyAccountingConfig& config) {
config.thresholds[0] = 5;
config.thresholds[1] = 10;
config.thresholds[2] = 15;
config.thresholds[3] = 20;
config.thresholds[4] = 25;
config.thresholds[5] = 50;
config.thresholds[6] = 75;
config.thresholds[7] = 100;
config.thresholds[8] = 150;
config.thresholds[9] = 0xFFFF;
config.hours = 3;
}
bool AmsConfiguration::isEnergyAccountingChanged() {
return energyAccountingChanged;
}
void AmsConfiguration::ackEnergyAccountingChange() {
energyAccountingChanged = false;
}
bool AmsConfiguration::getUiConfig(UiConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_UI_START, config);
if(config.showImport > 2) clearUiConfig(config); // Must be wrong
EEPROM.end();
return true;
} else {
clearUiConfig(config);
return false;
}
}
bool AmsConfiguration::setUiConfig(UiConfig& config) {
UiConfig existing;
if(getUiConfig(existing)) {
uiLanguageChanged |= strcmp(config.language, existing.language) != 0;
} else {
uiLanguageChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_UI_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearUiConfig(UiConfig& config) {
// 1 = Enable, 2 = Auto, 0 = Disable
config.showImport = 1;
config.showExport = 2;
config.showVoltage = 2;
config.showAmperage = 2;
config.showReactive = 0;
config.showRealtime = 1;
config.showPeaks = 2;
config.showPricePlot = 2;
config.showDayPlot = 1;
config.showMonthPlot = 1;
config.showTemperaturePlot = 2;
config.showRealtimePlot = 2;
config.showPerPhasePower = 2;
config.showPowerFactor = 2;
config.darkMode = 2;
memset(config.language, 0, 3);
}
bool AmsConfiguration::isUiLanguageChanged() {
return uiLanguageChanged;
}
void AmsConfiguration::ackUiLanguageChange() {
uiLanguageChanged = false;
}
bool AmsConfiguration::setUpgradeInformation(int16_t exitCode, int16_t errorCode, const char* currentVersion, const char* nextVersion) {
UpgradeInformation upinfo;
upinfo.exitCode = exitCode;
upinfo.errorCode = errorCode;
strcpy(upinfo.fromVersion, currentVersion);
strcpy(upinfo.toVersion, nextVersion);
stripNonAscii((uint8_t*) upinfo.fromVersion, 8);
stripNonAscii((uint8_t*) upinfo.toVersion, 8);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_UPGRADE_INFO_START, upinfo);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::getUpgradeInformation(UpgradeInformation& upinfo) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_UPGRADE_INFO_START, upinfo);
EEPROM.end();
if(stripNonAscii((uint8_t*) upinfo.fromVersion, 8) || stripNonAscii((uint8_t*) upinfo.toVersion, 8)) {
clearUpgradeInformation(upinfo);
}
return true;
} else {
clearUpgradeInformation(upinfo);
return false;
}
}
void AmsConfiguration::clearUpgradeInformation(UpgradeInformation& upinfo) {
upinfo.exitCode = -1;
upinfo.errorCode = 0;
memset(upinfo.fromVersion, 0, 8);
memset(upinfo.toVersion, 0, 8);
}
bool AmsConfiguration::getCloudConfig(CloudConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_CLOUD_START, config);
EEPROM.end();
return true;
} else {
clearCloudConfig(config);
return false;
}
}
bool AmsConfiguration::setCloudConfig(CloudConfig& config) {
CloudConfig existing;
if(getCloudConfig(existing)) {
cloudChanged |= config.enabled != existing.enabled;
cloudChanged |= config.interval!= existing.interval;
cloudChanged |= config.port!= existing.port;
cloudChanged |= strcmp(config.hostname, existing.hostname) != 0;
cloudChanged |= memcmp(config.clientId, existing.clientId, 16) != 0;
} else {
cloudChanged = true;
}
stripNonAscii((uint8_t*) config.hostname, 64);
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(CONFIG_CLOUD_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
void AmsConfiguration::clearCloudConfig(CloudConfig& config) {
config.enabled = false;
strcpy_P(config.hostname, PSTR("cloud.amsleser.no"));
config.port = 7443;
config.interval = 10;
memset(config.clientId, 0, 16);
}
bool AmsConfiguration::isCloudChanged() {
return cloudChanged;
}
void AmsConfiguration::ackCloudConfig() {
cloudChanged = false;
}
void AmsConfiguration::setUiLanguageChanged() {
uiLanguageChanged = true;
}
void AmsConfiguration::clear() {
EEPROM.begin(EEPROM_SIZE);
SystemConfig sys;
EEPROM.get(CONFIG_SYSTEM_START, sys);
sys.userConfigured = false;
sys.dataCollectionConsent = 0;
sys.energyspeedometer = 0;
memset(sys.country, 0, 3);
EEPROM.put(CONFIG_SYSTEM_START, sys);
MeterConfig meter;
clearMeter(meter);
EEPROM.put(CONFIG_METER_START, meter);
NetworkConfig network;
clearNetworkConfig(network);
EEPROM.put(CONFIG_NETWORK_START, network);
MqttConfig mqtt;
clearMqtt(mqtt);
EEPROM.put(CONFIG_MQTT_START, mqtt);
WebConfig web;
clearWebConfig(web);
EEPROM.put(CONFIG_WEB_START, web);
DomoticzConfig domo;
clearDomo(domo);
EEPROM.put(CONFIG_DOMOTICZ_START, domo);
HomeAssistantConfig haconf;
clearHomeAssistantConfig(haconf);
EEPROM.put(CONFIG_HA_START, haconf);
NtpConfig ntp;
clearNtp(ntp);
EEPROM.put(CONFIG_NTP_START, ntp);
PriceServiceConfig price;
clearPriceServiceConfig(price);
EEPROM.put(CONFIG_PRICE_START, price);
EnergyAccountingConfig eac;
clearEnergyAccountingConfig(eac);
EEPROM.put(CONFIG_ENERGYACCOUNTING_START, eac);
DebugConfig debug;
clearDebug(debug);
EEPROM.put(CONFIG_DEBUG_START, debug);
UiConfig ui;
clearUiConfig(ui);
EEPROM.put(CONFIG_UI_START, ui);
UpgradeInformation upinfo;
clearUpgradeInformation(upinfo);
EEPROM.put(CONFIG_UPGRADE_INFO_START, upinfo);
CloudConfig cloud;
clearCloudConfig(cloud);
EEPROM.put(CONFIG_CLOUD_START, cloud);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CLEARED_INDICATOR);
EEPROM.commit();
EEPROM.end();
}
bool AmsConfiguration::hasConfig() {
if(configVersion == 0) {
EEPROM.begin(EEPROM_SIZE);
configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
EEPROM.end();
}
if(configVersion > EEPROM_CHECK_SUM) {
if(loadFromFs(EEPROM_CHECK_SUM)) {
configVersion = EEPROM_CHECK_SUM;
} else {
configVersion = 0;
}
} else {
switch(configVersion) {
case 103:
configVersion = -1; // Prevent loop
if(relocateConfig103()) {
configVersion = 104;
} else {
configVersion = 0;
return false;
}
case EEPROM_CHECK_SUM:
return true;
default:
configVersion = 0;
return false;
}
}
return configVersion == EEPROM_CHECK_SUM;
}
int AmsConfiguration::getConfigVersion() {
return configVersion;
}
bool AmsConfiguration::relocateConfig103() {
EEPROM.begin(EEPROM_SIZE);
MeterConfig meter;
UpgradeInformation upinfo;
UiConfig ui;
GpioConfig103 gpio103;
PriceServiceConfig price;
NetworkConfig wifi;
EnergyAccountingConfig eac;
WebConfig103 web103;
DebugConfig debug;
DomoticzConfig domo;
NtpConfig ntp;
MqttConfig mqtt;
HomeAssistantConfig ha;
EEPROM.get(CONFIG_METER_START_103, meter);
EEPROM.get(CONFIG_UPGRADE_INFO_START_103, upinfo);
EEPROM.get(CONFIG_UI_START_103, ui);
EEPROM.get(CONFIG_GPIO_START_103, gpio103);
EEPROM.get(CONFIG_ENTSOE_START_103, price);
EEPROM.get(CONFIG_WIFI_START_103, wifi);
EEPROM.get(CONFIG_ENERGYACCOUNTING_START_103, eac);
EEPROM.get(CONFIG_WEB_START_103, web103);
EEPROM.get(CONFIG_DEBUG_START_103, debug);
EEPROM.get(CONFIG_DOMOTICZ_START_103, domo);
EEPROM.get(CONFIG_NTP_START_103, ntp);
EEPROM.get(CONFIG_MQTT_START_103, mqtt);
EEPROM.get(CONFIG_HA_START_103, ha);
meter.rxPin = gpio103.hanPin;
meter.txPin = 0xFF;
meter.rxPinPullup = gpio103.hanPinPullup;
meter.source = 1;
meter.parser = 0;
#if defined(ESP8266)
if(meter.rxPin != 3 && meter.rxPin != 113) {
meter.bufferSize = 1;
}
#endif
wifi.mode = 1; // 1 == WiFi client
wifi.ipv6 = false;
GpioConfig gpio = {
gpio103.apPin,
gpio103.ledPin,
gpio103.ledInverted,
gpio103.ledPinRed,
gpio103.ledPinGreen,
gpio103.ledPinBlue,
gpio103.ledRgbInverted,
gpio103.tempSensorPin,
gpio103.tempAnalogSensorPin,
gpio103.vccPin,
gpio103.vccOffset,
gpio103.vccMultiplier,
gpio103.vccBootLimit,
gpio103.vccResistorGnd,
gpio103.vccResistorVcc,
gpio103.ledDisablePin,
gpio103.ledBehaviour
};
WebConfig web = {web103.security};
strcpy(web.username, web103.username);
strcpy(web.password, web103.password);
memset(web.context, 0, 37);
strcpy_P(ui.language, PSTR("en"));
ui.showRealtimePlot = 2;
ui.showPerPhasePower = 2;
ui.showPowerFactor = 2;
ui.darkMode = 2;
EEPROM.put(CONFIG_UPGRADE_INFO_START, upinfo);
EEPROM.put(CONFIG_NETWORK_START, wifi);
EEPROM.put(CONFIG_METER_START, meter);
EEPROM.put(CONFIG_GPIO_START, gpio);
EEPROM.put(CONFIG_PRICE_START, price);
EEPROM.put(CONFIG_ENERGYACCOUNTING_START, eac);
EEPROM.put(CONFIG_WEB_START, web);
EEPROM.put(CONFIG_DEBUG_START, debug);
EEPROM.put(CONFIG_NTP_START, ntp);
EEPROM.put(CONFIG_MQTT_START, mqtt);
EEPROM.put(CONFIG_DOMOTICZ_START, domo);
EEPROM.put(CONFIG_HA_START, ha);
EEPROM.put(CONFIG_UI_START, ui);
CloudConfig cloud;
clearCloudConfig(cloud);
EEPROM.put(CONFIG_CLOUD_START, cloud);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 104);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::save() {
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);
bool success = EEPROM.commit();
EEPROM.end();
configVersion = EEPROM_CHECK_SUM;
return success;
}
void AmsConfiguration::saveToFs() {
}
bool AmsConfiguration::loadFromFs(uint8_t version) {
return false;
}
void AmsConfiguration::deleteFromFs(uint8_t version) {
}
void AmsConfiguration::print(Print* debugger)
{
debugger->println(F("-----------------------------------------------"));
NetworkConfig network;
if(getNetworkConfig(network)) {
debugger->println(F("--Network configuration--"));
switch(network.mode) {
case 1:
debugger->printf_P(PSTR("Mode: 'WiFi client'\r\n"));
break;
case 2:
debugger->printf_P(PSTR("Mode: 'WiFi AP'\r\n"));
break;
case 3:
debugger->printf_P(PSTR("Mode: 'Ethernet'\r\n"));
break;
}
debugger->printf_P(PSTR("SSID: '%s'\r\n"), network.ssid);
debugger->printf_P(PSTR("Psk: '%s'\r\n"), network.psk);
if(strlen(network.ip) > 0) {
debugger->printf_P(PSTR("IP: '%s'\r\n"), network.ip);
debugger->printf_P(PSTR("Gateway: '%s'\r\n"), network.gateway);
debugger->printf_P(PSTR("Subnet: '%s'\r\n"), network.subnet);
debugger->printf_P(PSTR("DNS1: '%s'\r\n"), network.dns1);
debugger->printf_P(PSTR("DNS2: '%s'\r\n"), network.dns2);
}
debugger->printf_P(PSTR("Hostname: '%s'\r\n"), network.hostname);
debugger->printf_P(PSTR("IPv6: '%s'\r\n"), network.ipv6 ? "Yes" : "No");
debugger->printf_P(PSTR("mDNS: '%s'\r\n"), network.mdns ? "Yes" : "No");
debugger->printf_P(PSTR("802.11b: '%s'\r\n"), network.use11b ? "Yes" : "No");
debugger->println(F(""));
delay(10);
debugger->flush();
}
MqttConfig mqtt;
if(getMqttConfig(mqtt)) {
debugger->println(F("--MQTT configuration--"));
if(strlen(mqtt.host) > 0) {
debugger->printf_P(PSTR("Enabled: Yes\r\n"));
debugger->printf_P(PSTR("Host: '%s'\r\n"), mqtt.host);
debugger->printf_P(PSTR("Port: %i\r\n"), mqtt.port);
debugger->printf_P(PSTR("Client ID: '%s'\r\n"), mqtt.clientId);
debugger->printf_P(PSTR("Publish topic: '%s'\r\n"), mqtt.publishTopic);
debugger->printf_P(PSTR("Subscribe topic: '%s'\r\n"), mqtt.subscribeTopic);
if (strlen(mqtt.username) > 0) {
debugger->printf_P(PSTR("Username: '%s'\r\n"), mqtt.username);
debugger->printf_P(PSTR("Password: '%s'\r\n"), mqtt.password);
}
debugger->printf_P(PSTR("Payload format: %i\r\n"), mqtt.payloadFormat);
debugger->printf_P(PSTR("SSL: %s\r\n"), mqtt.ssl ? "Yes" : "No");
} else {
debugger->printf_P(PSTR("Enabled: No\r\n"));
}
debugger->println(F(""));
delay(10);
debugger->flush();
}
WebConfig web;
if(getWebConfig(web)) {
debugger->println(F("--Web configuration--"));
debugger->printf_P(PSTR("Security: %i\r\n"), web.security);
if (web.security > 0) {
debugger->printf_P(PSTR("Username: '%s'\r\n"), web.username);
debugger->printf_P(PSTR("Password: '%s'\r\n"), web.password);
}
debugger->println(F(""));
delay(10);
debugger->flush();
}
MeterConfig meter;
if(getMeterConfig(meter)) {
debugger->println(F("--Meter configuration--"));
debugger->printf_P(PSTR("HAN RX: %i\r\n"), meter.rxPin);
debugger->printf_P(PSTR("HAN RX pullup %s\r\n"), meter.rxPinPullup ? "Yes" : "No");
debugger->printf_P(PSTR("Baud: %i\r\n"), meter.baud);
debugger->printf_P(PSTR("Parity: %i\r\n"), meter.parity);
debugger->printf_P(PSTR("Invert serial: %s\r\n"), meter.invert ? "Yes" : "No");
debugger->printf_P(PSTR("Buffer size: %i\r\n"), meter.bufferSize * 64);
debugger->printf_P(PSTR("Distribution system: %i\r\n"), meter.distributionSystem);
debugger->printf_P(PSTR("Main fuse: %i\r\n"), meter.mainFuse);
debugger->printf_P(PSTR("Production Capacity: %i\r\n"), meter.productionCapacity);
debugger->println(F(""));
delay(10);
debugger->flush();
}
GpioConfig gpio;
if(getGpioConfig(gpio)) {
debugger->println(F("--GPIO configuration--"));
debugger->printf_P(PSTR("LED pin: %i\r\n"), gpio.ledPin);
debugger->printf_P(PSTR("LED inverted: %s\r\n"), gpio.ledInverted ? "Yes" : "No");
debugger->printf_P(PSTR("LED red pin: %i\r\n"), gpio.ledPinRed);
debugger->printf_P(PSTR("LED green pin: %i\r\n"), gpio.ledPinGreen);
debugger->printf_P(PSTR("LED blue pin: %i\r\n"), gpio.ledPinBlue);
debugger->printf_P(PSTR("LED inverted: %s\r\n"), gpio.ledRgbInverted ? "Yes" : "No");
debugger->printf_P(PSTR("AP pin: %i\r\n"), gpio.apPin);
debugger->printf_P(PSTR("Temperature pin: %i\r\n"), gpio.tempSensorPin);
debugger->printf_P(PSTR("Temp analog pin: %i\r\n"), gpio.tempAnalogSensorPin);
debugger->printf_P(PSTR("Vcc pin: %i\r\n"), gpio.vccPin);
debugger->printf_P(PSTR("LED disable pin: %i\r\n"), gpio.ledDisablePin);
debugger->printf_P(PSTR("LED behaviour: %i\r\n"), gpio.ledBehaviour);
if(gpio.vccMultiplier > 0) {
debugger->printf_P(PSTR("Vcc multiplier: %f\r\n"), gpio.vccMultiplier / 1000.0);
}
if(gpio.vccOffset > 0) {
debugger->printf_P(PSTR("Vcc offset: %f\r\n"), gpio.vccOffset / 100.0);
}
if(gpio.vccBootLimit > 0) {
debugger->printf_P(PSTR("Vcc boot limit: %f\r\n"), gpio.vccBootLimit / 10.0);
}
debugger->printf_P(PSTR("GND resistor: %i\r\n"), gpio.vccResistorGnd);
debugger->printf_P(PSTR("Vcc resistor: %i\r\n"), gpio.vccResistorVcc);
debugger->println(F(""));
delay(10);
debugger->flush();
}
DomoticzConfig domo;
if(getDomoticzConfig(domo)) {
debugger->println(F("--Domoticz configuration--"));
if(mqtt.payloadFormat == 3 && domo.elidx > 0) {
debugger->printf_P(PSTR("Enabled: Yes\r\n"));
debugger->printf_P(PSTR("Domoticz ELIDX: %i\r\n"), domo.elidx);
debugger->printf_P(PSTR("Domoticz VL1IDX: %i\r\n"), domo.vl1idx);
debugger->printf_P(PSTR("Domoticz VL2IDX: %i\r\n"), domo.vl2idx);
debugger->printf_P(PSTR("Domoticz VL3IDX: %i\r\n"), domo.vl3idx);
debugger->printf_P(PSTR("Domoticz CL1IDX: %i\r\n"), domo.cl1idx);
} else {
debugger->printf_P(PSTR("Enabled: No\r\n"));
}
debugger->println(F(""));
delay(10);
debugger->flush();
}
NtpConfig ntp;
if(getNtpConfig(ntp)) {
debugger->println(F("--NTP configuration--"));
debugger->printf_P(PSTR("Enabled: %s\r\n"), ntp.enable ? "Yes" : "No");
if(ntp.enable) {
debugger->printf_P(PSTR("Timezone: %s\r\n"), ntp.timezone);
debugger->printf_P(PSTR("Server: %s\r\n"), ntp.server);
debugger->printf_P(PSTR("DHCP: %s\r\n"), ntp.dhcp ? "Yes" : "No");
}
debugger->println(F(""));
delay(10);
debugger->flush();
}
PriceServiceConfig price;
if(getPriceServiceConfig(price)) {
if(strlen(price.area) > 0) {
debugger->println(F("--Price configuration--"));
debugger->printf_P(PSTR("Area: %s\r\n"), price.area);
debugger->printf_P(PSTR("Currency: %s\r\n"), price.currency);
debugger->printf_P(PSTR("ENTSO-E Token: %s\r\n"), price.entsoeToken);
}
debugger->println(F(""));
delay(10);
debugger->flush();
}
UiConfig ui;
if(getUiConfig(ui)) {
debugger->println(F("--UI configuration--"));
debugger->printf_P(PSTR("Language: %s\r\n"), ui.language);
}
#if defined(ESP32)
CloudConfig cc;
if(getCloudConfig(cc)) {
String uuid = ESPRandom::uuidToString(cc.clientId);;
debugger->println(F("--UI configuration--"));
debugger->printf_P(PSTR("Enabled: %s\r\n"), cc.enabled ? "Yes" : "No");
debugger->printf_P(PSTR("Hostname: %s\r\n"), cc.hostname);
debugger->printf_P(PSTR("Client ID: %s\r\n"), uuid.c_str());
debugger->printf_P(PSTR("Interval: %d\r\n"), cc.interval);
}
#endif
debugger->println(F("-----------------------------------------------"));
debugger->flush();
}