refactor(mqtt): migrate observer settings to MQTTPrefs structure

Refactored the handling of observer-related settings by moving them from
NodePrefs to a new MQTTPrefs structure. This change centralizes MQTT,
WiFi, timezone, SNMP, and alert configurations, improving code organization
and maintainability. The new structure allows for better separation of
concerns and prepares the codebase for future enhancements.
This commit is contained in:
agessaman committed 2026-06-26 08:59:48 -07:00
1 parent 9535f243af
commit 2eb41baeda
14 files changed
+372 -561

No files matched your search

+14 -31
View File
@@ -951,21 +951,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
#ifdef WITH_MQTT_BRIDGE
_prefs.agc_reset_interval = 7; // 28 seconds (secs/4) — prevents AGC drift on long-running observers
#endif
_prefs.radio_watchdog_minutes = 5; // 5 minutes default
// Alert channel defaults — disabled by default, and the channel is left
// unconfigured so a freshly-flashed observer never broadcasts on the
// well-known Public hashtag. Operators must explicitly pick a private
// key (`set alert.psk`) or a hashtag (`set alert.hashtag`) before alerts
// can fire. The sender prefix on outgoing alert messages is always the
// node name (`set name ...`), so there's no separate `alert.name`.
_prefs.alert_enabled = 0;
_prefs.alert_psk_hex[0] = '\0';
_prefs.alert_hashtag[0] = '\0';
_prefs.alert_region[0] = '\0'; // empty = use default_scope
_prefs.alert_wifi_minutes = 30; // 30 minutes
_prefs.alert_mqtt_minutes = 240; // 4 hours
_prefs.alert_min_interval_min = 60; // re-arm window: 1 hour
// Observer defaults (radio_watchdog, alert.*, snmp.*) moved to applyMQTTDefaults()
// in MQTTDefaults.h — they live in /mqtt_prefs now, not NodePrefs.
// bridge defaults
_prefs.bridge_enabled = 1; // enabled
@@ -976,21 +963,12 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
StrHelper::strncpy(_prefs.bridge_secret, "LVSITANOS", sizeof(_prefs.bridge_secret));
// SNMP defaults
_prefs.snmp_enabled = 0;
StrHelper::strncpy(_prefs.snmp_community, "public", sizeof(_prefs.snmp_community));
// GPS defaults
_prefs.gps_enabled = 0;
_prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
// MQTT slot/IATA/timezone defaults come from /mqtt_prefs via loadPrefs (see MQTTDefaults.h)
_prefs.mqtt_origin[0] = '\0';
// WiFi defaults (user-configured via CLI; placeholders until set)
StrHelper::strncpy(_prefs.wifi_ssid, "ssid_here", sizeof(_prefs.wifi_ssid));
StrHelper::strncpy(_prefs.wifi_password, "password_here", sizeof(_prefs.wifi_password));
// MQTT/WiFi/timezone/radio_watchdog defaults live in /mqtt_prefs now (see applyMQTTDefaults).
_prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier
@@ -1042,7 +1020,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
if (_prefs.bridge_enabled) {
#ifdef WITH_MQTT_BRIDGE
// Defer construction to avoid static init crashes on ESP32 classic
bridge = new MQTTBridge(&_prefs, _mgr, getRTCClock(), &self_id);
bridge = new MQTTBridge(&_prefs, _cli.getObserverPrefs(), _mgr, getRTCClock(), &self_id);
#endif
if (bridge) {
// Set device public key for MQTT topics
@@ -1065,7 +1043,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
// Set stats sources for automatic stats collection
bridge->setStatsSources(this, _radio, _cli.getBoard(), _ms);
#ifdef WITH_SNMP
if (_prefs.snmp_enabled) {
if (_cli.getObserverPrefs()->snmp_enabled) {
_snmp_agent.setNodeName(_prefs.node_name);
_snmp_agent.setFirmwareVersion(getFirmwareVer());
bridge->setSNMPAgent(&_snmp_agent);
@@ -1082,8 +1060,8 @@ void MyMesh::begin(FILESYSTEM *fs) {
// Passing `this` as the callbacks lets the reporter resolve a TransportKey
// scope (alert.region override, falling back to default_scope) so alert
// floods ride the same scope as adverts/channel messages.
_alerter.begin(&_prefs, this, this);
#if defined(WITH_MQTT_BRIDGE)
#ifdef WITH_MQTT_BRIDGE
_alerter.begin(&_prefs, _cli.getObserverPrefs(), this, this);
_alerter.setBridge(bridge);
#endif
@@ -1118,12 +1096,15 @@ bool MyMesh::resolveAlertScope(TransportKey& dest) {
// RegionMap so the operator can name a region that doesn't exist yet
// without polluting region_map state — we just silently fall through
// to default_scope on miss.
if (_prefs.alert_region[0]) {
auto r = region_map.findByNamePrefix(_prefs.alert_region);
#ifdef WITH_MQTT_BRIDGE
const char* alert_region = _cli.getObserverPrefs()->alert_region;
if (alert_region[0]) {
auto r = region_map.findByNamePrefix(alert_region);
if (r && region_map.getTransportKeysFor(*r, &dest, 1) > 0 && !dest.isNull()) {
return true;
}
}
#endif
if (!default_scope.isNull()) {
dest = default_scope;
return true;
@@ -1473,7 +1454,9 @@ void MyMesh::loop() {
uptime_millis += now - last_millis;
last_millis = now;
#ifdef WITH_MQTT_BRIDGE
_alerter.onLoop(now);
#endif
#ifdef WITH_SNMP
// Push radio stats to SNMP agent every 2 seconds
+13 -3
View File
@@ -132,7 +132,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
#ifdef WITH_SNMP
MeshSNMPAgent _snmp_agent;
#endif
#ifdef WITH_MQTT_BRIDGE
AlertReporter _alerter;
#endif
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
@@ -168,9 +170,11 @@ protected:
int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
}
#ifdef WITH_MQTT_BRIDGE
uint32_t getRadioWatchdogMillis() const override {
return ((uint32_t)_prefs.radio_watchdog_minutes) * 60000UL;
return ((uint32_t)_cli.getObserverPrefs()->radio_watchdog_minutes) * 60000UL;
}
#endif
uint8_t getExtraAckTransmitCount() const override {
return _prefs.multi_acks;
}
@@ -215,8 +219,10 @@ public:
// CommonCLICallbacks
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
#ifdef WITH_MQTT_BRIDGE
void onAlertConfigChanged() override { _alerter.onConfigChanged(); }
bool sendAlertText(const char* text) override { return _alerter.sendText(text); }
#endif
bool resolveAlertScope(TransportKey& dest) override;
bool formatFileSystem() override;
void sendSelfAdvertisement(int delay_millis, bool flood) override;
@@ -253,7 +259,7 @@ public:
void setBridgeState(bool enable) override {
if (!bridge) {
#ifdef WITH_MQTT_BRIDGE
bridge = new MQTTBridge(&_prefs, _mgr, getRTCClock(), &self_id);
bridge = new MQTTBridge(&_prefs, _cli.getObserverPrefs(), _mgr, getRTCClock(), &self_id);
#endif
if (!bridge) return;
}
@@ -303,8 +309,12 @@ public:
}
void restartBridgeSlot(int slot) override {
#ifdef WITH_MQTT_BRIDGE
if (!bridge || !bridge->isRunning()) return;
bridge->setSlotPreset(slot, _prefs.mqtt_slot_preset[slot]);
bridge->setSlotPreset(slot, _cli.getObserverPrefs()->mqtt_slot_preset[slot]);
#else
(void)slot;
#endif
}
// Schedule the pull-OTA flash to run from loop() in ~2.5 s, leaving time for the
+13 -20
View File
@@ -681,15 +681,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.gps_interval = 0;
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
// Alert channel defaults (same as repeater; off by default and unconfigured).
// Operator must pick `set alert.psk` or `set alert.hashtag` before alerts fire.
_prefs.alert_enabled = 0;
_prefs.alert_psk_hex[0] = '\0';
_prefs.alert_hashtag[0] = '\0';
_prefs.alert_region[0] = '\0';
_prefs.alert_wifi_minutes = 30;
_prefs.alert_mqtt_minutes = 240;
_prefs.alert_min_interval_min = 60;
// Observer defaults (alert.*, etc.) moved to applyMQTTDefaults() — they live
// in /mqtt_prefs now, not NodePrefs.
// bridge defaults (same as repeater)
_prefs.bridge_enabled = 1; // enabled
@@ -698,12 +691,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.bridge_baud = 115200; // baud rate
_prefs.bridge_channel = 1; // channel 1
// MQTT slot/IATA/timezone defaults come from /mqtt_prefs via loadPrefs (see MQTTDefaults.h)
_prefs.mqtt_origin[0] = '\0';
// WiFi defaults (user-configured via CLI; placeholders until set)
StrHelper::strncpy(_prefs.wifi_ssid, "ssid_here", sizeof(_prefs.wifi_ssid));
StrHelper::strncpy(_prefs.wifi_password, "password_here", sizeof(_prefs.wifi_password));
// MQTT/WiFi/timezone defaults live in /mqtt_prefs now (see applyMQTTDefaults).
next_post_idx = 0;
next_client_idx = 0;
@@ -757,7 +745,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
#ifdef WITH_MQTT_BRIDGE
if (_prefs.bridge_enabled) {
// Defer construction to avoid static init crashes on ESP32 classic
bridge = new MQTTBridge(&_prefs, _mgr, getRTCClock(), &self_id);
bridge = new MQTTBridge(&_prefs, _cli.getObserverPrefs(), _mgr, getRTCClock(), &self_id);
if (bridge) {
// Set device public key for MQTT topics
char device_id[65];
@@ -787,8 +775,8 @@ void MyMesh::begin(FILESYSTEM *fs) {
// Passing `this` as the callbacks lets the reporter resolve a TransportKey
// scope (alert.region override, falling back to default_scope) so alert
// floods ride the same scope as adverts/channel messages.
_alerter.begin(&_prefs, this, this);
#if defined(WITH_MQTT_BRIDGE)
#ifdef WITH_MQTT_BRIDGE
_alerter.begin(&_prefs, _cli.getObserverPrefs(), this, this);
_alerter.setBridge(bridge);
#endif
}
@@ -806,12 +794,15 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3
bool MyMesh::resolveAlertScope(TransportKey& dest) {
// Same resolution policy as simple_repeater: alert.region > default_scope.
if (_prefs.alert_region[0]) {
auto r = region_map.findByNamePrefix(_prefs.alert_region);
#ifdef WITH_MQTT_BRIDGE
const char* alert_region = _cli.getObserverPrefs()->alert_region;
if (alert_region[0]) {
auto r = region_map.findByNamePrefix(alert_region);
if (r && region_map.getTransportKeysFor(*r, &dest, 1) > 0 && !dest.isNull()) {
return true;
}
}
#endif
if (!default_scope.isNull()) {
dest = default_scope;
return true;
@@ -1131,5 +1122,7 @@ void MyMesh::loop() {
uptime_millis += now - last_millis;
last_millis = now;
#ifdef WITH_MQTT_BRIDGE
_alerter.onLoop(now);
#endif
}
+10 -2
View File
@@ -126,7 +126,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
#ifdef WITH_MQTT_BRIDGE
MQTTBridge* bridge;
#endif
#ifdef WITH_MQTT_BRIDGE
AlertReporter _alerter;
#endif
void addPost(ClientInfo* client, const char* postData);
void pushPostToClient(ClientInfo* client, PostInfo& post);
@@ -201,8 +203,10 @@ public:
// CommonCLICallbacks
void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override;
#ifdef WITH_MQTT_BRIDGE
void onAlertConfigChanged() override { _alerter.onConfigChanged(); }
bool sendAlertText(const char* text) override { return _alerter.sendText(text); }
#endif
bool resolveAlertScope(TransportKey& dest) override;
bool formatFileSystem() override;
void sendSelfAdvertisement(int delay_millis, bool flood) override;
@@ -241,7 +245,7 @@ public:
void setBridgeState(bool enable) override {
if (!bridge) {
#ifdef WITH_MQTT_BRIDGE
bridge = new MQTTBridge(&_prefs, _mgr, getRTCClock(), &self_id);
bridge = new MQTTBridge(&_prefs, _cli.getObserverPrefs(), _mgr, getRTCClock(), &self_id);
#endif
if (!bridge) return;
}
@@ -289,8 +293,12 @@ public:
}
void restartBridgeSlot(int slot) override {
#ifdef WITH_MQTT_BRIDGE
if (!bridge || !bridge->isRunning()) return;
bridge->setSlotPreset(slot, _prefs.mqtt_slot_preset[slot]);
bridge->setSlotPreset(slot, _cli.getObserverPrefs()->mqtt_slot_preset[slot]);
#else
(void)slot;
#endif
}
int getQueueSize() override {