From f197249c07d222dfb5f5ba8a48efb20372126a49 Mon Sep 17 00:00:00 2001 From: Eric Bariaux <375613+ebariaux@users.noreply.github.com> Date: Tue, 26 May 2026 13:33:48 +0200 Subject: [PATCH 1/3] Initialize ESPProvisioningProvider with the proper realm (from URL or preferences) --- .../io/openremote/orlib/ui/OrMainActivity.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt b/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt index 5c51680..48e56b0 100644 --- a/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt +++ b/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt @@ -967,7 +967,8 @@ open class OrMainActivity : Activity() { val action = data.getString("action") if (espProvisionProvider == null) { if (baseUrl != null) { - espProvisionProvider = ESPProvisionProvider(activity, URL(URL(baseUrl), "/api/master")) + val realm = getESPProvisionRealm() + espProvisionProvider = ESPProvisionProvider(activity, getESPProvisionApiURL(baseUrl!!, realm)) } else { espProvisionProvider = ESPProvisionProvider(activity) } @@ -1061,6 +1062,27 @@ open class OrMainActivity : Activity() { } } } + + private fun getESPProvisionRealm(): String { + return baseUrl + ?.let { Uri.parse(it).getQueryParameter(ORConstants.REALM_KEY) } + ?.takeIf { it.isNotBlank() } + ?: sharedPreferences.getString(ORConstants.REALM_KEY, null) + ?.takeIf { it.isNotBlank() } + ?: "master" + } + + private fun getESPProvisionApiURL(baseUrl: String, realm: String): URL { + val appUri = Uri.parse(baseUrl) + val apiUri = Uri.Builder() + .scheme(appUri.scheme) + .encodedAuthority(appUri.encodedAuthority) + .appendPath("api") + .appendPath(realm) + .build() + + return URL(apiUri.toString()) + } } private fun notifyClient(data: Map?) { From e7231452fa5380cfa21dca253a4e68c3aed31ec7 Mon Sep 17 00:00:00 2001 From: Eric Bariaux <375613+ebariaux@users.noreply.github.com> Date: Wed, 27 May 2026 07:09:29 +0200 Subject: [PATCH 2/3] Pass URL only for privision step, not on init of ESPProvisioningProvider --- .../orlib/service/ESPProvisionProvider.kt | 8 ++++---- .../service/espprovision/DeviceProvision.kt | 16 +++++----------- .../service/espprovision/DeviceProvisionAPI.kt | 6 +++--- .../io/openremote/orlib/ui/OrMainActivity.kt | 13 +++++++------ 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ORLib/src/main/java/io/openremote/orlib/service/ESPProvisionProvider.kt b/ORLib/src/main/java/io/openremote/orlib/service/ESPProvisionProvider.kt index 589aa21..8165309 100644 --- a/ORLib/src/main/java/io/openremote/orlib/service/ESPProvisionProvider.kt +++ b/ORLib/src/main/java/io/openremote/orlib/service/ESPProvisionProvider.kt @@ -38,7 +38,7 @@ object ESPProvisionProviderActions { const val EXIT_PROVISIONING = "EXIT_PROVISIONING" } -class ESPProvisionProvider(val context: Context, val apiURL: URL = URL("http://localhost:8080/api/master")) { +class ESPProvisionProvider(val context: Context) { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) val deviceRegistry: DeviceRegistry var deviceConnection: DeviceConnection? = null @@ -269,10 +269,10 @@ class ESPProvisionProvider(val context: Context, val apiURL: URL = URL("http://l // OR Configuration - fun provisionDevice(userToken: String) { - val deviceProvision = DeviceProvision(deviceConnection, deviceRegistry.callbackChannel, apiURL) + fun provisionDevice(apiURL: URL = URL("http://localhost:8080/api/master"), userToken: String) { + val deviceProvision = DeviceProvision(deviceConnection, deviceRegistry.callbackChannel) CoroutineScope(Dispatchers.IO).launch { - deviceProvision.provision(userToken) + deviceProvision.provision(apiURL, userToken) } } diff --git a/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvision.kt b/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvision.kt index 53cea38..28c1593 100644 --- a/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvision.kt +++ b/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvision.kt @@ -14,16 +14,16 @@ import java.util.Locale import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -class DeviceProvision(var deviceConnection: DeviceConnection?, var callbackChannel: CallbackChannel?, var apiURL: URL) { +class DeviceProvision(var deviceConnection: DeviceConnection?, var callbackChannel: CallbackChannel?) { var deviceProvisionAPI: DeviceProvisionAPI var backendConnectionTimeoutMillis = 60_000 init { - deviceProvisionAPI = DeviceProvisionAPIREST(apiURL) + deviceProvisionAPI = DeviceProvisionAPIREST() } - suspend fun provision(userToken: String) { + suspend fun provision(apiURL: URL, userToken: String) { if (deviceConnection == null || !deviceConnection!!.isConnected) { sendProvisionDeviceStatus(false, ESPProviderErrorCode.NOT_CONNECTED, "No connection established to device") } @@ -34,11 +34,11 @@ class DeviceProvision(var deviceConnection: DeviceConnection?, var callbackChann val password = generatePassword() - val assetId = deviceProvisionAPI.provision(deviceInfo.modelName, deviceInfo.deviceId, password, userToken) + val assetId = deviceProvisionAPI.provision(apiURL, deviceInfo.modelName, deviceInfo.deviceId, password, userToken) val userName = deviceInfo.deviceId.lowercase(Locale("en")) deviceConnection?.sendOpenRemoteConfig( - mqttBrokerUrl = mqttURL, + mqttBrokerUrl = "mqtts://${apiURL.host ?: "localhost"}:8883", mqttUser = userName, mqttPassword = password, assetId = assetId @@ -115,10 +115,4 @@ class DeviceProvision(var deviceConnection: DeviceConnection?, var callbackChann .build() .generate() } - - private val mqttURL: String - get() { - // TODO: is this OK or do we want to get the mqtt url from the server? - return "mqtts://${apiURL.host ?: "localhost"}:8883" - } } \ No newline at end of file diff --git a/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvisionAPI.kt b/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvisionAPI.kt index 6815339..77fca54 100644 --- a/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvisionAPI.kt +++ b/ORLib/src/main/java/io/openremote/orlib/service/espprovision/DeviceProvisionAPI.kt @@ -13,16 +13,16 @@ import java.net.HttpURLConnection import java.net.URL interface DeviceProvisionAPI { - suspend fun provision(modelName: String, deviceId: String, password: String, token: String): String + suspend fun provision(apiURL: URL, modelName: String, deviceId: String, password: String, token: String): String } -class DeviceProvisionAPIREST(private val apiURL: URL) : DeviceProvisionAPI { +class DeviceProvisionAPIREST() : DeviceProvisionAPI { companion object { private const val TAG = "DeviceProvisionAPIREST" } - override suspend fun provision(modelName: String, deviceId: String, password: String, token: String): String = withContext(Dispatchers.IO) { + override suspend fun provision(apiURL: URL, modelName: String, deviceId: String, password: String, token: String): String = withContext(Dispatchers.IO) { Log.d(ESPProvisionProvider.TAG, "apiURL $apiURL") val uri = Uri.parse(apiURL.toString()).buildUpon() .appendPath("rest") diff --git a/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt b/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt index 5c51680..e85c76b 100644 --- a/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt +++ b/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt @@ -966,11 +966,7 @@ open class OrMainActivity : Activity() { private fun handleESPProvisionProviderMessage(data: JSONObject) { val action = data.getString("action") if (espProvisionProvider == null) { - if (baseUrl != null) { - espProvisionProvider = ESPProvisionProvider(activity, URL(URL(baseUrl), "/api/master")) - } else { - espProvisionProvider = ESPProvisionProvider(activity) - } + espProvisionProvider = ESPProvisionProvider(activity) } when { action.equals(ESPProvisionProviderActions.PROVIDER_INIT, ignoreCase = true) -> { @@ -1049,7 +1045,12 @@ open class OrMainActivity : Activity() { action.equals(ESPProvisionProviderActions.PROVISION_DEVICE) -> { val userToken = data.optString("userToken") if (!userToken.isNullOrEmpty()) { - espProvisionProvider?.provisionDevice(userToken) + val apiURL = baseUrl?.let { URL(URL(it), "/api/master") } + if (apiURL != null) { + espProvisionProvider?.provisionDevice(apiURL, userToken) + } else { + espProvisionProvider?.provisionDevice(userToken = userToken) + } } else { val payload: Map = hashMapOf( "action" to action, From dd18f991dac5958e0e309aabd7a372482070361b Mon Sep 17 00:00:00 2001 From: Eric Bariaux <375613+ebariaux@users.noreply.github.com> Date: Tue, 26 May 2026 13:33:48 +0200 Subject: [PATCH 3/3] Initialize ESPProvisioningProvider with the proper realm (from URL or preferences) --- .../io/openremote/orlib/ui/OrMainActivity.kt | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt b/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt index e85c76b..888eed3 100644 --- a/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt +++ b/ORLib/src/main/java/io/openremote/orlib/ui/OrMainActivity.kt @@ -1045,9 +1045,9 @@ open class OrMainActivity : Activity() { action.equals(ESPProvisionProviderActions.PROVISION_DEVICE) -> { val userToken = data.optString("userToken") if (!userToken.isNullOrEmpty()) { - val apiURL = baseUrl?.let { URL(URL(it), "/api/master") } - if (apiURL != null) { - espProvisionProvider?.provisionDevice(apiURL, userToken) + if (baseUrl != null) { + val realm = getESPProvisionRealm() + espProvisionProvider?.provisionDevice(getESPProvisionApiURL(baseUrl!!, realm), userToken) } else { espProvisionProvider?.provisionDevice(userToken = userToken) } @@ -1062,6 +1062,27 @@ open class OrMainActivity : Activity() { } } } + + private fun getESPProvisionRealm(): String { + return baseUrl + ?.let { Uri.parse(it).getQueryParameter(ORConstants.REALM_KEY) } + ?.takeIf { it.isNotBlank() } + ?: sharedPreferences.getString(ORConstants.REALM_KEY, null) + ?.takeIf { it.isNotBlank() } + ?: "master" + } + + private fun getESPProvisionApiURL(baseUrl: String, realm: String): URL { + val appUri = Uri.parse(baseUrl) + val apiUri = Uri.Builder() + .scheme(appUri.scheme) + .encodedAuthority(appUri.encodedAuthority) + .appendPath("api") + .appendPath(realm) + .build() + + return URL(apiUri.toString()) + } } private fun notifyClient(data: Map?) {