@nitra/zebra 6.1.3 → 6.1.5

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.
@@ -0,0 +1,404 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import CoreBluetooth
4
+
5
+ @objc(ZebraPrinterPlugin)
6
+ public class ZebraPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeripheralDelegate {
7
+ private var centralManager: CBCentralManager!
8
+ private var connectedPeripheral: CBPeripheral?
9
+ private var printerService: CBService?
10
+ private var printerCharacteristic: CBCharacteristic?
11
+ private var discoveredPeripherals: [CBPeripheral] = [] // Store discovered printers
12
+
13
+ override public func load() {
14
+ super.load()
15
+ centralManager = CBCentralManager(delegate: self, queue: nil)
16
+ }
17
+
18
+ // MARK: - CBCentralManagerDelegate
19
+ public func centralManagerDidUpdateState(_ central: CBCentralManager) {
20
+ switch central.state {
21
+ case .poweredOn:
22
+ print("✅ Bluetooth is ON")
23
+ // Automatically start scanning when Bluetooth is turned on
24
+ print("🔍 Auto-starting scan...")
25
+ central.scanForPeripherals(withServices: nil, options: nil)
26
+ case .poweredOff:
27
+ print("❌ Bluetooth is OFF")
28
+ case .unauthorized:
29
+ print("❌ Bluetooth unauthorized")
30
+ case .unsupported:
31
+ print("❌ Bluetooth unsupported")
32
+ case .unknown:
33
+ print("❌ Bluetooth state unknown")
34
+ case .resetting:
35
+ print("🔄 Bluetooth is resetting")
36
+ @unknown default:
37
+ print("❌ Unknown Bluetooth state: \(central.state)")
38
+ }
39
+ }
40
+
41
+ public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
42
+ print("📱 Found device: \(peripheral.name ?? "Unknown")")
43
+
44
+ // Auto-connect to Zebra printer
45
+ if peripheral.name?.contains("Zebra") == true ||
46
+ peripheral.name?.contains("ZT") == true ||
47
+ peripheral.name?.contains("XXZHN") == true { // Add specific printer ID
48
+ print("🖨️ Found Zebra printer: \(peripheral.name ?? "Unknown")")
49
+
50
+ // Store the printer so we keep a reference
51
+ if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
52
+ discoveredPeripherals.append(peripheral)
53
+ }
54
+
55
+ print("🔌 Auto-connecting to printer...")
56
+ central.connect(peripheral, options: nil)
57
+ }
58
+ }
59
+
60
+ public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
61
+ print("✅ Connected to: \(peripheral.name ?? "Unknown")")
62
+ connectedPeripheral = peripheral
63
+ peripheral.delegate = self
64
+ peripheral.discoverServices(nil)
65
+
66
+ // Store the printer as the connected one
67
+ if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
68
+ discoveredPeripherals.append(peripheral)
69
+ }
70
+ }
71
+
72
+ public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
73
+ print("❌ Failed to connect: \(error?.localizedDescription ?? "Unknown error")")
74
+ }
75
+
76
+ public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
77
+ print("🔌 Disconnected from: \(peripheral.name ?? "Unknown")")
78
+ connectedPeripheral = nil
79
+ printerService = nil
80
+ printerCharacteristic = nil
81
+ }
82
+
83
+ // MARK: - CBPeripheralDelegate
84
+ public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
85
+ guard error == nil else {
86
+ print("❌ Service discovery error: \(error!.localizedDescription)")
87
+ return
88
+ }
89
+
90
+ for service in peripheral.services ?? [] {
91
+ print("🔍 Found service: \(service.uuid)")
92
+ peripheral.discoverCharacteristics(nil, for: service)
93
+ }
94
+ }
95
+
96
+ public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
97
+ guard error == nil else {
98
+ print("❌ Characteristic discovery error: \(error!.localizedDescription)")
99
+ return
100
+ }
101
+
102
+ for characteristic in service.characteristics ?? [] {
103
+ print("🔍 Found characteristic: \(characteristic.uuid)")
104
+
105
+ // Choose the correct characteristic - usually the one that has the "write" property
106
+ if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {
107
+ // If this is the first characteristic with a write property, use it
108
+ if printerCharacteristic == nil {
109
+ printerService = service
110
+ printerCharacteristic = characteristic
111
+ print("✅ Selected printer characteristic: \(characteristic.uuid)")
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ // MARK: - Plugin Methods
118
+ @objc public override func checkPermissions(_ call: CAPPluginCall) {
119
+ print("🔍 Checking iOS Bluetooth permissions...")
120
+
121
+ let bluetoothState = centralManager.state
122
+ let hasBluetooth = bluetoothState == .poweredOn
123
+ let isAuthorized = bluetoothState != .unauthorized
124
+ let isSupported = bluetoothState != .unsupported
125
+
126
+ call.resolve([
127
+ "hasPermissions": hasBluetooth && isAuthorized,
128
+ "bluetoothSupported": isSupported,
129
+ "bleSupported": true, // iOS always supports BLE
130
+ "bluetoothState": bluetoothState.rawValue,
131
+ "missingPermissions": isAuthorized ? [] : ["NSBluetoothAlwaysUsageDescription"]
132
+ ])
133
+ }
134
+
135
+ @objc public override func requestPermissions(_ call: CAPPluginCall) {
136
+ print("🔐 Requesting iOS Bluetooth permissions...")
137
+
138
+ // On iOS, permissions are handled through Info.plist
139
+ // The actual permission request happens when the user first tries to use Bluetooth
140
+ let bluetoothState = centralManager.state
141
+
142
+ if bluetoothState == .unauthorized {
143
+ call.reject("Bluetooth unauthorized", "Please allow Bluetooth access in Settings > Privacy & Security > Bluetooth")
144
+ } else if bluetoothState == .poweredOff {
145
+ call.reject("Bluetooth is OFF", "Please turn on Bluetooth in Settings")
146
+ } else if bluetoothState == .poweredOn {
147
+ call.resolve([
148
+ "success": true,
149
+ "message": "Bluetooth permissions granted"
150
+ ])
151
+ } else {
152
+ call.reject("Bluetooth not ready", "Bluetooth state: \(bluetoothState.rawValue)")
153
+ }
154
+ }
155
+
156
+ @objc func echo(_ call: CAPPluginCall) {
157
+ let value = call.getString("value") ?? ""
158
+ call.resolve([
159
+ "value": value
160
+ ])
161
+ }
162
+
163
+ // MARK: - Print text (FIXED - sends RAW ZPL)
164
+ @objc func printText(_ call: CAPPluginCall) {
165
+ let text = call.getString("text") ?? ""
166
+ let fontSize = call.getInt("fontSize") ?? 12
167
+ let bold = call.getBool("bold") ?? false
168
+ let alignment = call.getString("alignment") ?? "left"
169
+
170
+ print("Printing text: \(text)")
171
+
172
+ // Check printer status first
173
+ guard let peripheral = connectedPeripheral,
174
+ let characteristic = printerCharacteristic else {
175
+ call.reject("Print failed", "Not connected to printer")
176
+ return
177
+ }
178
+
179
+ // CRITICAL FIX: Check if it's already a ZPL command
180
+ var dataToSend: String
181
+ if text.hasPrefix("^XA") || text.hasPrefix("~") || text.hasPrefix("!") {
182
+ // It's already a ZPL/CPCL command - send it RAW
183
+ print("Sending RAW ZPL/CPCL command...")
184
+ dataToSend = text
185
+ } else {
186
+ // It's plain text - create ZPL wrapper with proper formatting
187
+ print("Converting plain text to ZPL...")
188
+ let x = alignment == "center" ? 200 : (alignment == "right" ? 350 : 50)
189
+ let fontStyle = bold ? "B" : "N"
190
+ // Improved ZPL command with proper line breaks and formatting
191
+ dataToSend = "^XA\n^FO\(x),50^A0\(fontStyle),\(fontSize),\(fontSize)^FD\(text)^FS\n^XZ"
192
+ }
193
+
194
+ print("📦 Final data to send: \(dataToSend)")
195
+
196
+ // Send to printer
197
+ if let data = dataToSend.data(using: .utf8) {
198
+ if sendDataToPrinter(data) {
199
+ print("✅ Successfully sent \(data.count) bytes")
200
+ call.resolve([
201
+ "success": true,
202
+ "message": "Data sent to printer",
203
+ "zpl": dataToSend,
204
+ "bytes": data.count
205
+ ])
206
+ } else {
207
+ call.reject("Print failed", "Failed to send data to printer")
208
+ }
209
+ } else {
210
+ call.reject("Print failed", "Invalid data encoding")
211
+ }
212
+ }
213
+
214
+ @objc func getStatus(_ call: CAPPluginCall) {
215
+ let isConnected = connectedPeripheral != nil && printerCharacteristic != nil
216
+ var result: [String: Any] = [
217
+ "connected": isConnected,
218
+ "status": isConnected ? "connected" : "disconnected"
219
+ ]
220
+
221
+ if isConnected, let peripheral = connectedPeripheral {
222
+ result["printerAddress"] = peripheral.identifier.uuidString
223
+ result["printerType"] = "bluetooth"
224
+ }
225
+
226
+ call.resolve(result)
227
+ }
228
+
229
+ // NEW: Check printer status with ZPL command
230
+ @objc func checkPrinterStatus(_ call: CAPPluginCall) {
231
+ guard let peripheral = connectedPeripheral,
232
+ let characteristic = printerCharacteristic else {
233
+ call.reject("Status check failed", "Not connected to printer")
234
+ return
235
+ }
236
+
237
+ // Send printer status command
238
+ let statusCommand = "~HS" // Zebra printer status command
239
+ if let data = statusCommand.data(using: .utf8) {
240
+ if sendDataToPrinter(data) {
241
+ call.resolve([
242
+ "success": true,
243
+ "message": "Status command sent",
244
+ "command": statusCommand
245
+ ])
246
+ } else {
247
+ call.reject("Status check failed", "Failed to send status command")
248
+ }
249
+ } else {
250
+ call.reject("Status check failed", "Invalid status command")
251
+ }
252
+ }
253
+
254
+ @objc func scanForPrinters(_ call: CAPPluginCall) {
255
+ print("🔍 Starting printer scan...")
256
+ print("📱 Bluetooth state: \(centralManager.state.rawValue)")
257
+
258
+ switch centralManager.state {
259
+ case .poweredOn:
260
+ print("✅ Bluetooth is ON, starting scan...")
261
+ centralManager.scanForPeripherals(withServices: nil, options: nil)
262
+
263
+ // Return currently discovered printers
264
+ var printers: [[String: Any]] = []
265
+ for peripheral in discoveredPeripherals {
266
+ if let name = peripheral.name,
267
+ (name.contains("Zebra") || name.contains("ZT") || name.contains("XXZHN")) {
268
+ printers.append([
269
+ "name": name,
270
+ "address": peripheral.identifier.uuidString,
271
+ "type": "bluetooth",
272
+ "paired": false // BLE devices are not "paired" in traditional sense
273
+ ])
274
+ }
275
+ }
276
+
277
+ call.resolve([
278
+ "success": true,
279
+ "printers": printers,
280
+ "count": printers.count,
281
+ "message": "Found \(printers.count) printer(s)"
282
+ ])
283
+ case .poweredOff:
284
+ print("❌ Bluetooth is OFF")
285
+ call.reject("Bluetooth is OFF", "Please turn on Bluetooth in Settings")
286
+ case .unauthorized:
287
+ print("❌ Bluetooth unauthorized")
288
+ call.reject("Bluetooth unauthorized", "Please allow Bluetooth access in Settings")
289
+ case .unsupported:
290
+ print("❌ Bluetooth unsupported")
291
+ call.reject("Bluetooth unsupported", "This device doesn't support Bluetooth")
292
+ case .unknown:
293
+ print("❌ Bluetooth state unknown")
294
+ call.reject("Bluetooth state unknown", "Please check Bluetooth settings")
295
+ case .resetting:
296
+ print("🔄 Bluetooth is resetting")
297
+ call.reject("Bluetooth is resetting", "Please wait and try again")
298
+ @unknown default:
299
+ print("❌ Unknown Bluetooth state")
300
+ call.reject("Unknown Bluetooth state", "Please check Bluetooth settings")
301
+ }
302
+ }
303
+
304
+ @objc func connect(_ call: CAPPluginCall) {
305
+ print("🔌 Connect method called")
306
+
307
+ // Get connection parameters
308
+ let address = call.getString("address")
309
+ let type = call.getString("type") ?? "bluetooth"
310
+
311
+ print("📱 Connection request - Address: \(address ?? "none"), Type: \(type)")
312
+
313
+ // For iOS, we only support Bluetooth connections
314
+ if type != "bluetooth" {
315
+ call.reject("Unsupported connection type", "iOS only supports Bluetooth connections")
316
+ return
317
+ }
318
+
319
+ // If address is provided, try to connect to specific device
320
+ if let targetAddress = address {
321
+ // Find peripheral with matching address
322
+ if let targetPeripheral = discoveredPeripherals.first(where: { $0.identifier.uuidString == targetAddress }) {
323
+ print("🔌 Connecting to specific printer: \(targetPeripheral.name ?? "Unknown")")
324
+ centralManager.connect(targetPeripheral, options: nil)
325
+ call.resolve([
326
+ "success": true,
327
+ "connected": false,
328
+ "address": targetAddress,
329
+ "type": "bluetooth",
330
+ "message": "Connecting to printer..."
331
+ ])
332
+ return
333
+ } else {
334
+ call.reject("Printer not found", "Printer with address \(targetAddress) not found")
335
+ return
336
+ }
337
+ }
338
+
339
+ // If no address provided, try to connect to first available printer
340
+ if let firstPrinter = discoveredPeripherals.first {
341
+ print("🔌 Connecting to first available printer: \(firstPrinter.name ?? "Unknown")")
342
+ centralManager.connect(firstPrinter, options: nil)
343
+ call.resolve([
344
+ "success": true,
345
+ "connected": false,
346
+ "address": firstPrinter.identifier.uuidString,
347
+ "type": "bluetooth",
348
+ "message": "Connecting to printer..."
349
+ ])
350
+ } else {
351
+ print("❌ No printers discovered")
352
+ call.reject("No printers found", "No Zebra printers discovered. Please scan for printers first.")
353
+ }
354
+
355
+ }
356
+
357
+ @objc func disconnect(_ call: CAPPluginCall) {
358
+ if let peripheral = connectedPeripheral {
359
+ centralManager.cancelPeripheralConnection(peripheral)
360
+ }
361
+ call.resolve([
362
+ "success": true,
363
+ "connected": false
364
+ ])
365
+ }
366
+
367
+ // MARK: - Helper Methods
368
+ private func sendDataToPrinter(_ data: Data) -> Bool {
369
+ guard let peripheral = connectedPeripheral,
370
+ let characteristic = printerCharacteristic else {
371
+ print("❌ Not connected to printer")
372
+ return false
373
+ }
374
+
375
+ // FIXED: Use proper BLE MTU size (20 bytes for BLE, 512 for BLE 4.2+)
376
+ // But we'll use a conservative approach with 20 bytes chunks
377
+ let mtuSize = 20 // Standard BLE MTU size
378
+ print("📏 MTU size: \(mtuSize)")
379
+
380
+ // Split data into chunks
381
+ var offset = 0
382
+ while offset < data.count {
383
+ let chunkSize = min(mtuSize, data.count - offset)
384
+ let chunk = data.subdata(in: offset..<(offset + chunkSize))
385
+
386
+ print("📤 Sending chunk \(offset/mtuSize + 1): \(chunkSize) bytes")
387
+
388
+ // Add delay before sending to ensure printer is ready
389
+ if offset > 0 {
390
+ Thread.sleep(forTimeInterval: 0.05) // 50ms delay between chunks
391
+ }
392
+
393
+ peripheral.writeValue(chunk, for: characteristic, type: .withoutResponse)
394
+
395
+ offset += chunkSize
396
+ }
397
+
398
+ // Add final delay to ensure all data is processed
399
+ Thread.sleep(forTimeInterval: 0.1)
400
+
401
+ return true
402
+ }
403
+ }
404
+
package/package.json CHANGED
@@ -1,17 +1,40 @@
1
1
  {
2
2
  "name": "@nitra/zebra",
3
- "version": "6.1.3",
3
+ "version": "6.1.5",
4
4
  "description": "Nitra capacitor components",
5
+ "main": "dist/plugin.js",
6
+ "module": "dist/plugin.js",
5
7
  "type": "module",
6
8
  "exports": {
7
- ".": "./index.js"
9
+ ".": "./index.js",
10
+ "./plugin": "./dist/plugin.js"
8
11
  },
12
+ "files": [
13
+ "dist/",
14
+ "ios/",
15
+ "android/",
16
+ "NitraZebra.podspec",
17
+ "README.md"
18
+ ],
9
19
  "scripts": {
10
- "fix": "yarn dlx eslint --fix . && npx prettier --write . "
20
+ "build": "yarn run clean && rollup -c rollup.config.mjs",
21
+ "clean": "rimraf ./dist",
22
+ "watch": "rollup -c rollup.config.mjs --watch",
23
+ "prepublishOnly": "yarn run build",
24
+ "prettier": "prettier \"**/*.{css,html,js,java}\" --plugin=prettier-plugin-java",
25
+ "fix": "npx eslint --fix . && npx prettier --write . "
11
26
  },
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "native",
31
+ "zebra",
32
+ "printer"
33
+ ],
12
34
  "repository": {
13
35
  "type": "git",
14
- "url": "git+https://github.com/nitra/zebra.git"
36
+ "url": "git+https://github.com/nitra/zebra.git",
37
+ "directory": "npm"
15
38
  },
16
39
  "author": "v@nitra.ai",
17
40
  "license": "ISC",
@@ -19,7 +42,38 @@
19
42
  "url": "https://github.com/nitra/zebra/issues"
20
43
  },
21
44
  "homepage": "https://github.com/nitra/zebra",
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ },
22
48
  "dependencies": {
23
49
  "@capacitor/core": "^7.4.3"
50
+ },
51
+ "peerDependencies": {
52
+ "@capacitor/core": "^7.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@capacitor/android": "^7.4.2",
56
+ "@capacitor/cli": "^7.0.0",
57
+ "@capacitor/docgen": "^0.3.0",
58
+ "@capacitor/ios": "^7.4.2",
59
+ "@ionic/eslint-config": "^0.4.0",
60
+ "@ionic/prettier-config": "^4.0.0",
61
+ "@ionic/swiftlint-config": "^2.0.0",
62
+ "@rollup/plugin-commonjs": "^25.0.0",
63
+ "@rollup/plugin-node-resolve": "^15.0.0",
64
+ "eslint": "^8.57.0",
65
+ "prettier": "^3.4.2",
66
+ "prettier-plugin-java": "^2.6.6",
67
+ "rimraf": "^6.0.1",
68
+ "rollup": "^4.30.1",
69
+ "swiftlint": "^2.0.0"
70
+ },
71
+ "capacitor": {
72
+ "ios": {
73
+ "src": "ios"
74
+ },
75
+ "android": {
76
+ "src": "android"
77
+ }
24
78
  }
25
79
  }
package/README.md DELETED
@@ -1,6 +0,0 @@
1
- # Nitra Cap components
2
-
3
- | Папка | Описание |
4
- |-----------------------------|--------------------------------------------------------|
5
- | [app-update](./app-apdate/) | Проверка и установка новой версии приложения в маркете |
6
- | [save-token](./save-token/) | Отримання та збереження push токену |
package/index.js DELETED
@@ -1,3 +0,0 @@
1
- export const print = text => {
2
- console.log(text)
3
- }