@mentra/bluetooth-sdk 3.2.0-dev.200 → 3.2.0-dev.203
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/README.md +46 -45
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +5 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +63 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +5 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +285 -29
- package/android/src/main/java/com/mentra/bluetoothsdk/MessageAckPolicy.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/WifiRequestResolution.kt +246 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTracker.kt +104 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +312 -45
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +2 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +14 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/GlassesStatusClassicAudioTest.kt +14 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/MessageAckPolicyTest.kt +16 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/WifiRequestResolutionTest.kt +271 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTrackerTest.kt +131 -0
- package/build/BluetoothSdk.types.d.ts +62 -1
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +3 -2
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/generated/changelogCatalog.d.ts +1 -1
- package/build/generated/changelogCatalog.js +1 -1
- package/build/generated/changelogCatalog.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +8 -2
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/Bridge.swift +63 -0
- package/ios/Source/DeviceManager.swift +6 -3
- package/ios/Source/GeneratedChangelogCatalog.swift +1 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
- package/ios/Source/MentraBluetoothSDK.swift +365 -93
- package/ios/Source/WifiRequestResolution.swift +251 -0
- package/ios/Source/sgcs/MentraLive.swift +170 -35
- package/ios/Source/sgcs/SGCManager.swift +13 -1
- package/ios/Source/status/DeviceStatus.swift +28 -0
- package/ios/Tests/WifiRequestResolutionTests.swift +320 -0
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +76 -1
- package/src/_private/BluetoothSdkModule.ts +4 -1
- package/src/generated/changelogCatalog.ts +1 -1
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/index.ts +11 -0
package/README.md
CHANGED
|
@@ -152,21 +152,19 @@ iOS apps should include usage descriptions:
|
|
|
152
152
|
Use `scan()` when your app needs to show a picker. It calls `onResults` every time the discovered list changes, then resolves with the final list after the timeout.
|
|
153
153
|
|
|
154
154
|
```ts
|
|
155
|
-
import BluetoothSdk, {
|
|
156
|
-
DeviceModels,
|
|
157
|
-
} from '@mentra/bluetooth-sdk'
|
|
155
|
+
import BluetoothSdk, {DeviceModels} from "@mentra/bluetooth-sdk"
|
|
158
156
|
|
|
159
157
|
const devices = await BluetoothSdk.scan(DeviceModels.MentraLive, {
|
|
160
158
|
timeoutMs: 10_000,
|
|
161
159
|
onResults: (nextDevices) => {
|
|
162
|
-
console.log(
|
|
160
|
+
console.log("Nearby glasses:", nextDevices)
|
|
163
161
|
},
|
|
164
162
|
onDiagnostic: (hint) => showScanHint(hint.message),
|
|
165
163
|
})
|
|
166
164
|
|
|
167
165
|
const device = await chooseDevice(devices)
|
|
168
166
|
if (!device) {
|
|
169
|
-
throw new Error(
|
|
167
|
+
throw new Error("No Mentra Live glasses selected")
|
|
170
168
|
}
|
|
171
169
|
|
|
172
170
|
await BluetoothSdk.connect(device)
|
|
@@ -275,9 +273,9 @@ subpath for common lifecycle plumbing. Use the root `BluetoothSdk` object for
|
|
|
275
273
|
commands such as `requestPhoto()`, `startStream()`, and `setMicState()`.
|
|
276
274
|
|
|
277
275
|
```tsx
|
|
278
|
-
import {Button, Text, View} from
|
|
279
|
-
import {DeviceModels} from
|
|
280
|
-
import {useBluetoothEvent, useMentraBluetooth} from
|
|
276
|
+
import {Button, Text, View} from "react-native"
|
|
277
|
+
import {DeviceModels} from "@mentra/bluetooth-sdk"
|
|
278
|
+
import {useBluetoothEvent, useMentraBluetooth} from "@mentra/bluetooth-sdk/react"
|
|
281
279
|
|
|
282
280
|
export function DeviceScreen() {
|
|
283
281
|
const mentra = useMentraBluetooth({
|
|
@@ -285,13 +283,13 @@ export function DeviceScreen() {
|
|
|
285
283
|
scanTimeoutMs: 10_000,
|
|
286
284
|
})
|
|
287
285
|
|
|
288
|
-
useBluetoothEvent(
|
|
289
|
-
console.log(
|
|
286
|
+
useBluetoothEvent("button_press", (event) => {
|
|
287
|
+
console.log("Glasses button:", event.buttonId, event.pressType)
|
|
290
288
|
})
|
|
291
289
|
|
|
292
290
|
return (
|
|
293
291
|
<View>
|
|
294
|
-
<Text>{mentra.glasses.connected ?
|
|
292
|
+
<Text>{mentra.glasses.connected ? "Connected" : "Disconnected"}</Text>
|
|
295
293
|
<Button disabled={mentra.busy} title="Scan" onPress={() => mentra.scan.start()} />
|
|
296
294
|
{mentra.scan.devices.map((device) => (
|
|
297
295
|
<Button key={device.id} title={device.name} onPress={() => mentra.connect(device)} />
|
|
@@ -314,11 +312,11 @@ The React hook exposes `glasses.connection` as a discriminated union:
|
|
|
314
312
|
|
|
315
313
|
```ts
|
|
316
314
|
type GlassesConnectionStatus =
|
|
317
|
-
| {state:
|
|
318
|
-
| {state:
|
|
319
|
-
| {state:
|
|
320
|
-
| {state:
|
|
321
|
-
| {state:
|
|
315
|
+
| {state: "disconnected"}
|
|
316
|
+
| {state: "scanning"}
|
|
317
|
+
| {state: "connecting"}
|
|
318
|
+
| {state: "bonding"}
|
|
319
|
+
| {state: "connected"; fullyBooted: boolean}
|
|
322
320
|
```
|
|
323
321
|
|
|
324
322
|
Use `connection.state` for link progress. `fullyBooted` only exists when `state === 'connected'`. Android and iOS native APIs also keep `connectionState`, `connected`, and `fullyBooted` as native status properties for Kotlin and Swift callers.
|
|
@@ -374,11 +372,14 @@ await BluetoothSdk.setDashboardPosition(4, 2)
|
|
|
374
372
|
const networks = await BluetoothSdk.requestWifiScan()
|
|
375
373
|
console.log(networks.map((network) => network.ssid))
|
|
376
374
|
|
|
377
|
-
const
|
|
375
|
+
const savedResult = await BluetoothSdk.getSavedWifiNetworks()
|
|
376
|
+
if (savedResult.outcome === "confirmed") console.log(savedResult.networks)
|
|
377
|
+
|
|
378
|
+
const wifiStatus = await BluetoothSdk.sendWifiCredentials("Office WiFi", "secret")
|
|
378
379
|
console.log(wifiStatus.state)
|
|
379
380
|
|
|
380
|
-
const
|
|
381
|
-
console.log(
|
|
381
|
+
const forgetResult = await BluetoothSdk.forgetWifiNetwork("Office WiFi")
|
|
382
|
+
console.log(forgetResult.outcome)
|
|
382
383
|
|
|
383
384
|
const hotspotStatus = await BluetoothSdk.setHotspotState(true)
|
|
384
385
|
console.log(hotspotStatus.state)
|
|
@@ -389,19 +390,11 @@ const galleryAck = await BluetoothSdk.setGalleryModeEnabled(true)
|
|
|
389
390
|
console.log(galleryAck.status)
|
|
390
391
|
await BluetoothSdk.setGalleryModeEnabled(false)
|
|
391
392
|
|
|
392
|
-
await BluetoothSdk.setPreferredMic(
|
|
393
|
+
await BluetoothSdk.setPreferredMic("auto")
|
|
393
394
|
await BluetoothSdk.setMicState(true)
|
|
394
395
|
await BluetoothSdk.setOwnAppAudioPlaying(false)
|
|
395
396
|
|
|
396
|
-
const ledAck = await BluetoothSdk.rgbLedControl(
|
|
397
|
-
`led-${Date.now()}`,
|
|
398
|
-
'com.example.app',
|
|
399
|
-
'on',
|
|
400
|
-
'green',
|
|
401
|
-
500,
|
|
402
|
-
500,
|
|
403
|
-
3,
|
|
404
|
-
)
|
|
397
|
+
const ledAck = await BluetoothSdk.rgbLedControl(`led-${Date.now()}`, "com.example.app", "on", "green", 500, 500, 3)
|
|
405
398
|
console.log(ledAck.state)
|
|
406
399
|
```
|
|
407
400
|
|
|
@@ -440,7 +433,15 @@ await sdk.setDashboardContent("")
|
|
|
440
433
|
Settings commands that return `SettingsAckSuccessEvent` reject when the ASG reports an error ack. The SDK updates its local settings store only after that ASG ack resolves successfully, so observed SDK state reflects the acknowledged glasses state rather than a queued request. Raw `settings_ack` listener events still use `SettingsAckEvent` because they can include both success and failure statuses. `rgbLedControl(...)` resolves from a successful ASG `rgb_led_control_response` and rejects when the ASG reports `state: "error"`; raw `settings_ack` and `rgb_led_control_response` events remain available through listeners.
|
|
441
434
|
|
|
442
435
|
WiFi, hotspot, and version-info commands resolve from the ASG response path, not local dispatch:
|
|
443
|
-
`requestWifiScan()` resolves from the ASG `wifi_scan_result` completion response with the updated scan list, including `[]` when no networks are found. Intermediate `wifi_scan_result` events can arrive with `scanComplete: false` while the glasses stream discovered networks; the final event uses `scanComplete: true`. If older glasses stream non-empty scan results but never send the completion event, the request resolves with the accumulated scan list when the request times out. `sendWifiCredentials()` resolves when the requested SSID is connected, `forgetWifiNetwork()`
|
|
436
|
+
`requestWifiScan()` resolves from the ASG `wifi_scan_result` completion response with the updated scan list, including `[]` when no networks are found. Intermediate `wifi_scan_result` events can arrive with `scanComplete: false` while the glasses stream discovered networks; the final event uses `scanComplete: true`. If older glasses stream non-empty scan results but never send the completion event, the request resolves with the accumulated scan list when the request times out. `sendWifiCredentials()` resolves when the requested SSID is connected, `forgetWifiNetwork()` returns a semantic `WifiForgetResult`, `getSavedWifiNetworks()` returns a semantic `SavedWifiNetworksResult`, `setHotspotState()` resolves when the requested hotspot state is reported, and `requestVersionInfo()` waits for all chunks declared by the correlated response's `chunkCount`, `chunkIndex`, and `final` metadata. Legacy single-message responses complete immediately; legacy chunked responses complete on `version_info_3` after `version_info_1`. Missing final chunks time out rather than returning partial data after a quiet period.
|
|
437
|
+
|
|
438
|
+
Current Mentra Live builds advertise `wifiForgetResultVersion` and `savedWifiNetworksVersion` in `version_info_1`. Native queues calls while those capabilities are unknown, sharing one 15-second deadline for negotiation and response. A discovery timeout rejects and never guesses that the glasses are legacy. Only version 1 is supported: modern commands carry `protocolVersion: 1`, a nonempty request ID, and the glasses process session ID; legacy forget commands omit all three fields. Unknown or malformed versions fail closed. Correlated results must match the complete tuple and (for forget) exact SSID; unrelated `wifi_status` events never settle a modern request. Session changes and disconnects reject pending WiFi work instead of allowing stale responses to cross sessions.
|
|
439
|
+
|
|
440
|
+
`WifiForgetResult.outcome` is `confirmed`, `dispatched`, `not_found`, `unsupported`, `failed`, or `legacy_unverified`. K900 returns `dispatched`: ASG queued its asynchronous SystemUI broadcast, but the vendor API provides no completion callback and credential removal is not verified. `connected`, `currentSsid`, and `localIp` are best-effort diagnostic snapshot fields; `connected` is omitted when ASG cannot read the link state and must not be interpreted as `false`. Older firmware without the capability advertisement uses the isolated legacy path; accepted native dispatch immediately returns `legacy_unverified`, with no invented link-state snapshot. `getSavedWifiNetworks()` preserves exact SSID identity. It returns `confirmed` only for a backend with reliable enumeration, while K900 and legacy firmware return a typed `unsupported` result because the vendor credential store has no reliable list response.
|
|
441
|
+
|
|
442
|
+
Promise results expose semantic outcomes and optional link snapshots, not `mode`, `capabilityVersion`, `requestId`, or `sid`. A legacy result returns `legacy_unverified` immediately after actual native transport acceptance; no active or compatible glasses transport rejects with `dispatch_failed`.
|
|
443
|
+
|
|
444
|
+
Raw `wifi_forget_result` listeners receive a discriminated event union. `mode: "modern"` carries `requestId`, `sid`, `protocolVersion`, and semantic `outcome`; `mode: "legacy"` preserves the older uncorrelated wire frame's `dispatched` boolean. Partial tuples are rejected. Terminal Wi-Fi events use at-least-once BLE delivery, so raw listeners can observe retries; deduplicate modern events by `(requestId, sid)`. Promise coordinators accept only the first matching terminal frame.
|
|
444
445
|
|
|
445
446
|
The SDK automatically sends the phone wall clock once shortly after a glasses connection becomes ready. It waits for the initial command burst to drain before timestamping the command so startup queue delay does not become clock skew. The once-per-connection guard resets after disconnect, so every successful reconnect synchronizes again. The SDK does not periodically verify or correct clock skew during a long-lived connection; apps that require periodic reconciliation can compare `requestVersionInfo().systemTimeMs` with the phone clock.
|
|
446
447
|
|
|
@@ -525,15 +526,15 @@ Mentra Live also rejects `ota_start` before acknowledgement when its known batte
|
|
|
525
526
|
|
|
526
527
|
```ts
|
|
527
528
|
const photo = await BluetoothSdk.requestPhoto({
|
|
528
|
-
size:
|
|
529
|
-
webhookUrl:
|
|
530
|
-
authToken:
|
|
531
|
-
compress:
|
|
529
|
+
size: "medium",
|
|
530
|
+
webhookUrl: "https://api.example.com/mentra/photo",
|
|
531
|
+
authToken: "optional-token",
|
|
532
|
+
compress: "medium",
|
|
532
533
|
sound: true,
|
|
533
534
|
exposureTimeNs: null, // auto exposure; pass a positive nanosecond value for manual exposure
|
|
534
535
|
iso: null, // auto ISO; pass a positive ISO only with manual exposureTimeNs
|
|
535
536
|
})
|
|
536
|
-
console.log(
|
|
537
|
+
console.log("photo delivered", photo.photoUrl ?? photo.uploadUrl, photo.fileSizeBytes)
|
|
537
538
|
```
|
|
538
539
|
|
|
539
540
|
`requestPhoto(...)` resolves only after the full photo action reaches terminal success: capture completed and the photo was delivered to the webhook, either directly from the glasses over Wi-Fi or through the phone's Bluetooth fallback relay. If you omit `requestId`, the SDK generates one and the terminal response includes it. It rejects if the ASG reports `state: "error"`, if phone-side fallback upload fails, if the SDK cannot send the command, or if no terminal `photo_response` arrives within 30 seconds. Photo requests use this longer operation-specific deadline because max-quality BLE fallback can legitimately exceed the 15-second deadline used by ordinary commands. Use `photo_status` for intermediate stages such as `accepted`, `configuring`, `capturing`, `captured`, `uploading`, `ble_fallback_compression`, `ready_for_transfer`, and `transferring`; `photo_status` is progress, while `photo_response` is terminal success/error. The raw `photo_response` event stream still includes both success and error events for subscribers. The webhook should accept multipart form data with a `photo` file and `requestId`. If `authToken` is provided, the uploader adds `Authorization: Bearer <token>`. The camera light is always enabled for photo capture.
|
|
@@ -554,8 +555,8 @@ The missing/factory persistent FOV base is 102 degrees with centered ROI; existi
|
|
|
554
555
|
const streamId = `stream-${Date.now()}`
|
|
555
556
|
|
|
556
557
|
await BluetoothSdk.startStream({
|
|
557
|
-
type:
|
|
558
|
-
streamUrl:
|
|
558
|
+
type: "start_stream",
|
|
559
|
+
streamUrl: "http://192.168.1.42:8889/mentra-live/whip",
|
|
559
560
|
streamId,
|
|
560
561
|
video: {fps: 15},
|
|
561
562
|
})
|
|
@@ -571,19 +572,19 @@ Use `rtmp://` or `rtmps://` for RTMP, `srt://` for SRT, and `http://` or `https:
|
|
|
571
572
|
React Native components should use `useBluetoothEvent()` for hardware events:
|
|
572
573
|
|
|
573
574
|
```tsx
|
|
574
|
-
import {useBluetoothEvent} from
|
|
575
|
+
import {useBluetoothEvent} from "@mentra/bluetooth-sdk/react"
|
|
575
576
|
|
|
576
577
|
export function HardwareEventLogger() {
|
|
577
|
-
useBluetoothEvent(
|
|
578
|
-
useBluetoothEvent(
|
|
579
|
-
useBluetoothEvent(
|
|
580
|
-
useBluetoothEvent(
|
|
581
|
-
useBluetoothEvent(
|
|
582
|
-
useBluetoothEvent(
|
|
578
|
+
useBluetoothEvent("button_press", (event) => console.log(event))
|
|
579
|
+
useBluetoothEvent("touch_event", (event) => console.log(event))
|
|
580
|
+
useBluetoothEvent("photo_status", (event) => console.log(event.status, event.resolvedConfig, event.captureMetadata))
|
|
581
|
+
useBluetoothEvent("stream_status", (event) => console.log(event))
|
|
582
|
+
useBluetoothEvent("speaking_status", (event) => console.log(event.speaking))
|
|
583
|
+
useBluetoothEvent("mic_pcm", (event) => {
|
|
583
584
|
console.log(event.sampleRate, event.bitsPerSample, event.channels, event.encoding)
|
|
584
585
|
console.log(event.pcm)
|
|
585
586
|
})
|
|
586
|
-
useBluetoothEvent(
|
|
587
|
+
useBluetoothEvent("mic_health", (event) => {
|
|
587
588
|
console.log(event.reason, event.sequenceGapEvents, event.decodeFailures)
|
|
588
589
|
})
|
|
589
590
|
|
|
@@ -339,6 +339,8 @@ class BluetoothSdkModule : Module() {
|
|
|
339
339
|
"local_transcription",
|
|
340
340
|
"wifi_status_change",
|
|
341
341
|
"wifi_scan_result",
|
|
342
|
+
"wifi_forget_result",
|
|
343
|
+
"saved_wifi_networks",
|
|
342
344
|
"hotspot_status_change",
|
|
343
345
|
"hotspot_error",
|
|
344
346
|
"photo_response",
|
|
@@ -592,12 +594,14 @@ class BluetoothSdkModule : Module() {
|
|
|
592
594
|
|
|
593
595
|
SdkCoroutineFunction("requestWifiScan") { -> requireSdk().requestWifiScan().map { it.toMap() } }
|
|
594
596
|
|
|
597
|
+
SdkCoroutineFunction("getSavedWifiNetworks") { -> requireSdk().getSavedWifiNetworks().toMap() }
|
|
598
|
+
|
|
595
599
|
SdkCoroutineFunction("sendWifiCredentials") { ssid: String, password: String ->
|
|
596
600
|
requireSdk().sendWifiCredentials(ssid, password).values
|
|
597
601
|
}
|
|
598
602
|
|
|
599
603
|
SdkCoroutineFunction("forgetWifiNetwork") { ssid: String ->
|
|
600
|
-
requireSdk().forgetWifiNetwork(ssid).
|
|
604
|
+
requireSdk().forgetWifiNetwork(ssid).toMap()
|
|
601
605
|
}
|
|
602
606
|
|
|
603
607
|
SdkCoroutineFunction("setHotspotState") { enabled: Boolean ->
|
|
@@ -494,6 +494,8 @@ public class Bridge private constructor() {
|
|
|
494
494
|
?.let {
|
|
495
495
|
body[VersionInfoResponseAccumulator.RESPONSE_REQUEST_ID_KEY] = it
|
|
496
496
|
}
|
|
497
|
+
body["versionInfoType"] = stringField("versionInfoType", "version_info_type")
|
|
498
|
+
body["sid"] = stringField("sid")
|
|
497
499
|
body["androidVersion"] = stringField("androidVersion", "android_version")
|
|
498
500
|
body["firmwareVersion"] = stringField("firmwareVersion", "firmware_version")
|
|
499
501
|
body["besFirmwareVersion"] = stringField("besFirmwareVersion", "bes_fw_version")
|
|
@@ -512,6 +514,15 @@ public class Bridge private constructor() {
|
|
|
512
514
|
(values["hotspotOtaVersion"] as? Number
|
|
513
515
|
?: values["hotspot_ota_version"] as? Number)
|
|
514
516
|
?.let { body["hotspotOtaVersion"] = it.toInt() }
|
|
517
|
+
for ((key, wireKey) in listOf(
|
|
518
|
+
"wifiForgetResultVersion" to "wifi_forget_result_version",
|
|
519
|
+
"savedWifiNetworksVersion" to "saved_wifi_networks_version",
|
|
520
|
+
)) {
|
|
521
|
+
if (values.containsKey(key) || values.containsKey(wireKey)) {
|
|
522
|
+
// Preserve malformed presence: it must not become legacy or be rounded to v1.
|
|
523
|
+
body[key] = values[key] ?: values[wireKey] ?: -1
|
|
524
|
+
}
|
|
525
|
+
}
|
|
515
526
|
sendTypedMessage("version_info", body)
|
|
516
527
|
}
|
|
517
528
|
|
|
@@ -592,6 +603,58 @@ public class Bridge private constructor() {
|
|
|
592
603
|
sendTypedMessage("wifi_status_change", payload)
|
|
593
604
|
}
|
|
594
605
|
|
|
606
|
+
@JvmStatic
|
|
607
|
+
fun sendWifiForgetResult(
|
|
608
|
+
requestId: String,
|
|
609
|
+
sid: String,
|
|
610
|
+
ssid: String,
|
|
611
|
+
protocolVersion: Int,
|
|
612
|
+
outcome: String,
|
|
613
|
+
legacyDispatched: Boolean?,
|
|
614
|
+
connected: Boolean?,
|
|
615
|
+
currentSsid: String,
|
|
616
|
+
localIp: String,
|
|
617
|
+
error: String?,
|
|
618
|
+
) {
|
|
619
|
+
val body =
|
|
620
|
+
normalizeWifiForgetResultEvent(
|
|
621
|
+
requestId,
|
|
622
|
+
sid,
|
|
623
|
+
ssid,
|
|
624
|
+
protocolVersion,
|
|
625
|
+
outcome,
|
|
626
|
+
legacyDispatched,
|
|
627
|
+
connected,
|
|
628
|
+
currentSsid,
|
|
629
|
+
localIp,
|
|
630
|
+
error,
|
|
631
|
+
)
|
|
632
|
+
if (body == null) {
|
|
633
|
+
log("Dropping malformed wifi_forget_result without modern or legacy fields")
|
|
634
|
+
return
|
|
635
|
+
}
|
|
636
|
+
sendTypedMessage("wifi_forget_result", body)
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
@JvmStatic
|
|
640
|
+
fun sendSavedWifiNetworks(
|
|
641
|
+
requestId: String,
|
|
642
|
+
sid: String,
|
|
643
|
+
protocolVersion: Int,
|
|
644
|
+
outcome: String,
|
|
645
|
+
networks: List<String>,
|
|
646
|
+
error: String?,
|
|
647
|
+
) {
|
|
648
|
+
val body = HashMap<String, Any>()
|
|
649
|
+
body["requestId"] = requestId
|
|
650
|
+
body["sid"] = sid
|
|
651
|
+
body["protocolVersion"] = protocolVersion
|
|
652
|
+
body["outcome"] = outcome
|
|
653
|
+
body["networks"] = networks
|
|
654
|
+
if (error != null) body["error"] = error
|
|
655
|
+
sendTypedMessage("saved_wifi_networks", body)
|
|
656
|
+
}
|
|
657
|
+
|
|
595
658
|
/**
|
|
596
659
|
* Claim the WiFi scan-results store for a newly requested scan. Called by the
|
|
597
660
|
* SDK when it generates the scanId, BEFORE the scan command goes out: store
|
|
@@ -1784,6 +1784,9 @@ class DeviceManager {
|
|
|
1784
1784
|
sgc?.requestWifiScan(scanId)
|
|
1785
1785
|
}
|
|
1786
1786
|
|
|
1787
|
+
fun requestSavedWifiNetworks(requestId: String, sid: String): Boolean =
|
|
1788
|
+
sgc?.requestSavedWifiNetworks(requestId, sid) ?: false
|
|
1789
|
+
|
|
1787
1790
|
fun sendIncidentId(incidentId: String, apiBaseUrl: String? = null) {
|
|
1788
1791
|
Bridge.log("MAN: Sending incidentId to glasses for log upload: $incidentId")
|
|
1789
1792
|
sgc?.sendIncidentId(incidentId, apiBaseUrl)
|
|
@@ -1802,10 +1805,8 @@ class DeviceManager {
|
|
|
1802
1805
|
sgc?.sendWifiCredentials(ssid, password)
|
|
1803
1806
|
}
|
|
1804
1807
|
|
|
1805
|
-
fun forgetWifiNetwork(ssid: String)
|
|
1806
|
-
|
|
1807
|
-
sgc?.forgetWifiNetwork(ssid)
|
|
1808
|
-
}
|
|
1808
|
+
fun forgetWifiNetwork(ssid: String, requestId: String? = null, sid: String? = null): Boolean =
|
|
1809
|
+
sgc?.forgetWifiNetwork(ssid, requestId, sid) ?: false
|
|
1809
1810
|
|
|
1810
1811
|
fun setHotspotState(enabled: Boolean) {
|
|
1811
1812
|
Bridge.log("MAN: Setting glasses hotspot state: $enabled")
|
|
@@ -3,6 +3,6 @@ package com.mentra.bluetoothsdk
|
|
|
3
3
|
/** Generated from /changelogs. Do not edit directly. */
|
|
4
4
|
internal val GENERATED_RELEASE_CHANGELOGS: List<ReleaseChangelog> =
|
|
5
5
|
listOf(
|
|
6
|
-
ReleaseChangelog(version = "3.2.0", markdown = "This release is under active development. User-facing changes will be documented as they land
|
|
6
|
+
ReleaseChangelog(version = "3.2.0", markdown = "This release is under active development. User-facing changes will be documented as they land.\n\n- Breaking Bluetooth SDK change: `forgetWifiNetwork(ssid)` now returns `WifiForgetResult` instead of `WifiStatusChangeEvent`. Migrate status-only consumers to the semantic `outcome`; the requested network remains `ssid`, while optional post-command connectivity is reported by `connected`, `currentSsid`, and `localIp`."),
|
|
7
7
|
ReleaseChangelog(version = "3.1.0", markdown = "Software updates are more reliable, with clearer progress and recovery when the glasses restart.\n\n- MentraOS, Mentra Engine, the Bluetooth SDK, and the glasses client now share one coordinated release version.\n- Mentra Live updates can continue across APK, system, and firmware restarts without asking the user to start the same update again.\n- Bluetooth photo capture and transfer diagnostics are more precise and less disruptive to normal glasses traffic."),
|
|
8
8
|
)
|
|
@@ -3,9 +3,9 @@ package com.mentra.bluetoothsdk
|
|
|
3
3
|
/** Generated by release CI. Do not edit in a release checkout. */
|
|
4
4
|
internal object GeneratedReleaseMetadata {
|
|
5
5
|
const val FAMILY_BASE_VERSION: String = "3.2.0"
|
|
6
|
-
const val RELEASE_IDENTITY: String = "3.2.0-dev.
|
|
7
|
-
const val RELEASE_SET_ID: String = "mentra-3.2.0-dev.
|
|
8
|
-
const val SOURCE_COMMIT: String = "
|
|
9
|
-
const val OTA_MANIFEST_URL: String = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
10
|
-
const val OTA_MANIFEST_SHA256: String = "
|
|
6
|
+
const val RELEASE_IDENTITY: String = "3.2.0-dev.203"
|
|
7
|
+
const val RELEASE_SET_ID: String = "mentra-3.2.0-dev.203"
|
|
8
|
+
const val SOURCE_COMMIT: String = "7eb4deefff27d498c2b4562d628607f399aa1f40"
|
|
9
|
+
const val OTA_MANIFEST_URL: String = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.203.json"
|
|
10
|
+
const val OTA_MANIFEST_SHA256: String = "0ae026238c8e70970be4e181cc1ae73dd905418292edebddb610012f4f0a9b6f"
|
|
11
11
|
}
|