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].
This commit is contained in:
agessaman
2026-07-19 22:39:42 -07:00
parent 6911981573
commit d6f8a87183
4 changed files with 15 additions and 2 deletions
+2 -1
View File
@@ -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[] = {
+2
View File
@@ -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;
@@ -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"));
}
+9 -1
View File
@@ -313,12 +313,18 @@ canvas{width:100%;height:56px;display:block}
<span class="tgl"><input type="checkbox" data-k="mqtt.raw"><u></u></span></div>
<div class="sw"><span><b>Received packets</b><i>Report packets heard over the air</i></span>
<span class="tgl"><input type="checkbox" data-k="mqtt.rx"><u></u></span></div>
<div class="sw"><span><b>Publish neighbors</b><i>Periodic neighbor table + scopes (PSRAM boards only)</i></span>
<span class="tgl"><input type="checkbox" data-k="mqtt.neighbors"><u></u></span></div>
<div class="row" style="margin-top:6px">
<div class="f"><label>Transmitted packets</label>
<select data-k="mqtt.tx"><option value="off">Off</option><option value="on">All TX</option><option value="advert">Own adverts only</option></select>
<div class="hint">Report packets this node sends.</div></div>
<div class="f"><label>Status interval (min)</label><input type="number" data-k="mqtt.interval" min="1" max="60"></div>
</div>
<div class="row" style="margin-top:6px">
<div class="f"><label>Neighbors interval (hours)</label><input type="number" data-k="mqtt.neighbors.interval" min="12" max="336">
<div class="hint">How often to publish the neighbor table (12-336, default 24).</div></div>
</div>
</div>
<div class="card">
<h2>Servers</h2>
@@ -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;