@glassly/bluetooth-sdk 0.1.0-dev.100

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 (553) hide show
  1. package/README.md +563 -0
  2. package/android/build.gradle +450 -0
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/CMakeLists.txt +6 -0
  5. package/android/lc3Lib/build.gradle +212 -0
  6. package/android/lc3Lib/proguard-rules.pro +33 -0
  7. package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
  8. package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
  9. package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
  10. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
  11. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
  12. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
  13. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
  14. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
  15. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
  16. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
  17. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
  18. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
  19. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
  20. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
  21. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
  22. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
  23. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
  24. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
  25. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
  26. package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
  27. package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
  28. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
  29. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
  30. package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
  31. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
  32. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
  33. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
  34. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
  35. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
  36. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
  37. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
  38. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
  39. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
  40. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
  41. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
  42. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
  43. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
  44. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
  45. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
  46. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
  47. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
  48. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
  49. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
  50. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
  51. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
  52. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
  53. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
  54. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
  55. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
  56. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
  57. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
  58. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
  59. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
  60. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
  61. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
  62. package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
  63. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
  64. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
  65. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
  66. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
  67. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
  68. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
  69. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
  70. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
  71. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
  72. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
  73. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
  74. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
  75. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
  76. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
  77. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
  78. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
  79. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
  80. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
  81. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
  82. package/android/lc3Lib/src/main/java/com/glassly/lc3Lib/Lc3Cpp.java +38 -0
  83. package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
  84. package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
  85. package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
  86. package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
  87. package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
  88. package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
  89. package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
  90. package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
  91. package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
  92. package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
  93. package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
  94. package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
  95. package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
  96. package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
  97. package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
  98. package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
  99. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
  100. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
  101. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
  102. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
  103. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
  104. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
  105. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
  106. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
  107. package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
  108. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
  109. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
  110. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
  111. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
  112. package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
  113. package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
  114. package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
  115. package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
  116. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
  117. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
  118. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
  119. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
  120. package/android/settings.gradle +4 -0
  121. package/android/silero/LICENSE.md +17 -0
  122. package/android/silero/build.gradle +50 -0
  123. package/android/silero/proguard-rules.pro +24 -0
  124. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  125. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  126. package/android/silero/src/main/assets/LICENSE +21 -0
  127. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  128. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  129. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  130. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  131. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  132. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  133. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  134. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  135. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  136. package/android/src/main/AndroidManifest.xml +42 -0
  137. package/android/src/main/assets/G2Firmware/README.md +11 -0
  138. package/android/src/main/assets/G2Firmware/g2-glassly-cfw-2.2.9.22.evenota.bin +0 -0
  139. package/android/src/main/assets/G2Firmware/g2-stock-2.2.9.22.evenota.bin +0 -0
  140. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2321 -0
  141. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
  142. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
  143. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
  144. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1087 -0
  145. package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
  146. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2472 -0
  147. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +359 -0
  148. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +9 -0
  149. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
  150. package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
  151. package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
  152. package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
  153. package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
  154. package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
  155. package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
  156. package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
  157. package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  158. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  159. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +1057 -0
  160. package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
  161. package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
  162. package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
  163. package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
  164. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
  165. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
  166. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  167. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
  168. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
  169. package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
  170. package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
  171. package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
  172. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
  173. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
  174. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
  175. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3837 -0
  176. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
  177. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6457 -0
  178. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
  179. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1253 -0
  180. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
  181. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
  182. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
  183. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
  184. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1267 -0
  185. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10742 -0
  186. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
  187. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
  188. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2008 -0
  189. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  190. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
  191. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
  192. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
  193. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
  194. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
  195. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
  196. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
  197. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
  198. package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
  199. package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
  200. package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
  201. package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
  202. package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
  203. package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
  204. package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
  205. package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +227 -0
  206. package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
  207. package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
  208. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
  209. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  210. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  211. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  212. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
  213. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
  214. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
  215. package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
  216. package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
  217. package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  218. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  219. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
  220. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
  221. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
  222. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
  223. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
  224. package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
  225. package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
  226. package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  227. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
  228. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  229. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  230. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  231. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
  232. package/android/src/main/res/values/strings.xml +4 -0
  233. package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
  234. package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
  235. package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
  236. package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
  237. package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
  238. package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
  239. package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
  240. package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
  241. package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  242. package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
  243. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
  244. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
  245. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
  246. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
  247. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
  248. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
  249. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
  250. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
  251. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
  252. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
  253. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
  254. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  255. package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
  256. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
  257. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
  258. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
  259. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
  260. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
  261. package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
  262. package/android/src/test/resources/avif_with_exif.avif +0 -0
  263. package/app.plugin.js +3 -0
  264. package/build/BluetoothSdk.types.d.ts +1390 -0
  265. package/build/BluetoothSdk.types.d.ts.map +1 -0
  266. package/build/BluetoothSdk.types.js +41 -0
  267. package/build/BluetoothSdk.types.js.map +1 -0
  268. package/build/_internal.d.ts +17 -0
  269. package/build/_internal.d.ts.map +1 -0
  270. package/build/_internal.js +16 -0
  271. package/build/_internal.js.map +1 -0
  272. package/build/_private/BluetoothSdkModule.d.ts +171 -0
  273. package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
  274. package/build/_private/BluetoothSdkModule.js +300 -0
  275. package/build/_private/BluetoothSdkModule.js.map +1 -0
  276. package/build/_private/LocalNetworkModule.d.ts +38 -0
  277. package/build/_private/LocalNetworkModule.d.ts.map +1 -0
  278. package/build/_private/LocalNetworkModule.js +3 -0
  279. package/build/_private/LocalNetworkModule.js.map +1 -0
  280. package/build/_private/cameraRequestPayload.d.ts +4 -0
  281. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  282. package/build/_private/cameraRequestPayload.js +32 -0
  283. package/build/_private/cameraRequestPayload.js.map +1 -0
  284. package/build/_private/photoRequestPayload.d.ts +6 -0
  285. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  286. package/build/_private/photoRequestPayload.js +86 -0
  287. package/build/_private/photoRequestPayload.js.map +1 -0
  288. package/build/_private/sdkOtaManifest.d.ts +11 -0
  289. package/build/_private/sdkOtaManifest.d.ts.map +1 -0
  290. package/build/_private/sdkOtaManifest.js +15 -0
  291. package/build/_private/sdkOtaManifest.js.map +1 -0
  292. package/build/changelogs.d.ts +7 -0
  293. package/build/changelogs.d.ts.map +1 -0
  294. package/build/changelogs.js +46 -0
  295. package/build/changelogs.js.map +1 -0
  296. package/build/debug.d.ts +3 -0
  297. package/build/debug.d.ts.map +1 -0
  298. package/build/debug.js +8 -0
  299. package/build/debug.js.map +1 -0
  300. package/build/generated/changelogCatalog.d.ts +12 -0
  301. package/build/generated/changelogCatalog.d.ts.map +1 -0
  302. package/build/generated/changelogCatalog.js +16 -0
  303. package/build/generated/changelogCatalog.js.map +1 -0
  304. package/build/generated/releaseMetadata.d.ts +12 -0
  305. package/build/generated/releaseMetadata.d.ts.map +1 -0
  306. package/build/generated/releaseMetadata.js +10 -0
  307. package/build/generated/releaseMetadata.js.map +1 -0
  308. package/build/index.d.ts +6 -0
  309. package/build/index.d.ts.map +1 -0
  310. package/build/index.js +170 -0
  311. package/build/index.js.map +1 -0
  312. package/build/ota-server/OtaServer.types.d.ts +14 -0
  313. package/build/ota-server/OtaServer.types.d.ts.map +1 -0
  314. package/build/ota-server/OtaServer.types.js +2 -0
  315. package/build/ota-server/OtaServer.types.js.map +1 -0
  316. package/build/ota-server/OtaServerModule.d.ts +21 -0
  317. package/build/ota-server/OtaServerModule.d.ts.map +1 -0
  318. package/build/ota-server/OtaServerModule.js +3 -0
  319. package/build/ota-server/OtaServerModule.js.map +1 -0
  320. package/build/ota-server/OtaServerModule.web.d.ts +10 -0
  321. package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
  322. package/build/ota-server/OtaServerModule.web.js +12 -0
  323. package/build/ota-server/OtaServerModule.web.js.map +1 -0
  324. package/build/ota-server/index.d.ts +3 -0
  325. package/build/ota-server/index.d.ts.map +1 -0
  326. package/build/ota-server/index.js +3 -0
  327. package/build/ota-server/index.js.map +1 -0
  328. package/build/ota-transport/index.d.ts +67 -0
  329. package/build/ota-transport/index.d.ts.map +1 -0
  330. package/build/ota-transport/index.js +58 -0
  331. package/build/ota-transport/index.js.map +1 -0
  332. package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
  333. package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
  334. package/build/photo-receiver/PhotoReceiver.types.js +2 -0
  335. package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
  336. package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
  337. package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
  338. package/build/photo-receiver/PhotoReceiverModule.js +3 -0
  339. package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
  340. package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
  341. package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
  342. package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
  343. package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
  344. package/build/photo-receiver/index.d.ts +3 -0
  345. package/build/photo-receiver/index.d.ts.map +1 -0
  346. package/build/photo-receiver/index.js +3 -0
  347. package/build/photo-receiver/index.js.map +1 -0
  348. package/build/react/index.d.ts +5 -0
  349. package/build/react/index.d.ts.map +1 -0
  350. package/build/react/index.js +4 -0
  351. package/build/react/index.js.map +1 -0
  352. package/build/react/useBluetooth.d.ts +100 -0
  353. package/build/react/useBluetooth.d.ts.map +1 -0
  354. package/build/react/useBluetooth.js +167 -0
  355. package/build/react/useBluetooth.js.map +1 -0
  356. package/build/react/useBluetoothEvent.d.ts +6 -0
  357. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  358. package/build/react/useBluetoothEvent.js +21 -0
  359. package/build/react/useBluetoothEvent.js.map +1 -0
  360. package/build/react/useBluetoothScan.d.ts +22 -0
  361. package/build/react/useBluetoothScan.d.ts.map +1 -0
  362. package/build/react/useBluetoothScan.js +135 -0
  363. package/build/react/useBluetoothScan.js.map +1 -0
  364. package/build/react/useBluetoothStatus.d.ts +16 -0
  365. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  366. package/build/react/useBluetoothStatus.js +137 -0
  367. package/build/react/useBluetoothStatus.js.map +1 -0
  368. package/build/react/useGlassesConnection.d.ts +29 -0
  369. package/build/react/useGlassesConnection.d.ts.map +1 -0
  370. package/build/react/useGlassesConnection.js +153 -0
  371. package/build/react/useGlassesConnection.js.map +1 -0
  372. package/build/types/index.d.ts +3 -0
  373. package/build/types/index.d.ts.map +1 -0
  374. package/build/types/index.js +2 -0
  375. package/build/types/index.js.map +1 -0
  376. package/expo-module.config.json +14 -0
  377. package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
  378. package/ios/BluetoothSdkModule.swift +1028 -0
  379. package/ios/GlasslyBluetoothSDK.podspec +106 -0
  380. package/ios/LocalIPv4.swift +150 -0
  381. package/ios/LocalOtaServer.swift +309 -0
  382. package/ios/LocalPhotoUploadServer.swift +754 -0
  383. package/ios/OtaServerModule.swift +147 -0
  384. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  385. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  386. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  387. package/ios/Packages/CoreObjC/attdet.c +92 -0
  388. package/ios/Packages/CoreObjC/attdet.h +44 -0
  389. package/ios/Packages/CoreObjC/bits.c +375 -0
  390. package/ios/Packages/CoreObjC/bits.h +315 -0
  391. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  392. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  393. package/ios/Packages/CoreObjC/common.h +151 -0
  394. package/ios/Packages/CoreObjC/energy.c +70 -0
  395. package/ios/Packages/CoreObjC/energy.h +43 -0
  396. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  397. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  398. package/ios/Packages/CoreObjC/lc3.c +704 -0
  399. package/ios/Packages/CoreObjC/lc3.h +313 -0
  400. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  401. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  402. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  403. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  404. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  405. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  406. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  407. package/ios/Packages/CoreObjC/mdct.c +469 -0
  408. package/ios/Packages/CoreObjC/mdct.h +57 -0
  409. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  410. package/ios/Packages/CoreObjC/meson.build +61 -0
  411. package/ios/Packages/CoreObjC/plc.c +61 -0
  412. package/ios/Packages/CoreObjC/plc.h +57 -0
  413. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  414. package/ios/Packages/CoreObjC/sns.c +880 -0
  415. package/ios/Packages/CoreObjC/sns.h +103 -0
  416. package/ios/Packages/CoreObjC/spec.c +907 -0
  417. package/ios/Packages/CoreObjC/spec.h +119 -0
  418. package/ios/Packages/CoreObjC/tables.c +3457 -0
  419. package/ios/Packages/CoreObjC/tables.h +94 -0
  420. package/ios/Packages/CoreObjC/tns.c +457 -0
  421. package/ios/Packages/CoreObjC/tns.h +99 -0
  422. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  423. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  424. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
  425. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
  426. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  427. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  428. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  429. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  430. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  431. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  432. package/ios/Packages/libbz2/module.modulemap +5 -0
  433. package/ios/Packages/libbz2/shim.h +1 -0
  434. package/ios/PhotoReceiverModule.swift +129 -0
  435. package/ios/Source/BluetoothSDK.swift +2299 -0
  436. package/ios/Source/BluetoothSDKDebug.swift +12 -0
  437. package/ios/Source/BluetoothSdkDefaults.swift +29 -0
  438. package/ios/Source/Bridge.swift +708 -0
  439. package/ios/Source/Bridging-Header.h +16 -0
  440. package/ios/Source/DeviceManager.swift +2054 -0
  441. package/ios/Source/DeviceStore.swift +350 -0
  442. package/ios/Source/GeneratedChangelogCatalog.swift +8 -0
  443. package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
  444. package/ios/Source/ObservableStore.swift +118 -0
  445. package/ios/Source/OtaManifest.swift +211 -0
  446. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  447. package/ios/Source/ReleaseChangelog.swift +65 -0
  448. package/ios/Source/Resources/G2Firmware/README.md +10 -0
  449. package/ios/Source/Resources/G2Firmware/g2-glassly-cfw-2.2.9.22.evenota.bin +0 -0
  450. package/ios/Source/Resources/G2Firmware/g2-stock-2.2.9.22.evenota.bin +0 -0
  451. package/ios/Source/audio/AudioModels.swift +178 -0
  452. package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
  453. package/ios/Source/camera/CameraModels.swift +866 -0
  454. package/ios/Source/connection/ScanSession.swift +27 -0
  455. package/ios/Source/controllers/ControllerManager.swift +184 -0
  456. package/ios/Source/controllers/R1.swift +925 -0
  457. package/ios/Source/errors/BluetoothSdkError.swift +19 -0
  458. package/ios/Source/events/BluetoothEvents.swift +342 -0
  459. package/ios/Source/internal/BleTraceLogger.swift +192 -0
  460. package/ios/Source/internal/BluetoothAvailability.swift +78 -0
  461. package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
  462. package/ios/Source/internal/ValueParsing.swift +99 -0
  463. package/ios/Source/requests/DisplayRequests.swift +82 -0
  464. package/ios/Source/services/PhoneMic.swift +662 -0
  465. package/ios/Source/sgcs/Ar99.swift +2451 -0
  466. package/ios/Source/sgcs/G1.swift +2554 -0
  467. package/ios/Source/sgcs/G2.swift +7142 -0
  468. package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
  469. package/ios/Source/sgcs/G2CfwRenderer.swift +1295 -0
  470. package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
  471. package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
  472. package/ios/Source/sgcs/G2Flasher.swift +773 -0
  473. package/ios/Source/sgcs/Mach1.swift +541 -0
  474. package/ios/Source/sgcs/MentraLive.swift +7011 -0
  475. package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
  476. package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
  477. package/ios/Source/sgcs/MentraNex.swift +3220 -0
  478. package/ios/Source/sgcs/Nimo.swift +1886 -0
  479. package/ios/Source/sgcs/SGCManager.swift +621 -0
  480. package/ios/Source/sgcs/Simulated.swift +274 -0
  481. package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
  482. package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
  483. package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
  484. package/ios/Source/status/DeviceStatus.swift +981 -0
  485. package/ios/Source/status/RuntimeState.swift +359 -0
  486. package/ios/Source/status/WifiHotspotStatus.swift +345 -0
  487. package/ios/Source/streaming/StreamModels.swift +693 -0
  488. package/ios/Source/stt/STTTools.swift +191 -0
  489. package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
  490. package/ios/Source/tts/TTSTools.swift +167 -0
  491. package/ios/Source/types/DeviceModels.swift +177 -0
  492. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  493. package/ios/Source/utils/BinaryCodec.swift +61 -0
  494. package/ios/Source/utils/BleJsonCompact.swift +405 -0
  495. package/ios/Source/utils/BleWireProtocol.swift +92 -0
  496. package/ios/Source/utils/Constants.swift +78 -0
  497. package/ios/Source/utils/Enums.swift +95 -0
  498. package/ios/Source/utils/G1Text.swift +2096 -0
  499. package/ios/Source/utils/G2NotifyFile.swift +116 -0
  500. package/ios/Source/utils/JSCExperiment.swift +241 -0
  501. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  502. package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
  503. package/ios/Source/utils/MessageChunker.swift +294 -0
  504. package/ios/Source/utils/Models.swift +135 -0
  505. package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
  506. package/ios/Source/utils/TarBz2Extractor.swift +257 -0
  507. package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
  508. package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
  509. package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
  510. package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
  511. package/ios/Tests/BluetoothSDKTests/G2NotifyFileTests.swift +91 -0
  512. package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
  513. package/ios/Tests/ReleaseChangelogTests.swift +13 -0
  514. package/package.json +129 -0
  515. package/plugin/build/index.d.ts +10 -0
  516. package/plugin/build/index.js +12 -0
  517. package/plugin/build/withAndroid.d.ts +3 -0
  518. package/plugin/build/withAndroid.js +190 -0
  519. package/plugin/build/withIos.d.ts +5 -0
  520. package/plugin/build/withIos.js +108 -0
  521. package/scripts/inject-ios-sdk-version.mjs +65 -0
  522. package/scripts/public-ota-api.test.mjs +37 -0
  523. package/scripts/verify-release-package.mjs +46 -0
  524. package/scripts/write-release-metadata.mjs +154 -0
  525. package/scripts/write-release-metadata.test.mjs +37 -0
  526. package/src/BluetoothSdk.types.ts +1659 -0
  527. package/src/_internal.ts +16 -0
  528. package/src/_private/BluetoothSdkModule.ts +683 -0
  529. package/src/_private/LocalNetworkModule.ts +50 -0
  530. package/src/_private/cameraRequestPayload.ts +34 -0
  531. package/src/_private/photoRequestPayload.ts +90 -0
  532. package/src/_private/sdkOtaManifest.ts +18 -0
  533. package/src/changelogs.ts +46 -0
  534. package/src/debug.ts +9 -0
  535. package/src/generated/changelogCatalog.ts +17 -0
  536. package/src/generated/releaseMetadata.ts +20 -0
  537. package/src/index.ts +320 -0
  538. package/src/ota-server/OtaServer.types.ts +10 -0
  539. package/src/ota-server/OtaServerModule.ts +23 -0
  540. package/src/ota-server/OtaServerModule.web.ts +17 -0
  541. package/src/ota-server/index.ts +2 -0
  542. package/src/ota-transport/index.ts +116 -0
  543. package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
  544. package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
  545. package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
  546. package/src/photo-receiver/index.ts +2 -0
  547. package/src/react/index.ts +21 -0
  548. package/src/react/useBluetooth.ts +309 -0
  549. package/src/react/useBluetoothEvent.ts +35 -0
  550. package/src/react/useBluetoothScan.ts +173 -0
  551. package/src/react/useBluetoothStatus.ts +180 -0
  552. package/src/react/useGlassesConnection.ts +202 -0
  553. package/src/types/index.ts +12 -0
@@ -0,0 +1,925 @@
1
+ //
2
+ // R1.swift
3
+ // Glassly_Manager
4
+ //
5
+ // Even Realities R1 Smart Ring controller
6
+ // Protocol reverse-engineered from BTSnoop captures and firmware analysis
7
+ //
8
+
9
+ import Combine
10
+ import CoreBluetooth
11
+ import Foundation
12
+
13
+ // MARK: - R1 BLE Constants
14
+
15
+ private enum R1BLE {
16
+ /// Ring custom service
17
+ static let SERVICE_UUID = CBUUID(string: "BAE80001-4F05-4503-8E65-3AF1F7329D1F")
18
+
19
+ // Channel 1
20
+ static let WRITE_CHAR_1 = CBUUID(string: "BAE80010-4F05-4503-8E65-3AF1F7329D1F")
21
+ static let NOTIFY_CHAR_1 = CBUUID(string: "BAE80011-4F05-4503-8E65-3AF1F7329D1F")
22
+
23
+ // Channel 2
24
+ static let WRITE_CHAR_2 = CBUUID(string: "BAE80012-4F05-4503-8E65-3AF1F7329D1F")
25
+ static let NOTIFY_CHAR_2 = CBUUID(string: "BAE80013-4F05-4503-8E65-3AF1F7329D1F")
26
+
27
+ // Standard battery service (ring may expose this)
28
+ static let BATTERY_SERVICE = CBUUID(string: "180F")
29
+ static let BATTERY_LEVEL_CHAR = CBUUID(string: "2A19")
30
+
31
+ /// Name filters for scanning
32
+ static let NAME_FILTERS = ["EVEN R1", "BCL60"]
33
+
34
+ /// Gesture protocol marker
35
+ static let GESTURE_MARKER: UInt8 = 0xFF
36
+
37
+ static let SCAN_TIMEOUT: TimeInterval = 15.0
38
+ }
39
+
40
+ // MARK: - EUS transport encoder (phone → ring, channel 2)
41
+
42
+ /// Recovered firmware contract (openCFW r1/ research): every phone→ring packet
43
+ /// is a 12-byte "model" header
44
+ /// [version, module, moduleVersion, serial LE16, status, command, subcommand,
45
+ /// totalLength LE16, crc16 LE16]
46
+ /// followed by the payload, where the phone-direction CRC-16 (CCITT rounds,
47
+ /// init 0xFFFF) runs over the compact bytes [0,1,2,3,5,6,7,8,0x00] and then
48
+ /// continues over the payload bytes. The model is carried on channel 2 inside
49
+ /// a fragment train [sequence, crc32 LE32, ≤239 bytes]; the CRC-32 (poly
50
+ /// 0x1EDC6F41, MSB-first, zero seed, no final xor) covers the whole model and
51
+ /// sequence 0 terminates the train — all our packets fit a single fragment.
52
+ private enum R1Eus {
53
+ static let VERSION: UInt8 = 100
54
+ static let MODULE_SYSTEM: UInt8 = 0x01
55
+ static let MODULE_VERSION: UInt8 = 100
56
+ /// status bit 0x02 marks a SET; advStart requires it
57
+ static let STATUS_SET: UInt8 = 0x02
58
+
59
+ /// system subcommand 0x08: payload [0x01] claims the phone role; the ring
60
+ /// requests link encryption/bonding in response
61
+ static let SUBCMD_PAIR_AUTH: UInt8 = 0x08
62
+ /// system subcommand 0x0A: 12-byte payload = two 6-byte glasses arm MACs;
63
+ /// the ring stores both targets and fast-advertises for them
64
+ static let SUBCMD_ADV_START: UInt8 = 0x0A
65
+
66
+ private static func crc16Step(_ crc: UInt16, _ byte: UInt8) -> UInt16 {
67
+ var c = (crc >> 8) | (crc << 8)
68
+ c ^= UInt16(byte)
69
+ c ^= (c & 0x00FF) >> 4
70
+ c ^= c << 12
71
+ c ^= (c & 0x00FF) << 5
72
+ return c
73
+ }
74
+
75
+ private static func phoneCrc16(header: [UInt8], payload: Data) -> UInt16 {
76
+ var crc: UInt16 = 0xFFFF
77
+ // compact header: bytes 0-3 and 5-8, then a zero placeholder
78
+ for b in [header[0], header[1], header[2], header[3], header[5], header[6], header[7], header[8], 0] {
79
+ crc = crc16Step(crc, b)
80
+ }
81
+ for b in payload {
82
+ crc = crc16Step(crc, b)
83
+ }
84
+ return crc
85
+ }
86
+
87
+ private static func crc32(_ data: Data) -> UInt32 {
88
+ var crc: UInt32 = 0
89
+ for byte in data {
90
+ crc ^= UInt32(byte) << 24
91
+ for _ in 0 ..< 8 {
92
+ crc = (crc & 0x8000_0000) != 0 ? (crc << 1) ^ 0x1EDC_6F41 : crc << 1
93
+ }
94
+ }
95
+ return crc
96
+ }
97
+
98
+ /// Build one single-fragment system-module command packet.
99
+ static func frame(subcommand: UInt8, status: UInt8, serial: UInt16, payload: Data) -> Data {
100
+ let length = UInt16(12 + payload.count)
101
+ var header: [UInt8] = [
102
+ VERSION, MODULE_SYSTEM, MODULE_VERSION,
103
+ UInt8(serial & 0xFF), UInt8(serial >> 8),
104
+ status, 0x00, subcommand,
105
+ UInt8(length & 0xFF), UInt8(length >> 8),
106
+ 0, 0,
107
+ ]
108
+ let crc16 = phoneCrc16(header: header, payload: payload)
109
+ header[10] = UInt8(crc16 & 0xFF)
110
+ header[11] = UInt8(crc16 >> 8)
111
+ var model = Data(header)
112
+ model.append(payload)
113
+
114
+ let outer = crc32(model)
115
+ var packet = Data([0x00]) // sequence 0: single-fragment train
116
+ packet.append(UInt8(outer & 0xFF))
117
+ packet.append(UInt8((outer >> 8) & 0xFF))
118
+ packet.append(UInt8((outer >> 16) & 0xFF))
119
+ packet.append(UInt8((outer >> 24) & 0xFF))
120
+ packet.append(model)
121
+ return packet
122
+ }
123
+ }
124
+
125
+ // MARK: - R1 Gesture Types
126
+
127
+ private enum R1Gesture: String {
128
+ case hold
129
+ case singleTap = "single_tap"
130
+ case doubleTap = "double_tap"
131
+ case swipeUp = "swipe_up"
132
+ case swipeDown = "swipe_down"
133
+
134
+ /// Parse gesture from notification data: [0xFF, type, param]
135
+ static func parse(from data: Data) -> R1Gesture? {
136
+ guard data.count >= 3, data[0] == R1BLE.GESTURE_MARKER else { return nil }
137
+ switch data[1] {
138
+ case 0x03:
139
+ return .hold
140
+ case 0x04:
141
+ return data[2] == 0x01 ? .singleTap : data[2] == 0x02 ? .doubleTap : nil
142
+ case 0x05:
143
+ return data[2] < 0x80 ? .swipeUp : .swipeDown
144
+ default:
145
+ return nil
146
+ }
147
+ }
148
+ }
149
+
150
+ // MARK: - R1 Controller
151
+
152
+ @MainActor
153
+ class R1: NSObject, ControllerManager {
154
+ var type = ControllerTypes.R1
155
+ let hasMic = false // R1 ring has no microphone
156
+
157
+ // Connection state
158
+ private var centralManager: CBCentralManager?
159
+ private var ringPeripheral: CBPeripheral?
160
+ private var isDisconnecting = false
161
+
162
+ // BLE characteristics
163
+ private var writeChar1: CBCharacteristic?
164
+ private var notifyChar1: CBCharacteristic?
165
+ private var writeChar2: CBCharacteristic?
166
+ private var notifyChar2: CBCharacteristic?
167
+ private var batteryLevelChar: CBCharacteristic?
168
+ private var notifySubscriptionCount = 0
169
+ private var initSequenceRun = false
170
+ private var eusSerial: UInt16 = 0
171
+
172
+ /// Device search
173
+ var DEVICE_SEARCH_ID = "NOT_SET"
174
+
175
+ /// persisted state for ease of reconnection / background connection:
176
+ /// we could store these elsewhere to be like other settings / state, but in practice they will only ever be set and used here
177
+ /// Stored UUID for background reconnection
178
+ private var ringUUID: UUID? {
179
+ get { UserDefaults.standard.string(forKey: "r1_ringUUID").flatMap { UUID(uuidString: $0) } }
180
+ set {
181
+ if let v = newValue {
182
+ UserDefaults.standard.set(v.uuidString, forKey: "r1_ringUUID")
183
+ } else {
184
+ UserDefaults.standard.removeObject(forKey: "r1_ringUUID")
185
+ }
186
+ }
187
+ }
188
+
189
+ /// maps peripheral.name to 6-byte ring MAC address:
190
+ private var ringMacAddressMap: [String: Data] {
191
+ get {
192
+ UserDefaults.standard.dictionary(forKey: "r1_ringMacAddressMap") as? [String: Data]
193
+ ?? [:]
194
+ }
195
+ set { UserDefaults.standard.set(newValue, forKey: "r1_ringMacAddressMap") }
196
+ }
197
+
198
+ private var ringMacAddress: String? {
199
+ get { UserDefaults.standard.string(forKey: "r1_ringMacAddress") }
200
+ set { UserDefaults.standard.set(newValue, forKey: "r1_ringMacAddress") }
201
+ }
202
+
203
+ /// Reconnection
204
+ private let reconnectionManager = R1ReconnectionManager()
205
+
206
+ /// Battery
207
+ @Published private var _batteryLevel: Int = -1 {
208
+ didSet {
209
+ if _batteryLevel != oldValue && _batteryLevel >= 0 {
210
+ DeviceStore.shared.apply("glasses", "controllerBatteryLevel", _batteryLevel)
211
+ // Bridge.sendBatteryStatus(level: _batteryLevel, charging: isCharging)
212
+ }
213
+ }
214
+ }
215
+
216
+ private var isCharging = false
217
+
218
+ /// Heartbeat
219
+ private var heartbeatTimer: Timer?
220
+
221
+ static let _bluetoothQueue = DispatchQueue(label: "BluetoothR1", qos: .userInitiated)
222
+
223
+ // MARK: - Init
224
+
225
+ override init() {
226
+ super.init()
227
+ }
228
+
229
+ deinit {
230
+ centralManager?.delegate = nil
231
+ ringPeripheral?.delegate = nil
232
+ }
233
+
234
+ // MARK: - BLE Scanning
235
+
236
+ @discardableResult
237
+ private func startScan() -> Bool {
238
+ Bridge.log("R1: startScan()")
239
+ if centralManager == nil {
240
+ centralManager = CBCentralManager(
241
+ delegate: self, queue: R1._bluetoothQueue,
242
+ options: [CBCentralManagerOptionShowPowerAlertKey: 0]
243
+ )
244
+ }
245
+
246
+ isDisconnecting = false
247
+ guard centralManager!.state == .poweredOn else {
248
+ Bridge.log("R1: Bluetooth not powered on")
249
+ return false
250
+ }
251
+
252
+ // Try UUID-based reconnection first
253
+ if connectByUUID() {
254
+ return true
255
+ }
256
+
257
+ centralManager!.scanForPeripherals(
258
+ withServices: nil,
259
+ options: [
260
+ CBCentralManagerScanOptionAllowDuplicatesKey: false,
261
+ ]
262
+ )
263
+ return true
264
+ }
265
+
266
+ func stopScan() {
267
+ centralManager?.stopScan()
268
+ }
269
+
270
+ private func connectByUUID() -> Bool {
271
+ // don't do this if we don't have a search id set:
272
+ if DEVICE_SEARCH_ID == "NOT_SET" || DEVICE_SEARCH_ID.isEmpty {
273
+ Bridge.log("R1: 🔵 No DEVICE_SEARCH_ID set, skipping connect by UUID")
274
+ return false
275
+ }
276
+ guard let uuid = ringUUID else { return false }
277
+ guard let ringMac = ringMacAddress else { return false }
278
+ guard let peripheral = centralManager?.retrievePeripherals(withIdentifiers: [uuid]).first
279
+ else { return false }
280
+
281
+ ringPeripheral = peripheral
282
+ peripheral.delegate = self
283
+ centralManager?.connect(peripheral, options: nil)
284
+ Bridge.log("R1: Reconnecting by UUID to \(peripheral.name ?? "ring")")
285
+ return true
286
+ }
287
+
288
+ private func matchesNameFilter(_ name: String?) -> Bool {
289
+ guard let name = name else { return false }
290
+ return R1BLE.NAME_FILTERS.contains(where: { name.contains($0) })
291
+ }
292
+
293
+ /// Extract a device identifier from the ring name (e.g. "EVEN R1_CEC5BA" -> "CEC5BA")
294
+ private func extractRingId(_ name: String) -> String? {
295
+ if let range = name.range(of: "R1_") {
296
+ let id = String(name[range.upperBound...])
297
+ return id.isEmpty ? nil : id
298
+ }
299
+ return nil
300
+ }
301
+
302
+ // MARK: - EUS commands
303
+
304
+ private func nextEusSerial() -> UInt16 {
305
+ eusSerial &+= 1
306
+ if eusSerial == 0 { eusSerial = 1 }
307
+ return eusSerial
308
+ }
309
+
310
+ /// EUS system commands go to WRITE_CHAR_2 only — channel 2 is the framed
311
+ /// transport; channel 1 speaks the separate legacy protocol.
312
+ private func sendEusCommand(_ subcommand: UInt8, payload: Data, log: String) {
313
+ guard let wc = writeChar2 else {
314
+ Bridge.log("R1: \(log): no channel-2 write characteristic")
315
+ return
316
+ }
317
+ let packet = R1Eus.frame(
318
+ subcommand: subcommand, status: R1Eus.STATUS_SET,
319
+ serial: nextEusSerial(), payload: payload
320
+ )
321
+ ringPeripheral?.writeValue(packet, for: wc, type: .withoutResponse)
322
+ Bridge.log("R1: \(log) sent (\(packet.count) bytes)")
323
+ }
324
+
325
+ // MARK: - Init Sequence
326
+
327
+ /// Runs after notify subscriptions are confirmed: claim the phone role,
328
+ /// then mark the phone link ready.
329
+ private func runInitSequence() {
330
+ guard !initSequenceRun else { return }
331
+ initSequenceRun = true
332
+
333
+ Bridge.log("R1: Running init sequence")
334
+
335
+ guard writeChar2 != nil else {
336
+ Bridge.log("R1: No channel-2 write characteristic, skipping init")
337
+ markConnected()
338
+ return
339
+ }
340
+
341
+ sendEusCommand(R1Eus.SUBCMD_PAIR_AUTH, payload: Data([0x01]), log: "pairAuth")
342
+
343
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
344
+ self?.markConnected()
345
+ }
346
+ }
347
+
348
+ /// Tells the ring which glasses may connect to it: EUS advStart whose
349
+ /// 12-byte payload is the two glasses arm MACs. The ring stores both
350
+ /// targets, fast-advertises while a glasses role is open, and accepts a
351
+ /// connection from either arm. With only one arm MAC known it is sent for
352
+ /// both targets — the ring matches against either slot.
353
+ private func connectToGlasses() {
354
+ func stored(_ key: String) -> String? {
355
+ guard let value = DeviceStore.shared.get("glasses", key) as? String, !value.isEmpty else { return nil }
356
+ return value
357
+ }
358
+ let left = stored("leftMacAddress")
359
+ ?? stored("bluetoothMacAddress")
360
+ ?? UserDefaults.standard.string(forKey: "glasses_btMacAddress")
361
+ let right = stored("rightMacAddress")
362
+ ?? UserDefaults.standard.string(forKey: "glasses_btMacAddressRight")
363
+
364
+ guard let left, let leftBytes = parseMac(left) else {
365
+ Bridge.log("R1: connectToGlasses: no glasses MAC")
366
+ return
367
+ }
368
+ let rightBytes = right.flatMap(parseMac) ?? leftBytes
369
+
370
+ // Cache so we can re-send even before the glasses are next scanned.
371
+ UserDefaults.standard.set(left, forKey: "glasses_btMacAddress")
372
+ if let right {
373
+ UserDefaults.standard.set(right, forKey: "glasses_btMacAddressRight")
374
+ }
375
+
376
+ var payload = leftBytes
377
+ payload.append(rightBytes)
378
+ sendEusCommand(R1Eus.SUBCMD_ADV_START, payload: payload, log: "advStart")
379
+ }
380
+
381
+ /// Parse a MAC string like "AA:BB:CC:DD:EE:FF" or "AABBCCDDEEFF" into 6 raw bytes.
382
+ private func parseMac(_ s: String) -> Data? {
383
+ let cleaned = s.replacingOccurrences(of: ":", with: "")
384
+ .replacingOccurrences(of: "-", with: "")
385
+ guard cleaned.count == 12 else { return nil }
386
+ var out = Data(); out.reserveCapacity(6)
387
+ var idx = cleaned.startIndex
388
+ for _ in 0 ..< 6 {
389
+ let next = cleaned.index(idx, offsetBy: 2)
390
+ guard let byte = UInt8(cleaned[idx ..< next], radix: 16) else { return nil }
391
+ out.append(byte)
392
+ idx = next
393
+ }
394
+ return out
395
+ }
396
+
397
+ private func markConnected() {
398
+ Task { await reconnectionManager.stop() }
399
+ Bridge.log("R1: Ring connected")
400
+
401
+ if let name = ringPeripheral?.name, let id = extractRingId(name) {
402
+ DeviceStore.shared.apply("bluetooth", "controller_device_name", id)
403
+ }
404
+
405
+ guard let mac = ringMacAddress else {
406
+ Bridge.log("R1: No ring MAC address found")
407
+ return
408
+ }
409
+ DeviceStore.shared.apply("glasses", "controllerMacAddress", mac)
410
+
411
+ DeviceStore.shared.apply("glasses", "controllerConnected", true)
412
+ // The phone link owns these flags: a ready phone link IS pairing
413
+ // success (fires handleControllerReady). The glasses link is layered
414
+ // on afterwards via advStart + RING_CONNECT_INFO.
415
+ DeviceStore.shared.apply("glasses", "controllerFullyBooted", true)
416
+
417
+ // tell the ring to connect to the glasses if we have it's mac address:
418
+ connectToGlasses()
419
+
420
+ // after a second, connect the glasses to the controller if needed:
421
+ Task {
422
+ try? await Task.sleep(nanoseconds: 1_000_000_000)
423
+ await DeviceManager.shared.sgc?.connectController()
424
+ }
425
+
426
+ startHeartbeat()
427
+ }
428
+
429
+ // MARK: - Heartbeat
430
+
431
+ private func startHeartbeat() {
432
+ heartbeatTimer?.invalidate()
433
+ // Simple keepalive: re-read battery every 30 seconds
434
+ heartbeatTimer = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: true) {
435
+ [weak self] _ in
436
+ DispatchQueue.main.async {
437
+ guard let self = self, DeviceStore.shared.get("glasses", "controllerConnected") as? Bool ?? false else { return }
438
+ // Read battery if we have the standard battery char
439
+ if let char = self.batteryLevelChar {
440
+ self.ringPeripheral?.readValue(for: char)
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ private func stopHeartbeat() {
447
+ heartbeatTimer?.invalidate()
448
+ heartbeatTimer = nil
449
+ }
450
+
451
+ // MARK: - Incoming Data Handling
452
+
453
+ private func handleNotification(from characteristic: CBCharacteristic, data: Data) {
454
+ let hex = data.map { String(format: "%02X", $0) }.joined(separator: " ")
455
+ Bridge.log("R1: \(String(characteristic.uuid.uuidString.suffix(4))) -> \(hex)")
456
+
457
+ let timestamp = Int64(Date().timeIntervalSince1970 * 1000)
458
+
459
+ // Gesture: [0xFF, type, param]
460
+ if data.count >= 3, data[0] == R1BLE.GESTURE_MARKER {
461
+ if let gesture = R1Gesture.parse(from: data) {
462
+ Bridge.log("R1: Gesture: \(gesture.rawValue)")
463
+ Bridge.sendTouchEvent(
464
+ deviceModel: ControllerTypes.R1,
465
+ gestureName: gesture.rawValue,
466
+ timestamp: timestamp
467
+ )
468
+ } else {
469
+ Bridge.log(
470
+ "R1: Unknown gesture type=0x\(String(format: "%02X", data[1])) param=0x\(String(format: "%02X", data[2]))"
471
+ )
472
+ }
473
+ return
474
+ }
475
+
476
+ // Battery: 2 bytes, first byte is percentage
477
+ if data.count == 2, data[0] <= 100 {
478
+ _batteryLevel = Int(data[0])
479
+ Bridge.log("R1: Battery: \(data[0])%")
480
+ return
481
+ }
482
+
483
+ // State: single byte (0x01=ready, 0x00=menu)
484
+ if data.count == 1 {
485
+ let state = data[0] == 0x01 ? "ready" : data[0] == 0x00 ? "menu" : "unknown(\(data[0]))"
486
+ Bridge.log("R1: State: \(state)")
487
+ return
488
+ }
489
+
490
+ // Longer data: check for embedded gesture marker
491
+ if data.count > 3, let ffIndex = data.firstIndex(of: R1BLE.GESTURE_MARKER),
492
+ ffIndex + 2 < data.count
493
+ {
494
+ let gestureData = Data(data[ffIndex ... ffIndex + 2])
495
+ if let gesture = R1Gesture.parse(from: gestureData) {
496
+ Bridge.log("R1: Embedded gesture: \(gesture.rawValue)")
497
+ Bridge.sendTouchEvent(
498
+ deviceModel: ControllerTypes.R1,
499
+ gestureName: gesture.rawValue,
500
+ timestamp: timestamp
501
+ )
502
+ return
503
+ }
504
+ }
505
+ }
506
+
507
+ // MARK: - Connection State Reset
508
+
509
+ private func resetConnectionState() {
510
+ ringPeripheral = nil
511
+ writeChar1 = nil
512
+ notifyChar1 = nil
513
+ writeChar2 = nil
514
+ notifyChar2 = nil
515
+ batteryLevelChar = nil
516
+ notifySubscriptionCount = 0
517
+ initSequenceRun = false
518
+ // ringMacAddress/ringUUID persist across disconnects so reconnection
519
+ // can find the ring again; only forget() clears them.
520
+ DeviceStore.shared.apply("glasses", "controllerConnected", false)
521
+ DeviceStore.shared.apply("glasses", "controllerFullyBooted", false)
522
+ }
523
+
524
+ // MARK: - Reconnection
525
+
526
+ private func startReconnectionTimer() {
527
+ Task {
528
+ await reconnectionManager.start { [weak self] in
529
+ guard let self else { return false }
530
+ if await MainActor.run(body: { DeviceStore.shared.get("glasses", "controllerConnected") as? Bool ?? false }) {
531
+ return true // already connected
532
+ }
533
+ Bridge.log("R1: Attempting reconnection...")
534
+ await MainActor.run { self.startScan() }
535
+ return false // keep trying
536
+ }
537
+ }
538
+ }
539
+
540
+ // MARK: - ControllerManager Protocol
541
+
542
+ func findCompatibleDevices() {
543
+ Bridge.log("R1: findCompatibleDevices()")
544
+ DEVICE_SEARCH_ID = "NOT_SET"
545
+ startScan()
546
+ }
547
+
548
+ func connectById(_ id: String) {
549
+ Bridge.log("R1: connectById(\(id))")
550
+ DEVICE_SEARCH_ID = id
551
+ startScan()
552
+ }
553
+
554
+ func disconnect() {
555
+ Bridge.log("R1: disconnect()")
556
+ isDisconnecting = true
557
+ stopHeartbeat()
558
+ Task { await reconnectionManager.stop() }
559
+
560
+ if let peripheral = ringPeripheral {
561
+ centralManager?.cancelPeripheralConnection(peripheral)
562
+ }
563
+ resetConnectionState()
564
+ }
565
+
566
+ func forget() {
567
+ disconnect()
568
+ ringUUID = nil
569
+ ringMacAddress = nil
570
+ DEVICE_SEARCH_ID = "NOT_SET"
571
+ centralManager?.delegate = nil
572
+ }
573
+
574
+ func cleanup() {
575
+ disconnect()
576
+ }
577
+
578
+ func getConnectedBluetoothName() -> String? {
579
+ return ringPeripheral?.name
580
+ }
581
+
582
+ func ping() {
583
+ if let char = batteryLevelChar {
584
+ ringPeripheral?.readValue(for: char)
585
+ }
586
+ }
587
+
588
+ func getBatteryStatus() {
589
+ if let char = batteryLevelChar {
590
+ ringPeripheral?.readValue(for: char)
591
+ }
592
+ }
593
+
594
+ // MARK: - No-op implementations (ring has no display/camera/wifi/mic)
595
+
596
+ func sendIncidentId(_: String, apiBaseUrl _: String?) {}
597
+ func setMicEnabled(_: Bool) {}
598
+ func sortMicRanking(list: [String]) -> [String] {
599
+ return list
600
+ }
601
+
602
+ func sendJson(_: [String: Any], wakeUp _: Bool, requireAck _: Bool) {}
603
+ func requestPhoto(_: PhotoRequest) {}
604
+ func startVideoRecording(requestId _: String, save _: Bool, sound _: Bool) {}
605
+ func stopVideoRecording(requestId _: String) {}
606
+ func startStream(_: [String: Any]) {}
607
+ func stopStream() {}
608
+ func sendStreamKeepAlive(_: [String: Any]) {}
609
+ func sendButtonPhotoSettings() {}
610
+ func sendButtonVideoRecordingSettings() {}
611
+ func sendButtonMaxRecordingTime() {}
612
+ func setBrightness(_: Int, autoMode _: Bool) {}
613
+ func clearDisplay() {}
614
+ func sendTextWall(_: String) {}
615
+ func sendDoubleTextWall(_: String, _: String) {}
616
+ func displayBitmap(base64ImageData _: String, x _: Int32? = nil, y _: Int32? = nil, width _: Int32? = nil, height _: Int32? = nil) async -> Bool {
617
+ return false
618
+ }
619
+
620
+ func showDashboard() {}
621
+ func setDashboardPosition(_: Int, _: Int) {}
622
+ func setHeadUpAngle(_: Int) {}
623
+ func setSilentMode(_: Bool) {}
624
+ func exit() {}
625
+ func sendShutdown() {
626
+ disconnect()
627
+ }
628
+
629
+ func sendReboot() {}
630
+ func sendRgbLedControl(
631
+ requestId _: String, packageName _: String?, action _: String, color _: String?, onDurationMs _: Int,
632
+ offDurationMs _: Int, count _: Int
633
+ ) {}
634
+ func requestWifiScan() {}
635
+ func sendWifiCredentials(_: String, _: String) {}
636
+ func forgetWifiNetwork(_: String) {}
637
+ func sendHotspotState(_: Bool) {}
638
+ func sendOtaStart(otaVersionUrl: String?) {}
639
+ func sendOtaQueryStatus() {}
640
+ func sendUserEmailToGlasses(_: String) {}
641
+ func queryGalleryStatus() {}
642
+ func sendGalleryMode() {}
643
+ func requestVersionInfo() {}
644
+ }
645
+
646
+ // MARK: - CBCentralManagerDelegate
647
+
648
+ extension R1: CBCentralManagerDelegate {
649
+ nonisolated func centralManagerDidUpdateState(_ central: CBCentralManager) {
650
+ let state = central.state
651
+ DispatchQueue.main.async { [weak self] in
652
+ guard let self = self else { return }
653
+ Bridge.log("R1: Bluetooth state: \(state.rawValue)")
654
+ if state == .poweredOn {
655
+ _ = self.startScan()
656
+ }
657
+ }
658
+ }
659
+
660
+ nonisolated func centralManager(
661
+ _ central: CBCentralManager,
662
+ didDiscover peripheral: CBPeripheral,
663
+ advertisementData: [String: Any],
664
+ rssi RSSI: NSNumber
665
+ ) {
666
+ let name = peripheral.name ?? advertisementData[CBAdvertisementDataLocalNameKey] as? String
667
+ let mfgData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
668
+
669
+ DispatchQueue.main.async { [weak self] in
670
+ guard let self = self else { return }
671
+ guard self.matchesNameFilter(name) else { return }
672
+
673
+ Bridge.log(
674
+ "R1: Discovered: \(name ?? "?") (RSSI: \(RSSI)) mfgData: \(mfgData?.map { String(format: "%02X", $0) }.joined(separator: " ") ?? "none")"
675
+ )
676
+
677
+ // Extract ring MAC from manufacturer data if available and store to a map name:mac
678
+ if let mfgData = mfgData {
679
+ Bridge.log(
680
+ "R1: mfgData: \(mfgData.map { String(format: "%02X", $0) }.joined(separator: " "))"
681
+ )
682
+ if mfgData.count >= 6 {
683
+ self.ringMacAddressMap[name ?? ""] = Data(mfgData.suffix(6))
684
+ }
685
+ }
686
+
687
+ // Emit discovered device
688
+ if let name = name, let id = self.extractRingId(name) {
689
+ Bridge.sendDiscoveredDevice(ControllerTypes.R1, id)
690
+ }
691
+
692
+ // If scan-only mode, don't auto-connect
693
+ guard self.DEVICE_SEARCH_ID != "NOT_SET" else { return }
694
+
695
+ // If search ID is specific, check it matches the ring name/id
696
+ if let name = name, let id = self.extractRingId(name),
697
+ self.DEVICE_SEARCH_ID != id && !name.contains(self.DEVICE_SEARCH_ID)
698
+ {
699
+ return
700
+ }
701
+
702
+ if self.ringPeripheral == nil {
703
+ self.ringPeripheral = peripheral
704
+ peripheral.delegate = self
705
+ central.connect(peripheral, options: nil)
706
+ self.stopScan()
707
+ Bridge.log("R1: Connecting to \(name ?? "ring")")
708
+ }
709
+ }
710
+ }
711
+
712
+ nonisolated func centralManager(
713
+ _: CBCentralManager, didConnect peripheral: CBPeripheral
714
+ ) {
715
+ DispatchQueue.main.async { [weak self] in
716
+ guard let self = self else { return }
717
+ Bridge.log("R1: Connected to \(peripheral.name ?? "ring")")
718
+
719
+ self.ringUUID = peripheral.identifier
720
+
721
+ guard let name = peripheral.name, let mac = self.ringMacAddressMap[name] else {
722
+ Bridge.log("R1: No MAC stored in map found for \(peripheral.name ?? "ring")")
723
+ // stop the scan, disconnect, remove the uuid, and try again as we need the mac address to connect:
724
+ self.disconnect()
725
+ self.ringUUID = nil
726
+ // we are still searching!:
727
+ DeviceStore.shared.apply("glasses", "controllerConnected", false)
728
+ DeviceStore.shared.apply("glasses", "controllerFullyBooted", false)
729
+ DeviceStore.shared.apply("glasses", "controllerSearching", true)
730
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
731
+ self.startScan()
732
+ }
733
+ return
734
+ }
735
+
736
+ self.ringMacAddress = mac.map { String(format: "%02X", $0) }.joined(separator: ":")
737
+
738
+ // Discover all services
739
+ peripheral.discoverServices(nil)
740
+ }
741
+ }
742
+
743
+ nonisolated func centralManager(
744
+ _: CBCentralManager, didFailToConnect _: CBPeripheral, error: Error?
745
+ ) {
746
+ DispatchQueue.main.async { [weak self] in
747
+ guard let self = self else { return }
748
+ Bridge.log("R1: Failed to connect: \(error?.localizedDescription ?? "unknown")")
749
+ self.resetConnectionState()
750
+ }
751
+ }
752
+
753
+ nonisolated func centralManager(
754
+ _: CBCentralManager, didDisconnectPeripheral _: CBPeripheral, error: Error?
755
+ ) {
756
+ DispatchQueue.main.async { [weak self] in
757
+ guard let self = self else { return }
758
+ Bridge.log("R1: Disconnected: \(error?.localizedDescription ?? "clean")")
759
+
760
+ self.stopHeartbeat()
761
+
762
+ if self.isDisconnecting { return }
763
+
764
+ self.resetConnectionState()
765
+ self.startReconnectionTimer()
766
+ }
767
+ }
768
+ }
769
+
770
+ // MARK: - CBPeripheralDelegate
771
+
772
+ extension R1: CBPeripheralDelegate {
773
+ nonisolated func peripheral(_ peripheral: CBPeripheral, didDiscoverServices _: Error?) {
774
+ guard let services = peripheral.services else { return }
775
+ for service in services {
776
+ // Discover ALL characteristics on every service
777
+ peripheral.discoverCharacteristics(nil, for: service)
778
+ }
779
+ }
780
+
781
+ nonisolated func peripheral(
782
+ _ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error _: Error?
783
+ ) {
784
+ guard let characteristics = service.characteristics else { return }
785
+
786
+ DispatchQueue.main.async { [weak self] in
787
+ guard let self = self else { return }
788
+
789
+ for char in characteristics {
790
+ Bridge.log("R1: char discovered: \(char.uuid)")
791
+ let props = char.properties
792
+ var propStr: [String] = []
793
+ if props.contains(.read) { propStr.append("read") }
794
+ if props.contains(.write) { propStr.append("write") }
795
+ if props.contains(.writeWithoutResponse) { propStr.append("writeNoResp") }
796
+ if props.contains(.notify) { propStr.append("notify") }
797
+ if props.contains(.indicate) { propStr.append("indicate") }
798
+ Bridge.log("R1: char \(char.uuid) props=[\(propStr.joined(separator: ","))]")
799
+
800
+ // Store known characteristics
801
+ switch char.uuid {
802
+ case R1BLE.WRITE_CHAR_1:
803
+ self.writeChar1 = char
804
+ case R1BLE.NOTIFY_CHAR_1:
805
+ self.notifyChar1 = char
806
+ case R1BLE.WRITE_CHAR_2:
807
+ self.writeChar2 = char
808
+ case R1BLE.NOTIFY_CHAR_2:
809
+ self.notifyChar2 = char
810
+ case R1BLE.BATTERY_LEVEL_CHAR:
811
+ self.batteryLevelChar = char
812
+ default:
813
+ break
814
+ }
815
+
816
+ // Subscribe to any notify/indicate characteristic
817
+ if props.contains(.notify) || props.contains(.indicate) {
818
+ peripheral.setNotifyValue(true, for: char)
819
+ }
820
+
821
+ // Read any readable characteristic (triggers pairing if encrypted)
822
+ if props.contains(.read) {
823
+ peripheral.readValue(for: char)
824
+ }
825
+ }
826
+ }
827
+ }
828
+
829
+ nonisolated func peripheral(
830
+ _: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic,
831
+ error: Error?
832
+ ) {
833
+ Bridge.log("R1: didUpdateNotificationStateFor: \(characteristic.uuid)")
834
+ DispatchQueue.main.async { [weak self] in
835
+ guard let self = self else { return }
836
+
837
+ if let error = error {
838
+ Bridge.log(
839
+ "R1: Notify error on \(characteristic.uuid): \(error.localizedDescription)"
840
+ )
841
+ return
842
+ }
843
+ Bridge.log("R1: Notify enabled on \(characteristic.uuid)")
844
+
845
+ self.notifySubscriptionCount += 1
846
+
847
+ // Run init after subscribing to at least 2 notify characteristics
848
+ if self.notifySubscriptionCount >= 2 && !self.initSequenceRun {
849
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
850
+ self?.runInitSequence()
851
+ }
852
+ }
853
+ }
854
+ }
855
+
856
+ nonisolated func peripheral(
857
+ _: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,
858
+ error: Error?
859
+ ) {
860
+ Bridge.log("R1: didUpdateValueFor1: \(characteristic.uuid)")
861
+ guard let data = characteristic.value, !data.isEmpty, error == nil else { return }
862
+ Bridge.log("R1: didUpdateValueFor: \(characteristic.uuid) data: \(data.hexEncodedString())")
863
+
864
+ DispatchQueue.main.async { [weak self] in
865
+ guard let self = self else { return }
866
+
867
+ // Standard battery service
868
+ if characteristic.uuid == R1BLE.BATTERY_LEVEL_CHAR {
869
+ self._batteryLevel = Int(data[0])
870
+ Bridge.log("R1: Battery (std): \(data[0])%")
871
+ return
872
+ }
873
+
874
+ self.handleNotification(from: characteristic, data: data)
875
+ }
876
+ }
877
+
878
+ nonisolated func peripheral(
879
+ _: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?
880
+ ) {
881
+ Bridge.log("R1: didWriteValueFor: \(characteristic.uuid)")
882
+ if let error = error {
883
+ DispatchQueue.main.async {
884
+ Bridge.log(
885
+ "R1: Write error on \(characteristic.uuid): \(error.localizedDescription)"
886
+ )
887
+ }
888
+ }
889
+ }
890
+ }
891
+
892
+ // MARK: - R1 Reconnection Manager
893
+
894
+ actor R1ReconnectionManager {
895
+ private var task: Task<Void, Never>?
896
+ private let intervalSeconds: TimeInterval
897
+ private var attempts = 0
898
+ private let maxAttempts: Int
899
+
900
+ init(intervalSeconds: TimeInterval = 30, maxAttempts: Int = -1) {
901
+ self.intervalSeconds = intervalSeconds
902
+ self.maxAttempts = maxAttempts
903
+ }
904
+
905
+ func start(onAttempt: @escaping @Sendable () async -> Bool) {
906
+ stop()
907
+ attempts = 0
908
+ task = Task {
909
+ while !Task.isCancelled {
910
+ try? await Task.sleep(nanoseconds: UInt64(intervalSeconds * 1_000_000_000))
911
+ if Task.isCancelled { break }
912
+ attempts += 1
913
+ if maxAttempts > 0, attempts > maxAttempts { break }
914
+ let done = await onAttempt()
915
+ if done { break }
916
+ }
917
+ }
918
+ }
919
+
920
+ func stop() {
921
+ task?.cancel()
922
+ task = nil
923
+ attempts = 0
924
+ }
925
+ }