@leonardojc/capacitor-ioboard 1.1.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.
@@ -0,0 +1,154 @@
1
+ export interface CapacitorIoboardPlugin {
2
+ /**
3
+ * Open serial connection to IOBOARD device
4
+ * @param options Serial connection parameters
5
+ */
6
+ connect(options: SerialConnectionOptions): Promise<IOBoardResponse>;
7
+ /**
8
+ * Close serial connection
9
+ */
10
+ disconnect(): Promise<IOBoardResponse>;
11
+ /**
12
+ * Check if serial connection is active
13
+ */
14
+ isConnected(): Promise<{
15
+ connected: boolean;
16
+ }>;
17
+ /**
18
+ * Query the status of an IOBOARD device
19
+ * @param options Device address and timeout
20
+ */
21
+ getStatus(options: DeviceOptions): Promise<IOBoardStatusResponse>;
22
+ /**
23
+ * Unlock a specific pallet with LED feedback
24
+ * @param options Pallet control parameters
25
+ */
26
+ unlockPallet(options: UnlockPalletOptions): Promise<IOBoardResponse>;
27
+ /**
28
+ * Control multiple pallets simultaneously
29
+ * @param options Full pallet control parameters
30
+ */
31
+ controlMultiplePallets(options: MultiplePalletOptions): Promise<IOBoardResponse>;
32
+ /**
33
+ * Scan for IOBOARD devices on the network
34
+ * @param options Scan parameters
35
+ */
36
+ scanDevices(options: ScanOptions): Promise<ScanResponse>;
37
+ /**
38
+ * Send OTA update notification to IOBOARD
39
+ * @param options OTA notification parameters
40
+ */
41
+ sendOTANotification(options: OTANotificationOptions): Promise<IOBoardResponse>;
42
+ /**
43
+ * Send OTA data packet to IOBOARD
44
+ * @param options OTA data parameters
45
+ */
46
+ sendOTAData(options: OTADataOptions): Promise<IOBoardResponse>;
47
+ }
48
+ export interface IOBoardResponse {
49
+ success: boolean;
50
+ message?: string;
51
+ data?: any;
52
+ }
53
+ export interface IOBoardStatusResponse extends IOBoardResponse {
54
+ data?: {
55
+ doorLockStatus: number;
56
+ softwareVersion: {
57
+ major: number;
58
+ minor: number;
59
+ patch: number;
60
+ };
61
+ };
62
+ }
63
+ export interface SerialConnectionOptions {
64
+ portPath?: string;
65
+ baudRate?: number;
66
+ dataBits?: number;
67
+ stopBits?: number;
68
+ parity?: 'none' | 'even' | 'odd';
69
+ flowControl?: 'none' | 'hardware' | 'software';
70
+ timeout?: number;
71
+ }
72
+ export interface DeviceOptions {
73
+ address: number;
74
+ timeout?: number;
75
+ }
76
+ export interface UnlockPalletOptions extends DeviceOptions {
77
+ palletNumber: number;
78
+ ledColor?: 'red' | 'green' | 'blue' | 'yellow' | 'white' | 'off';
79
+ blinkTimes?: number;
80
+ blinkSpeed?: number;
81
+ }
82
+ export interface MultiplePalletOptions extends DeviceOptions {
83
+ doorLockMask: number;
84
+ ledControls?: LEDControlOptions[];
85
+ }
86
+ export interface ScanOptions {
87
+ startAddress?: number;
88
+ endAddress?: number;
89
+ timeout?: number;
90
+ }
91
+ export interface ScanResponse extends IOBoardResponse {
92
+ devices?: Array<{
93
+ address: number;
94
+ responding: boolean;
95
+ status?: any;
96
+ }>;
97
+ }
98
+ export interface LEDControlOptions {
99
+ red: number;
100
+ green: number;
101
+ blue: number;
102
+ intensity: number;
103
+ blinkTimes: number;
104
+ blinkSpeed: number;
105
+ }
106
+ export interface OTANotificationOptions {
107
+ address: number;
108
+ majorVersion: number;
109
+ minorVersion: number;
110
+ patchVersion: number;
111
+ firmwareSize: number;
112
+ }
113
+ export interface OTADataOptions {
114
+ address: number;
115
+ packetNumber: number;
116
+ data: string;
117
+ }
118
+ export declare const IOBOARD_PROTOCOL: {
119
+ SOI: number;
120
+ EOI: number;
121
+ FRAME_TYPES: {
122
+ STATUS_QUERY: number;
123
+ SINGLE_PALLET: number;
124
+ FULL_PALLET: number;
125
+ OTA_REQUEST: number;
126
+ OTA_DATA: number;
127
+ OTA_RESPONSE: number;
128
+ };
129
+ DEFAULT_BAUD_RATE: number;
130
+ DEFAULT_DATA_BITS: number;
131
+ DEFAULT_STOP_BITS: number;
132
+ DEFAULT_PARITY: "none";
133
+ DEFAULT_TIMEOUT: number;
134
+ MIN_ADDRESS: number;
135
+ MAX_ADDRESS: number;
136
+ MIN_PALLET: number;
137
+ MAX_PALLET: number;
138
+ OTA_PACKET_SIZE: number;
139
+ OTA_RESPONSES: {
140
+ AREA_REQUEST: number;
141
+ REQUEST_A_ZONE: number;
142
+ REQUEST_B_ZONE: number;
143
+ DATA_REQUEST: number;
144
+ COMPLETION: number;
145
+ ERROR: number;
146
+ };
147
+ OTA_ERROR_CODES: {
148
+ ILLEGAL_VERSION: number;
149
+ NON_OTA_STATUS: number;
150
+ WRITE_FAILED: number;
151
+ VERIFICATION_FAILED: number;
152
+ TRANSFER_FAILED: number;
153
+ };
154
+ };
@@ -0,0 +1,43 @@
1
+ // Protocol Constants
2
+ export const IOBOARD_PROTOCOL = {
3
+ SOI: 0x0D,
4
+ EOI: 0x0A,
5
+ FRAME_TYPES: {
6
+ STATUS_QUERY: 0x00,
7
+ SINGLE_PALLET: 0x01,
8
+ FULL_PALLET: 0x02,
9
+ OTA_REQUEST: 0xF1,
10
+ OTA_DATA: 0xF2,
11
+ OTA_RESPONSE: 0xF3
12
+ },
13
+ DEFAULT_BAUD_RATE: 115200,
14
+ DEFAULT_DATA_BITS: 8,
15
+ DEFAULT_STOP_BITS: 1,
16
+ DEFAULT_PARITY: 'none',
17
+ DEFAULT_TIMEOUT: 5000,
18
+ // Address range
19
+ MIN_ADDRESS: 1,
20
+ MAX_ADDRESS: 63,
21
+ // Pallet range
22
+ MIN_PALLET: 0,
23
+ MAX_PALLET: 7,
24
+ // OTA Constants
25
+ OTA_PACKET_SIZE: 128,
26
+ // Response codes
27
+ OTA_RESPONSES: {
28
+ AREA_REQUEST: 0x26,
29
+ REQUEST_A_ZONE: 0x41,
30
+ REQUEST_B_ZONE: 0x42,
31
+ DATA_REQUEST: 0x23,
32
+ COMPLETION: 0x4F,
33
+ ERROR: 0x45, // 'E'
34
+ },
35
+ // Error codes
36
+ OTA_ERROR_CODES: {
37
+ ILLEGAL_VERSION: 9,
38
+ NON_OTA_STATUS: 8,
39
+ WRITE_FAILED: 7,
40
+ VERIFICATION_FAILED: 6,
41
+ TRANSFER_FAILED: 5
42
+ }
43
+ };
@@ -0,0 +1,5 @@
1
+ import type { CapacitorIoboardPlugin } from './definitions';
2
+ declare const CapacitorIoboard: CapacitorIoboardPlugin;
3
+ export * from './definitions';
4
+ export * from './protocol';
5
+ export { CapacitorIoboard };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const CapacitorIoboard = registerPlugin('CapacitorIoboard', {
3
+ web: () => import('./web').then(m => new m.CapacitorIoboardWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export * from './protocol';
7
+ export { CapacitorIoboard };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * IOBOARD Protocol utilities for frame construction and parsing
3
+ */
4
+ export declare class IOBoardProtocol {
5
+ /**
6
+ * Calculate CRC-16 CCITT checksum
7
+ */
8
+ static calculateCRC16(data: number[]): number;
9
+ /**
10
+ * Build a command frame for IOBOARD
11
+ */
12
+ static buildFrame(address: number, frameType: number, data: number[]): number[];
13
+ /**
14
+ * Parse a response frame from IOBOARD
15
+ */
16
+ static parseFrame(frame: number[]): {
17
+ success: boolean;
18
+ address?: number;
19
+ frameType?: number;
20
+ data?: number[];
21
+ error?: string;
22
+ };
23
+ /**
24
+ * Build status query frame
25
+ */
26
+ static buildStatusQuery(address: number): number[];
27
+ /**
28
+ * Build single pallet control frame
29
+ */
30
+ static buildSinglePalletControl(address: number, palletNumber: number, doorLock: boolean, ledControl: any): number[];
31
+ /**
32
+ * Build full pallet control frame
33
+ */
34
+ static buildFullPalletControl(address: number, doorLockControl: number, extendedControl: number, ledControls: any[]): number[];
35
+ /**
36
+ * Build OTA notification frame
37
+ */
38
+ static buildOTANotification(address: number, majorVersion: number, minorVersion: number, patchVersion: number, firmwareSize: number): number[];
39
+ /**
40
+ * Build OTA data frame
41
+ */
42
+ static buildOTAData(address: number, packetNumber: number, data: Uint8Array): number[];
43
+ /**
44
+ * Parse status response
45
+ */
46
+ static parseStatusResponse(data: number[]): {
47
+ doorLockStatus: number;
48
+ softwareVersion: {
49
+ major: number;
50
+ minor: number;
51
+ patch: number;
52
+ };
53
+ } | null;
54
+ /**
55
+ * Convert frame to hex string for debugging
56
+ */
57
+ static frameToHex(frame: number[]): string;
58
+ /**
59
+ * Convert hex string to frame
60
+ */
61
+ static hexToFrame(hex: string): number[];
62
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * IOBOARD Protocol utilities for frame construction and parsing
3
+ */
4
+ export class IOBoardProtocol {
5
+ /**
6
+ * Calculate CRC-16 CCITT checksum
7
+ */
8
+ static calculateCRC16(data) {
9
+ let crc = 0xFFFF;
10
+ for (const byte of data) {
11
+ crc ^= (byte << 8);
12
+ for (let bit = 0; bit < 8; bit++) {
13
+ if (crc & 0x8000) {
14
+ crc = (crc << 1) ^ 0x1021;
15
+ }
16
+ else {
17
+ crc = crc << 1;
18
+ }
19
+ crc &= 0xFFFF;
20
+ }
21
+ }
22
+ return crc;
23
+ }
24
+ /**
25
+ * Build a command frame for IOBOARD
26
+ */
27
+ static buildFrame(address, frameType, data) {
28
+ const frame = [];
29
+ // SOI
30
+ frame.push(0x0D);
31
+ // Address
32
+ frame.push(address);
33
+ // Frame Type
34
+ frame.push(frameType);
35
+ // Data
36
+ frame.push(...data);
37
+ // Calculate CRC for address + frameType + data
38
+ const crcData = [address, frameType, ...data];
39
+ const crc = this.calculateCRC16(crcData);
40
+ // CRC (high byte first)
41
+ frame.push((crc >> 8) & 0xFF);
42
+ frame.push(crc & 0xFF);
43
+ // EOI
44
+ frame.push(0x0A);
45
+ return frame;
46
+ }
47
+ /**
48
+ * Parse a response frame from IOBOARD
49
+ */
50
+ static parseFrame(frame) {
51
+ if (frame.length < 6) {
52
+ return { success: false, error: 'Frame too short' };
53
+ }
54
+ // Check SOI and EOI
55
+ if (frame[0] !== 0x0D || frame[frame.length - 1] !== 0x0A) {
56
+ return { success: false, error: 'Invalid frame markers' };
57
+ }
58
+ const address = frame[1];
59
+ const frameType = frame[2];
60
+ const dataLength = frame.length - 6; // SOI + Address + FrameType + CRC(2) + EOI
61
+ const data = frame.slice(3, 3 + dataLength);
62
+ // Extract CRC from frame
63
+ const receivedCRC = (frame[frame.length - 3] << 8) | frame[frame.length - 2];
64
+ // Calculate expected CRC
65
+ const crcData = [address, frameType, ...data];
66
+ const expectedCRC = this.calculateCRC16(crcData);
67
+ if (receivedCRC !== expectedCRC) {
68
+ return {
69
+ success: false,
70
+ error: `CRC mismatch: expected ${expectedCRC.toString(16)}, got ${receivedCRC.toString(16)}`
71
+ };
72
+ }
73
+ return {
74
+ success: true,
75
+ address,
76
+ frameType,
77
+ data
78
+ };
79
+ }
80
+ /**
81
+ * Build status query frame
82
+ */
83
+ static buildStatusQuery(address) {
84
+ return this.buildFrame(address, 0x00, []);
85
+ }
86
+ /**
87
+ * Build single pallet control frame
88
+ */
89
+ static buildSinglePalletControl(address, palletNumber, doorLock, ledControl) {
90
+ const data = [];
91
+ // Pallet number
92
+ data.push(palletNumber);
93
+ // Door lock control (1 = unlock, 0 = no action)
94
+ data.push(doorLock ? 1 : 0);
95
+ // LED control (6 bytes)
96
+ data.push(ledControl.red);
97
+ data.push(ledControl.green);
98
+ data.push(ledControl.blue);
99
+ data.push(ledControl.intensity);
100
+ data.push(ledControl.blinkTimes);
101
+ data.push(ledControl.blinkSpeed);
102
+ return this.buildFrame(address, 0x01, data);
103
+ }
104
+ /**
105
+ * Build full pallet control frame
106
+ */
107
+ static buildFullPalletControl(address, doorLockControl, extendedControl, ledControls) {
108
+ const data = [];
109
+ // Door lock control (8-bit mask)
110
+ data.push(doorLockControl);
111
+ // Extended control
112
+ data.push(extendedControl);
113
+ // LED controls for 8 pallets (48 bytes total)
114
+ for (let i = 0; i < 8; i++) {
115
+ const led = ledControls[i] || {
116
+ red: 0, green: 0, blue: 0,
117
+ intensity: 0, blinkTimes: 0, blinkSpeed: 0
118
+ };
119
+ data.push(led.red);
120
+ data.push(led.green);
121
+ data.push(led.blue);
122
+ data.push(led.intensity);
123
+ data.push(led.blinkTimes);
124
+ data.push(led.blinkSpeed);
125
+ }
126
+ return this.buildFrame(address, 0x02, data);
127
+ }
128
+ /**
129
+ * Build OTA notification frame
130
+ */
131
+ static buildOTANotification(address, majorVersion, minorVersion, patchVersion, firmwareSize) {
132
+ const data = [];
133
+ // Version (3 bytes)
134
+ data.push(majorVersion);
135
+ data.push(minorVersion);
136
+ data.push(patchVersion);
137
+ // Firmware size (4 bytes, little-endian)
138
+ data.push(firmwareSize & 0xFF);
139
+ data.push((firmwareSize >> 8) & 0xFF);
140
+ data.push((firmwareSize >> 16) & 0xFF);
141
+ data.push((firmwareSize >> 24) & 0xFF);
142
+ return this.buildFrame(address, 0xF1, data);
143
+ }
144
+ /**
145
+ * Build OTA data frame
146
+ */
147
+ static buildOTAData(address, packetNumber, data) {
148
+ const frameData = [];
149
+ // Packet number (4 bytes, little-endian)
150
+ frameData.push(packetNumber & 0xFF);
151
+ frameData.push((packetNumber >> 8) & 0xFF);
152
+ frameData.push((packetNumber >> 16) & 0xFF);
153
+ frameData.push((packetNumber >> 24) & 0xFF);
154
+ // Data bytes (up to 128 bytes)
155
+ for (let i = 0; i < data.length; i++) {
156
+ frameData.push(data[i]);
157
+ }
158
+ return this.buildFrame(address, 0xF2, frameData);
159
+ }
160
+ /**
161
+ * Parse status response
162
+ */
163
+ static parseStatusResponse(data) {
164
+ if (data.length < 4) {
165
+ return null;
166
+ }
167
+ return {
168
+ doorLockStatus: data[0],
169
+ softwareVersion: {
170
+ major: data[1],
171
+ minor: data[2],
172
+ patch: data[3]
173
+ }
174
+ };
175
+ }
176
+ /**
177
+ * Convert frame to hex string for debugging
178
+ */
179
+ static frameToHex(frame) {
180
+ return frame.map(b => b.toString(16).padStart(2, '0')).join(' ').toUpperCase();
181
+ }
182
+ /**
183
+ * Convert hex string to frame
184
+ */
185
+ static hexToFrame(hex) {
186
+ return hex.split(' ')
187
+ .map(h => h.trim())
188
+ .filter(h => h.length > 0)
189
+ .map(h => parseInt(h, 16));
190
+ }
191
+ }
@@ -0,0 +1,17 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { CapacitorIoboardPlugin, IOBoardResponse, IOBoardStatusResponse, DeviceOptions, UnlockPalletOptions, MultiplePalletOptions, ScanOptions, ScanResponse, OTANotificationOptions, OTADataOptions, SerialConnectionOptions } from './definitions';
3
+ export declare class CapacitorIoboardWeb extends WebPlugin implements CapacitorIoboardPlugin {
4
+ private isConnectedState;
5
+ connect(options: SerialConnectionOptions): Promise<IOBoardResponse>;
6
+ disconnect(): Promise<IOBoardResponse>;
7
+ isConnected(): Promise<{
8
+ connected: boolean;
9
+ }>;
10
+ getStatus(options: DeviceOptions): Promise<IOBoardStatusResponse>;
11
+ unlockPallet(options: UnlockPalletOptions): Promise<IOBoardResponse>;
12
+ controlMultiplePallets(options: MultiplePalletOptions): Promise<IOBoardResponse>;
13
+ scanDevices(options: ScanOptions): Promise<ScanResponse>;
14
+ sendOTANotification(options: OTANotificationOptions): Promise<IOBoardResponse>;
15
+ sendOTAData(options: OTADataOptions): Promise<IOBoardResponse>;
16
+ private getMaskBits;
17
+ }
@@ -0,0 +1,167 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class CapacitorIoboardWeb extends WebPlugin {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.isConnectedState = false;
6
+ }
7
+ async connect(options) {
8
+ console.log('IOBOARD Web: Connect', options);
9
+ // Simulate connection delay
10
+ await new Promise(resolve => setTimeout(resolve, 500));
11
+ this.isConnectedState = true;
12
+ return {
13
+ success: true,
14
+ message: `Connected to ${options.portPath || '/dev/ttyS2'} (web mock)`
15
+ };
16
+ }
17
+ async disconnect() {
18
+ console.log('IOBOARD Web: Disconnect');
19
+ this.isConnectedState = false;
20
+ return {
21
+ success: true,
22
+ message: 'Disconnected successfully (web mock)'
23
+ };
24
+ }
25
+ async isConnected() {
26
+ return { connected: this.isConnectedState };
27
+ }
28
+ async getStatus(options) {
29
+ console.log('IOBOARD Web: Get Status', options);
30
+ if (!this.isConnectedState) {
31
+ return {
32
+ success: false,
33
+ message: 'No serial connection established'
34
+ };
35
+ }
36
+ // Mock response
37
+ return {
38
+ success: true,
39
+ message: 'Status retrieved successfully (web mock)',
40
+ data: {
41
+ doorLockStatus: 0b11010001,
42
+ softwareVersion: {
43
+ major: 1,
44
+ minor: 2,
45
+ patch: 3
46
+ }
47
+ }
48
+ };
49
+ }
50
+ async unlockPallet(options) {
51
+ console.log('IOBOARD Web: Unlock Pallet', options);
52
+ if (!this.isConnectedState) {
53
+ return {
54
+ success: false,
55
+ message: 'No serial connection established'
56
+ };
57
+ }
58
+ // Validate pallet number
59
+ if (options.palletNumber < 0 || options.palletNumber > 7) {
60
+ return {
61
+ success: false,
62
+ message: 'Invalid pallet number. Must be 0-7'
63
+ };
64
+ }
65
+ // Mock response
66
+ return {
67
+ success: true,
68
+ message: `Pallet ${options.palletNumber} unlocked successfully (web mock)`,
69
+ data: {
70
+ address: options.address,
71
+ palletNumber: options.palletNumber,
72
+ ledColor: options.ledColor || 'green'
73
+ }
74
+ };
75
+ }
76
+ async controlMultiplePallets(options) {
77
+ console.log('IOBOARD Web: Control Multiple Pallets', options);
78
+ if (!this.isConnectedState) {
79
+ return {
80
+ success: false,
81
+ message: 'No serial connection established'
82
+ };
83
+ }
84
+ // Mock response
85
+ return {
86
+ success: true,
87
+ message: 'Multiple pallets controlled successfully (web mock)',
88
+ data: {
89
+ address: options.address,
90
+ doorLockMask: options.doorLockMask,
91
+ affectedPallets: this.getMaskBits(options.doorLockMask)
92
+ }
93
+ };
94
+ }
95
+ async scanDevices(options) {
96
+ console.log('IOBOARD Web: Scan Devices', options);
97
+ if (!this.isConnectedState) {
98
+ return {
99
+ success: false,
100
+ message: 'No serial connection established'
101
+ };
102
+ }
103
+ // Mock scan results
104
+ const devices = [];
105
+ const start = options.startAddress || 1;
106
+ const end = Math.min(options.endAddress || 63, start + 5); // Mock max 5 devices
107
+ for (let addr = start; addr <= end; addr++) {
108
+ devices.push({
109
+ address: addr,
110
+ responding: Math.random() > 0.7,
111
+ status: Math.random() > 0.7 ? { doorLockStatus: Math.floor(Math.random() * 256) } : undefined
112
+ });
113
+ }
114
+ return {
115
+ success: true,
116
+ message: `Scanned addresses ${start}-${end} (web mock)`,
117
+ devices
118
+ };
119
+ }
120
+ async sendOTANotification(options) {
121
+ console.log('IOBOARD Web: Send OTA Notification', options);
122
+ if (!this.isConnectedState) {
123
+ return {
124
+ success: false,
125
+ message: 'No serial connection established'
126
+ };
127
+ }
128
+ // Mock response
129
+ return {
130
+ success: true,
131
+ message: 'OTA notification sent successfully (web mock)',
132
+ data: {
133
+ address: options.address,
134
+ version: `${options.majorVersion}.${options.minorVersion}.${options.patchVersion}`,
135
+ firmwareSize: options.firmwareSize
136
+ }
137
+ };
138
+ }
139
+ async sendOTAData(options) {
140
+ console.log('IOBOARD Web: Send OTA Data', options);
141
+ if (!this.isConnectedState) {
142
+ return {
143
+ success: false,
144
+ message: 'No serial connection established'
145
+ };
146
+ }
147
+ // Mock response
148
+ return {
149
+ success: true,
150
+ message: `OTA data packet ${options.packetNumber} sent successfully (web mock)`,
151
+ data: {
152
+ address: options.address,
153
+ packetNumber: options.packetNumber,
154
+ dataLength: options.data.length
155
+ }
156
+ };
157
+ }
158
+ getMaskBits(mask) {
159
+ const bits = [];
160
+ for (let i = 0; i < 8; i++) {
161
+ if (mask & (1 << i)) {
162
+ bits.push(i);
163
+ }
164
+ }
165
+ return bits;
166
+ }
167
+ }