diff --git a/src/app_config.cpp b/src/app_config.cpp index b2330440..d2328ea2 100644 --- a/src/app_config.cpp +++ b/src/app_config.cpp @@ -31,6 +31,8 @@ #define FACTORY_OFFSET CONFIG_SIZE #define FACTORY_SIZE 1024 +uint32_t config_version = 0; + // Wifi Network Strings String esid; String epass; @@ -267,6 +269,46 @@ config_load_settings() } } +void append_common_config(DynamicJsonDocument &doc) { + doc["config_version"] = config_version; + + #if ENABLE_CONFIG_CHANGE_NOTIFICATION + // Read only information + doc["firmware"] = evse.getFirmwareVersion(); + doc["protocol"] = "-"; + doc["espflash"] = ESPAL.getFlashChipSize(); + doc["espinfo"] = ESPAL.getChipInfo(); + doc["buildenv"] = buildenv; + doc["version"] = currentfirmware; + doc["evse_serial"] = evse.getSerial(); + doc["wifi_serial"] = serial; + + // Static supported protocols + JsonArray mqtt_supported_protocols = doc.createNestedArray("mqtt_supported_protocols"); + mqtt_supported_protocols.add("mqtt"); + mqtt_supported_protocols.add("mqtts"); + + JsonArray http_supported_protocols = doc.createNestedArray("http_supported_protocols"); + http_supported_protocols.add("http"); + + // OpenEVSE module config + if(!doc.containsKey("diode_check")) doc["diode_check"] = evse.isDiodeCheckEnabled(); + if(!doc.containsKey("gfci_check")) doc["gfci_check"] = evse.isGfiTestEnabled(); + if(!doc.containsKey("ground_check")) doc["ground_check"] = evse.isGroundCheckEnabled(); + if(!doc.containsKey("relay_check")) doc["relay_check"] = evse.isStuckRelayCheckEnabled(); + if(!doc.containsKey("vent_check")) doc["vent_check"] = evse.isVentRequiredEnabled(); + if(!doc.containsKey("temp_check")) doc["temp_check"] = evse.isTemperatureCheckEnabled(); + if(!doc.containsKey("service")) doc["service"] = static_cast(evse.getServiceLevel()); + if(!doc.containsKey("scale")) doc["scale"] = evse.getCurrentSensorScale(); + if(!doc.containsKey("offset")) doc["offset"] = evse.getCurrentSensorOffset(); + if(!doc.containsKey("max_current_soft")) doc["max_current_soft"] = evse.getMaxConfiguredCurrent(); + + // OpenEVSE Read only information + doc["min_current_hard"] = evse.getMinCurrent(); + doc["max_current_hard"] = evse.getMaxHardwareCurrent(); + #endif +} + void config_changed(String name) { DBUGF("%s changed", name.c_str()); @@ -349,16 +391,17 @@ bool config_deserialize(const char *json) bool config_deserialize(DynamicJsonDocument &doc) { - return user_config.deserialize(doc); -} + append_common_config(doc); -bool config_serialize(String& json, bool longNames, bool compactOutput, bool hideSecrets) -{ - return user_config.serialize(json, longNames, compactOutput, hideSecrets); + // WiFi module config + return user_config.deserialize(doc); } bool config_serialize(DynamicJsonDocument &doc, bool longNames, bool compactOutput, bool hideSecrets) { + append_common_config(doc); + + // WiFi module config return user_config.serialize(doc, longNames, compactOutput, hideSecrets); } @@ -382,5 +425,132 @@ void config_reset() config_load_settings(); } +bool web_server_config_deserialise(DynamicJsonDocument &doc, bool factory) +{ + bool config_modified = false; + + if(config_deserialize(doc)) + { + config_commit(factory); + config_modified = true; + DBUGLN("Config updated"); + } + + #if ENABLE_CONFIG_CHANGE_NOTIFICATION + // Update EVSE config + // Update the EVSE setting flags, a little low level, may move later + if(doc.containsKey("diode_check")) + { + bool enable = doc["diode_check"]; + if(enable != evse.isDiodeCheckEnabled()) { + evse.enableDiodeCheck(enable); + config_modified = true; + DBUGLN("diode_check changed"); + } + } + + if(doc.containsKey("gfci_check")) + { + bool enable = doc["gfci_check"]; + if(enable != evse.isGfiTestEnabled()) { + evse.enableGfiTestCheck(enable); + config_modified = true; + DBUGLN("gfci_check changed"); + } + } + + if(doc.containsKey("ground_check")) + { + bool enable = doc["ground_check"]; + if(enable != evse.isGroundCheckEnabled()) { + evse.enableGroundCheck(enable); + config_modified = true; + DBUGLN("ground_check changed"); + } + } + + if(doc.containsKey("relay_check")) + { + bool enable = doc["relay_check"]; + if(enable != evse.isStuckRelayCheckEnabled()) { + evse.enableStuckRelayCheck(enable); + config_modified = true; + DBUGLN("relay_check changed"); + } + } + + if(doc.containsKey("vent_check")) + { + bool enable = doc["vent_check"]; + if(enable != evse.isVentRequiredEnabled()) { + evse.enableVentRequired(enable); + config_modified = true; + DBUGLN("vent_check changed"); + } + } + + if(doc.containsKey("temp_check")) + { + bool enable = doc["temp_check"]; + if(enable != evse.isTemperatureCheckEnabled()) { + evse.enableTemperatureCheck(enable); + config_modified = true; + DBUGLN("temp_check changed"); + } + } + + if(doc.containsKey("service")) + { + EvseMonitor::ServiceLevel service = static_cast(doc["service"].as()); + if(service != evse.getServiceLevel()) { + evse.setServiceLevel(service); + config_modified = true; + DBUGLN("service changed"); + } + } + + if(doc.containsKey("max_current_soft")) + { + long current = doc["max_current_soft"]; + if(current != evse.getMaxConfiguredCurrent()) { + evse.setMaxConfiguredCurrent(current); + config_modified = true; + DBUGLN("max_current_soft changed"); + } + } + + if(doc.containsKey("scale") && doc.containsKey("offset")) + { + long scale = doc["scale"]; + long offset = doc["offset"]; + if(scale != evse.getCurrentSensorScale() || offset != evse.getCurrentSensorOffset()) { + evse.configureCurrentSensorScale(doc["scale"], doc["offset"]); + config_modified = true; + DBUGLN("scale changed"); + } + } + #endif + + if(config_modified) + { + config_version++; + doc["config_version"] = config_version; + DBUGVAR(config_version); + + #if ENABLE_CONFIG_CHANGE_NOTIFICATION + // HACK: force a flush of the RAPI command queue to make sure the config + // is updated before we send the response + DBUG("Flushing RAPI command queue ..."); + rapiSender.flush(); + DBUGLN(" Done"); + + StaticJsonDocument<128> event; + event["config_version"] = config_version; + event_send(event); + #endif + } + + return config_modified; +} diff --git a/src/app_config.h b/src/app_config.h index 5d80b422..5506af6e 100644 --- a/src/app_config.h +++ b/src/app_config.h @@ -223,7 +223,6 @@ bool config_deserialize(DynamicJsonDocument &doc); void config_commit(bool factory = false); // Write config settings to JSON object -bool config_serialize(String& json, bool longNames = true, bool compactOutput = false, bool hideSecrets = false); bool config_serialize(DynamicJsonDocument &doc, bool longNames = true, bool compactOutput = false, bool hideSecrets = false); #endif // _EMONESP_CONFIG_H diff --git a/src/mqtt.cpp b/src/mqtt.cpp index cefdadba..63d61a51 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -289,6 +289,7 @@ mqtt_connect() event_send(doc); // Publish MQTT override/claim + mqtt_publish_config(); mqtt_publish_override(); mqtt_publish_claim(); mqtt_publish_schedule(); @@ -399,6 +400,9 @@ mqtt_publish(JsonDocument &data) { topic = mqtt_topic + "/"; } + // re-publish config in any case + mqtt_publish_config(); + Profile_End(mqtt_publish, 5); } @@ -489,6 +493,22 @@ mqtt_publish_schedule() { } } +void +mqtt_publish_config() { + if(!config_mqtt_enabled() || !mqttclient.connected()) { + return; + } + const size_t capacity = JSON_OBJECT_SIZE(128) + 1024; + DynamicJsonDocument doc(capacity); + + config_serialize(doc, true, false, true); + mqtt_publish_json(doc, "/config"); + + String fulltopic = mqtt_topic + "/config_version"; + String config_version = doc["config_version"]; + mqttclient.publish(fulltopic, config_version, true); +} + void mqtt_publish_json(JsonDocument &data, const char* topic) { Profile_Start(mqtt_publish_json); @@ -499,7 +519,7 @@ mqtt_publish_json(JsonDocument &data, const char* topic) { String fulltopic = mqtt_topic + topic; String doc; serializeJson(data, doc); - mqttclient.publish(fulltopic,doc, true); // claims are always published as retained as they are not updated regularly + mqttclient.publish(fulltopic,doc, true); // json topics are always published as retained as they are not updated regularly Profile_End(mqtt_publish_json, 5); } diff --git a/src/mqtt.h b/src/mqtt.h index 68c9e548..8243a536 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -30,6 +30,7 @@ extern void mqtt_loop(); // data: a comma seperated list of name:value pairs to send // ------------------------------------------------------------------- extern void mqtt_publish(JsonDocument &data); +extern void mqtt_publish_config(); extern void mqtt_publish_claim(); extern void mqtt_set_claim(bool override, EvseProperties &props); extern void mqtt_publish_override(); diff --git a/src/web_server_config.cpp b/src/web_server_config.cpp index 1be04d6c..538c5243 100644 --- a/src/web_server_config.cpp +++ b/src/web_server_config.cpp @@ -13,8 +13,6 @@ typedef const __FlashStringHelper *fstr_t; #include "input.h" #include "event.h" -uint32_t config_version = 0; - extern bool isPositive(MongooseHttpServerRequest *request, const char *param); extern bool web_server_config_deserialise(DynamicJsonDocument &doc, bool factory); @@ -28,40 +26,6 @@ handleConfigGet(MongooseHttpServerRequest *request, MongooseHttpServerResponseSt const size_t capacity = JSON_OBJECT_SIZE(128) + 1024; DynamicJsonDocument doc(capacity); - // Read only information - doc["firmware"] = evse.getFirmwareVersion(); - doc["protocol"] = "-"; - doc["espflash"] = ESPAL.getFlashChipSize(); - doc["espinfo"] = ESPAL.getChipInfo(); - doc["buildenv"] = buildenv; - doc["version"] = currentfirmware; - doc["evse_serial"] = evse.getSerial(); - doc["wifi_serial"] = serial; - - // Static supported protocols - JsonArray mqtt_supported_protocols = doc.createNestedArray("mqtt_supported_protocols"); - mqtt_supported_protocols.add("mqtt"); - mqtt_supported_protocols.add("mqtts"); - - JsonArray http_supported_protocols = doc.createNestedArray("http_supported_protocols"); - http_supported_protocols.add("http"); - - // OpenEVSE module config - doc["diode_check"] = evse.isDiodeCheckEnabled(); - doc["gfci_check"] = evse.isGfiTestEnabled(); - doc["ground_check"] = evse.isGroundCheckEnabled(); - doc["relay_check"] = evse.isStuckRelayCheckEnabled(); - doc["vent_check"] = evse.isVentRequiredEnabled(); - doc["temp_check"] = evse.isTemperatureCheckEnabled(); - doc["service"] = static_cast(evse.getServiceLevel()); - doc["scale"] = evse.getCurrentSensorScale(); - doc["offset"] = evse.getCurrentSensorOffset(); - doc["max_current_soft"] = evse.getMaxConfiguredCurrent(); - - doc["min_current_hard"] = evse.getMinCurrent(); - doc["max_current_hard"] = evse.getMaxHardwareCurrent(); - - // WiFi module config config_serialize(doc, true, false, true); response->setCode(200); @@ -91,7 +55,7 @@ handleConfigPost(MongooseHttpServerRequest *request, MongooseHttpServerResponseS bool config_modified = web_server_config_deserialise(doc, storage.equals("factory")); StaticJsonDocument<128> reply; - reply["config_version"] = config_version; + reply["config_version"] = doc["config_version"]; reply["msg"] = config_modified ? "done" : "no change"; response->setCode(200); @@ -122,126 +86,3 @@ void handleConfig(MongooseHttpServerRequest *request) request->send(response); } -// TODO: move all this to app_config.cpp -bool web_server_config_deserialise(DynamicJsonDocument &doc, bool factory) -{ - bool config_modified = false; - - if(config_deserialize(doc)) - { - config_commit(factory); - config_modified = true; - DBUGLN("Config updated"); - } - - // Update EVSE config - // Update the EVSE setting flags, a little low level, may move later - if(doc.containsKey("diode_check")) - { - bool enable = doc["diode_check"]; - if(enable != evse.isDiodeCheckEnabled()) { - evse.enableDiodeCheck(enable); - config_modified = true; - DBUGLN("diode_check changed"); - } - } - - if(doc.containsKey("gfci_check")) - { - bool enable = doc["gfci_check"]; - if(enable != evse.isGfiTestEnabled()) { - evse.enableGfiTestCheck(enable); - config_modified = true; - DBUGLN("gfci_check changed"); - } - } - - if(doc.containsKey("ground_check")) - { - bool enable = doc["ground_check"]; - if(enable != evse.isGroundCheckEnabled()) { - evse.enableGroundCheck(enable); - config_modified = true; - DBUGLN("ground_check changed"); - } - } - - if(doc.containsKey("relay_check")) - { - bool enable = doc["relay_check"]; - if(enable != evse.isStuckRelayCheckEnabled()) { - evse.enableStuckRelayCheck(enable); - config_modified = true; - DBUGLN("relay_check changed"); - } - } - - if(doc.containsKey("vent_check")) - { - bool enable = doc["vent_check"]; - if(enable != evse.isVentRequiredEnabled()) { - evse.enableVentRequired(enable); - config_modified = true; - DBUGLN("vent_check changed"); - } - } - - if(doc.containsKey("temp_check")) - { - bool enable = doc["temp_check"]; - if(enable != evse.isTemperatureCheckEnabled()) { - evse.enableTemperatureCheck(enable); - config_modified = true; - DBUGLN("temp_check changed"); - } - } - - if(doc.containsKey("service")) - { - EvseMonitor::ServiceLevel service = static_cast(doc["service"].as()); - if(service != evse.getServiceLevel()) { - evse.setServiceLevel(service); - config_modified = true; - DBUGLN("service changed"); - } - } - - if(doc.containsKey("max_current_soft")) - { - long current = doc["max_current_soft"]; - if(current != evse.getMaxConfiguredCurrent()) { - evse.setMaxConfiguredCurrent(current); - config_modified = true; - DBUGLN("max_current_soft changed"); - } - } - - if(doc.containsKey("scale") && doc.containsKey("offset")) - { - long scale = doc["scale"]; - long offset = doc["offset"]; - if(scale != evse.getCurrentSensorScale() || offset != evse.getCurrentSensorOffset()) { - evse.configureCurrentSensorScale(doc["scale"], doc["offset"]); - config_modified = true; - DBUGLN("scale changed"); - } - } - - if(config_modified) - { - // HACK: force a flush of the RAPI command queue to make sure the config - // is updated before we send the response - DBUG("Flushing RAPI command queue ..."); - rapiSender.flush(); - DBUGLN(" Done"); - - config_version++; - DBUGVAR(config_version); - - StaticJsonDocument<128> event; - event["config_version"] = config_version; - event_send(event); - } - - return config_modified; -}