react-native-telpo 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 +300 -0
- package/lib/commonjs/index.js +40 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/nfc/index.js +322 -0
- package/lib/commonjs/nfc/index.js.map +1 -0
- package/lib/commonjs/printer/index.js +379 -0
- package/lib/commonjs/printer/index.js.map +1 -0
- package/lib/commonjs/scanner/index.js +301 -0
- package/lib/commonjs/scanner/index.js.map +1 -0
- package/lib/commonjs/types/index.js +37 -0
- package/lib/commonjs/types/index.js.map +1 -0
- package/lib/module/index.js +27 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/nfc/index.js +317 -0
- package/lib/module/nfc/index.js.map +1 -0
- package/lib/module/printer/index.js +374 -0
- package/lib/module/printer/index.js.map +1 -0
- package/lib/module/scanner/index.js +294 -0
- package/lib/module/scanner/index.js.map +1 -0
- package/lib/module/types/index.js +38 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/typescript/index.d.ts +21 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/nfc/index.d.ts +83 -0
- package/lib/typescript/nfc/index.d.ts.map +1 -0
- package/lib/typescript/printer/index.d.ts +91 -0
- package/lib/typescript/printer/index.d.ts.map +1 -0
- package/lib/typescript/scanner/index.d.ts +81 -0
- package/lib/typescript/scanner/index.d.ts.map +1 -0
- package/lib/typescript/types/index.d.ts +114 -0
- package/lib/typescript/types/index.d.ts.map +1 -0
- package/package.json +104 -0
- package/src/index.ts +55 -0
- package/src/nfc/index.ts +362 -0
- package/src/printer/index.ts +432 -0
- package/src/scanner/index.tsx +397 -0
- package/src/types/index.ts +161 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelpoPrinter
|
|
3
|
+
*
|
|
4
|
+
* Wraps @haroldtran/react-native-thermal-printer to provide a clean, typed API
|
|
5
|
+
* for Telpo M1 and TPS320 internal (USB) thermal printers.
|
|
6
|
+
*
|
|
7
|
+
* Connection priority: USB (internal) → Bluetooth → Network
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Platform } from 'react-native';
|
|
11
|
+
import {
|
|
12
|
+
TelpoError,
|
|
13
|
+
TelpoErrorCode,
|
|
14
|
+
TelpoResult,
|
|
15
|
+
PrinterDevice,
|
|
16
|
+
PrinterOptions,
|
|
17
|
+
PrintLine,
|
|
18
|
+
PrintTicket,
|
|
19
|
+
PrintReceiptOptions,
|
|
20
|
+
PrintAlignment,
|
|
21
|
+
PrintFontSize,
|
|
22
|
+
} from '../types';
|
|
23
|
+
|
|
24
|
+
// Lazy-loaded to avoid crashing if peer dep is missing
|
|
25
|
+
let USBPrinter: any;
|
|
26
|
+
let BLEPrinter: any;
|
|
27
|
+
let NetPrinter: any;
|
|
28
|
+
let COMMANDS: any;
|
|
29
|
+
|
|
30
|
+
function loadPrinterLib(): void {
|
|
31
|
+
try {
|
|
32
|
+
const lib = require('@haroldtran/react-native-thermal-printer');
|
|
33
|
+
USBPrinter = lib.USBPrinter;
|
|
34
|
+
BLEPrinter = lib.BLEPrinter;
|
|
35
|
+
NetPrinter = lib.NetPrinter;
|
|
36
|
+
COMMANDS = lib.COMMANDS;
|
|
37
|
+
} catch {
|
|
38
|
+
throw buildError(
|
|
39
|
+
TelpoErrorCode.PRINTER_INIT_FAILED,
|
|
40
|
+
'Peer dependency @haroldtran/react-native-thermal-printer is not installed. Run: npm install @haroldtran/react-native-thermal-printer'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
function buildError(code: TelpoErrorCode, message: string, raw?: unknown): TelpoError {
|
|
48
|
+
return { code, message, raw };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function wrapSuccess<T>(data: T): TelpoResult<T> {
|
|
52
|
+
return { success: true, data };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function wrapError<T>(error: TelpoError): TelpoResult<T> {
|
|
56
|
+
return { success: false, error };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function alignCmd(alignment: PrintAlignment): string {
|
|
60
|
+
if (!COMMANDS) return '';
|
|
61
|
+
switch (alignment) {
|
|
62
|
+
case 'LEFT': return COMMANDS.TEXT_FORMAT.TXT_ALIGN_LT;
|
|
63
|
+
case 'CENTER': return COMMANDS.TEXT_FORMAT.TXT_ALIGN_CT;
|
|
64
|
+
case 'RIGHT': return COMMANDS.TEXT_FORMAT.TXT_ALIGN_RT;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function fontSizeCmd(size: PrintFontSize): string {
|
|
69
|
+
if (!COMMANDS) return '';
|
|
70
|
+
switch (size) {
|
|
71
|
+
case 'SMALL': return COMMANDS.TEXT_FORMAT.TXT_CUSTOM_SIZE(1, 1);
|
|
72
|
+
case 'NORMAL': return COMMANDS.TEXT_FORMAT.TXT_CUSTOM_SIZE(1, 1);
|
|
73
|
+
case 'LARGE': return COMMANDS.TEXT_FORMAT.TXT_CUSTOM_SIZE(2, 2);
|
|
74
|
+
case 'DOUBLE': return COMMANDS.TEXT_FORMAT.TXT_CUSTOM_SIZE(2, 2);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function repeatChar(char: string, times: number): string {
|
|
79
|
+
return char.repeat(times);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const DIVIDER = repeatChar('-', 32);
|
|
83
|
+
const THICK_DIVIDER = repeatChar('=', 32);
|
|
84
|
+
|
|
85
|
+
/** Build a formatted two-column line: "Label Value" */
|
|
86
|
+
function twoCol(label: string, value: string, width = 32): string {
|
|
87
|
+
const gap = width - label.length - value.length;
|
|
88
|
+
return label + repeatChar(' ', Math.max(1, gap)) + value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function nowString(): string {
|
|
92
|
+
const d = new Date();
|
|
93
|
+
const pad = (n: number) => String(n).padStart(2, '0');
|
|
94
|
+
return (
|
|
95
|
+
`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +
|
|
96
|
+
`${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── State ─────────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
type ActivePrinterType = 'USB' | 'BLUETOOTH' | 'NETWORK' | null;
|
|
103
|
+
|
|
104
|
+
let _initialized = false;
|
|
105
|
+
let _activePrinterType: ActivePrinterType = null;
|
|
106
|
+
|
|
107
|
+
// ── Module ────────────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* TelpoPrinter
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* // Connect to internal USB printer (M1 / TPS320)
|
|
114
|
+
* await TelpoPrinter.init();
|
|
115
|
+
* const devices = await TelpoPrinter.getDeviceList();
|
|
116
|
+
* await TelpoPrinter.connect({ connectionType: 'USB' });
|
|
117
|
+
*
|
|
118
|
+
* // Print a bus ticket
|
|
119
|
+
* await TelpoPrinter.printTicket({
|
|
120
|
+
* ticketId: 'TKT-001234',
|
|
121
|
+
* route: 'Route 42 — City Centre → Airport',
|
|
122
|
+
* passenger: 'John Doe',
|
|
123
|
+
* fare: 'SLE 5.00',
|
|
124
|
+
* });
|
|
125
|
+
*/
|
|
126
|
+
export const TelpoPrinter = {
|
|
127
|
+
get _initialized(): boolean {
|
|
128
|
+
return _initialized;
|
|
129
|
+
},
|
|
130
|
+
set _initialized(value: boolean) {
|
|
131
|
+
_initialized = value;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
get _activePrinterType(): ActivePrinterType {
|
|
135
|
+
return _activePrinterType;
|
|
136
|
+
},
|
|
137
|
+
set _activePrinterType(value: ActivePrinterType) {
|
|
138
|
+
_activePrinterType = value;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Initialise the printer module.
|
|
143
|
+
* Must be called once before any other method.
|
|
144
|
+
* On Android only — iOS has no USB printer support.
|
|
145
|
+
*/
|
|
146
|
+
async init(): Promise<TelpoResult<void>> {
|
|
147
|
+
if (Platform.OS !== 'android') {
|
|
148
|
+
return wrapError(
|
|
149
|
+
buildError(TelpoErrorCode.PRINTER_INIT_FAILED, 'Thermal printing is only supported on Android.')
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
loadPrinterLib();
|
|
155
|
+
await USBPrinter.init();
|
|
156
|
+
_initialized = true;
|
|
157
|
+
return wrapSuccess(undefined);
|
|
158
|
+
} catch (e: unknown) {
|
|
159
|
+
const err = e as TelpoError;
|
|
160
|
+
if (err.code) return wrapError(err);
|
|
161
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_INIT_FAILED, 'Failed to initialise printer', e));
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* List all available USB printer devices.
|
|
167
|
+
* Returns empty array on non-Android platforms.
|
|
168
|
+
*/
|
|
169
|
+
async getDeviceList(): Promise<TelpoResult<PrinterDevice[]>> {
|
|
170
|
+
if (!_initialized) {
|
|
171
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_INIT_FAILED, 'Call TelpoPrinter.init() first.'));
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const devices: PrinterDevice[] = await USBPrinter.getDeviceList();
|
|
175
|
+
return wrapSuccess(devices);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_INIT_FAILED, 'Failed to list printer devices', e));
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Connect to the printer.
|
|
183
|
+
*
|
|
184
|
+
* - `USB` (default): connects to the first available USB device automatically.
|
|
185
|
+
* For Telpo M1 / TPS320 this is the internal printer — no vendorId/productId needed.
|
|
186
|
+
* - `BLUETOOTH`: requires `bluetoothAddress`.
|
|
187
|
+
* - `NETWORK`: requires `networkHost` (and optionally `networkPort`, default 9100).
|
|
188
|
+
*/
|
|
189
|
+
async connect(options: PrinterOptions = {}): Promise<TelpoResult<void>> {
|
|
190
|
+
if (!_initialized && options.connectionType !== 'BLUETOOTH' && options.connectionType !== 'NETWORK') {
|
|
191
|
+
const initResult = await TelpoPrinter.init();
|
|
192
|
+
if (!initResult.success) return initResult;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const type = options.connectionType ?? 'USB';
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
if (type === 'USB') {
|
|
199
|
+
const devResult = await TelpoPrinter.getDeviceList();
|
|
200
|
+
if (!devResult.success) return devResult;
|
|
201
|
+
|
|
202
|
+
const devices = devResult.data;
|
|
203
|
+
if (devices.length === 0) {
|
|
204
|
+
return wrapError(
|
|
205
|
+
buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'No USB printer devices found. Make sure the device has an internal printer.')
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const device = devices[0]!;
|
|
210
|
+
await USBPrinter.connectPrinter(device.vendorId, device.productId);
|
|
211
|
+
_activePrinterType = 'USB';
|
|
212
|
+
} else if (type === 'BLUETOOTH') {
|
|
213
|
+
if (!options.bluetoothAddress) {
|
|
214
|
+
return wrapError(
|
|
215
|
+
buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'bluetoothAddress is required for Bluetooth connection.')
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
await BLEPrinter.init();
|
|
219
|
+
await BLEPrinter.connectPrinter(options.bluetoothAddress);
|
|
220
|
+
_activePrinterType = 'BLUETOOTH';
|
|
221
|
+
} else if (type === 'NETWORK') {
|
|
222
|
+
if (!options.networkHost) {
|
|
223
|
+
return wrapError(
|
|
224
|
+
buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'networkHost is required for Network connection.')
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
await NetPrinter.init();
|
|
228
|
+
await NetPrinter.connectPrinter(options.networkHost, options.networkPort ?? 9100);
|
|
229
|
+
_activePrinterType = 'NETWORK';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return wrapSuccess(undefined);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
_activePrinterType = null;
|
|
235
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'Failed to connect to printer', e));
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
/** Disconnect from the printer and release resources. */
|
|
240
|
+
async disconnect(): Promise<TelpoResult<void>> {
|
|
241
|
+
try {
|
|
242
|
+
if (_activePrinterType === 'USB') await USBPrinter.closeConn();
|
|
243
|
+
else if (_activePrinterType === 'BLUETOOTH') await BLEPrinter.closeConn();
|
|
244
|
+
else if (_activePrinterType === 'NETWORK') await NetPrinter.closeConn();
|
|
245
|
+
_activePrinterType = null;
|
|
246
|
+
return wrapSuccess(undefined);
|
|
247
|
+
} catch (e) {
|
|
248
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_PRINT_FAILED, 'Failed to disconnect printer', e));
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Print an array of custom lines. Full control over alignment, bold, and font size.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* await TelpoPrinter.printLines([
|
|
257
|
+
* { text: 'CITY BUS', alignment: 'CENTER', bold: true, fontSize: 'LARGE' },
|
|
258
|
+
* { text: 'Ticket ID: TKT-0012', alignment: 'LEFT' },
|
|
259
|
+
* ]);
|
|
260
|
+
*/
|
|
261
|
+
async printLines(
|
|
262
|
+
lines: PrintLine[],
|
|
263
|
+
options: PrintReceiptOptions = {}
|
|
264
|
+
): Promise<TelpoResult<void>> {
|
|
265
|
+
if (!_activePrinterType) {
|
|
266
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'Not connected. Call TelpoPrinter.connect() first.'));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const printer = _getPrinter();
|
|
271
|
+
let output = '';
|
|
272
|
+
|
|
273
|
+
for (const line of lines) {
|
|
274
|
+
const align = alignCmd(line.alignment ?? 'LEFT');
|
|
275
|
+
const size = fontSizeCmd(line.fontSize ?? 'NORMAL');
|
|
276
|
+
const bold = line.bold ? COMMANDS.TEXT_FORMAT.TXT_BOLD_ON : COMMANDS.TEXT_FORMAT.TXT_BOLD_OFF;
|
|
277
|
+
const uline = line.underline ? COMMANDS.TEXT_FORMAT.TXT_UNDERL_ON : COMMANDS.TEXT_FORMAT.TXT_UNDERL_OFF;
|
|
278
|
+
const reset = COMMANDS.TEXT_FORMAT.TXT_NORMAL;
|
|
279
|
+
|
|
280
|
+
output += `${align}${size}${bold}${uline}${line.text}${reset}\n`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const feedLines = options.feedLines ?? 4;
|
|
284
|
+
output += '\n'.repeat(feedLines);
|
|
285
|
+
|
|
286
|
+
if (options.cutPaper !== false) {
|
|
287
|
+
output += COMMANDS.PAPER.PAPER_FULL_CUT;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
await printer.printText(output);
|
|
291
|
+
return wrapSuccess(undefined);
|
|
292
|
+
} catch (e) {
|
|
293
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_PRINT_FAILED, 'Failed to print lines', e));
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Print a formatted bus ticket receipt.
|
|
299
|
+
* Automatically formats header, route, passenger info,
|
|
300
|
+
* ticket ID, fare, and optional QR code.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* await TelpoPrinter.printTicket({
|
|
304
|
+
* operatorName: 'City Bus Services',
|
|
305
|
+
* route: 'Route 42 — City Centre → Airport',
|
|
306
|
+
* passenger: 'John Doe',
|
|
307
|
+
* ticketId: 'TKT-001234',
|
|
308
|
+
* fare: 'SLE 5.00',
|
|
309
|
+
* footer: 'Thank you for travelling with us!',
|
|
310
|
+
* });
|
|
311
|
+
*/
|
|
312
|
+
async printTicket(
|
|
313
|
+
ticket: PrintTicket,
|
|
314
|
+
options: PrintReceiptOptions = {}
|
|
315
|
+
): Promise<TelpoResult<void>> {
|
|
316
|
+
if (!_activePrinterType) {
|
|
317
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'Not connected. Call TelpoPrinter.connect() first.'));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
const printer = _getPrinter();
|
|
322
|
+
const dateTime = ticket.dateTime ?? nowString();
|
|
323
|
+
const qrValue = ticket.qrValue ?? ticket.ticketId;
|
|
324
|
+
|
|
325
|
+
// Build ESC/POS string
|
|
326
|
+
let out = '';
|
|
327
|
+
const C = COMMANDS.TEXT_FORMAT;
|
|
328
|
+
const P = COMMANDS.PAPER;
|
|
329
|
+
|
|
330
|
+
const centerBoldLarge = `${C.TXT_ALIGN_CT}${C.TXT_CUSTOM_SIZE(2, 2)}${C.TXT_BOLD_ON}`;
|
|
331
|
+
const centerNormal = `${C.TXT_ALIGN_CT}${C.TXT_NORMAL}`;
|
|
332
|
+
const leftNormal = `${C.TXT_ALIGN_LT}${C.TXT_NORMAL}`;
|
|
333
|
+
const reset = C.TXT_NORMAL;
|
|
334
|
+
|
|
335
|
+
// ── Header ──
|
|
336
|
+
if (ticket.operatorName) {
|
|
337
|
+
out += `${centerBoldLarge}${ticket.operatorName}${reset}\n`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
out += `${centerNormal}BUS TICKET\n`;
|
|
341
|
+
out += `${leftNormal}${THICK_DIVIDER}\n`;
|
|
342
|
+
|
|
343
|
+
// ── Route ──
|
|
344
|
+
if (ticket.route) {
|
|
345
|
+
out += `${leftNormal}${ticket.route}\n`;
|
|
346
|
+
out += `${leftNormal}${DIVIDER}\n`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ── Details ──
|
|
350
|
+
if (ticket.passenger) {
|
|
351
|
+
out += `${leftNormal}${twoCol('Passenger:', ticket.passenger)}\n`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
out += `${leftNormal}${twoCol('Date/Time:', dateTime)}\n`;
|
|
355
|
+
out += `${leftNormal}${twoCol('Ticket ID:', ticket.ticketId)}\n`;
|
|
356
|
+
|
|
357
|
+
if (ticket.fare) {
|
|
358
|
+
out += `${leftNormal}${twoCol('Fare:', ticket.fare)}\n`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ── Extras ──
|
|
362
|
+
if (ticket.extras) {
|
|
363
|
+
for (const [key, value] of Object.entries(ticket.extras)) {
|
|
364
|
+
out += `${leftNormal}${twoCol(`${key}:`, value)}\n`;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
out += `${leftNormal}${THICK_DIVIDER}\n`;
|
|
369
|
+
|
|
370
|
+
// ── QR Code ──
|
|
371
|
+
out += `${centerNormal}`;
|
|
372
|
+
out += COMMANDS.BARCODE?.QR_CODE_256?.(qrValue) ?? '';
|
|
373
|
+
out += '\n';
|
|
374
|
+
|
|
375
|
+
// ── Footer ──
|
|
376
|
+
if (ticket.footer) {
|
|
377
|
+
out += `${centerNormal}${ticket.footer}\n`;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ── Feed + Cut ──
|
|
381
|
+
const feedLines = options.feedLines ?? 4;
|
|
382
|
+
out += '\n'.repeat(feedLines);
|
|
383
|
+
if (options.cutPaper !== false) {
|
|
384
|
+
out += P.PAPER_FULL_CUT;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
await printer.printText(out);
|
|
388
|
+
return wrapSuccess(undefined);
|
|
389
|
+
} catch (e) {
|
|
390
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_PRINT_FAILED, 'Failed to print ticket', e));
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Print raw ESC/POS text. Use when you need full manual control.
|
|
396
|
+
*/
|
|
397
|
+
async printRaw(rawText: string): Promise<TelpoResult<void>> {
|
|
398
|
+
if (!_activePrinterType) {
|
|
399
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'Not connected. Call TelpoPrinter.connect() first.'));
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
await _getPrinter().printText(rawText);
|
|
403
|
+
return wrapSuccess(undefined);
|
|
404
|
+
} catch (e) {
|
|
405
|
+
return wrapError(buildError(TelpoErrorCode.PRINTER_PRINT_FAILED, 'Failed to print raw text', e));
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
/** Feed paper by N lines without printing. */
|
|
410
|
+
async feedPaper(lines = 3): Promise<TelpoResult<void>> {
|
|
411
|
+
return TelpoPrinter.printRaw('\n'.repeat(lines));
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
/** Whether the printer is currently connected. */
|
|
415
|
+
isConnected(): boolean {
|
|
416
|
+
return _activePrinterType !== null;
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
/** Returns the current connection type, or null if not connected. */
|
|
420
|
+
getConnectionType(): ActivePrinterType {
|
|
421
|
+
return _activePrinterType;
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
function _getPrinter() {
|
|
426
|
+
if (_activePrinterType === 'USB') return USBPrinter;
|
|
427
|
+
if (_activePrinterType === 'BLUETOOTH') return BLEPrinter;
|
|
428
|
+
if (_activePrinterType === 'NETWORK') return NetPrinter;
|
|
429
|
+
throw buildError(TelpoErrorCode.PRINTER_NOT_CONNECTED, 'No active printer connection.');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export type { PrinterDevice, PrinterOptions, PrintLine, PrintTicket, PrintReceiptOptions };
|