From 548012e90b794700176973bf42a9903641a7976c Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Thu, 19 Mar 2026 13:25:36 +0100 Subject: [PATCH] Config via MQTT JSON payload --- .../include/AmsConfiguration.h | 7 +- lib/AmsConfiguration/src/AmsConfiguration.cpp | 211 +++------ lib/AmsMqttHandler/include/AmsMqttHandler.h | 21 +- lib/JsonMqttHandler/include/JsonMqttHandler.h | 8 + lib/JsonMqttHandler/src/JsonMqttHandler.cpp | 427 +++++++++++++++++- platformio.ini | 4 +- src/AmsToMqttBridge.cpp | 4 +- 7 files changed, 519 insertions(+), 163 deletions(-) diff --git a/lib/AmsConfiguration/include/AmsConfiguration.h b/lib/AmsConfiguration/include/AmsConfiguration.h index cc949e2b..c62cfb7d 100644 --- a/lib/AmsConfiguration/include/AmsConfiguration.h +++ b/lib/AmsConfiguration/include/AmsConfiguration.h @@ -10,7 +10,7 @@ #include "Arduino.h" #define EEPROM_SIZE 1024*3 -#define EEPROM_CHECK_SUM 104 // Used to check if config is stored. Change if structure changes +#define EEPROM_EXPECTED_VERSION 104 // Used to check if config is stored. Change if structure changes #define EEPROM_CLEARED_INDICATOR 0xFC #define EEPROM_CONFIG_ADDRESS 0 @@ -283,11 +283,12 @@ struct ZmartChargeConfig { class AmsConfiguration { public: + bool load(); + bool save(); + bool hasConfig(); int getConfigVersion(); - bool save(); - bool getSystemConfig(SystemConfig&); bool setSystemConfig(SystemConfig&); bool isSystemConfigChanged(); diff --git a/lib/AmsConfiguration/src/AmsConfiguration.cpp b/lib/AmsConfiguration/src/AmsConfiguration.cpp index 30536fb5..41561c39 100644 --- a/lib/AmsConfiguration/src/AmsConfiguration.cpp +++ b/lib/AmsConfiguration/src/AmsConfiguration.cpp @@ -11,16 +11,14 @@ #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_CHECK_SUM) { + if(configVersion == EEPROM_EXPECTED_VERSION) { return true; } else { if(configVersion == EEPROM_CLEARED_INDICATOR && config.boardType > 0 && config.boardType < 250) { @@ -52,12 +50,9 @@ bool AmsConfiguration::setSystemConfig(SystemConfig& config) { } else { sysChanged = true; } - EEPROM.begin(EEPROM_SIZE); stripNonAscii((uint8_t*) config.country, 2); EEPROM.put(CONFIG_SYSTEM_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return sysChanged; } bool AmsConfiguration::isSystemConfigChanged() { @@ -70,10 +65,8 @@ void AmsConfiguration::ackSystemConfigChanged() { 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; + EEPROM.get(CONFIG_NETWORK_START, config); + if(config.sleep > 2) config.sleep = 1; return true; } else { clearNetworkConfig(config); @@ -113,11 +106,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return networkChanged; } void AmsConfiguration::clearNetworkConfig(NetworkConfig& config) { @@ -158,10 +148,8 @@ void AmsConfiguration::ackNetworkConfigChange() { bool AmsConfiguration::getMqttConfig(MqttConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_MQTT_START, config); - EEPROM.end(); - if(config.magic != 0xA5) { // New magic for 2.4.11 + EEPROM.get(CONFIG_MQTT_START, config); + if(config.magic != 0xA5) { // New magic for 2.4.11 if(config.magic != 0x9C) { if(config.magic != 0x7B) { config.stateUpdate = false; @@ -213,11 +201,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return mqttChanged; } void AmsConfiguration::clearMqtt(MqttConfig& config) { @@ -253,10 +238,8 @@ void AmsConfiguration::ackMqttChange() { bool AmsConfiguration::getWebConfig(WebConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_WEB_START, config); - EEPROM.end(); - return true; + EEPROM.get(CONFIG_WEB_START, config); + return true; } else { clearWebConfig(config); return false; @@ -277,11 +260,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return webChanged; } void AmsConfiguration::clearWebConfig(WebConfig& config) { @@ -300,12 +280,10 @@ void AmsConfiguration::ackWebChange() { } bool AmsConfiguration::getMeterConfig(MeterConfig& config) { - EEPROM.begin(EEPROM_SIZE); uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS); - if(configVersion == EEPROM_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) { + if(configVersion == EEPROM_EXPECTED_VERSION || configVersion == EEPROM_CLEARED_INDICATOR) { EEPROM.get(CONFIG_METER_START, config); - EEPROM.end(); - if(config.bufferSize < 1 || config.bufferSize > 64) { + if(config.bufferSize < 1 || config.bufferSize > 64) { #if defined(ESP32) config.bufferSize = 2; #else @@ -346,11 +324,8 @@ bool AmsConfiguration::setMeterConfig(MeterConfig& config) { } else { meterChanged = true; } - EEPROM.begin(EEPROM_SIZE); EEPROM.put(CONFIG_METER_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return meterChanged; } void AmsConfiguration::clearMeter(MeterConfig& config) { @@ -388,10 +363,8 @@ void AmsConfiguration::setMeterChanged() { bool AmsConfiguration::getDebugConfig(DebugConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_DEBUG_START, config); - EEPROM.end(); - return true; + EEPROM.get(CONFIG_DEBUG_START, config); + return true; } else { clearDebug(config); return false; @@ -401,11 +374,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return true; } void AmsConfiguration::clearDebug(DebugConfig& config) { @@ -416,10 +386,8 @@ void AmsConfiguration::clearDebug(DebugConfig& config) { bool AmsConfiguration::getDomoticzConfig(DomoticzConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_DOMOTICZ_START, config); - EEPROM.end(); - return true; + EEPROM.get(CONFIG_DOMOTICZ_START, config); + return true; } else { clearDomo(config); return false; @@ -437,11 +405,8 @@ bool AmsConfiguration::setDomoticzConfig(DomoticzConfig& config) { } else { mqttChanged = true; } - EEPROM.begin(EEPROM_SIZE); EEPROM.put(CONFIG_DOMOTICZ_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return mqttChanged; } void AmsConfiguration::clearDomo(DomoticzConfig& config) { @@ -454,10 +419,8 @@ void AmsConfiguration::clearDomo(DomoticzConfig& config) { 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)) { + 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)) { clearHomeAssistantConfig(config); return false; } @@ -482,11 +445,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return mqttChanged; } void AmsConfiguration::clearHomeAssistantConfig(HomeAssistantConfig& config) { @@ -512,12 +472,10 @@ 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_CHECK_SUM || configVersion == EEPROM_CLEARED_INDICATOR) { + if(configVersion == EEPROM_EXPECTED_VERSION || configVersion == EEPROM_CLEARED_INDICATOR) { EEPROM.get(CONFIG_GPIO_START, config); - EEPROM.end(); - if(config.powersaving > 4) config.powersaving = 0; + if(config.powersaving > 4) config.powersaving = 0; return true; } else { clearGpio(config); @@ -572,11 +530,8 @@ bool AmsConfiguration::setGpioConfig(GpioConfig& config) { 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; + return true; } void AmsConfiguration::clearGpio(GpioConfig& config, bool all) { @@ -605,10 +560,8 @@ void AmsConfiguration::clearGpio(GpioConfig& config, bool all) { bool AmsConfiguration::getNtpConfig(NtpConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_NTP_START, config); - EEPROM.end(); - return true; + EEPROM.get(CONFIG_NTP_START, config); + return true; } else { clearNtp(config); return false; @@ -635,11 +588,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return ntpChanged; } bool AmsConfiguration::isNtpChanged() { @@ -659,10 +609,8 @@ void AmsConfiguration::clearNtp(NtpConfig& config) { 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) { + EEPROM.get(CONFIG_PRICE_START, config); + if(strlen(config.entsoeToken) != 0 && strlen(config.entsoeToken) != 36) { clearPriceServiceConfig(config); return false; } @@ -692,11 +640,8 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return priceChanged; } void AmsConfiguration::clearPriceServiceConfig(PriceServiceConfig& config) { @@ -718,10 +663,8 @@ void AmsConfiguration::ackPriceServiceChange() { bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_ENERGYACCOUNTING_START, config); - EEPROM.end(); - if(config.thresholds[9] != 0xFFFF) { + EEPROM.get(CONFIG_ENERGYACCOUNTING_START, config); + if(config.thresholds[9] != 0xFFFF) { clearEnergyAccountingConfig(config); return false; } @@ -747,11 +690,8 @@ bool AmsConfiguration::setEnergyAccountingConfig(EnergyAccountingConfig& config) } else { energyAccountingChanged = true; } - EEPROM.begin(EEPROM_SIZE); EEPROM.put(CONFIG_ENERGYACCOUNTING_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return energyAccountingChanged; } void AmsConfiguration::clearEnergyAccountingConfig(EnergyAccountingConfig& config) { @@ -778,11 +718,9 @@ void AmsConfiguration::ackEnergyAccountingChange() { bool AmsConfiguration::getUiConfig(UiConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_UI_START, config); + EEPROM.get(CONFIG_UI_START, config); if(config.showImport > 2) clearUiConfig(config); // Must be wrong - EEPROM.end(); - return true; + return true; } else { clearUiConfig(config); return false; @@ -796,11 +734,8 @@ bool AmsConfiguration::setUiConfig(UiConfig& config) { } else { uiLanguageChanged = true; } - EEPROM.begin(EEPROM_SIZE); EEPROM.put(CONFIG_UI_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return uiLanguageChanged; } void AmsConfiguration::clearUiConfig(UiConfig& config) { @@ -835,19 +770,14 @@ 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); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return true; } 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, 16) || stripNonAscii((uint8_t*) upinfo.toVersion, 16)) { + EEPROM.get(CONFIG_UPGRADE_INFO_START, upinfo); + if(stripNonAscii((uint8_t*) upinfo.fromVersion, 16) || stripNonAscii((uint8_t*) upinfo.toVersion, 16)) { clearUpgradeInformation(upinfo); return false; } @@ -870,10 +800,8 @@ void AmsConfiguration::clearUpgradeInformation(UpgradeInformation& upinfo) { bool AmsConfiguration::getCloudConfig(CloudConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_CLOUD_START, config); - EEPROM.end(); - if(config.proto > 2) config.proto = 0; + EEPROM.get(CONFIG_CLOUD_START, config); + if(config.proto > 2) config.proto = 0; return true; } else { clearCloudConfig(config); @@ -896,11 +824,8 @@ bool AmsConfiguration::setCloudConfig(CloudConfig& config) { stripNonAscii((uint8_t*) config.hostname, 64); - EEPROM.begin(EEPROM_SIZE); EEPROM.put(CONFIG_CLOUD_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return cloudChanged; } void AmsConfiguration::clearCloudConfig(CloudConfig& config) { @@ -922,11 +847,9 @@ void AmsConfiguration::ackCloudConfig() { bool AmsConfiguration::getZmartChargeConfig(ZmartChargeConfig& config) { if(hasConfig()) { - EEPROM.begin(EEPROM_SIZE); - EEPROM.get(CONFIG_ZC_START, config); - EEPROM.end(); - stripNonAscii((uint8_t*) config.token, 21); - stripNonAscii((uint8_t*) config.baseUrl, 64); + EEPROM.get(CONFIG_ZC_START, config); + 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); @@ -959,11 +882,8 @@ bool AmsConfiguration::setZmartChargeConfig(ZmartChargeConfig& config) { memset(config.baseUrl, 0, 64); } - EEPROM.begin(EEPROM_SIZE); EEPROM.put(CONFIG_ZC_START, config); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return zcChanged; } void AmsConfiguration::clearZmartChargeConfig(ZmartChargeConfig& config) { @@ -984,8 +904,6 @@ void AmsConfiguration::setUiLanguageChanged() { } void AmsConfiguration::clear() { - EEPROM.begin(EEPROM_SIZE); - SystemConfig sys; EEPROM.get(CONFIG_SYSTEM_START, sys); sys.userConfigured = false; @@ -1053,18 +971,16 @@ void AmsConfiguration::clear() { EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CLEARED_INDICATOR); EEPROM.commit(); - EEPROM.end(); } -bool AmsConfiguration::hasConfig() { +bool AmsConfiguration::load() { + EEPROM.begin(EEPROM_SIZE); 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; + if(configVersion > EEPROM_EXPECTED_VERSION) { + if(loadFromFs(EEPROM_EXPECTED_VERSION)) { + configVersion = EEPROM_EXPECTED_VERSION; } else { configVersion = 0; } @@ -1078,14 +994,18 @@ bool AmsConfiguration::hasConfig() { configVersion = 0; return false; } - case EEPROM_CHECK_SUM: + case EEPROM_EXPECTED_VERSION: return true; default: configVersion = 0; return false; } } - return configVersion == EEPROM_CHECK_SUM; + return configVersion == EEPROM_EXPECTED_VERSION; +} + +bool AmsConfiguration::hasConfig() { + return configVersion == EEPROM_EXPECTED_VERSION; } int AmsConfiguration::getConfigVersion() { @@ -1093,8 +1013,6 @@ int AmsConfiguration::getConfigVersion() { } bool AmsConfiguration::relocateConfig103() { - EEPROM.begin(EEPROM_SIZE); - MeterConfig meter; UpgradeInformation upinfo; UiConfig ui; @@ -1191,19 +1109,14 @@ bool AmsConfiguration::relocateConfig103() { EEPROM.put(CONFIG_ZC_START, zcc); EEPROM.put(EEPROM_CONFIG_ADDRESS, 104); - bool ret = EEPROM.commit(); - EEPROM.end(); - return ret; + return EEPROM.commit(); } bool AmsConfiguration::save() { - EEPROM.begin(EEPROM_SIZE); uint8_t configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS); - EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM); + EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_EXPECTED_VERSION); bool success = EEPROM.commit(); - EEPROM.end(); - - configVersion = EEPROM_CHECK_SUM; + configVersion = EEPROM_EXPECTED_VERSION; return success; } diff --git a/lib/AmsMqttHandler/include/AmsMqttHandler.h b/lib/AmsMqttHandler/include/AmsMqttHandler.h index e4117100..c210ab0f 100644 --- a/lib/AmsMqttHandler/include/AmsMqttHandler.h +++ b/lib/AmsMqttHandler/include/AmsMqttHandler.h @@ -23,12 +23,19 @@ class AmsMqttHandler { public: #if defined(AMS_REMOTE_DEBUG) - AmsMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, AmsFirmwareUpdater* updater) { + AmsMqttHandler(AmsConfiguration* config, RemoteDebug* debugger, char* buf, AmsFirmwareUpdater* updater) { #else - AmsMqttHandler(MqttConfig& mqttConfig, Stream* debugger, char* buf, AmsFirmwareUpdater* updater) { + AmsMqttHandler(AmsConfiguration* config, Stream* debugger, char* buf, AmsFirmwareUpdater* updater) { + #endif + this->config = config; + config->getMqttConfig(mqttConfig); + AmsMqttHandler(mqttConfig, debugger, buf, updater); + }; + #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; - this->mqttConfigChanged = true; this->debugger = debugger; this->json = buf; this->updater = updater; @@ -37,7 +44,7 @@ public: pubTopic = String(mqttConfig.publishTopic); subTopic = String(mqttConfig.subscribeTopic); if(subTopic.isEmpty()) subTopic = pubTopic+"/command"; - }; + } void setCaVerification(bool); void setConfig(MqttConfig& mqttConfig); @@ -79,8 +86,10 @@ protected: #else Stream* debugger; #endif + AmsConfiguration* config; MqttConfig mqttConfig; - bool mqttConfigChanged = true; + bool mqttConfigChanged = false; + MQTTClient mqtt = MQTTClient(256); unsigned long lastMqttRetry = -10000; bool caVerification = true; diff --git a/lib/JsonMqttHandler/include/JsonMqttHandler.h b/lib/JsonMqttHandler/include/JsonMqttHandler.h index cffd816a..ae99fc00 100644 --- a/lib/JsonMqttHandler/include/JsonMqttHandler.h +++ b/lib/JsonMqttHandler/include/JsonMqttHandler.h @@ -11,6 +11,14 @@ 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 diff --git a/lib/JsonMqttHandler/src/JsonMqttHandler.cpp b/lib/JsonMqttHandler/src/JsonMqttHandler.cpp index 05d7d2e3..d9463e56 100644 --- a/lib/JsonMqttHandler/src/JsonMqttHandler.cpp +++ b/lib/JsonMqttHandler/src/JsonMqttHandler.cpp @@ -9,6 +9,7 @@ #include "hexutils.h" #include "Uptime.h" #include "AmsJsonGenerator.h" +#include "ArduinoJson.h" bool JsonMqttHandler::publish(AmsData* update, AmsData* previousState, EnergyAccounting* ea, PriceService* ps) { if(strlen(mqttConfig.publishTopic) == 0) { @@ -525,7 +526,431 @@ void JsonMqttHandler::onMessage(String &topic, String &payload) { if (debugger->isActive(RemoteDebug::DEBUG)) #endif debugger->printf_P(PSTR(" - this is our subscribed topic\n")); - if(payload.equals("fwupgrade")) { + + if(payload.startsWith("{")) { + DynamicJsonDocument doc(BufferSize); + 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(); + if(obj.containsKey("action")) { + const char* action = obj["action"]; + if(strcmp(action, "fwupgrade") == 0) { + if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) { + updater->setTargetVersion(updater->getNextVersion()); + } + } else if(strcmp(action, "dayplot") == 0) { + char pubTopic[192]; + snprintf_P(pubTopic, 192, PSTR("%s/dayplot"), mqttConfig.publishTopic); + AmsJsonGenerator::generateDayPlotJson(ds, json, BufferSize); + bool ret = mqtt.publish(pubTopic, json); + loop(); + } else if(strcmp(action, "monthplot") == 0) { + char pubTopic[192]; + snprintf_P(pubTopic, 192, PSTR("%s/monthplot"), mqttConfig.publishTopic); + AmsJsonGenerator::generateMonthPlotJson(ds, json, BufferSize); + bool ret = mqtt.publish(pubTopic, json); + loop(); + } else if(strcmp(action, "config") == 0 && obj.containsKey("config")) { + JsonObject configObj = obj["config"]; + + if(configObj.containsKey("system")) { + SystemConfig newConfig; + config->getSystemConfig(newConfig); + + JsonObject systemObj = configObj["system"]; + if(systemObj.containsKey("country")) { + strlcpy(newConfig.country, systemObj["country"], sizeof(newConfig.country)); + } + if(systemObj.containsKey("firmwareChannel")) { + newConfig.firmwareChannel = systemObj["firmwareChannel"]; + } + config->setSystemConfig(newConfig); + } + + if(configObj.containsKey("network")) { + NetworkConfig newConfig; + config->getNetworkConfig(newConfig); + + JsonObject networkObj = configObj["network"]; + if(networkObj.containsKey("mode")) { + newConfig.mode = networkObj["mode"]; + } + if(newConfig.mode == 1 || newConfig.mode == 2) { + if(networkObj.containsKey("ssid")) { + strlcpy(newConfig.ssid, networkObj["ssid"], sizeof(newConfig.ssid)); + } + if(networkObj.containsKey("psk")) { + strlcpy(newConfig.psk, networkObj["psk"], sizeof(newConfig.psk)); + } + if(networkObj.containsKey("power")) { + newConfig.power = networkObj["power"]; + } + if(networkObj.containsKey("sleep")) { + newConfig.sleep = networkObj["sleep"]; + } + if(networkObj.containsKey("use11b")) { + newConfig.use11b = networkObj["use11b"]; + } + } + if(networkObj.containsKey("ip")) { + strlcpy(newConfig.ip, networkObj["ip"], sizeof(newConfig.ip)); + } + if(networkObj.containsKey("gateway")) { + strlcpy(newConfig.gateway, networkObj["gateway"], sizeof(newConfig.gateway)); + } + if(networkObj.containsKey("subnet")) { + strlcpy(newConfig.subnet, networkObj["subnet"], sizeof(newConfig.subnet)); + } + if(networkObj.containsKey("dns1")) { + strlcpy(newConfig.dns1, networkObj["dns1"], sizeof(newConfig.dns1)); + } + if(networkObj.containsKey("dns2")) { + strlcpy(newConfig.dns2, networkObj["dns2"], sizeof(newConfig.dns2)); + } + if(networkObj.containsKey("hostname")) { + strlcpy(newConfig.hostname, networkObj["hostname"], sizeof(newConfig.hostname)); + } + if(networkObj.containsKey("mdns")) { + newConfig.mdns = networkObj["mdns"]; + } + if(networkObj.containsKey("ipv6")) { + newConfig.ipv6 = networkObj["ipv6"]; + } + config->setNetworkConfig(newConfig); + } + + if(configObj.containsKey("web")) { + WebConfig newConfig; + config->getWebConfig(newConfig); + + JsonObject webObj = configObj["web"]; + if(webObj.containsKey("security")) { + newConfig.security = webObj["security"]; + } + if(newConfig.security > 0) { + if(webObj.containsKey("username")) { + strlcpy(newConfig.username, webObj["username"], sizeof(newConfig.username)); + } + if(webObj.containsKey("password")) { + strlcpy(newConfig.password, webObj["password"], sizeof(newConfig.password)); + } + } + if(webObj.containsKey("context")) { + strlcpy(newConfig.context, webObj["context"], sizeof(newConfig.context)); + } + config->setWebConfig(newConfig); + } + + if(configObj.containsKey("meter")) { + MeterConfig newConfig; + config->getMeterConfig(newConfig); + + JsonObject meterObj = configObj["meter"]; + if(meterObj.containsKey("baud")) { + newConfig.baud = meterObj["baud"]; + } + if(meterObj.containsKey("parity")) { + newConfig.parity = meterObj["parity"]; + } + if(meterObj.containsKey("invert")) { + newConfig.invert = meterObj["invert"]; + } + if(meterObj.containsKey("distributionSystem")) { + newConfig.distributionSystem = meterObj["distributionSystem"]; + } + if(meterObj.containsKey("mainFuse")) { + newConfig.mainFuse = meterObj["mainFuse"]; + } + if(meterObj.containsKey("productionCapacity")) { + newConfig.productionCapacity = meterObj["productionCapacity"]; + } + if(meterObj.containsKey("wattageMultiplier")) { + newConfig.wattageMultiplier = meterObj["wattageMultiplier"]; + } + if(meterObj.containsKey("voltageMultiplier")) { + newConfig.voltageMultiplier = meterObj["voltageMultiplier"]; + } + if(meterObj.containsKey("amperageMultiplier")) { + newConfig.amperageMultiplier = meterObj["amperageMultiplier"]; + } + if(meterObj.containsKey("accumulatedMultiplier")) { + newConfig.accumulatedMultiplier = meterObj["accumulatedMultiplier"]; + } + if(meterObj.containsKey("parser")) { + newConfig.parser = meterObj["parser"]; + } + if(meterObj.containsKey("bufferSize")) { + newConfig.bufferSize = meterObj["bufferSize"]; + } + if(meterObj.containsKey("rxPin")) { + newConfig.rxPin = meterObj["rxPin"]; + } + if(meterObj.containsKey("rxPinPullup")) { + newConfig.rxPinPullup = meterObj["rxPinPullup"]; + } + if(meterObj.containsKey("txPin")) { + newConfig.txPin = meterObj["txPin"]; + } + } + + if(configObj.containsKey("mqtt")) { + MqttConfig newConfig; + config->getMqttConfig(newConfig); + + JsonObject mqttObj = configObj["mqtt"]; + if(mqttObj.containsKey("host")) { + strlcpy(newConfig.host, mqttObj["host"], sizeof(newConfig.host)); + } + if(mqttObj.containsKey("port")) { + newConfig.port = mqttObj["port"]; + } + if(mqttObj.containsKey("clientId")) { + strlcpy(newConfig.clientId, mqttObj["clientId"], sizeof(newConfig.clientId)); + } + if(mqttObj.containsKey("publishTopic")) { + strlcpy(newConfig.publishTopic, mqttObj["publishTopic"], sizeof(newConfig.publishTopic)); + } + if(mqttObj.containsKey("subscribeTopic")) { + strlcpy(newConfig.subscribeTopic, mqttObj["subscribeTopic"], sizeof(newConfig.subscribeTopic)); + } + if(mqttObj.containsKey("username")) { + strlcpy(newConfig.username, mqttObj["username"], sizeof(newConfig.username)); + } + if(mqttObj.containsKey("password")) { + strlcpy(newConfig.password, mqttObj["password"], sizeof(newConfig.password)); + } + if(mqttObj.containsKey("payloadFormat")) { + newConfig.payloadFormat = mqttObj["payloadFormat"]; + } + if(mqttObj.containsKey("ssl")) { + newConfig.ssl = mqttObj["ssl"]; + } + if(mqttObj.containsKey("stateUpdate")) { + newConfig.stateUpdate = mqttObj["stateUpdate"]; + } + if(mqttObj.containsKey("stateUpdateInterval")) { + newConfig.stateUpdateInterval = mqttObj["stateUpdateInterval"]; + } + if(mqttObj.containsKey("timeout")) { + newConfig.timeout = mqttObj["timeout"]; + } + if(mqttObj.containsKey("keepalive")) { + newConfig.keepalive = mqttObj["keepalive"]; + } + if(mqttObj.containsKey("rebootMinutes")) { + newConfig.rebootMinutes = mqttObj["rebootMinutes"]; + } + config->setMqttConfig(newConfig); + + if(mqttObj.containsKey("domoticz")) { + DomoticzConfig newConfig; + config->getDomoticzConfig(newConfig); + JsonObject domoticzObj = mqttObj["domoticz"]; + if(domoticzObj.containsKey("elidx")) { + newConfig.elidx = domoticzObj["elidx"]; + } + if(domoticzObj.containsKey("vl1idx")) { + newConfig.vl1idx = domoticzObj["vl1idx"]; + } + if(domoticzObj.containsKey("vl2idx")) { + newConfig.vl2idx = domoticzObj["vl2idx"]; + } + if(domoticzObj.containsKey("vl3idx")) { + newConfig.vl3idx = domoticzObj["vl3idx"]; + } + if(domoticzObj.containsKey("cl1idx")) { + newConfig.cl1idx = domoticzObj["cl1idx"]; + } + config->setDomoticzConfig(newConfig); + } + + if(mqttObj.containsKey("homeAssistant")) { + HomeAssistantConfig newConfig; + config->getHomeAssistantConfig(newConfig); + JsonObject haObj = mqttObj["homeAssistant"]; + if(haObj.containsKey("discoveryPrefix")) { + strlcpy(newConfig.discoveryPrefix, haObj["discoveryPrefix"], sizeof(newConfig.discoveryPrefix)); + } + if(haObj.containsKey("discoveryHostname")) { + strlcpy(newConfig.discoveryHostname, haObj["discoveryHostname"], sizeof(newConfig.discoveryHostname)); + } + if(haObj.containsKey("discoveryNameTag")) { + strlcpy(newConfig.discoveryNameTag, haObj["discoveryNameTag"], sizeof(newConfig.discoveryNameTag)); + } + config->setHomeAssistantConfig(newConfig); + } + } + + if(configObj.containsKey("debug")) { + DebugConfig newConfig; + config->getDebugConfig(newConfig); + + JsonObject debugObj = configObj["debug"]; + if(debugObj.containsKey("telnet")) { + newConfig.telnet = debugObj["telnet"]; + } + if(debugObj.containsKey("serial")) { + newConfig.serial = debugObj["serial"]; + } + if(debugObj.containsKey("level")) { + newConfig.level = debugObj["level"]; + } + config->setDebugConfig(newConfig); + } + + if(configObj.containsKey("gpio")) { + GpioConfig newConfig; + config->getGpioConfig(newConfig); + + JsonObject gpioObj = configObj["gpio"]; + if(gpioObj.containsKey("apPin")) { + newConfig.apPin = gpioObj["apPin"]; + } + if(gpioObj.containsKey("ledPin")) { + newConfig.ledPin = gpioObj["ledPin"]; + } + if(gpioObj.containsKey("ledInverted")) { + newConfig.ledInverted = gpioObj["ledInverted"]; + } + if(gpioObj.containsKey("ledPinRed")) { + newConfig.ledPinRed = gpioObj["ledPinRed"]; + } + if(gpioObj.containsKey("ledPinGreen")) { + newConfig.ledPinGreen = gpioObj["ledPinGreen"]; + } + if(gpioObj.containsKey("ledPinBlue")) { + newConfig.ledPinBlue = gpioObj["ledPinBlue"]; + } + if(gpioObj.containsKey("ledRgbInverted")) { + newConfig.ledRgbInverted = gpioObj["ledRgbInverted"]; + } + if(gpioObj.containsKey("tempSensorPin")) { + newConfig.tempSensorPin = gpioObj["tempSensorPin"]; + } + if(gpioObj.containsKey("tempAnalogSensorPin")) { + newConfig.tempAnalogSensorPin = gpioObj["tempAnalogSensorPin"]; + } + if(gpioObj.containsKey("vccPin")) { + newConfig.vccPin = gpioObj["vccPin"]; + } + if(gpioObj.containsKey("vccOffset")) { + newConfig.vccOffset = gpioObj["vccOffset"]; + } + if(gpioObj.containsKey("vccMultiplier")) { + newConfig.vccMultiplier = gpioObj["vccMultiplier"]; + } + if(gpioObj.containsKey("vccBootLimit")) { + newConfig.vccBootLimit = gpioObj["vccBootLimit"]; + } + if(gpioObj.containsKey("vccResistorGnd")) { + newConfig.vccResistorGnd = gpioObj["vccResistorGnd"]; + } + if(gpioObj.containsKey("vccResistorVcc")) { + newConfig.vccResistorVcc = gpioObj["vccResistorVcc"]; + } + if(gpioObj.containsKey("ledDisablePin")) { + newConfig.ledDisablePin = gpioObj["ledDisablePin"]; + } + if(gpioObj.containsKey("ledBehaviour")) { + newConfig.ledBehaviour = gpioObj["ledBehaviour"]; + } + config->setGpioConfig(newConfig); + } + + if(configObj.containsKey("ntp")) { + NtpConfig newConfig; + config->getNtpConfig(newConfig); + + JsonObject ntpObj = configObj["ntp"]; + if(ntpObj.containsKey("enable")) { + newConfig.enable = ntpObj["enable"]; + } + if(ntpObj.containsKey("dhcp")) { + newConfig.dhcp = ntpObj["dhcp"]; + } + if(ntpObj.containsKey("server")) { + strlcpy(newConfig.server, ntpObj["server"], sizeof(newConfig.server)); + } + if(ntpObj.containsKey("timezone")) { + strlcpy(newConfig.timezone, ntpObj["timezone"], sizeof(newConfig.timezone)); + } + config->setNtpConfig(newConfig); + } + + if(configObj.containsKey("priceService")) { + PriceServiceConfig newConfig; + config->getPriceServiceConfig(newConfig); + JsonObject priceServiceObj = configObj["priceService"]; + if(priceServiceObj.containsKey("area")) { + strlcpy(newConfig.area, priceServiceObj["area"], sizeof(newConfig.area)); + } + if(priceServiceObj.containsKey("currency")) { + strlcpy(newConfig.currency, priceServiceObj["currency"], sizeof(newConfig.currency)); + } + if(priceServiceObj.containsKey("resolutionInMinutes")) { + newConfig.resolutionInMinutes = priceServiceObj["resolutionInMinutes"]; + } + if(priceServiceObj.containsKey("enabled")) { + newConfig.enabled = priceServiceObj["enabled"]; + } + config->setPriceServiceConfig(newConfig); + } + + if(configObj.containsKey("cloud")) { + JsonObject cloudObj = configObj["cloud"]; + + if(cloudObj.containsKey("amsleser")) { + CloudConfig newConfig; + config->getCloudConfig(newConfig); + + JsonObject amsCloudObj = cloudObj["amsleser"]; + if(amsCloudObj.containsKey("enabled")) { + newConfig.enabled = amsCloudObj["enabled"]; + } + if(amsCloudObj.containsKey("interval")) { + newConfig.interval = amsCloudObj["interval"]; + } + if(amsCloudObj.containsKey("hostname")) { + strlcpy(newConfig.hostname, amsCloudObj["hostname"], sizeof(newConfig.hostname)); + } + if(amsCloudObj.containsKey("port")) { + newConfig.port = amsCloudObj["port"]; + } + if(amsCloudObj.containsKey("clientId")) { + strlcpy((char*)newConfig.clientId, amsCloudObj["clientId"], sizeof(newConfig.clientId)); + } + if(amsCloudObj.containsKey("proto")) { + newConfig.proto = amsCloudObj["proto"]; + } + config->setCloudConfig(newConfig); + } + + if(cloudObj.containsKey("zmartCharge")) { + ZmartChargeConfig newConfig; + config->getZmartChargeConfig(newConfig); + JsonObject zmartChargeObj = cloudObj["zmartCharge"]; + if(zmartChargeObj.containsKey("enabled")) { + newConfig.enabled = zmartChargeObj["enabled"]; + } + if(zmartChargeObj.containsKey("token")) { + strlcpy(newConfig.token, zmartChargeObj["token"], sizeof(newConfig.token)); + } + if(zmartChargeObj.containsKey("baseUrl")) { + strlcpy(newConfig.baseUrl, zmartChargeObj["baseUrl"], sizeof(newConfig.baseUrl)); + } + config->setZmartChargeConfig(newConfig); + } + } + } + } + } + } else if(payload.equals("fwupgrade")) { if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) { updater->setTargetVersion(updater->getNextVersion()); } diff --git a/platformio.ini b/platformio.ini index 147c58d0..72b2eec7 100755 --- a/platformio.ini +++ b/platformio.ini @@ -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, 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, bblanchon/ArduinoJson@7.0.4, 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}, bblanchon/ArduinoJson@7.0.4, CloudConnector, ZmartCharge, SvelteUi +lib_deps = WiFi, Ethernet, ESPmDNS, WiFiClientSecure, HTTPClient, FS, WebServer, ESP32 Async UDP, ESP32SSDP, mulmer89/ESPRandom@1.5.0, ${common.lib_deps}, CloudConnector, ZmartCharge, SvelteUi [env:esp8266] platform = espressif8266@4.2.1 diff --git a/src/AmsToMqttBridge.cpp b/src/AmsToMqttBridge.cpp index c0d09647..3f73900c 100644 --- a/src/AmsToMqttBridge.cpp +++ b/src/AmsToMqttBridge.cpp @@ -360,7 +360,7 @@ void resetBootCycleCounter(bool deepSleep) { void setup() { Serial.begin(115200); - config.hasConfig(); // Need to run this to make sure all configuration have been migrated before we load GPIO config + config.load(); // Need to run this to make sure all configuration have been migrated before we load GPIO config if(!config.getGpioConfig(gpioConfig)) { config.clearGpio(gpioConfig); @@ -1751,7 +1751,7 @@ void MQTT_connect() { case 0: case 5: case 6: - mqttHandler = new JsonMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &hw, &ds, &updater); + mqttHandler = new JsonMqttHandler(&config, &Debug, (char*) commonBuffer, &hw, &ds, &updater); break; case 1: case 2: