Preserve MQTT settings across slot changes

This commit is contained in:
mikecarper committed 2026-09-08 18:55:00 -07:00
1 parent aa8caa4ed2
commit cb79dcd4e3
16 files changed
+2209 -1999

No files matched your search

+8
View File
@@ -675,6 +675,14 @@ inline bool parseUnsignedIntegerStrict(const char* text, uint32_t& result) {
return true;
}
// Zero clears a custom port override; a full broker URI supplies its default.
inline bool parseMqttPort(const char* text, uint16_t& result) {
uint32_t value;
if (!parseUnsignedIntegerStrict(text, value) || value > 65535) return false;
result = static_cast<uint16_t>(value);
return true;
}
// Copy and split a bounded CLI argument string on runs of spaces/tabs. This
// provides scanf-like token handling without pulling scanf into firmware. The
// caller controls the maximum accepted token count through field_capacity.
+3 -3
View File
@@ -574,14 +574,14 @@ bool CommonCLI::handleObserverSetCmd(uint32_t sender_timestamp, const char* conf
_callbacks->restartBridgeSlot(slot);
strcpy(reply, "OK");
} else if (memcmp(subcmd, "port ", 5) == 0) {
int port = atoi(&subcmd[5]);
if (port > 0 && port <= 65535) {
uint16_t port;
if (mesh::cli::parseMqttPort(&subcmd[5], port)) {
_mqtt_prefs.mqtt_slot_port[slot] = port;
saveObserverPrefs();
_callbacks->restartBridgeSlot(slot);
strcpy(reply, "OK");
} else {
strcpy(reply, "Error: port must be between 1 and 65535");
strcpy(reply, "Error: port must be 0-65535 (0 uses the URI default)");
}
} else if (memcmp(subcmd, "username ", 9) == 0) {
if (valueTooLong(&subcmd[9], sizeof(_mqtt_prefs.mqtt_slot_username[slot]), reply, "username")) return true;
+28 -21
View File
@@ -612,31 +612,38 @@ bool CompanionMqttSetupPortal::loadStoredConfig(MQTTPrefs& prefs) {
return false;
}
const char* preset = loaded.mqtt_slot_preset[0];
const bool custom = strcmp(preset, MQTT_PRESET_CUSTOM) == 0;
const MQTTPresetDef* preset_def = custom ? nullptr : findMQTTPreset(preset);
if (!preset[0] || (!custom && !preset_def)) return false;
if (custom) {
const char* host = loaded.mqtt_slot_host[0];
if (!host[0]
|| (loaded.mqtt_slot_port[0] == 0 && strstr(host, "://") == nullptr)
|| (!loaded.mqtt_slot_topic[0][0] && !isThreeLetterCode(loaded.mqtt_iata))) {
return false;
}
} else {
if (preset_def->topic_style == MQTT_TOPIC_MESHCORE
&& !isThreeLetterCode(loaded.mqtt_iata)) return false;
if (preset_def->topic_style == MQTT_TOPIC_MESHRANK
&& !loaded.mqtt_slot_token[0][0]) return false;
if (mqttPresetNeedsSlotCredentials(preset_def)
&& (!loaded.mqtt_slot_username[0][0] || !loaded.mqtt_slot_password[0][0])) {
return false;
}
}
// Incomplete and disabled slots are valid saved settings. Readiness is a
// separate decision, so disabling slot 1 or saving a broker in stages cannot
// discard all slots and credentials on the next boot.
prefs = loaded;
return true;
}
bool CompanionMqttSetupPortal::hasConfiguredSlot(const MQTTPrefs& prefs) {
for (int i = 0; i < RUNTIME_MQTT_SLOTS; ++i) {
const char* preset = prefs.mqtt_slot_preset[i];
if (!preset[0] || strcmp(preset, MQTT_PRESET_NONE) == 0) continue;
if (strcmp(preset, MQTT_PRESET_CUSTOM) == 0) {
const char* host = prefs.mqtt_slot_host[i];
if (host[0] && (prefs.mqtt_slot_port[i] != 0 || strstr(host, "://"))
&& (prefs.mqtt_slot_topic[i][0] || isThreeLetterCode(prefs.mqtt_iata))) {
return true;
}
continue;
}
const MQTTPresetDef* def = findMQTTPreset(preset);
if (!def) continue;
if (def->topic_style == MQTT_TOPIC_MESHCORE
&& !isThreeLetterCode(prefs.mqtt_iata)) continue;
if (def->topic_style == MQTT_TOPIC_MESHRANK
&& !prefs.mqtt_slot_token[i][0]) continue;
if (mqttPresetNeedsSlotUsername(def) && !prefs.mqtt_slot_username[i][0]) continue;
if (mqttPresetNeedsSlotPassword(def) && !prefs.mqtt_slot_password[i][0]) continue;
return true;
}
return false;
}
bool CompanionMqttSetupPortal::loadEnabled() {
Preferences nvs;
if (!nvs.begin(NVS_NAMESPACE, false)) return true;
+1
View File
@@ -20,6 +20,7 @@ public:
bool isActive() const { return _active; }
static bool loadStoredConfig(MQTTPrefs& prefs);
static bool hasConfiguredSlot(const MQTTPrefs& prefs);
static bool saveStoredConfig(const MQTTPrefs& prefs);
static bool loadEnabled();
static bool saveEnabled(bool enabled);
File diff suppressed because it is too large. Load diff
+3 -1
View File
@@ -1671,7 +1671,9 @@ void WebConfigServer::handleStatus(AsyncWebServerRequest* req) {
#ifdef WITH_MQTT_BRIDGE
doc["runtime_slots"] = has_mqtt ? RUNTIME_MQTT_SLOTS : 0;
doc["max_slots"] = has_mqtt ? MAX_MQTT_SLOTS : 0;
doc["active_slots"] = has_mqtt ? MQTTBridge::getMaxActiveSlots() : 0;
const int active_slots = MQTTBridge::getMaxActiveSlots();
doc["active_slots"] = has_mqtt
? (active_slots < RUNTIME_MQTT_SLOTS ? active_slots : RUNTIME_MQTT_SLOTS) : 0;
#else
doc["runtime_slots"] = 0;
doc["max_slots"] = 0;