react-native-bluetooth-escpos-printer-fork 0.0.17 → 0.0.18
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/README.md +60 -5
- package/android/.classpath +6 -0
- package/android/.gradle/6.1.1/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/6.1.1/fileChanges/last-build.bin +0 -0
- package/android/.gradle/6.1.1/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/6.1.1/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/checksums/checksums.lock +0 -0
- package/android/.gradle/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.project +17 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +11 -0
- package/android/.settings/org.eclipse.jdt.core.prefs +4 -0
- package/ios/PrintImageBleWriteDelegate.m +3 -12
- package/ios/RNBluetoothEscposPrinter.m +1 -0
- package/ios/RNBluetoothManager.h +1 -0
- package/ios/RNBluetoothManager.m +397 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,21 @@ Link the plugin to your RN project
|
|
|
28
28
|
react-native link react-native-bluetooth-escpos-printer
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
### Step3 (iOS only) ###
|
|
32
|
+
Add Bluetooth permissions to your `Info.plist`:
|
|
33
|
+
|
|
34
|
+
```xml
|
|
35
|
+
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
36
|
+
<string>This app needs Bluetooth to discover and connect to printers</string>
|
|
37
|
+
<key>NSBluetoothPeripheralUsageDescription</key>
|
|
38
|
+
<string>This app needs Bluetooth to connect to printer peripherals</string>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Note:**
|
|
42
|
+
- For iOS 13+, `NSBluetoothAlwaysUsageDescription` is required.
|
|
43
|
+
- For iOS 12 and below, `NSBluetoothPeripheralUsageDescription` is required.
|
|
44
|
+
- It's recommended to include both for maximum compatibility.
|
|
45
|
+
|
|
31
46
|
### Manual linking (Android) ###
|
|
32
47
|
Ensure your build files match the following requirements:
|
|
33
48
|
|
|
@@ -73,8 +88,12 @@ async function, checks whether Bluetooth service is enabled.
|
|
|
73
88
|
});
|
|
74
89
|
```
|
|
75
90
|
|
|
76
|
-
* enableBluetooth ==>
|
|
77
|
-
async function, enables the bluetooth service, returns the devices information already bound and paired.
|
|
91
|
+
* enableBluetooth ==>
|
|
92
|
+
async function, enables the bluetooth service, returns the devices information already bound and paired.
|
|
93
|
+
|
|
94
|
+
**Platform differences:**
|
|
95
|
+
- **Android:** If Bluetooth is off, shows system dialog to enable it. Returns array of paired devices after enabling (or immediately if already enabled).
|
|
96
|
+
- **iOS:** Cannot programmatically enable Bluetooth (must be done in Settings by user). Returns empty array `[]` since iOS Core Bluetooth doesn't have a "paired devices" concept. Use `scanDevices()` to discover available devices.
|
|
78
97
|
|
|
79
98
|
```javascript
|
|
80
99
|
BluetoothManager.enableBluetooth().then((r)=>{
|
|
@@ -94,8 +113,12 @@ BluetoothManager.enableBluetooth().then((r)=>{
|
|
|
94
113
|
});
|
|
95
114
|
```
|
|
96
115
|
|
|
97
|
-
* disableBluetooth ==>
|
|
98
|
-
async function
|
|
116
|
+
* disableBluetooth ==>
|
|
117
|
+
async function, disables the bluetooth service.
|
|
118
|
+
|
|
119
|
+
**Platform differences:**
|
|
120
|
+
- **Android:** Programmatically disables Bluetooth and stops any active connections.
|
|
121
|
+
- **iOS:** Cannot programmatically disable Bluetooth (must be done in Settings or Control Center by user). This method resolves successfully but does nothing on iOS.
|
|
99
122
|
|
|
100
123
|
```javascript
|
|
101
124
|
BluetoothManager.disableBluetooth().then(()=>{
|
|
@@ -139,6 +162,36 @@ BluetoothManager.scanDevices()
|
|
|
139
162
|
});
|
|
140
163
|
```
|
|
141
164
|
|
|
165
|
+
**Device Information Structure:**
|
|
166
|
+
|
|
167
|
+
Each device object returned contains:
|
|
168
|
+
- `name` (string): Device name
|
|
169
|
+
- `address` (string): Device MAC address (Android) or UUID (iOS)
|
|
170
|
+
- `deviceClass` (number): Bluetooth device class
|
|
171
|
+
- `majorDeviceClass` (number): Major device class category
|
|
172
|
+
- `rssi` (number, iOS only): Signal strength indicator
|
|
173
|
+
|
|
174
|
+
**Filtering for Printers:**
|
|
175
|
+
|
|
176
|
+
To identify printer devices, check the `majorDeviceClass`:
|
|
177
|
+
```javascript
|
|
178
|
+
const MAJOR_CLASS_IMAGING = 1536; // 0x0600 - Printers, scanners, cameras
|
|
179
|
+
const DEVICE_CLASS_PRINTER = 1664; // 0x0680 - Specifically printers
|
|
180
|
+
|
|
181
|
+
// Filter found devices to show only printers
|
|
182
|
+
const printers = ss.found.filter(device =>
|
|
183
|
+
device.majorDeviceClass === MAJOR_CLASS_IMAGING ||
|
|
184
|
+
device.deviceClass === DEVICE_CLASS_PRINTER
|
|
185
|
+
);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Platform differences:**
|
|
189
|
+
- **Android:** Device classes are provided by the OS based on the device's Bluetooth profile
|
|
190
|
+
- **iOS:** Device classes are inferred from:
|
|
191
|
+
1. Advertised service UUIDs (SPP, ESC/POS services)
|
|
192
|
+
2. Device name patterns (contains "print", "pos", "thermal", "receipt", etc.)
|
|
193
|
+
3. If no printer indicators are found, device is marked as "unknown" (class 7936)
|
|
194
|
+
|
|
142
195
|
* connect ==>
|
|
143
196
|
async function, connects the specified device, if not bound, bound dailog prompts.
|
|
144
197
|
|
|
@@ -162,8 +215,10 @@ async function, connects the specified device, if not bound, bound dailog prompt
|
|
|
162
215
|
* unpair ==>
|
|
163
216
|
async function, disconnects and unpairs the specified devices
|
|
164
217
|
|
|
218
|
+
**Note for iOS:** Due to iOS security restrictions, this method can only disconnect the device but cannot programmatically unpair it. The device will remain paired at the iOS system level. Users must unpair manually from iOS Settings > Bluetooth if needed. On Android, full unpair functionality is supported.
|
|
219
|
+
|
|
165
220
|
```javascript
|
|
166
|
-
BluetoothManager.
|
|
221
|
+
BluetoothManager.unpaire(rowData.address)
|
|
167
222
|
.then((s)=>{
|
|
168
223
|
//success here
|
|
169
224
|
},
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<classpath>
|
|
3
|
+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
|
4
|
+
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
|
5
|
+
<classpathentry kind="output" path="bin/default"/>
|
|
6
|
+
</classpath>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
package/android/.project
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
<projects>
|
|
6
6
|
</projects>
|
|
7
7
|
<buildSpec>
|
|
8
|
+
<buildCommand>
|
|
9
|
+
<name>org.eclipse.jdt.core.javabuilder</name>
|
|
10
|
+
<arguments>
|
|
11
|
+
</arguments>
|
|
12
|
+
</buildCommand>
|
|
8
13
|
<buildCommand>
|
|
9
14
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
|
10
15
|
<arguments>
|
|
@@ -12,6 +17,18 @@
|
|
|
12
17
|
</buildCommand>
|
|
13
18
|
</buildSpec>
|
|
14
19
|
<natures>
|
|
20
|
+
<nature>org.eclipse.jdt.core.javanature</nature>
|
|
15
21
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
|
16
22
|
</natures>
|
|
23
|
+
<filteredResources>
|
|
24
|
+
<filter>
|
|
25
|
+
<id>1769891269456</id>
|
|
26
|
+
<name></name>
|
|
27
|
+
<type>30</type>
|
|
28
|
+
<matcher>
|
|
29
|
+
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
|
30
|
+
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
|
31
|
+
</matcher>
|
|
32
|
+
</filter>
|
|
33
|
+
</filteredResources>
|
|
17
34
|
</projectDescription>
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
+
arguments=--init-script /Users/lukaszprivate/.local/share/opencode/bin/jdtls/config_mac/org.eclipse.osgi/59/0/.cp/gradle/init/init.gradle
|
|
2
|
+
auto.sync=false
|
|
3
|
+
build.scans.enabled=false
|
|
4
|
+
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
|
1
5
|
connection.project.dir=
|
|
2
6
|
eclipse.preferences.version=1
|
|
7
|
+
gradle.user.home=
|
|
8
|
+
java.home=/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home
|
|
9
|
+
jvm.arguments=
|
|
10
|
+
offline.mode=false
|
|
11
|
+
override.workspace.settings=true
|
|
12
|
+
show.console.view=true
|
|
13
|
+
show.executions.view=true
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
- (void) didWriteDataToBle: (BOOL)success
|
|
15
|
-
{
|
|
15
|
+
{
|
|
16
16
|
if(success){
|
|
17
17
|
if(_now == -1){
|
|
18
18
|
if(_pendingResolve) {_pendingResolve(nil); _pendingResolve=nil;}
|
|
@@ -43,19 +43,10 @@
|
|
|
43
43
|
{
|
|
44
44
|
@synchronized (self) {
|
|
45
45
|
NSInteger sizePerLine = (int)(_width/8);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// sizePerLine = [_toPrint length] - _now;
|
|
49
|
-
// }
|
|
50
|
-
// if(sizePerLine>0){
|
|
51
|
-
NSData *subData = [_toPrint subdataWithRange:NSMakeRange(_now, sizePerLine)];
|
|
52
|
-
NSLog(@"Write data:%@",subData);
|
|
53
|
-
[RNBluetoothManager writeValue:subData withDelegate:self];
|
|
54
|
-
//}
|
|
46
|
+
NSData *subData = [_toPrint subdataWithRange:NSMakeRange(_now, sizePerLine)];
|
|
47
|
+
[RNBluetoothManager writeValue:subData withDelegate:self];
|
|
55
48
|
_now = _now+sizePerLine;
|
|
56
49
|
[NSThread sleepForTimeInterval:0.01f];
|
|
57
|
-
|
|
58
50
|
}
|
|
59
|
-
//}while(_now<[_toPrint length]);
|
|
60
51
|
}
|
|
61
52
|
@end
|
|
@@ -560,6 +560,7 @@ RCT_EXPORT_METHOD(printEscPosCommands:(NSString *)base64encodeStr
|
|
|
560
560
|
if(RNBluetoothManager.isConnected){
|
|
561
561
|
@try{
|
|
562
562
|
NSData *decoded = [[NSData alloc] initWithBase64EncodedString:base64encodeStr options:0];
|
|
563
|
+
|
|
563
564
|
pendingResolve = resolve;
|
|
564
565
|
pendingReject = reject;
|
|
565
566
|
[RNBluetoothManager writeValue:decoded withDelegate:self];
|
package/ios/RNBluetoothManager.h
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
@property (nonatomic,copy) RCTPromiseResolveBlock scanResolveBlock;
|
|
20
20
|
@property (nonatomic,copy) RCTPromiseRejectBlock scanRejectBlock;
|
|
21
21
|
@property (strong,nonatomic) NSMutableDictionary <NSString *,CBPeripheral *> *foundDevices;
|
|
22
|
+
@property (strong,nonatomic) NSMutableDictionary <NSString *,NSDictionary *> *foundDevicesMetadata;
|
|
22
23
|
@property (strong,nonatomic) NSString *waitingConnect;
|
|
23
24
|
@property (nonatomic,copy) RCTPromiseResolveBlock connectResolveBlock;
|
|
24
25
|
@property (nonatomic,copy) RCTPromiseRejectBlock connectRejectBlock;
|
package/ios/RNBluetoothManager.m
CHANGED
|
@@ -21,23 +21,104 @@ static NSArray<CBUUID *> *supportServices = nil;
|
|
|
21
21
|
static NSDictionary *writeableCharactiscs = nil;
|
|
22
22
|
bool hasListeners;
|
|
23
23
|
static CBPeripheral *connected;
|
|
24
|
+
static CBCharacteristic *cachedWriteCharacteristic = nil; // Cache the writable characteristic
|
|
24
25
|
static RNBluetoothManager *instance;
|
|
25
26
|
static NSObject<WriteDataToBleDelegate> *writeDataDelegate;// delegate of write data resule;
|
|
26
27
|
static NSData *toWrite;
|
|
27
28
|
static NSTimer *timer;
|
|
28
29
|
|
|
30
|
+
-(instancetype)init {
|
|
31
|
+
if (self = [super init]) {
|
|
32
|
+
// Initialize CBCentralManager eagerly so it's ready when methods are called
|
|
33
|
+
// This triggers the centralManagerDidUpdateState callback immediately
|
|
34
|
+
[self centralManager];
|
|
35
|
+
[self initSupportServices];
|
|
36
|
+
}
|
|
37
|
+
return self;
|
|
38
|
+
}
|
|
39
|
+
|
|
29
40
|
+(Boolean)isConnected{
|
|
30
41
|
return !(connected==nil);
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
// Helper method to write data with automatic chunking for BLE MTU limits
|
|
45
|
+
+(BOOL)writeDataWithChunking:(NSData *)data
|
|
46
|
+
toCharacteristic:(CBCharacteristic *)characteristic
|
|
47
|
+
onPeripheral:(CBPeripheral *)peripheral {
|
|
48
|
+
CBCharacteristicWriteType writeType = (characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse)
|
|
49
|
+
? CBCharacteristicWriteWithoutResponse
|
|
50
|
+
: CBCharacteristicWriteWithResponse;
|
|
51
|
+
|
|
52
|
+
// BLE has MTU limitations - need to chunk data if it's too large
|
|
53
|
+
// The minimum BLE MTU is 23 bytes, with 3 bytes overhead, leaving 20 bytes usable
|
|
54
|
+
// maximumWriteValueLengthForType often returns incorrect values for WriteWithoutResponse
|
|
55
|
+
// so we force a conservative chunk size
|
|
56
|
+
NSUInteger maxWriteLength = 20; // Safe default that works with all BLE devices
|
|
57
|
+
|
|
58
|
+
NSUInteger dataLength = [data length];
|
|
59
|
+
|
|
60
|
+
if(dataLength <= maxWriteLength) {
|
|
61
|
+
// Data fits in one write - no chunking needed
|
|
62
|
+
[peripheral writeValue:data forCharacteristic:characteristic type:writeType];
|
|
63
|
+
return YES;
|
|
64
|
+
} else {
|
|
65
|
+
// Need to chunk the data
|
|
66
|
+
NSLog(@"BLE chunking: %lu bytes -> %lu byte chunks", dataLength, maxWriteLength);
|
|
67
|
+
NSUInteger offset = 0;
|
|
68
|
+
BOOL allSuccess = YES;
|
|
69
|
+
NSUInteger chunkCount = 0;
|
|
70
|
+
|
|
71
|
+
while(offset < dataLength && allSuccess) {
|
|
72
|
+
NSUInteger chunkSize = MIN(maxWriteLength, dataLength - offset);
|
|
73
|
+
NSData *chunk = [data subdataWithRange:NSMakeRange(offset, chunkSize)];
|
|
74
|
+
|
|
75
|
+
@try {
|
|
76
|
+
[peripheral writeValue:chunk forCharacteristic:characteristic type:writeType];
|
|
77
|
+
chunkCount++;
|
|
78
|
+
|
|
79
|
+
// Small delay between chunks to avoid overwhelming the device
|
|
80
|
+
if(writeType == CBCharacteristicWriteWithoutResponse) {
|
|
81
|
+
[NSThread sleepForTimeInterval:0.01]; // 10ms delay
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
@catch(NSException *e) {
|
|
85
|
+
NSLog(@"BLE chunk write error at offset %lu: %@", offset, e);
|
|
86
|
+
allSuccess = NO;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
offset += chunkSize;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if(allSuccess) {
|
|
93
|
+
NSLog(@"BLE chunking complete: %lu chunks sent", chunkCount);
|
|
94
|
+
}
|
|
95
|
+
return allSuccess;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
33
99
|
+(void)writeValue:(NSData *) data withDelegate:(NSObject<WriteDataToBleDelegate> *) delegate
|
|
34
100
|
{
|
|
35
101
|
@try{
|
|
36
102
|
writeDataDelegate = delegate;
|
|
37
103
|
toWrite = data;
|
|
38
104
|
connected.delegate = instance;
|
|
39
|
-
|
|
40
|
-
//
|
|
105
|
+
|
|
106
|
+
// If we have a cached writable characteristic, use it directly
|
|
107
|
+
if(cachedWriteCharacteristic && connected){
|
|
108
|
+
// Use helper method to write with chunking
|
|
109
|
+
BOOL success = [RNBluetoothManager writeDataWithChunking:toWrite
|
|
110
|
+
toCharacteristic:cachedWriteCharacteristic
|
|
111
|
+
onPeripheral:connected];
|
|
112
|
+
|
|
113
|
+
if(writeDataDelegate) [writeDataDelegate didWriteDataToBle:success];
|
|
114
|
+
toWrite = nil;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// No cached characteristic, need to discover services first
|
|
119
|
+
NSLog(@"No cached characteristic, discovering services...");
|
|
120
|
+
// Discover all services (not just SPP which is for Classic BT, not BLE)
|
|
121
|
+
[connected discoverServices:nil];
|
|
41
122
|
}
|
|
42
123
|
@catch(NSException *e){
|
|
43
124
|
NSLog(@"error in writing data to %@,issue:%@",connected,e);
|
|
@@ -119,7 +200,11 @@ RCT_EXPORT_METHOD(isBluetoothEnabled:(RCTPromiseResolveBlock)resolve
|
|
|
119
200
|
RCT_EXPORT_METHOD(enableBluetooth:(RCTPromiseResolveBlock)resolve
|
|
120
201
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
121
202
|
{
|
|
122
|
-
|
|
203
|
+
// iOS doesn't allow programmatic Bluetooth enabling
|
|
204
|
+
// Bluetooth must be enabled from Settings by the user
|
|
205
|
+
// Return empty array to match Android's return type (array of paired devices)
|
|
206
|
+
// Since iOS Core Bluetooth doesn't have "paired devices" concept, we return empty array
|
|
207
|
+
resolve(@[]);
|
|
123
208
|
}
|
|
124
209
|
//disableBluetooth
|
|
125
210
|
RCT_EXPORT_METHOD(disableBluetooth:(RCTPromiseResolveBlock)resolve
|
|
@@ -141,15 +226,50 @@ RCT_EXPORT_METHOD(scanDevices:(RCTPromiseResolveBlock)resolve
|
|
|
141
226
|
}
|
|
142
227
|
self.scanResolveBlock = resolve;
|
|
143
228
|
self.scanRejectBlock = reject;
|
|
229
|
+
|
|
230
|
+
// Initialize metadata dictionary if needed
|
|
231
|
+
if(!self.foundDevicesMetadata){
|
|
232
|
+
self.foundDevicesMetadata = [[NSMutableDictionary alloc] init];
|
|
233
|
+
}
|
|
234
|
+
|
|
144
235
|
if(connected && connected.identifier){
|
|
145
|
-
|
|
146
|
-
|
|
236
|
+
NSString *name = connected.name ? connected.name : @"";
|
|
237
|
+
NSString *address = connected.identifier.UUIDString;
|
|
238
|
+
|
|
239
|
+
// Create device info with device class for connected device
|
|
240
|
+
NSMutableDictionary *deviceInfo = [[NSMutableDictionary alloc] init];
|
|
241
|
+
[deviceInfo setObject:address forKey:@"address"];
|
|
242
|
+
[deviceInfo setObject:name forKey:@"name"];
|
|
243
|
+
[deviceInfo setObject:@(0) forKey:@"rssi"]; // Unknown RSSI for already connected device
|
|
244
|
+
|
|
245
|
+
// Try to infer if it's a printer from the name
|
|
246
|
+
BOOL isPrinter = NO;
|
|
247
|
+
NSString *upperName = name.uppercaseString;
|
|
248
|
+
if ([upperName containsString:@"PRINT"] ||
|
|
249
|
+
[upperName containsString:@"POS"] ||
|
|
250
|
+
[upperName containsString:@"ESCPOS"] ||
|
|
251
|
+
[upperName containsString:@"THERMAL"] ||
|
|
252
|
+
[upperName containsString:@"RECEIPT"]) {
|
|
253
|
+
isPrinter = YES;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (isPrinter) {
|
|
257
|
+
[deviceInfo setObject:@(1536) forKey:@"majorDeviceClass"]; // Imaging
|
|
258
|
+
[deviceInfo setObject:@(1664) forKey:@"deviceClass"]; // Printer
|
|
259
|
+
} else {
|
|
260
|
+
[deviceInfo setObject:@(7936) forKey:@"majorDeviceClass"]; // Unknown
|
|
261
|
+
[deviceInfo setObject:@(7936) forKey:@"deviceClass"]; // Unknown
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
NSDictionary *peripheralStored = @{address:connected};
|
|
147
265
|
if(!self.foundDevices){
|
|
148
266
|
self.foundDevices = [[NSMutableDictionary alloc] init];
|
|
149
267
|
}
|
|
150
268
|
[self.foundDevices addEntriesFromDictionary:peripheralStored];
|
|
269
|
+
[self.foundDevicesMetadata setObject:deviceInfo forKey:address];
|
|
270
|
+
|
|
151
271
|
if(hasListeners){
|
|
152
|
-
[self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":
|
|
272
|
+
[self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":deviceInfo}];
|
|
153
273
|
}
|
|
154
274
|
}
|
|
155
275
|
[self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
|
|
@@ -183,6 +303,13 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
183
303
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
184
304
|
{
|
|
185
305
|
NSLog(@"Trying to connect....%@",address);
|
|
306
|
+
|
|
307
|
+
// Check if Bluetooth is ready
|
|
308
|
+
if(!self.centralManager || self.centralManager.state!=CBManagerStatePoweredOn){
|
|
309
|
+
reject(@"BLUETOOTH_NOT_READY",@"Bluetooth is not enabled or not ready. Please enable Bluetooth and try again.",nil);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
186
313
|
[self callStop];
|
|
187
314
|
if(connected){
|
|
188
315
|
NSString *connectedAddress =connected.identifier.UUIDString;
|
|
@@ -205,6 +332,13 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
205
332
|
// Callbacks:
|
|
206
333
|
// centralManager:didConnectPeripheral:
|
|
207
334
|
// centralManager:didFailToConnectPeripheral:error:
|
|
335
|
+
|
|
336
|
+
// Set connection timeout (30 seconds)
|
|
337
|
+
if(timer && timer.isValid){
|
|
338
|
+
[timer invalidate];
|
|
339
|
+
timer = nil;
|
|
340
|
+
}
|
|
341
|
+
timer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(handleConnectTimeout) userInfo:nil repeats:NO];
|
|
208
342
|
}else{
|
|
209
343
|
//starts the scan.
|
|
210
344
|
_waitingConnect = address;
|
|
@@ -212,32 +346,125 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
212
346
|
[self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@NO}];
|
|
213
347
|
//Callbacks:
|
|
214
348
|
//centralManager:didDiscoverPeripheral:advertisementData:RSSI:
|
|
349
|
+
|
|
350
|
+
// Set scan + connection timeout (30 seconds)
|
|
351
|
+
if(timer && timer.isValid){
|
|
352
|
+
[timer invalidate];
|
|
353
|
+
timer = nil;
|
|
354
|
+
}
|
|
355
|
+
timer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(handleConnectTimeout) userInfo:nil repeats:NO];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
//disconnect(address)
|
|
359
|
+
RCT_EXPORT_METHOD(disconnect:(NSString *)address
|
|
360
|
+
findEventsWithResolver:(RCTPromiseResolveBlock)resolve
|
|
361
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
362
|
+
{
|
|
363
|
+
NSLog(@"Trying to disconnect device: %@",address);
|
|
364
|
+
|
|
365
|
+
// Check if Bluetooth is ready
|
|
366
|
+
if(!self.centralManager || self.centralManager.state!=CBManagerStatePoweredOn){
|
|
367
|
+
reject(@"BLUETOOTH_NOT_READY",@"Bluetooth is not enabled or not ready.",nil);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if(connected && [connected.identifier.UUIDString isEqualToString:address]){
|
|
372
|
+
[self.centralManager cancelPeripheralConnection:connected];
|
|
373
|
+
// Note: disconnection is async, but we resolve immediately like Android does
|
|
374
|
+
// The didDisconnectPeripheral callback will handle setting connected = nil
|
|
375
|
+
resolve(address);
|
|
376
|
+
} else {
|
|
377
|
+
// Device not connected or different device
|
|
378
|
+
NSLog(@"Device not connected or different device: %@",address);
|
|
379
|
+
resolve(address);
|
|
215
380
|
}
|
|
216
381
|
}
|
|
217
382
|
//unpaire(address)
|
|
383
|
+
RCT_EXPORT_METHOD(unpaire:(NSString *)address
|
|
384
|
+
findEventsWithResolver:(RCTPromiseResolveBlock)resolve
|
|
385
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
386
|
+
{
|
|
387
|
+
NSLog(@"Trying to unpair/disconnect device: %@",address);
|
|
388
|
+
if(connected && [connected.identifier.UUIDString isEqualToString:address]){
|
|
389
|
+
[self.centralManager cancelPeripheralConnection:connected];
|
|
390
|
+
// Note: Core Bluetooth on iOS doesn't support programmatic unpairing
|
|
391
|
+
// The connection will be cancelled, but the device remains paired at OS level
|
|
392
|
+
// Users must unpair manually from iOS Settings if needed
|
|
393
|
+
connected = nil;
|
|
394
|
+
resolve(address);
|
|
395
|
+
} else {
|
|
396
|
+
// Device not connected, just resolve
|
|
397
|
+
NSLog(@"Device not connected, nothing to unpair: %@",address);
|
|
398
|
+
resolve(address);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
218
402
|
|
|
403
|
+
-(void)handleConnectTimeout{
|
|
404
|
+
NSLog(@"Connection timeout - device not found or unable to connect");
|
|
405
|
+
if(self.centralManager.isScanning){
|
|
406
|
+
[self.centralManager stopScan];
|
|
407
|
+
}
|
|
408
|
+
if(self.connectRejectBlock){
|
|
409
|
+
RCTPromiseRejectBlock rjBlock = self.connectRejectBlock;
|
|
410
|
+
rjBlock(@"CONNECTION_TIMEOUT",@"Could not connect to device - timeout after 30 seconds",nil);
|
|
411
|
+
self.connectRejectBlock = nil;
|
|
412
|
+
self.connectResolveBlock = nil;
|
|
413
|
+
}
|
|
414
|
+
_waitingConnect = nil;
|
|
415
|
+
if(timer && timer.isValid){
|
|
416
|
+
[timer invalidate];
|
|
417
|
+
timer = nil;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
219
420
|
|
|
220
421
|
-(void)callStop{
|
|
221
422
|
if(self.centralManager.isScanning){
|
|
222
423
|
[self.centralManager stopScan];
|
|
223
424
|
NSMutableArray *devices = [[NSMutableArray alloc] init];
|
|
224
425
|
for(NSString *key in self.foundDevices){
|
|
225
|
-
NSLog(@"insert found
|
|
226
|
-
|
|
227
|
-
if
|
|
228
|
-
|
|
426
|
+
NSLog(@"insert found devices:%@ =>%@",key,[self.foundDevices objectForKey:key]);
|
|
427
|
+
|
|
428
|
+
// Get stored metadata if available, otherwise create basic info
|
|
429
|
+
NSDictionary *deviceInfo = [self.foundDevicesMetadata objectForKey:key];
|
|
430
|
+
if (deviceInfo) {
|
|
431
|
+
[devices addObject:deviceInfo];
|
|
432
|
+
} else {
|
|
433
|
+
// Fallback if metadata wasn't stored (shouldn't happen with new code)
|
|
434
|
+
NSString *name = [self.foundDevices objectForKey:key].name;
|
|
435
|
+
if(!name){
|
|
436
|
+
name = @"";
|
|
437
|
+
}
|
|
438
|
+
[devices addObject:@{
|
|
439
|
+
@"address":key,
|
|
440
|
+
@"name":name,
|
|
441
|
+
@"deviceClass":@(7936), // Unknown device class
|
|
442
|
+
@"majorDeviceClass":@(7936) // Unknown major device class
|
|
443
|
+
}];
|
|
229
444
|
}
|
|
230
|
-
[devices addObject:@{@"address":key,@"name":name}];
|
|
231
445
|
}
|
|
446
|
+
|
|
447
|
+
// Create result object matching Android format: {"paired": [...], "found": [...]}
|
|
448
|
+
// Empty paired devices array (iOS doesn't have "paired" concept like Android)
|
|
449
|
+
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
|
|
450
|
+
[result setObject:@[] forKey:@"paired"];
|
|
451
|
+
[result setObject:devices forKey:@"found"];
|
|
452
|
+
|
|
453
|
+
// Convert the entire result to a JSON string (matching Android's behavior)
|
|
232
454
|
NSError *error = nil;
|
|
233
|
-
NSData*
|
|
234
|
-
NSString *
|
|
455
|
+
NSData* resultJsonData = [NSJSONSerialization dataWithJSONObject:result options:0 error:&error];
|
|
456
|
+
NSString *resultJsonStr = [[NSString alloc] initWithData:resultJsonData encoding:NSUTF8StringEncoding];
|
|
457
|
+
|
|
235
458
|
if(hasListeners){
|
|
236
|
-
|
|
459
|
+
// For events, send separate paired and found strings
|
|
460
|
+
NSData* foundJsonData = [NSJSONSerialization dataWithJSONObject:devices options:0 error:&error];
|
|
461
|
+
NSString *foundJsonStr = [[NSString alloc] initWithData:foundJsonData encoding:NSUTF8StringEncoding];
|
|
462
|
+
[self sendEventWithName:EVENT_DEVICE_DISCOVER_DONE body:@{@"found":foundJsonStr,@"paired":@"[]"}];
|
|
237
463
|
}
|
|
238
464
|
if(self.scanResolveBlock){
|
|
239
465
|
RCTPromiseResolveBlock rsBlock = self.scanResolveBlock;
|
|
240
|
-
|
|
466
|
+
// Return single JSON string: "{\"paired\":[],\"found\":[...]}"
|
|
467
|
+
rsBlock(resultJsonStr);
|
|
241
468
|
self.scanResolveBlock = nil;
|
|
242
469
|
}
|
|
243
470
|
}
|
|
@@ -251,9 +478,11 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
251
478
|
- (void) initSupportServices
|
|
252
479
|
{
|
|
253
480
|
if(!supportServices){
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
481
|
+
// Use standard SPP (Serial Port Profile) UUID - same as Android
|
|
482
|
+
CBUUID *spp = [CBUUID UUIDWithString: @"00001101-0000-1000-8000-00805F9B34FB"];
|
|
483
|
+
supportServices = [NSArray arrayWithObject:spp];
|
|
484
|
+
// SPP uses the same UUID for service and characteristic
|
|
485
|
+
writeableCharactiscs = @{spp:@"00001101-0000-1000-8000-00805F9B34FB"};
|
|
257
486
|
}
|
|
258
487
|
}
|
|
259
488
|
|
|
@@ -284,19 +513,108 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
284
513
|
* CBCentralManagerDelegate
|
|
285
514
|
**/
|
|
286
515
|
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
|
|
287
|
-
|
|
516
|
+
NSString *stateString;
|
|
517
|
+
switch (central.state) {
|
|
518
|
+
case CBManagerStateUnknown:
|
|
519
|
+
stateString = @"Unknown";
|
|
520
|
+
break;
|
|
521
|
+
case CBManagerStateResetting:
|
|
522
|
+
stateString = @"Resetting";
|
|
523
|
+
break;
|
|
524
|
+
case CBManagerStateUnsupported:
|
|
525
|
+
stateString = @"Unsupported";
|
|
526
|
+
break;
|
|
527
|
+
case CBManagerStateUnauthorized:
|
|
528
|
+
stateString = @"Unauthorized";
|
|
529
|
+
break;
|
|
530
|
+
case CBManagerStatePoweredOff:
|
|
531
|
+
stateString = @"PoweredOff";
|
|
532
|
+
break;
|
|
533
|
+
case CBManagerStatePoweredOn:
|
|
534
|
+
stateString = @"PoweredOn";
|
|
535
|
+
break;
|
|
536
|
+
default:
|
|
537
|
+
stateString = @"Unknown";
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
NSLog(@"Bluetooth State Changed: %@ (%ld)", stateString, (long)central.state);
|
|
288
541
|
}
|
|
289
542
|
|
|
290
543
|
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
|
|
291
544
|
NSLog(@"did discover peripheral: %@",peripheral);
|
|
292
|
-
|
|
545
|
+
|
|
546
|
+
// Extract device information with enhanced metadata
|
|
547
|
+
NSMutableDictionary *deviceInfo = [[NSMutableDictionary alloc] init];
|
|
548
|
+
[deviceInfo setObject:peripheral.identifier.UUIDString forKey:@"address"];
|
|
549
|
+
[deviceInfo setObject:(peripheral.name ? peripheral.name : @"") forKey:@"name"];
|
|
550
|
+
|
|
551
|
+
// Add RSSI for signal strength
|
|
552
|
+
[deviceInfo setObject:RSSI forKey:@"rssi"];
|
|
553
|
+
|
|
554
|
+
// Try to infer device class from advertising data
|
|
555
|
+
// iOS BLE doesn't expose Classic Bluetooth device class, but we can make educated guesses
|
|
556
|
+
NSInteger deviceClass = 0;
|
|
557
|
+
NSInteger majorDeviceClass = 0;
|
|
558
|
+
|
|
559
|
+
// Check if device advertises printer-related services
|
|
560
|
+
NSArray *serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey];
|
|
561
|
+
BOOL isPrinter = NO;
|
|
562
|
+
|
|
563
|
+
if (serviceUUIDs && serviceUUIDs.count > 0) {
|
|
564
|
+
for (CBUUID *uuid in serviceUUIDs) {
|
|
565
|
+
NSString *uuidString = uuid.UUIDString.uppercaseString;
|
|
566
|
+
// Serial Port Profile (SPP) UUID - common for printers
|
|
567
|
+
// Also check for other printer-related service UUIDs
|
|
568
|
+
if ([uuidString isEqualToString:@"00001101-0000-1000-8000-00805F9B34FB"] ||
|
|
569
|
+
[uuidString isEqualToString:@"49535343-FE7D-4AE5-8FA9-9FAFD205E455"] ||
|
|
570
|
+
[uuidString containsString:@"18F0"]) {
|
|
571
|
+
isPrinter = YES;
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Check device name for printer keywords
|
|
578
|
+
NSString *name = peripheral.name ? peripheral.name.uppercaseString : @"";
|
|
579
|
+
if ([name containsString:@"PRINT"] ||
|
|
580
|
+
[name containsString:@"POS"] ||
|
|
581
|
+
[name containsString:@"ESCPOS"] ||
|
|
582
|
+
[name containsString:@"THERMAL"] ||
|
|
583
|
+
[name containsString:@"RECEIPT"]) {
|
|
584
|
+
isPrinter = YES;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (isPrinter) {
|
|
588
|
+
// Android Bluetooth Class values for printers:
|
|
589
|
+
// Major Device Class: 0x0600 (Imaging - printers, scanners, cameras, etc.)
|
|
590
|
+
// Device Class: 0x000680 (Imaging/Printer)
|
|
591
|
+
majorDeviceClass = 1536; // 0x0600 in decimal
|
|
592
|
+
deviceClass = 1664; // 0x0680 in decimal
|
|
593
|
+
} else {
|
|
594
|
+
// Unknown/Unclassified device
|
|
595
|
+
// Major Device Class: 0x1F00 (Uncategorized)
|
|
596
|
+
majorDeviceClass = 7936; // 0x1F00 in decimal
|
|
597
|
+
deviceClass = 7936;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
[deviceInfo setObject:@(deviceClass) forKey:@"deviceClass"];
|
|
601
|
+
[deviceInfo setObject:@(majorDeviceClass) forKey:@"majorDeviceClass"];
|
|
602
|
+
|
|
603
|
+
// Store the peripheral object for connection
|
|
293
604
|
NSDictionary *peripheralStored = @{peripheral.identifier.UUIDString:peripheral};
|
|
294
605
|
if(!self.foundDevices){
|
|
295
606
|
self.foundDevices = [[NSMutableDictionary alloc] init];
|
|
296
607
|
}
|
|
297
608
|
[self.foundDevices addEntriesFromDictionary:peripheralStored];
|
|
609
|
+
|
|
610
|
+
// Store device metadata for later retrieval
|
|
611
|
+
if(!self.foundDevicesMetadata){
|
|
612
|
+
self.foundDevicesMetadata = [[NSMutableDictionary alloc] init];
|
|
613
|
+
}
|
|
614
|
+
[self.foundDevicesMetadata setObject:deviceInfo forKey:peripheral.identifier.UUIDString];
|
|
615
|
+
|
|
298
616
|
if(hasListeners){
|
|
299
|
-
[self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":
|
|
617
|
+
[self sendEventWithName:EVENT_DEVICE_FOUND body:@{@"device":deviceInfo}];
|
|
300
618
|
}
|
|
301
619
|
if(_waitingConnect && [_waitingConnect isEqualToString: peripheral.identifier.UUIDString]){
|
|
302
620
|
[self.centralManager connectPeripheral:peripheral options:nil];
|
|
@@ -308,10 +626,24 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
308
626
|
NSLog(@"did connected: %@",peripheral);
|
|
309
627
|
connected = peripheral;
|
|
310
628
|
NSString *pId = peripheral.identifier.UUIDString;
|
|
629
|
+
|
|
630
|
+
// Set peripheral delegate to receive service/characteristic callbacks
|
|
631
|
+
peripheral.delegate = self;
|
|
632
|
+
|
|
311
633
|
if(_waitingConnect && [_waitingConnect isEqualToString: pId] && self.connectResolveBlock){
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
634
|
+
// Cancel connection timeout timer if it's running
|
|
635
|
+
if(timer && timer.isValid){
|
|
636
|
+
[timer invalidate];
|
|
637
|
+
timer = nil;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Pre-discover services to prepare for future write operations
|
|
641
|
+
// This matches Android's behavior where the connection is fully ready after connect
|
|
642
|
+
// Pass nil to discover ALL services (not just SPP which is for Classic BT, not BLE)
|
|
643
|
+
NSLog(@"Pre-discovering all services for faster write operations...");
|
|
644
|
+
[peripheral discoverServices:nil];
|
|
645
|
+
|
|
646
|
+
// Resolve promise immediately (iOS BLE connects fast, service discovery happens async)
|
|
315
647
|
self.connectResolveBlock(nil);
|
|
316
648
|
_waitingConnect = nil;
|
|
317
649
|
self.connectRejectBlock = nil;
|
|
@@ -324,6 +656,9 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
324
656
|
}
|
|
325
657
|
|
|
326
658
|
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
|
|
659
|
+
// Clear cached characteristic when disconnecting
|
|
660
|
+
cachedWriteCharacteristic = nil;
|
|
661
|
+
|
|
327
662
|
if(!connected && _waitingConnect && [_waitingConnect isEqualToString:peripheral.identifier.UUIDString]){
|
|
328
663
|
if(self.connectRejectBlock){
|
|
329
664
|
RCTPromiseRejectBlock rjBlock = self.connectRejectBlock;
|
|
@@ -345,6 +680,9 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
345
680
|
}
|
|
346
681
|
|
|
347
682
|
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
|
|
683
|
+
// Clear cached characteristic when connection fails
|
|
684
|
+
cachedWriteCharacteristic = nil;
|
|
685
|
+
|
|
348
686
|
if(self.connectRejectBlock){
|
|
349
687
|
RCTPromiseRejectBlock rjBlock = self.connectRejectBlock;
|
|
350
688
|
rjBlock(@"",@"",error);
|
|
@@ -412,38 +750,59 @@ RCT_EXPORT_METHOD(connect:(NSString *)address
|
|
|
412
750
|
*/
|
|
413
751
|
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error{
|
|
414
752
|
if(toWrite && connected
|
|
415
|
-
&& [connected.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]
|
|
416
|
-
&& [service.UUID.UUIDString isEqualToString:supportServices[0].UUIDString]){
|
|
753
|
+
&& [connected.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]){
|
|
417
754
|
if(error){
|
|
418
|
-
NSLog(@"
|
|
755
|
+
NSLog(@"Discover characteristics error:%@",error);
|
|
419
756
|
if(writeDataDelegate)
|
|
420
757
|
{
|
|
421
758
|
[writeDataDelegate didWriteDataToBle:false];
|
|
422
759
|
return;
|
|
423
760
|
}
|
|
424
761
|
}
|
|
762
|
+
|
|
763
|
+
// Try to write to any service that has writable characteristics
|
|
764
|
+
// Don't filter by supportServices - many BLE printers use custom UUIDs
|
|
765
|
+
NSLog(@"Checking service %@ for writable characteristics...", service.UUID.UUIDString);
|
|
766
|
+
|
|
767
|
+
// Auto-detect writable characteristic by checking properties
|
|
425
768
|
for(CBCharacteristic *cc in service.characteristics){
|
|
426
|
-
NSLog(@"
|
|
427
|
-
|
|
769
|
+
NSLog(@"Characteristic found: %@ in service: %@" ,cc,service.UUID.UUIDString);
|
|
770
|
+
|
|
771
|
+
// Check if characteristic supports write operations
|
|
772
|
+
BOOL canWrite = (cc.properties & CBCharacteristicPropertyWrite) ||
|
|
773
|
+
(cc.properties & CBCharacteristicPropertyWriteWithoutResponse);
|
|
774
|
+
|
|
775
|
+
if(canWrite){
|
|
428
776
|
@try{
|
|
429
|
-
[
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
777
|
+
NSLog(@"Writing %lu bytes to characteristic %@ in service %@",[toWrite length], cc.UUID.UUIDString, service.UUID.UUIDString);
|
|
778
|
+
|
|
779
|
+
// Use helper method to write with chunking
|
|
780
|
+
BOOL success = [RNBluetoothManager writeDataWithChunking:toWrite
|
|
781
|
+
toCharacteristic:cc
|
|
782
|
+
onPeripheral:connected];
|
|
783
|
+
|
|
784
|
+
// Cache this characteristic for future writes
|
|
785
|
+
cachedWriteCharacteristic = cc;
|
|
786
|
+
NSLog(@"Cached characteristic %@ for future writes", cc.UUID.UUIDString);
|
|
787
|
+
|
|
788
|
+
if(writeDataDelegate) [writeDataDelegate didWriteDataToBle:success];
|
|
789
|
+
toWrite = nil; // Clear the write buffer
|
|
790
|
+
return; // Success, exit after first successful write
|
|
434
791
|
}
|
|
435
792
|
@catch(NSException *e){
|
|
436
|
-
NSLog(@"
|
|
437
|
-
|
|
793
|
+
NSLog(@"ERROR IN WRITE VALUE: %@",e);
|
|
794
|
+
[writeDataDelegate didWriteDataToBle:false];
|
|
438
795
|
}
|
|
439
796
|
}
|
|
440
797
|
}
|
|
441
798
|
|
|
442
|
-
|
|
799
|
+
// If we get here, no writable characteristic was found in this service
|
|
800
|
+
NSLog(@"No writable characteristic found in service: %@", service.UUID.UUIDString);
|
|
801
|
+
// Don't call didWriteDataToBle:false here - there might be other services to check
|
|
443
802
|
}
|
|
444
803
|
|
|
445
804
|
if(error){
|
|
446
|
-
NSLog(@"
|
|
805
|
+
NSLog(@"Discover characteristics error:%@",error);
|
|
447
806
|
return;
|
|
448
807
|
}
|
|
449
808
|
|