@ruhiverse/thermal-printer-plugin 1.0.9 → 1.0.13

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 CHANGED
@@ -136,22 +136,28 @@ Returns a list of connected USB printers.
136
136
 
137
137
  ## Platform Support
138
138
 
139
- - Android (USB & Bluetooth)
140
- - iOS (USB & Bluetooth - basic implementation)
141
- - Web (not supported)
139
+ | Platform | Bluetooth | USB | Printer Discovery |
140
+ | -------- | --------- | --- | ----------------- |
141
+ | Android | ✅ | ✅ | ✅ (paired + scan) |
142
+ | iOS | ✅ (MFi & BLE) | ✅ (MFi only) | ✅ (MFi + BLE scan) |
143
+ | Web | ❌ | ❌ | ❌ |
142
144
 
143
145
  ## Android Setup
144
146
 
145
147
  ### Permissions
146
148
 
147
- The plugin automatically requests the following permissions:
149
+ The plugin declares the following permissions in its `AndroidManifest.xml`:
148
150
 
149
151
  - `BLUETOOTH`
150
152
  - `BLUETOOTH_ADMIN`
151
153
  - `BLUETOOTH_CONNECT` (Android 12+)
152
154
  - `BLUETOOTH_SCAN` (Android 12+)
155
+ - `ACCESS_FINE_LOCATION` (required for Bluetooth scanning on Android 6-11)
156
+ - `ACCESS_COARSE_LOCATION`
153
157
  - `USB_PERMISSION`
154
158
 
159
+ The plugin automatically requests Bluetooth permissions at runtime when needed.
160
+
155
161
  ### USB Printing
156
162
 
157
163
  1. Connect your thermal printer via USB
@@ -160,20 +166,21 @@ The plugin automatically requests the following permissions:
160
166
 
161
167
  ### Bluetooth Printing
162
168
 
163
- 1. Pair your thermal printer with the device via Bluetooth settings
164
- 2. The plugin will automatically find and connect to paired printers
169
+ 1. Pair your thermal printer with the device via **Android Settings > Bluetooth**
170
+ 2. Use `listBluetoothPrinters()` to get the list of paired devices
171
+ 3. Use `printByBluetooth()` with the printer's `address` to print, or omit `address` to use the first paired printer
165
172
 
166
173
  ## iOS Setup
167
174
 
168
- ### Info.plist Configuration
175
+ ### Required Info.plist Configuration
169
176
 
170
- Add the following to your app's `Info.plist`:
177
+ Add the following keys to your app's `Info.plist`. **Without these, Bluetooth will not work on iOS.**
171
178
 
172
179
  ```xml
173
180
  <key>NSBluetoothAlwaysUsageDescription</key>
174
- <string>This app needs Bluetooth access to connect to thermal printers</string>
181
+ <string>This app needs Bluetooth access to connect to thermal printers.</string>
175
182
  <key>NSBluetoothPeripheralUsageDescription</key>
176
- <string>This app needs Bluetooth access to connect to thermal printers</string>
183
+ <string>This app needs Bluetooth access to connect to thermal printers.</string>
177
184
  <key>UISupportedExternalAccessoryProtocols</key>
178
185
  <array>
179
186
  <string>com.epson.escpos</string>
@@ -181,72 +188,70 @@ Add the following to your app's `Info.plist`:
181
188
  </array>
182
189
  ```
183
190
 
184
- **Note:** iOS implementation may require additional setup based on your printer model. USB printing on iOS requires MFi (Made for iPhone) certification.
191
+ | Key | Required | Description |
192
+ | --- | -------- | ----------- |
193
+ | `NSBluetoothAlwaysUsageDescription` | **Yes** | iOS will silently block Bluetooth access without this. Triggers the permission dialog. |
194
+ | `NSBluetoothPeripheralUsageDescription` | **Yes** | Required for connecting to Bluetooth peripherals (iOS 12 and earlier). |
195
+ | `UISupportedExternalAccessoryProtocols` | **Yes** (for MFi printers) | Lists the protocol strings your printer supports. Update with your printer's protocols. |
185
196
 
186
- ## Local Testing
197
+ ### iOS Bluetooth Printing
187
198
 
188
- ### Testing Locally Before Publishing to npm
199
+ The plugin supports two types of Bluetooth connections on iOS:
189
200
 
190
- 1. **Link the plugin locally:**
201
+ **MFi / ExternalAccessory printers (classic Bluetooth):**
202
+ - Requires the printer to be MFi-certified (Made for iPhone)
203
+ - Uses the ExternalAccessory framework
204
+ - Printer must be paired in iOS Settings > Bluetooth
205
+ - Protocol strings must be listed in `UISupportedExternalAccessoryProtocols`
191
206
 
192
- ```bash
193
- cd /Users/apple/Desktop/ionic/workspace/plugin/thermal-printer-plugin
194
- npm link
195
- ```
207
+ **BLE printers (Bluetooth Low Energy):**
208
+ - Uses CoreBluetooth framework
209
+ - Discovered via BLE scanning (4-second scan)
210
+ - Use the `address` from `listBluetoothPrinters()` when calling `printByBluetooth()`
196
211
 
197
- 2. **In your Ionic/Capacitor project (e.g., milkwala project):**
212
+ ### iOS USB Printing
198
213
 
199
- ```bash
200
- cd /path/to/milkwala/project
201
- npm link @ruhiverse/thermal-printer-plugin
202
- npx cap sync
203
- ```
214
+ USB printing on iOS requires MFi (Made for iPhone) certification for the printer. The printer is accessed through the ExternalAccessory framework.
204
215
 
205
- 3. **Build and test:**
216
+ ### iOS Troubleshooting
206
217
 
207
- ```bash
208
- # For Android
209
- npx cap run android
210
-
211
- # For iOS
212
- npx cap run ios
213
- ```
218
+ | Issue | Solution |
219
+ | ----- | -------- |
220
+ | `listBluetoothPrinters()` returns empty | Make sure `NSBluetoothAlwaysUsageDescription` is in your Info.plist and Bluetooth permission is granted |
221
+ | Printer not found (MFi) | Verify the printer's protocol string is listed in `UISupportedExternalAccessoryProtocols` |
222
+ | Printer not found (BLE) | Ensure the printer is powered on and in range. BLE scan runs for 4 seconds |
223
+ | Permission dialog not showing | Delete and reinstall the app to reset permissions |
214
224
 
215
- ### Alternative: Using file path
225
+ ## Local Development
216
226
 
217
- You can also install directly from the local path:
227
+ ### Testing Locally Before Publishing
218
228
 
219
- ```bash
220
- cd /path/to/milkwala/project
221
- npm install /Users/apple/Desktop/ionic/workspace/plugin/thermal-printer-plugin
222
- npx cap sync
223
- ```
224
-
225
- ## Publishing to npm
226
-
227
- 1. **Login to npm (if not already logged in):**
229
+ 1. **Link the plugin locally:**
228
230
 
229
231
  ```bash
230
- npm login
231
- # Use your ruhiverse npm account credentials
232
+ cd path/to/thermal-printer-plugin
233
+ npm link
232
234
  ```
233
235
 
234
- 2. **Build the plugin:**
236
+ 2. **In your Ionic/Capacitor project:**
235
237
 
236
238
  ```bash
237
- npm run build
239
+ cd path/to/your-project
240
+ npm link @ruhiverse/thermal-printer-plugin
241
+ npx cap sync
238
242
  ```
239
243
 
240
- 3. **Publish to npm:**
244
+ 3. **Build and test:**
241
245
 
242
246
  ```bash
243
- npm publish --access public
247
+ npx cap run android
248
+ npx cap run ios
244
249
  ```
245
250
 
246
- 4. **After publishing, install in your project:**
251
+ ### Alternative: Install from local path
247
252
 
248
253
  ```bash
249
- npm install @ruhiverse/thermal-printer-plugin
254
+ npm install path/to/thermal-printer-plugin
250
255
  npx cap sync
251
256
  ```
252
257
 
@@ -2,6 +2,8 @@
2
2
  <uses-permission android:name="android.permission.BLUETOOTH" />
3
3
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
4
4
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
5
- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
5
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
6
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
7
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
6
8
  <uses-permission android:name="android.permission.USB_PERMISSION" />
7
9
  </manifest>
@@ -13,8 +13,6 @@ import android.content.IntentFilter;
13
13
  import android.content.pm.PackageManager;
14
14
  import android.hardware.usb.UsbDevice;
15
15
  import android.hardware.usb.UsbManager;
16
- import android.os.Handler;
17
- import android.os.Looper;
18
16
  import android.util.Log;
19
17
  import androidx.core.app.ActivityCompat;
20
18
  import androidx.core.content.ContextCompat;
@@ -67,11 +65,7 @@ public class ThermalPrinterPlugin extends Plugin {
67
65
  public static final int PERMISSION_BLUETOOTH_SCAN = 4;
68
66
  private static final String ACTION_USB_PERMISSION = "com.ruhiverse.thermalprinter.USB_PERMISSION";
69
67
 
70
- // Bluetooth discovery
71
- private Set<BluetoothDevice> discoveredDevices = new HashSet<>();
72
- private PluginCall discoveryCall;
73
- private BroadcastReceiver discoveryReceiver;
74
- private Handler discoveryHandler = new Handler(Looper.getMainLooper());
68
+
75
69
 
76
70
  private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
77
71
  public void onReceive(Context context, Intent intent) {
@@ -276,164 +270,92 @@ public class ThermalPrinterPlugin extends Plugin {
276
270
  () -> {
277
271
  try {
278
272
  JSArray printers = new JSArray();
279
- discoveredDevices.clear();
280
- discoveryCall = call;
273
+ Set<String> addedAddresses = new HashSet<>();
274
+
275
+ // Method 1: Get ALL paired Bluetooth devices directly from BluetoothAdapter
276
+ // This is more reliable than BluetoothPrintersConnections which filters by device class
277
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
281
278
 
282
- // First, get paired printers
283
- BluetoothConnection[] bluetoothPrinters = (new BluetoothPrintersConnections()).getList();
284
- Set<String> pairedAddresses = new HashSet<>();
279
+ if (bluetoothAdapter == null) {
280
+ Log.e(TAG, "Bluetooth not supported on this device");
281
+ JSObject ret = new JSObject();
282
+ ret.put("printers", printers);
283
+ call.resolve(ret);
284
+ return;
285
+ }
285
286
 
286
- if (bluetoothPrinters != null && bluetoothPrinters.length > 0) {
287
- for (BluetoothConnection printer : bluetoothPrinters) {
287
+ if (!bluetoothAdapter.isEnabled()) {
288
+ Log.e(TAG, "Bluetooth is not enabled");
289
+ call.reject("Bluetooth is not enabled. Please turn on Bluetooth.");
290
+ return;
291
+ }
292
+
293
+ Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
294
+ Log.d(TAG, "Found " + (bondedDevices != null ? bondedDevices.size() : 0) + " paired Bluetooth devices");
295
+
296
+ if (bondedDevices != null) {
297
+ for (BluetoothDevice device : bondedDevices) {
288
298
  try {
289
- JSObject printerObj = new JSObject();
290
- String name = printer.getDevice().getName();
291
- String address = printer.getDevice().getAddress();
299
+ String name = device.getName();
300
+ String address = device.getAddress();
292
301
 
293
- // Handle null/empty names
294
302
  if (name == null || name.isEmpty()) {
295
303
  name = "Unknown Device";
296
304
  }
305
+ if (address == null) {
306
+ continue;
307
+ }
297
308
 
309
+ JSObject printerObj = new JSObject();
298
310
  printerObj.put("name", name);
299
- printerObj.put("address", address != null ? address : "");
311
+ printerObj.put("address", address);
300
312
  printers.put(printerObj);
301
- pairedAddresses.add(address);
313
+ addedAddresses.add(address);
302
314
 
303
- Log.d(TAG, "Found paired Bluetooth printer: " + name + " (" + address + ")");
315
+ Log.d(TAG, "Paired device: " + name + " (" + address + ")");
304
316
  } catch (Exception e) {
305
- Log.e(TAG, "Error processing printer: " + e.getMessage());
317
+ Log.e(TAG, "Error processing paired device: " + e.getMessage());
306
318
  }
307
319
  }
308
- } else {
309
- Log.d(TAG, "No paired Bluetooth printers found. Starting discovery...");
310
320
  }
311
321
 
312
- // Start Bluetooth discovery for unpaired devices
313
- startBluetoothDiscovery(printers, pairedAddresses);
314
- } catch (Exception e) {
315
- Log.e(TAG, "Error listing Bluetooth printers: " + e.getMessage(), e);
316
- call.reject("Error listing Bluetooth printers: " + e.getMessage());
317
- }
318
- }
319
- );
320
- }
321
-
322
- private void startBluetoothDiscovery(JSArray printers, java.util.Set<String> pairedAddresses) {
323
- BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
324
-
325
- if (bluetoothAdapter == null) {
326
- Log.d(TAG, "Bluetooth not supported on this device");
327
- JSObject ret = new JSObject();
328
- ret.put("printers", printers);
329
- if (discoveryCall != null) {
330
- discoveryCall.resolve(ret);
331
- discoveryCall = null;
332
- }
333
- return;
334
- }
335
-
336
- if (!bluetoothAdapter.isEnabled()) {
337
- Log.d(TAG, "Bluetooth is not enabled");
338
- JSObject ret = new JSObject();
339
- ret.put("printers", printers);
340
- if (discoveryCall != null) {
341
- discoveryCall.resolve(ret);
342
- discoveryCall = null;
343
- }
344
- return;
345
- }
346
-
347
- // Register receiver for discovered devices
348
- discoveryReceiver = new BroadcastReceiver() {
349
- @Override
350
- public void onReceive(Context context, Intent intent) {
351
- String action = intent.getAction();
352
- if (BluetoothDevice.ACTION_FOUND.equals(action)) {
353
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
354
- if (device != null && !pairedAddresses.contains(device.getAddress())) {
355
- discoveredDevices.add(device);
356
- String name = device.getName();
357
- if (name == null || name.isEmpty()) {
358
- name = "Unknown Device";
359
- }
360
- Log.d(TAG, "Discovered Bluetooth device: " + name + " (" + device.getAddress() + ")");
361
- }
362
- } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
363
- // Discovery finished, add discovered devices to results
364
- JSArray finalPrinters = printers;
365
- for (BluetoothDevice device : discoveredDevices) {
366
- try {
367
- JSObject printerObj = new JSObject();
368
- String name = device.getName();
369
- if (name == null || name.isEmpty()) {
370
- name = "Unknown Device";
322
+ // Method 2: Also try the ESCPOS library (may find additional devices)
323
+ try {
324
+ BluetoothConnection[] escposPrinters = (new BluetoothPrintersConnections()).getList();
325
+ if (escposPrinters != null) {
326
+ for (BluetoothConnection printer : escposPrinters) {
327
+ String address = printer.getDevice().getAddress();
328
+ if (address != null && !addedAddresses.contains(address)) {
329
+ String name = printer.getDevice().getName();
330
+ if (name == null || name.isEmpty()) {
331
+ name = "Unknown Device";
332
+ }
333
+
334
+ JSObject printerObj = new JSObject();
335
+ printerObj.put("name", name);
336
+ printerObj.put("address", address);
337
+ printers.put(printerObj);
338
+ addedAddresses.add(address);
339
+
340
+ Log.d(TAG, "ESCPOS printer: " + name + " (" + address + ")");
341
+ }
371
342
  }
372
- printerObj.put("name", name);
373
- printerObj.put("address", device.getAddress());
374
- finalPrinters.put(printerObj);
375
- Log.d(TAG, "Added discovered device: " + name + " (" + device.getAddress() + ")");
376
- } catch (Exception e) {
377
- Log.e(TAG, "Error adding discovered device: " + e.getMessage());
378
343
  }
379
- }
380
-
381
- // Unregister receiver
382
- try {
383
- getContext().unregisterReceiver(discoveryReceiver);
384
344
  } catch (Exception e) {
385
- Log.e(TAG, "Error unregistering receiver: " + e.getMessage());
345
+ Log.d(TAG, "ESCPOS library scan: " + e.getMessage());
386
346
  }
387
347
 
388
- // Return results
348
+ Log.d(TAG, "Total printers found: " + printers.length());
349
+
389
350
  JSObject ret = new JSObject();
390
- ret.put("printers", finalPrinters);
391
- if (discoveryCall != null) {
392
- discoveryCall.resolve(ret);
393
- discoveryCall = null;
394
- }
395
- discoveryReceiver = null;
351
+ ret.put("printers", printers);
352
+ call.resolve(ret);
353
+ } catch (Exception e) {
354
+ Log.e(TAG, "Error listing Bluetooth printers: " + e.getMessage(), e);
355
+ call.reject("Error listing Bluetooth printers: " + e.getMessage());
396
356
  }
397
357
  }
398
- };
399
-
400
- // Register receiver
401
- IntentFilter filter = new IntentFilter();
402
- filter.addAction(BluetoothDevice.ACTION_FOUND);
403
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
404
- getContext().registerReceiver(discoveryReceiver, filter);
405
-
406
- // Start discovery
407
- if (bluetoothAdapter.isDiscovering()) {
408
- bluetoothAdapter.cancelDiscovery();
409
- }
410
-
411
- Log.d(TAG, "Starting Bluetooth discovery...");
412
- boolean started = bluetoothAdapter.startDiscovery();
413
-
414
- if (!started) {
415
- Log.d(TAG, "Failed to start Bluetooth discovery");
416
- try {
417
- getContext().unregisterReceiver(discoveryReceiver);
418
- } catch (Exception e) {
419
- Log.e(TAG, "Error unregistering receiver: " + e.getMessage());
420
- }
421
- JSObject ret = new JSObject();
422
- ret.put("printers", printers);
423
- if (discoveryCall != null) {
424
- discoveryCall.resolve(ret);
425
- discoveryCall = null;
426
- }
427
- discoveryReceiver = null;
428
- } else {
429
- // Stop discovery after 8 seconds
430
- discoveryHandler.postDelayed(() -> {
431
- if (bluetoothAdapter.isDiscovering()) {
432
- bluetoothAdapter.cancelDiscovery();
433
- Log.d(TAG, "Discovery timeout, stopping scan");
434
- }
435
- }, 8000);
436
- }
358
+ );
437
359
  }
438
360
 
439
361
  @PluginMethod
package/dist/docs.json CHANGED
@@ -53,9 +53,9 @@
53
53
  },
54
54
  {
55
55
  "name": "listBluetoothPrinters",
56
- "signature": "() => Promise<{ name: string; address: string; }[]>",
56
+ "signature": "() => Promise<{ printers: { name: string; address: string; }[]; }>",
57
57
  "parameters": [],
58
- "returns": "Promise<{ name: string; address: string; }[]>",
58
+ "returns": "Promise<{ printers: { name: string; address: string; }[]; }>",
59
59
  "tags": [],
60
60
  "docs": "Get list of paired Bluetooth printers",
61
61
  "complexTypes": [],
@@ -63,9 +63,9 @@
63
63
  },
64
64
  {
65
65
  "name": "listUsbPrinters",
66
- "signature": "() => Promise<{ name: string; address: string; }[]>",
66
+ "signature": "() => Promise<{ printers: { name: string; address: string; }[]; }>",
67
67
  "parameters": [],
68
- "returns": "Promise<{ name: string; address: string; }[]>",
68
+ "returns": "Promise<{ printers: { name: string; address: string; }[]; }>",
69
69
  "tags": [],
70
70
  "docs": "Get list of connected USB printers",
71
71
  "complexTypes": [],
@@ -13,16 +13,20 @@ export interface ThermalPrinterPlugin {
13
13
  * Get list of paired Bluetooth printers
14
14
  */
15
15
  listBluetoothPrinters(): Promise<{
16
- name: string;
17
- address: string;
18
- }[]>;
16
+ printers: {
17
+ name: string;
18
+ address: string;
19
+ }[];
20
+ }>;
19
21
  /**
20
22
  * Get list of connected USB printers
21
23
  */
22
24
  listUsbPrinters(): Promise<{
23
- name: string;
24
- address: string;
25
- }[]>;
25
+ printers: {
26
+ name: string;
27
+ address: string;
28
+ }[];
29
+ }>;
26
30
  }
27
31
  export interface PrintObject {
28
32
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface ThermalPrinterPlugin {\n /**\n * Print text using USB connection\n * @param printObject Object containing text to print\n */\n printByUsb(printObject: PrintObject): Promise<void>;\n\n /**\n * Print text using Bluetooth connection\n * @param printObject Object containing text to print\n */\n printByBluetooth(printObject: PrintObject): Promise<void>;\n\n /**\n * Get list of paired Bluetooth printers\n */\n listBluetoothPrinters(): Promise<{ name: string; address: string }[]>;\n\n /**\n * Get list of connected USB printers\n */\n listUsbPrinters(): Promise<{ name: string; address: string }[]>;\n}\n\nexport interface PrintObject {\n /**\n * Text to print. Supports ESC/POS formatting commands.\n * Example: \"[C]<b>Hello World</b>\\n[C]This is a test\"\n */\n textToPrint: string;\n\n /**\n * Optional Bluetooth MAC address of the printer to use.\n * Only applicable for printByBluetooth.\n */\n address?: string;\n\n /**\n * Optional USB device name of the printer to use.\n * Only applicable for printByUsb.\n */\n name?: string;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface ThermalPrinterPlugin {\n /**\n * Print text using USB connection\n * @param printObject Object containing text to print\n */\n printByUsb(printObject: PrintObject): Promise<void>;\n\n /**\n * Print text using Bluetooth connection\n * @param printObject Object containing text to print\n */\n printByBluetooth(printObject: PrintObject): Promise<void>;\n\n /**\n * Get list of paired Bluetooth printers\n */\n listBluetoothPrinters(): Promise<{ printers: { name: string; address: string }[] }>;\n\n /**\n * Get list of connected USB printers\n */\n listUsbPrinters(): Promise<{ printers: { name: string; address: string }[] }>;\n}\n\nexport interface PrintObject {\n /**\n * Text to print. Supports ESC/POS formatting commands.\n * Example: \"[C]<b>Hello World</b>\\n[C]This is a test\"\n */\n textToPrint: string;\n\n /**\n * Optional Bluetooth MAC address of the printer to use.\n * Only applicable for printByBluetooth.\n */\n address?: string;\n\n /**\n * Optional USB device name of the printer to use.\n * Only applicable for printByUsb.\n */\n name?: string;\n}\n"]}
@@ -286,26 +286,32 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
286
286
  print("ThermalPrinter: listBluetoothPrinters called")
287
287
  var printers: [[String: String]] = []
288
288
 
289
- // First, get MFi/ExternalAccessory printers
289
+ // Method 1: Get MFi/ExternalAccessory printers (classic Bluetooth printers)
290
+ // Most thermal printers use classic Bluetooth, which on iOS requires
291
+ // ExternalAccessory framework + MFi certification
290
292
  let manager = EAAccessoryManager.shared()
291
- let accessories = manager.connectedAccessories
293
+ let allAccessories = manager.connectedAccessories
292
294
 
293
- print("ThermalPrinter: Found \(accessories.count) ExternalAccessory devices")
295
+ print("ThermalPrinter: Found \(allAccessories.count) total ExternalAccessory devices")
294
296
 
295
- let mfiPrinters: [[String: String]] = accessories.compactMap { accessory in
296
- guard accessory.protocolStrings.contains(where: { supportedPrinterProtocols.contains($0) }) else {
297
- return nil
298
- }
299
- print("ThermalPrinter: Found MFi printer: \(accessory.name)")
300
- return [
301
- "name": accessory.name,
302
- "address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
303
- ]
297
+ // Return ALL connected accessories (not just those matching printer protocols)
298
+ // because many printers use custom protocol strings
299
+ for accessory in allAccessories {
300
+ let name = accessory.name
301
+ let address = accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber
302
+ let protocols = accessory.protocolStrings.joined(separator: ", ")
303
+
304
+ print("ThermalPrinter: Accessory: \(name), protocols: [\(protocols)]")
305
+
306
+ printers.append([
307
+ "name": name,
308
+ "address": address,
309
+ ])
304
310
  }
305
- printers.append(contentsOf: mfiPrinters)
306
- print("ThermalPrinter: Found \(mfiPrinters.count) MFi printers")
307
311
 
308
- // Then scan for BLE printers
312
+ print("ThermalPrinter: Found \(printers.count) ExternalAccessory printers")
313
+
314
+ // Method 2: Also scan for BLE printers (some thermal printers support BLE)
309
315
  discoveredPeripherals.removeAll()
310
316
  listPrintersCall = call
311
317
 
@@ -323,69 +329,59 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
323
329
 
324
330
  print("ThermalPrinter: Bluetooth state: \(centralManager.state.rawValue)")
325
331
 
326
- // Check Bluetooth state
327
332
  switch centralManager.state {
328
333
  case .poweredOn:
329
- // Start scanning immediately
330
- print("ThermalPrinter: Bluetooth powered on, starting scan")
334
+ print("ThermalPrinter: Bluetooth powered on, starting BLE scan")
331
335
  startBLEScan(for: call, existingPrinters: printers)
332
336
  case .poweredOff:
333
- print("ThermalPrinter: Bluetooth powered off, returning \(printers.count) printers")
337
+ print("ThermalPrinter: Bluetooth powered off")
338
+ // Still return any MFi printers we found
334
339
  call.resolve(["printers": printers])
340
+ listPrintersCall = nil
335
341
  case .unauthorized:
336
342
  print("ThermalPrinter: Bluetooth unauthorized")
337
- call.reject("Bluetooth access denied. Please enable Bluetooth permissions in Settings.")
338
- case .unsupported:
339
- print("ThermalPrinter: Bluetooth unsupported")
340
- call.reject("Bluetooth is not supported on this device.")
343
+ // Still return any MFi printers, but warn about BLE
344
+ call.resolve(["printers": printers])
345
+ listPrintersCall = nil
341
346
  case .resetting, .unknown:
342
347
  print("ThermalPrinter: Bluetooth state unknown/resetting, waiting for update")
343
- // Wait for state update
344
- // The state will be updated via centralManagerDidUpdateState
345
348
  break
346
- @unknown default:
347
- print("ThermalPrinter: Unknown Bluetooth state, returning \(printers.count) printers")
349
+ default:
348
350
  call.resolve(["printers": printers])
351
+ listPrintersCall = nil
349
352
  }
350
353
  }
351
354
 
352
355
  private func startBLEScan(for call: CAPPluginCall, existingPrinters: [[String: String]]) {
353
- guard let centralManager = centralManager else {
354
- call.resolve(["printers": existingPrinters])
355
- return
356
- }
357
-
358
- guard centralManager.state == .poweredOn else {
356
+ guard let centralManager = centralManager, centralManager.state == .poweredOn else {
359
357
  call.resolve(["printers": existingPrinters])
358
+ listPrintersCall = nil
360
359
  return
361
360
  }
362
361
 
363
- // Clear previous discoveries
364
362
  discoveredPeripherals.removeAll()
365
363
 
366
- // Scan for all peripherals
367
364
  print("ThermalPrinter: Starting BLE scan...")
368
365
  centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
369
366
 
370
- // Stop scanning after 8 seconds (increased from 5 to give more time)
371
- DispatchQueue.main.asyncAfter(deadline: .now() + 8.0) {
367
+ // Scan for 4 seconds (shorter BLE devices respond quickly)
368
+ DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
372
369
  centralManager.stopScan()
373
370
  print("ThermalPrinter: Stopped BLE scan. Found \(self.discoveredPeripherals.count) peripherals")
374
371
 
375
372
  var printers = existingPrinters
376
373
 
377
- // Add discovered BLE printers - return all devices, even without names
378
374
  let blePrinters: [[String: String]] = self.discoveredPeripherals.compactMap { peripheral in
379
- // Include all peripherals, even if they don't have names
380
- // Some printers don't advertise names until connected
381
- let name = peripheral.name ?? "Unknown Device"
375
+ guard let name = peripheral.name, !name.isEmpty else {
376
+ return nil
377
+ }
382
378
  return [
383
379
  "name": name,
384
380
  "address": peripheral.identifier.uuidString,
385
381
  ]
386
382
  }
387
383
 
388
- print("ThermalPrinter: Returning \(blePrinters.count) BLE printers")
384
+ print("ThermalPrinter: Returning \(blePrinters.count) BLE printers + \(existingPrinters.count) MFi printers")
389
385
  printers.append(contentsOf: blePrinters)
390
386
  call.resolve(["printers": printers])
391
387
  self.listPrintersCall = nil
@@ -399,36 +395,25 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
399
395
 
400
396
  // If we're waiting to list printers, start scanning now
401
397
  if let call = listPrintersCall {
398
+ // Get MFi printers
399
+ var mfiPrinters: [[String: String]] = []
400
+ let manager = EAAccessoryManager.shared()
401
+ for accessory in manager.connectedAccessories {
402
+ mfiPrinters.append([
403
+ "name": accessory.name,
404
+ "address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
405
+ ])
406
+ }
407
+
402
408
  switch central.state {
403
409
  case .poweredOn:
404
- print("ThermalPrinter: Bluetooth now powered on, starting scan")
405
- // Get existing MFi printers first
406
- let manager = EAAccessoryManager.shared()
407
- let accessories = manager.connectedAccessories
408
- let mfiPrinters: [[String: String]] = accessories.compactMap { accessory in
409
- guard accessory.protocolStrings.contains(where: { supportedPrinterProtocols.contains($0) }) else {
410
- return nil
411
- }
412
- return [
413
- "name": accessory.name,
414
- "address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
415
- ]
416
- }
410
+ print("ThermalPrinter: Bluetooth now powered on, starting BLE scan")
417
411
  startBLEScan(for: call, existingPrinters: mfiPrinters)
418
- case .poweredOff:
419
- print("ThermalPrinter: Bluetooth powered off")
420
- call.resolve(["printers": []])
421
- listPrintersCall = nil
422
- case .unauthorized:
423
- print("ThermalPrinter: Bluetooth unauthorized")
424
- call.reject("Bluetooth access denied. Please enable Bluetooth permissions in Settings.")
425
- listPrintersCall = nil
426
- case .unsupported:
427
- print("ThermalPrinter: Bluetooth unsupported")
428
- call.reject("Bluetooth is not supported on this device.")
412
+ case .poweredOff, .unauthorized, .unsupported:
413
+ print("ThermalPrinter: Bluetooth not available (state: \(central.state.rawValue))")
414
+ call.resolve(["printers": mfiPrinters])
429
415
  listPrintersCall = nil
430
416
  default:
431
- print("ThermalPrinter: Bluetooth state: \(central.state.rawValue)")
432
417
  break
433
418
  }
434
419
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruhiverse/thermal-printer-plugin",
3
- "version": "1.0.9",
3
+ "version": "1.0.13",
4
4
  "description": "Capacitor plugin for thermal printing via USB and Bluetooth",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",