react-native-ble-nitro 1.10.3 → 1.12.0
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.
- package/android/src/main/cpp/cpp-adapter.cpp +4 -1
- package/android/src/main/java/com/margelo/nitro/co/zyke/ble/BleNitroBleManager.kt +58 -21
- package/ios/BleNitroBleManager.swift +60 -3
- package/ios/BlePeripheralDelegate.swift +68 -9
- package/lib/commonjs/index.d.ts +1 -1
- package/lib/commonjs/index.d.ts.map +1 -1
- package/lib/commonjs/index.js +2 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/manager.d.ts +19 -4
- package/lib/commonjs/manager.d.ts.map +1 -1
- package/lib/commonjs/manager.js +68 -33
- package/lib/commonjs/manager.js.map +1 -1
- package/lib/commonjs/specs/NativeBleNitro.nitro.d.ts +3 -0
- package/lib/commonjs/specs/NativeBleNitro.nitro.d.ts.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/manager.d.ts +19 -4
- package/lib/manager.js +66 -32
- package/lib/specs/NativeBleNitro.nitro.d.ts +3 -0
- package/nitrogen/generated/android/BleNitroOnLoad.cpp +48 -32
- package/nitrogen/generated/android/BleNitroOnLoad.hpp +13 -4
- package/nitrogen/generated/android/c++/JHybridNativeBleNitroFactorySpec.cpp +20 -26
- package/nitrogen/generated/android/c++/JHybridNativeBleNitroFactorySpec.hpp +19 -22
- package/nitrogen/generated/android/c++/JHybridNativeBleNitroSpec.cpp +52 -49
- package/nitrogen/generated/android/c++/JHybridNativeBleNitroSpec.hpp +21 -22
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/HybridNativeBleNitroFactorySpec.kt +15 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/HybridNativeBleNitroSpec.kt +28 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/Variant_NullType_BLEDevice.kt +0 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/Variant_NullType_String.kt +0 -6
- package/nitrogen/generated/ios/c++/HybridNativeBleNitroSpecSwift.hpp +14 -0
- package/nitrogen/generated/ios/swift/HybridNativeBleNitroSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNativeBleNitroSpec_cxx.swift +28 -0
- package/nitrogen/generated/shared/c++/HybridNativeBleNitroSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridNativeBleNitroSpec.hpp +2 -0
- package/package.json +9 -9
- package/src/__tests__/index.test.ts +145 -1
- package/src/index.ts +1 -0
- package/src/manager.ts +96 -34
- package/src/specs/NativeBleNitro.nitro.ts +3 -0
|
@@ -24,23 +24,6 @@ import com.margelo.nitro.core.HybridObject
|
|
|
24
24
|
"LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
|
|
25
25
|
)
|
|
26
26
|
abstract class HybridNativeBleNitroFactorySpec: HybridObject() {
|
|
27
|
-
@DoNotStrip
|
|
28
|
-
private var mHybridData: HybridData = initHybrid()
|
|
29
|
-
|
|
30
|
-
init {
|
|
31
|
-
super.updateNative(mHybridData)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
override fun updateNative(hybridData: HybridData) {
|
|
35
|
-
mHybridData = hybridData
|
|
36
|
-
super.updateNative(hybridData)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Default implementation of `HybridObject.toString()`
|
|
40
|
-
override fun toString(): String {
|
|
41
|
-
return "[HybridObject NativeBleNitroFactory]"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
27
|
// Properties
|
|
45
28
|
|
|
46
29
|
|
|
@@ -54,7 +37,21 @@ abstract class HybridNativeBleNitroFactorySpec: HybridObject() {
|
|
|
54
37
|
return __result
|
|
55
38
|
}
|
|
56
39
|
|
|
57
|
-
|
|
40
|
+
// Default implementation of `HybridObject.toString()`
|
|
41
|
+
override fun toString(): String {
|
|
42
|
+
return "[HybridObject NativeBleNitroFactory]"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// C++ backing class
|
|
46
|
+
@DoNotStrip
|
|
47
|
+
@Keep
|
|
48
|
+
protected open class CxxPart(javaPart: HybridNativeBleNitroFactorySpec): HybridObject.CxxPart(javaPart) {
|
|
49
|
+
// C++ JHybridNativeBleNitroFactorySpec::CxxPart::initHybrid(...)
|
|
50
|
+
external override fun initHybrid(): HybridData
|
|
51
|
+
}
|
|
52
|
+
override fun createCxxPart(): CxxPart {
|
|
53
|
+
return CxxPart(this)
|
|
54
|
+
}
|
|
58
55
|
|
|
59
56
|
companion object {
|
|
60
57
|
protected const val TAG = "HybridNativeBleNitroFactorySpec"
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/HybridNativeBleNitroSpec.kt
CHANGED
|
@@ -27,23 +27,6 @@ import com.margelo.nitro.core.HybridObject
|
|
|
27
27
|
"LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
|
|
28
28
|
)
|
|
29
29
|
abstract class HybridNativeBleNitroSpec: HybridObject() {
|
|
30
|
-
@DoNotStrip
|
|
31
|
-
private var mHybridData: HybridData = initHybrid()
|
|
32
|
-
|
|
33
|
-
init {
|
|
34
|
-
super.updateNative(mHybridData)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
override fun updateNative(hybridData: HybridData) {
|
|
38
|
-
mHybridData = hybridData
|
|
39
|
-
super.updateNative(hybridData)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Default implementation of `HybridObject.toString()`
|
|
43
|
-
override fun toString(): String {
|
|
44
|
-
return "[HybridObject NativeBleNitro]"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
30
|
// Properties
|
|
48
31
|
@get:DoNotStrip
|
|
49
32
|
@get:Keep
|
|
@@ -130,6 +113,15 @@ abstract class HybridNativeBleNitroSpec: HybridObject() {
|
|
|
130
113
|
return __result
|
|
131
114
|
}
|
|
132
115
|
|
|
116
|
+
abstract fun discoverServicesWithCharacteristics(deviceId: String, callback: (success: Boolean, error: String) -> Unit): Unit
|
|
117
|
+
|
|
118
|
+
@DoNotStrip
|
|
119
|
+
@Keep
|
|
120
|
+
private fun discoverServicesWithCharacteristics_cxx(deviceId: String, callback: Func_void_bool_std__string): Unit {
|
|
121
|
+
val __result = discoverServicesWithCharacteristics(deviceId, callback)
|
|
122
|
+
return __result
|
|
123
|
+
}
|
|
124
|
+
|
|
133
125
|
@DoNotStrip
|
|
134
126
|
@Keep
|
|
135
127
|
abstract fun getServices(deviceId: String): Array<String>
|
|
@@ -174,6 +166,10 @@ abstract class HybridNativeBleNitroSpec: HybridObject() {
|
|
|
174
166
|
return __result
|
|
175
167
|
}
|
|
176
168
|
|
|
169
|
+
@DoNotStrip
|
|
170
|
+
@Keep
|
|
171
|
+
abstract fun isSubscribedToCharacteristic(deviceId: String, serviceId: String, characteristicId: String): Boolean
|
|
172
|
+
|
|
177
173
|
abstract fun requestBluetoothEnable(callback: (success: Boolean, error: String) -> Unit): Unit
|
|
178
174
|
|
|
179
175
|
@DoNotStrip
|
|
@@ -204,7 +200,21 @@ abstract class HybridNativeBleNitroSpec: HybridObject() {
|
|
|
204
200
|
@Keep
|
|
205
201
|
abstract fun openSettings(): Promise<Unit>
|
|
206
202
|
|
|
207
|
-
|
|
203
|
+
// Default implementation of `HybridObject.toString()`
|
|
204
|
+
override fun toString(): String {
|
|
205
|
+
return "[HybridObject NativeBleNitro]"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// C++ backing class
|
|
209
|
+
@DoNotStrip
|
|
210
|
+
@Keep
|
|
211
|
+
protected open class CxxPart(javaPart: HybridNativeBleNitroSpec): HybridObject.CxxPart(javaPart) {
|
|
212
|
+
// C++ JHybridNativeBleNitroSpec::CxxPart::initHybrid(...)
|
|
213
|
+
external override fun initHybrid(): HybridData
|
|
214
|
+
}
|
|
215
|
+
override fun createCxxPart(): CxxPart {
|
|
216
|
+
return CxxPart(this)
|
|
217
|
+
}
|
|
208
218
|
|
|
209
219
|
companion object {
|
|
210
220
|
protected const val TAG = "HybridNativeBleNitroSpec"
|
|
@@ -21,12 +21,6 @@ sealed class Variant_NullType_BLEDevice {
|
|
|
21
21
|
@DoNotStrip
|
|
22
22
|
data class Second(@DoNotStrip val value: BLEDevice): Variant_NullType_BLEDevice()
|
|
23
23
|
|
|
24
|
-
@Deprecated("getAs() is not type-safe. Use fold/asFirstOrNull/asSecondOrNull instead.", level = DeprecationLevel.ERROR)
|
|
25
|
-
inline fun <reified T> getAs(): T? = when (this) {
|
|
26
|
-
is First -> value as? T
|
|
27
|
-
is Second -> value as? T
|
|
28
|
-
}
|
|
29
|
-
|
|
30
24
|
val isFirst: Boolean
|
|
31
25
|
get() = this is First
|
|
32
26
|
val isSecond: Boolean
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/Variant_NullType_String.kt
CHANGED
|
@@ -21,12 +21,6 @@ sealed class Variant_NullType_String {
|
|
|
21
21
|
@DoNotStrip
|
|
22
22
|
data class Second(@DoNotStrip val value: String): Variant_NullType_String()
|
|
23
23
|
|
|
24
|
-
@Deprecated("getAs() is not type-safe. Use fold/asFirstOrNull/asSecondOrNull instead.", level = DeprecationLevel.ERROR)
|
|
25
|
-
inline fun <reified T> getAs(): T? = when (this) {
|
|
26
|
-
is First -> value as? T
|
|
27
|
-
is Second -> value as? T
|
|
28
|
-
}
|
|
29
|
-
|
|
30
24
|
val isFirst: Boolean
|
|
31
25
|
get() = this is First
|
|
32
26
|
val isSecond: Boolean
|
|
@@ -184,6 +184,12 @@ namespace margelo::nitro::co::zyke::ble {
|
|
|
184
184
|
std::rethrow_exception(__result.error());
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
+
inline void discoverServicesWithCharacteristics(const std::string& deviceId, const std::function<void(bool /* success */, const std::string& /* error */)>& callback) override {
|
|
188
|
+
auto __result = _swiftPart.discoverServicesWithCharacteristics(deviceId, callback);
|
|
189
|
+
if (__result.hasError()) [[unlikely]] {
|
|
190
|
+
std::rethrow_exception(__result.error());
|
|
191
|
+
}
|
|
192
|
+
}
|
|
187
193
|
inline std::vector<std::string> getServices(const std::string& deviceId) override {
|
|
188
194
|
auto __result = _swiftPart.getServices(deviceId);
|
|
189
195
|
if (__result.hasError()) [[unlikely]] {
|
|
@@ -224,6 +230,14 @@ namespace margelo::nitro::co::zyke::ble {
|
|
|
224
230
|
std::rethrow_exception(__result.error());
|
|
225
231
|
}
|
|
226
232
|
}
|
|
233
|
+
inline bool isSubscribedToCharacteristic(const std::string& deviceId, const std::string& serviceId, const std::string& characteristicId) override {
|
|
234
|
+
auto __result = _swiftPart.isSubscribedToCharacteristic(deviceId, serviceId, characteristicId);
|
|
235
|
+
if (__result.hasError()) [[unlikely]] {
|
|
236
|
+
std::rethrow_exception(__result.error());
|
|
237
|
+
}
|
|
238
|
+
auto __value = std::move(__result.value());
|
|
239
|
+
return __value;
|
|
240
|
+
}
|
|
227
241
|
inline void requestBluetoothEnable(const std::function<void(bool /* success */, const std::string& /* error */)>& callback) override {
|
|
228
242
|
auto __result = _swiftPart.requestBluetoothEnable(callback);
|
|
229
243
|
if (__result.hasError()) [[unlikely]] {
|
|
@@ -25,12 +25,14 @@ public protocol HybridNativeBleNitroSpec_protocol: HybridObject {
|
|
|
25
25
|
func requestMTU(deviceId: String, mtu: Double) throws -> Double
|
|
26
26
|
func readRSSI(deviceId: String, callback: @escaping (_ success: Bool, _ rssi: Double, _ error: String) -> Void) throws -> Void
|
|
27
27
|
func discoverServices(deviceId: String, callback: @escaping (_ success: Bool, _ error: String) -> Void) throws -> Void
|
|
28
|
+
func discoverServicesWithCharacteristics(deviceId: String, callback: @escaping (_ success: Bool, _ error: String) -> Void) throws -> Void
|
|
28
29
|
func getServices(deviceId: String) throws -> [String]
|
|
29
30
|
func getCharacteristics(deviceId: String, serviceId: String) throws -> [String]
|
|
30
31
|
func readCharacteristic(deviceId: String, serviceId: String, characteristicId: String, callback: @escaping (_ success: Bool, _ data: ArrayBuffer, _ error: String) -> Void) throws -> Void
|
|
31
32
|
func writeCharacteristic(deviceId: String, serviceId: String, characteristicId: String, data: ArrayBuffer, withResponse: Bool, callback: @escaping (_ success: Bool, _ responseData: ArrayBuffer, _ error: String) -> Void) throws -> Void
|
|
32
33
|
func subscribeToCharacteristic(deviceId: String, serviceId: String, characteristicId: String, updateCallback: @escaping (_ characteristicId: String, _ data: ArrayBuffer) -> Void, completionCallback: @escaping (_ success: Bool, _ error: String) -> Void) throws -> Void
|
|
33
34
|
func unsubscribeFromCharacteristic(deviceId: String, serviceId: String, characteristicId: String, callback: @escaping (_ success: Bool, _ error: String) -> Void) throws -> Void
|
|
35
|
+
func isSubscribedToCharacteristic(deviceId: String, serviceId: String, characteristicId: String) throws -> Bool
|
|
34
36
|
func requestBluetoothEnable(callback: @escaping (_ success: Bool, _ error: String) -> Void) throws -> Void
|
|
35
37
|
func state() throws -> BLEState
|
|
36
38
|
func subscribeToStateChange(stateCallback: @escaping (_ state: BLEState) -> Void) throws -> OperationResult
|
|
@@ -370,6 +370,22 @@ open class HybridNativeBleNitroSpec_cxx {
|
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
@inline(__always)
|
|
374
|
+
public final func discoverServicesWithCharacteristics(deviceId: std.string, callback: bridge.Func_void_bool_std__string) -> bridge.Result_void_ {
|
|
375
|
+
do {
|
|
376
|
+
try self.__implementation.discoverServicesWithCharacteristics(deviceId: String(deviceId), callback: { () -> (Bool, String) -> Void in
|
|
377
|
+
let __wrappedFunction = bridge.wrap_Func_void_bool_std__string(callback)
|
|
378
|
+
return { (__success: Bool, __error: String) -> Void in
|
|
379
|
+
__wrappedFunction.call(__success, std.string(__error))
|
|
380
|
+
}
|
|
381
|
+
}())
|
|
382
|
+
return bridge.create_Result_void_()
|
|
383
|
+
} catch (let __error) {
|
|
384
|
+
let __exceptionPtr = __error.toCpp()
|
|
385
|
+
return bridge.create_Result_void_(__exceptionPtr)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
373
389
|
@inline(__always)
|
|
374
390
|
public final func getServices(deviceId: std.string) -> bridge.Result_std__vector_std__string__ {
|
|
375
391
|
do {
|
|
@@ -475,6 +491,18 @@ open class HybridNativeBleNitroSpec_cxx {
|
|
|
475
491
|
}
|
|
476
492
|
}
|
|
477
493
|
|
|
494
|
+
@inline(__always)
|
|
495
|
+
public final func isSubscribedToCharacteristic(deviceId: std.string, serviceId: std.string, characteristicId: std.string) -> bridge.Result_bool_ {
|
|
496
|
+
do {
|
|
497
|
+
let __result = try self.__implementation.isSubscribedToCharacteristic(deviceId: String(deviceId), serviceId: String(serviceId), characteristicId: String(characteristicId))
|
|
498
|
+
let __resultCpp = __result
|
|
499
|
+
return bridge.create_Result_bool_(__resultCpp)
|
|
500
|
+
} catch (let __error) {
|
|
501
|
+
let __exceptionPtr = __error.toCpp()
|
|
502
|
+
return bridge.create_Result_bool_(__exceptionPtr)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
478
506
|
@inline(__always)
|
|
479
507
|
public final func requestBluetoothEnable(callback: bridge.Func_void_bool_std__string) -> bridge.Result_void_ {
|
|
480
508
|
do {
|
|
@@ -28,12 +28,14 @@ namespace margelo::nitro::co::zyke::ble {
|
|
|
28
28
|
prototype.registerHybridMethod("requestMTU", &HybridNativeBleNitroSpec::requestMTU);
|
|
29
29
|
prototype.registerHybridMethod("readRSSI", &HybridNativeBleNitroSpec::readRSSI);
|
|
30
30
|
prototype.registerHybridMethod("discoverServices", &HybridNativeBleNitroSpec::discoverServices);
|
|
31
|
+
prototype.registerHybridMethod("discoverServicesWithCharacteristics", &HybridNativeBleNitroSpec::discoverServicesWithCharacteristics);
|
|
31
32
|
prototype.registerHybridMethod("getServices", &HybridNativeBleNitroSpec::getServices);
|
|
32
33
|
prototype.registerHybridMethod("getCharacteristics", &HybridNativeBleNitroSpec::getCharacteristics);
|
|
33
34
|
prototype.registerHybridMethod("readCharacteristic", &HybridNativeBleNitroSpec::readCharacteristic);
|
|
34
35
|
prototype.registerHybridMethod("writeCharacteristic", &HybridNativeBleNitroSpec::writeCharacteristic);
|
|
35
36
|
prototype.registerHybridMethod("subscribeToCharacteristic", &HybridNativeBleNitroSpec::subscribeToCharacteristic);
|
|
36
37
|
prototype.registerHybridMethod("unsubscribeFromCharacteristic", &HybridNativeBleNitroSpec::unsubscribeFromCharacteristic);
|
|
38
|
+
prototype.registerHybridMethod("isSubscribedToCharacteristic", &HybridNativeBleNitroSpec::isSubscribedToCharacteristic);
|
|
37
39
|
prototype.registerHybridMethod("requestBluetoothEnable", &HybridNativeBleNitroSpec::requestBluetoothEnable);
|
|
38
40
|
prototype.registerHybridMethod("state", &HybridNativeBleNitroSpec::state);
|
|
39
41
|
prototype.registerHybridMethod("subscribeToStateChange", &HybridNativeBleNitroSpec::subscribeToStateChange);
|
|
@@ -79,12 +79,14 @@ namespace margelo::nitro::co::zyke::ble {
|
|
|
79
79
|
virtual double requestMTU(const std::string& deviceId, double mtu) = 0;
|
|
80
80
|
virtual void readRSSI(const std::string& deviceId, const std::function<void(bool /* success */, double /* rssi */, const std::string& /* error */)>& callback) = 0;
|
|
81
81
|
virtual void discoverServices(const std::string& deviceId, const std::function<void(bool /* success */, const std::string& /* error */)>& callback) = 0;
|
|
82
|
+
virtual void discoverServicesWithCharacteristics(const std::string& deviceId, const std::function<void(bool /* success */, const std::string& /* error */)>& callback) = 0;
|
|
82
83
|
virtual std::vector<std::string> getServices(const std::string& deviceId) = 0;
|
|
83
84
|
virtual std::vector<std::string> getCharacteristics(const std::string& deviceId, const std::string& serviceId) = 0;
|
|
84
85
|
virtual void readCharacteristic(const std::string& deviceId, const std::string& serviceId, const std::string& characteristicId, const std::function<void(bool /* success */, const std::shared_ptr<ArrayBuffer>& /* data */, const std::string& /* error */)>& callback) = 0;
|
|
85
86
|
virtual void writeCharacteristic(const std::string& deviceId, const std::string& serviceId, const std::string& characteristicId, const std::shared_ptr<ArrayBuffer>& data, bool withResponse, const std::function<void(bool /* success */, const std::shared_ptr<ArrayBuffer>& /* responseData */, const std::string& /* error */)>& callback) = 0;
|
|
86
87
|
virtual void subscribeToCharacteristic(const std::string& deviceId, const std::string& serviceId, const std::string& characteristicId, const std::function<void(const std::string& /* characteristicId */, const std::shared_ptr<ArrayBuffer>& /* data */)>& updateCallback, const std::function<void(bool /* success */, const std::string& /* error */)>& completionCallback) = 0;
|
|
87
88
|
virtual void unsubscribeFromCharacteristic(const std::string& deviceId, const std::string& serviceId, const std::string& characteristicId, const std::function<void(bool /* success */, const std::string& /* error */)>& callback) = 0;
|
|
89
|
+
virtual bool isSubscribedToCharacteristic(const std::string& deviceId, const std::string& serviceId, const std::string& characteristicId) = 0;
|
|
88
90
|
virtual void requestBluetoothEnable(const std::function<void(bool /* success */, const std::string& /* error */)>& callback) = 0;
|
|
89
91
|
virtual BLEState state() = 0;
|
|
90
92
|
virtual OperationResult subscribeToStateChange(const std::function<void(BLEState /* state */)>& stateCallback) = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-ble-nitro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "High-performance React Native BLE library built on Nitro Modules",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -77,25 +77,25 @@
|
|
|
77
77
|
"url": "https://github.com/zykeco/react-native-ble-nitro/issues"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|
|
80
|
-
"node": ">=
|
|
80
|
+
"node": ">=20.0.0"
|
|
81
81
|
},
|
|
82
82
|
"peerDependencies": {
|
|
83
83
|
"react-native": ">=0.76.0",
|
|
84
|
-
"react-native-nitro-modules": ">=0.
|
|
84
|
+
"react-native-nitro-modules": ">=0.35.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@expo/config-plugins": "^
|
|
87
|
+
"@expo/config-plugins": "^55.0.6",
|
|
88
88
|
"@types/jest": "^29.5.0",
|
|
89
|
-
"@types/node": "^
|
|
90
|
-
"@types/react": "^19.2.
|
|
89
|
+
"@types/node": "^24.11.0",
|
|
90
|
+
"@types/react": "^19.2.14",
|
|
91
91
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
92
92
|
"@typescript-eslint/parser": "^6.21.0",
|
|
93
93
|
"eslint": "^8.57.1",
|
|
94
|
-
"expo-module-scripts": "^
|
|
94
|
+
"expo-module-scripts": "^55.0.2",
|
|
95
95
|
"jest": "^29.0.0",
|
|
96
|
-
"nitrogen": "^0.
|
|
96
|
+
"nitrogen": "^0.35.0",
|
|
97
97
|
"react-native": "^0.83.2",
|
|
98
|
-
"react-native-nitro-modules": "^0.
|
|
98
|
+
"react-native-nitro-modules": "^0.35.0",
|
|
99
99
|
"rimraf": "^6.1.3",
|
|
100
100
|
"ts-jest": "^29.4.6",
|
|
101
101
|
"typescript": "^5.9.3"
|
|
@@ -10,12 +10,14 @@ const mockNativeInstance = {
|
|
|
10
10
|
requestMTU: jest.fn(),
|
|
11
11
|
readRSSI: jest.fn(),
|
|
12
12
|
discoverServices: jest.fn(),
|
|
13
|
+
discoverServicesWithCharacteristics: jest.fn(),
|
|
13
14
|
getServices: jest.fn(),
|
|
14
15
|
getCharacteristics: jest.fn(),
|
|
15
16
|
readCharacteristic: jest.fn(),
|
|
16
17
|
writeCharacteristic: jest.fn(),
|
|
17
18
|
subscribeToCharacteristic: jest.fn(),
|
|
18
19
|
unsubscribeFromCharacteristic: jest.fn(),
|
|
20
|
+
isSubscribedToCharacteristic: jest.fn(),
|
|
19
21
|
getConnectedDevices: jest.fn(),
|
|
20
22
|
requestBluetoothEnable: jest.fn(),
|
|
21
23
|
state: jest.fn(),
|
|
@@ -143,6 +145,10 @@ describe('BleNitro', () => {
|
|
|
143
145
|
});
|
|
144
146
|
await BleManager.connect('device-write');
|
|
145
147
|
|
|
148
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
149
|
+
return id === 'device-write';
|
|
150
|
+
});
|
|
151
|
+
|
|
146
152
|
// Mock writeCharacteristic with new signature (success, responseData, error)
|
|
147
153
|
mockNative.writeCharacteristic.mockImplementation((_deviceId: string, _serviceId: string, _charId: string, _data: ArrayBuffer, withResponse: boolean, callback: (success: boolean, responseData: ArrayBuffer, error: string) => void) => {
|
|
148
154
|
// For withResponse=false, return empty ArrayBuffer
|
|
@@ -171,6 +177,10 @@ describe('BleNitro', () => {
|
|
|
171
177
|
});
|
|
172
178
|
await BleManager.connect('device-write-resp');
|
|
173
179
|
|
|
180
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
181
|
+
return id === 'device-write-resp';
|
|
182
|
+
});
|
|
183
|
+
|
|
174
184
|
// Mock writeCharacteristic to return response data
|
|
175
185
|
mockNative.writeCharacteristic.mockImplementation((_deviceId: string, _serviceId: string, _charId: string, _data: ArrayBuffer, withResponse: boolean, callback: (success: boolean, responseData: ArrayBuffer, error: string) => void) => {
|
|
176
186
|
// For withResponse=true, return some response data
|
|
@@ -192,12 +202,99 @@ describe('BleNitro', () => {
|
|
|
192
202
|
expect(result).toEqual([0xAA, 0xBB, 0xCC]); // Response data as ByteArray
|
|
193
203
|
});
|
|
194
204
|
|
|
205
|
+
test('getServicesWithCharacteristics discovers and returns services with characteristics', async () => {
|
|
206
|
+
// First connect
|
|
207
|
+
mockNative.connect.mockImplementation((id: string, callback: (success: boolean, deviceId: string, error: string) => void, _disconnectCallback?: (deviceId: string, interrupted: boolean, error: string) => void) => {
|
|
208
|
+
callback(true, id, '');
|
|
209
|
+
});
|
|
210
|
+
await BleManager.connect('device-svc');
|
|
211
|
+
|
|
212
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
213
|
+
return id === 'device-svc';
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Mock discoverServicesWithCharacteristics
|
|
217
|
+
mockNative.discoverServicesWithCharacteristics.mockImplementation((_deviceId: string, callback: (success: boolean, error: string) => void) => {
|
|
218
|
+
callback(true, '');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Mock getServices to return service UUIDs
|
|
222
|
+
mockNative.getServices.mockReturnValue(['0000180a-0000-1000-8000-00805f9b34fb', '0000180f-0000-1000-8000-00805f9b34fb']);
|
|
223
|
+
|
|
224
|
+
// Mock getCharacteristics per service
|
|
225
|
+
mockNative.getCharacteristics.mockImplementation((_deviceId: string, serviceId: string) => {
|
|
226
|
+
if (serviceId === '0000180a-0000-1000-8000-00805f9b34fb') {
|
|
227
|
+
return ['00002a29-0000-1000-8000-00805f9b34fb'];
|
|
228
|
+
}
|
|
229
|
+
return ['00002a19-0000-1000-8000-00805f9b34fb'];
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const result = await BleManager.getServicesWithCharacteristics('device-svc');
|
|
233
|
+
|
|
234
|
+
expect(mockNative.discoverServicesWithCharacteristics).toHaveBeenCalledWith(
|
|
235
|
+
'device-svc',
|
|
236
|
+
expect.any(Function)
|
|
237
|
+
);
|
|
238
|
+
expect(result).toHaveLength(2);
|
|
239
|
+
expect(result[0]).toHaveProperty('uuid');
|
|
240
|
+
expect(result[0]).toHaveProperty('characteristics');
|
|
241
|
+
expect(result[0].characteristics.length).toBeGreaterThan(0);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('getServicesWithCharacteristics rejects on discovery failure', async () => {
|
|
245
|
+
mockNative.connect.mockImplementation((id: string, callback: (success: boolean, deviceId: string, error: string) => void, _disconnectCallback?: (deviceId: string, interrupted: boolean, error: string) => void) => {
|
|
246
|
+
callback(true, id, '');
|
|
247
|
+
});
|
|
248
|
+
await BleManager.connect('device-svc-fail');
|
|
249
|
+
|
|
250
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
251
|
+
return id === 'device-svc-fail';
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
mockNative.discoverServicesWithCharacteristics.mockImplementation((_deviceId: string, callback: (success: boolean, error: string) => void) => {
|
|
255
|
+
callback(false, 'Discovery failed');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
await expect(BleManager.getServicesWithCharacteristics('device-svc-fail')).rejects.toThrow('Discovery failed');
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('getServicesWithCharacteristics rejects when not connected', async () => {
|
|
262
|
+
mockNative.isConnected.mockReturnValue(false);
|
|
263
|
+
|
|
264
|
+
await expect(BleManager.getServicesWithCharacteristics('unknown-device')).rejects.toThrow('Device not connected');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('getServicesWithCharacteristics rejects with BleTimeoutError on timeout', async () => {
|
|
268
|
+
jest.useFakeTimers();
|
|
269
|
+
|
|
270
|
+
mockNative.connect.mockImplementation((id: string, callback: (success: boolean, deviceId: string, error: string) => void) => {
|
|
271
|
+
callback(true, id, '');
|
|
272
|
+
});
|
|
273
|
+
await BleManager.connect('device-timeout');
|
|
274
|
+
|
|
275
|
+
mockNative.isConnected.mockImplementation((id: string) => id === 'device-timeout');
|
|
276
|
+
|
|
277
|
+
// Mock that NEVER calls its callback — simulates hung native discovery
|
|
278
|
+
mockNative.discoverServicesWithCharacteristics.mockImplementation(() => {});
|
|
279
|
+
|
|
280
|
+
const promise = BleManager.getServicesWithCharacteristics('device-timeout');
|
|
281
|
+
|
|
282
|
+
jest.advanceTimersByTime(30_000);
|
|
283
|
+
|
|
284
|
+
await expect(promise).rejects.toThrow('timed out after 30000ms');
|
|
285
|
+
|
|
286
|
+
jest.useRealTimers();
|
|
287
|
+
});
|
|
288
|
+
|
|
195
289
|
test('readCharacteristic works after connection', async () => {
|
|
196
290
|
// First connect
|
|
197
291
|
mockNative.connect.mockImplementation((id: string, callback: (success: boolean, deviceId: string, error: string) => void, _disconnectCallback?: (deviceId: string, interrupted: boolean, error: string) => void) => {
|
|
198
292
|
callback(true, id, '');
|
|
199
293
|
});
|
|
200
294
|
await BleManager.connect('device');
|
|
295
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
296
|
+
return id === 'device';
|
|
297
|
+
});
|
|
201
298
|
|
|
202
299
|
// Then read
|
|
203
300
|
mockNative.readCharacteristic.mockImplementation((_device: string, _service: string, _char: string, callback: (success: boolean, data: ArrayBuffer, error: string) => void) => {
|
|
@@ -239,7 +336,7 @@ describe('BleNitro', () => {
|
|
|
239
336
|
const result = await BleManager.disconnect('device');
|
|
240
337
|
|
|
241
338
|
expect(mockNative.disconnect).toHaveBeenCalledWith('device', expect.any(Function));
|
|
242
|
-
expect(result).toBe(
|
|
339
|
+
expect(result).toBe('device');
|
|
243
340
|
});
|
|
244
341
|
|
|
245
342
|
test('subscribeToCharacteristic calls callback', async () => {
|
|
@@ -293,6 +390,47 @@ describe('BleNitro', () => {
|
|
|
293
390
|
expect(onDisconnect).toHaveBeenCalledWith(deviceId, true, 'Connection lost');
|
|
294
391
|
});
|
|
295
392
|
|
|
393
|
+
test('isSubscribedToCharacteristic delegates to native with normalized UUIDs', () => {
|
|
394
|
+
mockNative.isSubscribedToCharacteristic.mockReturnValueOnce(true);
|
|
395
|
+
|
|
396
|
+
const result = BleManager.isSubscribedToCharacteristic('device', 'service', 'char');
|
|
397
|
+
|
|
398
|
+
expect(result).toBe(true);
|
|
399
|
+
expect(mockNative.isSubscribedToCharacteristic).toHaveBeenCalledWith(
|
|
400
|
+
'device',
|
|
401
|
+
'0service-0000-1000-8000-00805f9b34fb',
|
|
402
|
+
'0000char-0000-1000-8000-00805f9b34fb'
|
|
403
|
+
);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('isSubscribedToCharacteristic returns false when not subscribed', () => {
|
|
407
|
+
mockNative.isSubscribedToCharacteristic.mockReturnValueOnce(false);
|
|
408
|
+
|
|
409
|
+
const result = BleManager.isSubscribedToCharacteristic('device', 'service', 'char');
|
|
410
|
+
|
|
411
|
+
expect(result).toBe(false);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
test('isSubscribedToCharacteristic delegates to native for unknown devices', () => {
|
|
415
|
+
// Native layer handles unknown/disconnected devices by returning false
|
|
416
|
+
mockNative.isSubscribedToCharacteristic.mockReturnValueOnce(false);
|
|
417
|
+
|
|
418
|
+
const result = BleManager.isSubscribedToCharacteristic('unknown-device', 'service', 'char');
|
|
419
|
+
|
|
420
|
+
expect(result).toBe(false);
|
|
421
|
+
expect(mockNative.isSubscribedToCharacteristic).toHaveBeenCalled();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('isSubscribedToCharacteristic propagates native exception', () => {
|
|
425
|
+
mockNative.isSubscribedToCharacteristic.mockImplementationOnce(() => {
|
|
426
|
+
throw new Error('Native error');
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(() =>
|
|
430
|
+
BleManager.isSubscribedToCharacteristic('device', 'service', 'char')
|
|
431
|
+
).toThrow('Native error');
|
|
432
|
+
});
|
|
433
|
+
|
|
296
434
|
test('readRSSI requires connected device', async () => {
|
|
297
435
|
await expect(
|
|
298
436
|
BleManager.readRSSI('device-not-connected')
|
|
@@ -305,6 +443,9 @@ describe('BleNitro', () => {
|
|
|
305
443
|
callback(true, id, '');
|
|
306
444
|
});
|
|
307
445
|
await BleManager.connect('device-rssi');
|
|
446
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
447
|
+
return id === 'device-rssi';
|
|
448
|
+
});
|
|
308
449
|
|
|
309
450
|
// Mock readRSSI with new signature (success, rssi, error)
|
|
310
451
|
mockNative.readRSSI.mockImplementation((_deviceId: string, callback: (success: boolean, rssi: number, error: string) => void) => {
|
|
@@ -326,6 +467,9 @@ describe('BleNitro', () => {
|
|
|
326
467
|
callback(true, id, '');
|
|
327
468
|
});
|
|
328
469
|
await BleManager.connect('device-rssi-fail');
|
|
470
|
+
mockNative.isConnected.mockImplementation((id: string) => {
|
|
471
|
+
return id === 'device-rssi-fail';
|
|
472
|
+
});
|
|
329
473
|
|
|
330
474
|
// Mock readRSSI failure
|
|
331
475
|
mockNative.readRSSI.mockImplementation((_deviceId: string, callback: (success: boolean, rssi: number, error: string) => void) => {
|