tauri-plugin-serialplugin-api 2.18.0 → 2.20.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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![npm version](https://img.shields.io/npm/v/tauri-plugin-serialplugin/latest?style=for-the-badge)](https://www.npmjs.com/package/tauri-plugin-serialplugin)
1
+ [![npm version](https://img.shields.io/npm/v/tauri-plugin-serialplugin-api/latest?style=for-the-badge)](https://www.npmjs.com/package/tauri-plugin-serialplugin-api)
2
2
  [![Crates.io](https://img.shields.io/crates/v/tauri-plugin-serialplugin?style=for-the-badge)](https://crates.io/crates/tauri-plugin-serialplugin)
3
3
  [![Documentation](https://img.shields.io/badge/docs-docs.rs-blue?style=for-the-badge)](https://docs.rs/tauri-plugin-serialplugin/latest/tauri_plugin_serialplugin/all.html)
4
4
  [![GitHub issues](https://img.shields.io/github/issues/s00d/tauri-plugin-serialplugin?style=for-the-badge)](https://github.com/s00d/tauri-plugin-serialplugin/issues)
@@ -18,22 +18,24 @@ A comprehensive plugin for Tauri applications to communicate with serial ports.
18
18
  1. [Installation](#installation)
19
19
  2. [Basic Usage](#basic-usage)
20
20
  3. [TypeScript Support](#typescript-support)
21
- 4. [Rust Usage](#rust-usage)
22
- 5. [Permissions](#permissions)
23
- 6. [API Reference](#api-reference)
24
- 6.1. [Port Discovery](#port-discovery)
25
- 6.2. [Connection Management](#connection-management)
26
- 6.3. [Data Transfer](#data-transfer)
27
- 6.4. [Port Configuration](#port-configuration)
28
- 6.5. [Control Signals](#control-signals)
29
- 6.6. [Buffer Management](#buffer-management)
30
- 7. [Common Use Cases](#common-use-cases)
31
- 8. [Android Setup](#android-setup)
32
- 9. [Contributing](#contributing)
33
- 10. [Development Setup](#development-setup)
34
- 11. [Testing](#testing)
35
- 12. [Partners](#partners)
36
- 13. [License](#license)
21
+ 4. [Log Level Control](#log-level-control)
22
+ 5. [Rust Usage](#rust-usage)
23
+ 6. [Permissions](#permissions)
24
+ 7. [API Reference](#api-reference)
25
+ 7.1. [Port Discovery](#port-discovery)
26
+ 7.2. [Connection Management](#connection-management)
27
+ 7.3. [Data Transfer](#data-transfer)
28
+ 7.4. [Port Configuration](#port-configuration)
29
+ 7.5. [Control Signals](#control-signals)
30
+ 7.6. [Buffer Management](#buffer-management)
31
+ 7.7. [Log Control](#log-control)
32
+ 8. [Common Use Cases](#common-use-cases)
33
+ 9. [Android Setup](#android-setup)
34
+ 10. [Contributing](#contributing)
35
+ 11. [Development Setup](#development-setup)
36
+ 12. [Testing](#testing)
37
+ 13. [Partners](#partners)
38
+ 14. [License](#license)
37
39
 
38
40
  ---
39
41
 
@@ -143,9 +145,12 @@ pnpm add tauri-plugin-serialplugin-api
143
145
  // Write data
144
146
  await port.write("Hello, Serial Port!");
145
147
 
148
+ // Start port listening
149
+ await port.startListening();
150
+
146
151
  // Start port listening
147
152
  const unsubscribe = await port.listen((data) => {
148
- console.log("Received:", data);
153
+ console.log("Received:", data);
149
154
  });
150
155
 
151
156
  // Stop listening when done
@@ -336,6 +341,113 @@ await port.clearBreak();
336
341
 
337
342
  ---
338
343
 
344
+ ## Log Level Control
345
+
346
+ The plugin provides comprehensive logging control to help you manage verbosity in production environments. By default, the plugin logs informational messages, but you can adjust this to reduce noise or enable detailed debugging.
347
+
348
+ ### TypeScript/JavaScript Usage
349
+
350
+ ```typescript
351
+ import { SerialPort, LogLevel } from "tauri-plugin-serialplugin-api";
352
+
353
+ // Disable all logs (recommended for production)
354
+ await SerialPort.setLogLevel(LogLevel.None);
355
+
356
+ // Show only errors
357
+ await SerialPort.setLogLevel(LogLevel.Error);
358
+
359
+ // Show errors and warnings
360
+ await SerialPort.setLogLevel(LogLevel.Warn);
361
+
362
+ // Show errors, warnings, and info (default)
363
+ await SerialPort.setLogLevel(LogLevel.Info);
364
+
365
+ // Enable all logs including debug information
366
+ await SerialPort.setLogLevel(LogLevel.Debug);
367
+
368
+ // Get current log level
369
+ const currentLevel = await SerialPort.getLogLevel();
370
+ console.log("Current log level:", currentLevel);
371
+ ```
372
+
373
+ ### Rust Usage
374
+
375
+ ```rust
376
+ use tauri_plugin_serialplugin::state::{LogLevel, set_log_level};
377
+
378
+ // Set log level on plugin initialization
379
+ fn main() {
380
+ // Disable logs in production
381
+ set_log_level(LogLevel::None);
382
+
383
+ tauri::Builder::default()
384
+ .plugin(tauri_plugin_serialplugin::init())
385
+ .run(tauri::generate_context!())
386
+ .expect("error while running tauri application");
387
+ }
388
+ ```
389
+
390
+ Or configure via command:
391
+
392
+ ```rust
393
+ use tauri_plugin_serialplugin::commands::set_log_level;
394
+ use tauri_plugin_serialplugin::state::LogLevel;
395
+ use tauri::{AppHandle, State};
396
+
397
+ #[tauri::command]
398
+ async fn configure_production_logging(
399
+ app: AppHandle<tauri::Wry>,
400
+ serial: State<'_, tauri_plugin_serialplugin::desktop_api::SerialPort<tauri::Wry>>
401
+ ) -> Result<(), String> {
402
+ // Only show errors in production
403
+ set_log_level(app, serial, LogLevel::Error)
404
+ .map_err(|e| e.to_string())
405
+ }
406
+ ```
407
+
408
+ ### Log Levels
409
+
410
+ - **`None`** - No logging output (recommended for production)
411
+ - **`Error`** - Only critical errors
412
+ - **`Warn`** - Errors and warnings
413
+ - **`Info`** - Errors, warnings, and general information (default)
414
+ - **`Debug`** - All logging including debug information (for development)
415
+
416
+ ### Common Use Cases
417
+
418
+ #### Production Environment
419
+
420
+ ```typescript
421
+ // Disable noisy logs when polling for available ports
422
+ await SerialPort.setLogLevel(LogLevel.None);
423
+
424
+ setInterval(async () => {
425
+ const ports = await SerialPort.available_ports();
426
+ // No "listen event: plugin-serialplugin-disconnected-COM6" logs
427
+ }, 1000);
428
+ ```
429
+
430
+ #### Development with Debugging
431
+
432
+ ```typescript
433
+ // Enable detailed logging for troubleshooting
434
+ await SerialPort.setLogLevel(LogLevel.Debug);
435
+
436
+ const port = new SerialPort({ path: "COM1", baudRate: 9600 });
437
+ await port.open();
438
+ // See all internal events and state changes
439
+ ```
440
+
441
+ #### Conditional Logging
442
+
443
+ ```typescript
444
+ // Set log level based on environment
445
+ const isDevelopment = import.meta.env.DEV;
446
+ await SerialPort.setLogLevel(isDevelopment ? LogLevel.Debug : LogLevel.Error);
447
+ ```
448
+
449
+ ---
450
+
339
451
  ## Rust Usage
340
452
 
341
453
  This plugin can also be used directly from Rust code in your Tauri backend. For complete API documentation, see [docs.rs](https://docs.rs/tauri-plugin-serialplugin/).
@@ -899,6 +1011,10 @@ Below is a list of all permissions the plugin supports. Granting or denying them
899
1011
  | `serialplugin:deny-start-listening` | Denies starting automatic port monitoring and data listening |
900
1012
  | `serialplugin:allow-stop-listening` | Allows stopping automatic port monitoring and data listening |
901
1013
  | `serialplugin:deny-stop-listening` | Denies stopping automatic port monitoring and data listening |
1014
+ | `serialplugin:allow-set-log-level` | Allows setting the global log level |
1015
+ | `serialplugin:deny-set-log-level` | Denies setting the global log level |
1016
+ | `serialplugin:allow-get-log-level` | Allows getting the current log level |
1017
+ | `serialplugin:deny-get-log-level` | Denies getting the current log level |
902
1018
 
903
1019
  ### Granting All Permissions (Example)
904
1020
 
@@ -934,7 +1050,9 @@ Below is a list of all permissions the plugin supports. Granting or denying them
934
1050
  "serialplugin:allow-set-break",
935
1051
  "serialplugin:allow-clear-break",
936
1052
  "serialplugin:allow-start-listening",
937
- "serialplugin:allow-stop-listening"
1053
+ "serialplugin:allow-stop-listening",
1054
+ "serialplugin:allow-set-log-level",
1055
+ "serialplugin:allow-get-log-level"
938
1056
  ]
939
1057
  ```
940
1058
 
@@ -1281,6 +1399,46 @@ class SerialPort {
1281
1399
  }
1282
1400
  ```
1283
1401
 
1402
+ ### Log Control
1403
+
1404
+ ```typescript
1405
+ class SerialPort {
1406
+ /**
1407
+ * Sets the global log level for the plugin
1408
+ * @param {LogLevel} level The log level to set (None, Error, Warn, Info, Debug)
1409
+ * @returns {Promise<void>}
1410
+ * @example
1411
+ * // Disable all logs in production
1412
+ * await SerialPort.setLogLevel(LogLevel.None);
1413
+ *
1414
+ * // Show only errors
1415
+ * await SerialPort.setLogLevel(LogLevel.Error);
1416
+ *
1417
+ * // Enable debug logs
1418
+ * await SerialPort.setLogLevel(LogLevel.Debug);
1419
+ */
1420
+ static async setLogLevel(level: LogLevel): Promise<void>;
1421
+
1422
+ /**
1423
+ * Gets the current global log level
1424
+ * @returns {Promise<LogLevel>} A promise that resolves to the current log level
1425
+ * @example
1426
+ * const currentLevel = await SerialPort.getLogLevel();
1427
+ * console.log("Current log level:", currentLevel);
1428
+ */
1429
+ static async getLogLevel(): Promise<LogLevel>;
1430
+ }
1431
+
1432
+ // Available log levels
1433
+ enum LogLevel {
1434
+ None = "None", // No logging output
1435
+ Error = "Error", // Only critical errors
1436
+ Warn = "Warn", // Errors and warnings
1437
+ Info = "Info", // Errors, warnings, and info (default)
1438
+ Debug = "Debug" // All logging including debug information
1439
+ }
1440
+ ```
1441
+
1284
1442
  ### Auto-Reconnect Management
1285
1443
 
1286
1444
  ```typescript
@@ -1355,6 +1513,7 @@ const port = new SerialPort({
1355
1513
  });
1356
1514
 
1357
1515
  await port.open();
1516
+ await port.startListening();
1358
1517
  await port.listen((data) => {
1359
1518
  const sensorValue = parseFloat(data);
1360
1519
  console.log("Sensor reading:", sensorValue);
@@ -1375,6 +1534,9 @@ await port.open();
1375
1534
  const command = new Uint8Array([0x02, 0x01, 0x03]);
1376
1535
  await port.writeBinary(command);
1377
1536
 
1537
+ // Start listening for response
1538
+ await port.startListening();
1539
+
1378
1540
  // Read response (raw bytes)
1379
1541
  await port.listen((data) => {
1380
1542
  const response = data instanceof Uint8Array ? data : new Uint8Array();
@@ -1433,6 +1595,7 @@ await port.enableAutoReconnect({
1433
1595
  });
1434
1596
 
1435
1597
  // Set up data listener
1598
+ await port.startListening();
1436
1599
  const unsubscribe = await port.listen((data) => {
1437
1600
  console.log("Received data:", data);
1438
1601
  });
package/dist-js/index.cjs CHANGED
@@ -3,6 +3,58 @@
3
3
  var core = require('@tauri-apps/api/core');
4
4
  var event = require('@tauri-apps/api/event');
5
5
 
6
+ // Centralized logger for the serial plugin
7
+ // Avoids circular dependencies and code duplication
8
+ // Global log level state
9
+ let currentLogLevel = "Info";
10
+ /**
11
+ * Sets the global log level
12
+ */
13
+ function setLogLevel(level) {
14
+ currentLogLevel = level;
15
+ }
16
+ /**
17
+ * Checks if a message should be logged based on current log level
18
+ */
19
+ function shouldLog(level) {
20
+ const levels = ["None", "Error", "Warn", "Info", "Debug"];
21
+ const currentIndex = levels.indexOf(currentLogLevel);
22
+ const requestedIndex = levels.indexOf(level);
23
+ return requestedIndex <= currentIndex && currentIndex > 0;
24
+ }
25
+ /**
26
+ * Logs an error message
27
+ */
28
+ function logError(...args) {
29
+ if (shouldLog("Error")) {
30
+ console.error(...args);
31
+ }
32
+ }
33
+ /**
34
+ * Logs a warning message
35
+ */
36
+ function logWarn(...args) {
37
+ if (shouldLog("Warn")) {
38
+ console.warn(...args);
39
+ }
40
+ }
41
+ /**
42
+ * Logs an info message
43
+ */
44
+ function logInfo(...args) {
45
+ if (shouldLog("Info")) {
46
+ console.log(...args);
47
+ }
48
+ }
49
+ /**
50
+ * Logs a debug message
51
+ */
52
+ function logDebug(...args) {
53
+ if (shouldLog("Debug")) {
54
+ console.log(...args);
55
+ }
56
+ }
57
+
6
58
  /**
7
59
  * Auto-reconnect manager for serial ports
8
60
  * Handles automatic reconnection logic with configurable settings
@@ -26,7 +78,7 @@ class AutoReconnectManager {
26
78
  */
27
79
  async enable(options) {
28
80
  if (this.enabled) {
29
- console.warn('Auto-reconnect is already enabled');
81
+ logWarn('Auto-reconnect is already enabled');
30
82
  return;
31
83
  }
32
84
  this.enabled = true;
@@ -35,7 +87,7 @@ class AutoReconnectManager {
35
87
  this.currentAttempts = 0;
36
88
  this.callback = options.onReconnect;
37
89
  this.reconnectFunction = options.reconnectFunction;
38
- console.log(`Auto-reconnect enabled: interval=${this.interval}ms, maxAttempts=${this.maxAttempts === null ? 'infinite' : this.maxAttempts}`);
90
+ logInfo(`Auto-reconnect enabled: interval=${this.interval}ms, maxAttempts=${this.maxAttempts === null ? 'infinite' : this.maxAttempts}`);
39
91
  }
40
92
  /**
41
93
  * @description Disables auto-reconnect functionality
@@ -43,7 +95,7 @@ class AutoReconnectManager {
43
95
  */
44
96
  async disable() {
45
97
  if (!this.enabled) {
46
- console.warn('Auto-reconnect is not enabled');
98
+ logWarn('Auto-reconnect is not enabled');
47
99
  return;
48
100
  }
49
101
  this.enabled = false;
@@ -55,7 +107,7 @@ class AutoReconnectManager {
55
107
  clearTimeout(this.timer);
56
108
  this.timer = null;
57
109
  }
58
- console.log('Auto-reconnect disabled');
110
+ logInfo('Auto-reconnect disabled');
59
111
  }
60
112
  /**
61
113
  * @description Gets auto-reconnect status and configuration
@@ -108,18 +160,18 @@ class AutoReconnectManager {
108
160
  }
109
161
  // Проверяем лимит ДО увеличения счётчика
110
162
  if (this.maxAttempts !== null && this.currentAttempts >= this.maxAttempts) {
111
- console.error(`Auto-reconnect failed after ${this.maxAttempts} attempts`);
163
+ logError(`Auto-reconnect failed after ${this.maxAttempts} attempts`);
112
164
  if (this.callback) {
113
165
  this.callback(false, this.currentAttempts);
114
166
  }
115
167
  return;
116
168
  }
117
169
  this.currentAttempts++;
118
- console.log(`Auto-reconnect attempt ${this.currentAttempts}${this.maxAttempts !== null ? `/${this.maxAttempts}` : ''}`);
170
+ logInfo(`Auto-reconnect attempt ${this.currentAttempts}${this.maxAttempts !== null ? `/${this.maxAttempts}` : ''}`);
119
171
  try {
120
172
  const success = await this.reconnectFunction();
121
173
  if (success) {
122
- console.log('Auto-reconnect successful');
174
+ logInfo('Auto-reconnect successful');
123
175
  this.currentAttempts = 0; // Reset counter on success
124
176
  if (this.callback) {
125
177
  this.callback(true, this.currentAttempts);
@@ -130,7 +182,7 @@ class AutoReconnectManager {
130
182
  }
131
183
  }
132
184
  catch (error) {
133
- console.error(`Auto-reconnect attempt ${this.currentAttempts} failed:`, error);
185
+ logError(`Auto-reconnect attempt ${this.currentAttempts} failed:`, error);
134
186
  if (this.callback) {
135
187
  this.callback(false, this.currentAttempts);
136
188
  }
@@ -175,7 +227,7 @@ class ListenerManager {
175
227
  }
176
228
  }
177
229
  catch (error) {
178
- console.warn(`Error in unlisten function for ${id}:`, error);
230
+ logWarn(`Error in unlisten function for ${id}:`, error);
179
231
  }
180
232
  };
181
233
  }
@@ -189,7 +241,7 @@ class ListenerManager {
189
241
  }
190
242
  }
191
243
  catch (error) {
192
- console.warn(`Error in unlisten function for ${id}:`, error);
244
+ logWarn(`Error in unlisten function for ${id}:`, error);
193
245
  }
194
246
  };
195
247
  }
@@ -327,19 +379,33 @@ class SerialPort {
327
379
  return await core.invoke('plugin:serialplugin|close_all');
328
380
  }
329
381
  /**
330
- * @description Gets the full serial number for a specific port
331
- * On Windows, this can fix truncated serial numbers (e.g., "B4" "B4:3D:4D:B1:5A:C8" for ESP32 devices)
332
- * @param {string} path The port path (e.g., "COM3", "/dev/ttyUSB0")
333
- * @returns {Promise<string>} A promise that resolves to the full serial number, or "Unknown" if not found
382
+ * @description Sets the global log level for the plugin
383
+ * @param {LogLevel} level The log level to set (None, Error, Warn, Info, Debug)
384
+ * @returns {Promise<void>} A promise that resolves when the log level is set
385
+ * @example
386
+ * // Disable all logs in production
387
+ * await SerialPort.setLogLevel(LogLevel.None);
388
+ *
389
+ * // Show only errors
390
+ * await SerialPort.setLogLevel(LogLevel.Error);
391
+ *
392
+ * // Enable all logs for debugging
393
+ * await SerialPort.setLogLevel(LogLevel.Debug);
334
394
  */
335
- static async get_full_serial_number(path) {
336
- try {
337
- const result = await core.invoke('plugin:serialplugin|get_full_serial_number', { path });
338
- return Promise.resolve(result);
339
- }
340
- catch (error) {
341
- return Promise.reject(error);
342
- }
395
+ static async setLogLevel(level) {
396
+ // Sync log level with internal logger
397
+ setLogLevel(level);
398
+ return await core.invoke('plugin:serialplugin|set_log_level', { level });
399
+ }
400
+ /**
401
+ * @description Gets the current global log level
402
+ * @returns {Promise<LogLevel>} A promise that resolves to the current log level
403
+ * @example
404
+ * const currentLevel = await SerialPort.getLogLevel();
405
+ * console.log("Current log level:", currentLevel);
406
+ */
407
+ static async getLogLevel() {
408
+ return await core.invoke('plugin:serialplugin|get_log_level');
343
409
  }
344
410
  /**
345
411
  * @description Cancels listening for serial port data (does not affect disconnect listeners)
@@ -356,7 +422,7 @@ class SerialPort {
356
422
  }
357
423
  }
358
424
  catch (error) {
359
- console.warn(`Error unlistening data listener ${id}:`, error);
425
+ logWarn(`Error unlistening data listener ${id}:`, error);
360
426
  }
361
427
  // Remove from map regardless of success/failure
362
428
  this.listeners.delete(id);
@@ -381,7 +447,7 @@ class SerialPort {
381
447
  }
382
448
  }
383
449
  catch (error) {
384
- console.warn(`Error unlistening listener ${id}:`, error);
450
+ logWarn(`Error unlistening listener ${id}:`, error);
385
451
  }
386
452
  // Remove from map regardless of success/failure
387
453
  this.listeners.delete(id);
@@ -461,7 +527,7 @@ class SerialPort {
461
527
  await this.cancelRead();
462
528
  }
463
529
  catch (cancelReadError) {
464
- console.warn('Error during cancelRead:', cancelReadError);
530
+ logWarn('Error during cancelRead:', cancelReadError);
465
531
  }
466
532
  // Closing the port
467
533
  let res = undefined;
@@ -471,20 +537,20 @@ class SerialPort {
471
537
  });
472
538
  }
473
539
  catch (closeError) {
474
- console.warn('Error during port close:', closeError);
540
+ logWarn('Error during port close:', closeError);
475
541
  }
476
542
  // Cancel all listeners
477
543
  try {
478
544
  await this.cancelAllListeners();
479
545
  }
480
546
  catch (cancelListenError) {
481
- console.warn('Error during cancelAllListeners:', cancelListenError);
547
+ logWarn('Error during cancelAllListeners:', cancelListenError);
482
548
  // Try to clear listeners manually as fallback
483
549
  try {
484
550
  this.listeners.clear();
485
551
  }
486
552
  catch (clearError) {
487
- console.warn('Error during manual listener clear:', clearError);
553
+ logWarn('Error during manual listener clear:', clearError);
488
554
  }
489
555
  }
490
556
  this.isOpen = false;
@@ -502,20 +568,20 @@ class SerialPort {
502
568
  async disconnected(fn) {
503
569
  let sub_path = this.options.path?.toString().replaceAll(".", "-").replaceAll("/", "-");
504
570
  let checkEvent = `plugin-serialplugin-disconnected-${sub_path}`;
505
- console.log('listen event: ' + checkEvent);
571
+ logDebug('listen event: ' + checkEvent);
506
572
  const unListenResult = await event.listen(checkEvent, () => {
507
573
  try {
508
574
  fn();
509
575
  }
510
576
  catch (error) {
511
- console.error(error);
577
+ logError(error);
512
578
  }
513
579
  });
514
580
  if (typeof unListenResult === 'function') {
515
581
  this.listeners.add('disconnect', unListenResult);
516
582
  }
517
583
  else {
518
- console.warn('disconnected() did not return a valid unlisten function');
584
+ logWarn('disconnected() did not return a valid unlisten function');
519
585
  }
520
586
  }
521
587
  /**
@@ -587,16 +653,16 @@ class SerialPort {
587
653
  async manualReconnect() {
588
654
  try {
589
655
  if (this.isOpen) {
590
- console.log('Port is already open, no need to reconnect');
656
+ logInfo('Port is already open, no need to reconnect');
591
657
  return true;
592
658
  }
593
- console.log('Manual reconnection attempt...');
659
+ logInfo('Manual reconnection attempt...');
594
660
  await this.open();
595
- console.log('Manual reconnection successful');
661
+ logInfo('Manual reconnection successful');
596
662
  return true;
597
663
  }
598
664
  catch (error) {
599
- console.error('Manual reconnection failed:', error);
665
+ logError('Manual reconnection failed:', error);
600
666
  return false;
601
667
  }
602
668
  }
@@ -613,7 +679,7 @@ class SerialPort {
613
679
  }
614
680
  let sub_path = this.options.path?.toString().replaceAll(".", "-").replaceAll("/", "-");
615
681
  let readEvent = `plugin-serialplugin-read-${sub_path}`;
616
- console.log('listen event: ' + readEvent);
682
+ logDebug('listen event: ' + readEvent);
617
683
  try {
618
684
  const unListenResult = await event.listen(readEvent, ({ payload }) => {
619
685
  try {
@@ -625,14 +691,14 @@ class SerialPort {
625
691
  fn(textData);
626
692
  }
627
693
  catch (error) {
628
- console.error('Error converting to text with configured encoding:', error);
694
+ logError('Error converting to text with configured encoding:', error);
629
695
  try {
630
696
  const fallbackDecoder = new TextDecoder('utf-8');
631
697
  const textData = fallbackDecoder.decode(uint8Array);
632
698
  fn(textData);
633
699
  }
634
700
  catch (fallbackError) {
635
- console.error('Fallback decoding also failed:', fallbackError);
701
+ logError('Fallback decoding also failed:', fallbackError);
636
702
  fn(String.fromCharCode(...uint8Array));
637
703
  }
638
704
  }
@@ -642,19 +708,19 @@ class SerialPort {
642
708
  }
643
709
  }
644
710
  catch (error) {
645
- console.error(error);
711
+ logError(error);
646
712
  }
647
713
  });
648
714
  if (typeof unListenResult === 'function') {
649
715
  return this.listeners.add('data', unListenResult);
650
716
  }
651
717
  else {
652
- console.warn('listen() did not return a valid unlisten function');
718
+ logWarn('listen() did not return a valid unlisten function');
653
719
  return Promise.reject('Failed to get unlisten function');
654
720
  }
655
721
  }
656
722
  catch (listenError) {
657
- console.error('Error setting up listener:', listenError);
723
+ logError('Error setting up listener:', listenError);
658
724
  throw listenError;
659
725
  }
660
726
  }
@@ -689,7 +755,7 @@ class SerialPort {
689
755
  this.isOpen = true;
690
756
  this.disconnected(() => {
691
757
  this.isOpen = false;
692
- }).catch(err => console.error(err));
758
+ }).catch(err => logError(err));
693
759
  return Promise.resolve(res);
694
760
  }
695
761
  catch (error) {
@@ -1,4 +1,5 @@
1
1
  import { UnlistenFn } from '@tauri-apps/api/event';
2
+ import { type LogLevel } from './logger';
2
3
  export interface PortInfo {
3
4
  path: "Unknown" | string;
4
5
  manufacturer: "Unknown" | string;
@@ -115,12 +116,28 @@ declare class SerialPort {
115
116
  */
116
117
  static closeAll(): Promise<void>;
117
118
  /**
118
- * @description Gets the full serial number for a specific port
119
- * On Windows, this can fix truncated serial numbers (e.g., "B4" "B4:3D:4D:B1:5A:C8" for ESP32 devices)
120
- * @param {string} path The port path (e.g., "COM3", "/dev/ttyUSB0")
121
- * @returns {Promise<string>} A promise that resolves to the full serial number, or "Unknown" if not found
119
+ * @description Sets the global log level for the plugin
120
+ * @param {LogLevel} level The log level to set (None, Error, Warn, Info, Debug)
121
+ * @returns {Promise<void>} A promise that resolves when the log level is set
122
+ * @example
123
+ * // Disable all logs in production
124
+ * await SerialPort.setLogLevel(LogLevel.None);
125
+ *
126
+ * // Show only errors
127
+ * await SerialPort.setLogLevel(LogLevel.Error);
128
+ *
129
+ * // Enable all logs for debugging
130
+ * await SerialPort.setLogLevel(LogLevel.Debug);
131
+ */
132
+ static setLogLevel(level: LogLevel): Promise<void>;
133
+ /**
134
+ * @description Gets the current global log level
135
+ * @returns {Promise<LogLevel>} A promise that resolves to the current log level
136
+ * @example
137
+ * const currentLevel = await SerialPort.getLogLevel();
138
+ * console.log("Current log level:", currentLevel);
122
139
  */
123
- static get_full_serial_number(path: string): Promise<string>;
140
+ static getLogLevel(): Promise<LogLevel>;
124
141
  /**
125
142
  * @description Cancels listening for serial port data (does not affect disconnect listeners)
126
143
  * @returns {Promise<void>} A promise that resolves when listening is cancelled
@@ -370,3 +387,4 @@ declare class SerialPort {
370
387
  writeBinary(value: Uint8Array | number[]): Promise<number>;
371
388
  }
372
389
  export { SerialPort };
390
+ export type { LogLevel };
package/dist-js/index.js CHANGED
@@ -1,6 +1,58 @@
1
1
  import { invoke } from '@tauri-apps/api/core';
2
2
  import { listen } from '@tauri-apps/api/event';
3
3
 
4
+ // Centralized logger for the serial plugin
5
+ // Avoids circular dependencies and code duplication
6
+ // Global log level state
7
+ let currentLogLevel = "Info";
8
+ /**
9
+ * Sets the global log level
10
+ */
11
+ function setLogLevel(level) {
12
+ currentLogLevel = level;
13
+ }
14
+ /**
15
+ * Checks if a message should be logged based on current log level
16
+ */
17
+ function shouldLog(level) {
18
+ const levels = ["None", "Error", "Warn", "Info", "Debug"];
19
+ const currentIndex = levels.indexOf(currentLogLevel);
20
+ const requestedIndex = levels.indexOf(level);
21
+ return requestedIndex <= currentIndex && currentIndex > 0;
22
+ }
23
+ /**
24
+ * Logs an error message
25
+ */
26
+ function logError(...args) {
27
+ if (shouldLog("Error")) {
28
+ console.error(...args);
29
+ }
30
+ }
31
+ /**
32
+ * Logs a warning message
33
+ */
34
+ function logWarn(...args) {
35
+ if (shouldLog("Warn")) {
36
+ console.warn(...args);
37
+ }
38
+ }
39
+ /**
40
+ * Logs an info message
41
+ */
42
+ function logInfo(...args) {
43
+ if (shouldLog("Info")) {
44
+ console.log(...args);
45
+ }
46
+ }
47
+ /**
48
+ * Logs a debug message
49
+ */
50
+ function logDebug(...args) {
51
+ if (shouldLog("Debug")) {
52
+ console.log(...args);
53
+ }
54
+ }
55
+
4
56
  /**
5
57
  * Auto-reconnect manager for serial ports
6
58
  * Handles automatic reconnection logic with configurable settings
@@ -24,7 +76,7 @@ class AutoReconnectManager {
24
76
  */
25
77
  async enable(options) {
26
78
  if (this.enabled) {
27
- console.warn('Auto-reconnect is already enabled');
79
+ logWarn('Auto-reconnect is already enabled');
28
80
  return;
29
81
  }
30
82
  this.enabled = true;
@@ -33,7 +85,7 @@ class AutoReconnectManager {
33
85
  this.currentAttempts = 0;
34
86
  this.callback = options.onReconnect;
35
87
  this.reconnectFunction = options.reconnectFunction;
36
- console.log(`Auto-reconnect enabled: interval=${this.interval}ms, maxAttempts=${this.maxAttempts === null ? 'infinite' : this.maxAttempts}`);
88
+ logInfo(`Auto-reconnect enabled: interval=${this.interval}ms, maxAttempts=${this.maxAttempts === null ? 'infinite' : this.maxAttempts}`);
37
89
  }
38
90
  /**
39
91
  * @description Disables auto-reconnect functionality
@@ -41,7 +93,7 @@ class AutoReconnectManager {
41
93
  */
42
94
  async disable() {
43
95
  if (!this.enabled) {
44
- console.warn('Auto-reconnect is not enabled');
96
+ logWarn('Auto-reconnect is not enabled');
45
97
  return;
46
98
  }
47
99
  this.enabled = false;
@@ -53,7 +105,7 @@ class AutoReconnectManager {
53
105
  clearTimeout(this.timer);
54
106
  this.timer = null;
55
107
  }
56
- console.log('Auto-reconnect disabled');
108
+ logInfo('Auto-reconnect disabled');
57
109
  }
58
110
  /**
59
111
  * @description Gets auto-reconnect status and configuration
@@ -106,18 +158,18 @@ class AutoReconnectManager {
106
158
  }
107
159
  // Проверяем лимит ДО увеличения счётчика
108
160
  if (this.maxAttempts !== null && this.currentAttempts >= this.maxAttempts) {
109
- console.error(`Auto-reconnect failed after ${this.maxAttempts} attempts`);
161
+ logError(`Auto-reconnect failed after ${this.maxAttempts} attempts`);
110
162
  if (this.callback) {
111
163
  this.callback(false, this.currentAttempts);
112
164
  }
113
165
  return;
114
166
  }
115
167
  this.currentAttempts++;
116
- console.log(`Auto-reconnect attempt ${this.currentAttempts}${this.maxAttempts !== null ? `/${this.maxAttempts}` : ''}`);
168
+ logInfo(`Auto-reconnect attempt ${this.currentAttempts}${this.maxAttempts !== null ? `/${this.maxAttempts}` : ''}`);
117
169
  try {
118
170
  const success = await this.reconnectFunction();
119
171
  if (success) {
120
- console.log('Auto-reconnect successful');
172
+ logInfo('Auto-reconnect successful');
121
173
  this.currentAttempts = 0; // Reset counter on success
122
174
  if (this.callback) {
123
175
  this.callback(true, this.currentAttempts);
@@ -128,7 +180,7 @@ class AutoReconnectManager {
128
180
  }
129
181
  }
130
182
  catch (error) {
131
- console.error(`Auto-reconnect attempt ${this.currentAttempts} failed:`, error);
183
+ logError(`Auto-reconnect attempt ${this.currentAttempts} failed:`, error);
132
184
  if (this.callback) {
133
185
  this.callback(false, this.currentAttempts);
134
186
  }
@@ -173,7 +225,7 @@ class ListenerManager {
173
225
  }
174
226
  }
175
227
  catch (error) {
176
- console.warn(`Error in unlisten function for ${id}:`, error);
228
+ logWarn(`Error in unlisten function for ${id}:`, error);
177
229
  }
178
230
  };
179
231
  }
@@ -187,7 +239,7 @@ class ListenerManager {
187
239
  }
188
240
  }
189
241
  catch (error) {
190
- console.warn(`Error in unlisten function for ${id}:`, error);
242
+ logWarn(`Error in unlisten function for ${id}:`, error);
191
243
  }
192
244
  };
193
245
  }
@@ -325,19 +377,33 @@ class SerialPort {
325
377
  return await invoke('plugin:serialplugin|close_all');
326
378
  }
327
379
  /**
328
- * @description Gets the full serial number for a specific port
329
- * On Windows, this can fix truncated serial numbers (e.g., "B4" "B4:3D:4D:B1:5A:C8" for ESP32 devices)
330
- * @param {string} path The port path (e.g., "COM3", "/dev/ttyUSB0")
331
- * @returns {Promise<string>} A promise that resolves to the full serial number, or "Unknown" if not found
380
+ * @description Sets the global log level for the plugin
381
+ * @param {LogLevel} level The log level to set (None, Error, Warn, Info, Debug)
382
+ * @returns {Promise<void>} A promise that resolves when the log level is set
383
+ * @example
384
+ * // Disable all logs in production
385
+ * await SerialPort.setLogLevel(LogLevel.None);
386
+ *
387
+ * // Show only errors
388
+ * await SerialPort.setLogLevel(LogLevel.Error);
389
+ *
390
+ * // Enable all logs for debugging
391
+ * await SerialPort.setLogLevel(LogLevel.Debug);
332
392
  */
333
- static async get_full_serial_number(path) {
334
- try {
335
- const result = await invoke('plugin:serialplugin|get_full_serial_number', { path });
336
- return Promise.resolve(result);
337
- }
338
- catch (error) {
339
- return Promise.reject(error);
340
- }
393
+ static async setLogLevel(level) {
394
+ // Sync log level with internal logger
395
+ setLogLevel(level);
396
+ return await invoke('plugin:serialplugin|set_log_level', { level });
397
+ }
398
+ /**
399
+ * @description Gets the current global log level
400
+ * @returns {Promise<LogLevel>} A promise that resolves to the current log level
401
+ * @example
402
+ * const currentLevel = await SerialPort.getLogLevel();
403
+ * console.log("Current log level:", currentLevel);
404
+ */
405
+ static async getLogLevel() {
406
+ return await invoke('plugin:serialplugin|get_log_level');
341
407
  }
342
408
  /**
343
409
  * @description Cancels listening for serial port data (does not affect disconnect listeners)
@@ -354,7 +420,7 @@ class SerialPort {
354
420
  }
355
421
  }
356
422
  catch (error) {
357
- console.warn(`Error unlistening data listener ${id}:`, error);
423
+ logWarn(`Error unlistening data listener ${id}:`, error);
358
424
  }
359
425
  // Remove from map regardless of success/failure
360
426
  this.listeners.delete(id);
@@ -379,7 +445,7 @@ class SerialPort {
379
445
  }
380
446
  }
381
447
  catch (error) {
382
- console.warn(`Error unlistening listener ${id}:`, error);
448
+ logWarn(`Error unlistening listener ${id}:`, error);
383
449
  }
384
450
  // Remove from map regardless of success/failure
385
451
  this.listeners.delete(id);
@@ -459,7 +525,7 @@ class SerialPort {
459
525
  await this.cancelRead();
460
526
  }
461
527
  catch (cancelReadError) {
462
- console.warn('Error during cancelRead:', cancelReadError);
528
+ logWarn('Error during cancelRead:', cancelReadError);
463
529
  }
464
530
  // Closing the port
465
531
  let res = undefined;
@@ -469,20 +535,20 @@ class SerialPort {
469
535
  });
470
536
  }
471
537
  catch (closeError) {
472
- console.warn('Error during port close:', closeError);
538
+ logWarn('Error during port close:', closeError);
473
539
  }
474
540
  // Cancel all listeners
475
541
  try {
476
542
  await this.cancelAllListeners();
477
543
  }
478
544
  catch (cancelListenError) {
479
- console.warn('Error during cancelAllListeners:', cancelListenError);
545
+ logWarn('Error during cancelAllListeners:', cancelListenError);
480
546
  // Try to clear listeners manually as fallback
481
547
  try {
482
548
  this.listeners.clear();
483
549
  }
484
550
  catch (clearError) {
485
- console.warn('Error during manual listener clear:', clearError);
551
+ logWarn('Error during manual listener clear:', clearError);
486
552
  }
487
553
  }
488
554
  this.isOpen = false;
@@ -500,20 +566,20 @@ class SerialPort {
500
566
  async disconnected(fn) {
501
567
  let sub_path = this.options.path?.toString().replaceAll(".", "-").replaceAll("/", "-");
502
568
  let checkEvent = `plugin-serialplugin-disconnected-${sub_path}`;
503
- console.log('listen event: ' + checkEvent);
569
+ logDebug('listen event: ' + checkEvent);
504
570
  const unListenResult = await listen(checkEvent, () => {
505
571
  try {
506
572
  fn();
507
573
  }
508
574
  catch (error) {
509
- console.error(error);
575
+ logError(error);
510
576
  }
511
577
  });
512
578
  if (typeof unListenResult === 'function') {
513
579
  this.listeners.add('disconnect', unListenResult);
514
580
  }
515
581
  else {
516
- console.warn('disconnected() did not return a valid unlisten function');
582
+ logWarn('disconnected() did not return a valid unlisten function');
517
583
  }
518
584
  }
519
585
  /**
@@ -585,16 +651,16 @@ class SerialPort {
585
651
  async manualReconnect() {
586
652
  try {
587
653
  if (this.isOpen) {
588
- console.log('Port is already open, no need to reconnect');
654
+ logInfo('Port is already open, no need to reconnect');
589
655
  return true;
590
656
  }
591
- console.log('Manual reconnection attempt...');
657
+ logInfo('Manual reconnection attempt...');
592
658
  await this.open();
593
- console.log('Manual reconnection successful');
659
+ logInfo('Manual reconnection successful');
594
660
  return true;
595
661
  }
596
662
  catch (error) {
597
- console.error('Manual reconnection failed:', error);
663
+ logError('Manual reconnection failed:', error);
598
664
  return false;
599
665
  }
600
666
  }
@@ -611,7 +677,7 @@ class SerialPort {
611
677
  }
612
678
  let sub_path = this.options.path?.toString().replaceAll(".", "-").replaceAll("/", "-");
613
679
  let readEvent = `plugin-serialplugin-read-${sub_path}`;
614
- console.log('listen event: ' + readEvent);
680
+ logDebug('listen event: ' + readEvent);
615
681
  try {
616
682
  const unListenResult = await listen(readEvent, ({ payload }) => {
617
683
  try {
@@ -623,14 +689,14 @@ class SerialPort {
623
689
  fn(textData);
624
690
  }
625
691
  catch (error) {
626
- console.error('Error converting to text with configured encoding:', error);
692
+ logError('Error converting to text with configured encoding:', error);
627
693
  try {
628
694
  const fallbackDecoder = new TextDecoder('utf-8');
629
695
  const textData = fallbackDecoder.decode(uint8Array);
630
696
  fn(textData);
631
697
  }
632
698
  catch (fallbackError) {
633
- console.error('Fallback decoding also failed:', fallbackError);
699
+ logError('Fallback decoding also failed:', fallbackError);
634
700
  fn(String.fromCharCode(...uint8Array));
635
701
  }
636
702
  }
@@ -640,19 +706,19 @@ class SerialPort {
640
706
  }
641
707
  }
642
708
  catch (error) {
643
- console.error(error);
709
+ logError(error);
644
710
  }
645
711
  });
646
712
  if (typeof unListenResult === 'function') {
647
713
  return this.listeners.add('data', unListenResult);
648
714
  }
649
715
  else {
650
- console.warn('listen() did not return a valid unlisten function');
716
+ logWarn('listen() did not return a valid unlisten function');
651
717
  return Promise.reject('Failed to get unlisten function');
652
718
  }
653
719
  }
654
720
  catch (listenError) {
655
- console.error('Error setting up listener:', listenError);
721
+ logError('Error setting up listener:', listenError);
656
722
  throw listenError;
657
723
  }
658
724
  }
@@ -687,7 +753,7 @@ class SerialPort {
687
753
  this.isOpen = true;
688
754
  this.disconnected(() => {
689
755
  this.isOpen = false;
690
- }).catch(err => console.error(err));
756
+ }).catch(err => logError(err));
691
757
  return Promise.resolve(res);
692
758
  }
693
759
  catch (error) {
@@ -0,0 +1,25 @@
1
+ export type LogLevel = "None" | "Error" | "Warn" | "Info" | "Debug";
2
+ /**
3
+ * Sets the global log level
4
+ */
5
+ export declare function setLogLevel(level: LogLevel): void;
6
+ /**
7
+ * Gets the current log level
8
+ */
9
+ export declare function getLogLevel(): LogLevel;
10
+ /**
11
+ * Logs an error message
12
+ */
13
+ export declare function logError(...args: any[]): void;
14
+ /**
15
+ * Logs a warning message
16
+ */
17
+ export declare function logWarn(...args: any[]): void;
18
+ /**
19
+ * Logs an info message
20
+ */
21
+ export declare function logInfo(...args: any[]): void;
22
+ /**
23
+ * Logs a debug message
24
+ */
25
+ export declare function logDebug(...args: any[]): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tauri-plugin-serialplugin-api",
3
- "version": "2.18.0",
3
+ "version": "2.20.0",
4
4
  "license": "MIT or APACHE-2.0",
5
5
  "type": "module",
6
6
  "types": "./dist-js/index.d.ts",