@mentra/bluetooth-sdk 3.2.0-dev.181 → 3.2.0-dev.190

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 (37) hide show
  1. package/README.md +22 -0
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +15 -0
  3. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +8 -0
  4. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +9 -0
  6. package/android/src/main/java/com/mentra/bluetoothsdk/NativeNotifications.kt +42 -0
  7. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/EvenFileService.kt +101 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/EvenNotificationProtocol.kt +113 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +378 -24
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/NotificationIds.kt +19 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +11 -0
  12. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/EvenFileServiceTest.kt +153 -0
  13. package/build/BluetoothSdk.types.d.ts +50 -0
  14. package/build/BluetoothSdk.types.d.ts.map +1 -1
  15. package/build/BluetoothSdk.types.js.map +1 -1
  16. package/build/_private/BluetoothSdkModule.d.ts +5 -1
  17. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  18. package/build/_private/BluetoothSdkModule.js.map +1 -1
  19. package/build/generated/releaseMetadata.js +5 -5
  20. package/build/generated/releaseMetadata.js.map +1 -1
  21. package/build/index.d.ts +1 -0
  22. package/build/index.d.ts.map +1 -1
  23. package/build/index.js +4 -0
  24. package/build/index.js.map +1 -1
  25. package/ios/BluetoothSdkModule.swift +23 -0
  26. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  27. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  28. package/ios/Source/MentraBluetoothSDK.swift +10 -0
  29. package/ios/Source/NativeNotifications.swift +52 -0
  30. package/ios/Source/sgcs/G2.swift +131 -21
  31. package/ios/Source/sgcs/SGCManager.swift +22 -0
  32. package/ios/Tests/NativeNotificationTests.swift +20 -0
  33. package/package.json +1 -1
  34. package/src/BluetoothSdk.types.ts +54 -0
  35. package/src/_private/BluetoothSdkModule.ts +8 -0
  36. package/src/generated/releaseMetadata.ts +5 -5
  37. package/src/index.ts +6 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import PrivateBluetoothSdkModule from \"./_private/BluetoothSdkModule\"\nimport type {\n BluetoothSdkEventListener,\n BluetoothSdkEventName,\n BluetoothSdkPublicModule,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n VideoRecordingDefaults,\n} from \"./BluetoothSdk.types\"\nimport {getReleaseChangelogs} from \"./changelogs\"\n\nconst PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([\n \"log\",\n \"device_discovered\",\n \"default_device_changed\",\n \"glasses_not_ready\",\n \"button_press\",\n \"touch_event\",\n \"accel_event\",\n \"head_up\",\n \"voice_activity_detection_status\",\n \"speaking_status\",\n \"battery_status\",\n \"local_transcription\",\n \"wifi_status_change\",\n \"wifi_scan_result\",\n \"hotspot_status_change\",\n \"hotspot_error\",\n \"photo_response\",\n \"photo_status\",\n \"camera_status\",\n \"video_recording_status\",\n \"media_success\",\n \"media_error\",\n \"gallery_status\",\n \"compatible_glasses_search_stop\",\n \"swipe_volume_status\",\n \"switch_status\",\n \"rgb_led_control_response\",\n \"settings_ack\",\n \"pair_failure\",\n \"pairing_info\",\n \"entering_pairing_mode\",\n \"owner_replaced\",\n \"audio_pairing_needed\",\n \"audio_connected\",\n \"audio_disconnected\",\n \"mic_pcm\",\n \"mic_lc3\",\n \"mic_health\",\n \"stream_status\",\n \"mtk_update_complete\",\n \"glasses_session_changed\",\n \"ota_start_ack\",\n \"ota_status\",\n \"ar99_ota_status\",\n \"version_info\",\n \"extraction_progress\",\n])\n\nconst addListener: BluetoothSdkPublicModule[\"addListener\"] = (eventName, listener) => {\n if (!PUBLIC_EVENT_NAMES.has(eventName)) {\n throw new Error(`Unsupported BluetoothSdk event \"${eventName}\". Use @mentra/bluetooth-sdk/react for status state.`)\n }\n return PrivateBluetoothSdkModule.addListener(eventName, listener as BluetoothSdkEventListener<BluetoothSdkEventName>)\n}\n\nconst startOtaUpdate: BluetoothSdkPublicModule[\"startOtaUpdate\"] = (otaVersionUrl) => {\n return PrivateBluetoothSdkModule.startOtaUpdate(otaVersionUrl)\n}\n\nconst queryOtaStatus: BluetoothSdkPublicModule[\"queryOtaStatus\"] = () => {\n return PrivateBluetoothSdkModule.sendOtaQueryStatus()\n}\n\nconst bindPublicMethod = <K extends keyof BluetoothSdkPublicModule>(name: K): BluetoothSdkPublicModule[K] => {\n const method = (PrivateBluetoothSdkModule as unknown as Record<string, unknown>)[name]\n if (typeof method === \"function\") {\n return method.bind(PrivateBluetoothSdkModule) as BluetoothSdkPublicModule[K]\n }\n\n return (async () => {\n throw new Error(`BluetoothSdk.${String(name)} is not available in this native build. Rebuild the app.`)\n }) as unknown as BluetoothSdkPublicModule[K]\n}\n\nexport const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({\n addListener,\n getGlassesStatus: bindPublicMethod(\"getGlassesStatus\"),\n getBluetoothStatus: bindPublicMethod(\"getBluetoothStatus\"),\n subscribeGlassesStatus: (listener: (changed: Partial<PublicGlassesStatus>) => void) =>\n PrivateBluetoothSdkModule.onGlassesStatus(listener),\n subscribeBluetoothStatus: (listener: (changed: Partial<PublicBluetoothStatus>) => void) =>\n PrivateBluetoothSdkModule.onBluetoothStatus(listener),\n getDefaultDevice: bindPublicMethod(\"getDefaultDevice\"),\n setDefaultDevice: bindPublicMethod(\"setDefaultDevice\"),\n clearDefaultDevice: bindPublicMethod(\"clearDefaultDevice\"),\n startScan: bindPublicMethod(\"startScan\"),\n stopScan: bindPublicMethod(\"stopScan\"),\n scan: bindPublicMethod(\"scan\") as BluetoothSdkPublicModule[\"scan\"],\n connect: bindPublicMethod(\"connect\"),\n connectDefault: bindPublicMethod(\"connectDefault\"),\n cancelConnectionAttempt: bindPublicMethod(\"cancelConnectionAttempt\"),\n disconnect: bindPublicMethod(\"disconnect\"),\n forget: bindPublicMethod(\"forget\"),\n displayText: bindPublicMethod(\"displayText\"),\n clearDisplay: bindPublicMethod(\"clearDisplay\"),\n setDashboardContent: bindPublicMethod(\"setDashboardContent\"),\n showDashboard: bindPublicMethod(\"showDashboard\"),\n setDashboardPosition: bindPublicMethod(\"setDashboardPosition\"),\n setHeadUpAngle: bindPublicMethod(\"setHeadUpAngle\"),\n setImuEnabled: bindPublicMethod(\"setImuEnabled\"),\n setScreenDisabled: bindPublicMethod(\"setScreenDisabled\"),\n ping: bindPublicMethod(\"ping\"),\n requestWifiScan: bindPublicMethod(\"requestWifiScan\"),\n sendWifiCredentials: bindPublicMethod(\"sendWifiCredentials\"),\n forgetWifiNetwork: bindPublicMethod(\"forgetWifiNetwork\"),\n setHotspotState: bindPublicMethod(\"setHotspotState\"),\n setSystemTime: bindPublicMethod(\"setSystemTime\"),\n setWifiAdbState: bindPublicMethod(\"setWifiAdbState\"),\n setGalleryModeEnabled: bindPublicMethod(\"setGalleryModeEnabled\"),\n setVoiceActivityDetectionEnabled: bindPublicMethod(\"setVoiceActivityDetectionEnabled\"),\n setLoudnessGateEnabled: bindPublicMethod(\"setLoudnessGateEnabled\"),\n /**\n * @deprecated Sticky action-button photo presets are deprecated. Prefer per-request\n * `requestPhoto(...)` options (e.g. `mode: \"text\"` for text sensor size/crop, or explicit per-shot\n * fields). Still functional until removed in a future release.\n */\n setPhotoCaptureDefaults: bindPublicMethod(\"setPhotoCaptureDefaults\"),\n setVideoRecordingDefaults: ({width, height, fps}: VideoRecordingDefaults) => {\n const method = (PrivateBluetoothSdkModule as unknown as Record<string, unknown>).setVideoRecordingDefaults\n if (typeof method !== \"function\") {\n return Promise.reject(\n new Error(\"BluetoothSdk.setVideoRecordingDefaults is not available in this native build. Rebuild the app.\"),\n )\n }\n return PrivateBluetoothSdkModule.setVideoRecordingDefaults(width, height, fps)\n },\n setMaxVideoRecordingDuration: bindPublicMethod(\"setMaxVideoRecordingDuration\"),\n setCameraFov: bindPublicMethod(\"setCameraFov\"),\n setLegacyCameraFov: bindPublicMethod(\"setLegacyCameraFov\"),\n setCameraFovOverride: bindPublicMethod(\"setCameraFovOverride\"),\n releaseCameraFovOverride: bindPublicMethod(\"releaseCameraFovOverride\"),\n setCameraTuningConfig: bindPublicMethod(\"setCameraTuningConfig\"),\n queryGalleryStatus: bindPublicMethod(\"queryGalleryStatus\"),\n requestPhoto: bindPublicMethod(\"requestPhoto\"),\n warmUpCamera: bindPublicMethod(\"warmUpCamera\"),\n stopCameraWarmUp: bindPublicMethod(\"stopCameraWarmUp\"),\n startVideoRecording: bindPublicMethod(\"startVideoRecording\"),\n stopVideoRecording: bindPublicMethod(\"stopVideoRecording\"),\n queryVideoRecordingStatus: bindPublicMethod(\"queryVideoRecordingStatus\"),\n startStream: bindPublicMethod(\"startStream\"),\n stopStream: bindPublicMethod(\"stopStream\"),\n setMicState: bindPublicMethod(\"setMicState\"),\n setPreferredMic: bindPublicMethod(\"setPreferredMic\"),\n setOwnAppAudioPlaying: bindPublicMethod(\"setOwnAppAudioPlaying\"),\n getGlassesMediaVolume: bindPublicMethod(\"getGlassesMediaVolume\"),\n setGlassesMediaVolume: bindPublicMethod(\"setGlassesMediaVolume\"),\n rgbLedControl: bindPublicMethod(\"rgbLedControl\"),\n requestVersionInfo: bindPublicMethod(\"requestVersionInfo\"),\n setOtaVersionUrl: bindPublicMethod(\"setOtaVersionUrl\"),\n getOtaVersionUrl: bindPublicMethod(\"getOtaVersionUrl\"),\n checkForOtaUpdate: bindPublicMethod(\"checkForOtaUpdate\"),\n getReleaseChangelogs,\n startOtaUpdate,\n queryOtaStatus,\n startAr99OtaFromFile: bindPublicMethod(\"startAr99OtaFromFile\"),\n cancelAr99Ota: bindPublicMethod(\"cancelAr99Ota\"),\n sendAr99FactoryReset: bindPublicMethod(\"sendAr99FactoryReset\"),\n buildAr99OtaSignature: bindPublicMethod(\"buildAr99OtaSignature\"),\n setSttModelDetails: bindPublicMethod(\"setSttModelDetails\"),\n getSttModelPath: bindPublicMethod(\"getSttModelPath\"),\n checkSttModelAvailable: bindPublicMethod(\"checkSttModelAvailable\"),\n validateSttModel: bindPublicMethod(\"validateSttModel\"),\n extractTarBz2: bindPublicMethod(\"extractTarBz2\"),\n restartTranscriber: bindPublicMethod(\"restartTranscriber\"),\n setTtsModelDetails: bindPublicMethod(\"setTtsModelDetails\"),\n getTtsModelPath: bindPublicMethod(\"getTtsModelPath\"),\n getTtsModelLanguage: bindPublicMethod(\"getTtsModelLanguage\"),\n checkTtsModelAvailable: bindPublicMethod(\"checkTtsModelAvailable\"),\n validateTtsModel: bindPublicMethod(\"validateTtsModel\"),\n generateTtsAudio: bindPublicMethod(\"generateTtsAudio\"),\n})\n\nexport default BluetoothSdk\n\nexport {\n CAMERA_FOV_DEFAULT,\n CAMERA_FOV_MAX,\n CAMERA_FOV_MIN,\n DeviceModels,\n isBusyGlassesConnectionStatus,\n isConnectedGlassesConnectionStatus,\n isConnectedWifiStatus,\n isEnabledHotspotStatus,\n isReadyGlassesConnectionStatus,\n} from \"./BluetoothSdk.types\"\n\nexport type {\n AccelEvent,\n Ar99OtaStatusEvent,\n AudioConnectedEvent,\n AudioDisconnectedEvent,\n AudioPairingNeededEvent,\n BatteryStatusEvent,\n BluetoothSdkEvent,\n BluetoothSdkEventListener,\n BluetoothSdkEventMap,\n BluetoothSdkEventName,\n BluetoothSdkPublicModule as BluetoothSdkModule,\n BluetoothSdkSubscription,\n ButtonPhotoSize,\n PhotoCaptureDefaults,\n ButtonPressEvent,\n CameraFovPreset,\n CameraFovRequest,\n CameraFovResult,\n CameraRoiPosition,\n CompatibleGlassesSearchStopEvent,\n ConnectOptions,\n ConnectedGlassesConnectionStatus,\n ConnectedWifiStatus,\n Device,\n DeviceModel,\n EnabledHotspotStatus,\n GalleryStatusEvent,\n GlassesConnectionStatus,\n GlassesMediaVolumeGetResult,\n GlassesMediaVolumeSetResult,\n GlassesNotReadyEvent,\n HeadUpEvent,\n HotspotErrorEvent,\n HotspotStatus,\n HotspotStatusChangeEvent,\n GlassesSessionChangedEvent,\n KeepAliveAckEvent,\n LocalTranscriptionEvent,\n LogEvent,\n MicLc3Event,\n MicHealthEvent,\n MicMode,\n MicPcmEvent,\n MicPreference,\n OtaStartAckEvent,\n OtaProgress,\n OtaProgressStatus,\n OtaStage,\n OtaStatus,\n OtaStatusEvent,\n OtaQueryResult,\n OtaUpdateInfo,\n ReleaseChangelog,\n MtkUpdateCompleteEvent,\n PairFailureEvent,\n PairingInfoEvent,\n EnteringPairingModeEvent,\n OwnerReplacedEvent,\n PhotoCaptureMetadata,\n PhotoResolvedConfig,\n PhotoCompression,\n PhotoFpsRange,\n PhotoMeteredPreview,\n PhotoMode,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n PhotoTransferMethod,\n PhotoResponseEvent,\n PhotoRequestedCaptureConfig,\n PhotoRequestParams,\n PhotoSize,\n PhotoStatusEvent,\n PhotoStatusState,\n PhotoSuccessResponseEvent,\n RgbLedAction,\n RgbLedColor,\n RgbLedControlResponseEvent,\n RgbLedControlSuccessResponseEvent,\n ScanModelOptions,\n ScanDiagnostic,\n ScanOptions,\n ScanResultsCallback,\n SpeakingStatusEvent,\n SettingsAckEvent,\n SettingsAckSetting,\n SettingsAckSuccessEvent,\n SettingsAckSuccessStatus,\n SettingsAckStatus,\n StreamAudioConfig,\n StreamResolvedConfig,\n StreamStartRequest,\n StreamStatusEvent,\n StreamStatusLifecycleState,\n StreamStatusReconnectState,\n StreamStatusState,\n StreamVideoConfig,\n SwipeVolumeStatusEvent,\n SwitchStatusEvent,\n TouchEvent,\n VideoRecordingDefaults,\n VideoRecordingStartedStatusEvent,\n VideoRecordingStatusEvent,\n VideoRecordingStatusState,\n VideoRecordingStoppedStatusEvent,\n VideoRecordingSuccessStatusEvent,\n VersionInfoEvent,\n VersionInfoResult,\n WifiScanResultEvent,\n VoiceActivityDetectionStatusEvent,\n WifiSearchResult,\n WifiStatus,\n WifiStatusChangeEvent,\n} from \"./BluetoothSdk.types\"\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import PrivateBluetoothSdkModule from \"./_private/BluetoothSdkModule\"\nimport type {\n BluetoothSdkEventListener,\n BluetoothSdkEventName,\n BluetoothSdkPublicModule,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n VideoRecordingDefaults,\n} from \"./BluetoothSdk.types\"\nimport {getReleaseChangelogs} from \"./changelogs\"\n\nconst PUBLIC_EVENT_NAMES = new Set<BluetoothSdkEventName>([\n \"native_notification_status\",\n \"native_notification_delivery\",\n \"log\",\n \"device_discovered\",\n \"default_device_changed\",\n \"glasses_not_ready\",\n \"button_press\",\n \"touch_event\",\n \"accel_event\",\n \"head_up\",\n \"voice_activity_detection_status\",\n \"speaking_status\",\n \"battery_status\",\n \"local_transcription\",\n \"wifi_status_change\",\n \"wifi_scan_result\",\n \"hotspot_status_change\",\n \"hotspot_error\",\n \"photo_response\",\n \"photo_status\",\n \"camera_status\",\n \"video_recording_status\",\n \"media_success\",\n \"media_error\",\n \"gallery_status\",\n \"compatible_glasses_search_stop\",\n \"swipe_volume_status\",\n \"switch_status\",\n \"rgb_led_control_response\",\n \"settings_ack\",\n \"pair_failure\",\n \"pairing_info\",\n \"entering_pairing_mode\",\n \"owner_replaced\",\n \"audio_pairing_needed\",\n \"audio_connected\",\n \"audio_disconnected\",\n \"mic_pcm\",\n \"mic_lc3\",\n \"mic_health\",\n \"stream_status\",\n \"mtk_update_complete\",\n \"glasses_session_changed\",\n \"ota_start_ack\",\n \"ota_status\",\n \"ar99_ota_status\",\n \"version_info\",\n \"extraction_progress\",\n])\n\nconst addListener: BluetoothSdkPublicModule[\"addListener\"] = (eventName, listener) => {\n if (!PUBLIC_EVENT_NAMES.has(eventName)) {\n throw new Error(`Unsupported BluetoothSdk event \"${eventName}\". Use @mentra/bluetooth-sdk/react for status state.`)\n }\n return PrivateBluetoothSdkModule.addListener(eventName, listener as BluetoothSdkEventListener<BluetoothSdkEventName>)\n}\n\nconst startOtaUpdate: BluetoothSdkPublicModule[\"startOtaUpdate\"] = (otaVersionUrl) => {\n return PrivateBluetoothSdkModule.startOtaUpdate(otaVersionUrl)\n}\n\nconst queryOtaStatus: BluetoothSdkPublicModule[\"queryOtaStatus\"] = () => {\n return PrivateBluetoothSdkModule.sendOtaQueryStatus()\n}\n\nconst bindPublicMethod = <K extends keyof BluetoothSdkPublicModule>(name: K): BluetoothSdkPublicModule[K] => {\n const method = (PrivateBluetoothSdkModule as unknown as Record<string, unknown>)[name]\n if (typeof method === \"function\") {\n return method.bind(PrivateBluetoothSdkModule) as BluetoothSdkPublicModule[K]\n }\n\n return (async () => {\n throw new Error(`BluetoothSdk.${String(name)} is not available in this native build. Rebuild the app.`)\n }) as unknown as BluetoothSdkPublicModule[K]\n}\n\nexport const BluetoothSdk: BluetoothSdkPublicModule = Object.freeze({\n configureNativeNotifications: bindPublicMethod(\"configureNativeNotifications\"),\n getNativeNotificationStatus: bindPublicMethod(\"getNativeNotificationStatus\"),\n addListener,\n getGlassesStatus: bindPublicMethod(\"getGlassesStatus\"),\n getBluetoothStatus: bindPublicMethod(\"getBluetoothStatus\"),\n subscribeGlassesStatus: (listener: (changed: Partial<PublicGlassesStatus>) => void) =>\n PrivateBluetoothSdkModule.onGlassesStatus(listener),\n subscribeBluetoothStatus: (listener: (changed: Partial<PublicBluetoothStatus>) => void) =>\n PrivateBluetoothSdkModule.onBluetoothStatus(listener),\n getDefaultDevice: bindPublicMethod(\"getDefaultDevice\"),\n setDefaultDevice: bindPublicMethod(\"setDefaultDevice\"),\n clearDefaultDevice: bindPublicMethod(\"clearDefaultDevice\"),\n startScan: bindPublicMethod(\"startScan\"),\n stopScan: bindPublicMethod(\"stopScan\"),\n scan: bindPublicMethod(\"scan\") as BluetoothSdkPublicModule[\"scan\"],\n connect: bindPublicMethod(\"connect\"),\n connectDefault: bindPublicMethod(\"connectDefault\"),\n cancelConnectionAttempt: bindPublicMethod(\"cancelConnectionAttempt\"),\n disconnect: bindPublicMethod(\"disconnect\"),\n forget: bindPublicMethod(\"forget\"),\n displayText: bindPublicMethod(\"displayText\"),\n clearDisplay: bindPublicMethod(\"clearDisplay\"),\n setDashboardContent: bindPublicMethod(\"setDashboardContent\"),\n showDashboard: bindPublicMethod(\"showDashboard\"),\n setDashboardPosition: bindPublicMethod(\"setDashboardPosition\"),\n setHeadUpAngle: bindPublicMethod(\"setHeadUpAngle\"),\n setImuEnabled: bindPublicMethod(\"setImuEnabled\"),\n setScreenDisabled: bindPublicMethod(\"setScreenDisabled\"),\n ping: bindPublicMethod(\"ping\"),\n requestWifiScan: bindPublicMethod(\"requestWifiScan\"),\n sendWifiCredentials: bindPublicMethod(\"sendWifiCredentials\"),\n forgetWifiNetwork: bindPublicMethod(\"forgetWifiNetwork\"),\n setHotspotState: bindPublicMethod(\"setHotspotState\"),\n setSystemTime: bindPublicMethod(\"setSystemTime\"),\n setWifiAdbState: bindPublicMethod(\"setWifiAdbState\"),\n setGalleryModeEnabled: bindPublicMethod(\"setGalleryModeEnabled\"),\n setVoiceActivityDetectionEnabled: bindPublicMethod(\"setVoiceActivityDetectionEnabled\"),\n setLoudnessGateEnabled: bindPublicMethod(\"setLoudnessGateEnabled\"),\n /**\n * @deprecated Sticky action-button photo presets are deprecated. Prefer per-request\n * `requestPhoto(...)` options (e.g. `mode: \"text\"` for text sensor size/crop, or explicit per-shot\n * fields). Still functional until removed in a future release.\n */\n setPhotoCaptureDefaults: bindPublicMethod(\"setPhotoCaptureDefaults\"),\n setVideoRecordingDefaults: ({width, height, fps}: VideoRecordingDefaults) => {\n const method = (PrivateBluetoothSdkModule as unknown as Record<string, unknown>).setVideoRecordingDefaults\n if (typeof method !== \"function\") {\n return Promise.reject(\n new Error(\"BluetoothSdk.setVideoRecordingDefaults is not available in this native build. Rebuild the app.\"),\n )\n }\n return PrivateBluetoothSdkModule.setVideoRecordingDefaults(width, height, fps)\n },\n setMaxVideoRecordingDuration: bindPublicMethod(\"setMaxVideoRecordingDuration\"),\n setCameraFov: bindPublicMethod(\"setCameraFov\"),\n setLegacyCameraFov: bindPublicMethod(\"setLegacyCameraFov\"),\n setCameraFovOverride: bindPublicMethod(\"setCameraFovOverride\"),\n releaseCameraFovOverride: bindPublicMethod(\"releaseCameraFovOverride\"),\n setCameraTuningConfig: bindPublicMethod(\"setCameraTuningConfig\"),\n queryGalleryStatus: bindPublicMethod(\"queryGalleryStatus\"),\n requestPhoto: bindPublicMethod(\"requestPhoto\"),\n warmUpCamera: bindPublicMethod(\"warmUpCamera\"),\n stopCameraWarmUp: bindPublicMethod(\"stopCameraWarmUp\"),\n startVideoRecording: bindPublicMethod(\"startVideoRecording\"),\n stopVideoRecording: bindPublicMethod(\"stopVideoRecording\"),\n queryVideoRecordingStatus: bindPublicMethod(\"queryVideoRecordingStatus\"),\n startStream: bindPublicMethod(\"startStream\"),\n stopStream: bindPublicMethod(\"stopStream\"),\n setMicState: bindPublicMethod(\"setMicState\"),\n setPreferredMic: bindPublicMethod(\"setPreferredMic\"),\n setOwnAppAudioPlaying: bindPublicMethod(\"setOwnAppAudioPlaying\"),\n getGlassesMediaVolume: bindPublicMethod(\"getGlassesMediaVolume\"),\n setGlassesMediaVolume: bindPublicMethod(\"setGlassesMediaVolume\"),\n rgbLedControl: bindPublicMethod(\"rgbLedControl\"),\n requestVersionInfo: bindPublicMethod(\"requestVersionInfo\"),\n setOtaVersionUrl: bindPublicMethod(\"setOtaVersionUrl\"),\n getOtaVersionUrl: bindPublicMethod(\"getOtaVersionUrl\"),\n checkForOtaUpdate: bindPublicMethod(\"checkForOtaUpdate\"),\n getReleaseChangelogs,\n startOtaUpdate,\n queryOtaStatus,\n startAr99OtaFromFile: bindPublicMethod(\"startAr99OtaFromFile\"),\n cancelAr99Ota: bindPublicMethod(\"cancelAr99Ota\"),\n sendAr99FactoryReset: bindPublicMethod(\"sendAr99FactoryReset\"),\n buildAr99OtaSignature: bindPublicMethod(\"buildAr99OtaSignature\"),\n setSttModelDetails: bindPublicMethod(\"setSttModelDetails\"),\n getSttModelPath: bindPublicMethod(\"getSttModelPath\"),\n checkSttModelAvailable: bindPublicMethod(\"checkSttModelAvailable\"),\n validateSttModel: bindPublicMethod(\"validateSttModel\"),\n extractTarBz2: bindPublicMethod(\"extractTarBz2\"),\n restartTranscriber: bindPublicMethod(\"restartTranscriber\"),\n setTtsModelDetails: bindPublicMethod(\"setTtsModelDetails\"),\n getTtsModelPath: bindPublicMethod(\"getTtsModelPath\"),\n getTtsModelLanguage: bindPublicMethod(\"getTtsModelLanguage\"),\n checkTtsModelAvailable: bindPublicMethod(\"checkTtsModelAvailable\"),\n validateTtsModel: bindPublicMethod(\"validateTtsModel\"),\n generateTtsAudio: bindPublicMethod(\"generateTtsAudio\"),\n})\n\nexport default BluetoothSdk\n\nexport {\n CAMERA_FOV_DEFAULT,\n CAMERA_FOV_MAX,\n CAMERA_FOV_MIN,\n DeviceModels,\n isBusyGlassesConnectionStatus,\n isConnectedGlassesConnectionStatus,\n isConnectedWifiStatus,\n isEnabledHotspotStatus,\n isReadyGlassesConnectionStatus,\n} from \"./BluetoothSdk.types\"\n\nexport type {\n AccelEvent,\n Ar99OtaStatusEvent,\n AudioConnectedEvent,\n AudioDisconnectedEvent,\n AudioPairingNeededEvent,\n BatteryStatusEvent,\n BluetoothSdkEvent,\n BluetoothSdkEventListener,\n BluetoothSdkEventMap,\n BluetoothSdkEventName,\n BluetoothSdkPublicModule as BluetoothSdkModule,\n BluetoothSdkSubscription,\n ButtonPhotoSize,\n PhotoCaptureDefaults,\n ButtonPressEvent,\n CameraFovPreset,\n CameraFovRequest,\n CameraFovResult,\n CameraRoiPosition,\n CompatibleGlassesSearchStopEvent,\n ConnectOptions,\n ConnectedGlassesConnectionStatus,\n ConnectedWifiStatus,\n Device,\n DeviceModel,\n EnabledHotspotStatus,\n GalleryStatusEvent,\n GlassesConnectionStatus,\n GlassesMediaVolumeGetResult,\n GlassesMediaVolumeSetResult,\n GlassesNotReadyEvent,\n HeadUpEvent,\n HotspotErrorEvent,\n HotspotStatus,\n HotspotStatusChangeEvent,\n GlassesSessionChangedEvent,\n KeepAliveAckEvent,\n LocalTranscriptionEvent,\n LogEvent,\n MicLc3Event,\n MicHealthEvent,\n MicMode,\n MicPcmEvent,\n MicPreference,\n OtaStartAckEvent,\n OtaProgress,\n OtaProgressStatus,\n OtaStage,\n OtaStatus,\n OtaStatusEvent,\n OtaQueryResult,\n OtaUpdateInfo,\n ReleaseChangelog,\n MtkUpdateCompleteEvent,\n PairFailureEvent,\n PairingInfoEvent,\n EnteringPairingModeEvent,\n OwnerReplacedEvent,\n PhotoCaptureMetadata,\n PhotoResolvedConfig,\n PhotoCompression,\n PhotoFpsRange,\n PhotoMeteredPreview,\n PhotoMode,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n PhotoTransferMethod,\n PhotoResponseEvent,\n PhotoRequestedCaptureConfig,\n PhotoRequestParams,\n PhotoSize,\n PhotoStatusEvent,\n PhotoStatusState,\n PhotoSuccessResponseEvent,\n RgbLedAction,\n RgbLedColor,\n RgbLedControlResponseEvent,\n RgbLedControlSuccessResponseEvent,\n ScanModelOptions,\n ScanDiagnostic,\n ScanOptions,\n ScanResultsCallback,\n SpeakingStatusEvent,\n SettingsAckEvent,\n SettingsAckSetting,\n SettingsAckSuccessEvent,\n SettingsAckSuccessStatus,\n SettingsAckStatus,\n StreamAudioConfig,\n StreamResolvedConfig,\n StreamStartRequest,\n StreamStatusEvent,\n StreamStatusLifecycleState,\n StreamStatusReconnectState,\n StreamStatusState,\n StreamVideoConfig,\n SwipeVolumeStatusEvent,\n SwitchStatusEvent,\n TouchEvent,\n VideoRecordingDefaults,\n VideoRecordingStartedStatusEvent,\n VideoRecordingStatusEvent,\n VideoRecordingStatusState,\n VideoRecordingStoppedStatusEvent,\n VideoRecordingSuccessStatusEvent,\n VersionInfoEvent,\n VersionInfoResult,\n WifiScanResultEvent,\n VoiceActivityDetectionStatusEvent,\n WifiSearchResult,\n WifiStatus,\n WifiStatusChangeEvent,\n} from \"./BluetoothSdk.types\"\n\nexport type {NativeNotificationConfig, NativeNotificationStatus, NativeNotificationDelivery} from \"./BluetoothSdk.types\"\n"]}
@@ -55,6 +55,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
55
55
  "save_setting",
56
56
  "local_transcription",
57
57
  "phone_notification",
58
+ "native_notification_status",
59
+ "native_notification_delivery",
58
60
  "phone_notification_dismissed",
59
61
  "ws_text",
60
62
  "ws_bin",
@@ -297,6 +299,27 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
297
299
  }
298
300
  }
299
301
 
302
+ AsyncFunction("configureNativeNotifications") { (values: [String: Any]) in
303
+ try await MainActor.run {
304
+ try self.bluetoothSdk().configureNativeNotifications(NativeNotificationConfig(
305
+ enabled: values["enabled"] as? Bool ?? false,
306
+ autoDisplay: values["autoDisplay"] as? Bool ?? true,
307
+ durationSeconds: (values["durationSeconds"] as? NSNumber)?.intValue ?? 5,
308
+ doNotDisturb: values["doNotDisturb"] as? Bool ?? false,
309
+ blockedApps: values["blockedApps"] as? [String] ?? []
310
+ ))
311
+ }
312
+ }
313
+ AsyncFunction("getNativeNotificationStatus") {
314
+ await MainActor.run { self.bluetoothSdk().getNativeNotificationStatus().dictionary }
315
+ }
316
+
317
+ // MARK: - Native Notification Centre
318
+
319
+ AsyncFunction("sendPhoneNotification") { (_: [String: Any]) in
320
+ throw NativeNotificationError.unsupported
321
+ }
322
+
300
323
  // MARK: - WiFi Commands
301
324
 
302
325
  AsyncFunction("requestWifiScan") {
@@ -11,7 +11,7 @@ enum BluetoothSdkDefaults {
11
11
  static let voiceActivityDetectionEnabled = false
12
12
  static let loudnessGateEnabled = false
13
13
  private static let infoSdkVersionKey = "MentraBluetoothSdkVersion"
14
- private static let swiftPackageSdkVersion = "3.2.0-dev.181"
14
+ private static let swiftPackageSdkVersion = "3.2.0-dev.190"
15
15
  private static let swiftPackageSdkVersionPlaceholder = "__MENTRA" + "_BLUETOOTH_SDK_VERSION__"
16
16
 
17
17
  private static func normalizedSdkVersion(_ value: String?) -> String? {
@@ -3,9 +3,9 @@ import Foundation
3
3
  /// Generated by release CI. Do not edit in a release checkout.
4
4
  enum GeneratedReleaseMetadata {
5
5
  static let familyBaseVersion = "3.2.0"
6
- static let releaseIdentity = "3.2.0-dev.181"
7
- static let releaseSetId = "mentra-3.2.0-dev.181"
8
- static let sourceCommit = "89a4792321ce95bd16aadd721bc3dbc20e338861"
9
- static let otaManifestUrl = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.181.json"
10
- static let otaManifestSha256 = "d9a100cc81733d02848a86c7617eff154f4cd9d714d1d36407789aeebbf71251"
6
+ static let releaseIdentity = "3.2.0-dev.190"
7
+ static let releaseSetId = "mentra-3.2.0-dev.190"
8
+ static let sourceCommit = "9cec54c1238d6d52a90e2edc84f0b99692b8a81a"
9
+ static let otaManifestUrl = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.190.json"
10
+ static let otaManifestSha256 = "00504171cce268dae2a22d9c4156cb7bac0b8a4397b35a3890ed561c619e5df0"
11
11
  }
@@ -498,6 +498,16 @@ public final class MentraBluetoothSDK {
498
498
  DeviceManager.shared.showDashboard()
499
499
  }
500
500
 
501
+ public func configureNativeNotifications(_ config: NativeNotificationConfig) throws {
502
+ try config.validate()
503
+ guard let driver = DeviceManager.shared.sgc else { throw NativeNotificationError.notConnected }
504
+ try driver.configureNativeNotifications(config)
505
+ }
506
+
507
+ public func getNativeNotificationStatus() -> NativeNotificationStatus {
508
+ DeviceManager.shared.sgc?.getNativeNotificationStatus() ?? .unavailable
509
+ }
510
+
501
511
  public func showNotificationsPanel() {
502
512
  DeviceManager.shared.showNotificationsPanel()
503
513
  }
@@ -0,0 +1,52 @@
1
+ import Foundation
2
+
3
+ /// Desired notification-centre controls. Submission does not imply firmware acknowledgement.
4
+ public struct NativeNotificationConfig: Equatable, Sendable {
5
+ public var enabled: Bool
6
+ public var autoDisplay: Bool
7
+ public var durationSeconds: Int
8
+ public var doNotDisturb: Bool
9
+ public var blockedApps: [String]
10
+
11
+ public init(enabled: Bool = false, autoDisplay: Bool = true, durationSeconds: Int = 5, doNotDisturb: Bool = false, blockedApps: [String] = []) {
12
+ self.enabled = enabled
13
+ self.autoDisplay = autoDisplay
14
+ self.durationSeconds = durationSeconds
15
+ self.doNotDisturb = doNotDisturb
16
+ self.blockedApps = blockedApps
17
+ }
18
+
19
+ func validate() throws {
20
+ guard (1 ... 30).contains(durationSeconds) else { throw NativeNotificationError.invalidDuration }
21
+ }
22
+
23
+ func validateForAncs() throws {
24
+ try validate()
25
+ guard blockedApps.isEmpty else { throw NativeNotificationError.unsupported }
26
+ }
27
+
28
+ var dictionary: [String: Any] {
29
+ ["enabled": enabled, "autoDisplay": autoDisplay, "durationSeconds": durationSeconds, "doNotDisturb": doNotDisturb, "blockedApps": blockedApps]
30
+ }
31
+ }
32
+
33
+ public enum NativeNotificationError: Error {
34
+ case unsupported
35
+ case notConnected
36
+ case invalidDuration
37
+ }
38
+
39
+ public struct NativeNotificationStatus: Sendable {
40
+ public let supported: Bool
41
+ public let source: String
42
+ public let authorization: String
43
+ public let state: String
44
+ public let config: NativeNotificationConfig
45
+ public let error: String
46
+
47
+ static let unavailable = NativeNotificationStatus(supported: false, source: "unsupported", authorization: "unknown", state: "unavailable", config: .init(), error: "")
48
+
49
+ var dictionary: [String: Any] {
50
+ ["supported": supported, "source": source, "authorization": authorization, "state": state, "config": config.dictionary, "error": error]
51
+ }
52
+ }
@@ -8,8 +8,8 @@
8
8
 
9
9
  import Combine
10
10
  import CoreBluetooth
11
- import Foundation
12
11
  import CoreGraphics
12
+ import Foundation
13
13
 
14
14
  /// Keep a low-rate, non-audio EvenHub sensor stream active so iOS continues receiving BLE
15
15
  /// notifications while the Mentra App is backgrounded. The samples stay internal unless IMU was
@@ -890,7 +890,7 @@ private enum EvenAIProto {
890
890
 
891
891
  // MARK: - Notification Protobuf Builders (notification.proto, service ID 4)
892
892
 
893
- private enum NotificationProto {
893
+ enum G2NotificationProto {
894
894
  /// NotificationDataPackage with commandId=NOTIFICATION_IOS (2), carrying
895
895
  /// `NotificationIOS { appID, displayName }`. We saw the glasses emit this
896
896
  /// inbound after "Hey Even, show notifications" with appID="com.burbn.instagram";
@@ -1714,8 +1714,13 @@ class G2: NSObject, SGCManager {
1714
1714
  // The pace replaces the gate's overflow protection (the original reason for gating). Any packet
1715
1715
  // CoreBluetooth still drops self-heals: text is re-sent (TextContainer.pendingSends) and image
1716
1716
  // fragments retry on their ACK (`awaitImageAck`).
1717
- private var leftWriteQueue: [Data] = []
1718
- private var rightWriteQueue: [Data] = []
1717
+ private struct PendingWrite {
1718
+ let data: Data
1719
+ let notificationEpoch: Int?
1720
+ }
1721
+
1722
+ private var leftWriteQueue: [PendingWrite] = []
1723
+ private var rightWriteQueue: [PendingWrite] = []
1719
1724
  private var leftDraining = false
1720
1725
  private var rightDraining = false
1721
1726
  // Pace between consecutive packets (~G1's chunk pacing). Off any external callback, so the drain
@@ -1725,13 +1730,14 @@ class G2: NSObject, SGCManager {
1725
1730
  // making progress). Rate-limited. Prefixed "BGCAP:" so it's easy to grep/strip after validation.
1726
1731
  private var bgcapDepthLogAt: Double = 0
1727
1732
 
1728
- private func sendToGlasses(_ packets: [Data], left: Bool = false, right: Bool = true) {
1733
+ private func sendToGlasses(_ packets: [Data], left: Bool = false, right: Bool = true, notificationEpoch: Int? = nil) {
1734
+ let writes = packets.map { PendingWrite(data: $0, notificationEpoch: notificationEpoch) }
1729
1735
  if right {
1730
- rightWriteQueue.append(contentsOf: packets)
1736
+ rightWriteQueue.append(contentsOf: writes)
1731
1737
  startDrain(right: true)
1732
1738
  }
1733
1739
  if left {
1734
- leftWriteQueue.append(contentsOf: packets)
1740
+ leftWriteQueue.append(contentsOf: writes)
1735
1741
  startDrain(right: false)
1736
1742
  }
1737
1743
  }
@@ -1777,7 +1783,8 @@ class G2: NSObject, SGCManager {
1777
1783
  }
1778
1784
  }
1779
1785
  let packet = right ? rightWriteQueue.removeFirst() : leftWriteQueue.removeFirst()
1780
- peripheral.writeValue(packet, for: char, type: .withoutResponse)
1786
+ if let epoch = packet.notificationEpoch, epoch != notificationEpoch { continue }
1787
+ peripheral.writeValue(packet.data, for: char, type: .withoutResponse)
1781
1788
  try? await Task.sleep(nanoseconds: writePaceNanos)
1782
1789
  }
1783
1790
  }
@@ -2659,7 +2666,7 @@ class G2: NSObject, SGCManager {
2659
2666
  // "G2: sendImageData(\(containerName)) - \(fragmentCount) fragments, \(bmpData.count) bytes"
2660
2667
  // )
2661
2668
 
2662
- for attempt in 1...IMG_MAX_ATTEMPTS {
2669
+ for _ in 1 ... IMG_MAX_ATTEMPTS {
2663
2670
  // One session id per WHOLE image transfer (per attempt). The glasses key their
2664
2671
  // reassembly buffer on MapSessionId, so every fragment of this image must reuse the
2665
2672
  // same session id with an incrementing MapFragmentIndex; the per-fragment ACK is
@@ -3659,6 +3666,8 @@ class G2: NSObject, SGCManager {
3659
3666
  func disconnect() {
3660
3667
  Bridge.log("G2: disconnect()")
3661
3668
  isDisconnecting = true
3669
+ notificationEpoch += 1
3670
+ notificationControlMagic = nil
3662
3671
  clearDisplay()
3663
3672
  cancelPairingTimeout()
3664
3673
  stopHeartbeats()
@@ -3807,6 +3816,88 @@ class G2: NSObject, SGCManager {
3807
3816
  sendEvenAICommand(payload)
3808
3817
  }
3809
3818
 
3819
+ private var notificationConfig = NativeNotificationConfig()
3820
+ private var notificationEpoch = 0
3821
+ private var notificationControlMagic: Int32?
3822
+ private var notificationError = ""
3823
+
3824
+ private var notificationAuthorization: String {
3825
+ #if os(macOS)
3826
+ return "unknown"
3827
+ #else
3828
+ let connected = [leftPeripheral, rightPeripheral].compactMap { $0 }.filter { $0.state == .connected }
3829
+ guard !connected.isEmpty else { return "unknown" }
3830
+ return connected.contains { $0.ancsAuthorized } ? "authorized" : "not_authorized"
3831
+ #endif
3832
+ }
3833
+
3834
+ func getNativeNotificationStatus() -> NativeNotificationStatus {
3835
+ #if os(macOS)
3836
+ return .unavailable
3837
+ #else
3838
+ let ready = DeviceStore.shared.get("glasses", "fullyBooted") as? Bool ?? false
3839
+ return NativeNotificationStatus(
3840
+ supported: true, source: "ancs", authorization: notificationAuthorization,
3841
+ state: !ready ? "unavailable" : !notificationError.isEmpty ? "failed" : notificationConfig.enabled ? "submitted" : "disabled",
3842
+ config: notificationConfig, error: notificationError
3843
+ )
3844
+ #endif
3845
+ }
3846
+
3847
+ private func publishNotificationStatus() {
3848
+ Bridge.sendTypedMessage("native_notification_status", body: getNativeNotificationStatus().dictionary)
3849
+ }
3850
+
3851
+ func configureNativeNotifications(_ config: NativeNotificationConfig) throws {
3852
+ #if os(macOS)
3853
+ throw NativeNotificationError.unsupported
3854
+ #else
3855
+ try config.validateForAncs()
3856
+ notificationConfig = config
3857
+ applyNotificationControls()
3858
+ #endif
3859
+ }
3860
+
3861
+ private func applyNotificationControls() {
3862
+ #if os(macOS)
3863
+ publishNotificationStatus()
3864
+ return
3865
+ #endif
3866
+ notificationControlMagic = nil
3867
+ notificationEpoch += 1 // Discard unsent controls from the previous settings/connection.
3868
+ notificationError = ""
3869
+ guard DeviceStore.shared.get("glasses", "fullyBooted") as? Bool == true else {
3870
+ publishNotificationStatus()
3871
+ return
3872
+ }
3873
+ let config = notificationConfig
3874
+ // Never enable ANCS presentation before the accessory is authorized. Disabling
3875
+ // still reaches firmware after permission revocation.
3876
+ let enabled = config.enabled && notificationAuthorization == "authorized"
3877
+ let magic = sendManager.nextMagicRandom()
3878
+ notificationControlMagic = magic
3879
+ let payload = G2NotificationProto.notificationCtrl(
3880
+ magicRandom: magic, notifEnable: enabled ? 1 : 0,
3881
+ autoDispEnable: config.autoDisplay ? 1 : 0,
3882
+ dispTime: Int32(config.durationSeconds), avoidDisturbEnable: config.doNotDisturb ? 1 : 0
3883
+ )
3884
+ sendToGlasses(sendManager.buildPackets(serviceId: ServiceID.notification.rawValue, payload: payload, reserveFlag: true), notificationEpoch: notificationEpoch)
3885
+ // Preserve the firmware's existing iOS app whitelist. Android package filters
3886
+ // cannot safely be applied to the ANCS stream or translated to bundle IDs.
3887
+ publishNotificationStatus()
3888
+ }
3889
+
3890
+ private var notificationConnectionOptions: [String: Any] {
3891
+ var options: [String: Any] = [
3892
+ CBConnectPeripheralOptionNotifyOnConnectionKey: true,
3893
+ CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,
3894
+ ]
3895
+ #if !os(macOS)
3896
+ options[CBConnectPeripheralOptionRequiresANCS] = true
3897
+ #endif
3898
+ return options
3899
+ }
3900
+
3810
3901
  /// Open the on-glasses notification panel — same effect as the user saying
3811
3902
  /// "Hey Even, show notifications". Replicates the official-app voice flow:
3812
3903
  /// 1. CTRL{status=ENTER} — puts glasses in AI session
@@ -4102,10 +4193,7 @@ class G2: NSObject, SGCManager {
4102
4193
  device.discoverServices([G2BLE.SERVICE_UUID])
4103
4194
  centralManager!.connect(
4104
4195
  leftPeripheral!,
4105
- options: [
4106
- CBConnectPeripheralOptionNotifyOnConnectionKey: true,
4107
- CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,
4108
- ]
4196
+ options: notificationConnectionOptions
4109
4197
  )
4110
4198
  } else if name.contains("_R_") && serialNumber.contains(DEVICE_SEARCH_ID) {
4111
4199
  rightPeripheral = device
@@ -4113,10 +4201,7 @@ class G2: NSObject, SGCManager {
4113
4201
  device.discoverServices([G2BLE.SERVICE_UUID])
4114
4202
  centralManager!.connect(
4115
4203
  rightPeripheral!,
4116
- options: [
4117
- CBConnectPeripheralOptionNotifyOnConnectionKey: true,
4118
- CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,
4119
- ]
4204
+ options: notificationConnectionOptions
4120
4205
  )
4121
4206
  }
4122
4207
  // we can't emit the serial number here unfortunately:
@@ -4180,8 +4265,8 @@ class G2: NSObject, SGCManager {
4180
4265
  rightPeripheral = right
4181
4266
  left.delegate = self
4182
4267
  right.delegate = self
4183
- centralManager?.connect(left, options: nil)
4184
- centralManager?.connect(right, options: nil)
4268
+ centralManager?.connect(left, options: notificationConnectionOptions)
4269
+ centralManager?.connect(right, options: notificationConnectionOptions)
4185
4270
  return true
4186
4271
  }
4187
4272
 
@@ -4231,6 +4316,7 @@ class G2: NSObject, SGCManager {
4231
4316
  // MARK: - Incoming Data Handling
4232
4317
 
4233
4318
  private func handleNotifyData(_ data: Data, from peripheral: CBPeripheral) async {
4319
+ guard peripheral === leftPeripheral || peripheral === rightPeripheral else { return }
4234
4320
  // Distinguish left vs right peripheral so multi-packet reassembly doesn't collide
4235
4321
  let sourceKey = peripheral === leftPeripheral ? "L" : "R"
4236
4322
  guard let result = receiveManager.handlePacket(data, sourceKey: sourceKey) else { return }
@@ -4286,6 +4372,16 @@ class G2: NSObject, SGCManager {
4286
4372
  let fields = reader.parseFields()
4287
4373
  let cmd = fields[1] as? Int32 ?? 0
4288
4374
 
4375
+ if cmd == 161, let response = fields[5] as? Data {
4376
+ var responseReader = ProtobufReader(response)
4377
+ let values = responseReader.parseFields()
4378
+ if fields[2] as? Int32 == notificationControlMagic,
4379
+ values[1] as? Int32 == 1, let code = values[2] as? Int32, code != 0
4380
+ {
4381
+ notificationError = "configuration_rejected_\(values[2] as? Int32 ?? -1)"
4382
+ publishNotificationStatus()
4383
+ }
4384
+ }
4289
4385
  var detail = ""
4290
4386
  if let iosData = fields[4] as? Data {
4291
4387
  var ios = ProtobufReader(iosData)
@@ -4525,6 +4621,7 @@ class G2: NSObject, SGCManager {
4525
4621
  }
4526
4622
  if !isFullyBooted {
4527
4623
  DeviceStore.shared.apply("glasses", "fullyBooted", true)
4624
+ applyNotificationControls()
4528
4625
  }
4529
4626
  }
4530
4627
 
@@ -5157,14 +5254,14 @@ extension G2: CBCentralManagerDelegate {
5157
5254
  if self.leftPeripheral == nil {
5158
5255
  self.leftPeripheral = peripheral
5159
5256
  peripheral.delegate = self
5160
- central.connect(peripheral, options: nil)
5257
+ central.connect(peripheral, options: self.notificationConnectionOptions)
5161
5258
  // Bridge.log("G2: Connecting to LEFT: \(name)")
5162
5259
  }
5163
5260
  } else if name.contains("_R_") {
5164
5261
  if self.rightPeripheral == nil {
5165
5262
  self.rightPeripheral = peripheral
5166
5263
  peripheral.delegate = self
5167
- central.connect(peripheral, options: nil)
5264
+ central.connect(peripheral, options: self.notificationConnectionOptions)
5168
5265
  // Bridge.log("G2: Connecting to RIGHT: \(name)")
5169
5266
  }
5170
5267
  }
@@ -5177,6 +5274,15 @@ extension G2: CBCentralManagerDelegate {
5177
5274
  }
5178
5275
  }
5179
5276
 
5277
+ #if !os(macOS)
5278
+ nonisolated func centralManager(_: CBCentralManager, didUpdateANCSAuthorizationFor peripheral: CBPeripheral) {
5279
+ DispatchQueue.main.async { [weak self] in
5280
+ guard let self, peripheral === self.leftPeripheral || peripheral === self.rightPeripheral else { return }
5281
+ self.applyNotificationControls()
5282
+ }
5283
+ }
5284
+ #endif
5285
+
5180
5286
  nonisolated func centralManager(_: CBCentralManager, didConnect peripheral: CBPeripheral) {
5181
5287
  DispatchQueue.main.async { [weak self] in
5182
5288
  guard let self = self else { return }
@@ -5209,6 +5315,9 @@ extension G2: CBCentralManagerDelegate {
5209
5315
  let side = peripheral === self.leftPeripheral ? "LEFT" : "RIGHT"
5210
5316
  Bridge.log("G2: Disconnected \(side): \(error?.localizedDescription ?? "clean")")
5211
5317
 
5318
+ guard peripheral === self.leftPeripheral || peripheral === self.rightPeripheral else { return }
5319
+ self.notificationEpoch += 1
5320
+ self.notificationControlMagic = nil
5212
5321
  // Only reconnect if not intentionally disconnecting
5213
5322
  if self.isDisconnecting { return }
5214
5323
 
@@ -5233,6 +5342,7 @@ extension G2: CBCentralManagerDelegate {
5233
5342
  self.dashboardOpening = false
5234
5343
  DeviceStore.shared.apply("glasses", "connected", false)
5235
5344
  DeviceStore.shared.apply("glasses", "fullyBooted", false)
5345
+ self.publishNotificationStatus()
5236
5346
 
5237
5347
  // Start persistent reconnection loop (every 30s, unlimited attempts)
5238
5348
  self.startReconnectionTimer()
@@ -145,6 +145,14 @@ protocol SGCManager {
145
145
 
146
146
  func showNotificationsPanel() async
147
147
 
148
+ /// Push a phone notification into the glasses' own notification centre.
149
+ ///
150
+ /// Android/G2 only in practice — iOS glasses read notifications over ANCS. Declared for
151
+ /// parity so the shared TypeScript module type is callable on both platforms.
152
+ func sendPhoneNotification(_ notification: [String: Any]) throws
153
+ func configureNativeNotifications(_ config: NativeNotificationConfig) throws
154
+ func getNativeNotificationStatus() -> NativeNotificationStatus
155
+
148
156
  // MARK: - Calendar Events
149
157
 
150
158
  func sendCalendarEvents(_ events: [[String: Any]])
@@ -357,6 +365,20 @@ extension SGCManager {
357
365
 
358
366
  func showNotificationsPanel() async {}
359
367
 
368
+ // MARK: - Native notification centre (default no-op — Android/G2 only; iOS uses ANCS)
369
+
370
+ func sendPhoneNotification(_: [String: Any]) throws {
371
+ throw NativeNotificationError.unsupported
372
+ }
373
+
374
+ func configureNativeNotifications(_: NativeNotificationConfig) throws {
375
+ throw NativeNotificationError.unsupported
376
+ }
377
+
378
+ func getNativeNotificationStatus() -> NativeNotificationStatus {
379
+ .unavailable
380
+ }
381
+
360
382
  // MARK: - IMU (default no-op — only G2 streams accelerometer data)
361
383
 
362
384
  func setImuEnabled(_: Bool) async {
@@ -0,0 +1,20 @@
1
+ @testable import MentraBluetoothSDK
2
+ import XCTest
3
+
4
+ final class NativeNotificationTests: XCTestCase {
5
+ func testDisableIsExplicitAndQuietModeDoesNotEnablePopups() {
6
+ let bytes = G2NotificationProto.notificationCtrl(magicRandom: 7, notifEnable: 0, autoDispEnable: 0, dispTime: 10, avoidDisturbEnable: 1)
7
+ XCTAssertEqual(bytes, Data([0x08, 1, 0x10, 7, 0x1A, 8, 0x08, 0, 0x10, 0, 0x18, 10, 0x28, 1]))
8
+ }
9
+
10
+ func testConfigRejectsInvalidDurationBeforeEncoding() {
11
+ XCTAssertThrowsError(try NativeNotificationConfig(durationSeconds: 0).validate())
12
+ XCTAssertThrowsError(try NativeNotificationConfig(durationSeconds: 31).validate())
13
+ XCTAssertNoThrow(try NativeNotificationConfig(durationSeconds: 30).validate())
14
+ }
15
+
16
+ func testAncsRejectsPhoneSideFiltering() {
17
+ XCTAssertThrowsError(try NativeNotificationConfig(blockedApps: ["com.example"]).validateForAncs())
18
+ XCTAssertNoThrow(try NativeNotificationConfig(blockedApps: []).validateForAncs())
19
+ }
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/bluetooth-sdk",
3
- "version": "3.2.0-dev.181",
3
+ "version": "3.2.0-dev.190",
4
4
  "description": "SDK for communicating with smart glasses",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -924,6 +924,8 @@ export type BluetoothSdkModuleEvents = {
924
924
  speaking_status: (event: SpeakingStatusEvent) => void
925
925
  battery_status: (event: BatteryStatusEvent) => void
926
926
  local_transcription: (event: LocalTranscriptionEvent) => void
927
+ native_notification_status: (event: NativeNotificationStatus) => void
928
+ native_notification_delivery: (event: NativeNotificationDelivery) => void
927
929
  phone_notification: (event: PhoneNotificationEvent) => void
928
930
  phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void
929
931
  wifi_status_change: (event: WifiStatusChangeEvent) => void
@@ -1004,6 +1006,54 @@ export interface PhoneNotificationDismissedEvent {
1004
1006
  timestamp: number
1005
1007
  }
1006
1008
 
1009
+ /**
1010
+ * Outbound payload for `sendPhoneNotification` — a notification pushed INTO the glasses' own
1011
+ * notification centre. The inverse of {@link PhoneNotificationEvent}, which reports
1012
+ * notifications the glasses relayed TO the phone (iOS/ANCS). Drivers map these keys onto
1013
+ * whatever their firmware expects.
1014
+ */
1015
+ export interface NativeNotificationConfig {
1016
+ enabled: boolean
1017
+ autoDisplay: boolean
1018
+ durationSeconds: number
1019
+ doNotDisturb: boolean
1020
+ /** Android package names. iOS rejects nonempty lists; its ANCS filter is firmware-owned. */
1021
+ blockedApps: string[]
1022
+ }
1023
+
1024
+ export interface NativeNotificationStatus {
1025
+ supported: boolean
1026
+ source: "phone" | "ancs" | "unsupported"
1027
+ authorization: "system" | "authorized" | "not_authorized" | "unknown"
1028
+ /** `submitted` is not a firmware-confirmed acknowledgement. */
1029
+ state: "unavailable" | "disabled" | "configuring" | "submitted" | "needs_reconnect" | "failed"
1030
+ config: NativeNotificationConfig
1031
+ error: string
1032
+ }
1033
+
1034
+ export interface NativeNotificationDelivery {
1035
+ notificationId: string
1036
+ status: "delivered" | "failed" | "cancelled" | "dropped"
1037
+ reason: string
1038
+ }
1039
+
1040
+ export interface NativePhoneNotification {
1041
+ /** Stable id from the phone's notification listener; parsed to an int where firmware needs one. */
1042
+ notificationId: string
1043
+ /** Reverse-DNS package id of the originating app. */
1044
+ packageName: string
1045
+ /** Human app name (e.g. "Messages"). */
1046
+ appName: string
1047
+ title: string
1048
+ /** Android `android.subText`. Empty when the listener didn't capture one. */
1049
+ subtitle: string
1050
+ body: string
1051
+ /** Unix ms post time. */
1052
+ timestampMs: number
1053
+ /** Posted or updated. Removal is not supported by the verified protocol. */
1054
+ action: 0
1055
+ }
1056
+
1007
1057
  export type PublicGlassesStatus = Omit<
1008
1058
  GlassesStatus,
1009
1059
  "otaUpdateAvailable" | "otaProgress" | "otaInProgress" | "otaVersionUrl"
@@ -1024,6 +1074,8 @@ export type PublicBluetoothStatus = Pick<
1024
1074
  >
1025
1075
 
1026
1076
  export type BluetoothSdkEventMap = {
1077
+ native_notification_status: NativeNotificationStatus
1078
+ native_notification_delivery: NativeNotificationDelivery
1027
1079
  log: LogEvent
1028
1080
  device_discovered: Device
1029
1081
  default_device_changed: {device?: Device}
@@ -1087,6 +1139,8 @@ export type BluetoothSdkSubscription = {
1087
1139
  export type BluetoothSdkEvent = BluetoothSdkEventMap[BluetoothSdkEventName]
1088
1140
 
1089
1141
  export interface BluetoothSdkPublicModule {
1142
+ configureNativeNotifications(config: NativeNotificationConfig): Promise<void>
1143
+ getNativeNotificationStatus(): Promise<NativeNotificationStatus>
1090
1144
  addListener<EventName extends BluetoothSdkEventName>(
1091
1145
  eventName: EventName,
1092
1146
  listener: BluetoothSdkEventListener<EventName>,