Skip to content

Commit 1ebc57a

Browse files
Merge pull request #634 from mathieucarbou/config
Expose config and config updates through MQTT
2 parents beb2a21 + b513675 commit 1ebc57a

File tree

7 files changed

+209
-154
lines changed

7 files changed

+209
-154
lines changed

docs/mqtt.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ Main settings:
5555
`<base-topic>/shaper/set [0 | 1]` : temporary enable (1)/ disable (0) current shaper ( doesn't survive reboot )
5656
`<base-topic>/restart {"device": "gateway|evse"}` : restart the gateway or openevse module
5757

58+
Config:
59+
60+
`<base-topic>/config_version` : a volatile counter incremented for each config change
61+
`<base-topic>/config` : expose the configuration as a json object
5862

5963

6064
MQTT setup is pre-populated with OpenEnergyMonitor [emonPi default MQTT server credentials](https://guide.openenergymonitor.org/technical/credentials/#mqtt).

src/app_config.cpp

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#define FACTORY_OFFSET CONFIG_SIZE
3232
#define FACTORY_SIZE 1024
3333

34+
uint32_t config_ver = INITIAL_CONFIG_VERSION;
35+
3436
// Wifi Network Strings
3537
String esid;
3638
String epass;
@@ -251,6 +253,26 @@ ConfigOpt *opts[] =
251253
ConfigJson user_config(opts, sizeof(opts) / sizeof(opts[0]), EEPROM_SIZE, CONFIG_OFFSET);
252254
ConfigJson factory_config(opts, sizeof(opts) / sizeof(opts[0]), EEPROM_SIZE, FACTORY_OFFSET);
253255

256+
// -------------------------------------------------------------------
257+
// config version handling
258+
// -------------------------------------------------------------------
259+
uint32_t
260+
config_version() {
261+
return config_ver;
262+
}
263+
264+
void
265+
increment_config() {
266+
config_ver++;
267+
DBUGVAR(config_ver);
268+
269+
#if ENABLE_CONFIG_CHANGE_NOTIFICATION
270+
StaticJsonDocument<128> event;
271+
event["config_version"] = config_ver;
272+
event_send(event);
273+
#endif
274+
}
275+
254276
// -------------------------------------------------------------------
255277
// Reset EEPROM, wipes all settings
256278
// -------------------------------------------------------------------
@@ -368,7 +390,117 @@ bool config_deserialize(const char *json)
368390

369391
bool config_deserialize(DynamicJsonDocument &doc)
370392
{
371-
return user_config.deserialize(doc);
393+
bool config_modified = user_config.deserialize(doc);
394+
395+
#if ENABLE_CONFIG_CHANGE_NOTIFICATION
396+
// Update EVSE config
397+
// Update the EVSE setting flags, a little low level, may move later
398+
if(doc.containsKey("diode_check"))
399+
{
400+
bool enable = doc["diode_check"];
401+
if(enable != evse.isDiodeCheckEnabled()) {
402+
evse.enableDiodeCheck(enable);
403+
config_modified = true;
404+
DBUGLN("diode_check changed");
405+
}
406+
}
407+
408+
if(doc.containsKey("gfci_check"))
409+
{
410+
bool enable = doc["gfci_check"];
411+
if(enable != evse.isGfiTestEnabled()) {
412+
evse.enableGfiTestCheck(enable);
413+
config_modified = true;
414+
DBUGLN("gfci_check changed");
415+
}
416+
}
417+
418+
if(doc.containsKey("ground_check"))
419+
{
420+
bool enable = doc["ground_check"];
421+
if(enable != evse.isGroundCheckEnabled()) {
422+
evse.enableGroundCheck(enable);
423+
config_modified = true;
424+
DBUGLN("ground_check changed");
425+
}
426+
}
427+
428+
if(doc.containsKey("relay_check"))
429+
{
430+
bool enable = doc["relay_check"];
431+
if(enable != evse.isStuckRelayCheckEnabled()) {
432+
evse.enableStuckRelayCheck(enable);
433+
config_modified = true;
434+
DBUGLN("relay_check changed");
435+
}
436+
}
437+
438+
if(doc.containsKey("vent_check"))
439+
{
440+
bool enable = doc["vent_check"];
441+
if(enable != evse.isVentRequiredEnabled()) {
442+
evse.enableVentRequired(enable);
443+
config_modified = true;
444+
DBUGLN("vent_check changed");
445+
}
446+
}
447+
448+
if(doc.containsKey("temp_check"))
449+
{
450+
bool enable = doc["temp_check"];
451+
if(enable != evse.isTemperatureCheckEnabled()) {
452+
evse.enableTemperatureCheck(enable);
453+
config_modified = true;
454+
DBUGLN("temp_check changed");
455+
}
456+
}
457+
458+
if(doc.containsKey("service"))
459+
{
460+
EvseMonitor::ServiceLevel service = static_cast<EvseMonitor::ServiceLevel>(doc["service"].as<uint8_t>());
461+
if(service != evse.getServiceLevel()) {
462+
evse.setServiceLevel(service);
463+
config_modified = true;
464+
DBUGLN("service changed");
465+
}
466+
}
467+
468+
if(doc.containsKey("max_current_soft"))
469+
{
470+
long current = doc["max_current_soft"];
471+
if(current != evse.getMaxConfiguredCurrent()) {
472+
evse.setMaxConfiguredCurrent(current);
473+
config_modified = true;
474+
DBUGLN("max_current_soft changed");
475+
}
476+
}
477+
478+
if(doc.containsKey("scale") && doc.containsKey("offset"))
479+
{
480+
long scale = doc["scale"];
481+
long offset = doc["offset"];
482+
if(scale != evse.getCurrentSensorScale() || offset != evse.getCurrentSensorOffset()) {
483+
evse.configureCurrentSensorScale(doc["scale"], doc["offset"]);
484+
config_modified = true;
485+
DBUGLN("scale changed");
486+
}
487+
}
488+
#endif
489+
490+
if(config_modified)
491+
{
492+
#if ENABLE_CONFIG_CHANGE_NOTIFICATION
493+
// HACK: force a flush of the RAPI command queue to make sure the config
494+
// is updated before we send the response
495+
DBUG("Flushing RAPI command queue ...");
496+
rapiSender.flush();
497+
DBUGLN(" Done");
498+
#endif
499+
500+
increment_config();
501+
}
502+
503+
return config_modified;
372504
}
373505

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

379511
bool config_serialize(DynamicJsonDocument &doc, bool longNames, bool compactOutput, bool hideSecrets)
380512
{
513+
// Static supported protocols
514+
JsonArray mqtt_supported_protocols = doc.createNestedArray("mqtt_supported_protocols");
515+
mqtt_supported_protocols.add("mqtt");
516+
mqtt_supported_protocols.add("mqtts");
517+
JsonArray http_supported_protocols = doc.createNestedArray("http_supported_protocols");
518+
http_supported_protocols.add("http");
519+
520+
#if ENABLE_CONFIG_CHANGE_NOTIFICATION
521+
doc["buildenv"] = buildenv;
522+
doc["version"] = currentfirmware;
523+
doc["wifi_serial"] = serial;
524+
doc["protocol"] = "-";
525+
doc["espinfo"] = ESPAL.getChipInfo();
526+
doc["espflash"] = ESPAL.getFlashChipSize();
527+
528+
// EVSE information are only evailable when config_version is incremented
529+
if(config_ver > 0) {
530+
// Read only information
531+
doc["firmware"] = evse.getFirmwareVersion();
532+
doc["evse_serial"] = evse.getSerial();
533+
// OpenEVSE module config
534+
doc["diode_check"] = evse.isDiodeCheckEnabled();
535+
doc["gfci_check"] = evse.isGfiTestEnabled();
536+
doc["ground_check"] = evse.isGroundCheckEnabled();
537+
doc["relay_check"] = evse.isStuckRelayCheckEnabled();
538+
doc["vent_check"] = evse.isVentRequiredEnabled();
539+
doc["temp_check"] = evse.isTemperatureCheckEnabled();
540+
doc["max_current_soft"] = evse.getMaxConfiguredCurrent();
541+
// OpenEVSE Read only information
542+
doc["service"] = static_cast<uint8_t>(evse.getServiceLevel());
543+
doc["scale"] = evse.getCurrentSensorScale();
544+
doc["offset"] = evse.getCurrentSensorOffset();
545+
doc["min_current_hard"] = evse.getMinCurrent();
546+
doc["max_current_hard"] = evse.getMaxHardwareCurrent();
547+
}
548+
#endif
549+
381550
return user_config.serialize(doc, longNames, compactOutput, hideSecrets);
382551
}
383552

src/app_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ extern uint32_t flags;
128128
#define CONFIG_WIZARD (1 << 25)
129129
#define CONFIG_DEFAULT_STATE (1 << 26)
130130

131+
#define INITIAL_CONFIG_VERSION 1
132+
131133
inline bool config_emoncms_enabled() {
132134
return CONFIG_SERVICE_EMONCMS == (flags & CONFIG_SERVICE_EMONCMS);
133135
}
@@ -221,6 +223,8 @@ inline EvseState config_default_state()
221223
// Ohm Connect Settings
222224
extern String ohm;
223225

226+
extern uint32_t config_version();
227+
224228
// -------------------------------------------------------------------
225229
// Load saved settings
226230
// -------------------------------------------------------------------

src/mqtt.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ uint8_t claimsVersion = 0;
3535
uint8_t overrideVersion = 0;
3636
uint8_t scheduleVersion = 0;
3737
uint8_t limitVersion = 0;
38+
uint32_t configVersion = 0;
3839

3940
String lastWill = "";
4041

@@ -300,6 +301,7 @@ mqtt_connect()
300301
event_send(doc);
301302

302303
// Publish MQTT override/claim
304+
mqtt_publish_config();
303305
mqtt_publish_override();
304306
mqtt_publish_claim();
305307
mqtt_publish_schedule();
@@ -507,6 +509,25 @@ mqtt_publish_schedule() {
507509
}
508510
}
509511

512+
bool
513+
mqtt_publish_config() {
514+
if(!config_mqtt_enabled() || !mqttclient.connected() || evse.getEvseState() == OPENEVSE_STATE_STARTING) {
515+
return false;
516+
}
517+
const size_t capacity = JSON_OBJECT_SIZE(128) + 1024;
518+
DynamicJsonDocument doc(capacity);
519+
config_serialize(doc, true, false, true);
520+
mqtt_publish_json(doc, "/config");
521+
522+
if(config_version() == INITIAL_CONFIG_VERSION) {
523+
String fulltopic = mqtt_topic + "/config_version";
524+
String payload = String(config_version());
525+
mqttclient.publish(fulltopic, payload, true);
526+
}
527+
528+
return true;
529+
}
530+
510531
void
511532
mqtt_set_limit(LimitProperties &limitProps) {
512533
Profile_Start(mqtt_set_limit);
@@ -614,6 +635,11 @@ mqtt_loop() {
614635
DBUGF("Limit has changed, publishing to MQTT");
615636
limitVersion = limit.getVersion();
616637
}
638+
639+
if(configVersion != config_version() && mqtt_publish_config()) {
640+
DBUGF("Config has changed, publishing to MQTT");
641+
configVersion = config_version();
642+
}
617643
}
618644
Profile_End(mqtt_loop, 5);
619645
}

src/mqtt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extern void mqtt_loop();
3333
// data: a comma seperated list of name:value pairs to send
3434
// -------------------------------------------------------------------
3535
extern void mqtt_publish(JsonDocument &data);
36+
extern bool mqtt_publish_config();
3637
extern void mqtt_publish_claim();
3738
extern void mqtt_set_claim(bool override, EvseProperties &props);
3839
extern void mqtt_publish_override();

src/web_server.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ void handleUpdateClose(MongooseHttpServerRequest *request);
7878
void handleTime(MongooseHttpServerRequest *request);
7979
void handleTimePost(MongooseHttpServerRequest *request, MongooseHttpServerResponseStream *response);
8080

81-
extern uint32_t config_version;
82-
8381
void dumpRequest(MongooseHttpServerRequest *request)
8482
{
8583
#ifdef ENABLE_DEBUG_WEB_REQUEST
@@ -253,7 +251,7 @@ void buildStatus(DynamicJsonDocument &doc) {
253251

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

256-
doc["config_version"] = config_version;
254+
doc["config_version"] = config_version();
257255
doc["claims_version"] = evse.getClaimsVersion();
258256
doc["override_version"] = manual.getVersion();
259257
doc["schedule_version"] = scheduler.getVersion();

0 commit comments

Comments
 (0)