@munchi_oy/react-native-epson-printer 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.
Files changed (87) hide show
  1. package/README.md +223 -0
  2. package/android/build.gradle +34 -0
  3. package/android/consumer-rules.pro +0 -0
  4. package/android/libs/ePOS2.jar +0 -0
  5. package/android/src/main/AndroidManifest.xml +17 -0
  6. package/android/src/main/java/com/munchiepsonprinter/MunchiEpsonModule.java +1212 -0
  7. package/android/src/main/java/com/munchiepsonprinter/MunchiPrinterPackage.java +24 -0
  8. package/android/src/main/jniLibs/arm64-v8a/libepos2.so +0 -0
  9. package/android/src/main/jniLibs/armeabi-v7a/libepos2.so +0 -0
  10. package/android/src/main/jniLibs/x86/libepos2.so +0 -0
  11. package/android/src/main/jniLibs/x86_64/libepos2.so +0 -0
  12. package/dist/config.d.ts +14 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/epson/constants.d.ts +124 -0
  15. package/dist/epson/constants.d.ts.map +1 -0
  16. package/dist/epson/discovery.d.ts +14 -0
  17. package/dist/epson/discovery.d.ts.map +1 -0
  18. package/dist/epson/driver.d.ts +26 -0
  19. package/dist/epson/driver.d.ts.map +1 -0
  20. package/dist/epson/errors.d.ts +8 -0
  21. package/dist/epson/errors.d.ts.map +1 -0
  22. package/dist/epson/eventRouter.d.ts +23 -0
  23. package/dist/epson/eventRouter.d.ts.map +1 -0
  24. package/dist/epson/mapper.d.ts +22 -0
  25. package/dist/epson/mapper.d.ts.map +1 -0
  26. package/dist/epson/modelUtils.d.ts +16 -0
  27. package/dist/epson/modelUtils.d.ts.map +1 -0
  28. package/dist/epson/native.d.ts +21 -0
  29. package/dist/epson/native.d.ts.map +1 -0
  30. package/dist/epson/payloadMapper.d.ts +7 -0
  31. package/dist/epson/payloadMapper.d.ts.map +1 -0
  32. package/dist/epson/queueController.d.ts +15 -0
  33. package/dist/epson/queueController.d.ts.map +1 -0
  34. package/dist/epson/recoveryController.d.ts +15 -0
  35. package/dist/epson/recoveryController.d.ts.map +1 -0
  36. package/dist/epson/sessionManager.d.ts +11 -0
  37. package/dist/epson/sessionManager.d.ts.map +1 -0
  38. package/dist/errorResolver.d.ts +15 -0
  39. package/dist/errorResolver.d.ts.map +1 -0
  40. package/dist/errors.d.ts +23 -0
  41. package/dist/errors.d.ts.map +1 -0
  42. package/dist/index.d.ts +15 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +11 -0
  45. package/dist/index.mjs +11 -0
  46. package/dist/react/PrinterProvider.d.ts +19 -0
  47. package/dist/react/PrinterProvider.d.ts.map +1 -0
  48. package/dist/react/usePrinterStatus.d.ts +12 -0
  49. package/dist/react/usePrinterStatus.d.ts.map +1 -0
  50. package/dist/types.d.ts +123 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/utils/errorUtils.d.ts +3 -0
  53. package/dist/utils/errorUtils.d.ts.map +1 -0
  54. package/dist/version.d.ts +2 -0
  55. package/dist/version.d.ts.map +1 -0
  56. package/ios/MunchiPrinter-Bridging-Header.h +4 -0
  57. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/Info.plist +48 -0
  58. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/_CodeSignature/CodeDirectory +0 -0
  59. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/_CodeSignature/CodeRequirements +0 -0
  60. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/_CodeSignature/CodeRequirements-1 +0 -0
  61. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/_CodeSignature/CodeResources +398 -0
  62. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/_CodeSignature/CodeSignature +0 -0
  63. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/dSYMs/libepos2.framework.dSYM/Contents/Info.plist +20 -0
  64. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/dSYMs/libepos2.framework.dSYM/Contents/Resources/DWARF/libepos2 +0 -0
  65. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/dSYMs/libepos2.framework.dSYM/Contents/Resources/Relocations/aarch64/libepos2.yml +5177 -0
  66. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/libepos2.framework/Headers/ePOS2.h +1809 -0
  67. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/libepos2.framework/Headers/libepos2.h +13 -0
  68. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/libepos2.framework/Info.plist +0 -0
  69. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/libepos2.framework/Modules/module.modulemap +6 -0
  70. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/libepos2.framework/PrivacyInfo.xcprivacy +31 -0
  71. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64/libepos2.framework/libepos2 +0 -0
  72. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/dSYMs/libepos2.framework.dSYM/Contents/Info.plist +20 -0
  73. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/dSYMs/libepos2.framework.dSYM/Contents/Resources/DWARF/libepos2 +0 -0
  74. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/dSYMs/libepos2.framework.dSYM/Contents/Resources/Relocations/aarch64/libepos2.yml +5177 -0
  75. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/dSYMs/libepos2.framework.dSYM/Contents/Resources/Relocations/x86_64/libepos2.yml +5062 -0
  76. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/Headers/ePOS2.h +1809 -0
  77. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/Headers/libepos2.h +13 -0
  78. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/Info.plist +0 -0
  79. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/Modules/module.modulemap +6 -0
  80. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/PrivacyInfo.xcprivacy +31 -0
  81. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/_CodeSignature/CodeResources +146 -0
  82. package/ios/Vendors/Epson/Frameworks/libepos2.xcframework/ios-arm64_x86_64-simulator/libepos2.framework/libepos2 +0 -0
  83. package/ios/Vendors/Epson/MunchiEpsonModule.m +35 -0
  84. package/ios/Vendors/Epson/MunchiEpsonModule.swift +455 -0
  85. package/munchi-printer.podspec +22 -0
  86. package/package.json +59 -0
  87. package/react-native.config.js +12 -0
@@ -0,0 +1,13 @@
1
+ //
2
+ // epos2_dynamic.h
3
+ // epos2_dynamic
4
+ //
5
+ // Copyright © 2025 PS. All rights reserved.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+ #import <libepos2/ePOS2.h>
10
+
11
+ // In this header, you should import all the public headers of your framework using statements like #import <epos2_dynamic/PublicHeader.h>
12
+
13
+
@@ -0,0 +1,6 @@
1
+
2
+ framework module libepos2 {
3
+ umbrella header "libepos2.h"
4
+ export *
5
+ module * { export * }
6
+ }
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>NSPrivacyTracking</key>
6
+ <false/>
7
+ <key>NSPrivacyTrackingDomains</key>
8
+ <array/>
9
+ <key>NSPrivacyCollectedDataTypes</key>
10
+ <array/>
11
+ <key>NSPrivacyAccessedAPITypes</key>
12
+ <array>
13
+ <dict>
14
+ <key>NSPrivacyAccessedAPIType</key>
15
+ <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
16
+ <key>NSPrivacyAccessedAPITypeReasons</key>
17
+ <array>
18
+ <string>35F9.1</string>
19
+ </array>
20
+ </dict>
21
+ <dict>
22
+ <key>NSPrivacyAccessedAPIType</key>
23
+ <string>NSPrivacyAccessedAPICategoryDiskSpace</string>
24
+ <key>NSPrivacyAccessedAPITypeReasons</key>
25
+ <array>
26
+ <string>E174.1</string>
27
+ </array>
28
+ </dict>
29
+ </array>
30
+ </dict>
31
+ </plist>
@@ -0,0 +1,146 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>files</key>
6
+ <dict>
7
+ <key>Headers/ePOS2.h</key>
8
+ <data>
9
+ Js86p44I3q+R/g75vf1k8x2N7sY=
10
+ </data>
11
+ <key>Headers/libepos2.h</key>
12
+ <data>
13
+ cNH3T+tKkOY+I8/dqK7nwOwP3oY=
14
+ </data>
15
+ <key>Info.plist</key>
16
+ <data>
17
+ zI+rO5IKtgOEDL8reApMalE+e5Y=
18
+ </data>
19
+ <key>Modules/module.modulemap</key>
20
+ <data>
21
+ 0CQQogyWjeKhdoD+t57YYdikdqc=
22
+ </data>
23
+ <key>PrivacyInfo.xcprivacy</key>
24
+ <data>
25
+ xtUp+VJ9chSU5lq39fn1lelR9c8=
26
+ </data>
27
+ </dict>
28
+ <key>files2</key>
29
+ <dict>
30
+ <key>Headers/ePOS2.h</key>
31
+ <dict>
32
+ <key>hash2</key>
33
+ <data>
34
+ hRTVG71sSWquKNIT0GXQd+uOf+q6pzV8f+ZZtFG+xCo=
35
+ </data>
36
+ </dict>
37
+ <key>Headers/libepos2.h</key>
38
+ <dict>
39
+ <key>hash2</key>
40
+ <data>
41
+ R2js4J7KKBr8EHyyC8vKiyAQ4fxoO4Zk9MTkm/V6KNA=
42
+ </data>
43
+ </dict>
44
+ <key>Modules/module.modulemap</key>
45
+ <dict>
46
+ <key>hash2</key>
47
+ <data>
48
+ YTvV3H9DOgxVTppCLVawhZhf9FN57UdU3CTFKZyEaN0=
49
+ </data>
50
+ </dict>
51
+ <key>PrivacyInfo.xcprivacy</key>
52
+ <dict>
53
+ <key>hash2</key>
54
+ <data>
55
+ FZCqCdK7GpecFGmN6sRNy6TtFOJqWkeJGdcn7ggNoOQ=
56
+ </data>
57
+ </dict>
58
+ </dict>
59
+ <key>rules</key>
60
+ <dict>
61
+ <key>^.*</key>
62
+ <true/>
63
+ <key>^.*\.lproj/</key>
64
+ <dict>
65
+ <key>optional</key>
66
+ <true/>
67
+ <key>weight</key>
68
+ <real>1000</real>
69
+ </dict>
70
+ <key>^.*\.lproj/locversion.plist$</key>
71
+ <dict>
72
+ <key>omit</key>
73
+ <true/>
74
+ <key>weight</key>
75
+ <real>1100</real>
76
+ </dict>
77
+ <key>^Base\.lproj/</key>
78
+ <dict>
79
+ <key>weight</key>
80
+ <real>1010</real>
81
+ </dict>
82
+ <key>^version.plist$</key>
83
+ <true/>
84
+ </dict>
85
+ <key>rules2</key>
86
+ <dict>
87
+ <key>.*\.dSYM($|/)</key>
88
+ <dict>
89
+ <key>weight</key>
90
+ <real>11</real>
91
+ </dict>
92
+ <key>^(.*/)?\.DS_Store$</key>
93
+ <dict>
94
+ <key>omit</key>
95
+ <true/>
96
+ <key>weight</key>
97
+ <real>2000</real>
98
+ </dict>
99
+ <key>^.*</key>
100
+ <true/>
101
+ <key>^.*\.lproj/</key>
102
+ <dict>
103
+ <key>optional</key>
104
+ <true/>
105
+ <key>weight</key>
106
+ <real>1000</real>
107
+ </dict>
108
+ <key>^.*\.lproj/locversion.plist$</key>
109
+ <dict>
110
+ <key>omit</key>
111
+ <true/>
112
+ <key>weight</key>
113
+ <real>1100</real>
114
+ </dict>
115
+ <key>^Base\.lproj/</key>
116
+ <dict>
117
+ <key>weight</key>
118
+ <real>1010</real>
119
+ </dict>
120
+ <key>^Info\.plist$</key>
121
+ <dict>
122
+ <key>omit</key>
123
+ <true/>
124
+ <key>weight</key>
125
+ <real>20</real>
126
+ </dict>
127
+ <key>^PkgInfo$</key>
128
+ <dict>
129
+ <key>omit</key>
130
+ <true/>
131
+ <key>weight</key>
132
+ <real>20</real>
133
+ </dict>
134
+ <key>^embedded\.provisionprofile$</key>
135
+ <dict>
136
+ <key>weight</key>
137
+ <real>20</real>
138
+ </dict>
139
+ <key>^version\.plist$</key>
140
+ <dict>
141
+ <key>weight</key>
142
+ <real>20</real>
143
+ </dict>
144
+ </dict>
145
+ </dict>
146
+ </plist>
@@ -0,0 +1,35 @@
1
+ #if __has_include(<React/RCTBridgeModule.h>)
2
+ #import <React/RCTBridgeModule.h>
3
+ #elif __has_include("RCTBridgeModule.h")
4
+ #import "RCTBridgeModule.h"
5
+ #else
6
+ #import "React/RCTBridgeModule.h" // Fallback
7
+ #endif
8
+
9
+ #if __has_include(<React/RCTEventEmitter.h>)
10
+ #import <React/RCTEventEmitter.h>
11
+ #elif __has_include("RCTEventEmitter.h")
12
+ #import "RCTEventEmitter.h"
13
+ #else
14
+ #import "React/RCTEventEmitter.h" // Fallback
15
+ #endif
16
+
17
+ @interface RCT_EXTERN_MODULE(MunchiEpsonModule, RCTEventEmitter)
18
+
19
+ RCT_EXTERN_METHOD(discover:(NSDictionary *)params resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
20
+
21
+ RCT_EXTERN_METHOD(initSession:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
22
+
23
+ RCT_EXTERN_METHOD(disposeSession:(NSString *)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
24
+
25
+ RCT_EXTERN_METHOD(connect:(NSString *)sessionId target:(NSString *)target timeout:(double)timeout model:(NSInteger)model lang:(NSInteger)lang resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
26
+
27
+ RCT_EXTERN_METHOD(disconnect:(NSString *)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
28
+
29
+ RCT_EXTERN_METHOD(print:(NSString *)sessionId commands:(NSArray *)commands resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
30
+
31
+ RCT_EXTERN_METHOD(getStatus:(NSString *)sessionId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
32
+
33
+ RCT_EXTERN_METHOD(supportedEvents)
34
+
35
+ @end
@@ -0,0 +1,455 @@
1
+ import Foundation
2
+ import React
3
+
4
+ @objc(MunchiEpsonModule)
5
+ class MunchiEpsonModule: RCTEventEmitter, Epos2ConnectionDelegate, Epos2PtrStatusChangeDelegate, Epos2PtrReceiveDelegate, Epos2DiscoveryDelegate {
6
+
7
+ private final class EpsonSession {
8
+ let sessionId: String
9
+ var printer: Epos2Printer?
10
+ var target: String?
11
+ var printResolve: RCTPromiseResolveBlock?
12
+ var printReject: RCTPromiseRejectBlock?
13
+ var printTimeoutWork: DispatchWorkItem?
14
+
15
+ init(sessionId: String) {
16
+ self.sessionId = sessionId
17
+ }
18
+ }
19
+
20
+ private var sessions: [String: EpsonSession] = [:]
21
+ private var printerToSessionId: [ObjectIdentifier: String] = [:]
22
+ private var targetToSessionId: [String: String] = [:]
23
+ private var discoveryResolve: RCTPromiseResolveBlock?
24
+ private var discoveryReject: RCTPromiseRejectBlock?
25
+ private var foundPrinters: [NSDictionary] = []
26
+
27
+ override func supportedEvents() -> [String]! {
28
+ return ["onPrinterStatusChange", "onPrinterConnectionChange", "onPrinterReceive"]
29
+ }
30
+
31
+ @objc
32
+ override static func requiresMainQueueSetup() -> Bool {
33
+ return true
34
+ }
35
+
36
+ @objc(discover:resolver:rejecter:)
37
+ func discover(_ params: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
38
+ // 1. Stop any existing discovery
39
+ var result = Epos2Discovery.stop()
40
+
41
+ // 2. Clear previous results
42
+ foundPrinters = []
43
+ discoveryResolve = resolve
44
+ discoveryReject = reject
45
+
46
+ // 3. Start new discovery
47
+ let filter = Epos2FilterOption()
48
+ filter.portType = EPOS2_PORTTYPE_ALL.rawValue
49
+
50
+ result = Epos2Discovery.start(filter, delegate: self)
51
+
52
+ if result != EPOS2_SUCCESS.rawValue {
53
+ reject("DISCOVERY_ERROR", "Failed to start discovery: \(result)", nil)
54
+ discoveryResolve = nil
55
+ discoveryReject = nil
56
+ return
57
+ }
58
+
59
+ // 4. Set a timeout to stop discovery and return results
60
+ let timeout = params["timeout"] as? Double ?? 5000.0
61
+ DispatchQueue.main.asyncAfter(deadline: .now() + (timeout / 1000.0)) { [weak self] in
62
+ self?.stopDiscoveryAndResolve()
63
+ }
64
+ }
65
+
66
+ func stopDiscoveryAndResolve() {
67
+ // Only resolve if we haven't already (e.g. if start failed)
68
+ guard let resolve = discoveryResolve else { return }
69
+
70
+ Epos2Discovery.stop()
71
+ resolve(foundPrinters)
72
+
73
+ // Cleanup
74
+ discoveryResolve = nil
75
+ discoveryReject = nil
76
+ foundPrinters = []
77
+ }
78
+
79
+ func onDiscovery(_ deviceInfo: Epos2DeviceInfo!) {
80
+ let printerInfo: [String: Any] = [
81
+ "target": deviceInfo.target ?? "",
82
+ "name": deviceInfo.deviceName ?? "",
83
+ "macAddress": deviceInfo.macAddress ?? "",
84
+ "ipAddress": deviceInfo.ipAddress ?? "",
85
+ "bdAddress": deviceInfo.bdAddress ?? "",
86
+ "type": "EPSON"
87
+ ]
88
+
89
+ // Dedup based on target
90
+ if !foundPrinters.contains(where: { ($0["target"] as? String) == deviceInfo.target }) {
91
+ foundPrinters.append(printerInfo as NSDictionary)
92
+ }
93
+ }
94
+
95
+ @objc(initSession:rejecter:)
96
+ func initSession(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
97
+ let sessionId = UUID().uuidString
98
+ sessions[sessionId] = EpsonSession(sessionId: sessionId)
99
+ resolve(sessionId)
100
+ }
101
+
102
+ @objc(disposeSession:resolver:rejecter:)
103
+ func disposeSession(_ sessionId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
104
+ guard let session = sessions.removeValue(forKey: sessionId) else {
105
+ resolve(true)
106
+ return
107
+ }
108
+
109
+ teardownPrinter(session, disconnect: true)
110
+ resolve(true)
111
+ }
112
+
113
+ @objc(connect:target:timeout:model:lang:resolver:rejecter:)
114
+ func connect(_ sessionId: String, target: String, timeout: Double, model: Int, lang: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
115
+ guard let session = sessions[sessionId] else {
116
+ reject("SESSION_ERROR", "Session not found", nil)
117
+ return
118
+ }
119
+
120
+ var connectionDelay = 0.0
121
+
122
+ if session.printer != nil {
123
+ teardownPrinter(session, disconnect: true)
124
+ connectionDelay = 0.5
125
+ }
126
+
127
+ DispatchQueue.main.asyncAfter(deadline: .now() + connectionDelay) { [weak self] in
128
+ guard let self = self else { return }
129
+ guard let latestSession = self.sessions[sessionId] else {
130
+ reject("SESSION_ERROR", "Session disposed while connecting", nil)
131
+ return
132
+ }
133
+
134
+ if let ownerSessionId = self.targetToSessionId[target], ownerSessionId != sessionId {
135
+ reject("TARGET_IN_USE", "Target is already connected by another session: \(target)", nil)
136
+ return
137
+ }
138
+
139
+ let safeLang = self.normalizedLanguage(lang)
140
+ let safeModel = self.normalizedModel(model)
141
+
142
+ latestSession.printer = Epos2Printer(printerSeries: safeModel, lang: safeLang)
143
+
144
+ guard let p = latestSession.printer else {
145
+ reject("INIT_ERROR", "Failed to initialize printer object", nil)
146
+ return
147
+ }
148
+
149
+ p.setConnectionEventDelegate(self)
150
+ p.setStatusChangeEventDelegate(self)
151
+ p.setReceiveEventDelegate(self)
152
+ self.printerToSessionId[ObjectIdentifier(p)] = sessionId
153
+ latestSession.target = target
154
+
155
+ let connectTimeout = Int(timeout)
156
+ let result = p.connect(target, timeout: connectTimeout)
157
+
158
+ if result == EPOS2_SUCCESS.rawValue {
159
+ self.targetToSessionId[target] = sessionId
160
+ resolve("Connected to \(target)")
161
+ } else {
162
+ self.teardownPrinter(latestSession, disconnect: false)
163
+ reject("CONNECT_ERROR", "Failed to connect: \(result)", nil)
164
+ }
165
+ }
166
+ }
167
+
168
+ @objc(disconnect:resolver:rejecter:)
169
+ func disconnect(_ sessionId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
170
+ guard let session = sessions[sessionId] else {
171
+ resolve(true)
172
+ return
173
+ }
174
+
175
+ let result = teardownPrinter(session, disconnect: true)
176
+
177
+ if result == nil || result == EPOS2_SUCCESS.rawValue {
178
+ resolve(true)
179
+ } else {
180
+ resolve(false)
181
+ }
182
+ }
183
+
184
+ // --- Delegate Methods ---
185
+
186
+ func onConnection(_ deviceObj: Any!, eventType: Int32) {
187
+ guard
188
+ let obj = deviceObj as AnyObject?,
189
+ let sessionId = printerToSessionId[ObjectIdentifier(obj)],
190
+ let session = sessions[sessionId]
191
+ else { return }
192
+
193
+ var status = "UNKNOWN"
194
+ switch eventType {
195
+ case EPOS2_EVENT_RECONNECTING.rawValue:
196
+ status = "RECONNECTING"
197
+ case EPOS2_EVENT_RECONNECT.rawValue:
198
+ status = "RECONNECTED"
199
+ case EPOS2_EVENT_DISCONNECT.rawValue:
200
+ status = "DISCONNECTED"
201
+ default:
202
+ break
203
+ }
204
+
205
+ sendEvent(withName: "onPrinterConnectionChange", body: [
206
+ "sessionId": session.sessionId,
207
+ "status": status,
208
+ "target": session.target ?? ""
209
+ ])
210
+ }
211
+
212
+ func onPtrStatusChange(_ printerObj: Epos2Printer!, eventType: Int32) {
213
+ guard
214
+ let sessionId = printerToSessionId[ObjectIdentifier(printerObj)],
215
+ let session = sessions[sessionId]
216
+ else { return }
217
+
218
+ sendEvent(withName: "onPrinterStatusChange", body: [
219
+ "sessionId": session.sessionId,
220
+ "target": session.target ?? "",
221
+ "eventType": eventType
222
+ ])
223
+ }
224
+
225
+ func onPtrReceive(_ printerObj: Epos2Printer!, code: Int32, status: Epos2PrinterStatusInfo!, printJobId: String!) {
226
+ guard
227
+ let sessionId = printerToSessionId[ObjectIdentifier(printerObj)],
228
+ let session = sessions[sessionId]
229
+ else { return }
230
+
231
+ session.printTimeoutWork?.cancel()
232
+ session.printTimeoutWork = nil
233
+
234
+ if let resolve = session.printResolve, let reject = session.printReject {
235
+ if code == EPOS2_CODE_SUCCESS.rawValue {
236
+ resolve(true)
237
+ } else {
238
+ reject("PRINT_FAILURE", "Print finished with error code: \(code)", nil)
239
+ }
240
+ session.printResolve = nil
241
+ session.printReject = nil
242
+ }
243
+
244
+ sendEvent(withName: "onPrinterReceive", body: [
245
+ "sessionId": session.sessionId,
246
+ "target": session.target ?? "",
247
+ "code": code
248
+ ])
249
+ }
250
+
251
+ @objc(print:commands:resolver:rejecter:)
252
+ func print(_ sessionId: String, commands: NSArray, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
253
+ guard let session = sessions[sessionId], let p = session.printer else {
254
+ reject("PRINT_ERROR", "Printer not connected", nil)
255
+ return
256
+ }
257
+
258
+ if session.printResolve != nil {
259
+ reject("PRINT_BUSY", "Another print job is in progress", nil)
260
+ return
261
+ }
262
+
263
+ p.clearCommandBuffer()
264
+
265
+ for command in commands {
266
+ if let cmd = command as? NSDictionary, let type = cmd["cmd"] as? String {
267
+ switch type {
268
+ case "addText":
269
+ if let data = cmd["data"] as? String {
270
+ if let align = cmd["align"] as? String {
271
+ var alignVal = EPOS2_ALIGN_LEFT.rawValue
272
+ if align == "center" { alignVal = EPOS2_ALIGN_CENTER.rawValue }
273
+ else if align == "right" { alignVal = EPOS2_ALIGN_RIGHT.rawValue }
274
+ p.addTextAlign(alignVal)
275
+ }
276
+ if let bold = cmd["bold"] as? Bool {
277
+ p.addTextStyle(EPOS2_PARAM_DEFAULT, ul: EPOS2_PARAM_DEFAULT, em: bold ? EPOS2_TRUE : EPOS2_FALSE, color: EPOS2_PARAM_DEFAULT)
278
+ }
279
+ if let size = cmd["size"] as? String {
280
+ }
281
+ p.addText(data)
282
+ }
283
+ case "addTextAlign":
284
+ if let align = cmd["align"] as? String {
285
+ var alignVal = EPOS2_ALIGN_LEFT.rawValue
286
+ if align == "center" { alignVal = EPOS2_ALIGN_CENTER.rawValue }
287
+ else if align == "right" { alignVal = EPOS2_ALIGN_RIGHT.rawValue }
288
+ p.addTextAlign(alignVal)
289
+ }
290
+ case "addTextStyle":
291
+ if let bold = cmd["bold"] as? Bool {
292
+ p.addTextStyle(EPOS2_PARAM_DEFAULT, ul: EPOS2_PARAM_DEFAULT, em: bold ? EPOS2_TRUE : EPOS2_FALSE, color: EPOS2_PARAM_DEFAULT)
293
+ }
294
+ case "addTextSize":
295
+ if let w = cmd["width"] as? Int, let h = cmd["height"] as? Int {
296
+ p.addTextSize(Int(w), height: Int(h))
297
+ }
298
+ case "addBarcode":
299
+ if let data = cmd["data"] as? String, let typeStr = cmd["type"] as? String {
300
+ var type = EPOS2_BARCODE_CODE39.rawValue
301
+ if typeStr == "UPC-A" { type = EPOS2_BARCODE_UPC_A.rawValue }
302
+ else if typeStr == "UPC-E" { type = EPOS2_BARCODE_UPC_E.rawValue }
303
+ else if typeStr == "EAN13" { type = EPOS2_BARCODE_EAN13.rawValue }
304
+ else if typeStr == "EAN8" { type = EPOS2_BARCODE_EAN8.rawValue }
305
+ else if typeStr == "CODE39" { type = EPOS2_BARCODE_CODE39.rawValue }
306
+ else if typeStr == "ITF" { type = EPOS2_BARCODE_ITF.rawValue }
307
+ else if typeStr == "CODABAR" { type = EPOS2_BARCODE_CODABAR.rawValue }
308
+
309
+ p.addBarcode(data, type: type, hri: EPOS2_HRI_BELOW.rawValue, font: EPOS2_FONT_A.rawValue, width: 2, height: 100)
310
+ }
311
+ case "addSymbol":
312
+ if let data = cmd["data"] as? String {
313
+ p.addSymbol(data, type: EPOS2_SYMBOL_QRCODE_MODEL_2.rawValue, level: EPOS2_LEVEL_M.rawValue, width: 3, height: 3, size: 0)
314
+ }
315
+ case "addCut":
316
+ p.addCut(EPOS2_CUT_FEED.rawValue)
317
+ case "addFeedLine":
318
+ if let line = cmd["line"] as? Int {
319
+ p.addFeedLine(Int(line))
320
+ }
321
+ case "addPulse":
322
+ p.addPulse(EPOS2_DRAWER_2PIN.rawValue, time: EPOS2_PULSE_100.rawValue)
323
+ case "addTextLang":
324
+ if let lang = cmd["lang"] as? Int {
325
+ p.addTextLang(Int32(lang))
326
+ }
327
+ case "addTextFont":
328
+ if let font = cmd["font"] as? Int {
329
+ p.addTextFont(Int32(font))
330
+ }
331
+ default:
332
+ break
333
+ }
334
+ }
335
+ }
336
+
337
+ session.printResolve = resolve
338
+ session.printReject = reject
339
+
340
+ let result = p.sendData(Int(EPOS2_PARAM_DEFAULT))
341
+ if result != EPOS2_SUCCESS.rawValue {
342
+ let errorMsg = getErrorDescription(result)
343
+ session.printResolve = nil
344
+ session.printReject = nil
345
+ reject("PRINT_ERROR", "Failed to send data: \(result) (\(errorMsg))", nil)
346
+ } else {
347
+ let timeoutWork = DispatchWorkItem { [weak self] in
348
+ guard
349
+ let self = self,
350
+ let timedSession = self.sessions[sessionId]
351
+ else { return }
352
+
353
+ if let reject = timedSession.printReject {
354
+ reject("PRINT_TIMEOUT", "Print timed out waiting for printer response", nil)
355
+ }
356
+ timedSession.printResolve = nil
357
+ timedSession.printReject = nil
358
+ timedSession.printTimeoutWork = nil
359
+ timedSession.printer?.clearCommandBuffer()
360
+ }
361
+ session.printTimeoutWork = timeoutWork
362
+ DispatchQueue.main.asyncAfter(deadline: .now() + 30, execute: timeoutWork)
363
+ }
364
+ }
365
+
366
+ @objc(getStatus:resolver:rejecter:)
367
+ func getStatus(_ sessionId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
368
+ guard let session = sessions[sessionId], let p = session.printer else {
369
+ reject("STATUS_ERROR", "Printer not connected", nil)
370
+ return
371
+ }
372
+
373
+ if let status = p.getStatus() {
374
+ let statusDict: [String: Any] = [
375
+ "online": status.connection == EPOS2_TRUE,
376
+ "coverOpen": status.coverOpen == EPOS2_TRUE,
377
+ "paper": status.paper == EPOS2_PAPER_EMPTY.rawValue ? "EMPTY" : (status.paper == EPOS2_PAPER_NEAR_END.rawValue ? "NEAR_EMPTY" : "OK"),
378
+ "paperEmpty": status.paper == EPOS2_PAPER_EMPTY.rawValue, // legacy compat
379
+ "drawerOpen": status.drawer == EPOS2_DRAWER_HIGH.rawValue, // HIGH typically means open/signal high depending on wiring, assuming HIGH = Open for now based on common usage
380
+ "errorStatus": status.errorStatus == EPOS2_MECHANICAL_ERR.rawValue ? "MECHANICAL_ERR" : (status.errorStatus == EPOS2_AUTOCUTTER_ERR.rawValue ? "AUTOCUTTER_ERR" : "NONE")
381
+ ]
382
+ resolve(statusDict)
383
+ } else {
384
+ reject("STATUS_ERROR", "Failed to retrieve status", nil)
385
+ }
386
+ }
387
+
388
+ // Helper to map error codes to descriptions
389
+ private func getErrorDescription(_ code: Int32) -> String {
390
+ switch code {
391
+ case EPOS2_SUCCESS.rawValue: return "SUCCESS"
392
+ case EPOS2_ERR_PARAM.rawValue: return "ERR_PARAM"
393
+ case EPOS2_ERR_CONNECT.rawValue: return "ERR_CONNECT"
394
+ case EPOS2_ERR_TIMEOUT.rawValue: return "ERR_TIMEOUT"
395
+ case EPOS2_ERR_MEMORY.rawValue: return "ERR_MEMORY"
396
+ case EPOS2_ERR_ILLEGAL.rawValue: return "ERR_ILLEGAL"
397
+ case EPOS2_ERR_PROCESSING.rawValue: return "ERR_PROCESSING"
398
+ case EPOS2_ERR_NOT_FOUND.rawValue: return "ERR_NOT_FOUND"
399
+ case EPOS2_ERR_IN_USE.rawValue: return "ERR_IN_USE"
400
+ case EPOS2_ERR_TYPE_INVALID.rawValue: return "ERR_TYPE_INVALID"
401
+ case EPOS2_ERR_DISCONNECT.rawValue: return "ERR_DISCONNECT"
402
+ case EPOS2_ERR_ALREADY_OPENED.rawValue: return "ERR_ALREADY_OPENED"
403
+ case EPOS2_ERR_ALREADY_USED.rawValue: return "ERR_ALREADY_USED"
404
+ case EPOS2_ERR_BOX_COUNT_OVER.rawValue: return "ERR_BOX_COUNT_OVER"
405
+ case EPOS2_ERR_BOX_CLIENT_OVER.rawValue: return "ERR_BOX_CLIENT_OVER"
406
+ case EPOS2_ERR_UNSUPPORTED.rawValue: return "ERR_UNSUPPORTED"
407
+ case EPOS2_ERR_FAILURE.rawValue: return "ERR_FAILURE"
408
+ default: return "UNKNOWN_ERROR_\(code)"
409
+ }
410
+ }
411
+
412
+ private func normalizedLanguage(_ lang: Int) -> Int32 {
413
+ if lang >= 0 && lang <= 7 {
414
+ return Int32(lang)
415
+ }
416
+ return 0
417
+ }
418
+
419
+ private func normalizedModel(_ model: Int) -> Int32 {
420
+ if model >= 0 && model <= 32 {
421
+ return Int32(model)
422
+ }
423
+ return EPOS2_TM_M30III.rawValue
424
+ }
425
+
426
+ private func teardownPrinter(_ session: EpsonSession, disconnect: Bool) -> Int32? {
427
+ session.printTimeoutWork?.cancel()
428
+ session.printTimeoutWork = nil
429
+ session.printResolve = nil
430
+ session.printReject = nil
431
+
432
+ if let target = session.target, targetToSessionId[target] == session.sessionId {
433
+ targetToSessionId.removeValue(forKey: target)
434
+ }
435
+
436
+ guard let printer = session.printer else {
437
+ session.target = nil
438
+ return nil
439
+ }
440
+
441
+ printerToSessionId.removeValue(forKey: ObjectIdentifier(printer))
442
+ printer.setReceiveEventDelegate(nil)
443
+ printer.setConnectionEventDelegate(nil)
444
+ printer.setStatusChangeEventDelegate(nil)
445
+
446
+ var result: Int32? = nil
447
+ if disconnect {
448
+ result = printer.disconnect()
449
+ }
450
+
451
+ session.printer = nil
452
+ session.target = nil
453
+ return result
454
+ }
455
+ }