@bota.dev/react-native-app-sdk 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BotaDeviceSDK.podspec +55 -0
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/android/build.gradle +76 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevices.kt +172 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLifecycle.kt +100 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogs.kt +82 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidModels.kt +476 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidOTA.kt +58 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploads.kt +180 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordings.kt +751 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidSecurity.kt +509 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidWiFi.kt +125 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKEncryptedUploadV2Materials.kt +46 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKModule.kt +723 -0
- package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKPackage.kt +33 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevicesTest.kt +211 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLifecycleTest.kt +132 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogsTest.kt +57 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidOTATest.kt +75 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploadsTest.kt +147 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingsTest.kt +355 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidSecurityTest.kt +523 -0
- package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidWiFiTest.kt +100 -0
- package/generated/codegen-contract.json +3593 -0
- package/ios/BotaDeviceSDK.mm +1300 -0
- package/ios/BotaDeviceSDKAppleBridge.swift +2260 -0
- package/ios/BotaDeviceSDKAppleDevices.swift +191 -0
- package/ios/BotaDeviceSDKAppleLifecycle.swift +167 -0
- package/ios/BotaDeviceSDKAppleLogs.swift +79 -0
- package/ios/BotaDeviceSDKAppleOTA.swift +73 -0
- package/ios/BotaDeviceSDKAppleRecordingUploads.swift +178 -0
- package/ios/BotaDeviceSDKAppleRecordings.swift +670 -0
- package/ios/BotaDeviceSDKAppleSecurity.swift +586 -0
- package/ios/BotaDeviceSDKAppleWiFi.swift +128 -0
- package/ios/BotaDeviceSDKEncryptedUploadV2Materials.swift +99 -0
- package/lib/commonjs/BotaClient.js +194 -0
- package/lib/commonjs/BotaClient.js.map +1 -0
- package/lib/commonjs/ble/constants.js +10 -0
- package/lib/commonjs/ble/constants.js.map +1 -0
- package/lib/commonjs/ble/deviceLogs.js +50 -0
- package/lib/commonjs/ble/deviceLogs.js.map +1 -0
- package/lib/commonjs/cache/DeviceStateCache.js +6 -0
- package/lib/commonjs/cache/DeviceStateCache.js.map +1 -0
- package/lib/commonjs/client.js +746 -0
- package/lib/commonjs/client.js.map +1 -0
- package/lib/commonjs/compatibility/runtime.js +44 -0
- package/lib/commonjs/compatibility/runtime.js.map +1 -0
- package/lib/commonjs/index.js +123 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/managers/DeviceManager.js +647 -0
- package/lib/commonjs/managers/DeviceManager.js.map +1 -0
- package/lib/commonjs/managers/OTAManager.js +140 -0
- package/lib/commonjs/managers/OTAManager.js.map +1 -0
- package/lib/commonjs/managers/RecordingManager.js +444 -0
- package/lib/commonjs/managers/RecordingManager.js.map +1 -0
- package/lib/commonjs/managers/StreamingSession.js +175 -0
- package/lib/commonjs/managers/StreamingSession.js.map +1 -0
- package/lib/commonjs/managers/types.js +6 -0
- package/lib/commonjs/managers/types.js.map +1 -0
- package/lib/commonjs/models/Device.js +2 -0
- package/lib/commonjs/models/Device.js.map +1 -0
- package/lib/commonjs/models/Recording.js +2 -0
- package/lib/commonjs/models/Recording.js.map +1 -0
- package/lib/commonjs/models/Status.js +6 -0
- package/lib/commonjs/models/Status.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/NativeBotaDeviceSDK.js +9 -0
- package/lib/commonjs/specs/NativeBotaDeviceSDK.js.map +1 -0
- package/lib/commonjs/sync/syncStatus.js +178 -0
- package/lib/commonjs/sync/syncStatus.js.map +1 -0
- package/lib/commonjs/utils/errors.js +194 -0
- package/lib/commonjs/utils/errors.js.map +1 -0
- package/lib/commonjs/utils/logger.js +84 -0
- package/lib/commonjs/utils/logger.js.map +1 -0
- package/lib/module/BotaClient.js +189 -0
- package/lib/module/BotaClient.js.map +1 -0
- package/lib/module/ble/constants.js +6 -0
- package/lib/module/ble/constants.js.map +1 -0
- package/lib/module/ble/deviceLogs.js +45 -0
- package/lib/module/ble/deviceLogs.js.map +1 -0
- package/lib/module/cache/DeviceStateCache.js +4 -0
- package/lib/module/cache/DeviceStateCache.js.map +1 -0
- package/lib/module/client.js +740 -0
- package/lib/module/client.js.map +1 -0
- package/lib/module/compatibility/runtime.js +35 -0
- package/lib/module/compatibility/runtime.js.map +1 -0
- package/lib/module/index.js +17 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/managers/DeviceManager.js +641 -0
- package/lib/module/managers/DeviceManager.js.map +1 -0
- package/lib/module/managers/OTAManager.js +134 -0
- package/lib/module/managers/OTAManager.js.map +1 -0
- package/lib/module/managers/RecordingManager.js +438 -0
- package/lib/module/managers/RecordingManager.js.map +1 -0
- package/lib/module/managers/StreamingSession.js +168 -0
- package/lib/module/managers/StreamingSession.js.map +1 -0
- package/lib/module/managers/types.js +4 -0
- package/lib/module/managers/types.js.map +1 -0
- package/lib/module/models/Device.js +2 -0
- package/lib/module/models/Device.js.map +1 -0
- package/lib/module/models/Recording.js +2 -0
- package/lib/module/models/Recording.js.map +1 -0
- package/lib/module/models/Status.js +4 -0
- package/lib/module/models/Status.js.map +1 -0
- package/lib/module/specs/NativeBotaDeviceSDK.js +5 -0
- package/lib/module/specs/NativeBotaDeviceSDK.js.map +1 -0
- package/lib/module/sync/syncStatus.js +174 -0
- package/lib/module/sync/syncStatus.js.map +1 -0
- package/lib/module/utils/errors.js +183 -0
- package/lib/module/utils/errors.js.map +1 -0
- package/lib/module/utils/logger.js +80 -0
- package/lib/module/utils/logger.js.map +1 -0
- package/lib/typescript/BotaClient.d.ts +40 -0
- package/lib/typescript/BotaClient.d.ts.map +1 -0
- package/lib/typescript/ble/constants.d.ts +4 -0
- package/lib/typescript/ble/constants.d.ts.map +1 -0
- package/lib/typescript/ble/deviceLogs.d.ts +10 -0
- package/lib/typescript/ble/deviceLogs.d.ts.map +1 -0
- package/lib/typescript/cache/DeviceStateCache.d.ts +14 -0
- package/lib/typescript/cache/DeviceStateCache.d.ts.map +1 -0
- package/lib/typescript/client.d.ts +208 -0
- package/lib/typescript/client.d.ts.map +1 -0
- package/lib/typescript/compatibility/runtime.d.ts +11 -0
- package/lib/typescript/compatibility/runtime.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +19 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/managers/DeviceManager.d.ts +117 -0
- package/lib/typescript/managers/DeviceManager.d.ts.map +1 -0
- package/lib/typescript/managers/OTAManager.d.ts +25 -0
- package/lib/typescript/managers/OTAManager.d.ts.map +1 -0
- package/lib/typescript/managers/RecordingManager.d.ts +54 -0
- package/lib/typescript/managers/RecordingManager.d.ts.map +1 -0
- package/lib/typescript/managers/StreamingSession.d.ts +35 -0
- package/lib/typescript/managers/StreamingSession.d.ts.map +1 -0
- package/lib/typescript/managers/types.d.ts +19 -0
- package/lib/typescript/managers/types.d.ts.map +1 -0
- package/lib/typescript/models/Device.d.ts +392 -0
- package/lib/typescript/models/Device.d.ts.map +1 -0
- package/lib/typescript/models/Recording.d.ts +259 -0
- package/lib/typescript/models/Recording.d.ts.map +1 -0
- package/lib/typescript/models/Status.d.ts +106 -0
- package/lib/typescript/models/Status.d.ts.map +1 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts +393 -0
- package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts.map +1 -0
- package/lib/typescript/sync/syncStatus.d.ts +123 -0
- package/lib/typescript/sync/syncStatus.d.ts.map +1 -0
- package/lib/typescript/utils/errors.d.ts +83 -0
- package/lib/typescript/utils/errors.d.ts.map +1 -0
- package/lib/typescript/utils/logger.d.ts +36 -0
- package/lib/typescript/utils/logger.d.ts.map +1 -0
- package/package.json +100 -0
- package/scripts/bota_device_sdk_spm_workaround.rb +53 -0
- package/src/BotaClient.ts +231 -0
- package/src/ble/constants.ts +3 -0
- package/src/ble/deviceLogs.ts +61 -0
- package/src/cache/DeviceStateCache.ts +20 -0
- package/src/client.ts +1410 -0
- package/src/compatibility/runtime.ts +55 -0
- package/src/index.ts +146 -0
- package/src/managers/DeviceManager.ts +927 -0
- package/src/managers/OTAManager.ts +195 -0
- package/src/managers/RecordingManager.ts +578 -0
- package/src/managers/StreamingSession.ts +212 -0
- package/src/managers/types.ts +36 -0
- package/src/models/Device.ts +484 -0
- package/src/models/Recording.ts +281 -0
- package/src/models/Status.ts +128 -0
- package/src/specs/NativeBotaDeviceSDK.ts +542 -0
- package/src/sync/syncStatus.ts +243 -0
- package/src/utils/errors.ts +327 -0
- package/src/utils/logger.ts +122 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
package dev.bota.sdk.reactnative
|
|
2
|
+
|
|
3
|
+
import dev.bota.sdk.model.ConnectedDevice
|
|
4
|
+
import dev.bota.sdk.DeviceApiEnvironment
|
|
5
|
+
import dev.bota.sdk.model.ConnectionState
|
|
6
|
+
import dev.bota.sdk.model.DeviceType
|
|
7
|
+
import dev.bota.sdk.model.DeviceConnectionSettings
|
|
8
|
+
import dev.bota.sdk.model.DeprovisionResult
|
|
9
|
+
import dev.bota.sdk.model.FactoryResetCompletion
|
|
10
|
+
import dev.bota.sdk.model.FactoryResetGrantRequest
|
|
11
|
+
import dev.bota.sdk.model.FactoryResetPersistenceResult
|
|
12
|
+
import dev.bota.sdk.model.ProvisioningMaterial
|
|
13
|
+
import dev.bota.sdk.model.ProvisioningMaterialRequest
|
|
14
|
+
import dev.bota.sdk.model.RecordingControlError
|
|
15
|
+
import dev.bota.sdk.model.RecordingControlResult
|
|
16
|
+
import dev.bota.sdk.model.RecordingInitiator
|
|
17
|
+
import dev.bota.sdk.model.RecordingState
|
|
18
|
+
import kotlinx.coroutines.CompletableDeferred
|
|
19
|
+
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
20
|
+
import kotlinx.coroutines.awaitCancellation
|
|
21
|
+
import kotlinx.coroutines.async
|
|
22
|
+
import kotlinx.coroutines.flow.Flow
|
|
23
|
+
import kotlinx.coroutines.flow.flow
|
|
24
|
+
import kotlinx.coroutines.test.runCurrent
|
|
25
|
+
import kotlinx.coroutines.supervisorScope
|
|
26
|
+
import kotlinx.coroutines.test.runTest
|
|
27
|
+
import org.junit.Assert.assertArrayEquals
|
|
28
|
+
import org.junit.Assert.assertEquals
|
|
29
|
+
import org.junit.Assert.assertTrue
|
|
30
|
+
import org.junit.Test
|
|
31
|
+
|
|
32
|
+
class BotaDeviceSDKAndroidSecurityTest {
|
|
33
|
+
@Test
|
|
34
|
+
fun deviceControlsDelegateTypedValuesToAndroidFacade() = runTest {
|
|
35
|
+
val connected = ConnectedDevice(
|
|
36
|
+
id = "selected",
|
|
37
|
+
serialNumber = "EVFXXW67KP",
|
|
38
|
+
deviceType = DeviceType.BotaPin,
|
|
39
|
+
firmwareVersion = "1.0.11",
|
|
40
|
+
isProvisioned = true,
|
|
41
|
+
connectionState = ConnectionState.Connected,
|
|
42
|
+
mtu = 247,
|
|
43
|
+
)
|
|
44
|
+
val client = TestAndroidSecurityClient()
|
|
45
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
46
|
+
|
|
47
|
+
assertTrue(security.isProvisioned(connected))
|
|
48
|
+
assertEquals("public-key", security.readPublicKey(connected))
|
|
49
|
+
assertEquals("nonce", security.readAuthNonce(connected))
|
|
50
|
+
security.setApiEndpoint(DeviceApiEnvironment.Gamma, connected)
|
|
51
|
+
security.deliverCertificate("cert", "key", connected)
|
|
52
|
+
security.deliverBackendPublicKey(byteArrayOf(1, 2, 3), connected)
|
|
53
|
+
security.writeGrant("AQID", connected)
|
|
54
|
+
security.syncTime(connected)
|
|
55
|
+
|
|
56
|
+
assertEquals(DeviceApiEnvironment.Gamma, client.environment)
|
|
57
|
+
assertEquals("cert", client.certificate)
|
|
58
|
+
assertEquals("key", client.privateKey)
|
|
59
|
+
assertArrayEquals(byteArrayOf(1, 2, 3), client.backendPublicKey)
|
|
60
|
+
assertEquals("AQID", client.grantBlob)
|
|
61
|
+
assertTrue(client.timeSynced)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
66
|
+
fun recordingControlsAndStateStreamDelegateToAndroidFacade() = runTest {
|
|
67
|
+
val connected = ConnectedDevice(
|
|
68
|
+
id = "selected",
|
|
69
|
+
serialNumber = "EVFXXW67KP",
|
|
70
|
+
deviceType = DeviceType.BotaPin,
|
|
71
|
+
firmwareVersion = "1.0.11",
|
|
72
|
+
isProvisioned = true,
|
|
73
|
+
connectionState = ConnectionState.Connected,
|
|
74
|
+
mtu = 247,
|
|
75
|
+
)
|
|
76
|
+
val client = TestAndroidSecurityClient()
|
|
77
|
+
val security = BotaDeviceSDKAndroidSecurity(client, backgroundScope)
|
|
78
|
+
val update = CompletableDeferred<RecordingState>()
|
|
79
|
+
|
|
80
|
+
assertEquals(
|
|
81
|
+
RecordingControlResult(success = true),
|
|
82
|
+
security.requestStartRecording(connected, "c3RhcnQ="),
|
|
83
|
+
)
|
|
84
|
+
assertEquals(
|
|
85
|
+
RecordingControlResult(success = false, error = RecordingControlError.NotRecording),
|
|
86
|
+
security.requestStopRecording(connected, "c3RvcA=="),
|
|
87
|
+
)
|
|
88
|
+
assertEquals(
|
|
89
|
+
RecordingState(true, "recording-1", RecordingInitiator.Remote),
|
|
90
|
+
security.readRecordingState(connected),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
security.startRecordingStateUpdates(connected) { update.complete(it) }
|
|
94
|
+
runCurrent()
|
|
95
|
+
assertEquals(
|
|
96
|
+
RecordingState(false, initiatedBy = RecordingInitiator.Local),
|
|
97
|
+
update.await(),
|
|
98
|
+
)
|
|
99
|
+
security.stopRecordingStateUpdates()
|
|
100
|
+
security.stopRecordingStateUpdates()
|
|
101
|
+
client.recordingStateTerminated.await()
|
|
102
|
+
|
|
103
|
+
assertEquals("c3RhcnQ=", client.startRecordingGrant)
|
|
104
|
+
assertEquals("c3RvcA==", client.stopRecordingGrant)
|
|
105
|
+
assertEquals(1, client.recordingStateTerminationCount)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Test
|
|
109
|
+
fun provisioningMaterialRoundTripAndDeprovisionDelegateToAndroidFacade() = runTest {
|
|
110
|
+
val connected = ConnectedDevice(
|
|
111
|
+
id = "selected",
|
|
112
|
+
serialNumber = "EVFXXW67KP",
|
|
113
|
+
deviceType = DeviceType.BotaPin,
|
|
114
|
+
firmwareVersion = "1.0.11",
|
|
115
|
+
isProvisioned = false,
|
|
116
|
+
connectionState = ConnectionState.Connected,
|
|
117
|
+
mtu = 247,
|
|
118
|
+
)
|
|
119
|
+
val client = TestAndroidSecurityClient()
|
|
120
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
121
|
+
val requests = CompletableDeferred<BotaDeviceSDKAndroidProvisioningRequest>()
|
|
122
|
+
|
|
123
|
+
val operation = async {
|
|
124
|
+
security.provision(connected) { requests.complete(it) }
|
|
125
|
+
}
|
|
126
|
+
val request = requests.await()
|
|
127
|
+
assertEquals(connected.serialNumber, request.serialNumber)
|
|
128
|
+
assertEquals("00112233", request.nonce)
|
|
129
|
+
assertEquals("aabbccdd", request.devicePublicKey)
|
|
130
|
+
|
|
131
|
+
security.resolveProvisioningMaterial(
|
|
132
|
+
requestId = request.requestId,
|
|
133
|
+
apiEndpoint = "https://api.bota.dev",
|
|
134
|
+
deviceToken = "dtok_example",
|
|
135
|
+
mtu = 247u,
|
|
136
|
+
)
|
|
137
|
+
operation.await()
|
|
138
|
+
val deprovision = security.deprovision(connected, "AQID")
|
|
139
|
+
|
|
140
|
+
assertArrayEquals("https://api.bota.dev".encodeToByteArray(), client.material?.apiEndpoint)
|
|
141
|
+
assertArrayEquals("dtok_example".encodeToByteArray(), client.material?.deviceToken)
|
|
142
|
+
assertEquals(247uL, client.material?.mtu)
|
|
143
|
+
assertEquals(listOf(connected.serialNumber), client.deprovisionedSerials)
|
|
144
|
+
assertEquals("AQID", client.deprovisionGrant)
|
|
145
|
+
assertEquals(DeprovisionResult(success = true), deprovision)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Test
|
|
149
|
+
fun factoryResetGrantRoundTripAndExactGenerationResumeDelegateToAndroidFacade() = runTest {
|
|
150
|
+
val connected = ConnectedDevice(
|
|
151
|
+
id = "selected",
|
|
152
|
+
serialNumber = "EVFXXW67KP",
|
|
153
|
+
deviceType = DeviceType.BotaPin,
|
|
154
|
+
firmwareVersion = "1.0.11",
|
|
155
|
+
isProvisioned = true,
|
|
156
|
+
connectionState = ConnectionState.Connected,
|
|
157
|
+
mtu = 247,
|
|
158
|
+
)
|
|
159
|
+
val client = TestAndroidSecurityClient()
|
|
160
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
161
|
+
val requests = CompletableDeferred<BotaDeviceSDKAndroidFactoryResetRequest>()
|
|
162
|
+
|
|
163
|
+
val operation = async {
|
|
164
|
+
security.factoryReset(
|
|
165
|
+
connected,
|
|
166
|
+
commandId = "reset-command-1",
|
|
167
|
+
bindingGeneration = 9u,
|
|
168
|
+
onGrantRequest = { requests.complete(it) },
|
|
169
|
+
onPersistenceRequest = null,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
val request = requests.await()
|
|
173
|
+
assertEquals(connected.serialNumber, request.serialNumber)
|
|
174
|
+
assertEquals("44556677", request.nonce)
|
|
175
|
+
assertEquals("reset-command-1", request.commandId)
|
|
176
|
+
assertEquals(9uL, request.bindingGeneration)
|
|
177
|
+
|
|
178
|
+
security.resolveFactoryResetGrant(request.requestId, "Z3JhbnQ=")
|
|
179
|
+
assertEquals(
|
|
180
|
+
FactoryResetCompletion("reset-command-1", 9u),
|
|
181
|
+
operation.await(),
|
|
182
|
+
)
|
|
183
|
+
assertEquals(
|
|
184
|
+
FactoryResetCompletion("reset-command-1", 9u),
|
|
185
|
+
security.resumePendingFactoryReset(connected, 9u, null),
|
|
186
|
+
)
|
|
187
|
+
assertArrayEquals("grant".encodeToByteArray(), client.factoryResetGrant)
|
|
188
|
+
assertEquals(listOf(9uL), client.resumedBindingGenerations)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@Test
|
|
192
|
+
fun factoryResetWaitsForApplicationPersistenceResolution() = runTest {
|
|
193
|
+
val connected = ConnectedDevice(
|
|
194
|
+
id = "selected",
|
|
195
|
+
serialNumber = "EVFXXW67KP",
|
|
196
|
+
deviceType = DeviceType.BotaPin,
|
|
197
|
+
firmwareVersion = "1.0.11",
|
|
198
|
+
isProvisioned = true,
|
|
199
|
+
connectionState = ConnectionState.Connected,
|
|
200
|
+
mtu = 247,
|
|
201
|
+
)
|
|
202
|
+
val client = TestAndroidSecurityClient()
|
|
203
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
204
|
+
val grantRequests = CompletableDeferred<BotaDeviceSDKAndroidFactoryResetRequest>()
|
|
205
|
+
val persistenceRequests = CompletableDeferred<
|
|
206
|
+
BotaDeviceSDKAndroidFactoryResetPersistenceRequest
|
|
207
|
+
>()
|
|
208
|
+
|
|
209
|
+
val operation = async {
|
|
210
|
+
security.factoryReset(
|
|
211
|
+
connected,
|
|
212
|
+
commandId = "reset-command-1",
|
|
213
|
+
bindingGeneration = 9u,
|
|
214
|
+
onGrantRequest = { grantRequests.complete(it) },
|
|
215
|
+
onPersistenceRequest = { persistenceRequests.complete(it) },
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
val grantRequest = grantRequests.await()
|
|
219
|
+
security.resolveFactoryResetGrant(grantRequest.requestId, "Z3JhbnQ=")
|
|
220
|
+
val persistenceRequest = persistenceRequests.await()
|
|
221
|
+
assertEquals(7u.toUShort(), persistenceRequest.localRecordingsDeleted)
|
|
222
|
+
assertEquals(0, client.factoryResetPersistenceCompletions)
|
|
223
|
+
|
|
224
|
+
security.resolveFactoryResetResultPersistence(persistenceRequest.requestId)
|
|
225
|
+
operation.await()
|
|
226
|
+
|
|
227
|
+
assertEquals(1, client.factoryResetPersistenceCompletions)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Test
|
|
231
|
+
fun factoryResetResumeFallsBackToFirmwareReplayAfterReinstall() = runTest {
|
|
232
|
+
val connected = ConnectedDevice(
|
|
233
|
+
id = "selected",
|
|
234
|
+
serialNumber = "EVFXXW67KP",
|
|
235
|
+
deviceType = DeviceType.BotaPin,
|
|
236
|
+
firmwareVersion = "1.0.11",
|
|
237
|
+
isProvisioned = true,
|
|
238
|
+
connectionState = ConnectionState.Connected,
|
|
239
|
+
mtu = 247,
|
|
240
|
+
)
|
|
241
|
+
val client = TestAndroidSecurityClient().apply { hasPendingFactoryReset = false }
|
|
242
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
243
|
+
val persistenceRequests = CompletableDeferred<
|
|
244
|
+
BotaDeviceSDKAndroidFactoryResetPersistenceRequest
|
|
245
|
+
>()
|
|
246
|
+
|
|
247
|
+
val operation = async {
|
|
248
|
+
security.resumePendingFactoryReset(
|
|
249
|
+
connected,
|
|
250
|
+
currentBindingGeneration = 0u,
|
|
251
|
+
onPersistenceRequest = { persistenceRequests.complete(it) },
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
val request = persistenceRequests.await()
|
|
255
|
+
assertEquals(7u.toUShort(), request.localRecordingsDeleted)
|
|
256
|
+
security.resolveFactoryResetResultPersistence(request.requestId)
|
|
257
|
+
|
|
258
|
+
assertEquals(0uL, operation.await()?.bindingGeneration)
|
|
259
|
+
assertEquals(listOf(0uL), client.unjournaledResumeBindingGenerations)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@Test
|
|
263
|
+
fun connectionSettingsDelegateToAndroidFacade() = runTest {
|
|
264
|
+
val connected = ConnectedDevice(
|
|
265
|
+
id = "selected",
|
|
266
|
+
serialNumber = "EVFXXW67KP",
|
|
267
|
+
deviceType = DeviceType.BotaNote,
|
|
268
|
+
firmwareVersion = "1.0.11",
|
|
269
|
+
isProvisioned = true,
|
|
270
|
+
connectionState = ConnectionState.Connected,
|
|
271
|
+
mtu = 247,
|
|
272
|
+
)
|
|
273
|
+
val client = TestAndroidSecurityClient()
|
|
274
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
275
|
+
val settings = DeviceConnectionSettings(
|
|
276
|
+
enabledConnections = DeviceConnectionSettings.EnabledConnections(true, true),
|
|
277
|
+
heartbeatEnabledConnections = DeviceConnectionSettings.EnabledConnections(true, true),
|
|
278
|
+
uploadNetworkPreference = listOf(
|
|
279
|
+
DeviceConnectionSettings.ConnectionType.Wifi,
|
|
280
|
+
DeviceConnectionSettings.ConnectionType.Ble,
|
|
281
|
+
DeviceConnectionSettings.ConnectionType.Cellular,
|
|
282
|
+
),
|
|
283
|
+
powerManagement = DeviceConnectionSettings.PowerManagement(0, -1),
|
|
284
|
+
streamingEnabled = false,
|
|
285
|
+
streamingFlushIntervalSeconds = 30,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
security.writeConnectionSettings(settings, connected)
|
|
289
|
+
|
|
290
|
+
assertEquals(settings, client.connectionSettings)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@Test
|
|
294
|
+
fun connectionSettingsReadDelegatesToAndroidFacade() = runTest {
|
|
295
|
+
val connected = ConnectedDevice(
|
|
296
|
+
id = "selected",
|
|
297
|
+
serialNumber = "EVFXXW67KP",
|
|
298
|
+
deviceType = DeviceType.BotaNote,
|
|
299
|
+
firmwareVersion = "1.0.11",
|
|
300
|
+
isProvisioned = true,
|
|
301
|
+
connectionState = ConnectionState.Connected,
|
|
302
|
+
mtu = 247,
|
|
303
|
+
)
|
|
304
|
+
val expected = DeviceConnectionSettings(
|
|
305
|
+
enabledConnections = DeviceConnectionSettings.EnabledConnections(true, false),
|
|
306
|
+
uploadNetworkPreference = listOf(
|
|
307
|
+
DeviceConnectionSettings.ConnectionType.Wifi,
|
|
308
|
+
DeviceConnectionSettings.ConnectionType.Ble,
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
val client = TestAndroidSecurityClient().apply { connectionSettingsReadResult = expected }
|
|
312
|
+
val security = BotaDeviceSDKAndroidSecurity(client)
|
|
313
|
+
|
|
314
|
+
val settings = security.readConnectionSettings(connected)
|
|
315
|
+
|
|
316
|
+
assertEquals(expected, settings)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@Test
|
|
320
|
+
fun factoryResetRejectsMalformedGrantAndCancelsPendingRequestsOnDestroy() = runTest {
|
|
321
|
+
val connected = ConnectedDevice(
|
|
322
|
+
id = "selected",
|
|
323
|
+
serialNumber = "EVFXXW67KP",
|
|
324
|
+
deviceType = DeviceType.BotaPin,
|
|
325
|
+
firmwareVersion = "1.0.11",
|
|
326
|
+
isProvisioned = true,
|
|
327
|
+
connectionState = ConnectionState.Connected,
|
|
328
|
+
mtu = 247,
|
|
329
|
+
)
|
|
330
|
+
supervisorScope {
|
|
331
|
+
val malformedClient = TestAndroidSecurityClient()
|
|
332
|
+
val malformedSecurity = BotaDeviceSDKAndroidSecurity(malformedClient)
|
|
333
|
+
val malformedRequests = CompletableDeferred<BotaDeviceSDKAndroidFactoryResetRequest>()
|
|
334
|
+
val malformedOperation = async {
|
|
335
|
+
malformedSecurity.factoryReset(
|
|
336
|
+
connected,
|
|
337
|
+
commandId = "reset-command-1",
|
|
338
|
+
bindingGeneration = 9u,
|
|
339
|
+
onGrantRequest = { malformedRequests.complete(it) },
|
|
340
|
+
onPersistenceRequest = null,
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
val malformedRequest = malformedRequests.await()
|
|
344
|
+
|
|
345
|
+
malformedSecurity.resolveFactoryResetGrant(malformedRequest.requestId, "not-encoded")
|
|
346
|
+
assertEquals(
|
|
347
|
+
"factory reset grant is not valid encoded data",
|
|
348
|
+
runCatching { malformedOperation.await() }.exceptionOrNull()?.message,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
val cancelledClient = TestAndroidSecurityClient()
|
|
352
|
+
val cancelledSecurity = BotaDeviceSDKAndroidSecurity(cancelledClient)
|
|
353
|
+
val cancelledRequests = CompletableDeferred<BotaDeviceSDKAndroidFactoryResetRequest>()
|
|
354
|
+
val cancelledOperation = async {
|
|
355
|
+
cancelledSecurity.factoryReset(
|
|
356
|
+
connected,
|
|
357
|
+
commandId = "reset-command-2",
|
|
358
|
+
bindingGeneration = 10u,
|
|
359
|
+
onGrantRequest = { cancelledRequests.complete(it) },
|
|
360
|
+
onPersistenceRequest = null,
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
cancelledRequests.await()
|
|
364
|
+
cancelledSecurity.cancelAll()
|
|
365
|
+
|
|
366
|
+
assertTrue(runCatching { cancelledOperation.await() }.isFailure)
|
|
367
|
+
assertTrue(cancelledClient.factoryResetCancelled)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private class TestAndroidSecurityClient : BotaDeviceSDKAndroidSecurityClient {
|
|
372
|
+
var material: ProvisioningMaterial? = null
|
|
373
|
+
val deprovisionedSerials = mutableListOf<String>()
|
|
374
|
+
var deprovisionGrant: String? = null
|
|
375
|
+
var factoryResetGrant: ByteArray? = null
|
|
376
|
+
var factoryResetCancelled = false
|
|
377
|
+
var factoryResetPersistenceCompletions = 0
|
|
378
|
+
val resumedBindingGenerations = mutableListOf<ULong>()
|
|
379
|
+
val unjournaledResumeBindingGenerations = mutableListOf<ULong>()
|
|
380
|
+
var hasPendingFactoryReset = true
|
|
381
|
+
var connectionSettings: DeviceConnectionSettings? = null
|
|
382
|
+
var connectionSettingsReadResult = DeviceConnectionSettings(
|
|
383
|
+
enabledConnections = DeviceConnectionSettings.EnabledConnections(true, false),
|
|
384
|
+
uploadNetworkPreference = listOf(DeviceConnectionSettings.ConnectionType.Wifi),
|
|
385
|
+
)
|
|
386
|
+
var environment: DeviceApiEnvironment? = null
|
|
387
|
+
var certificate: String? = null
|
|
388
|
+
var privateKey: String? = null
|
|
389
|
+
var backendPublicKey: ByteArray? = null
|
|
390
|
+
var grantBlob: String? = null
|
|
391
|
+
var timeSynced = false
|
|
392
|
+
var startRecordingGrant: String? = null
|
|
393
|
+
var stopRecordingGrant: String? = null
|
|
394
|
+
val recordingStateTerminated = CompletableDeferred<Unit>()
|
|
395
|
+
var recordingStateTerminationCount = 0
|
|
396
|
+
|
|
397
|
+
override suspend fun isProvisioned(device: ConnectedDevice): Boolean = true
|
|
398
|
+
override suspend fun readPublicKey(device: ConnectedDevice): String? = "public-key"
|
|
399
|
+
override suspend fun readAuthNonce(device: ConnectedDevice): String? = "nonce"
|
|
400
|
+
override suspend fun setApiEndpoint(environment: DeviceApiEnvironment, device: ConnectedDevice) {
|
|
401
|
+
this.environment = environment
|
|
402
|
+
}
|
|
403
|
+
override suspend fun deliverCertificate(
|
|
404
|
+
certificatePem: String,
|
|
405
|
+
privateKeyPem: String,
|
|
406
|
+
device: ConnectedDevice,
|
|
407
|
+
) {
|
|
408
|
+
certificate = certificatePem
|
|
409
|
+
privateKey = privateKeyPem
|
|
410
|
+
}
|
|
411
|
+
override suspend fun deliverBackendPublicKey(publicKey: ByteArray, device: ConnectedDevice) {
|
|
412
|
+
backendPublicKey = publicKey
|
|
413
|
+
}
|
|
414
|
+
override suspend fun writeGrant(grantBlob: String, device: ConnectedDevice) {
|
|
415
|
+
this.grantBlob = grantBlob
|
|
416
|
+
}
|
|
417
|
+
override suspend fun syncTime(device: ConnectedDevice) { timeSynced = true }
|
|
418
|
+
override suspend fun requestStartRecording(
|
|
419
|
+
device: ConnectedDevice,
|
|
420
|
+
grantBlob: String,
|
|
421
|
+
): RecordingControlResult {
|
|
422
|
+
startRecordingGrant = grantBlob
|
|
423
|
+
return RecordingControlResult(success = true)
|
|
424
|
+
}
|
|
425
|
+
override suspend fun requestStopRecording(
|
|
426
|
+
device: ConnectedDevice,
|
|
427
|
+
grantBlob: String,
|
|
428
|
+
): RecordingControlResult {
|
|
429
|
+
stopRecordingGrant = grantBlob
|
|
430
|
+
return RecordingControlResult(success = false, error = RecordingControlError.NotRecording)
|
|
431
|
+
}
|
|
432
|
+
override suspend fun readRecordingState(device: ConnectedDevice): RecordingState =
|
|
433
|
+
RecordingState(true, "recording-1", RecordingInitiator.Remote)
|
|
434
|
+
override fun recordingStateUpdates(device: ConnectedDevice): Flow<RecordingState> = flow {
|
|
435
|
+
try {
|
|
436
|
+
emit(RecordingState(false, initiatedBy = RecordingInitiator.Local))
|
|
437
|
+
awaitCancellation()
|
|
438
|
+
} finally {
|
|
439
|
+
recordingStateTerminationCount += 1
|
|
440
|
+
recordingStateTerminated.complete(Unit)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
override suspend fun provision(
|
|
445
|
+
device: ConnectedDevice,
|
|
446
|
+
provider: suspend (ProvisioningMaterialRequest) -> ProvisioningMaterial,
|
|
447
|
+
) {
|
|
448
|
+
material = provider(
|
|
449
|
+
ProvisioningMaterialRequest(
|
|
450
|
+
serialNumber = device.serialNumber,
|
|
451
|
+
nonce = byteArrayOf(0x00, 0x11, 0x22, 0x33),
|
|
452
|
+
devicePublicKey = byteArrayOf(0xAA.toByte(), 0xBB.toByte(), 0xCC.toByte(), 0xDD.toByte()),
|
|
453
|
+
),
|
|
454
|
+
)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
override suspend fun deprovision(
|
|
458
|
+
device: ConnectedDevice,
|
|
459
|
+
grantBlob: String,
|
|
460
|
+
): DeprovisionResult {
|
|
461
|
+
deprovisionedSerials += device.serialNumber
|
|
462
|
+
deprovisionGrant = grantBlob
|
|
463
|
+
return DeprovisionResult(success = true)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
override suspend fun writeConnectionSettings(
|
|
467
|
+
settings: DeviceConnectionSettings,
|
|
468
|
+
device: ConnectedDevice,
|
|
469
|
+
) {
|
|
470
|
+
connectionSettings = settings
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
override suspend fun readConnectionSettings(device: ConnectedDevice): DeviceConnectionSettings =
|
|
474
|
+
connectionSettingsReadResult
|
|
475
|
+
|
|
476
|
+
override suspend fun cancelCurrentOperation() = Unit
|
|
477
|
+
|
|
478
|
+
override suspend fun factoryReset(
|
|
479
|
+
device: ConnectedDevice,
|
|
480
|
+
commandId: String,
|
|
481
|
+
bindingGeneration: ULong,
|
|
482
|
+
persistResult: (suspend (FactoryResetPersistenceResult) -> Unit)?,
|
|
483
|
+
provider: suspend (FactoryResetGrantRequest) -> ByteArray,
|
|
484
|
+
): FactoryResetCompletion {
|
|
485
|
+
factoryResetGrant = provider(
|
|
486
|
+
FactoryResetGrantRequest(
|
|
487
|
+
serialNumber = device.serialNumber,
|
|
488
|
+
nonce = byteArrayOf(0x44, 0x55, 0x66, 0x77),
|
|
489
|
+
commandId = commandId,
|
|
490
|
+
bindingGeneration = bindingGeneration,
|
|
491
|
+
),
|
|
492
|
+
)
|
|
493
|
+
persistResult?.invoke(FactoryResetPersistenceResult(7u))
|
|
494
|
+
if (persistResult != null) factoryResetPersistenceCompletions += 1
|
|
495
|
+
return FactoryResetCompletion(commandId, bindingGeneration)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
override suspend fun resumePendingFactoryReset(
|
|
499
|
+
device: ConnectedDevice,
|
|
500
|
+
currentBindingGeneration: ULong,
|
|
501
|
+
persistResult: (suspend (FactoryResetPersistenceResult) -> Unit)?,
|
|
502
|
+
): FactoryResetCompletion? {
|
|
503
|
+
resumedBindingGenerations += currentBindingGeneration
|
|
504
|
+
if (!hasPendingFactoryReset) return null
|
|
505
|
+
return FactoryResetCompletion("reset-command-1", currentBindingGeneration)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
override suspend fun resumeUnjournaledFactoryReset(
|
|
509
|
+
device: ConnectedDevice,
|
|
510
|
+
commandId: String,
|
|
511
|
+
bindingGeneration: ULong,
|
|
512
|
+
persistResult: suspend (FactoryResetPersistenceResult) -> Unit,
|
|
513
|
+
): FactoryResetCompletion {
|
|
514
|
+
unjournaledResumeBindingGenerations += bindingGeneration
|
|
515
|
+
persistResult(FactoryResetPersistenceResult(7u))
|
|
516
|
+
return FactoryResetCompletion(commandId, bindingGeneration)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
override suspend fun cancelFactoryReset() {
|
|
520
|
+
factoryResetCancelled = true
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
package dev.bota.sdk.reactnative
|
|
2
|
+
|
|
3
|
+
import dev.bota.sdk.model.ConnectedDevice
|
|
4
|
+
import dev.bota.sdk.model.ConnectionState
|
|
5
|
+
import dev.bota.sdk.model.DeviceType
|
|
6
|
+
import dev.bota.sdk.model.DeviceWiFiScanResult
|
|
7
|
+
import dev.bota.sdk.model.WiFiConfigResult
|
|
8
|
+
import dev.bota.sdk.model.WiFiConnectionStatus
|
|
9
|
+
import dev.bota.sdk.model.WiFiScanNetwork
|
|
10
|
+
import dev.bota.sdk.model.WiFiStatusInfo
|
|
11
|
+
import kotlinx.coroutines.CoroutineScope
|
|
12
|
+
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
13
|
+
import kotlinx.coroutines.awaitCancellation
|
|
14
|
+
import kotlinx.coroutines.flow.Flow
|
|
15
|
+
import kotlinx.coroutines.flow.flow
|
|
16
|
+
import kotlinx.coroutines.test.runCurrent
|
|
17
|
+
import kotlinx.coroutines.test.runTest
|
|
18
|
+
import org.junit.Assert.assertEquals
|
|
19
|
+
import org.junit.Test
|
|
20
|
+
|
|
21
|
+
class BotaDeviceSDKAndroidWiFiTest {
|
|
22
|
+
@Test
|
|
23
|
+
fun configurationAndScanDelegateToNativeWiFiFacade() = runTest {
|
|
24
|
+
val client = TestAndroidWiFiClient()
|
|
25
|
+
val wifi = BotaDeviceSDKAndroidWiFi(client, CoroutineScope(coroutineContext))
|
|
26
|
+
val device = wifiTestDevice()
|
|
27
|
+
|
|
28
|
+
val configured = wifi.configure(device, "Bota", "secret", "grant.test")
|
|
29
|
+
val scan = wifi.scanNetworks(device)
|
|
30
|
+
|
|
31
|
+
assertEquals(WiFiConfigResult.Success, configured)
|
|
32
|
+
assertEquals("Bota", scan.currentSsid)
|
|
33
|
+
assertEquals(ConfigurationInput("Bota", "secret", "grant.test"), client.configurationInput)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
37
|
+
@Test
|
|
38
|
+
fun statusStreamOwnsExactlyOneNativeSubscription() = runTest {
|
|
39
|
+
val client = TestAndroidWiFiClient()
|
|
40
|
+
val wifi = BotaDeviceSDKAndroidWiFi(client, CoroutineScope(coroutineContext))
|
|
41
|
+
val values = mutableListOf<WiFiStatusInfo>()
|
|
42
|
+
|
|
43
|
+
wifi.startStatusUpdates(wifiTestDevice(), onStatus = values::add)
|
|
44
|
+
runCurrent()
|
|
45
|
+
wifi.stopStatusUpdates()
|
|
46
|
+
wifi.stopStatusUpdates()
|
|
47
|
+
|
|
48
|
+
assertEquals(listOf(WiFiStatusInfo(WiFiConnectionStatus.Connected, 87u, "Bota")), values)
|
|
49
|
+
assertEquals(1, client.subscriptionTerminations)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private data class ConfigurationInput(val ssid: String, val password: String, val grantBlob: String)
|
|
54
|
+
|
|
55
|
+
private class TestAndroidWiFiClient : BotaDeviceSDKAndroidWiFiClient {
|
|
56
|
+
var configurationInput: ConfigurationInput? = null
|
|
57
|
+
var subscriptionTerminations = 0
|
|
58
|
+
|
|
59
|
+
override suspend fun configure(
|
|
60
|
+
device: ConnectedDevice,
|
|
61
|
+
ssid: String,
|
|
62
|
+
password: String,
|
|
63
|
+
grantBlob: String,
|
|
64
|
+
): WiFiConfigResult {
|
|
65
|
+
configurationInput = ConfigurationInput(ssid, password, grantBlob)
|
|
66
|
+
return WiFiConfigResult.Success
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override suspend fun disconnect(device: ConnectedDevice): WiFiConfigResult = WiFiConfigResult.Success
|
|
70
|
+
|
|
71
|
+
override suspend fun readStatus(device: ConnectedDevice): WiFiStatusInfo =
|
|
72
|
+
WiFiStatusInfo(WiFiConnectionStatus.Connected, 87u, "Bota")
|
|
73
|
+
|
|
74
|
+
override fun statusUpdates(device: ConnectedDevice): Flow<WiFiStatusInfo> = flow {
|
|
75
|
+
try {
|
|
76
|
+
emit(WiFiStatusInfo(WiFiConnectionStatus.Connected, 87u, "Bota"))
|
|
77
|
+
awaitCancellation()
|
|
78
|
+
} finally {
|
|
79
|
+
subscriptionTerminations += 1
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
override suspend fun scanNetworks(device: ConnectedDevice): DeviceWiFiScanResult =
|
|
84
|
+
DeviceWiFiScanResult(
|
|
85
|
+
listOf(WiFiScanNetwork("Bota", 100u, isCurrent = true, isOpen = false)),
|
|
86
|
+
"Bota",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
override suspend fun cancelCurrentOperation() = Unit
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private fun wifiTestDevice(): ConnectedDevice = ConnectedDevice(
|
|
93
|
+
id = "selected",
|
|
94
|
+
serialNumber = "EVFXXW67KP",
|
|
95
|
+
deviceType = DeviceType.BotaNote,
|
|
96
|
+
firmwareVersion = "1.0.17",
|
|
97
|
+
isProvisioned = true,
|
|
98
|
+
connectionState = ConnectionState.Connected,
|
|
99
|
+
mtu = 247,
|
|
100
|
+
)
|