- Scan for a supported glasses model.
- Connect to a discovered
Deviceor an app-restored default device. - Read typed lifecycle state through the public surface for your platform.
- Subscribe to typed hardware events.
- Send camera, stream, audio, Wi-Fi, hotspot, LED, and settings commands for Mentra Live.
Packages And Imports
| Platform | Install package | Import |
|---|---|---|
| Android | com.mentraglass:bluetooth-sdk | import com.mentra.bluetoothsdk.* |
| iOS | MentraBluetoothSDK Swift package | import MentraBluetoothSDK |
| React Native / Expo | @mentra/bluetooth-sdk | import BluetoothSdk, {DeviceModels} from '@mentra/bluetooth-sdk' |
| React Native hooks | @mentra/bluetooth-sdk | import {useMentraBluetooth} from '@mentra/bluetooth-sdk/react' |
| React Native photo receiver | @mentra/bluetooth-sdk | import MentraPhotoReceiver from '@mentra/bluetooth-sdk/photo-receiver' |
Lifecycle
- React Native
- Android
- iOS
Connection
- React Native
- Android
- iOS
Device returned by SDK scan callbacks. If your app wants connectDefault() to work after restart, persist a small default-device record in app storage and restore it with setDefaultDevice() before calling connectDefault().
Use scan() for user-facing device pickers. The progressive result callback is for UI: render the nearby-device list every time it changes during scanning. The returned final result is for control flow: after the timeout/completion, choose a device from the last list and connect. In multi-device environments, do not auto-connect to the first nearby glasses; present an explicit picker.
Device Identity
Device.id is the stable app-facing key for a scan result, within the limits of the platform identifier available to the SDK. Use it as a list key, selected-device key, and persisted default-device key.
Do not parse id for model, name, or address information. Use the typed fields instead:
| Field | Meaning |
|---|---|
model | Glasses family reported by the SDK, expected to be Mentra Live in these flows. |
name | Bluetooth name reported during scan. |
address / identifier | Platform device handle when available. Android commonly provides a Bluetooth address. iOS commonly provides a CoreBluetooth identifier. React Native exposes this value as address. |
rssi | Optional signal strength from scan results. It may be undefined at first discovery and appear in a later scan update when the platform reports RSSI metadata. |
model:name key.
Do not require rssi for picker rows. Use SDK-provided stable discovery order by default, and treat RSSI as supplemental signal-strength metadata when it is present.
Status
- React Native
- Android
- iOS
mentra.glasses.connection as a discriminated union:mentra.glasses.connection.state for link-layer progress. fullyBooted only exists when state === 'connected'. The hook exposes the React app state as glasses, sdk, and scan.| Field | Meaning |
|---|---|
glasses | Connected-glasses runtime state. Includes connection/readiness, connected device identity, battery, firmware, Wi-Fi, hotspot, and signal metadata when connected. |
sdk | Phone-side SDK runtime state. Includes default device, gallery mode, microphone route, mic ranking, Wi-Fi scan results, scan activity, system mic availability, other Bluetooth audio status, and recent SDK log lines. |
scan | User-facing scan state. Includes whether a scan is active, whether controller scanning is active, and the stable-order discovered Device[] list. |
glasses.connected / mentra.glasses.connected before reading connected-only fields. Native Android uses GlassesRuntimeState.Connected; native iOS uses GlassesRuntimeState.connected(...); React Native exposes mentra.glasses with the same grouped concepts in React-friendly objects.
React Native Public Surface
These are the supported React Native app developer entrypoints:| Area | Methods |
|---|---|
| Status and subscriptions | useMentraBluetooth, useBluetoothScan, and useBluetoothEvent for React components; addListener is available as the lower-level non-React subscription API |
| Default device | getDefaultDevice, setDefaultDevice, clearDefaultDevice |
| Connection | scan, startScan, stopScan, connect, connectDefault, cancelConnectionAttempt, disconnect, forget |
| Wi-Fi and hotspot | requestWifiScan, sendWifiCredentials, forgetWifiNetwork, setHotspotState |
| Camera and gallery | requestPhoto, queryGalleryStatus, setGalleryModeEnabled, setButtonPhotoSettings, setButtonVideoRecordingSettings, setButtonCameraLed, setButtonMaxRecordingTime, setCameraFov, startVideoRecording, stopVideoRecording |
| Streaming | startStream, stopStream |
| Audio | setMicState, setVoiceActivityDetectionEnabled, setPreferredMic, setOwnAppAudioPlaying, getGlassesMediaVolume, setGlassesMediaVolume |
| LED, version, and OTA | rgbLedControl, requestVersionInfo, checkForOtaUpdate, startOtaUpdate, retryOtaVersionCheck |
DeviceModels, isConnectedGlassesConnectionStatus, isReadyGlassesConnectionStatus, isBusyGlassesConnectionStatus, isConnectedWifiStatus, and isEnabledHotspotStatus. The React subpath exports useMentraBluetooth, useBluetoothScan, and useBluetoothEvent.
For React Native status UI, use useMentraBluetooth() from @mentra/bluetooth-sdk/react. It returns mentra.glasses, mentra.sdk, and mentra.scan for connection, battery, Wi-Fi, hotspot, scan, and SDK runtime state.
Important defaults:
scan(model, options)reports progressive results throughoptions.onResults, resolves with the final matchingDevice[], and times out after 15 seconds unlessoptions.timeoutMsis set.connectandconnectDefaultdefaultsaveAsDefaultandcancelExistingConnectionAttempttotrue.setMicState(enabled)defaults to glasses microphone on, transcript events off, and LC3 events off. Microphone audio events are continuous while capture is enabled. UsesetVoiceActivityDetectionEnabled(...)for glasses-side Voice Activity Detection;voice_activity_detection_statusreports whether it is enabled, andspeaking_statusreports speaking/not-speaking when supported. Microphone audio events include the latestvoiceActivityDetectionEnabledvalue.requestPhoto({authToken, ...})omits theAuthorizationheader whenauthTokenisnullor empty.requestPhoto({exposureTimeNs, ...})uses auto exposure whenexposureTimeNsis omitted ornull; pass a positive nanosecond value for one-shot manual exposure.requestPhoto({exposureTimeNs, iso, ...})usesisoonly whenexposureTimeNsenables one-shot manual exposure. Omitiso/ passnullfor auto ISO selection.- Request/response commands now resolve from the glasses response rather than only updating local SDK state. React Native returns success-only values for commands whose raw events can also carry errors:
requestPhoto(...)returns terminalPhotoSuccessResponseEventafter capture and delivery finish,startVideoRecording(...)returnsVideoRecordingStartedStatusEvent,stopVideoRecording(...)returnsVideoRecordingStoppedStatusEvent, andrgbLedControl(...)returnsRgbLedControlSuccessResponseEvent. The corresponding raw events still include error variants for listeners. setCameraFov({fov, roiPosition})clampsfovto 62-118 degrees and acceptsroiPositionas"center","bottom", or"top". You can also callsetCameraFov({preset: "narrow" | "standard" | "wide"}); presets map to 82, 102, and 118 degrees with center ROI. On Mentra Live this persists the setting, restarts the camera, and resolves withCameraFovResultonly after the ASG client reports the setting was applied to camera hardware after the restart cooldown. The promise rejects if the glasses report an error, persist the setting without hardware application, or time out. Treat FOV as a framing/ROI control; output resolution and effective detail can vary by capture path, firmware, and camera mode.startStreamoptionalvideoandaudioconfigs are omitted unless supplied, so the connected glasses use their model defaults. The SDK sends stream keep-alives automatically and reports timeout/error state throughstream_status.- Photo capture, video recording, and streaming always enable the camera light as a privacy indicator.
Promise Results And Errors
One-shot commands resolve only after the matching ASG client response arrives. They reject/throw when the ASG response is an error, when the SDK cannot send the command, when an incompatible command is already in flight, or when the matching response times out. Keep listeners for streaming progress and status; use the returned value for the one-shot decision. React Native exposes the narrowest return types for clear branching afterawait. Android and iOS async APIs throw BluetoothException / BluetoothError for the same error cases, so successful calls return the success payload even when the native event struct can also represent raw listener errors.
| API | Resolves With | Rejection / Throw Criteria |
|---|---|---|
requestWifiScan() | Final WifiSearchResult[] from wifi_scan_result where scanComplete: true, including [] when no networks are found. | Scan response timeout, send failure, or another Wi-Fi scan already in flight. |
sendWifiCredentials(ssid, password) | WifiStatusChangeEvent whose state is connected for the requested SSID. | Wi-Fi disconnect/error state, timeout, send failure, or another Wi-Fi status command in flight. |
forgetWifiNetwork(ssid) | WifiStatusChangeEvent once the requested SSID is no longer connected. | Timeout, send failure, or another Wi-Fi status command in flight. |
setHotspotState(enabled) | HotspotStatusChangeEvent matching the requested enabled/disabled state. | hotspot_error, timeout, send failure, or another hotspot command in flight. |
requestVersionInfo() | VersionInfoResult from the ASG version_info response. | Timeout, send failure, or another version query in flight. |
| Gallery/button settings | SettingsAckSuccessEvent with a non-failure status such as applied or ready; the SDK local settings store updates only after this ASG ack resolves. | settings_ack.status of error, failed, failure, or rejected; timeout; or send failure. |
setCameraFov(...) | CameraFovResult with fov, roiPosition, requestId, and timestamp after the ASG client reports the setting was applied to camera hardware after the restart cooldown; the SDK local FOV setting updates only after this result resolves. | Glasses-side FOV/settings error, persist-only/no hardware-application ack, timeout, or send failure. |
queryGalleryStatus() | GalleryStatusEvent, including cameraBusy and optional cameraBusyReason. | Timeout, send failure, or another gallery status query in flight. |
requestPhoto(...) | Terminal response after capture and delivery finish: React Native PhotoSuccessResponseEvent; Android/iOS successful PhotoResponseEvent. The success includes uploadUrl, and may include webhook-returned photoUrl, statusUrl, contentType, or fileSizeBytes. Progress follows through photo_status. | Raw photo_response.state === "error" from glasses/phone, fallback upload failure, terminal response timeout, or send failure. |
startVideoRecording(...) | React Native VideoRecordingStartedStatusEvent with status: "recording_started"; Android/iOS successful VideoRecordingStatusEvent with status: "recording_started". | success: false statuses such as already_recording, timeout, send failure, or duplicate request ID. |
stopVideoRecording(requestId) | React Native VideoRecordingStoppedStatusEvent with status: "recording_stopped"; Android/iOS successful VideoRecordingStatusEvent with status: "recording_stopped". | success: false statuses such as not_recording, timeout, send failure, or duplicate request ID. |
startStream(...) | StreamStatusEvent for the requested stream once status is streaming. | stream_status error/reconnect failure, timeout, or send failure. |
stopStream() | StreamStatusEvent once status is stopped; an already-stopped / not-streaming glasses reply resolves as a normalized stopped event. | Real stop error, timeout, send failure, or another stream stop in flight. |
rgbLedControl(...) | React Native RgbLedControlSuccessResponseEvent; Android/iOS successful RgbLedControlResponseEvent. | Raw rgb_led_control_response.state === "error", timeout, or send failure. |
checkForOtaUpdate() / retryOtaVersionCheck() | OtaQueryResult: either ota_update_available or the current ota_status. | Command response timeout, send failure, or another OTA query in flight. A returned ota_status.status === "failed" is a glasses OTA state to show in UI, not a command transport rejection. |
startOtaUpdate() | OtaStartAckEvent from ota_start_ack. | Start-ack timeout, send failure, or another OTA start in flight. |
Gallery Mode
Mentra Live has a gallery mode for the right action button. When gallery mode is enabled, a short press takes a photo, a long press starts video recording, and a short press stops the active video recording. Button and touch events are still reported to the SDK. UsesetGalleryModeEnabled(true) to enable local button capture, and setGalleryModeEnabled(false) to report button events without triggering local gallery capture while the glasses are connected. These setting calls return SettingsAckSuccessEvent from the ASG client and reject on failure statuses. Raw settings_ack listeners still receive SettingsAckEvent because listener events can include both success and failure statuses. The SDK can also configure the capture settings used by gallery mode:
| Setting | API |
|---|---|
| Photo size | setButtonPhotoSettings(...) |
| Video resolution and frame rate | setButtonVideoRecordingSettings(width, height, fps) |
| Maximum video length | setButtonMaxRecordingTime(minutes) |
| Camera field of view | setCameraFov(...) |
| Camera LED preference | setButtonCameraLed(...) |
Common Commands
| Area | Commands |
|---|---|
| Connection | scan, startScan, stopScan, connect, connectDefault, disconnect, forget |
| Wi-Fi and hotspot | requestWifiScan, sendWifiCredentials, forgetWifiNetwork, setHotspotState |
| Camera | requestPhoto, gallery mode, button photo settings, button video settings, camera field of view |
| Streaming | startStream, stopStream |
| Audio | setMicState, Voice Activity Detection setting and events, audio callbacks, local transcription, media volume |
| Settings | Gallery mode, camera options, microphone route, Wi-Fi, hotspot, and OTA |
| LEDs | rgbLedControl on supported glasses |
| System | requestVersionInfo, checkForOtaUpdate, startOtaUpdate, retryOtaVersionCheck |
OTA Updates
Mentra Live OTA is glasses-owned. The SDK exposes the same command and event semantics used by the MentraOS app:| API | Glasses command | Purpose |
|---|---|---|
checkForOtaUpdate() | ota_query_status | Ask the glasses to report update availability or current progress. Resolves with OtaQueryResult from the ASG client. |
startOtaUpdate() | ota_start | Start OTA after your app presents the update and the user accepts it. Resolves with OtaStartAckEvent from the ASG client. |
retryOtaVersionCheck() | ota_retry_version_check | Re-run the glasses-side version check after a known clock-skew/TLS failure. |
ota_status listeners/delegates for install progress and terminal complete / failed state. React Native still receives ota_update_available, ota_start_ack, and ota_status events for low-level integrations; Android listeners receive onOtaUpdateAvailable, onOtaStartAck, and onOtaStatus; iOS delegates receive .otaUpdateAvailable, .otaStartAck, and .otaStatus through BluetoothEvent.
OTA requires Mentra Live glasses firmware that supports the ASG OTA protocol and network access from the glasses. During install, normal BLE traffic can be interrupted and the glasses may restart; keep the app connected and avoid sending unrelated commands until ota_status.status is complete or failed.
Events
React Native components should useuseBluetoothEvent() for hardware events. The hook keeps the callback typed and removes the native subscription when the component unmounts. Native apps receive the same event categories through listener/delegate methods:
- React Native
- Android
- iOS
BluetoothSdk.addListener(...) is the low-level subscription API. Keep the returned subscription and call remove() when the listener is no longer needed.
The React Native event surface is typed through BluetoothSdkEventMap. These are the public event names accepted by useBluetoothEvent() and BluetoothSdk.addListener():
| Event name | Payload type | When it fires |
|---|---|---|
log | LogEvent | SDK diagnostic log line. |
device_discovered | Device | A supported glasses device is discovered during scan. |
default_device_changed | {device?: Device} | The SDK default device changes. |
glasses_not_ready | GlassesNotReadyEvent | A command needs ready glasses but the connected device is not ready. |
button_press | ButtonPressEvent | Glasses button press. |
touch_event | TouchEvent | Glasses touch or swipe gesture. |
head_up | HeadUpEvent | Head-up state changes. |
voice_activity_detection_status | VoiceActivityDetectionStatusEvent | Glasses-side Voice Activity Detection is enabled or disabled. |
speaking_status | SpeakingStatusEvent | Glasses-side Voice Activity Detection reports speaking or not speaking. |
battery_status | BatteryStatusEvent | Battery update from glasses. |
local_transcription | LocalTranscriptionEvent | SDK local transcription text update. |
wifi_status_change | WifiStatusChangeEvent | Glasses Wi-Fi connection state changes. |
wifi_scan_result | WifiScanResultEvent | Wi-Fi scan results. Intermediate events can have scanComplete: false; the final event has scanComplete: true and is what requestWifiScan() awaits. |
version_info | VersionInfoEvent | Glasses version metadata response. Also returned by requestVersionInfo(). |
hotspot_status_change | HotspotStatusChangeEvent | Glasses hotspot state changes. |
hotspot_error | HotspotErrorEvent | Hotspot operation fails. |
photo_response | PhotoResponseEvent | Terminal photo result. Success means capture and webhook delivery completed; error means the glasses or phone rejected/failed the request. requestPhoto(...) resolves with the success case and rejects on the error case; progress follows through photo_status. |
photo_status | PhotoStatusEvent | Photo capture progress changes. May include resolvedConfig, requestedCaptureConfig, meteredPreview, or captureMetadata depending on status. |
video_recording_status | VideoRecordingStatusEvent | Video recording start/stop succeeds or fails, or a status query reports status: "recording_status" with data.recording. startVideoRecording(...) and stopVideoRecording(...) resolve only from their matching terminal statuses (recording_started / recording_stopped); unsuccessful statuses such as already_recording reject the promise. |
gallery_status | GalleryStatusEvent | Gallery content/camera-busy status changes. cameraBusyReason is included when the glasses report a busy camera reason such as video or stream. |
settings_ack | SettingsAckEvent | Raw gallery/button/FOV setting acknowledgements from the glasses. Gallery and button setting methods return SettingsAckSuccessEvent and reject failure statuses; setCameraFov(...) resolves a friendlier CameraFovResult after the raw ack reports camera hardware application. |
compatible_glasses_search_stop | CompatibleGlassesSearchStopEvent | Compatible-glasses search stops for a model. |
swipe_volume_status | SwipeVolumeStatusEvent | Swipe-volume setting changes. |
switch_status | SwitchStatusEvent | Glasses switch status changes. |
rgb_led_control_response | RgbLedControlResponseEvent | RGB LED command succeeds or fails. rgbLedControl(...) resolves with the success case and rejects on the error case. |
pair_failure | PairFailureEvent | Bluetooth pairing fails. |
audio_pairing_needed | AudioPairingNeededEvent | The phone needs Bluetooth audio pairing for the device. |
audio_connected | AudioConnectedEvent | Bluetooth audio connects. |
audio_disconnected | AudioDisconnectedEvent | Bluetooth audio disconnects. |
mic_pcm | MicPcmEvent | PCM microphone frame arrives. |
mic_lc3 | MicLc3Event | LC3 microphone frame arrives. |
stream_status | StreamStatusEvent | Camera stream lifecycle, reconnect, or error state changes. May include resolvedConfig with effective stream settings: encoded output size in video.width / video.height, native camera capture size in video.captureWidth / video.captureHeight, video bitrate and fps, plus audio bitrate, sample rate, echo cancellation, and noise suppression. |
ota_update_available | OtaUpdateAvailableEvent | Mentra Live reports an available OTA package. Also returned by checkForOtaUpdate() when an update is available. |
ota_start_ack | OtaStartAckEvent | Mentra Live acknowledges startOtaUpdate(). Also returned by startOtaUpdate(). |
ota_status | OtaStatusEvent | OTA step, phase, status, progress, or error changes. |
PhotoSuccessResponseEvent returned by React Native requestPhoto(...) or the operation-specific video result returned by React Native startVideoRecording(...) / stopVideoRecording(...). requestPhoto(...) waits for the terminal photo_response: it resolves after the photo is captured and delivered to the webhook, and rejects if the glasses reject the request, the phone-side fallback upload fails, or the terminal response times out. Capture/upload/Bluetooth fallback progress follows through photo_status. Use event listeners for ongoing hardware streams and status updates: for example, useBluetoothEvent('mic_pcm', ...) receives a MicPcmEvent. MicPcmEvent includes sampleRate, bitsPerSample, channels, encoding, and voiceActivityDetectionEnabled; MicLc3Event includes sampleRate, channels, encoding, frameDurationMs, frameSizeBytes, bitrate, packetizedFromGlasses, and voiceActivityDetectionEnabled. speaking_status is separate from microphone audio frames so apps can use continuous audio while still reacting to speech activity.
Public React Native event payload fields usually use camelCase. OTA events intentionally mirror the glasses firmware field names, such as overall_percent and version_name. For example, touch events expose deviceModel and gestureName, successful photo responses expose uploadUrl and may include webhook-returned photoUrl, statusUrl, contentType, and fileSizeBytes, hotspot errors expose errorMessage, Wi-Fi scan events expose scanComplete, and gallery status exposes hasContent, cameraBusy, and optional cameraBusyReason.
Photo Status Metadata
PhotoStatusEvent reports progress through capture and transfer. Capture metadata is attached to the stage where the glasses know that data:
| Status | Optional field | Description |
|---|---|---|
configuring | resolvedConfig | Effective JPEG dimensions, quality, requested size, source (sdk or button), transfer method, compression, and manual exposure fields when present. |
capturing | requestedCaptureConfig | Camera2 still request values submitted to the HAL, such as exposure time, ISO, frame duration, AE mode, AE lock, exposure compensation, target FPS range, AF mode, and ZSL. |
capturing | meteredPreview | Latest preview auto-exposure estimate before the still capture, including exposure time, ISO, and a light proxy when available. |
captured | captureMetadata | Actual still capture result returned by the HAL, including applied exposure time, ISO, frame duration, AE state/name, noise reduction mode, edge mode, sensor timestamp, and MFNR hint when available. |
uploading, compressing, ble_fallback_compression, ready_for_transfer, and transferring describe upload or BLE progress only and do not carry capture metadata. ble_fallback_compression means the direct Wi-Fi/webhook upload failed and the glasses are compressing the already-captured photo for Bluetooth fallback delivery. Apps should read actual capture values from event.captureMetadata on the captured status, not from upload statuses.
Local action-button photos emitted by the glasses use the same photo_status shape when the phone SDK is connected. Those local captures set resolvedConfig.source to button and resolvedConfig.transferMethod to local.
Android and iOS expose typed callbacks/delegate methods instead of the React Native string event API. Android uses MentraBluetoothSdkListener methods such as onStateChanged, onGlassesChanged, onSdkStateChanged, onScanChanged, onDeviceDiscovered, onButtonPress, onSpeakingStatus, onPhotoResponse, onPhotoStatus, onMicPcm, onStreamStatus, and onOtaStatus. iOS uses MentraBluetoothSDKDelegate methods such as mentraBluetoothSDK(_:didUpdate:), mentraBluetoothSDK(_:didUpdateGlasses:), mentraBluetoothSDK(_:didUpdateSdkState:), mentraBluetoothSDK(_:didUpdateScan:), mentraBluetoothSDK(_:didDiscover:), mentraBluetoothSDK(_:didReceive:), mentraBluetoothSDK(_:didReceiveMicPcm:), and mentraBluetoothSDK(_:didReceiveMicLc3:); OTA arrives through didReceive as .otaUpdateAvailable, .otaStartAck, or .otaStatus. Microphone audio callbacks use MicPcmEvent and MicLc3Event objects with the same metadata as React Native.
| Event area | Examples |
|---|---|
| Input | Button press, touch, swipe, head-up |
| Device state | Battery, case, Wi-Fi, hotspot, connection, RSSI |
| Camera | Photo request result, gallery state, camera settings |
| Streaming | Stream initializing, active, stopped, error |
| OTA | Update available, start acknowledged, step/progress status |
| Audio | Microphone PCM, LC3, local transcription, audio route state |
| Diagnostics | SDK log events |
Version Fields
CallrequestVersionInfo() after connection when your app wants the glasses to refresh version metadata. Updated values arrive through the normal glasses-status callback and are also available in the next status snapshot.
| Field | Meaning |
|---|---|
firmwareVersion | Generic glasses firmware version when the connected model reports one. |
deviceFirmwareVersion | Device firmware version for models that report device info as a structured payload. |
leftFirmwareVersion / rightFirmwareVersion | Per-side firmware versions for glasses that report left/right firmware separately. |
besFirmwareVersion | Mentra Live BES firmware version. |
mtkFirmwareVersion | Mentra Live MTK/system OTA firmware version. |
appVersion | Glasses-side companion app version. On Mentra Live this is the ASG client APK version, not firmware. |
androidVersion | Android OS version on Android-based glasses. This is not firmware. |

