From d6f8a871830e7eaaf48a5c4e13e4a57a31d8e1b1 Mon Sep 17 00:00:00 2001 From: agessaman Date: Sun, 19 Jul 2026 22:38:19 -0700 Subject: [PATCH] feat(webconfig): expose mqtt.neighbors controls in the web portal - Allow mqtt.neighbors and mqtt.neighbors.interval in the WebConfigKeys set-key allowlist (the CLI enforces the PSRAM guard; the stub reply handles non-PSRAM). - Emit neighbors + neighbors_interval (hours) in the WebConfigServer config JSON. - Add a "Publish neighbors" toggle and a "Neighbors interval (hours)" field (12-336) to the Publishing card, with getVal() cases in webui/index.html. - Cover both keys in test_webconfig_keys. WebConfigHtml.h is a gitignored build artifact regenerated by the pre-build hook from index.html, so it is not committed. Verified: test_webconfig_keys passes and the T_Beam_S3_Supreme observer_mqtt firmware builds [SUCCESS]. --- src/helpers/WebConfigKeys.h | 3 ++- src/helpers/esp32/WebConfigServer.cpp | 2 ++ test/test_webconfig_keys/test_webconfig_keys.cpp | 2 ++ webui/index.html | 10 +++++++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/helpers/WebConfigKeys.h b/src/helpers/WebConfigKeys.h index 0b1f088c..7579cc12 100644 --- a/src/helpers/WebConfigKeys.h +++ b/src/helpers/WebConfigKeys.h @@ -23,7 +23,8 @@ static const char* const WC_ALLOWED_SET_KEYS[] = { // MQTTPrefs (WiFi / MQTT / misc observer) "wifi.ssid", "wifi.pwd", "wifi.powersave", "mqtt.origin", "mqtt.iata", "mqtt.status", "mqtt.packets", "mqtt.raw", - "mqtt.tx", "mqtt.rx", "mqtt.interval", "mqtt.ntp", "mqtt.owner", "mqtt.email", + "mqtt.tx", "mqtt.rx", "mqtt.interval", "mqtt.neighbors", "mqtt.neighbors.interval", + "mqtt.ntp", "mqtt.owner", "mqtt.email", "timezone", "timezone.offset", "snmp", "snmp.community", }; static const char* const WC_ALLOWED_SLOT_KEYS[] = { diff --git a/src/helpers/esp32/WebConfigServer.cpp b/src/helpers/esp32/WebConfigServer.cpp index 5603a55d..8b4edb6a 100644 --- a/src/helpers/esp32/WebConfigServer.cpp +++ b/src/helpers/esp32/WebConfigServer.cpp @@ -591,6 +591,8 @@ void WebConfigServer::handleConfigGet(AsyncWebServerRequest* req) { : _obs->mqtt_tx_enabled == 1 ? "on" : "off"; mqtt["rx"] = (bool)_obs->mqtt_rx_enabled; mqtt["interval"] = _obs->mqtt_status_interval / 60000; // CLI takes minutes + mqtt["neighbors"] = (bool)_obs->mqtt_neighbors_enabled; + mqtt["neighbors_interval"] = _obs->mqtt_neighbors_interval / 3600000UL; // CLI takes hours mqtt["timezone"] = (const char*)_obs->timezone_string; mqtt["timezone_offset"] = _obs->timezone_offset; mqtt["ntp"] = (const char*)_obs->mqtt_ntp_server; diff --git a/test/test_webconfig_keys/test_webconfig_keys.cpp b/test/test_webconfig_keys/test_webconfig_keys.cpp index 098463fc..b2ef4fe4 100644 --- a/test/test_webconfig_keys/test_webconfig_keys.cpp +++ b/test/test_webconfig_keys/test_webconfig_keys.cpp @@ -12,6 +12,8 @@ TEST(WebConfigKeys, AllowsKnownScalarKeys) { EXPECT_TRUE(wcIsAllowedSetKey("repeat")); EXPECT_TRUE(wcIsAllowedSetKey("wifi.ssid")); EXPECT_TRUE(wcIsAllowedSetKey("mqtt.iata")); + EXPECT_TRUE(wcIsAllowedSetKey("mqtt.neighbors")); + EXPECT_TRUE(wcIsAllowedSetKey("mqtt.neighbors.interval")); EXPECT_TRUE(wcIsAllowedSetKey("snmp.community")); EXPECT_TRUE(wcIsAllowedSetKey("timezone.offset")); } diff --git a/webui/index.html b/webui/index.html index cb6e5e82..b7b65474 100644 --- a/webui/index.html +++ b/webui/index.html @@ -313,12 +313,18 @@ canvas{width:100%;height:56px;display:block}
Received packetsReport packets heard over the air
+
Publish neighborsPeriodic neighbor table + scopes (PSRAM boards only) +
Report packets this node sends.
+
+
+
How often to publish the neighbor table (12-336, default 24).
+

Servers

@@ -520,7 +526,9 @@ function cfgVal(k){ // map a `set` key to its current value string, from st.cfg case"mqtt.origin":return q.origin;case"mqtt.iata":return q.iata; case"mqtt.status":return q.status?"on":"off";case"mqtt.packets":return q.packets?"on":"off"; case"mqtt.raw":return q.raw?"on":"off";case"mqtt.tx":return q.tx;case"mqtt.rx":return q.rx?"on":"off"; - case"mqtt.interval":return String(q.interval);case"mqtt.ntp":return q.ntp; + case"mqtt.interval":return String(q.interval); + case"mqtt.neighbors":return q.neighbors?"on":"off";case"mqtt.neighbors.interval":return String(q.neighbors_interval); + case"mqtt.ntp":return q.ntp; case"mqtt.owner":return q.owner;case"mqtt.email":return q.email; case"timezone":return q.timezone;case"timezone.offset":return String(q.timezone_offset); case"snmp":return q.snmp?"on":"off";case"snmp.community":return q.snmp_community;