@sincpro/printer-expo 0.1.2 → 1.0.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 +589 -415
- package/android/.editorconfig +20 -0
- package/android/README.md +103 -0
- package/android/build.gradle +36 -34
- package/android/libs/pdf/Bixolon_pdf.aar +0 -0
- package/android/libs/{jniLibs/x86_64/libbxl_common.so → sincpro-printer-sdk.aar} +0 -0
- package/android/src/main/java/sincpro/expo/printer/entrypoint/PrinterModule.kt +387 -0
- package/build/SincproPrinter.d.ts +191 -0
- package/build/SincproPrinter.d.ts.map +1 -0
- package/build/SincproPrinter.js +120 -0
- package/build/SincproPrinter.js.map +1 -0
- package/build/index.d.ts +11 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +13 -3
- package/build/index.js.map +1 -1
- package/build/types/bluetooth.types.d.ts +19 -0
- package/build/types/bluetooth.types.d.ts.map +1 -0
- package/build/types/bluetooth.types.js +5 -0
- package/build/types/bluetooth.types.js.map +1 -0
- package/build/types/index.d.ts +7 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/index.js +7 -0
- package/build/types/index.js.map +1 -0
- package/build/types/printer.types.d.ts +118 -0
- package/build/types/printer.types.d.ts.map +1 -0
- package/build/types/printer.types.js +5 -0
- package/build/types/printer.types.js.map +1 -0
- package/build/types/receipt.types.d.ts +96 -0
- package/build/types/receipt.types.d.ts.map +1 -0
- package/build/types/receipt.types.js +5 -0
- package/build/types/receipt.types.js.map +1 -0
- package/expo-module.config.json +2 -5
- package/package.json +6 -5
- package/src/SincproPrinter.ts +208 -0
- package/src/index.ts +15 -3
- package/src/types/bluetooth.types.ts +20 -0
- package/src/types/index.ts +7 -0
- package/src/types/printer.types.ts +141 -0
- package/src/types/receipt.types.ts +115 -0
- package/android/libs/BixolonLabelPrinterLibrary_V2.0.9.jar +0 -0
- package/android/libs/jniLibs/arm64-v8a/libbxl_common.so +0 -0
- package/android/libs/jniLibs/armeabi-v7a/libbxl_common.so +0 -0
- package/android/libs/jniLibs/x86/libbxl_common.so +0 -0
- package/android/libs/libcommon_V1.4.0.jar +0 -0
- package/android/src/main/java/expo/sincpro/ExpoBixolonModule.kt +0 -271
- package/android/src/main/java/expo/sincpro/bixolon/BixolonQRPrinter.kt +0 -423
- package/android/src/main/java/expo/sincpro/managers/BluetoothManager.kt +0 -139
- package/android/src/main/java/expo/sincpro/managers/ConnectionManager.kt +0 -124
- package/android/src/main/java/expo/sincpro/managers/PermissionManager.kt +0 -122
- package/android/src/main/java/expo/sincpro/managers/PrinterManager.kt +0 -396
- package/android/src/main/jniLibs/arm64-v8a/libbxl_common.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libbxl_common.so +0 -0
- package/android/src/main/jniLibs/x86/libbxl_common.so +0 -0
- package/android/src/main/jniLibs/x86_64/libbxl_common.so +0 -0
- package/build/BixolonPrinter.d.ts +0 -4
- package/build/BixolonPrinter.d.ts.map +0 -1
- package/build/BixolonPrinter.js +0 -12
- package/build/BixolonPrinter.js.map +0 -1
- package/build/ExpoBixolon.types.d.ts +0 -45
- package/build/ExpoBixolon.types.d.ts.map +0 -1
- package/build/ExpoBixolon.types.js +0 -2
- package/build/ExpoBixolon.types.js.map +0 -1
- package/build/ExpoBixolonModule.d.ts +0 -24
- package/build/ExpoBixolonModule.d.ts.map +0 -1
- package/build/ExpoBixolonModule.js +0 -3
- package/build/ExpoBixolonModule.js.map +0 -1
- package/build/QrCodePrinter.d.ts +0 -45
- package/build/QrCodePrinter.d.ts.map +0 -1
- package/build/QrCodePrinter.js +0 -118
- package/build/QrCodePrinter.js.map +0 -1
- package/src/BixolonPrinter.ts +0 -16
- package/src/ExpoBixolon.types.ts +0 -60
- package/src/ExpoBixolonModule.ts +0 -38
- package/src/QrCodePrinter.ts +0 -201
package/README.md
CHANGED
|
@@ -1,101 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sincpro/printer-expo
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/js/@sincpro/printer-expo)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://expo.dev)
|
|
6
6
|
|
|
7
|
-
A powerful React Native module for controlling
|
|
7
|
+
A powerful React Native module for controlling thermal printers in Expo applications. Built with **Clean Architecture** and **Hexagonal Architecture** (Ports & Adapters) for maximum flexibility and maintainability.
|
|
8
8
|
|
|
9
9
|
## ✨ Features
|
|
10
10
|
|
|
11
|
-
- 🔗 **Bluetooth Connectivity**: Full
|
|
12
|
-
- 📱 **Permission Management**:
|
|
13
|
-
- 🖨️ **Advanced Printing**:
|
|
14
|
-
- 📋 **QR Code
|
|
15
|
-
-
|
|
16
|
-
- 📚 **Official SDK**: Integration with
|
|
17
|
-
- 📝 **TypeScript**:
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- 📱 **Cross-Platform**: Works on both Android and iOS (Android focus)
|
|
11
|
+
- 🔗 **Bluetooth Connectivity**: Full device discovery and connection management
|
|
12
|
+
- 📱 **Permission Management**: Smart handling of Android Bluetooth permissions
|
|
13
|
+
- 🖨️ **Advanced Printing**: Receipt printing with flexible line types
|
|
14
|
+
- 📋 **QR Code Support**: Print QR codes with customizable sizes
|
|
15
|
+
- 🏗️ **Clean Architecture**: SOLID principles, testable, swappable adapters
|
|
16
|
+
- 📚 **Official SDK**: Integration with Bixolon SDK (extensible to other brands)
|
|
17
|
+
- 📝 **TypeScript**: 100% type-safe API with comprehensive definitions
|
|
18
|
+
- 🛠️ **Easy Setup**: Simple installation and minimal configuration
|
|
19
|
+
- 📱 **Android Focus**: Optimized for Android thermal printers
|
|
21
20
|
|
|
22
21
|
## 📦 Installation
|
|
23
22
|
|
|
24
|
-
### Method 1: Install from NPM (when published)
|
|
25
|
-
|
|
26
23
|
```bash
|
|
27
|
-
npm install expo
|
|
24
|
+
npm install @sincpro/printer-expo
|
|
28
25
|
# or
|
|
29
|
-
yarn add expo
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Method 2: Install from Local Tarball
|
|
33
|
-
|
|
34
|
-
If you have the module locally or want to use a specific version:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
# First, create a tarball from the module directory
|
|
38
|
-
cd /path/to/expo-bixolon
|
|
39
|
-
npm pack
|
|
40
|
-
|
|
41
|
-
# Then install in your app
|
|
42
|
-
cd /path/to/your-app
|
|
43
|
-
npm install /path/to/expo-bixolon-0.1.0.tgz
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Method 3: Install as Local Development Dependency
|
|
47
|
-
|
|
48
|
-
For development or testing purposes:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# In your app directory
|
|
52
|
-
npm install /absolute/path/to/expo-bixolon
|
|
26
|
+
yarn add @sincpro/printer-expo
|
|
53
27
|
```
|
|
54
28
|
|
|
55
|
-
### Post-Installation
|
|
56
|
-
|
|
57
|
-
After installation, run these commands in your app directory:
|
|
29
|
+
### Post-Installation
|
|
58
30
|
|
|
59
31
|
```bash
|
|
60
|
-
#
|
|
61
|
-
npx expo
|
|
62
|
-
|
|
63
|
-
# Generate native code
|
|
64
|
-
npx expo prebuild
|
|
32
|
+
# Rebuild native modules
|
|
33
|
+
npx expo prebuild --clean
|
|
65
34
|
|
|
66
|
-
#
|
|
35
|
+
# Run on device
|
|
67
36
|
npx expo run:android
|
|
68
|
-
# or
|
|
69
|
-
npx expo run:ios
|
|
70
37
|
```
|
|
71
38
|
|
|
72
39
|
## ⚙️ Configuration
|
|
73
40
|
|
|
74
|
-
###
|
|
41
|
+
### Android Permissions
|
|
75
42
|
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
"expo": {
|
|
79
|
-
"plugins": [
|
|
80
|
-
[
|
|
81
|
-
"expo-bixolon",
|
|
82
|
-
{
|
|
83
|
-
"bluetoothPermissions": [
|
|
84
|
-
"android.permission.BLUETOOTH",
|
|
85
|
-
"android.permission.BLUETOOTH_ADMIN",
|
|
86
|
-
"android.permission.BLUETOOTH_SCAN",
|
|
87
|
-
"android.permission.BLUETOOTH_CONNECT",
|
|
88
|
-
"android.permission.ACCESS_FINE_LOCATION",
|
|
89
|
-
"android.permission.ACCESS_COARSE_LOCATION"
|
|
90
|
-
]
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
]
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 2. Android Permissions (app.json)
|
|
43
|
+
The module requires Bluetooth permissions. Add to your `app.json`:
|
|
99
44
|
|
|
100
45
|
```json
|
|
101
46
|
{
|
|
@@ -114,72 +59,95 @@ npx expo run:ios
|
|
|
114
59
|
}
|
|
115
60
|
```
|
|
116
61
|
|
|
62
|
+
**Note**: The module automatically handles runtime permission requests on Android 12+.
|
|
63
|
+
|
|
64
|
+
````
|
|
65
|
+
|
|
117
66
|
## 🚀 Quick Start
|
|
118
67
|
|
|
119
|
-
### Basic
|
|
68
|
+
### Basic Example
|
|
120
69
|
|
|
121
70
|
```typescript
|
|
122
|
-
import
|
|
71
|
+
import Printer, { BluetoothDevice } from '@sincpro/printer-expo';
|
|
123
72
|
|
|
124
|
-
//
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
const success = await BixolonPrinter.initializePrinter();
|
|
128
|
-
if (success) {
|
|
129
|
-
console.log('✅ Printer initialized successfully');
|
|
130
|
-
}
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.error('❌ Error initializing printer:', error);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
```
|
|
73
|
+
// 1. Check Bluetooth status
|
|
74
|
+
const isEnabled = await Printer.bluetooth.isEnabled();
|
|
75
|
+
const isSupported = await Printer.bluetooth.isSupported();
|
|
136
76
|
|
|
137
|
-
|
|
77
|
+
// 2. Check permissions
|
|
78
|
+
const permStatus = Printer.permission.getStatus();
|
|
79
|
+
if (!permStatus.allGranted) {
|
|
80
|
+
console.log('Missing permissions:', permStatus.deniedPermissions);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 3. Get paired devices
|
|
84
|
+
const devices = await Printer.bluetooth.getPairedDevices();
|
|
85
|
+
const printers = devices.filter(d => d.isPrinter);
|
|
86
|
+
|
|
87
|
+
// 4. Connect to printer
|
|
88
|
+
await Printer.connection.connect(printers[0].address);
|
|
89
|
+
|
|
90
|
+
// 5. Print receipt
|
|
91
|
+
await Printer.print.receipt({
|
|
92
|
+
header: [
|
|
93
|
+
{ type: 'text', content: 'MY STORE', fontSize: 'large', alignment: 'center', bold: true },
|
|
94
|
+
{ type: 'separator' },
|
|
95
|
+
],
|
|
96
|
+
details: [
|
|
97
|
+
{ type: 'keyValue', key: 'Product', value: '$10.00' },
|
|
98
|
+
{ type: 'keyValue', key: 'Tax', value: '$1.00' },
|
|
99
|
+
],
|
|
100
|
+
footer: [
|
|
101
|
+
{ type: 'separator' },
|
|
102
|
+
{ type: 'keyValue', key: 'TOTAL', value: '$11.00', bold: true },
|
|
103
|
+
{ type: 'space', lines: 2 },
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
````
|
|
107
|
+
|
|
108
|
+
### Complete React Component
|
|
138
109
|
|
|
139
110
|
```typescript
|
|
140
111
|
import React, { useState, useEffect } from 'react';
|
|
141
|
-
import { View, Button, Alert } from 'react-native';
|
|
142
|
-
import
|
|
112
|
+
import { View, Button, FlatList, Text, Alert } from 'react-native';
|
|
113
|
+
import Printer, { BluetoothDevice } from '@sincpro/printer-expo';
|
|
143
114
|
|
|
144
|
-
export default function
|
|
115
|
+
export default function PrinterScreen() {
|
|
145
116
|
const [devices, setDevices] = useState<BluetoothDevice[]>([]);
|
|
146
|
-
const [
|
|
117
|
+
const [connected, setConnected] = useState(false);
|
|
147
118
|
|
|
148
119
|
useEffect(() => {
|
|
149
|
-
initializePrinter();
|
|
150
120
|
checkPermissions();
|
|
151
121
|
}, []);
|
|
152
122
|
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
await BixolonPrinter.initializePrinter();
|
|
156
|
-
console.log('Printer initialized');
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error('Initialization failed:', error);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
123
|
+
const checkPermissions = () => {
|
|
124
|
+
const status = Printer.permission.getStatus();
|
|
161
125
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
126
|
+
if (!status.allGranted) {
|
|
127
|
+
Alert.alert('Permissions Required', `Missing: ${status.deniedPermissions.join(', ')}`);
|
|
128
|
+
}
|
|
165
129
|
};
|
|
166
130
|
|
|
167
|
-
const
|
|
131
|
+
const scanDevices = async () => {
|
|
168
132
|
try {
|
|
169
|
-
const
|
|
170
|
-
|
|
133
|
+
const isEnabled = await Printer.bluetooth.isEnabled();
|
|
134
|
+
if (!isEnabled) {
|
|
135
|
+
Alert.alert('Error', 'Please enable Bluetooth');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const foundDevices = await Printer.bluetooth.getPairedDevices();
|
|
140
|
+
setDevices(foundDevices.filter((d) => d.isPrinter));
|
|
171
141
|
} catch (error) {
|
|
172
|
-
Alert.alert('Error',
|
|
142
|
+
Alert.alert('Error', error.message);
|
|
173
143
|
}
|
|
174
144
|
};
|
|
175
145
|
|
|
176
|
-
const
|
|
146
|
+
const connectDevice = async (device: BluetoothDevice) => {
|
|
177
147
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Alert.alert('Success', 'Connected to printer');
|
|
182
|
-
}
|
|
148
|
+
await Printer.connection.connect(device.address);
|
|
149
|
+
setConnected(true);
|
|
150
|
+
Alert.alert('Success', `Connected to ${device.name}`);
|
|
183
151
|
} catch (error) {
|
|
184
152
|
Alert.alert('Error', 'Connection failed');
|
|
185
153
|
}
|
|
@@ -187,25 +155,39 @@ export default function PrinterApp() {
|
|
|
187
155
|
|
|
188
156
|
const printTest = async () => {
|
|
189
157
|
try {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
158
|
+
await Printer.print.receipt({
|
|
159
|
+
header: [
|
|
160
|
+
{ type: 'text', content: 'Test Receipt', fontSize: 'large', alignment: 'center' },
|
|
161
|
+
{ type: 'separator' },
|
|
162
|
+
],
|
|
163
|
+
details: [{ type: 'text', content: 'This is a test print' }],
|
|
164
|
+
footer: [{ type: 'space', lines: 2 }],
|
|
165
|
+
});
|
|
166
|
+
Alert.alert('Success', 'Receipt printed');
|
|
194
167
|
} catch (error) {
|
|
195
|
-
Alert.alert('Error',
|
|
168
|
+
Alert.alert('Error', error.message);
|
|
196
169
|
}
|
|
197
170
|
};
|
|
198
171
|
|
|
199
172
|
return (
|
|
200
173
|
<View style={{ padding: 20 }}>
|
|
201
|
-
<Button title="
|
|
202
|
-
<Button title="Print Test" onPress={printTest} disabled={!
|
|
203
|
-
|
|
174
|
+
<Button title="Scan Devices" onPress={scanDevices} />
|
|
175
|
+
<Button title="Print Test" onPress={printTest} disabled={!connected} />
|
|
176
|
+
|
|
177
|
+
<FlatList
|
|
178
|
+
data={devices}
|
|
179
|
+
keyExtractor={(item) => item.address}
|
|
180
|
+
renderItem={({ item }) => (
|
|
181
|
+
<Button title={`${item.name} (${item.address})`} onPress={() => connectDevice(item)} />
|
|
182
|
+
)}
|
|
183
|
+
/>
|
|
204
184
|
</View>
|
|
205
185
|
);
|
|
206
186
|
}
|
|
207
187
|
```
|
|
208
188
|
|
|
189
|
+
````
|
|
190
|
+
|
|
209
191
|
## 📱 Detailed Usage
|
|
210
192
|
|
|
211
193
|
### Bluetooth Device Discovery
|
|
@@ -264,7 +246,7 @@ const discoverDevices = async () => {
|
|
|
264
246
|
await BixolonPrinter.stopBluetoothDiscovery();
|
|
265
247
|
}
|
|
266
248
|
};
|
|
267
|
-
|
|
249
|
+
````
|
|
268
250
|
|
|
269
251
|
### Connecting to a Printer
|
|
270
252
|
|
|
@@ -450,468 +432,660 @@ npx expo run:ios
|
|
|
450
432
|
|
|
451
433
|
## 📚 API Reference
|
|
452
434
|
|
|
453
|
-
###
|
|
435
|
+
### Namespaces
|
|
454
436
|
|
|
455
|
-
|
|
437
|
+
The API is organized into four main namespaces:
|
|
456
438
|
|
|
457
|
-
|
|
439
|
+
```typescript
|
|
440
|
+
import Printer from '@sincpro/printer-expo';
|
|
458
441
|
|
|
459
|
-
|
|
442
|
+
Printer.bluetooth; // Bluetooth operations
|
|
443
|
+
Printer.permission; // Permission management
|
|
444
|
+
Printer.connection; // Connection control
|
|
445
|
+
Printer.print; // Printing operations
|
|
446
|
+
```
|
|
460
447
|
|
|
461
|
-
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
### Bluetooth API
|
|
451
|
+
|
|
452
|
+
#### `bluetooth.isEnabled(): Promise<boolean>`
|
|
453
|
+
|
|
454
|
+
Check if Bluetooth is enabled on the device.
|
|
462
455
|
|
|
463
456
|
```typescript
|
|
464
|
-
const
|
|
457
|
+
const enabled = await Printer.bluetooth.isEnabled();
|
|
465
458
|
```
|
|
466
459
|
|
|
467
|
-
#### `
|
|
468
|
-
|
|
469
|
-
Connects to a printer using the specified interface type, address, and port.
|
|
460
|
+
#### `bluetooth.isSupported(): Promise<boolean>`
|
|
470
461
|
|
|
471
|
-
|
|
462
|
+
Check if Bluetooth is supported on the device.
|
|
472
463
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
464
|
+
```typescript
|
|
465
|
+
const supported = await Printer.bluetooth.isSupported();
|
|
466
|
+
```
|
|
476
467
|
|
|
477
|
-
|
|
468
|
+
#### `bluetooth.getPairedDevices(): Promise<BluetoothDevice[]>`
|
|
478
469
|
|
|
479
|
-
|
|
470
|
+
Get list of paired/bonded Bluetooth devices.
|
|
480
471
|
|
|
481
472
|
```typescript
|
|
482
|
-
const
|
|
473
|
+
const devices = await Printer.bluetooth.getPairedDevices();
|
|
474
|
+
const printers = devices.filter((d) => d.isPrinter);
|
|
483
475
|
```
|
|
484
476
|
|
|
485
|
-
#### `
|
|
477
|
+
#### `bluetooth.startDiscovery(): Promise<boolean>`
|
|
486
478
|
|
|
487
|
-
|
|
479
|
+
Start Bluetooth discovery to find nearby devices.
|
|
488
480
|
|
|
489
|
-
|
|
481
|
+
```typescript
|
|
482
|
+
await Printer.bluetooth.startDiscovery();
|
|
483
|
+
```
|
|
490
484
|
|
|
491
|
-
#### `
|
|
485
|
+
#### `bluetooth.stopDiscovery(): Promise<boolean>`
|
|
492
486
|
|
|
493
|
-
|
|
487
|
+
Stop Bluetooth discovery.
|
|
494
488
|
|
|
495
|
-
|
|
489
|
+
```typescript
|
|
490
|
+
await Printer.bluetooth.stopDiscovery();
|
|
491
|
+
```
|
|
496
492
|
|
|
497
|
-
|
|
493
|
+
---
|
|
498
494
|
|
|
499
|
-
###
|
|
495
|
+
### Permission API
|
|
500
496
|
|
|
501
|
-
#### `
|
|
497
|
+
#### `permission.hasBluetoothPermissions(): boolean`
|
|
502
498
|
|
|
503
|
-
|
|
499
|
+
Check if all required Bluetooth permissions are granted (synchronous).
|
|
504
500
|
|
|
505
|
-
|
|
501
|
+
```typescript
|
|
502
|
+
const hasAll = Printer.permission.hasBluetoothPermissions();
|
|
503
|
+
```
|
|
506
504
|
|
|
507
|
-
|
|
505
|
+
#### `permission.getRequiredPermissions(): string[]`
|
|
508
506
|
|
|
509
|
-
|
|
507
|
+
Get list of required permissions for the current Android version.
|
|
510
508
|
|
|
511
|
-
|
|
509
|
+
```typescript
|
|
510
|
+
const required = Printer.permission.getRequiredPermissions();
|
|
511
|
+
// Returns: ['android.permission.BLUETOOTH_SCAN', 'android.permission.BLUETOOTH_CONNECT', ...]
|
|
512
|
+
```
|
|
512
513
|
|
|
513
|
-
|
|
514
|
+
#### `permission.getMissingPermissions(): string[]`
|
|
515
|
+
|
|
516
|
+
Get list of missing/denied permissions.
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
const missing = Printer.permission.getMissingPermissions();
|
|
520
|
+
console.log('Need to request:', missing);
|
|
521
|
+
```
|
|
514
522
|
|
|
515
|
-
|
|
516
|
-
- `fontSize`: Optional font size (default: 10)
|
|
523
|
+
#### `permission.getStatus(): PermissionStatus`
|
|
517
524
|
|
|
518
|
-
|
|
525
|
+
Get detailed permission status.
|
|
519
526
|
|
|
520
|
-
|
|
527
|
+
```typescript
|
|
528
|
+
const status = Printer.permission.getStatus();
|
|
529
|
+
console.log('All granted:', status.allGranted);
|
|
530
|
+
console.log('Granted:', status.grantedPermissions);
|
|
531
|
+
console.log('Denied:', status.deniedPermissions);
|
|
532
|
+
console.log('Android version:', status.androidVersion);
|
|
533
|
+
```
|
|
521
534
|
|
|
522
|
-
|
|
535
|
+
---
|
|
523
536
|
|
|
524
|
-
|
|
537
|
+
### Connection API
|
|
525
538
|
|
|
526
|
-
#### `
|
|
539
|
+
#### `connection.connect(address: string, port?: number): Promise<boolean>`
|
|
527
540
|
|
|
528
|
-
|
|
541
|
+
Connect to a printer via Bluetooth.
|
|
529
542
|
|
|
530
543
|
**Parameters:**
|
|
531
544
|
|
|
532
|
-
- `
|
|
545
|
+
- `address`: MAC address of the printer (e.g., `"00:11:22:AA:BB:CC"`)
|
|
546
|
+
- `port`: TCP port (default: `9100`)
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
await Printer.connection.connect('00:11:22:AA:BB:CC');
|
|
550
|
+
```
|
|
533
551
|
|
|
534
|
-
|
|
552
|
+
#### `connection.disconnect(): Promise<boolean>`
|
|
535
553
|
|
|
536
|
-
|
|
554
|
+
Disconnect from the current printer.
|
|
537
555
|
|
|
538
|
-
|
|
556
|
+
```typescript
|
|
557
|
+
await Printer.connection.disconnect();
|
|
558
|
+
```
|
|
539
559
|
|
|
540
|
-
|
|
560
|
+
#### `connection.getStatus(): Promise<ConnectionInfo>`
|
|
541
561
|
|
|
542
|
-
|
|
562
|
+
Get current connection status and details.
|
|
543
563
|
|
|
544
|
-
|
|
564
|
+
```typescript
|
|
565
|
+
const info = await Printer.connection.getStatus();
|
|
566
|
+
console.log('Address:', info.address);
|
|
567
|
+
console.log('Type:', info.type); // 'BLUETOOTH' | 'WIFI' | 'USB'
|
|
568
|
+
console.log('Status:', info.status); // 'CONNECTED' | 'CONNECTING' | 'DISCONNECTED' | 'ERROR'
|
|
569
|
+
```
|
|
545
570
|
|
|
546
|
-
|
|
571
|
+
#### `connection.isConnected(): boolean`
|
|
547
572
|
|
|
548
|
-
|
|
573
|
+
Check if currently connected (synchronous).
|
|
549
574
|
|
|
550
|
-
|
|
575
|
+
```typescript
|
|
576
|
+
const connected = Printer.connection.isConnected();
|
|
577
|
+
```
|
|
551
578
|
|
|
552
|
-
|
|
579
|
+
---
|
|
553
580
|
|
|
554
|
-
|
|
581
|
+
### Print API
|
|
555
582
|
|
|
556
|
-
|
|
583
|
+
#### `print.receipt(receipt: Receipt): Promise<void>`
|
|
557
584
|
|
|
558
|
-
|
|
585
|
+
Print a complete receipt with header, details, and footer sections.
|
|
559
586
|
|
|
560
|
-
|
|
587
|
+
**Parameters:**
|
|
561
588
|
|
|
562
|
-
|
|
589
|
+
- `receipt`: Receipt object with sections and configuration
|
|
563
590
|
|
|
564
|
-
|
|
591
|
+
```typescript
|
|
592
|
+
await Printer.print.receipt({
|
|
593
|
+
header: [
|
|
594
|
+
{ type: 'text', content: 'STORE NAME', fontSize: 'large', alignment: 'center', bold: true },
|
|
595
|
+
{ type: 'text', content: '123 Main St', alignment: 'center' },
|
|
596
|
+
{ type: 'separator' },
|
|
597
|
+
],
|
|
598
|
+
details: [
|
|
599
|
+
{ type: 'keyValue', key: 'Product A', value: '$10.00' },
|
|
600
|
+
{ type: 'keyValue', key: 'Product B', value: '$15.00' },
|
|
601
|
+
{ type: 'keyValue', key: 'Tax (10%)', value: '$2.50' },
|
|
602
|
+
],
|
|
603
|
+
footer: [
|
|
604
|
+
{ type: 'separator' },
|
|
605
|
+
{ type: 'keyValue', key: 'TOTAL', value: '$27.50', bold: true, fontSize: 'large' },
|
|
606
|
+
{ type: 'qrCode', data: 'https://mystore.com/receipt/12345', size: 5, alignment: 'center' },
|
|
607
|
+
{ type: 'space', lines: 2 },
|
|
608
|
+
],
|
|
609
|
+
copies: 1,
|
|
610
|
+
});
|
|
611
|
+
```
|
|
565
612
|
|
|
566
|
-
|
|
613
|
+
#### `print.lines(lines: ReceiptLine[]): Promise<void>`
|
|
567
614
|
|
|
568
|
-
|
|
615
|
+
Print a list of receipt lines without sections.
|
|
569
616
|
|
|
570
617
|
```typescript
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
type: '
|
|
575
|
-
|
|
576
|
-
|
|
618
|
+
await Printer.print.lines([
|
|
619
|
+
{ type: 'text', content: 'Quick Receipt', alignment: 'center' },
|
|
620
|
+
{ type: 'separator' },
|
|
621
|
+
{ type: 'keyValue', key: 'Item', value: '$5.00' },
|
|
622
|
+
{ type: 'space', lines: 1 },
|
|
623
|
+
]);
|
|
577
624
|
```
|
|
578
625
|
|
|
579
|
-
|
|
626
|
+
#### `print.qrCode(data: string, size?: number): Promise<void>`
|
|
627
|
+
|
|
628
|
+
Print a standalone QR code.
|
|
629
|
+
|
|
630
|
+
**Parameters:**
|
|
631
|
+
|
|
632
|
+
- `data`: QR code content (URL, text, etc.)
|
|
633
|
+
- `size`: QR code size 1-10 (default: `5`)
|
|
580
634
|
|
|
581
635
|
```typescript
|
|
582
|
-
|
|
583
|
-
ACCESS_FINE_LOCATION: boolean;
|
|
584
|
-
ACCESS_COARSE_LOCATION: boolean;
|
|
585
|
-
BLUETOOTH_SCAN?: boolean;
|
|
586
|
-
BLUETOOTH_CONNECT?: boolean;
|
|
587
|
-
}
|
|
636
|
+
await Printer.print.qrCode('https://example.com', 7);
|
|
588
637
|
```
|
|
589
638
|
|
|
590
|
-
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## 🎨 Receipt Line Types
|
|
642
|
+
|
|
643
|
+
### TextLine
|
|
644
|
+
|
|
645
|
+
Print formatted text with customizable style.
|
|
591
646
|
|
|
592
647
|
```typescript
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
648
|
+
{
|
|
649
|
+
type: 'text',
|
|
650
|
+
content: string,
|
|
651
|
+
fontSize?: 'small' | 'medium' | 'large' | 'xlarge',
|
|
652
|
+
bold?: boolean,
|
|
653
|
+
alignment?: 'left' | 'center' | 'right'
|
|
597
654
|
}
|
|
598
655
|
```
|
|
599
656
|
|
|
600
|
-
|
|
657
|
+
**Example:**
|
|
601
658
|
|
|
602
|
-
|
|
659
|
+
```typescript
|
|
660
|
+
{ type: 'text', content: 'INVOICE', fontSize: 'xlarge', alignment: 'center', bold: true }
|
|
661
|
+
```
|
|
603
662
|
|
|
604
|
-
###
|
|
663
|
+
### KeyValueLine
|
|
664
|
+
|
|
665
|
+
Print key-value pairs (common in receipts).
|
|
605
666
|
|
|
606
667
|
```typescript
|
|
607
|
-
|
|
668
|
+
{
|
|
669
|
+
type: 'keyValue',
|
|
670
|
+
key: string,
|
|
671
|
+
value: string,
|
|
672
|
+
fontSize?: FontSize,
|
|
673
|
+
bold?: boolean
|
|
674
|
+
}
|
|
675
|
+
```
|
|
608
676
|
|
|
609
|
-
|
|
610
|
-
const qrPrinter = QrCodePrinter.getInstance();
|
|
677
|
+
**Example:**
|
|
611
678
|
|
|
612
|
-
|
|
613
|
-
|
|
679
|
+
```typescript
|
|
680
|
+
{ type: 'keyValue', key: 'Subtotal', value: '$25.00' }
|
|
681
|
+
{ type: 'keyValue', key: 'TOTAL', value: '$27.50', bold: true }
|
|
614
682
|
```
|
|
615
683
|
|
|
616
|
-
###
|
|
684
|
+
### QRCodeLine
|
|
685
|
+
|
|
686
|
+
Embed QR codes in receipts.
|
|
617
687
|
|
|
618
688
|
```typescript
|
|
619
|
-
|
|
689
|
+
{
|
|
690
|
+
type: 'qrCode',
|
|
691
|
+
data: string,
|
|
692
|
+
size?: number, // 1-10, default: 5
|
|
693
|
+
alignment?: Alignment
|
|
694
|
+
}
|
|
695
|
+
```
|
|
620
696
|
|
|
621
|
-
|
|
622
|
-
data: 'https://www.example.com',
|
|
623
|
-
horizontalPosition: 200,
|
|
624
|
-
verticalPosition: 100,
|
|
625
|
-
model: 'MODEL2',
|
|
626
|
-
eccLevel: 'ECC_LEVEL_15',
|
|
627
|
-
size: 9,
|
|
628
|
-
rotation: 'NONE',
|
|
629
|
-
};
|
|
697
|
+
**Example:**
|
|
630
698
|
|
|
631
|
-
|
|
699
|
+
```typescript
|
|
700
|
+
{ type: 'qrCode', data: 'https://store.com/receipt/12345', size: 6, alignment: 'center' }
|
|
632
701
|
```
|
|
633
702
|
|
|
634
|
-
###
|
|
703
|
+
### SeparatorLine
|
|
635
704
|
|
|
636
|
-
|
|
705
|
+
Print horizontal separator lines.
|
|
637
706
|
|
|
638
707
|
```typescript
|
|
639
|
-
|
|
708
|
+
{
|
|
709
|
+
type: 'separator',
|
|
710
|
+
char?: string, // Character to repeat, default: '-'
|
|
711
|
+
length?: number // Line length, default: 48
|
|
712
|
+
}
|
|
640
713
|
```
|
|
641
714
|
|
|
642
|
-
|
|
715
|
+
**Example:**
|
|
643
716
|
|
|
644
717
|
```typescript
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
phone: '+1234567890',
|
|
648
|
-
email: 'john.doe@example.com',
|
|
649
|
-
company: 'Example Corp',
|
|
650
|
-
title: 'Software Engineer',
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
await qrPrinter.printContactQRCode(contact, 9);
|
|
718
|
+
{ type: 'separator' }
|
|
719
|
+
{ type: 'separator', char: '=', length: 32 }
|
|
654
720
|
```
|
|
655
721
|
|
|
656
|
-
|
|
722
|
+
### SpaceLine
|
|
723
|
+
|
|
724
|
+
Add blank lines for spacing.
|
|
657
725
|
|
|
658
726
|
```typescript
|
|
659
|
-
|
|
727
|
+
{
|
|
728
|
+
type: 'space',
|
|
729
|
+
lines?: number // Number of blank lines, default: 1
|
|
730
|
+
}
|
|
660
731
|
```
|
|
661
732
|
|
|
662
|
-
|
|
733
|
+
**Example:**
|
|
663
734
|
|
|
664
735
|
```typescript
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
736
|
+
{ type: 'space', lines: 2 }
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
## 📦 TypeScript Types
|
|
671
742
|
|
|
672
|
-
|
|
743
|
+
### BluetoothDevice
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
interface BluetoothDevice {
|
|
747
|
+
name: string;
|
|
748
|
+
address: string;
|
|
749
|
+
type: 'CLASSIC' | 'LE' | 'DUAL' | 'UNKNOWN';
|
|
750
|
+
isPrinter: boolean;
|
|
751
|
+
}
|
|
673
752
|
```
|
|
674
753
|
|
|
675
|
-
###
|
|
754
|
+
### PermissionStatus
|
|
676
755
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
756
|
+
```typescript
|
|
757
|
+
interface PermissionStatus {
|
|
758
|
+
allGranted: boolean;
|
|
759
|
+
grantedPermissions: string[];
|
|
760
|
+
deniedPermissions: string[];
|
|
761
|
+
androidVersion: number;
|
|
762
|
+
}
|
|
763
|
+
```
|
|
681
764
|
|
|
682
|
-
|
|
765
|
+
### ConnectionInfo
|
|
683
766
|
|
|
684
|
-
|
|
767
|
+
```typescript
|
|
768
|
+
interface ConnectionInfo {
|
|
769
|
+
address: string;
|
|
770
|
+
port: number;
|
|
771
|
+
type: 'BLUETOOTH' | 'WIFI' | 'USB' | 'UNKNOWN';
|
|
772
|
+
status: 'DISCONNECTED' | 'CONNECTING' | 'CONNECTED' | 'ERROR';
|
|
773
|
+
}
|
|
774
|
+
```
|
|
685
775
|
|
|
686
|
-
|
|
776
|
+
### Receipt
|
|
687
777
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
778
|
+
```typescript
|
|
779
|
+
interface Receipt {
|
|
780
|
+
header?: ReceiptLine[];
|
|
781
|
+
details?: ReceiptLine[];
|
|
782
|
+
footer?: ReceiptLine[];
|
|
783
|
+
mediaConfig?: MediaConfig;
|
|
784
|
+
copies?: number;
|
|
785
|
+
}
|
|
786
|
+
```
|
|
694
787
|
|
|
695
|
-
|
|
788
|
+
### MediaConfig
|
|
696
789
|
|
|
697
|
-
|
|
790
|
+
```typescript
|
|
791
|
+
interface MediaConfig {
|
|
792
|
+
type: 'continuous' | 'labelGap' | 'labelBlackMark';
|
|
793
|
+
width: number;
|
|
794
|
+
height: number;
|
|
795
|
+
offset?: number;
|
|
796
|
+
gap?: number;
|
|
797
|
+
}
|
|
798
|
+
```
|
|
698
799
|
|
|
699
|
-
|
|
700
|
-
- Permission management
|
|
701
|
-
- Printer connection
|
|
702
|
-
- Text and invoice printing
|
|
703
|
-
- Event handling
|
|
800
|
+
---
|
|
704
801
|
|
|
705
|
-
##
|
|
802
|
+
## 🎯 Advanced Examples
|
|
706
803
|
|
|
707
|
-
|
|
708
|
-
- **Expo SDK**: 53+
|
|
709
|
-
- **Android**: API level 21+ (Android 5.0+)
|
|
710
|
-
- **iOS**: 13+ (limited support)
|
|
711
|
-
- **Node.js**: 18+
|
|
712
|
-
- **TypeScript**: 4.9+ (optional but recommended)
|
|
804
|
+
### Complete Receipt with All Line Types
|
|
713
805
|
|
|
714
|
-
|
|
806
|
+
```typescript
|
|
807
|
+
const fullReceipt: Receipt = {
|
|
808
|
+
header: [
|
|
809
|
+
{
|
|
810
|
+
type: 'text',
|
|
811
|
+
content: '🏪 MY RETAIL STORE',
|
|
812
|
+
fontSize: 'xlarge',
|
|
813
|
+
alignment: 'center',
|
|
814
|
+
bold: true,
|
|
815
|
+
},
|
|
816
|
+
{ type: 'text', content: '123 Commerce Street', alignment: 'center' },
|
|
817
|
+
{ type: 'text', content: 'Phone: (555) 123-4567', alignment: 'center' },
|
|
818
|
+
{ type: 'separator', char: '=' },
|
|
819
|
+
{ type: 'text', content: 'SALES RECEIPT', fontSize: 'large', alignment: 'center' },
|
|
820
|
+
{ type: 'separator', char: '=' },
|
|
821
|
+
{ type: 'space' },
|
|
822
|
+
],
|
|
823
|
+
details: [
|
|
824
|
+
{ type: 'keyValue', key: 'Date', value: new Date().toLocaleDateString() },
|
|
825
|
+
{ type: 'keyValue', key: 'Receipt #', value: '00123' },
|
|
826
|
+
{ type: 'keyValue', key: 'Cashier', value: 'John Doe' },
|
|
827
|
+
{ type: 'space' },
|
|
828
|
+
{ type: 'separator' },
|
|
829
|
+
{ type: 'text', content: 'ITEMS', bold: true },
|
|
830
|
+
{ type: 'separator' },
|
|
831
|
+
{ type: 'keyValue', key: 'Coffee (x2)', value: '$9.00' },
|
|
832
|
+
{ type: 'keyValue', key: 'Croissant (x1)', value: '$3.50' },
|
|
833
|
+
{ type: 'keyValue', key: 'Orange Juice (x1)', value: '$4.00' },
|
|
834
|
+
{ type: 'space' },
|
|
835
|
+
{ type: 'separator' },
|
|
836
|
+
{ type: 'keyValue', key: 'Subtotal', value: '$16.50' },
|
|
837
|
+
{ type: 'keyValue', key: 'Tax (8%)', value: '$1.32' },
|
|
838
|
+
],
|
|
839
|
+
footer: [
|
|
840
|
+
{ type: 'separator', char: '=' },
|
|
841
|
+
{ type: 'keyValue', key: 'TOTAL', value: '$17.82', fontSize: 'large', bold: true },
|
|
842
|
+
{ type: 'separator', char: '=' },
|
|
843
|
+
{ type: 'space', lines: 2 },
|
|
844
|
+
{ type: 'text', content: 'Thank you for your purchase!', alignment: 'center' },
|
|
845
|
+
{ type: 'text', content: 'Visit us at www.mystore.com', alignment: 'center' },
|
|
846
|
+
{ type: 'space' },
|
|
847
|
+
{ type: 'qrCode', data: 'https://mystore.com/receipt/00123', size: 6, alignment: 'center' },
|
|
848
|
+
{ type: 'space', lines: 3 },
|
|
849
|
+
],
|
|
850
|
+
copies: 1,
|
|
851
|
+
};
|
|
715
852
|
|
|
716
|
-
|
|
853
|
+
await Printer.print.receipt(fullReceipt);
|
|
854
|
+
```
|
|
717
855
|
|
|
718
|
-
|
|
719
|
-
- **SPP-R200III** ✅
|
|
720
|
-
- **SPP-R300** ✅
|
|
721
|
-
- **SPP-R400** ✅
|
|
722
|
-
- **SPP-L410** ✅
|
|
723
|
-
- **SPP-L420** ✅
|
|
856
|
+
### Error Handling Pattern
|
|
724
857
|
|
|
725
|
-
|
|
858
|
+
```typescript
|
|
859
|
+
const safePrint = async (receipt: Receipt) => {
|
|
860
|
+
try {
|
|
861
|
+
// Check connection
|
|
862
|
+
if (!Printer.connection.isConnected()) {
|
|
863
|
+
throw new Error('Printer not connected');
|
|
864
|
+
}
|
|
726
865
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
866
|
+
// Check status
|
|
867
|
+
const status = await Printer.connection.getStatus();
|
|
868
|
+
if (status.status !== 'CONNECTED') {
|
|
869
|
+
throw new Error(`Connection ${status.status.toLowerCase()}`);
|
|
870
|
+
}
|
|
730
871
|
|
|
731
|
-
|
|
872
|
+
// Print
|
|
873
|
+
await Printer.print.receipt(receipt);
|
|
874
|
+
console.log('✅ Print successful');
|
|
875
|
+
} catch (error) {
|
|
876
|
+
console.error('❌ Print failed:', error.message);
|
|
732
877
|
|
|
733
|
-
|
|
878
|
+
// Show user-friendly message
|
|
879
|
+
Alert.alert('Print Error', error.message || 'Could not print receipt', [{ text: 'OK' }]);
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
```
|
|
734
883
|
|
|
735
|
-
|
|
884
|
+
### Custom Hook for Printer Management
|
|
736
885
|
|
|
737
886
|
```typescript
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
console.log('Permissions:', permissions);
|
|
887
|
+
import { useState, useEffect } from 'react';
|
|
888
|
+
import Printer, { BluetoothDevice, ConnectionInfo } from '@sincpro/printer-expo';
|
|
741
889
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
```
|
|
890
|
+
export function usePrinter() {
|
|
891
|
+
const [devices, setDevices] = useState<BluetoothDevice[]>([]);
|
|
892
|
+
const [connected, setConnected] = useState(false);
|
|
893
|
+
const [connectionInfo, setConnectionInfo] = useState<ConnectionInfo | null>(null);
|
|
747
894
|
|
|
748
|
-
|
|
895
|
+
useEffect(() => {
|
|
896
|
+
checkConnection();
|
|
897
|
+
}, []);
|
|
749
898
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
899
|
+
const checkConnection = () => {
|
|
900
|
+
const isConnected = Printer.connection.isConnected();
|
|
901
|
+
setConnected(isConnected);
|
|
902
|
+
};
|
|
754
903
|
|
|
755
|
-
|
|
904
|
+
const scanDevices = async () => {
|
|
905
|
+
try {
|
|
906
|
+
const isEnabled = await Printer.bluetooth.isEnabled();
|
|
907
|
+
if (!isEnabled) {
|
|
908
|
+
throw new Error('Bluetooth is disabled');
|
|
909
|
+
}
|
|
756
910
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
- Check if another app is connected to the printer
|
|
760
|
-
- Try disconnecting and reconnecting
|
|
911
|
+
const foundDevices = await Printer.bluetooth.getPairedDevices();
|
|
912
|
+
setDevices(foundDevices.filter((d) => d.isPrinter));
|
|
761
913
|
|
|
762
|
-
|
|
914
|
+
return foundDevices;
|
|
915
|
+
} catch (error) {
|
|
916
|
+
console.error('Scan failed:', error);
|
|
917
|
+
throw error;
|
|
918
|
+
}
|
|
919
|
+
};
|
|
763
920
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
921
|
+
const connect = async (address: string) => {
|
|
922
|
+
try {
|
|
923
|
+
await Printer.connection.connect(address);
|
|
924
|
+
setConnected(true);
|
|
768
925
|
|
|
769
|
-
|
|
926
|
+
const info = await Printer.connection.getStatus();
|
|
927
|
+
setConnectionInfo(info);
|
|
770
928
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
929
|
+
return true;
|
|
930
|
+
} catch (error) {
|
|
931
|
+
console.error('Connection failed:', error);
|
|
932
|
+
throw error;
|
|
933
|
+
}
|
|
934
|
+
};
|
|
774
935
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
936
|
+
const disconnect = async () => {
|
|
937
|
+
try {
|
|
938
|
+
await Printer.connection.disconnect();
|
|
939
|
+
setConnected(false);
|
|
940
|
+
setConnectionInfo(null);
|
|
941
|
+
return true;
|
|
942
|
+
} catch (error) {
|
|
943
|
+
console.error('Disconnection failed:', error);
|
|
944
|
+
throw error;
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
const printReceipt = async (receipt: Receipt) => {
|
|
949
|
+
if (!connected) {
|
|
950
|
+
throw new Error('Not connected to printer');
|
|
951
|
+
}
|
|
778
952
|
|
|
779
|
-
|
|
953
|
+
await Printer.print.receipt(receipt);
|
|
954
|
+
};
|
|
780
955
|
|
|
781
|
-
|
|
956
|
+
return {
|
|
957
|
+
devices,
|
|
958
|
+
connected,
|
|
959
|
+
connectionInfo,
|
|
960
|
+
scanDevices,
|
|
961
|
+
connect,
|
|
962
|
+
disconnect,
|
|
963
|
+
printReceipt,
|
|
964
|
+
};
|
|
965
|
+
}
|
|
782
966
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
967
|
+
// Usage
|
|
968
|
+
function MyComponent() {
|
|
969
|
+
const printer = usePrinter();
|
|
786
970
|
|
|
787
|
-
|
|
788
|
-
|
|
971
|
+
return (
|
|
972
|
+
<View>
|
|
973
|
+
<Button title="Scan" onPress={printer.scanDevices} />
|
|
974
|
+
<Button
|
|
975
|
+
title="Print"
|
|
976
|
+
onPress={() => printer.printReceipt(myReceipt)}
|
|
977
|
+
disabled={!printer.connected}
|
|
978
|
+
/>
|
|
979
|
+
</View>
|
|
980
|
+
);
|
|
981
|
+
}
|
|
789
982
|
```
|
|
790
983
|
|
|
791
|
-
|
|
984
|
+
---
|
|
792
985
|
|
|
793
|
-
|
|
794
|
-
2. **Reuse connections**: Keep connections alive when possible
|
|
795
|
-
3. **Batch operations**: Group multiple print operations together
|
|
796
|
-
4. **Error handling**: Always wrap print operations in try-catch blocks
|
|
986
|
+
## 🏗️ Architecture
|
|
797
987
|
|
|
798
|
-
|
|
988
|
+
This module follows **Clean Architecture** with **Hexagonal Architecture** (Ports & Adapters) principles:
|
|
799
989
|
|
|
800
|
-
|
|
990
|
+
```
|
|
991
|
+
TypeScript (React Native)
|
|
992
|
+
↓
|
|
993
|
+
ENTRYPOINT - Expo Modules API bridge
|
|
994
|
+
↓
|
|
995
|
+
SERVICE - Use cases & orchestration
|
|
996
|
+
↓
|
|
997
|
+
DOMAIN - Business entities & rules
|
|
998
|
+
↓
|
|
999
|
+
ADAPTER - Printer implementations (Bixolon, Zebra, etc.)
|
|
1000
|
+
↓
|
|
1001
|
+
INFRASTRUCTURE - Android APIs & SDKs
|
|
1002
|
+
```
|
|
801
1003
|
|
|
802
|
-
|
|
803
|
-
2. Enable debug mode and check logs
|
|
804
|
-
3. Verify your printer model is supported
|
|
805
|
-
4. Test with a simple print operation first
|
|
1004
|
+
**Benefits:**
|
|
806
1005
|
|
|
807
|
-
|
|
1006
|
+
- ✅ **Testable**: Mock adapters and services
|
|
1007
|
+
- ✅ **Maintainable**: Clear separation of concerns
|
|
1008
|
+
- ✅ **Extensible**: Easy to add new printer brands
|
|
1009
|
+
- ✅ **Swappable**: Change implementations without affecting business logic
|
|
808
1010
|
|
|
809
|
-
|
|
1011
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for details.
|
|
810
1012
|
|
|
811
|
-
|
|
1013
|
+
---
|
|
812
1014
|
|
|
813
|
-
|
|
814
|
-
# Clone the repository
|
|
815
|
-
git clone https://github.com/your-username/expo-bixolon.git
|
|
816
|
-
cd expo-bixolon
|
|
1015
|
+
## 🛠️ Troubleshooting
|
|
817
1016
|
|
|
818
|
-
|
|
819
|
-
npm install
|
|
1017
|
+
### Bluetooth Permissions
|
|
820
1018
|
|
|
821
|
-
|
|
822
|
-
npm run build
|
|
1019
|
+
**Problem**: "Permission denied" errors
|
|
823
1020
|
|
|
824
|
-
|
|
825
|
-
cd example
|
|
826
|
-
npm install
|
|
827
|
-
npx expo run:android
|
|
828
|
-
```
|
|
1021
|
+
**Solution**: Check permission status and request if needed
|
|
829
1022
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
6. **Submit a pull request**
|
|
1023
|
+
```typescript
|
|
1024
|
+
const status = Printer.permission.getStatus();
|
|
1025
|
+
if (!status.allGranted) {
|
|
1026
|
+
console.log('Missing:', status.deniedPermissions);
|
|
1027
|
+
// Guide user to app settings to grant permissions
|
|
1028
|
+
}
|
|
1029
|
+
```
|
|
838
1030
|
|
|
839
|
-
###
|
|
1031
|
+
### Connection Issues
|
|
840
1032
|
|
|
841
|
-
|
|
1033
|
+
**Problem**: Connection fails or times out
|
|
842
1034
|
|
|
843
|
-
|
|
844
|
-
2. **Commit**: `git add . && git commit -m "chore: bump version to x.y.z"`
|
|
845
|
-
3. **Push**: `git push origin main`
|
|
846
|
-
4. **Create GitHub Release**: El CI/CD publicará automáticamente a NPM
|
|
847
|
-
- Requiere secret `NPM_TOKEN` configurado en GitHub
|
|
1035
|
+
**Solutions**:
|
|
848
1036
|
|
|
849
|
-
|
|
1037
|
+
1. Verify Bluetooth is enabled: `await Printer.bluetooth.isEnabled()`
|
|
1038
|
+
2. Check device is paired: `await Printer.bluetooth.getPairedDevices()`
|
|
1039
|
+
3. Ensure printer is powered on and in range
|
|
1040
|
+
4. Try reconnecting: `await Printer.connection.connect(address)`
|
|
850
1041
|
|
|
851
|
-
|
|
852
|
-
- `make test` - Ejecuta tests
|
|
853
|
-
- `make format` - Formatea código
|
|
854
|
-
- `make verify-format` - Verifica formato (usado en CI)
|
|
855
|
-
- `make publish-dry-run` - Simula publicación
|
|
856
|
-
- `make publish` - Publica manualmente (requiere `npm login`)
|
|
1042
|
+
### Print Failures
|
|
857
1043
|
|
|
858
|
-
|
|
1044
|
+
**Problem**: Print command succeeds but nothing prints
|
|
859
1045
|
|
|
860
|
-
|
|
861
|
-
- Follow existing code patterns
|
|
862
|
-
- Add proper error handling
|
|
863
|
-
- Include JSDoc comments for public APIs
|
|
864
|
-
- Test on real Bixolon printers when possible
|
|
1046
|
+
**Solutions**:
|
|
865
1047
|
|
|
866
|
-
|
|
1048
|
+
1. Check printer status (paper, errors)
|
|
1049
|
+
2. Verify connection: `Printer.connection.isConnected()`
|
|
1050
|
+
3. Try smaller test print first
|
|
1051
|
+
4. Check printer-specific requirements (media config)
|
|
867
1052
|
|
|
868
|
-
|
|
1053
|
+
---
|
|
869
1054
|
|
|
870
|
-
##
|
|
1055
|
+
## 📖 Resources
|
|
871
1056
|
|
|
872
|
-
|
|
1057
|
+
- **Architecture**: [ARCHITECTURE.md](ARCHITECTURE.md) - Detailed architecture guide
|
|
1058
|
+
- **Contributing**: [CONTRIBUTING.md](CONTRIBUTING.md) - Development guidelines
|
|
1059
|
+
- **Copilot**: [.github/copilot-instructions.md](.github/copilot-instructions.md) - AI coding assistant rules
|
|
1060
|
+
- **Expo Modules**: [Official Documentation](https://docs.expo.dev/modules/overview/)
|
|
1061
|
+
- **Bixolon SDK**: [Official Documentation](https://www.bixolon.com/)
|
|
873
1062
|
|
|
874
|
-
|
|
875
|
-
- 🐛 **Bug Reports**: Open an issue with detailed information
|
|
876
|
-
- 💡 **Feature Requests**: Open an issue with use case description
|
|
877
|
-
- 💬 **Discussions**: Use GitHub Discussions for questions
|
|
1063
|
+
---
|
|
878
1064
|
|
|
879
|
-
|
|
1065
|
+
## 🤝 Contributing
|
|
880
1066
|
|
|
881
|
-
|
|
1067
|
+
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
|
|
882
1068
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1069
|
+
- Development setup
|
|
1070
|
+
- Code standards (ktlint, Prettier)
|
|
1071
|
+
- Architecture guidelines
|
|
1072
|
+
- Git workflow
|
|
1073
|
+
- Pull request process
|
|
888
1074
|
|
|
889
|
-
|
|
890
|
-
2. Connect to device
|
|
891
|
-
3. Print text
|
|
892
|
-
**Expected Behavior**: Text should print
|
|
893
|
-
**Actual Behavior**: Connection fails
|
|
894
|
-
**Logs**: [Include relevant logs]
|
|
895
|
-
```
|
|
1075
|
+
---
|
|
896
1076
|
|
|
897
|
-
##
|
|
1077
|
+
## 📄 License
|
|
898
1078
|
|
|
899
|
-
|
|
1079
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
900
1080
|
|
|
901
|
-
|
|
902
|
-
- [ ] **Image Printing**: Support for printing images and logos
|
|
903
|
-
- [ ] **Barcode Printing**: Various barcode formats
|
|
904
|
-
- [ ] **iOS Support**: Full iOS implementation
|
|
905
|
-
- [ ] **Web Support**: Web-based printing interface
|
|
906
|
-
- [ ] **Cloud Printing**: Remote printing capabilities
|
|
1081
|
+
---
|
|
907
1082
|
|
|
908
|
-
|
|
1083
|
+
## 🙏 Acknowledgments
|
|
909
1084
|
|
|
910
|
-
-
|
|
911
|
-
-
|
|
912
|
-
-
|
|
913
|
-
- ✅ **Local Installation**: Support for local package distribution
|
|
1085
|
+
- Bixolon for the official printer SDK
|
|
1086
|
+
- Expo team for the Modules API
|
|
1087
|
+
- Contributors and testers
|
|
914
1088
|
|
|
915
1089
|
---
|
|
916
1090
|
|
|
917
|
-
**Made with ❤️
|
|
1091
|
+
**Made with ❤️ by Sincpro SRL**
|