From 765594f7ee03a980f4eba22b08372c070558e86a Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:05:24 -0500 Subject: [PATCH] fix: MQTT proxy connection and probe test failures (#5215) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../core/data/manager/MqttManagerImpl.kt | 3 +++ .../core/network/repository/MQTTRepositoryImpl.kt | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt index 5693d343b..ac790a191 100644 --- a/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt +++ b/core/data/src/commonMain/kotlin/org/meshtastic/core/data/manager/MqttManagerImpl.kt @@ -44,6 +44,7 @@ import org.meshtastic.mqtt.ProbeResult import org.meshtastic.mqtt.probe import org.meshtastic.proto.MqttClientProxyMessage import org.meshtastic.proto.ToRadio +import kotlin.time.Clock @Single class MqttManagerImpl( @@ -125,6 +126,8 @@ class MqttManagerImpl( val endpoint = resolveEndpoint(address, tlsEnabled) val result = MqttClient.probe(endpoint = endpoint) { + // Provide a valid client ID for the probe; brokers reject empty identifiers + clientId = "MeshtasticProbe-${Clock.System.now().toEpochMilliseconds()}" val user = username?.takeUnless { it.isEmpty() } val pass = password?.takeUnless { it.isEmpty() } if (user != null) this.username = user diff --git a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt index 47cfb6f7a..537e2af37 100644 --- a/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt +++ b/core/network/src/commonMain/kotlin/org/meshtastic/core/network/repository/MQTTRepositoryImpl.kt @@ -219,15 +219,17 @@ class MQTTRepositoryImpl( } } +private const val MQTT_PORT_PLAIN = 1883 +private const val MQTT_PORT_TLS = 8883 + /** * Resolve a user-supplied broker address into an [MqttEndpoint]. * * Address resolution rules: * - If [rawAddress] already contains a URI scheme (`scheme://…`), parse it directly via [MqttEndpoint.parse] and * respect whatever transport / port the user encoded. - * - Otherwise wrap it as a WebSocket endpoint (`ws[s]://host${WEBSOCKET_PATH}`) so the proxy works over CDNs and - * firewall-restricted networks where raw 1883/8883 may be blocked. The scheme is `wss` when [tlsEnabled] is `true`, - * `ws` otherwise. + * - Otherwise wrap it as a TCP endpoint using standard MQTT ports: port 8883 if [tlsEnabled] is `true`, port 1883 + * otherwise. This allows standard MQTT brokers to work out of the box. * * Extracted as a top-level function so [MQTTRepositoryImplTest] can exercise every branch without spinning up the full * repository, and so `MqttManagerImpl` (in `:core:data`) can reuse the same parsing rules for the probe API. Visibility @@ -236,8 +238,7 @@ class MQTTRepositoryImpl( fun resolveEndpoint(rawAddress: String, tlsEnabled: Boolean): MqttEndpoint = if (rawAddress.contains("://")) { MqttEndpoint.parse(rawAddress) } else { - val scheme = if (tlsEnabled) "wss" else "ws" - MqttEndpoint.parse("$scheme://$rawAddress$WEBSOCKET_PATH") + val port = if (tlsEnabled) MQTT_PORT_TLS else MQTT_PORT_PLAIN + val scheme = if (tlsEnabled) "ssl" else "tcp" + MqttEndpoint.parse("$scheme://$rawAddress:$port") } - -private const val WEBSOCKET_PATH = "/mqtt"