/** * Zebra 프린터 Web Bluetooth LE 연동 * Chrome/Edge (Chromium) 에서만 지원. BLE로 ZPL 전송 (512바이트 청크) * 참고: https://developer.zebra.com/content/printing-webapp-using-webbluetooth */ const ZEBRA_BLE_SERVICE_UUID = "38eb4a80-c570-11e3-9507-0002a5d5c51b"; const ZEBRA_BLE_CHAR_UUID = "38eb4a82-c570-11e3-9507-0002a5d5c51b"; const CHUNK_SIZE = 512; const CHUNK_DELAY_MS = 20; export function isWebBluetoothSupported(): boolean { if (typeof window === "undefined") return false; return !!(navigator.bluetooth && navigator.bluetooth.requestDevice); } /** 지원 브라우저 안내 문구 */ export function getUnsupportedMessage(): string { if (!isWebBluetoothSupported()) { return "이 브라우저는 Web Bluetooth를 지원하지 않습니다. Chrome 또는 Edge(Chromium)에서 열어주세요. HTTPS 또는 localhost 필요."; } return ""; } export interface ZebraPrintResult { success: boolean; message: string; } /** * Zebra 프린터를 BLE로 선택·연결 후 ZPL 데이터 전송 * - 사용자에게 블루투스 기기 선택 창이 뜸 (Zebra 프린터 BLE 선택) * - ZPL을 512바이트 단위로 나누어 순차 전송 */ export async function printZPLToZebraBLE(zpl: string): Promise { if (!isWebBluetoothSupported()) { return { success: false, message: "Web Bluetooth를 지원하지 않는 브라우저입니다. Chrome 또는 Edge에서 시도해주세요.", }; } let device: BluetoothDevice | null = null; let server: BluetoothRemoteGATTServer | null = null; try { // 1) 서비스 UUID로만 필터 시 Android에서 Zebra가 광고하지 않으면 목록에 안 나옴. // 2) acceptAllDevices + optionalServices 로 모든 BLE 기기 표시 후, 연결해 Zebra 서비스 사용. const useAcceptAll = typeof navigator !== "undefined" && /Android/i.test(navigator.userAgent); if (useAcceptAll) { device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true, optionalServices: [ZEBRA_BLE_SERVICE_UUID], }); } else { device = await navigator.bluetooth.requestDevice({ filters: [{ services: [ZEBRA_BLE_SERVICE_UUID] }], optionalServices: [ZEBRA_BLE_SERVICE_UUID], }); } if (!device) { return { success: false, message: "프린터를 선택하지 않았습니다." }; } server = await device.gatt!.connect(); let service: BluetoothRemoteGATTService; try { service = await server.getPrimaryService(ZEBRA_BLE_SERVICE_UUID); } catch { return { success: false, message: "선택한 기기는 Zebra 프린터가 아니거나 BLE 인쇄를 지원하지 않습니다. 'ZD421' 등 Zebra 프린터를 선택해 주세요.", }; } const characteristic = await service.getCharacteristic(ZEBRA_BLE_CHAR_UUID); const encoder = new TextEncoder(); const data = encoder.encode(zpl); const totalChunks = Math.ceil(data.length / CHUNK_SIZE); for (let i = 0; i < totalChunks; i++) { const start = i * CHUNK_SIZE; const end = Math.min(start + CHUNK_SIZE, data.length); const chunk = data.slice(start, end); await characteristic.writeValue(chunk); if (i < totalChunks - 1 && CHUNK_DELAY_MS > 0) { await new Promise((r) => setTimeout(r, CHUNK_DELAY_MS)); } } return { success: true, message: "Zebra 프린터로 전송했습니다." }; } catch (err: unknown) { const e = err as Error & { name?: string }; if (e.name === "NotFoundError") { return { success: false, message: "Zebra 프린터(BLE)를 찾을 수 없습니다. 프린터 전원과 블루투스 설정을 확인하세요." }; } if (e.name === "NotAllowedError") { return { success: false, message: "블루투스 연결이 거부되었습니다." }; } return { success: false, message: e.message || "Zebra BLE 출력 중 오류가 발생했습니다.", }; } finally { if (server && device?.gatt?.connected) { try { device.gatt.disconnect(); } catch { // ignore } } } }