diff --git a/MQTT_IMPLEMENTATION.md b/MQTT_IMPLEMENTATION.md index 4ec105f4..2e855457 100644 --- a/MQTT_IMPLEMENTATION.md +++ b/MQTT_IMPLEMENTATION.md @@ -328,10 +328,11 @@ For community brokers that support the MeshCore JWT auth protocol (same as the b ```bash set mqtt3.preset custom set mqtt3.server wss://my-broker.example.com:443/mqtt -set mqtt3.port 443 set mqtt3.audience my-broker.example.com ``` +When the server is given as a full URL with a scheme (`mqtt://`, `mqtts://`, `ws://`, `wss://`), `set mqttN.port` is optional — an explicit port in the URL is used as-is, and without one the scheme's default port applies. + When `audience` is set, the device will: - Connect with username `v1_{PUBLIC_KEY}` and an Ed25519-signed JWT as the password - Automatically renew tokens before expiry (default 24h lifetime) @@ -342,6 +343,17 @@ To revert a slot back to username/password auth, clear the audience: set mqtt3.audience ``` +#### Example: Local Development Broker (plain WebSocket, no TLS) + +For local development (e.g. a LAN broker without SSL termination), use a full `ws://` URL. Non-TLS transports (`ws://`, `mqtt://`) skip certificate verification entirely: +```bash +set mqtt3.preset custom +set mqtt3.server ws://192.168.1.50:9001/mqtt +set mqtt3.audience my-local-broker +``` + +The `audience` line is optional — set it if your local broker uses the same JWT auth as the production presets, or use `set mqtt3.username` / `set mqtt3.password` instead. + #### Example: Custom Broker with Custom Topic Template ```bash set mqtt3.preset custom diff --git a/src/helpers/bridges/MQTTBridge.cpp b/src/helpers/bridges/MQTTBridge.cpp index 9dcf5561..98876e56 100644 --- a/src/helpers/bridges/MQTTBridge.cpp +++ b/src/helpers/bridges/MQTTBridge.cpp @@ -107,13 +107,20 @@ static bool isWiFiConfigValid(const NodePrefs* prefs) { #ifdef WITH_MQTT_BRIDGE +// A custom slot endpoint is complete if a port is set, or if the host is a +// full URI with a scheme (esp-mqtt applies scheme default ports, and the URI +// builder in setupSlot() preserves any embedded port/path). +static bool customEndpointComplete(const char* host, uint16_t port) { + return host[0] != '\0' && (port != 0 || strstr(host, "://") != nullptr); +} + bool MQTTBridge::isConfigValid(const NodePrefs* prefs) { if (!prefs || !isWiFiConfigValid(prefs)) return false; for (int i = 0; i < RUNTIME_MQTT_SLOTS; i++) { const char* preset_name = prefs->mqtt_slot_preset[i]; if (preset_name[0] == '\0' || strcmp(preset_name, MQTT_PRESET_NONE) == 0) continue; if (strcmp(preset_name, MQTT_PRESET_CUSTOM) == 0) { - if (prefs->mqtt_slot_host[i][0] != '\0' && prefs->mqtt_slot_port[i] != 0) return true; + if (customEndpointComplete(prefs->mqtt_slot_host[i], prefs->mqtt_slot_port[i])) return true; } else if (findMQTTPreset(preset_name) != nullptr) { return true; } @@ -1925,7 +1932,7 @@ void MQTTBridge::applySlotPreset(int slot_index, const char* preset_name) { slot.enabled = true; slot.preset = nullptr; // Custom broker settings should already be set via setSlotCustomBroker - if (_initialized && strlen(slot.host) > 0 && slot.port > 0) { + if (_initialized && customEndpointComplete(slot.host, slot.port)) { setupSlot(slot_index); } return;