Skip to main content
The Mentra Bluetooth SDK exposes the same core glasses lifecycle across Android, iOS, and React Native:
  • Scan for a supported glasses model.
  • Connect to a discovered Device or 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

Only documented imports are part of the supported app developer API. Undocumented package subpaths or symbols with a leading underscore can change without notice.

Lifecycle

Keep one SDK instance per active app session. The SDK owns Bluetooth connection state, native event delivery, and cleanup. Your app owns user identity, UI state, and whether a default device record is persisted across app restarts.

Mentra SDK Usage Analytics

The SDK reports anonymous usage events to Mentra’s PostHog project by default so Mentra can understand SDK adoption and successful glasses connections: Analytics delivery is fire-and-forget: events are submitted asynchronously, do not block Bluetooth SDK behavior, and are not retried if delivery fails. Mentra’s PostHog project API key is embedded in the SDK as a public analytics write token, not a private PostHog personal API key. Apps do not need to configure PostHog to send these Mentra SDK usage events.
Disable analytics before native SDK startup through the Expo config plugin:
Apps do not configure the analytics destination; these SDK usage events are always sent to Mentra’s PostHog project unless analytics are disabled. Captured properties are intentionally narrow: The SDK does not upload BLE MAC addresses, CoreBluetooth identifiers, serial numbers, Bluetooth device names, user ids, tokens, Wi-Fi credentials, microphone data, photos, or transcripts. It stores a locally generated anonymous SDK install id and sends it as PostHog distinct_id; events include $process_person_profile: false so PostHog does not create person profiles for these SDK usage pings.

Connection

Prefer connecting to a 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: When the platform does not provide an address or identifier, the SDK falls back to a 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 exposes mentra.glasses.connection as a discriminated union:
Use 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.
Status snapshots are safe to read at any time. For continuous subsystems, keep UI state derived from status callbacks or hook state. Request/response commands document their exact promise resolution and error criteria below. Public status is grouped the same way across platforms: Use 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: React Native helper exports include 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 through options.onResults, resolves with the final matching Device[], and times out after 15 seconds unless options.timeoutMs is set.
  • connect and connectDefault default saveAsDefault and cancelExistingConnectionAttempt to true.
  • setMicState(enabled) defaults to glasses microphone on, transcript events off, and LC3 events off. Microphone audio events are continuous while capture is enabled. Use setVoiceActivityDetectionEnabled(...) for glasses-side Voice Activity Detection; voice_activity_detection_status reports whether it is enabled, and speaking_status reports speaking/not-speaking when supported. Microphone audio events include the latest voiceActivityDetectionEnabled value.
  • requestPhoto({authToken, ...}) omits the Authorization header when authToken is null or empty.
  • requestPhoto(...) and warmUpCamera(...) generate unique request IDs when requestId is omitted or blank. Pass an explicit requestId only when your app needs to know it before the response, for example to poll a predictable upload-status URL.
  • requestPhoto({exposureTimeNs, ...}) uses auto exposure when exposureTimeNs is omitted or null; pass a positive nanosecond value for one-shot manual exposure.
  • requestPhoto({exposureTimeNs, iso, ...}) uses iso only when exposureTimeNs enables one-shot manual exposure. Omit iso / pass null for auto ISO selection.
  • requestPhoto(...) capture fields apply to that one request. setPhotoCaptureDefaults(...) is deprecated; it still persists gallery-mode action-button photo defaults, but prefer per-request requestPhoto(...) options (e.g. mode: "text") instead.
  • warmUpCamera({size, exposureTimeNs, durationMs}) opens/configures Mentra Live’s camera and holds it warm without capturing or uploading media. Use it while a foreground camera UI is active, refresh before durationMs expires, and match the next requestPhoto(...) size/exposure settings for reuse. The promise resolves on camera_status.state === "ready"; the later stopped event marks lease expiry/teardown for listeners and does not settle the already-resolved promise.
  • 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 terminal PhotoSuccessResponseEvent after capture and delivery finish, startVideoRecording(...) returns VideoRecordingStartedStatusEvent, stopVideoRecording(...) returns VideoRecordingStoppedStatusEvent, and rgbLedControl(...) returns RgbLedControlSuccessResponseEvent. When stopVideoRecording(...) includes a webhook URL, the promise resolves after the video upload succeeds. The corresponding raw events still include error variants for listeners.
  • setCameraFov({fov, roiPosition}) clamps fov to 62-118 degrees and accepts roiPosition as "center", "bottom", or "top". You can also call setCameraFov({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 with CameraFovResult only 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.
  • startStream optional video and audio configs are omitted unless supplied, so the connected glasses use their model defaults. Stream video input fields are width, height, bitrate, and fps; stream status reports the resolved effective frame rate as resolvedConfig.video.fps. The SDK sends stream keep-alives automatically and reports timeout/error state through stream_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 after await. 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. 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. Use setGalleryModeEnabled(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: requestPhoto(...) accepts optional requestId plus size, webhookUrl, authToken, compress, save, sound, exposureTimeNs, iso, aeExposureDivisor, isoCap, mfnr, zsl, noiseReduction, edgeEnhancement, ispDigitalGain, and ispAnalogGain. Photo, video, and stream capture always enable the camera light as a privacy indicator. Manual iso is used only when exposureTimeNs enables manual exposure. warmUpCamera(...) accepts optional requestId plus size, exposureTimeNs, and durationMs. Omit or pass a non-positive durationMs for the default 15 second warm hold. camera_status reports warming, ready, stopped, or error; the method resolves on ready and rejects on error. Every request ID that receives ready also receives stopped when its warm lease expires or the camera service tears down. A warm-up that is still pending when the camera service tears down rejects with error instead. setPhotoCaptureDefaults(...) is deprecated. It still accepts size, mfnr, zsl, noiseReduction, edgeEnhancement, ispDigitalGain, ispAnalogGain, aeExposureDivisor, isoCap, compress, sound, and resetCaptureTuning, but prefer per-request requestPhoto(...) options (for example mode: "text" for AE ÷3) instead of sticky button-photo presets. Omitted fields leave existing action-button photo defaults unchanged. resetCaptureTuning: true restores optional tuning fields to standard capture behavior and does not change photo size unless size is also provided.

Common Commands

Mentra Live has camera, microphone, speaker, Wi-Fi, LED, and OTA capabilities. Gate UI by connection readiness, permissions, and the latest SDK status.

OTA Updates

Each Bluetooth SDK release has a matching Mentra Live glasses software release with the same version number. When your app checks for an update, the SDK uses the OTA manifest for its own release. For the current 0.1.20 software and complete installation instructions, see Update Mentra Live. Mentra Live OTA installation is glasses-owned. The SDK checks its release-specific manifest on the phone, then sends ota_start with that same manifest URL when the user accepts the update: Use the boolean check result for update prompts and keep ota_status listeners/delegates for install progress and terminal complete / failed state. Availability is exposed through checkForOtaUpdate() rather than a public event; React Native receives ota_start_ack and ota_status events during the install flow. Android listeners receive onOtaStartAck and onOtaStatus; iOS delegates receive .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 use useBluetoothEvent() 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:
For non-React modules, 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(): For request/response commands, prefer the returned value from the method, such as the 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. stopVideoRecording(...) keeps the same end-to-end shape for video webhook stops: it waits for recording stop plus upload success, and rejects on upload failure. Photo capture/upload/Bluetooth fallback progress follows through photo_status; raw video upload completion arrives through media_success and media_error for listeners that need the glasses event stream directly. 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, successful media upload events expose requestId, mediaUrl, and mediaType, 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: Transport statuses such as 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 install progress arrives through didReceive as .otaStartAck or .otaStatus. Microphone audio callbacks use MicPcmEvent and MicLc3Event objects with the same metadata as React Native.

Version Fields

Call requestVersionInfo() 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. Different glasses models expose different version fields, so apps should prefer the generic firmware field when present, then fall back to model-specific firmware fields. Keep app and OS versions visibly labeled as app/OS versions.