@mentra/bluetooth-sdk 0.1.5 → 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.
- package/README.md +29 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +15 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +34 -46
- package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +125 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +187 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +78 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +60 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +132 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +39 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +605 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +199 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +173 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +348 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +75 -0
- package/build/BluetoothSdk.types.d.ts +4 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +3 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +4 -4
- package/ios/Source/Audio/AudioModels.swift +159 -0
- package/ios/Source/Camera/CameraModels.swift +246 -0
- package/ios/Source/Connection/ScanSession.swift +27 -0
- package/ios/Source/DeviceStore.swift +1 -0
- package/ios/Source/Errors/BluetoothError.swift +19 -0
- package/ios/Source/Events/BluetoothEvents.swift +115 -0
- package/ios/Source/Internal/BluetoothAvailability.swift +58 -0
- package/ios/Source/Internal/ValueParsing.swift +90 -0
- package/ios/Source/MentraBluetoothSDK.swift +25 -2140
- package/ios/Source/Requests/DisplayRequests.swift +58 -0
- package/ios/Source/Status/DeviceStatus.swift +463 -0
- package/ios/Source/Status/RuntimeState.swift +350 -0
- package/ios/Source/Status/WifiHotspotStatus.swift +326 -0
- package/ios/Source/Streaming/StreamModels.swift +436 -0
- package/ios/Source/Types/DeviceModels.swift +134 -0
- package/ios/Source/sgcs/G2.swift +97 -36
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +4 -0
- package/src/_private/BluetoothSdkModule.ts +3 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1810
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
data class MentraBluetoothState(
|
|
4
|
+
val glasses: GlassesRuntimeState,
|
|
5
|
+
val sdk: PhoneSdkRuntimeState,
|
|
6
|
+
val scan: BluetoothScanState,
|
|
7
|
+
) {
|
|
8
|
+
internal companion object {
|
|
9
|
+
fun from(
|
|
10
|
+
glassesStatus: GlassesStatus,
|
|
11
|
+
bluetoothStatus: BluetoothStatus,
|
|
12
|
+
): MentraBluetoothState =
|
|
13
|
+
MentraBluetoothState(
|
|
14
|
+
glasses = GlassesRuntimeState.from(glassesStatus),
|
|
15
|
+
sdk = PhoneSdkRuntimeState.from(bluetoothStatus),
|
|
16
|
+
scan = BluetoothScanState.from(bluetoothStatus),
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
data class GlassesBatteryState(
|
|
22
|
+
val charging: Boolean,
|
|
23
|
+
val level: Int?,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
data class ConnectedGlassesInfo(
|
|
27
|
+
val appVersion: String?,
|
|
28
|
+
val bluetoothName: String?,
|
|
29
|
+
val buildNumber: String?,
|
|
30
|
+
val color: String?,
|
|
31
|
+
val deviceModel: DeviceModel?,
|
|
32
|
+
val firmwareVersion: String?,
|
|
33
|
+
val serialNumber: String?,
|
|
34
|
+
val style: String?,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
enum class FirmwareSource {
|
|
38
|
+
APP,
|
|
39
|
+
BES,
|
|
40
|
+
FIRMWARE,
|
|
41
|
+
MTK,
|
|
42
|
+
UNKNOWN,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
data class FirmwareInfo(
|
|
46
|
+
val appVersion: String?,
|
|
47
|
+
val buildNumber: String?,
|
|
48
|
+
val source: FirmwareSource,
|
|
49
|
+
val version: String?,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
data class SignalState(
|
|
53
|
+
val strengthDbm: Int?,
|
|
54
|
+
val updatedAt: Long?,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
sealed interface GlassesRuntimeState {
|
|
58
|
+
val connected: Boolean
|
|
59
|
+
val connection: GlassesConnectionState
|
|
60
|
+
val ready: Boolean
|
|
61
|
+
|
|
62
|
+
data class Disconnected(
|
|
63
|
+
override val connection: GlassesConnectionState = GlassesConnectionState.DISCONNECTED,
|
|
64
|
+
) : GlassesRuntimeState {
|
|
65
|
+
override val connected: Boolean = false
|
|
66
|
+
override val ready: Boolean = false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
data class Connected(
|
|
70
|
+
val battery: GlassesBatteryState,
|
|
71
|
+
override val connection: GlassesConnectionState,
|
|
72
|
+
val device: ConnectedGlassesInfo,
|
|
73
|
+
val firmware: FirmwareInfo,
|
|
74
|
+
val hotspot: HotspotStatus,
|
|
75
|
+
override val ready: Boolean,
|
|
76
|
+
val signal: SignalState,
|
|
77
|
+
val wifi: WifiStatus,
|
|
78
|
+
) : GlassesRuntimeState {
|
|
79
|
+
override val connected: Boolean = true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
companion object {
|
|
83
|
+
internal fun from(status: GlassesStatus): GlassesRuntimeState {
|
|
84
|
+
if (!status.connected && status.connectionState != GlassesConnectionState.CONNECTED) {
|
|
85
|
+
return Disconnected(connection = status.connectionState)
|
|
86
|
+
}
|
|
87
|
+
return Connected(
|
|
88
|
+
battery = batteryState(status),
|
|
89
|
+
connection = GlassesConnectionState.CONNECTED,
|
|
90
|
+
device = connectedGlassesInfo(status),
|
|
91
|
+
firmware = firmwareInfo(status),
|
|
92
|
+
hotspot = status.hotspot,
|
|
93
|
+
ready = status.fullyBooted,
|
|
94
|
+
signal = SignalState(
|
|
95
|
+
strengthDbm = status.signalStrength.takeUnless { it == -1 },
|
|
96
|
+
updatedAt = status.signalStrengthUpdatedAt.takeUnless { it <= 0L },
|
|
97
|
+
),
|
|
98
|
+
wifi = status.wifi,
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private fun batteryState(status: GlassesStatus): GlassesBatteryState =
|
|
103
|
+
GlassesBatteryState(
|
|
104
|
+
charging = status.charging,
|
|
105
|
+
level = status.batteryLevel.takeUnless { it < 0 },
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
private fun connectedGlassesInfo(status: GlassesStatus): ConnectedGlassesInfo =
|
|
109
|
+
ConnectedGlassesInfo(
|
|
110
|
+
appVersion = status.appVersion.nonBlank(),
|
|
111
|
+
bluetoothName = status.bluetoothName.nonBlank(),
|
|
112
|
+
buildNumber = status.buildNumber.nonBlank(),
|
|
113
|
+
color = status.color.nonBlank(),
|
|
114
|
+
deviceModel = status.deviceModel.takeIf { it.isNotBlank() }?.let(DeviceModel::fromDeviceType),
|
|
115
|
+
firmwareVersion = status.firmwareVersion.nonBlank(),
|
|
116
|
+
serialNumber = status.serialNumber.nonBlank(),
|
|
117
|
+
style = status.style.nonBlank(),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
private fun firmwareInfo(status: GlassesStatus): FirmwareInfo {
|
|
121
|
+
val sources =
|
|
122
|
+
listOf(
|
|
123
|
+
status.firmwareVersion to FirmwareSource.FIRMWARE,
|
|
124
|
+
status.besFirmwareVersion to FirmwareSource.BES,
|
|
125
|
+
status.mtkFirmwareVersion to FirmwareSource.MTK,
|
|
126
|
+
status.appVersion to FirmwareSource.APP,
|
|
127
|
+
)
|
|
128
|
+
val match = sources.firstOrNull { (version, _) -> version.isNotBlank() }
|
|
129
|
+
return FirmwareInfo(
|
|
130
|
+
appVersion = status.appVersion.nonBlank(),
|
|
131
|
+
buildNumber = status.buildNumber.nonBlank(),
|
|
132
|
+
source = match?.second ?: FirmwareSource.UNKNOWN,
|
|
133
|
+
version = match?.first?.nonBlank(),
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
enum class MicMode(val value: String) {
|
|
140
|
+
PHONE("phone"),
|
|
141
|
+
GLASSES("glasses"),
|
|
142
|
+
BLUETOOTH_CLASSIC("bluetoothClassic"),
|
|
143
|
+
BLUETOOTH("bluetooth");
|
|
144
|
+
|
|
145
|
+
internal companion object {
|
|
146
|
+
fun fromValue(value: String?): MicMode? =
|
|
147
|
+
values().firstOrNull { it.value == value }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
data class GalleryModeState(
|
|
152
|
+
val desired: GalleryMode,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
data class PhoneSdkRuntimeState(
|
|
156
|
+
val currentMic: MicMode?,
|
|
157
|
+
val defaultDevice: Device?,
|
|
158
|
+
val galleryMode: GalleryModeState,
|
|
159
|
+
val lastLog: List<String>,
|
|
160
|
+
val micRanking: List<MicMode>,
|
|
161
|
+
val otherBluetoothConnected: Boolean,
|
|
162
|
+
val searching: Boolean,
|
|
163
|
+
val searchingController: Boolean,
|
|
164
|
+
val systemMicUnavailable: Boolean,
|
|
165
|
+
val wifiScanResults: List<WifiScanResult>,
|
|
166
|
+
) {
|
|
167
|
+
internal companion object {
|
|
168
|
+
fun from(status: BluetoothStatus): PhoneSdkRuntimeState =
|
|
169
|
+
PhoneSdkRuntimeState(
|
|
170
|
+
currentMic = MicMode.fromValue(status.currentMic),
|
|
171
|
+
defaultDevice = status.defaultDevice,
|
|
172
|
+
galleryMode = GalleryModeState(if (status.galleryModeAuto) GalleryMode.AUTO else GalleryMode.MANUAL),
|
|
173
|
+
lastLog = status.lastLog,
|
|
174
|
+
micRanking = status.micRanking.mapNotNull { MicMode.fromValue(it) },
|
|
175
|
+
otherBluetoothConnected = status.otherBtConnected,
|
|
176
|
+
searching = status.searching,
|
|
177
|
+
searchingController = status.searchingController,
|
|
178
|
+
systemMicUnavailable = status.systemMicUnavailable,
|
|
179
|
+
wifiScanResults = status.wifiScanResults,
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
data class BluetoothScanState(
|
|
185
|
+
val active: Boolean,
|
|
186
|
+
val devices: List<Device>,
|
|
187
|
+
val searchingController: Boolean,
|
|
188
|
+
) {
|
|
189
|
+
internal companion object {
|
|
190
|
+
fun from(status: BluetoothStatus): BluetoothScanState =
|
|
191
|
+
BluetoothScanState(
|
|
192
|
+
active = status.searching,
|
|
193
|
+
devices = status.searchResults,
|
|
194
|
+
searchingController = status.searchingController,
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private fun String.nonBlank(): String? = takeIf { it.isNotBlank() }
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
sealed interface WifiStatus {
|
|
4
|
+
val state: String
|
|
5
|
+
|
|
6
|
+
fun toMap(): Map<String, Any> =
|
|
7
|
+
when (this) {
|
|
8
|
+
is Connected -> {
|
|
9
|
+
val values =
|
|
10
|
+
mutableMapOf<String, Any>(
|
|
11
|
+
"state" to state,
|
|
12
|
+
"ssid" to ssid,
|
|
13
|
+
)
|
|
14
|
+
localIp?.let { values["localIp"] = it }
|
|
15
|
+
values
|
|
16
|
+
}
|
|
17
|
+
Disconnected -> mapOf("state" to state)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fun toEventMap(): Map<String, Any> =
|
|
21
|
+
toMap() + mapOf("type" to "wifi_status_change")
|
|
22
|
+
|
|
23
|
+
object Disconnected : WifiStatus {
|
|
24
|
+
override val state: String = "disconnected"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
data class Connected(
|
|
28
|
+
val ssid: String,
|
|
29
|
+
val localIp: String?,
|
|
30
|
+
) : WifiStatus {
|
|
31
|
+
override val state: String = "connected"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
companion object {
|
|
35
|
+
internal fun fromMap(values: Map<String, Any>): WifiStatus? {
|
|
36
|
+
val wifiValues = (values["wifi"] as? Map<*, *>)?.stringKeyedMap() ?: values
|
|
37
|
+
return when (stringValue(wifiValues, "state")?.lowercase()) {
|
|
38
|
+
"connected" -> connectedFrom(wifiValues)
|
|
39
|
+
"disconnected" -> Disconnected
|
|
40
|
+
else -> null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
internal fun fromStoreMap(values: Map<String, Any>): WifiStatus? {
|
|
45
|
+
val connected = boolValue(values, "wifiConnected") ?: return null
|
|
46
|
+
return fromStoreFields(
|
|
47
|
+
connected = connected,
|
|
48
|
+
ssid = stringValue(values, "wifiSsid"),
|
|
49
|
+
localIp = stringValue(values, "wifiLocalIp"),
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
internal fun fromStoreFields(connected: Boolean, ssid: String?, localIp: String?): WifiStatus? {
|
|
54
|
+
if (!connected) return Disconnected
|
|
55
|
+
val nonEmptySsid = ssid?.trim()?.takeIf { it.isNotEmpty() }
|
|
56
|
+
val nonEmptyLocalIp = localIp?.trim()?.takeIf { it.isNotEmpty() }
|
|
57
|
+
return nonEmptySsid?.let { Connected(it, nonEmptyLocalIp) }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private fun connectedFrom(values: Map<String, Any>): WifiStatus? =
|
|
61
|
+
fromStoreFields(
|
|
62
|
+
connected = true,
|
|
63
|
+
ssid = stringValue(values, "ssid"),
|
|
64
|
+
localIp = stringValue(values, "localIp"),
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
data class WifiStatusEvent(
|
|
70
|
+
val status: WifiStatus,
|
|
71
|
+
) {
|
|
72
|
+
internal constructor(values: Map<String, Any>) : this(WifiStatus.fromMap(values) ?: WifiStatus.Disconnected)
|
|
73
|
+
internal constructor(connected: Boolean, ssid: String?, localIp: String?) : this(
|
|
74
|
+
WifiStatus.fromStoreFields(connected, ssid, localIp) ?: WifiStatus.Disconnected
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
val values: Map<String, Any> get() = status.toEventMap()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
sealed interface HotspotStatus {
|
|
81
|
+
val state: String
|
|
82
|
+
|
|
83
|
+
fun toMap(): Map<String, Any> =
|
|
84
|
+
when (this) {
|
|
85
|
+
is Enabled ->
|
|
86
|
+
mapOf(
|
|
87
|
+
"state" to state,
|
|
88
|
+
"ssid" to ssid,
|
|
89
|
+
"password" to password,
|
|
90
|
+
"localIp" to localIp,
|
|
91
|
+
)
|
|
92
|
+
Disabled -> mapOf("state" to state)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fun toEventMap(): Map<String, Any> =
|
|
96
|
+
toMap() + mapOf("type" to "hotspot_status_change")
|
|
97
|
+
|
|
98
|
+
object Disabled : HotspotStatus {
|
|
99
|
+
override val state: String = "disabled"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
data class Enabled(
|
|
103
|
+
val ssid: String,
|
|
104
|
+
val password: String,
|
|
105
|
+
val localIp: String,
|
|
106
|
+
) : HotspotStatus {
|
|
107
|
+
override val state: String = "enabled"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
companion object {
|
|
111
|
+
internal fun fromMap(values: Map<String, Any>): HotspotStatus? {
|
|
112
|
+
val hotspotValues = (values["hotspot"] as? Map<*, *>)?.stringKeyedMap() ?: values
|
|
113
|
+
return when (stringValue(hotspotValues, "state")?.lowercase()) {
|
|
114
|
+
"enabled" -> enabledFrom(hotspotValues)
|
|
115
|
+
"disabled" -> Disabled
|
|
116
|
+
else -> null
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
internal fun fromStoreMap(values: Map<String, Any>): HotspotStatus? {
|
|
121
|
+
val enabled = boolValue(values, "hotspotEnabled") ?: return null
|
|
122
|
+
return fromStoreFields(
|
|
123
|
+
enabled = enabled,
|
|
124
|
+
ssid = stringValue(values, "hotspotSsid"),
|
|
125
|
+
password = stringValue(values, "hotspotPassword"),
|
|
126
|
+
localIp = stringValue(values, "hotspotGatewayIp"),
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
internal fun fromStoreFields(
|
|
131
|
+
enabled: Boolean,
|
|
132
|
+
ssid: String?,
|
|
133
|
+
password: String?,
|
|
134
|
+
localIp: String?,
|
|
135
|
+
): HotspotStatus? {
|
|
136
|
+
if (!enabled) return Disabled
|
|
137
|
+
val nonEmptySsid = ssid?.trim()?.takeIf { it.isNotEmpty() }
|
|
138
|
+
val nonEmptyPassword = password?.trim()?.takeIf { it.isNotEmpty() }
|
|
139
|
+
val nonEmptyLocalIp = localIp?.trim()?.takeIf { it.isNotEmpty() }
|
|
140
|
+
return if (nonEmptySsid != null && nonEmptyPassword != null && nonEmptyLocalIp != null) {
|
|
141
|
+
Enabled(nonEmptySsid, nonEmptyPassword, nonEmptyLocalIp)
|
|
142
|
+
} else {
|
|
143
|
+
null
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private fun enabledFrom(values: Map<String, Any>): HotspotStatus? =
|
|
148
|
+
fromStoreFields(
|
|
149
|
+
enabled = true,
|
|
150
|
+
ssid = stringValue(values, "ssid"),
|
|
151
|
+
password = stringValue(values, "password"),
|
|
152
|
+
localIp = stringValue(values, "localIp"),
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
data class HotspotStatusEvent(
|
|
158
|
+
val status: HotspotStatus,
|
|
159
|
+
) {
|
|
160
|
+
internal constructor(values: Map<String, Any>) : this(HotspotStatus.fromMap(values) ?: HotspotStatus.Disabled)
|
|
161
|
+
internal constructor(enabled: Boolean, ssid: String?, password: String?, localIp: String?) : this(
|
|
162
|
+
HotspotStatus.fromStoreFields(enabled, ssid, password, localIp) ?: HotspotStatus.Disabled
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
val values: Map<String, Any> get() = status.toEventMap()
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
data class HotspotErrorEvent(
|
|
169
|
+
val values: Map<String, Any>,
|
|
170
|
+
) {
|
|
171
|
+
val message: String? get() = stringValue(values, "errorMessage")
|
|
172
|
+
val timestamp: Long? get() = longValue(values, "timestamp")
|
|
173
|
+
}
|