munim-bluetooth 0.2.1 → 0.2.3

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.
@@ -1,11 +1,17 @@
1
1
  package com.munimbluetooth
2
2
 
3
- import com.margelo.nitro.munimbluetooth.HybridMunimBluetoothSpec
4
3
  import android.bluetooth.*
5
4
  import android.bluetooth.le.*
6
5
  import android.content.Context
6
+ import android.os.Build
7
7
  import android.os.ParcelUuid
8
8
  import android.util.Log
9
+ import com.facebook.react.bridge.Arguments
10
+ import com.facebook.react.bridge.WritableArray
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
+ import com.margelo.nitro.NitroModules
14
+ import com.margelo.nitro.munimbluetooth.HybridMunimBluetoothSpec
9
15
  import kotlinx.coroutines.*
10
16
  import java.util.*
11
17
 
@@ -28,6 +34,7 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
28
34
  private val discoveredDevices = mutableMapOf<String, BluetoothDevice>()
29
35
  private val connectedDevices = mutableMapOf<String, BluetoothGatt>()
30
36
  private val deviceCharacteristics = mutableMapOf<String, MutableList<BluetoothGattCharacteristic>>()
37
+ private val eventEmitter = NitroEventEmitter(TAG)
31
38
 
32
39
  init {
33
40
  // Initialize Bluetooth managers - this would need ReactApplicationContext in real implementation
@@ -35,9 +42,8 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
35
42
  }
36
43
 
37
44
  private fun getBluetoothManager(): BluetoothManager? {
38
- // In a real implementation, this would get the context from ReactApplicationContext
39
- // For now, return null - actual implementation would need context injection
40
- return null
45
+ val context = NitroModules.applicationContext ?: return null
46
+ return context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
41
47
  }
42
48
 
43
49
  private fun ensureBluetoothManager() {
@@ -317,8 +323,10 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
317
323
  val deviceId = device.address
318
324
  discoveredDevices[deviceId] = device
319
325
 
320
- // Emit deviceFound event
321
- // In real implementation, this would use event emitter
326
+ eventEmitter.emit(
327
+ "deviceFound",
328
+ buildScanPayload(result)
329
+ )
322
330
  }
323
331
 
324
332
  override fun onBatchScanResults(results: MutableList<ScanResult>) {
@@ -588,6 +596,60 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
588
596
  // This is a no-op as Nitro modules handle events differently
589
597
  }
590
598
 
599
+ private fun buildScanPayload(result: ScanResult): Map<String, Any?> {
600
+ val record = result.scanRecord
601
+ val manufacturerData = extractManufacturerData(record)
602
+ val serviceUUIDs = record?.serviceUuids?.map { it.uuid.toString() }
603
+ val serviceData = extractServiceData(record)
604
+ val txPower = record?.txPowerLevel?.takeIf { it != Int.MIN_VALUE }
605
+ val advertisementData = mutableMapOf<String, Any?>()
606
+ record?.deviceName?.let { advertisementData["completeLocalName"] = it }
607
+ txPower?.let { advertisementData["txPowerLevel"] = it }
608
+ manufacturerData?.let { advertisementData["manufacturerData"] = it }
609
+ serviceUUIDs?.let { advertisementData["serviceUUIDs"] = it }
610
+ serviceData?.takeIf { it.isNotEmpty() }?.let { advertisementData["serviceData"] = it }
611
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
612
+ advertisementData["isConnectable"] = result.isConnectable
613
+ }
614
+ advertisementData["rssi"] = result.rssi
615
+
616
+ return mapOf(
617
+ "id" to result.device.address,
618
+ "name" to result.device.name,
619
+ "localName" to record?.deviceName,
620
+ "manufacturerData" to manufacturerData,
621
+ "serviceUUIDs" to serviceUUIDs,
622
+ "serviceData" to serviceData,
623
+ "rssi" to result.rssi,
624
+ "txPowerLevel" to txPower,
625
+ "isConnectable" to if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) result.isConnectable else null,
626
+ "advertisementData" to advertisementData
627
+ )
628
+ }
629
+
630
+ private fun extractManufacturerData(record: ScanRecord?): String? {
631
+ val data = record?.manufacturerSpecificData ?: return null
632
+ if (data.size() == 0) return null
633
+ val bytes = data.valueAt(0) ?: return null
634
+ return bytes.toHexString()
635
+ }
636
+
637
+ private fun extractServiceData(record: ScanRecord?): List<Map<String, Any?>>? {
638
+ val map = record?.serviceData ?: return null
639
+ val result = map.entries.mapNotNull { entry ->
640
+ val bytes = entry.value ?: return@mapNotNull null
641
+ mapOf(
642
+ "uuid" to entry.key.uuid.toString(),
643
+ "data" to bytes.toHexString()
644
+ )
645
+ }
646
+ return result.takeIf { it.isNotEmpty() }
647
+ }
648
+
649
+ private fun ByteArray.toHexString(): String {
650
+ return joinToString("") { "%02x".format(it) }
651
+ }
652
+
591
653
  // MARK: - Helper Methods
592
654
 
593
655
  private fun processAdvertisingData(
@@ -704,3 +766,66 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
704
766
  gattServerReady = true
705
767
  }
706
768
  }
769
+
770
+ private class NitroEventEmitter(private val tag: String) {
771
+ fun emit(eventName: String, payload: Map<String, Any?>) {
772
+ val context = NitroModules.applicationContext
773
+ if (context == null) {
774
+ Log.w(tag, "Unable to emit $eventName: React context unavailable")
775
+ return
776
+ }
777
+
778
+ val writable = Arguments.createMap()
779
+ payload.forEach { (key, value) ->
780
+ writeValue(writable, key, value)
781
+ }
782
+
783
+ context
784
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
785
+ .emit(eventName, writable)
786
+ }
787
+
788
+ private fun writeValue(map: WritableMap, key: String, value: Any?) {
789
+ when (value) {
790
+ null -> map.putNull(key)
791
+ is String -> map.putString(key, value)
792
+ is Boolean -> map.putBoolean(key, value)
793
+ is Int -> map.putInt(key, value)
794
+ is Double -> map.putDouble(key, value)
795
+ is Float -> map.putDouble(key, value.toDouble())
796
+ is Long -> map.putDouble(key, value.toDouble())
797
+ is Map<*, *> -> map.putMap(key, convertMap(value))
798
+ is List<*> -> map.putArray(key, convertArray(value))
799
+ else -> map.putString(key, value.toString())
800
+ }
801
+ }
802
+
803
+ private fun convertMap(map: Map<*, *>): WritableMap {
804
+ val writable = Arguments.createMap()
805
+ map.forEach { (key, value) ->
806
+ if (key is String) {
807
+ writeValue(writable, key, value)
808
+ }
809
+ }
810
+ return writable
811
+ }
812
+
813
+ private fun convertArray(list: List<*>): WritableArray {
814
+ val writable = Arguments.createArray()
815
+ list.forEach { value ->
816
+ when (value) {
817
+ null -> writable.pushNull()
818
+ is String -> writable.pushString(value)
819
+ is Boolean -> writable.pushBoolean(value)
820
+ is Int -> writable.pushInt(value)
821
+ is Double -> writable.pushDouble(value)
822
+ is Float -> writable.pushDouble(value.toDouble())
823
+ is Long -> writable.pushDouble(value.toDouble())
824
+ is Map<*, *> -> writable.pushMap(convertMap(value))
825
+ is List<*> -> writable.pushArray(convertArray(value))
826
+ else -> writable.pushString(value.toString())
827
+ }
828
+ }
829
+ return writable
830
+ }
831
+ }
@@ -8,6 +8,7 @@
8
8
  import Foundation
9
9
  import CoreBluetooth
10
10
  import ReactNativeNitroModules
11
+ import React
11
12
 
12
13
  class HybridMunimBluetooth: HybridMunimBluetoothSpec {
13
14
  // Peripheral Manager
@@ -627,7 +628,24 @@ class NitroEventEmitter {
627
628
  }
628
629
 
629
630
  func emit(_ eventName: String, _ body: [String: Any]) {
630
- // Nitro modules event emission would be handled here
631
- // This is a placeholder for the actual implementation
631
+ let sendEvent = {
632
+ guard let bridge = RCTBridge.current() ?? RCTBridge.currentBridge() else {
633
+ NSLog("[\(self.moduleName)] Unable to emit event \(eventName): missing bridge")
634
+ return
635
+ }
636
+
637
+ bridge.enqueueJSCall(
638
+ "RCTDeviceEventEmitter",
639
+ method: "emit",
640
+ args: [eventName, body],
641
+ completion: nil
642
+ )
643
+ }
644
+
645
+ if Thread.isMainThread {
646
+ sendEvent()
647
+ } else {
648
+ DispatchQueue.main.async(execute: sendEvent)
649
+ }
632
650
  }
633
651
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "munim-bluetooth",
3
- "version": "0.2.1",
4
- "description": "munim-bluetooth is a react native package for all your Bluetooth, BLE, and BLE pheripheral needs",
3
+ "version": "0.2.3",
4
+ "description": "A comprehensive React Native library for all your Bluetooth Low Energy (BLE) needs, supporting both peripheral and central roles with Expo support",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
7
7
  "types": "./lib/typescript/src/index.d.ts",
@@ -16,7 +16,18 @@
16
16
  },
17
17
  "keywords": [
18
18
  "react-native",
19
- "munim-bluetooth"
19
+ "expo",
20
+ "bluetooth",
21
+ "ble",
22
+ "peripheral",
23
+ "central",
24
+ "gatt",
25
+ "bluetooth-low-energy",
26
+ "ios",
27
+ "android",
28
+ "typescript",
29
+ "nitro-modules",
30
+ "munim-technologies"
20
31
  ],
21
32
  "files": [
22
33
  "src",
@@ -46,14 +57,28 @@
46
57
  "type": "git",
47
58
  "url": "https://github.com/munimtechnologies/munim-bluetooth.git"
48
59
  },
49
- "author": "sheehanmunim",
60
+ "author": "sheehanmunim <support@munimtech.com> (https://github.com/sheehanmunim)",
50
61
  "license": "MIT",
51
- "bugs": "https://github.com/munimtechnologies/munim-bluetooth/issues",
62
+ "bugs": {
63
+ "url": "https://github.com/munimtechnologies/munim-bluetooth/issues"
64
+ },
52
65
  "homepage": "https://github.com/munimtechnologies/munim-bluetooth#readme",
53
66
  "publishConfig": {
54
67
  "access": "public",
55
68
  "registry": "https://registry.npmjs.org/"
56
69
  },
70
+ "expo": {
71
+ "name": "munim-bluetooth",
72
+ "platforms": [
73
+ "ios",
74
+ "android"
75
+ ],
76
+ "install": {
77
+ "exclude": [
78
+ "react-native"
79
+ ]
80
+ }
81
+ },
57
82
  "devDependencies": {
58
83
  "@jamesacarr/eslint-formatter-github-actions": "^0.2.0",
59
84
  "@semantic-release/changelog": "^6.0.3",