Skip to content

Commit

Permalink
Expose config and config updates through MQTT
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieucarbou committed May 21, 2023
1 parent beb2a21 commit f4344ab
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 154 deletions.
4 changes: 4 additions & 0 deletions docs/mqtt.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ Main settings:
`<base-topic>/shaper/set [0 | 1]` : temporary enable (1)/ disable (0) current shaper ( doesn't survive reboot )
`<base-topic>/restart {"device": "gateway|evse"}` : restart the gateway or openevse module

Config:

`<base-topic>/config_version` : a volatile counter incremented for each config change
`<base-topic>/config` : expose the configuration as a json object


MQTT setup is pre-populated with OpenEnergyMonitor [emonPi default MQTT server credentials](https://guide.openenergymonitor.org/technical/credentials/#mqtt).
Expand Down
171 changes: 170 additions & 1 deletion src/app_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#define FACTORY_OFFSET CONFIG_SIZE
#define FACTORY_SIZE 1024

uint32_t config_ver = 1;

// Wifi Network Strings
String esid;
String epass;
Expand Down Expand Up @@ -251,6 +253,26 @@ ConfigOpt *opts[] =
ConfigJson user_config(opts, sizeof(opts) / sizeof(opts[0]), EEPROM_SIZE, CONFIG_OFFSET);
ConfigJson factory_config(opts, sizeof(opts) / sizeof(opts[0]), EEPROM_SIZE, FACTORY_OFFSET);

// -------------------------------------------------------------------
// config version handling
// -------------------------------------------------------------------
uint32_t
config_version() {
return config_ver;
}

void
increment_config() {
config_ver++;
DBUGVAR(config_ver);

#if ENABLE_CONFIG_CHANGE_NOTIFICATION
StaticJsonDocument<128> event;
event["config_version"] = config_ver;
event_send(event);
#endif
}

// -------------------------------------------------------------------
// Reset EEPROM, wipes all settings
// -------------------------------------------------------------------
Expand Down Expand Up @@ -368,7 +390,117 @@ bool config_deserialize(const char *json)

bool config_deserialize(DynamicJsonDocument &doc)
{
return user_config.deserialize(doc);
bool config_modified = user_config.deserialize(doc);

#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<EvseMonitor::ServiceLevel>(doc["service"].as<uint8_t>());
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)
{
#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");
#endif

increment_config();
}

return config_modified;
}

bool config_serialize(String& json, bool longNames, bool compactOutput, bool hideSecrets)
Expand All @@ -378,6 +510,43 @@ bool config_serialize(String& json, bool longNames, bool compactOutput, bool hid

bool config_serialize(DynamicJsonDocument &doc, bool longNames, bool compactOutput, bool hideSecrets)
{
// 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");

#if ENABLE_CONFIG_CHANGE_NOTIFICATION
doc["buildenv"] = buildenv;
doc["version"] = currentfirmware;
doc["wifi_serial"] = serial;
doc["protocol"] = "-";
doc["espinfo"] = ESPAL.getChipInfo();
doc["espflash"] = ESPAL.getFlashChipSize();

// EVSE information are only evailable when config_version is incremented
if(config_ver > 0) {
// Read only information
doc["firmware"] = evse.getFirmwareVersion();
doc["evse_serial"] = evse.getSerial();
// 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["max_current_soft"] = evse.getMaxConfiguredCurrent();
// OpenEVSE Read only information
doc["service"] = static_cast<uint8_t>(evse.getServiceLevel());
doc["scale"] = evse.getCurrentSensorScale();
doc["offset"] = evse.getCurrentSensorOffset();
doc["min_current_hard"] = evse.getMinCurrent();
doc["max_current_hard"] = evse.getMaxHardwareCurrent();
}
#endif

return user_config.serialize(doc, longNames, compactOutput, hideSecrets);
}

Expand Down
2 changes: 2 additions & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ inline EvseState config_default_state()
// Ohm Connect Settings
extern String ohm;

extern uint32_t config_version();

// -------------------------------------------------------------------
// Load saved settings
// -------------------------------------------------------------------
Expand Down
24 changes: 24 additions & 0 deletions src/mqtt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ uint8_t claimsVersion = 0;
uint8_t overrideVersion = 0;
uint8_t scheduleVersion = 0;
uint8_t limitVersion = 0;
uint32_t configVersion = 0;

String lastWill = "";

Expand Down Expand Up @@ -300,6 +301,7 @@ mqtt_connect()
event_send(doc);

// Publish MQTT override/claim
mqtt_publish_config();
mqtt_publish_override();
mqtt_publish_claim();
mqtt_publish_schedule();
Expand Down Expand Up @@ -507,6 +509,23 @@ mqtt_publish_schedule() {
}
}

bool
mqtt_publish_config() {
if(!config_mqtt_enabled() || !mqttclient.connected() || evse.getEvseState() == OPENEVSE_STATE_STARTING) {
return false;
}
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 payload = String(config_version());
mqttclient.publish(fulltopic, payload, true);

return true;
}

void
mqtt_set_limit(LimitProperties &limitProps) {
Profile_Start(mqtt_set_limit);
Expand Down Expand Up @@ -614,6 +633,11 @@ mqtt_loop() {
DBUGF("Limit has changed, publishing to MQTT");
limitVersion = limit.getVersion();
}

if(configVersion != config_version() && mqtt_publish_config()) {
DBUGF("Config has changed, publishing to MQTT");
configVersion = config_version();
}
}
Profile_End(mqtt_loop, 5);
}
Expand Down
1 change: 1 addition & 0 deletions src/mqtt.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extern void mqtt_loop();
// data: a comma seperated list of name:value pairs to send
// -------------------------------------------------------------------
extern void mqtt_publish(JsonDocument &data);
extern bool mqtt_publish_config();
extern void mqtt_publish_claim();
extern void mqtt_set_claim(bool override, EvseProperties &props);
extern void mqtt_publish_override();
Expand Down
4 changes: 1 addition & 3 deletions src/web_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ void handleUpdateClose(MongooseHttpServerRequest *request);
void handleTime(MongooseHttpServerRequest *request);
void handleTimePost(MongooseHttpServerRequest *request, MongooseHttpServerResponseStream *response);

extern uint32_t config_version;

void dumpRequest(MongooseHttpServerRequest *request)
{
#ifdef ENABLE_DEBUG_WEB_REQUEST
Expand Down Expand Up @@ -253,7 +251,7 @@ void buildStatus(DynamicJsonDocument &doc) {

doc["ota_update"] = (int)Update.isRunning();

doc["config_version"] = config_version;
doc["config_version"] = config_version();
doc["claims_version"] = evse.getClaimsVersion();
doc["override_version"] = manual.getVersion();
doc["schedule_version"] = scheduler.getVersion();
Expand Down
Loading

0 comments on commit f4344ab

Please sign in to comment.