taro-bluetooth-print 2.2.0 → 2.3.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +128 -22
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.es.js +1 -6995
  5. package/dist/index.umd.js +1 -1
  6. package/dist/types/adapters/AdapterFactory.d.ts +0 -1
  7. package/dist/types/adapters/AlipayAdapter.d.ts +6 -34
  8. package/dist/types/adapters/BaiduAdapter.d.ts +6 -34
  9. package/dist/types/adapters/BaseAdapter.d.ts +112 -1
  10. package/dist/types/adapters/ByteDanceAdapter.d.ts +6 -34
  11. package/dist/types/adapters/TaroAdapter.d.ts +6 -34
  12. package/dist/types/adapters/WebBluetoothAdapter.d.ts +0 -1
  13. package/dist/types/config/PrinterConfig.d.ts +0 -1
  14. package/dist/types/core/BluetoothPrinter.d.ts +0 -1
  15. package/dist/types/drivers/EscPos.d.ts +0 -1
  16. package/dist/types/drivers/TsplDriver.d.ts +251 -0
  17. package/dist/types/encoding/gbk-data.d.ts +12 -0
  18. package/dist/types/encoding/gbk-table.d.ts +5 -1
  19. package/dist/types/index.d.ts +5 -0
  20. package/dist/types/plugins/PluginManager.d.ts +87 -0
  21. package/dist/types/plugins/builtin/LoggingPlugin.d.ts +14 -0
  22. package/dist/types/plugins/builtin/RetryPlugin.d.ts +18 -0
  23. package/dist/types/plugins/index.d.ts +7 -0
  24. package/dist/types/plugins/types.d.ts +97 -0
  25. package/dist/types/services/CommandBuilder.d.ts +6 -1
  26. package/dist/types/services/ConnectionManager.d.ts +0 -1
  27. package/dist/types/services/PrintJobManager.d.ts +6 -2
  28. package/dist/types/services/interfaces/index.d.ts +0 -1
  29. package/dist/types/template/TemplateEngine.d.ts +0 -1
  30. package/package.json +16 -18
  31. package/src/adapters/AlipayAdapter.ts +8 -314
  32. package/src/adapters/BaiduAdapter.ts +8 -312
  33. package/src/adapters/BaseAdapter.ts +366 -0
  34. package/src/adapters/ByteDanceAdapter.ts +8 -316
  35. package/src/adapters/TaroAdapter.ts +8 -367
  36. package/src/core/EventEmitter.ts +9 -6
  37. package/src/drivers/TsplDriver.ts +417 -0
  38. package/src/encoding/gbk-data.ts +1911 -0
  39. package/src/encoding/gbk-table.ts +22 -498
  40. package/src/index.ts +14 -0
  41. package/src/plugins/PluginManager.ts +193 -0
  42. package/src/plugins/builtin/LoggingPlugin.ts +97 -0
  43. package/src/plugins/builtin/RetryPlugin.ts +109 -0
  44. package/src/plugins/index.ts +10 -0
  45. package/src/plugins/types.ts +119 -0
  46. package/src/preview/PreviewRenderer.ts +7 -1
  47. package/src/queue/PrintQueue.ts +10 -6
  48. package/src/services/CommandBuilder.ts +30 -0
  49. package/src/services/PrintJobManager.ts +51 -35
@@ -3,75 +3,19 @@
3
3
  * Implements the IPrinterAdapter interface for Alipay Mini Program
4
4
  */
5
5
 
6
- import { IAdapterOptions, PrinterState } from '@/types';
7
- import { BaseAdapter } from './BaseAdapter';
8
- import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
6
+ import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
9
7
 
10
8
  // Declare Alipay global for TypeScript
11
- interface AlipayBLEService {
12
- uuid: string;
13
- }
14
-
15
- interface AlipayBLECharacteristicProperties {
16
- write?: boolean;
17
- writeWithoutResponse?: boolean;
18
- read?: boolean;
19
- notify?: boolean;
20
- indicate?: boolean;
21
- }
22
-
23
- interface AlipayBLECharacteristic {
24
- uuid: string;
25
- properties: AlipayBLECharacteristicProperties;
26
- }
27
-
28
- interface AlipayBLEDeviceServicesResult {
29
- services: AlipayBLEService[];
30
- }
31
-
32
- interface AlipayBLEDeviceCharacteristicsResult {
33
- characteristics: AlipayBLECharacteristic[];
34
- }
35
-
36
- interface AlipayBLEConnectionStateResult {
37
- connected: boolean;
38
- }
39
-
40
- interface AlipayBLEConnectionOptions {
41
- deviceId: string;
42
- }
43
-
44
- interface AlipayBLEWriteOptions {
45
- deviceId: string;
46
- serviceId: string;
47
- characteristicId: string;
48
- value: ArrayBuffer;
49
- }
50
-
51
- interface AlipayBLEConnectionStateChangeCallback {
52
- (res: { deviceId: string; connected: boolean }): void;
53
- }
54
-
55
- interface AlipayGlobal {
56
- createBLEConnection(options: AlipayBLEConnectionOptions): Promise<void>;
57
- closeBLEConnection(options: AlipayBLEConnectionOptions): Promise<void>;
58
- getBLEConnectionState(
59
- options: AlipayBLEConnectionOptions
60
- ): Promise<AlipayBLEConnectionStateResult>;
61
- writeBLECharacteristicValue(options: AlipayBLEWriteOptions): Promise<void>;
62
- getBLEDeviceServices(options: AlipayBLEConnectionOptions): Promise<AlipayBLEDeviceServicesResult>;
63
- getBLEDeviceCharacteristics(options: {
64
- deviceId: string;
65
- serviceId: string;
66
- }): Promise<AlipayBLEDeviceCharacteristicsResult>;
67
- onBLEConnectionStateChange(callback: AlipayBLEConnectionStateChangeCallback): void;
68
- }
9
+ interface AlipayGlobal extends MiniProgramBLEApi {}
69
10
 
70
11
  declare const my: AlipayGlobal;
71
12
 
72
13
  /**
73
14
  * Alipay Bluetooth Low Energy adapter
74
15
  *
16
+ * Uses the Alipay mini-program's BLE APIs (my.xxx).
17
+ * All connection, write, and service discovery logic is inherited from MiniProgramAdapter.
18
+ *
75
19
  * @example
76
20
  * ```typescript
77
21
  * const adapter = new AlipayAdapter();
@@ -80,258 +24,8 @@ declare const my: AlipayGlobal;
80
24
  * await adapter.disconnect('device-id-123');
81
25
  * ```
82
26
  */
83
- export class AlipayAdapter extends BaseAdapter {
84
- /**
85
- * Connects to a Bluetooth device and discovers services
86
- *
87
- * @param deviceId - Bluetooth device ID
88
- * @throws {BluetoothPrintError} When connection fails
89
- */
90
- async connect(deviceId: string): Promise<void> {
91
- this.validateDeviceId(deviceId);
92
-
93
- // Check if already connected
94
- if (this.isDeviceConnected(deviceId)) {
95
- this.logger.warn('Device already connected:', deviceId);
96
- this.updateState(PrinterState.CONNECTED);
97
- return;
98
- }
99
-
100
- this.updateState(PrinterState.CONNECTING);
101
- this.logger.debug('Connecting to device:', deviceId);
102
-
103
- try {
104
- // Add connection timeout handling
105
- let timeoutId: NodeJS.Timeout | undefined;
106
- const connectionPromise = my.createBLEConnection({
107
- deviceId,
108
- });
109
-
110
- const timeoutPromise = new Promise((_, reject) => {
111
- timeoutId = setTimeout(() => {
112
- reject(new Error('Connection timeout after 10 seconds'));
113
- }, 10000);
114
- });
115
-
116
- await Promise.race([connectionPromise, timeoutPromise]);
117
- if (timeoutId) {
118
- clearTimeout(timeoutId); // Clear timeout after successful connection
119
- }
120
- this.logger.info('BLE connection established');
121
-
122
- // Discover and cache services
123
- await this.discoverServices(deviceId);
124
-
125
- this.updateState(PrinterState.CONNECTED);
126
- this.logger.info('Device connected successfully');
127
-
128
- // Listen for connection state changes
129
- my.onBLEConnectionStateChange((res: { deviceId: string; connected: boolean }) => {
130
- if (res.deviceId === deviceId && !res.connected) {
131
- this.logger.warn('Device disconnected unexpectedly');
132
- this.updateState(PrinterState.DISCONNECTED);
133
- this.cleanupDevice(deviceId);
134
- }
135
- });
136
- } catch (error) {
137
- this.updateState(PrinterState.DISCONNECTED);
138
- this.logger.error('Connection failed:', error);
139
-
140
- // Return more specific error codes based on error message
141
- const errorMessage = (error as Error).message || '';
142
- if (errorMessage.includes('timeout')) {
143
- throw new BluetoothPrintError(
144
- ErrorCode.CONNECTION_TIMEOUT,
145
- `Connection to device ${deviceId} timed out`,
146
- error as Error
147
- );
148
- } else if (errorMessage.includes('not found')) {
149
- throw new BluetoothPrintError(
150
- ErrorCode.DEVICE_NOT_FOUND,
151
- `Device ${deviceId} not found`,
152
- error as Error
153
- );
154
- }
155
-
156
- throw new BluetoothPrintError(
157
- ErrorCode.CONNECTION_FAILED,
158
- `Failed to connect to device ${deviceId}`,
159
- error as Error
160
- );
161
- }
162
- }
163
-
164
- /**
165
- * Disconnects from a Bluetooth device
166
- *
167
- * @param deviceId - Bluetooth device ID
168
- */
169
- async disconnect(deviceId: string): Promise<void> {
170
- this.validateDeviceId(deviceId);
171
- this.updateState(PrinterState.DISCONNECTING);
172
- this.logger.debug('Disconnecting from device:', deviceId);
173
-
174
- try {
175
- await my.closeBLEConnection({
176
- deviceId,
177
- });
178
- this.cleanupDevice(deviceId);
179
- this.updateState(PrinterState.DISCONNECTED);
180
- this.logger.info('Device disconnected successfully');
181
- } catch (error) {
182
- // Ignore error on disconnect, but log it
183
- this.logger.warn('Disconnect error (ignored):', error);
184
- this.cleanupDevice(deviceId);
185
- this.updateState(PrinterState.DISCONNECTED);
186
- }
187
- }
188
-
189
- /**
190
- * Writes data to the Bluetooth device in chunks with retry logic
191
- *
192
- * @param deviceId - Bluetooth device ID
193
- * @param buffer - Data to write as ArrayBuffer
194
- * @param options - Write options (chunk size, delay, retries)
195
- * @throws {BluetoothPrintError} When write fails after retries
196
- */
197
- async write(deviceId: string, buffer: ArrayBuffer, options?: IAdapterOptions): Promise<void> {
198
- this.validateDeviceId(deviceId);
199
- this.validateBuffer(buffer);
200
- const serviceInfo = this.getServiceInfo(deviceId);
201
- const validatedOptions = this.validateOptions(options);
202
-
203
- // Validate device is still connected
204
- try {
205
- const state = await my.getBLEConnectionState({
206
- deviceId,
207
- });
208
- if (!state.connected) {
209
- this.cleanupDevice(deviceId);
210
- throw new BluetoothPrintError(ErrorCode.DEVICE_DISCONNECTED, 'Device disconnected');
211
- }
212
- } catch (error) {
213
- this.cleanupDevice(deviceId);
214
- throw new BluetoothPrintError(
215
- ErrorCode.DEVICE_DISCONNECTED,
216
- 'Device disconnected',
217
- error as Error
218
- );
219
- }
220
-
221
- const { chunkSize, delay, retries } = validatedOptions;
222
- const data = new Uint8Array(buffer);
223
- const totalChunks = Math.ceil(data.length / chunkSize);
224
-
225
- this.logger.debug(`Writing ${data.length} bytes in ${totalChunks} chunks`);
226
-
227
- // If no data to write, return directly
228
- if (data.length === 0) {
229
- this.logger.warn('No data to write');
230
- return;
231
- }
232
-
233
- for (let i = 0; i < data.length; i += chunkSize) {
234
- const chunk = data.slice(i, i + chunkSize);
235
- const chunkNum = Math.floor(i / chunkSize) + 1;
236
-
237
- let attempt = 0;
238
- while (attempt <= retries) {
239
- try {
240
- // Add write timeout handling
241
- const writePromise = my.writeBLECharacteristicValue({
242
- deviceId,
243
- serviceId: serviceInfo.serviceId,
244
- characteristicId: serviceInfo.characteristicId,
245
- value: chunk.buffer,
246
- });
247
-
248
- const timeoutPromise = new Promise((_, reject) => {
249
- setTimeout(() => {
250
- reject(new Error('Write timeout after 5 seconds'));
251
- }, 5000);
252
- });
253
-
254
- await Promise.race([writePromise, timeoutPromise]);
255
-
256
- this.logger.debug(`Chunk ${chunkNum}/${totalChunks} written successfully`);
257
- break; // Success
258
- } catch (error) {
259
- attempt++;
260
- if (attempt > retries) {
261
- this.logger.error(`Chunk ${chunkNum} failed after ${retries} retries`);
262
- throw new BluetoothPrintError(
263
- ErrorCode.WRITE_FAILED,
264
- `Failed to write chunk ${chunkNum}/${totalChunks}`,
265
- error as Error
266
- );
267
- }
268
- this.logger.warn(`Chunk ${chunkNum} write failed, retry ${attempt}/${retries}`);
269
- // Wait before retry
270
- await new Promise(r => setTimeout(r, delay * 2));
271
- }
272
- }
273
-
274
- // Small delay to prevent congestion
275
- if (i + chunkSize < data.length) {
276
- await new Promise(r => setTimeout(r, delay));
277
- }
278
- }
279
-
280
- this.logger.info(`Successfully wrote ${data.length} bytes`);
281
- }
282
-
283
- /**
284
- * Discovers services and characteristics for a device
285
- * Caches the writeable characteristic for future writes
286
- *
287
- * @param deviceId - Bluetooth device ID
288
- * @throws {BluetoothPrintError} When no writeable characteristic is found
289
- */
290
- private async discoverServices(deviceId: string): Promise<void> {
291
- this.logger.debug('Discovering services for device:', deviceId);
292
-
293
- try {
294
- const services = await my.getBLEDeviceServices({
295
- deviceId,
296
- });
297
-
298
- for (const service of services.services) {
299
- const chars = await my.getBLEDeviceCharacteristics({
300
- deviceId,
301
- serviceId: service.uuid,
302
- });
303
-
304
- const writeChar = chars.characteristics.find(
305
- (c: AlipayBLECharacteristic) => c.properties.write || c.properties.writeWithoutResponse
306
- );
307
-
308
- if (writeChar) {
309
- this.serviceCache.set(deviceId, {
310
- serviceId: service.uuid,
311
- characteristicId: writeChar.uuid,
312
- });
313
- this.logger.info('Found writeable characteristic:', {
314
- service: service.uuid,
315
- characteristic: writeChar.uuid,
316
- });
317
- return;
318
- }
319
- }
320
-
321
- // No writeable characteristic found
322
- throw new BluetoothPrintError(
323
- ErrorCode.CHARACTERISTIC_NOT_FOUND,
324
- 'No writeable characteristic found. Make sure the device is a supported printer.'
325
- );
326
- } catch (error) {
327
- if (error instanceof BluetoothPrintError) {
328
- throw error;
329
- }
330
- throw new BluetoothPrintError(
331
- ErrorCode.SERVICE_DISCOVERY_FAILED,
332
- 'Failed to discover device services',
333
- error as Error
334
- );
335
- }
27
+ export class AlipayAdapter extends MiniProgramAdapter {
28
+ protected getApi(): MiniProgramBLEApi {
29
+ return my;
336
30
  }
337
31
  }
@@ -3,73 +3,19 @@
3
3
  * Implements the IPrinterAdapter interface for Baidu Smart Program
4
4
  */
5
5
 
6
- import { IAdapterOptions, PrinterState } from '@/types';
7
- import { BaseAdapter } from './BaseAdapter';
8
- import { BluetoothPrintError, ErrorCode } from '@/errors/BluetoothError';
6
+ import { MiniProgramAdapter, MiniProgramBLEApi } from './BaseAdapter';
9
7
 
10
8
  // Declare Baidu global for TypeScript
11
- interface BaiduBLEService {
12
- uuid: string;
13
- }
14
-
15
- interface BaiduBLECharacteristicProperties {
16
- write?: boolean;
17
- writeWithoutResponse?: boolean;
18
- read?: boolean;
19
- notify?: boolean;
20
- indicate?: boolean;
21
- }
22
-
23
- interface BaiduBLECharacteristic {
24
- uuid: string;
25
- properties: BaiduBLECharacteristicProperties;
26
- }
27
-
28
- interface BaiduBLEDeviceServicesResult {
29
- services: BaiduBLEService[];
30
- }
31
-
32
- interface BaiduBLEDeviceCharacteristicsResult {
33
- characteristics: BaiduBLECharacteristic[];
34
- }
35
-
36
- interface BaiduBLEConnectionStateResult {
37
- connected: boolean;
38
- }
39
-
40
- interface BaiduBLEConnectionOptions {
41
- deviceId: string;
42
- }
43
-
44
- interface BaiduBLEWriteOptions {
45
- deviceId: string;
46
- serviceId: string;
47
- characteristicId: string;
48
- value: ArrayBuffer;
49
- }
50
-
51
- interface BaiduBLEConnectionStateChangeCallback {
52
- (res: { deviceId: string; connected: boolean }): void;
53
- }
54
-
55
- interface BaiduGlobal {
56
- createBLEConnection(options: BaiduBLEConnectionOptions): Promise<void>;
57
- closeBLEConnection(options: BaiduBLEConnectionOptions): Promise<void>;
58
- getBLEConnectionState(options: BaiduBLEConnectionOptions): Promise<BaiduBLEConnectionStateResult>;
59
- writeBLECharacteristicValue(options: BaiduBLEWriteOptions): Promise<void>;
60
- getBLEDeviceServices(options: BaiduBLEConnectionOptions): Promise<BaiduBLEDeviceServicesResult>;
61
- getBLEDeviceCharacteristics(options: {
62
- deviceId: string;
63
- serviceId: string;
64
- }): Promise<BaiduBLEDeviceCharacteristicsResult>;
65
- onBLEConnectionStateChange(callback: BaiduBLEConnectionStateChangeCallback): void;
66
- }
9
+ interface BaiduGlobal extends MiniProgramBLEApi {}
67
10
 
68
11
  declare const swan: BaiduGlobal;
69
12
 
70
13
  /**
71
14
  * Baidu Bluetooth Low Energy adapter
72
15
  *
16
+ * Uses the Baidu smart program's BLE APIs (swan.xxx).
17
+ * All connection, write, and service discovery logic is inherited from MiniProgramAdapter.
18
+ *
73
19
  * @example
74
20
  * ```typescript
75
21
  * const adapter = new BaiduAdapter();
@@ -78,258 +24,8 @@ declare const swan: BaiduGlobal;
78
24
  * await adapter.disconnect('device-id-123');
79
25
  * ```
80
26
  */
81
- export class BaiduAdapter extends BaseAdapter {
82
- /**
83
- * Connects to a Bluetooth device and discovers services
84
- *
85
- * @param deviceId - Bluetooth device ID
86
- * @throws {BluetoothPrintError} When connection fails
87
- */
88
- async connect(deviceId: string): Promise<void> {
89
- this.validateDeviceId(deviceId);
90
-
91
- // Check if already connected
92
- if (this.isDeviceConnected(deviceId)) {
93
- this.logger.warn('Device already connected:', deviceId);
94
- this.updateState(PrinterState.CONNECTED);
95
- return;
96
- }
97
-
98
- this.updateState(PrinterState.CONNECTING);
99
- this.logger.debug('Connecting to device:', deviceId);
100
-
101
- try {
102
- // Add connection timeout handling
103
- let timeoutId: NodeJS.Timeout | undefined;
104
- const connectionPromise = swan.createBLEConnection({
105
- deviceId,
106
- });
107
-
108
- const timeoutPromise = new Promise((_, reject) => {
109
- timeoutId = setTimeout(() => {
110
- reject(new Error('Connection timeout after 10 seconds'));
111
- }, 10000);
112
- });
113
-
114
- await Promise.race([connectionPromise, timeoutPromise]);
115
- if (timeoutId) {
116
- clearTimeout(timeoutId); // Clear timeout after successful connection
117
- }
118
- this.logger.info('BLE connection established');
119
-
120
- // Discover and cache services
121
- await this.discoverServices(deviceId);
122
-
123
- this.updateState(PrinterState.CONNECTED);
124
- this.logger.info('Device connected successfully');
125
-
126
- // Listen for connection state changes
127
- swan.onBLEConnectionStateChange((res: { deviceId: string; connected: boolean }) => {
128
- if (res.deviceId === deviceId && !res.connected) {
129
- this.logger.warn('Device disconnected unexpectedly');
130
- this.updateState(PrinterState.DISCONNECTED);
131
- this.cleanupDevice(deviceId);
132
- }
133
- });
134
- } catch (error) {
135
- this.updateState(PrinterState.DISCONNECTED);
136
- this.logger.error('Connection failed:', error);
137
-
138
- // Return more specific error codes based on error message
139
- const errorMessage = (error as Error).message || '';
140
- if (errorMessage.includes('timeout')) {
141
- throw new BluetoothPrintError(
142
- ErrorCode.CONNECTION_TIMEOUT,
143
- `Connection to device ${deviceId} timed out`,
144
- error as Error
145
- );
146
- } else if (errorMessage.includes('not found')) {
147
- throw new BluetoothPrintError(
148
- ErrorCode.DEVICE_NOT_FOUND,
149
- `Device ${deviceId} not found`,
150
- error as Error
151
- );
152
- }
153
-
154
- throw new BluetoothPrintError(
155
- ErrorCode.CONNECTION_FAILED,
156
- `Failed to connect to device ${deviceId}`,
157
- error as Error
158
- );
159
- }
160
- }
161
-
162
- /**
163
- * Disconnects from a Bluetooth device
164
- *
165
- * @param deviceId - Bluetooth device ID
166
- */
167
- async disconnect(deviceId: string): Promise<void> {
168
- this.validateDeviceId(deviceId);
169
- this.updateState(PrinterState.DISCONNECTING);
170
- this.logger.debug('Disconnecting from device:', deviceId);
171
-
172
- try {
173
- await swan.closeBLEConnection({
174
- deviceId,
175
- });
176
- this.cleanupDevice(deviceId);
177
- this.updateState(PrinterState.DISCONNECTED);
178
- this.logger.info('Device disconnected successfully');
179
- } catch (error) {
180
- // Ignore error on disconnect, but log it
181
- this.logger.warn('Disconnect error (ignored):', error);
182
- this.cleanupDevice(deviceId);
183
- this.updateState(PrinterState.DISCONNECTED);
184
- }
185
- }
186
-
187
- /**
188
- * Writes data to the Bluetooth device in chunks with retry logic
189
- *
190
- * @param deviceId - Bluetooth device ID
191
- * @param buffer - Data to write as ArrayBuffer
192
- * @param options - Write options (chunk size, delay, retries)
193
- * @throws {BluetoothPrintError} When write fails after retries
194
- */
195
- async write(deviceId: string, buffer: ArrayBuffer, options?: IAdapterOptions): Promise<void> {
196
- this.validateDeviceId(deviceId);
197
- this.validateBuffer(buffer);
198
- const serviceInfo = this.getServiceInfo(deviceId);
199
- const validatedOptions = this.validateOptions(options);
200
-
201
- // Validate device is still connected
202
- try {
203
- const state = await swan.getBLEConnectionState({
204
- deviceId,
205
- });
206
- if (!state.connected) {
207
- this.cleanupDevice(deviceId);
208
- throw new BluetoothPrintError(ErrorCode.DEVICE_DISCONNECTED, 'Device disconnected');
209
- }
210
- } catch (error) {
211
- this.cleanupDevice(deviceId);
212
- throw new BluetoothPrintError(
213
- ErrorCode.DEVICE_DISCONNECTED,
214
- 'Device disconnected',
215
- error as Error
216
- );
217
- }
218
-
219
- const { chunkSize, delay, retries } = validatedOptions;
220
- const data = new Uint8Array(buffer);
221
- const totalChunks = Math.ceil(data.length / chunkSize);
222
-
223
- this.logger.debug(`Writing ${data.length} bytes in ${totalChunks} chunks`);
224
-
225
- // If no data to write, return directly
226
- if (data.length === 0) {
227
- this.logger.warn('No data to write');
228
- return;
229
- }
230
-
231
- for (let i = 0; i < data.length; i += chunkSize) {
232
- const chunk = data.slice(i, i + chunkSize);
233
- const chunkNum = Math.floor(i / chunkSize) + 1;
234
-
235
- let attempt = 0;
236
- while (attempt <= retries) {
237
- try {
238
- // Add write timeout handling
239
- const writePromise = swan.writeBLECharacteristicValue({
240
- deviceId,
241
- serviceId: serviceInfo.serviceId,
242
- characteristicId: serviceInfo.characteristicId,
243
- value: chunk.buffer,
244
- });
245
-
246
- const timeoutPromise = new Promise((_, reject) => {
247
- setTimeout(() => {
248
- reject(new Error('Write timeout after 5 seconds'));
249
- }, 5000);
250
- });
251
-
252
- await Promise.race([writePromise, timeoutPromise]);
253
-
254
- this.logger.debug(`Chunk ${chunkNum}/${totalChunks} written successfully`);
255
- break; // Success
256
- } catch (error) {
257
- attempt++;
258
- if (attempt > retries) {
259
- this.logger.error(`Chunk ${chunkNum} failed after ${retries} retries`);
260
- throw new BluetoothPrintError(
261
- ErrorCode.WRITE_FAILED,
262
- `Failed to write chunk ${chunkNum}/${totalChunks}`,
263
- error as Error
264
- );
265
- }
266
- this.logger.warn(`Chunk ${chunkNum} write failed, retry ${attempt}/${retries}`);
267
- // Wait before retry
268
- await new Promise(r => setTimeout(r, delay * 2));
269
- }
270
- }
271
-
272
- // Small delay to prevent congestion
273
- if (i + chunkSize < data.length) {
274
- await new Promise(r => setTimeout(r, delay));
275
- }
276
- }
277
-
278
- this.logger.info(`Successfully wrote ${data.length} bytes`);
279
- }
280
-
281
- /**
282
- * Discovers services and characteristics for a device
283
- * Caches the writeable characteristic for future writes
284
- *
285
- * @param deviceId - Bluetooth device ID
286
- * @throws {BluetoothPrintError} When no writeable characteristic is found
287
- */
288
- private async discoverServices(deviceId: string): Promise<void> {
289
- this.logger.debug('Discovering services for device:', deviceId);
290
-
291
- try {
292
- const services = await swan.getBLEDeviceServices({
293
- deviceId,
294
- });
295
-
296
- for (const service of services.services) {
297
- const chars = await swan.getBLEDeviceCharacteristics({
298
- deviceId,
299
- serviceId: service.uuid,
300
- });
301
-
302
- const writeChar = chars.characteristics.find(
303
- (c: BaiduBLECharacteristic) => c.properties.write || c.properties.writeWithoutResponse
304
- );
305
-
306
- if (writeChar) {
307
- this.serviceCache.set(deviceId, {
308
- serviceId: service.uuid,
309
- characteristicId: writeChar.uuid,
310
- });
311
- this.logger.info('Found writeable characteristic:', {
312
- service: service.uuid,
313
- characteristic: writeChar.uuid,
314
- });
315
- return;
316
- }
317
- }
318
-
319
- // No writeable characteristic found
320
- throw new BluetoothPrintError(
321
- ErrorCode.CHARACTERISTIC_NOT_FOUND,
322
- 'No writeable characteristic found. Make sure the device is a supported printer.'
323
- );
324
- } catch (error) {
325
- if (error instanceof BluetoothPrintError) {
326
- throw error;
327
- }
328
- throw new BluetoothPrintError(
329
- ErrorCode.SERVICE_DISCOVERY_FAILED,
330
- 'Failed to discover device services',
331
- error as Error
332
- );
333
- }
27
+ export class BaiduAdapter extends MiniProgramAdapter {
28
+ protected getApi(): MiniProgramBLEApi {
29
+ return swan;
334
30
  }
335
31
  }