@mentra/bluetooth-sdk 0.1.4 → 0.1.6

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.
Files changed (72) hide show
  1. package/README.md +61 -42
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +20 -12
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +38 -12
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +2 -1
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +45 -51
  6. package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +125 -0
  7. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +187 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +60 -0
  10. package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +132 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +39 -0
  12. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +605 -0
  13. package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +199 -0
  14. package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +173 -0
  15. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +348 -0
  16. package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +75 -0
  17. package/build/BluetoothSdk.types.d.ts +24 -7
  18. package/build/BluetoothSdk.types.d.ts.map +1 -1
  19. package/build/BluetoothSdk.types.js.map +1 -1
  20. package/build/_internal.d.ts +1 -1
  21. package/build/_internal.js +1 -1
  22. package/build/_internal.js.map +1 -1
  23. package/build/_private/BluetoothSdkModule.d.ts +5 -4
  24. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  25. package/build/_private/BluetoothSdkModule.js +4 -1
  26. package/build/_private/BluetoothSdkModule.js.map +1 -1
  27. package/build/index.d.ts +4 -3
  28. package/build/index.d.ts.map +1 -1
  29. package/build/index.js +81 -2
  30. package/build/index.js.map +1 -1
  31. package/build/react/index.d.ts +2 -2
  32. package/build/react/index.d.ts.map +1 -1
  33. package/build/react/index.js +1 -2
  34. package/build/react/index.js.map +1 -1
  35. package/build/react/useBluetoothStatus.js +1 -1
  36. package/build/react/useBluetoothStatus.js.map +1 -1
  37. package/build/react/useGlassesConnection.d.ts.map +1 -1
  38. package/build/react/useGlassesConnection.js +13 -0
  39. package/build/react/useGlassesConnection.js.map +1 -1
  40. package/build/react/useMentraBluetooth.d.ts +98 -0
  41. package/build/react/useMentraBluetooth.d.ts.map +1 -0
  42. package/build/react/useMentraBluetooth.js +156 -0
  43. package/build/react/useMentraBluetooth.js.map +1 -0
  44. package/ios/BluetoothSdkModule.swift +9 -9
  45. package/ios/Source/Audio/AudioModels.swift +159 -0
  46. package/ios/Source/Bridge.swift +42 -8
  47. package/ios/Source/Camera/CameraModels.swift +246 -0
  48. package/ios/Source/Connection/ScanSession.swift +27 -0
  49. package/ios/Source/DeviceManager.swift +0 -1
  50. package/ios/Source/DeviceStore.swift +2 -1
  51. package/ios/Source/Errors/BluetoothError.swift +19 -0
  52. package/ios/Source/Events/BluetoothEvents.swift +115 -0
  53. package/ios/Source/Internal/BluetoothAvailability.swift +58 -0
  54. package/ios/Source/Internal/ValueParsing.swift +90 -0
  55. package/ios/Source/MentraBluetoothSDK.swift +32 -2040
  56. package/ios/Source/Requests/DisplayRequests.swift +58 -0
  57. package/ios/Source/Status/DeviceStatus.swift +463 -0
  58. package/ios/Source/Status/RuntimeState.swift +350 -0
  59. package/ios/Source/Status/WifiHotspotStatus.swift +326 -0
  60. package/ios/Source/Streaming/StreamModels.swift +436 -0
  61. package/ios/Source/Types/DeviceModels.swift +134 -0
  62. package/ios/Source/sgcs/G2.swift +97 -36
  63. package/package.json +1 -1
  64. package/src/BluetoothSdk.types.ts +24 -7
  65. package/src/_internal.ts +1 -1
  66. package/src/_private/BluetoothSdkModule.ts +10 -5
  67. package/src/index.ts +93 -4
  68. package/src/react/index.ts +13 -11
  69. package/src/react/useBluetoothStatus.ts +1 -1
  70. package/src/react/useGlassesConnection.ts +15 -0
  71. package/src/react/useMentraBluetooth.ts +307 -0
  72. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1725
@@ -0,0 +1,187 @@
1
+ package com.mentra.bluetoothsdk
2
+
3
+ enum class GalleryMode {
4
+ AUTO,
5
+ MANUAL,
6
+ }
7
+
8
+ enum class PhotoSize(val value: String) {
9
+ SMALL("small"),
10
+ MEDIUM("medium"),
11
+ LARGE("large"),
12
+ FULL("full");
13
+
14
+ companion object {
15
+ @JvmStatic
16
+ fun fromValue(value: String?): PhotoSize =
17
+ values().firstOrNull { it.value == value } ?: MEDIUM
18
+ }
19
+ }
20
+
21
+ enum class ButtonPhotoSize(val value: String) {
22
+ SMALL("small"),
23
+ MEDIUM("medium"),
24
+ LARGE("large");
25
+
26
+ companion object {
27
+ @JvmStatic
28
+ fun fromValue(value: String?): ButtonPhotoSize =
29
+ values().firstOrNull { it.value == value } ?: MEDIUM
30
+ }
31
+ }
32
+
33
+ enum class PhotoCompression(val value: String) {
34
+ NONE("none"),
35
+ MEDIUM("medium"),
36
+ HEAVY("heavy");
37
+
38
+ companion object {
39
+ @JvmStatic
40
+ fun fromValue(value: String?): PhotoCompression =
41
+ values().firstOrNull { it.value == value } ?: NONE
42
+ }
43
+ }
44
+
45
+ data class ButtonPhotoSettings(
46
+ val size: ButtonPhotoSize,
47
+ )
48
+
49
+ data class ButtonVideoRecordingSettings(
50
+ val width: Int,
51
+ val height: Int,
52
+ val frameRate: Int,
53
+ )
54
+
55
+ enum class CameraFov(val fov: Int, val roiPosition: Int) {
56
+ STANDARD(118, 0),
57
+ WIDE(118, 0),
58
+ }
59
+
60
+ data class PhotoRequest @JvmOverloads constructor(
61
+ val requestId: String,
62
+ val appId: String,
63
+ val size: PhotoSize,
64
+ val webhookUrl: String,
65
+ val authToken: String? = null,
66
+ val compress: PhotoCompression = PhotoCompression.MEDIUM,
67
+ val sound: Boolean = true,
68
+ )
69
+
70
+ enum class RgbLedAction(val value: String) {
71
+ ON("on"),
72
+ OFF("off");
73
+
74
+ companion object {
75
+ @JvmStatic
76
+ fun fromValue(value: String?): RgbLedAction =
77
+ values().firstOrNull { it.value == value } ?: OFF
78
+ }
79
+ }
80
+
81
+ enum class RgbLedColor(val value: String) {
82
+ RED("red"),
83
+ GREEN("green"),
84
+ BLUE("blue"),
85
+ ORANGE("orange"),
86
+ WHITE("white");
87
+
88
+ companion object {
89
+ @JvmStatic
90
+ fun fromValue(value: String?): RgbLedColor? =
91
+ values().firstOrNull { it.value == value }
92
+ }
93
+ }
94
+
95
+ data class RgbLedRequest @JvmOverloads constructor(
96
+ val requestId: String,
97
+ val packageName: String?,
98
+ val action: RgbLedAction,
99
+ val color: RgbLedColor?,
100
+ val onDurationMs: Int,
101
+ val offDurationMs: Int,
102
+ val count: Int,
103
+ )
104
+
105
+ data class VideoRecordingRequest(
106
+ val requestId: String,
107
+ val save: Boolean,
108
+ val sound: Boolean,
109
+ )
110
+
111
+ data class GalleryStatusEvent(
112
+ val values: Map<String, Any>,
113
+ )
114
+
115
+ sealed interface PhotoResponse {
116
+ val state: String
117
+ val requestId: String
118
+ val timestamp: Long
119
+
120
+ fun toMap(): Map<String, Any> =
121
+ when (this) {
122
+ is Success -> mapOf(
123
+ "state" to state,
124
+ "requestId" to requestId,
125
+ "uploadUrl" to uploadUrl,
126
+ "timestamp" to timestamp,
127
+ )
128
+
129
+ is Error -> mutableMapOf<String, Any>(
130
+ "state" to state,
131
+ "requestId" to requestId,
132
+ "timestamp" to timestamp,
133
+ "errorMessage" to errorMessage,
134
+ ).apply {
135
+ if (!errorCode.isNullOrBlank()) {
136
+ this["errorCode"] = errorCode
137
+ }
138
+ }
139
+ }
140
+
141
+ fun toEventMap(): Map<String, Any> = toMap() + mapOf("type" to "photo_response")
142
+
143
+ data class Success(
144
+ override val requestId: String,
145
+ val uploadUrl: String,
146
+ override val timestamp: Long,
147
+ ) : PhotoResponse {
148
+ override val state: String = "success"
149
+ }
150
+
151
+ data class Error(
152
+ override val requestId: String,
153
+ val errorCode: String?,
154
+ val errorMessage: String,
155
+ override val timestamp: Long,
156
+ ) : PhotoResponse {
157
+ override val state: String = "error"
158
+ }
159
+
160
+ companion object {
161
+ fun fromMap(values: Map<String, Any>): PhotoResponse {
162
+ val requestId = stringValue(values, "requestId").orEmpty()
163
+ val timestamp = longValue(values, "timestamp") ?: System.currentTimeMillis()
164
+ val state = stringValue(values, "state")?.lowercase()
165
+ return if (state == "success") {
166
+ val uploadUrl = stringValue(values, "uploadUrl").orEmpty()
167
+ Success(requestId = requestId, uploadUrl = uploadUrl, timestamp = timestamp)
168
+ } else {
169
+ Error(
170
+ requestId = requestId,
171
+ errorCode = stringValue(values, "errorCode"),
172
+ errorMessage = stringValue(values, "errorMessage") ?: "Unknown photo error",
173
+ timestamp = timestamp,
174
+ )
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ data class PhotoResponseEvent(
181
+ val response: PhotoResponse,
182
+ ) {
183
+ constructor(values: Map<String, Any>) : this(PhotoResponse.fromMap(values))
184
+
185
+ val requestId: String get() = response.requestId
186
+ val values: Map<String, Any> get() = response.toEventMap()
187
+ }
@@ -0,0 +1,78 @@
1
+ package com.mentra.bluetoothsdk
2
+
3
+ import com.mentra.bluetoothsdk.utils.ConnTypes
4
+ import java.util.concurrent.atomic.AtomicBoolean
5
+
6
+ enum class GlassesConnectionState(val value: String) {
7
+ DISCONNECTED(ConnTypes.DISCONNECTED),
8
+ SCANNING(ConnTypes.SCANNING),
9
+ CONNECTING(ConnTypes.CONNECTING),
10
+ BONDING(ConnTypes.BONDING),
11
+ CONNECTED(ConnTypes.CONNECTED);
12
+
13
+ val isConnected: Boolean
14
+ get() = this == CONNECTED
15
+
16
+ val isBusy: Boolean
17
+ get() = this == SCANNING || this == CONNECTING || this == BONDING
18
+
19
+ internal fun toStatusMap(
20
+ connected: Boolean,
21
+ fullyBooted: Boolean,
22
+ ): Map<String, Any> =
23
+ when {
24
+ this == CONNECTED || connected || fullyBooted ->
25
+ mapOf("state" to "connected", "fullyBooted" to fullyBooted)
26
+ this == SCANNING -> mapOf("state" to "scanning")
27
+ this == CONNECTING -> mapOf("state" to "connecting")
28
+ this == BONDING -> mapOf("state" to "bonding")
29
+ else -> mapOf("state" to "disconnected")
30
+ }
31
+
32
+ companion object {
33
+ @JvmStatic
34
+ fun fromValue(value: String?): GlassesConnectionState =
35
+ optionalFromValue(value) ?: DISCONNECTED
36
+
37
+ internal fun optionalFromValue(value: String?): GlassesConnectionState? {
38
+ val normalized = value?.trim()?.takeIf { it.isNotEmpty() } ?: return null
39
+ return values().firstOrNull { it.value.equals(normalized, ignoreCase = true) }
40
+ }
41
+ }
42
+ }
43
+
44
+ data class BluetoothError(
45
+ val code: String,
46
+ val message: String,
47
+ val cause: Throwable? = null,
48
+ )
49
+
50
+ enum class ScanStopReason {
51
+ COMPLETED,
52
+ CANCELLED,
53
+ ERROR,
54
+ }
55
+
56
+ interface ScanCallback {
57
+ fun onResults(devices: List<Device>) {}
58
+ fun onComplete(devices: List<Device>) {}
59
+ fun onError(error: BluetoothError) {}
60
+ }
61
+
62
+ abstract class MentraBluetoothScanCallback : ScanCallback
63
+
64
+ class ScanSession internal constructor(
65
+ private val stopAction: () -> Unit,
66
+ ) {
67
+ private val stopped = AtomicBoolean(false)
68
+
69
+ fun stop() {
70
+ if (stopped.compareAndSet(false, true)) {
71
+ stopAction()
72
+ }
73
+ }
74
+
75
+ internal fun markStopped() {
76
+ stopped.set(true)
77
+ }
78
+ }
@@ -0,0 +1,60 @@
1
+ package com.mentra.bluetoothsdk
2
+
3
+ data class ButtonPressEvent(
4
+ val buttonId: String,
5
+ val pressType: String,
6
+ val timestamp: Long? = null,
7
+ )
8
+
9
+ data class TouchEvent(
10
+ val values: Map<String, Any>,
11
+ ) {
12
+ val deviceModel: String? get() = stringValue(values, "deviceModel")
13
+ val gestureName: String? get() = stringValue(values, "gestureName")
14
+ val timestamp: Long? get() = longValue(values, "timestamp")
15
+ val isSwipe: Boolean get() = gestureName?.contains("swipe", ignoreCase = true) == true
16
+ }
17
+
18
+ data class SwipeEvent(
19
+ val values: Map<String, Any>,
20
+ ) {
21
+ val deviceModel: String? get() = stringValue(values, "deviceModel")
22
+ val gestureName: String? get() = stringValue(values, "gestureName")
23
+ val timestamp: Long? get() = longValue(values, "timestamp")
24
+ }
25
+
26
+ data class BatteryStatusEvent(
27
+ val level: Int?,
28
+ val charging: Boolean?,
29
+ val values: Map<String, Any>,
30
+ )
31
+
32
+ interface MentraBluetoothSdkListener {
33
+ fun onStateChanged(state: MentraBluetoothState) {}
34
+ fun onGlassesChanged(glasses: GlassesRuntimeState) {}
35
+ fun onSdkStateChanged(sdk: PhoneSdkRuntimeState) {}
36
+ fun onScanChanged(scan: BluetoothScanState) {}
37
+ fun onDeviceDiscovered(device: Device) {}
38
+ fun onScanStopped(reason: ScanStopReason) {}
39
+ fun onButtonPress(event: ButtonPressEvent) {}
40
+ fun onTouch(event: TouchEvent) {}
41
+ fun onSwipe(event: SwipeEvent) {}
42
+ fun onHeadUpChanged(headUp: Boolean) {}
43
+ fun onBatteryStatus(event: BatteryStatusEvent) {}
44
+ fun onWifiStatusChanged(event: WifiStatusEvent) {}
45
+ fun onHotspotStatusChanged(event: HotspotStatusEvent) {}
46
+ fun onHotspotError(event: HotspotErrorEvent) {}
47
+ fun onGalleryStatus(event: GalleryStatusEvent) {}
48
+ fun onPhotoResponse(event: PhotoResponseEvent) {}
49
+ fun onStreamStatus(event: StreamStatusEvent) {}
50
+ fun onKeepAliveAck(event: KeepAliveAckEvent) {}
51
+ fun onMicPcm(event: MicPcmEvent) {}
52
+ fun onMicLc3(event: MicLc3Event) {}
53
+ fun onLocalTranscription(event: LocalTranscriptionEvent) {}
54
+ fun onDefaultDeviceChanged(device: Device?) {}
55
+ fun onLog(message: String) {}
56
+ fun onError(error: BluetoothError) {}
57
+ fun onRawEvent(eventName: String, values: Map<String, Any>) {}
58
+ }
59
+
60
+ abstract class MentraBluetoothSdkCallback : MentraBluetoothSdkListener
@@ -0,0 +1,132 @@
1
+ package com.mentra.bluetoothsdk
2
+
3
+ internal fun numberValue(
4
+ values: Map<String, Any>,
5
+ vararg keys: String,
6
+ ): Int? =
7
+ keys.firstNotNullOfOrNull { key ->
8
+ (values[key] as? Number)?.toInt()
9
+ }
10
+
11
+ internal fun stringValue(
12
+ values: Map<String, Any>,
13
+ vararg keys: String,
14
+ ): String? =
15
+ keys.firstNotNullOfOrNull { key ->
16
+ values[key]?.let { it as? String }
17
+ }
18
+
19
+ internal fun boolValue(
20
+ values: Map<String, Any>,
21
+ vararg keys: String,
22
+ ): Boolean? =
23
+ keys.firstNotNullOfOrNull { key ->
24
+ values[key] as? Boolean
25
+ }
26
+
27
+ internal fun longValue(
28
+ values: Map<String, Any>,
29
+ vararg keys: String,
30
+ ): Long? =
31
+ keys.firstNotNullOfOrNull { key ->
32
+ (values[key] as? Number)?.toLong()
33
+ }
34
+
35
+ internal fun hasAnyKey(
36
+ values: Map<String, Any>,
37
+ vararg keys: String,
38
+ ): Boolean = keys.any(values::containsKey)
39
+
40
+ internal fun Map<*, *>.stringKeyedMap(): Map<String, Any> =
41
+ entries.mapNotNull { (key, value) ->
42
+ val stringKey = key as? String ?: return@mapNotNull null
43
+ val anyValue = value ?: return@mapNotNull null
44
+ stringKey to anyValue
45
+ }.toMap()
46
+
47
+ internal fun optionalNumberValue(
48
+ values: Map<String, Any>,
49
+ vararg keys: String,
50
+ ): Int? =
51
+ if (hasAnyKey(values, *keys)) {
52
+ numberValue(values, *keys)
53
+ } else {
54
+ null
55
+ }
56
+
57
+ internal fun optionalLongValue(
58
+ values: Map<String, Any>,
59
+ vararg keys: String,
60
+ ): Long? =
61
+ if (hasAnyKey(values, *keys)) {
62
+ longValue(values, *keys)
63
+ } else {
64
+ null
65
+ }
66
+
67
+ internal fun optionalStringValue(
68
+ values: Map<String, Any>,
69
+ vararg keys: String,
70
+ ): String? =
71
+ if (hasAnyKey(values, *keys)) {
72
+ stringValue(values, *keys)
73
+ } else {
74
+ null
75
+ }
76
+
77
+ internal fun optionalBoolValue(
78
+ values: Map<String, Any>,
79
+ vararg keys: String,
80
+ ): Boolean? =
81
+ if (hasAnyKey(values, *keys)) {
82
+ boolValue(values, *keys)
83
+ } else {
84
+ null
85
+ }
86
+
87
+ internal fun stringListValue(
88
+ values: Map<String, Any>,
89
+ key: String,
90
+ ): List<String> = (values[key] as? List<*>)?.mapNotNull { it as? String } ?: emptyList()
91
+
92
+ internal fun optionalStringListValue(
93
+ values: Map<String, Any>,
94
+ key: String,
95
+ ): List<String>? =
96
+ if (values.containsKey(key)) {
97
+ stringListValue(values, key)
98
+ } else {
99
+ null
100
+ }
101
+
102
+ internal fun mapListValue(
103
+ values: Map<String, Any>,
104
+ key: String,
105
+ ): List<Map<String, Any>> =
106
+ (values[key] as? List<*>)?.mapNotNull(::stringMapValue) ?: emptyList()
107
+
108
+ internal fun optionalMapListValue(
109
+ values: Map<String, Any>,
110
+ key: String,
111
+ ): List<Map<String, Any>>? =
112
+ if (values.containsKey(key)) {
113
+ mapListValue(values, key)
114
+ } else {
115
+ null
116
+ }
117
+
118
+ internal fun stringMapValue(value: Any?): Map<String, Any>? =
119
+ (value as? Map<*, *>)?.entries?.mapNotNull { (key, mapValue) ->
120
+ val stringKey = key as? String ?: return@mapNotNull null
121
+ val nonNullValue = mapValue ?: return@mapNotNull null
122
+ stringKey to nonNullValue
123
+ }?.toMap()
124
+
125
+ internal fun MutableMap<String, Any>.putIfNotNull(
126
+ key: String,
127
+ value: Any?,
128
+ ) {
129
+ if (value != null) {
130
+ put(key, value)
131
+ }
132
+ }
@@ -0,0 +1,39 @@
1
+ package com.mentra.bluetoothsdk
2
+
3
+ data class DisplayTextRequest(
4
+ val text: String,
5
+ val x: Int = 0,
6
+ val y: Int = 0,
7
+ val size: Int = 24,
8
+ ) {
9
+ fun toMap(): Map<String, Any> =
10
+ mapOf(
11
+ "text" to text,
12
+ "x" to x,
13
+ "y" to y,
14
+ "size" to size,
15
+ )
16
+ }
17
+
18
+ internal data class DisplayEventRequest(
19
+ val values: Map<String, Any>,
20
+ ) {
21
+ fun toMap(): Map<String, Any> = values
22
+ }
23
+
24
+ data class DashboardPositionRequest(
25
+ val height: Int,
26
+ val depth: Int,
27
+ )
28
+
29
+ internal data class DashboardMenuItem(
30
+ val title: String,
31
+ val packageName: String,
32
+ val values: Map<String, Any> = emptyMap(),
33
+ ) {
34
+ fun toMap(): Map<String, Any> =
35
+ values + mapOf(
36
+ "title" to title,
37
+ "packageName" to packageName,
38
+ )
39
+ }