@fedejm/capacitor-esc-pos-printer 0.1.0 → 0.2.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/README.md CHANGED
@@ -2,19 +2,138 @@
2
2
 
3
3
  CapacitorJS wrapper for ESC POS (native) printers.
4
4
 
5
+ ## Features
6
+
7
+ - **Bluetooth printing** - Connect to Bluetooth ESC/POS printers (Android, iOS)
8
+ - **USB printing** - Connect to USB ESC/POS printers via USB Host API (Android only)
9
+ - **Raw data sending** - Send raw ESC/POS commands for full control
10
+ - **Bi-directional communication** - Read responses from printer
11
+ - **Permission management** - Built-in USB permission request flow
12
+
5
13
  ## Install
6
14
 
7
15
  ```bash
8
- npm install capacitor-esc-pos-printer
16
+ npm install @fedejm/capacitor-esc-pos-printer
9
17
  npx cap sync
10
18
  ```
11
19
 
20
+ ## Platform Support
21
+
22
+ | Feature | Android | iOS | Web |
23
+ |------------|---------|---------|---------|
24
+ | Bluetooth | Yes | Yes | Limited |
25
+ | USB | Yes | No | No |
26
+ | Network | Planned | Planned | Planned |
27
+
28
+ ### Android Requirements
29
+
30
+ - **Bluetooth**: Requires `BLUETOOTH_CONNECT` and `BLUETOOTH_SCAN` permissions (Android 12+)
31
+ - **USB**: Requires `android.hardware.usb.host` feature (automatically declared as optional)
32
+
33
+ ### iOS Requirements
34
+
35
+ - Bluetooth printing is supported via CoreBluetooth framework
36
+ - USB printing is not available on iOS
37
+
38
+ ## Usage
39
+
40
+ ### Bluetooth Printer
41
+
42
+ ```typescript
43
+ import { EscPosPrinter, BluetoothPrinter } from '@fedejm/capacitor-esc-pos-printer';
44
+
45
+ // Request Bluetooth enable (Android)
46
+ await EscPosPrinter.requestBluetoothEnable();
47
+
48
+ // Get paired Bluetooth printers
49
+ const { devices } = await EscPosPrinter.getBluetoothPrinterDevices();
50
+ console.log('Found printers:', devices);
51
+
52
+ // Create and connect to a printer
53
+ const printer = new BluetoothPrinter(devices[0].address);
54
+ await printer.link();
55
+ await printer.connect();
56
+
57
+ // Send ESC/POS commands
58
+ await printer.send([0x1B, 0x40]); // Initialize printer
59
+ await printer.send([0x48, 0x65, 0x6C, 0x6C, 0x6F]); // "Hello"
60
+ await printer.send([0x0A]); // Line feed
61
+
62
+ // Disconnect
63
+ await printer.disconnect();
64
+ await printer.dispose();
65
+ ```
66
+
67
+ ### USB Printer (Android only)
68
+
69
+ ```typescript
70
+ import { EscPosPrinter, UsbPrinter } from '@fedejm/capacitor-esc-pos-printer';
71
+
72
+ // Get connected USB printers
73
+ const { devices } = await EscPosPrinter.getUsbPrinterDevices();
74
+ console.log('Found USB printers:', devices);
75
+
76
+ // Find a device and check permission status
77
+ const device = devices[0];
78
+ if (!device.hasPermission) {
79
+ // Request USB permission
80
+ const { value: granted } = await EscPosPrinter.requestUsbPermission({
81
+ address: device.id
82
+ });
83
+
84
+ if (!granted) {
85
+ console.log('USB permission denied for:', device.name);
86
+ return;
87
+ }
88
+ }
89
+
90
+ // Create and connect to a USB printer
91
+ const printer = new UsbPrinter(device.id);
92
+ await printer.link();
93
+ await printer.connect();
94
+
95
+ // Send ESC/POS commands (same as Bluetooth)
96
+ await printer.send([0x1B, 0x40]); // Initialize printer
97
+ await printer.send([0x48, 0x65, 0x6C, 0x6C, 0x6F]); // "Hello"
98
+ await printer.send([0x0A]); // Line feed
99
+
100
+ // Disconnect
101
+ await printer.disconnect();
102
+ await printer.dispose();
103
+ ```
104
+
105
+ ## Connection Lifecycle
106
+
107
+ The printer connection follows this lifecycle:
108
+
109
+ ```
110
+ ┌─────────┐ link() ┌────────┐ connect() ┌───────────┐
111
+ │ Created ├───────────────►│ Linked ├─────────────────►│ Connected │
112
+ └─────────┘ └────────┘ └───────────┘
113
+
114
+ disconnect()
115
+
116
+
117
+ ┌─────────┐ dispose() ┌────────────┐
118
+ │Disposed │◄──────────────┤Disconnected│
119
+ └─────────┘ └────────────┘
120
+ ```
121
+
122
+ 1. **Create** - Instantiate `BluetoothPrinter` or `UsbPrinter` with device address
123
+ 2. **Link** - Register with native plugin (`link()`)
124
+ 3. **Connect** - Open connection to physical device (`connect()`)
125
+ 4. **Send/Read** - Perform I/O operations (`send()`, `read()`)
126
+ 5. **Disconnect** - Close connection (`disconnect()`)
127
+ 6. **Dispose** - Unregister from plugin (`dispose()`)
128
+
12
129
  ## API
13
130
 
14
131
  <docgen-index>
15
132
 
16
133
  * [`requestBluetoothEnable()`](#requestbluetoothenable)
17
134
  * [`getBluetoothPrinterDevices()`](#getbluetoothprinterdevices)
135
+ * [`getUsbPrinterDevices()`](#getusbprinterdevices)
136
+ * [`requestUsbPermission(...)`](#requestusbpermission)
18
137
  * [`createPrinter(...)`](#createprinter)
19
138
  * [`disposePrinter(...)`](#disposeprinter)
20
139
  * [`isPrinterConnected(...)`](#isprinterconnected)
@@ -52,6 +171,38 @@ getBluetoothPrinterDevices() => Promise<BluetoothDevicesResult>
52
171
  --------------------
53
172
 
54
173
 
174
+ ### getUsbPrinterDevices()
175
+
176
+ ```typescript
177
+ getUsbPrinterDevices() => Promise<UsbDevicesResult>
178
+ ```
179
+
180
+ Discovers USB devices that could be ESC/POS printers.
181
+ Returns devices with bulk OUT endpoints that may be suitable for printing.
182
+
183
+ **Returns:** <code>Promise&lt;<a href="#usbdevicesresult">UsbDevicesResult</a>&gt;</code>
184
+
185
+ --------------------
186
+
187
+
188
+ ### requestUsbPermission(...)
189
+
190
+ ```typescript
191
+ requestUsbPermission(options: WithAddress) => Promise<ValueResult<boolean>>
192
+ ```
193
+
194
+ Requests USB permission for a specific device.
195
+ Note: May require user interaction via system UI.
196
+
197
+ | Param | Type |
198
+ | ------------- | --------------------------------------------------- |
199
+ | **`options`** | <code><a href="#withaddress">WithAddress</a></code> |
200
+
201
+ **Returns:** <code>Promise&lt;<a href="#valueresult">ValueResult</a>&lt;boolean&gt;&gt;</code>
202
+
203
+ --------------------
204
+
205
+
55
206
  ### createPrinter(...)
56
207
 
57
208
  ```typescript
@@ -168,12 +319,46 @@ readFromPrinter(options: WithHashKey) => Promise<ValueResult<number[]>>
168
319
  | **`devices`** | <code>{ address: string; alias?: string; name: string; bondState: number; type: number; uuids: string[]; }[]</code> |
169
320
 
170
321
 
322
+ #### UsbDevicesResult
323
+
324
+ Result from USB device discovery.
325
+ Contains list of USB devices that could be ESC/POS printers.
326
+
327
+ | Prop | Type |
328
+ | ------------- | ---------------------------- |
329
+ | **`devices`** | <code>UsbDeviceInfo[]</code> |
330
+
331
+
332
+ #### UsbDeviceInfo
333
+
334
+ Information about a discovered USB device.
335
+
336
+ | Prop | Type | Description |
337
+ | ---------------------- | -------------------- | -------------------------------------------------------------------------- |
338
+ | **`id`** | <code>string</code> | Stable identifier for the device (format: "vendorId:productId:deviceName") |
339
+ | **`name`** | <code>string</code> | Human-readable name (product name or fallback) |
340
+ | **`vendorId`** | <code>number</code> | USB Vendor ID |
341
+ | **`productId`** | <code>number</code> | USB Product ID |
342
+ | **`deviceClass`** | <code>number</code> | USB Device Class |
343
+ | **`deviceSubclass`** | <code>number</code> | USB Device Subclass |
344
+ | **`deviceName`** | <code>string</code> | System device name/path |
345
+ | **`manufacturerName`** | <code>string</code> | Manufacturer name if available |
346
+ | **`hasPermission`** | <code>boolean</code> | Whether the app has USB permission for this device |
347
+
348
+
349
+ #### WithAddress
350
+
351
+ | Prop | Type |
352
+ | ------------- | ------------------- |
353
+ | **`address`** | <code>string</code> |
354
+
355
+
171
356
  #### CreatePrinterOptions
172
357
 
173
- | Prop | Type |
174
- | -------------------- | ----------------------------------------------------------------------- |
175
- | **`connectionType`** | <code><a href="#printerconnectiontype">PrinterConnectionType</a></code> |
176
- | **`address`** | <code>string</code> |
358
+ | Prop | Type | Description |
359
+ | -------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
360
+ | **`connectionType`** | <code><a href="#printerconnectiontype">PrinterConnectionType</a></code> | |
361
+ | **`address`** | <code>string</code> | Address/identifier for the printer: - Bluetooth: MAC address (e.g., "00:11:22:33:44:55") - USB: Device identifier (e.g., "1234:5678:002") - Network: IP address and optional port (e.g., "192.168.1.100:9100") |
177
362
 
178
363
 
179
364
  #### WithHashKey
@@ -199,5 +384,170 @@ readFromPrinter(options: WithHashKey) => Promise<ValueResult<number[]>>
199
384
  | Members | Value |
200
385
  | --------------- | ------------------------ |
201
386
  | **`Bluetooth`** | <code>'bluetooth'</code> |
387
+ | **`Usb`** | <code>'usb'</code> |
388
+ | **`Network`** | <code>'network'</code> |
202
389
 
203
390
  </docgen-api>
391
+
392
+ ## Error Handling
393
+
394
+ All printer operations may throw a `PrinterError` with a specific error code:
395
+
396
+ ```typescript
397
+ import { PrinterError, PrinterErrorCode } from '@fedejm/capacitor-esc-pos-printer';
398
+
399
+ try {
400
+ await printer.connect();
401
+ } catch (error) {
402
+ if (error instanceof PrinterError) {
403
+ switch (error.code) {
404
+ case PrinterErrorCode.Connect:
405
+ console.log('Connection failed');
406
+ break;
407
+ case PrinterErrorCode.NotConnected:
408
+ console.log('Printer not connected');
409
+ break;
410
+ case PrinterErrorCode.Send:
411
+ console.log('Failed to send data');
412
+ break;
413
+ case PrinterErrorCode.Read:
414
+ console.log('Failed to read data');
415
+ break;
416
+ case PrinterErrorCode.Permissions:
417
+ console.log('Permission denied');
418
+ break;
419
+ case PrinterErrorCode.DeviceNotFound:
420
+ console.log('Device not found');
421
+ break;
422
+ }
423
+ }
424
+ }
425
+ ```
426
+
427
+ ### Error Code Reference
428
+
429
+ | Code | Name | Value | Description |
430
+ |------|----------------|-------|---------------------------------------------------------------|
431
+ | 1 | Connect | 1 | Failed to establish connection to the printer |
432
+ | 2 | NotConnected | 2 | Attempted operation on disconnected printer |
433
+ | 3 | Send | 3 | Failed to send data to the printer |
434
+ | 4 | Read | 4 | Failed to read data from the printer |
435
+ | 5 | Permissions | 5 | Required permission not granted (Bluetooth or USB) |
436
+ | 6 | DeviceNotFound | 6 | Device not found or no longer available |
437
+
438
+ ## USB Permissions (Android)
439
+
440
+ USB devices require explicit permission from the user. The plugin provides a complete permission flow:
441
+
442
+ ### Checking Permission
443
+
444
+ ```typescript
445
+ const { devices } = await EscPosPrinter.getUsbPrinterDevices();
446
+ const device = devices.find(d => d.vendorId === 0x0483); // Find by vendor ID
447
+
448
+ if (device && !device.hasPermission) {
449
+ console.log('Permission required for:', device.name);
450
+ }
451
+ ```
452
+
453
+ ### Requesting Permission
454
+
455
+ ```typescript
456
+ // Request permission - triggers system dialog
457
+ const { value: granted } = await EscPosPrinter.requestUsbPermission({
458
+ address: device.id
459
+ });
460
+
461
+ if (granted) {
462
+ console.log('Permission granted!');
463
+ // Refresh device list to update permission status
464
+ const { devices: updated } = await EscPosPrinter.getUsbPrinterDevices();
465
+ } else {
466
+ console.log('Permission denied by user');
467
+ }
468
+ ```
469
+
470
+ ### Permission Flow Diagram
471
+
472
+ ```
473
+ ┌─────────────────────┐
474
+ │ getUsbPrinterDevices│
475
+ └──────────┬──────────┘
476
+
477
+
478
+ ┌──────────────┐
479
+ │hasPermission?│
480
+ └──────┬───────┘
481
+
482
+ ┌─────┴─────┐
483
+ │ │
484
+ Yes No
485
+ │ │
486
+ ▼ ▼
487
+ ┌─────────┐ ┌────────────────────┐
488
+ │ Connect │ │requestUsbPermission│
489
+ └─────────┘ └─────────┬──────────┘
490
+
491
+
492
+ ┌──────────────────┐
493
+ │System Permission │
494
+ │ Dialog │
495
+ └────────┬─────────┘
496
+
497
+ ┌─────┴─────┐
498
+ │ │
499
+ Granted Denied
500
+ │ │
501
+ ▼ ▼
502
+ ┌─────────┐ ┌───────┐
503
+ │ Connect │ │ Error │
504
+ └─────────┘ └───────┘
505
+ ```
506
+
507
+ ### Notes on USB Permission
508
+
509
+ - Permission is granted per-device and persists until the app is uninstalled
510
+ - If the device is unplugged and replugged, permission may need to be re-requested
511
+ - The permission dialog is a system UI and cannot be customized
512
+ - Permission requests are idempotent - multiple calls for already-permitted devices return immediately
513
+
514
+ ## Reading from Printer
515
+
516
+ Some printers support bi-directional communication. The `read()` method is best-effort:
517
+
518
+ ```typescript
519
+ // Send status request command
520
+ await printer.send([0x10, 0x04, 0x01]); // DLE EOT 1 (transmit status)
521
+
522
+ // Read response
523
+ const response = await printer.read();
524
+ if (response.length > 0) {
525
+ console.log('Printer status:', response);
526
+ } else {
527
+ console.log('No response (printer may not support reading)');
528
+ }
529
+ ```
530
+
531
+ **Note**: Many USB ESC/POS printers do not implement a read endpoint. The `read()` method will return an empty array in such cases without throwing an error.
532
+
533
+ ## Future: Network Printing
534
+
535
+ Network/TCP printing is planned for a future release. The API design is already prepared:
536
+
537
+ ```typescript
538
+ // Future API (not yet implemented)
539
+ const printer = new NetworkPrinter('192.168.1.100:9100');
540
+ await printer.link();
541
+ await printer.connect();
542
+ // ... same send/read API
543
+ ```
544
+
545
+ The `PrinterConnectionType.Network` enum value is already defined for forward compatibility.
546
+
547
+ ## Contributing
548
+
549
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.
550
+
551
+ ## License
552
+
553
+ MIT
@@ -1,5 +1,6 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
2
  xmlns:tools="http://schemas.android.com/tools">
3
+ <!-- Bluetooth permissions -->
3
4
  <uses-permission android:name="android.permission.BLUETOOTH"
4
5
  android:maxSdkVersion="30" />
5
6
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
@@ -14,8 +15,13 @@
14
15
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
15
16
  tools:targetApi="s" />
16
17
 
18
+ <!-- Bluetooth features (not required) -->
17
19
  <uses-feature android:name="android.hardware.bluetooth"
18
20
  android:required="false" />
19
21
  <uses-feature android:name="android.hardware.bluetooth_le"
20
22
  android:required="false" />
23
+
24
+ <!-- USB Host feature (not required - allows app to work on devices without USB host) -->
25
+ <uses-feature android:name="android.hardware.usb.host"
26
+ android:required="false" />
21
27
  </manifest>