@alexgyver/serial 1.0.11 → 1.0.13

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,32 @@
1
+
2
+ name: Publish to PlatformIO Registry
3
+
4
+ on:
5
+ release:
6
+ types: [published]
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout release tag
14
+ uses: actions/checkout@v4
15
+ with:
16
+ ref: ${{ github.event.release.tag_name }}
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.x"
22
+
23
+ - name: Install PlatformIO
24
+ run: pip install -U platformio
25
+
26
+ - name: Check package
27
+ run: pio pkg pack
28
+
29
+ - name: Publish to PlatformIO Registry
30
+ env:
31
+ PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }}
32
+ run: pio pkg publish --owner gyverlibs --non-interactive
@@ -0,0 +1,35 @@
1
+
2
+ name: Telegram Message
3
+ on:
4
+ release:
5
+ types: [published]
6
+ jobs:
7
+ build:
8
+ name: Send Message
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Check if major or minor release
12
+ id: semver
13
+ shell: bash
14
+ run: |
15
+ TAG="${{ github.event.release.tag_name }}"
16
+ VERSION="${TAG#v}"
17
+ PATCH="$(echo "$VERSION" | cut -d. -f3)"
18
+
19
+ if [[ "$PATCH" == "0" ]]; then
20
+ echo "should_send=true" >> "$GITHUB_OUTPUT"
21
+ else
22
+ echo "should_send=false" >> "$GITHUB_OUTPUT"
23
+ fi
24
+
25
+ - name: send telegram message on release
26
+ if: steps.semver.outputs.should_send == 'true'
27
+ uses: appleboy/telegram-action@master
28
+ with:
29
+ to: ${{ secrets.TELEGRAM_TO }}
30
+ token: ${{ secrets.TELEGRAM_TOKEN }}
31
+ disable_web_page_preview: true
32
+ message: |
33
+ ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}
34
+ ${{ github.event.release.body }}
35
+ https://github.com/${{ github.repository }}
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Serial.js
2
2
  Обёртка на Web Serial API
3
3
  - Автоматическое переподключение
4
- - Сохранение последнего выбранного порта
4
+ - Восстановление ранее разрешённого порта
5
+ - Безопасная работа с несколькими разрешёнными портами
5
6
  - Буферизация отправки
6
7
  - Буферизация приёма, разделение текста по разделителю
7
8
 
@@ -17,8 +18,14 @@ constructor(params = {});
17
18
  config(params = {});
18
19
  // eol: /\r?\n/
19
20
  // baud: 115200
21
+ // dataBits: 8
22
+ // stopBits: 1
23
+ // parity: 'none'
24
+ // bufferSize: 255
25
+ // flowControl: 'none'
20
26
  // auto_open: false
21
27
  // reconnect: 1000
28
+ // forgetOtherPorts: true
22
29
 
23
30
  onbin(b);
24
31
  ontext(t);
@@ -32,12 +39,40 @@ onerror(e);
32
39
  static supported();
33
40
  opened();
34
41
  selected();
42
+ getInfo();
35
43
  getName();
36
44
 
37
45
  select();
46
+ restore();
47
+ getPorts();
48
+ forget();
38
49
  open();
39
50
  close();
40
51
 
41
52
  sendBin(data);
42
53
  sendText(text);
43
- ```
54
+ ```
55
+
56
+ `select()` использует порт, возвращённый системным диалогом выбора. По умолчанию `forgetOtherPorts: true` - разрешение остаётся только у выбранного порта, поэтому после перезагрузки страницы его можно восстановить автоматически.
57
+
58
+ Для приложений, работающих с несколькими портами, нужен `forgetOtherPorts: false`. В этом режиме библиотека не отзывает чужие разрешения, а `restore()` автоматически выбирает порт только тогда, когда разрешён ровно один. Если портов несколько, надо вызвать `select()`.
59
+
60
+ - `getPorts()` возвращает все ранее разрешённые порты
61
+ - `restore()` восстанавливает порт без системного диалога, если выбор однозначен
62
+ - `forget()` закрывает текущий порт и отзывает только его разрешение
63
+
64
+ ## Настройки порта
65
+
66
+ При открытии библиотека передаёт в `SerialPort.open()` параметры `baud`, `dataBits`, `stopBits`, `parity`, `bufferSize` и `flowControl`. Например:
67
+
68
+ ```js
69
+ const serial = new SerialJS({
70
+ baud: 115200,
71
+ bufferSize: 4096,
72
+ flowControl: 'hardware',
73
+ });
74
+ ```
75
+
76
+ `flowControl: 'hardware'` включает RTS/CTS и работает только при поддержке со стороны порта и физического подключения. Значение по умолчанию — `'none'`.
77
+
78
+ `sendBin()` последовательно выполняет отправки и ожидает Promise от `writer.write()`. Это использует backpressure Web Serial, но не является прикладным подтверждением обработки данных устройством.
package/README_EN.md ADDED
@@ -0,0 +1,70 @@
1
+ This is an automatic translation and may be incorrect in some places. See the source README and examples for authoritative information.
2
+
3
+ # Serial.js
4
+ Wrapper on Web Serial API
5
+ - Automatic reconnection
6
+ - Restoration of a previously authorized port
7
+ - Safe handling of multiple authorized ports
8
+ - Sequential writes
9
+ - Buffered text reception and splitting
10
+
11
+ [demo](https://gyverlibs.github.io/Serial.js/test/)
12
+
13
+ > **Browser**: https://gyverlibs.github.io/Serial.js/Serial.min.js
14
+
15
+ > **Node**: npm i @alexgyver/serial
16
+
17
+ ## Doc.
18
+ ```js
19
+ constructor(params = {});
20
+ config(params = {});
21
+ // eol: /\r?\n/
22
+ // baud: 115200
23
+ // dataBits: 8
24
+ // stopBits: 1
25
+ // parity: 'none'
26
+ // bufferSize: 255
27
+ // flowControl: 'none'
28
+ // auto_open: false
29
+ // reconnect: 1000
30
+ // forgetOtherPorts: true
31
+
32
+ onbin(b);
33
+ ontext(t);
34
+
35
+ onopen():
36
+ onclose():
37
+ onchange(s):
38
+ onselect(name);
39
+ onerror(e);
40
+
41
+ static supported();
42
+ opened();
43
+ selected();
44
+ getInfo();
45
+ getName();
46
+
47
+ select();
48
+ restore();
49
+ getPorts();
50
+ forget();
51
+ open();
52
+ close();
53
+
54
+ sendBin(data);
55
+ sendText(text);
56
+ ```
57
+
58
+ The library passes `baud`, `dataBits`, `stopBits`, `parity`, `bufferSize`, and `flowControl` to `SerialPort.open()`. For example:
59
+
60
+ ```js
61
+ const serial = new SerialJS({
62
+ baud: 115200,
63
+ bufferSize: 4096,
64
+ flowControl: 'hardware',
65
+ });
66
+ ```
67
+
68
+ Hardware flow control uses RTS/CTS and only works when both the port and the physical connection support it. The default is `'none'`.
69
+
70
+ `sendBin()` serializes writes and awaits the Promise returned by `writer.write()`. This applies Web Serial backpressure, but it does not confirm that the remote application has processed the data.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgyver/serial",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Web Serial API wrapper",
5
5
  "main": "./serial.js",
6
6
  "module": "./serial.js",
@@ -14,14 +14,14 @@
14
14
  "url": "git+https://github.com/GyverLibs/Serial.js.git"
15
15
  },
16
16
  "devDependencies": {
17
- "webpack": "^5.106.2",
18
- "webpack-cli": "^7.0.2",
19
- "webpack-dev-server": "^5.2.4",
17
+ "webpack": "^5.110.3",
18
+ "webpack-cli": "^7.2.3",
19
+ "webpack-dev-server": "^6.0.0",
20
20
  "style-loader": "^4.0.0",
21
- "css-loader": "^7.1.4"
21
+ "css-loader": "^7.1.5"
22
22
  },
23
23
  "dependencies": {
24
- "@alexgyver/utils": "^1.2.8"
24
+ "@alexgyver/utils": "^1.2.18"
25
25
  },
26
26
  "author": "AlexGyver <alex@alexgyver.ru>",
27
27
  "license": "MIT"
package/serial.js CHANGED
@@ -23,12 +23,18 @@ export default class SerialJS {
23
23
  const def = {
24
24
  eol: /\r?\n/,
25
25
  baud: 115200,
26
+ dataBits: 8,
27
+ stopBits: 1,
28
+ parity: 'none',
29
+ bufferSize: 255,
30
+ flowControl: 'none',
26
31
  auto_open: false,
27
32
  reconnect: 1000,
33
+ forgetOtherPorts: true,
28
34
  };
29
35
  this.cfg = { ...def, ...params };
30
36
 
31
- this._setLastPort().then(() => this.onselect(this.getName()));
37
+ this.restore().then(() => this.onselect(this.getName()));
32
38
  this.splitter = new StreamSplitter(this.cfg.eol);
33
39
  this.splitter.ontext = (t) => {
34
40
  try {
@@ -57,10 +63,14 @@ export default class SerialJS {
57
63
  return !!this._port;
58
64
  }
59
65
 
66
+ getInfo() {
67
+ return this._port?.getInfo() ?? null;
68
+ }
69
+
60
70
  getName() {
61
71
  if (!this._port) return 'None';
62
72
 
63
- switch (this._port.getInfo().usbProductId) {
73
+ switch (this.getInfo().usbProductId) {
64
74
  case 0x55d3: return 'CH343';
65
75
  case 0x7584: return 'CH340S';
66
76
  case 0x7522: case 0x7523: return 'CH340';
@@ -74,48 +84,87 @@ export default class SerialJS {
74
84
  async select() {
75
85
  try {
76
86
  await this.close();
77
- this._port = null;
78
- let ports = await navigator.serial.getPorts();
79
- for (let p of ports) await p.forget();
80
- await sleep(50);
81
- await navigator.serial.requestPort();
82
- await this._setLastPort();
87
+ const port = await navigator.serial.requestPort();
88
+
89
+ if (this.cfg.forgetOtherPorts) {
90
+ const ports = await navigator.serial.getPorts();
91
+ for (const p of ports) {
92
+ if (p !== port) await p.forget();
93
+ }
94
+ }
95
+
96
+ this._port = port;
83
97
  } catch (e) {
84
98
  this._error(e);
85
99
  }
86
100
  this.onselect(this.getName());
87
- if (this.cfg.auto_open) this.open();
101
+ if (this.cfg.auto_open) return this.open();
102
+ return this.selected();
103
+ }
104
+
105
+ async getPorts() {
106
+ return navigator.serial.getPorts();
107
+ }
108
+
109
+ async restore() {
110
+ const ports = await this.getPorts();
111
+ this._port = this.cfg.forgetOtherPorts
112
+ ? (ports[0] ?? null)
113
+ : (ports.length === 1 ? ports[0] : null);
88
114
  return this.selected();
89
115
  }
90
116
 
117
+ async forget() {
118
+ await this.close();
119
+ if (!this._port) return false;
120
+
121
+ const port = this._port;
122
+ this._port = null;
123
+
124
+ try {
125
+ await port.forget();
126
+ this.onselect(this.getName());
127
+ return true;
128
+ } catch (e) {
129
+ this._port = port;
130
+ this._error(e);
131
+ return false;
132
+ }
133
+ }
134
+
91
135
  async open() {
92
- const ports = await navigator.serial.getPorts();
93
- if (!ports.length) return false;
136
+ if (this.opened()) return true;
137
+ if (this._openPromise) return this._openPromise;
138
+ if (this._state !== SerialJS.State.Closed) return false;
94
139
 
95
140
  if (this.cfg.reconnect) this.retry = true;
96
141
 
97
- this._open();
98
- return true;
142
+ const promise = this._connect();
143
+ this._openPromise = promise;
144
+ const result = await promise;
145
+ if (this._openPromise === promise) this._openPromise = null;
146
+ return result;
99
147
  }
100
- async _open() {
101
- if (this._state !== SerialJS.State.Closed) return;
102
-
148
+ async _connect() {
103
149
  this._change(SerialJS.State.Opening);
104
150
 
105
151
  try {
106
- await this._setLastPort();
152
+ if (!this._port) await this.restore();
107
153
 
108
154
  if (!this._port) {
109
155
  throw new Error('No port');
110
156
  }
111
157
 
112
- if (this._state === SerialJS.State.Closing) return;
158
+ if (this._state === SerialJS.State.Closing) {
159
+ await this._cleanup();
160
+ return false;
161
+ }
113
162
 
114
- await this._port.open({ baudRate: this.cfg.baud });
163
+ await this._port.open(this._openOptions());
115
164
 
116
165
  if (this._state === SerialJS.State.Closing) {
117
- await this._port.close();
118
- return;
166
+ await this._cleanup();
167
+ return false;
119
168
  }
120
169
 
121
170
  this.writer = this._port.writable.getWriter();
@@ -123,7 +172,18 @@ export default class SerialJS {
123
172
 
124
173
  this._sender.reset();
125
174
  this._change(SerialJS.State.Open);
175
+ void this._listen();
176
+ return true;
177
+ } catch (e) {
178
+ this._error(e);
179
+ }
180
+
181
+ await this._cleanup();
182
+ return false;
183
+ }
126
184
 
185
+ async _listen() {
186
+ try {
127
187
  while (this._state === SerialJS.State.Open) {
128
188
  const { value, done } = await this.reader.read();
129
189
 
@@ -147,6 +207,10 @@ export default class SerialJS {
147
207
  this._error(e);
148
208
  }
149
209
 
210
+ await this._cleanup();
211
+ }
212
+
213
+ async _cleanup() {
150
214
  this._sender.reset();
151
215
 
152
216
  try {
@@ -168,7 +232,7 @@ export default class SerialJS {
168
232
 
169
233
  if (this.retry) {
170
234
  setTimeout(() => {
171
- if (this.retry) this._open();
235
+ if (this.retry) this.open();
172
236
  }, this.cfg.reconnect);
173
237
  }
174
238
  }
@@ -219,11 +283,16 @@ export default class SerialJS {
219
283
  async sendBin(data) {
220
284
  if (!this.opened() || !this.writer) return false;
221
285
 
222
- return this._sender.runNothrow(async () => {
223
- if (!this.opened() || !this.writer) return false;
224
- await this.writer.write(data);
225
- return true;
226
- });
286
+ try {
287
+ return await this._sender.run(async () => {
288
+ if (!this.opened() || !this.writer) return false;
289
+ await this.writer.write(data);
290
+ return true;
291
+ });
292
+ } catch (e) {
293
+ if (this.opened()) this._error(e);
294
+ return false;
295
+ }
227
296
  }
228
297
 
229
298
  //#region private
@@ -231,10 +300,17 @@ export default class SerialJS {
231
300
  _decoder = new TextDecoder();
232
301
  _sender = new SerialExecutor();
233
302
  _state = SerialJS.State.Closed;
234
-
235
- async _setLastPort() {
236
- let ports = await navigator.serial.getPorts();
237
- this._port = ports.length ? ports[0] : null;
303
+ _openPromise = null;
304
+
305
+ _openOptions() {
306
+ return {
307
+ baudRate: this.cfg.baud,
308
+ dataBits: this.cfg.dataBits,
309
+ stopBits: this.cfg.stopBits,
310
+ parity: this.cfg.parity,
311
+ bufferSize: this.cfg.bufferSize,
312
+ flowControl: this.cfg.flowControl,
313
+ };
238
314
  }
239
315
 
240
316
  _error(e) {
@@ -251,4 +327,4 @@ export default class SerialJS {
251
327
  case SerialJS.State.Closed: this.onclose(); break;
252
328
  }
253
329
  }
254
- }
330
+ }
package/serial.min.js CHANGED
@@ -1 +1 @@
1
- const t=t=>new Promise(e=>setTimeout(e,t));class e{ontext=null;constructor(t=/\r?\n/,e=!1){this.eol=t,this.skip=e}reset(){this._buf=""}write(t){if(!this.ontext)return;if(!this.eol)return void this.ontext(t);this._buf+=t;let e=this._buf.split(this.eol);1!=e.length&&(e[e.length-1].length?this._buf=e.pop():(this._buf="",e.pop()),this.skip&&(e.shift(),this.skip=!1),e.forEach(t=>this.ontext(t)))}_buf=""}class s{constructor(){this._session=0,this.reset()}run(t){const e=this._session,s=this._queue.then(()=>{if(e!==this._session)throw new Error("Queue empty");return t()});return this._queue=s.catch(()=>{}),s}runNothrow(t){return this.run(t).catch(()=>null)}reset(){this._session++,this._queue=Promise.resolve()}}class r{static State={Closed:"closed",Opening:"opening",Open:"open",Closing:"closing"};onbin=null;ontext=null;onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,auto_open:!1,reconnect:1e3,...t},this._setLastPort().then(()=>this.onselect(this.getName())),this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>{try{this.ontext?.(t)}catch(t){this._error(t)}}}config(t={}){this.cfg={...this.cfg,...t},this.splitter.eol=this.cfg.eol}static supported(){return"serial"in navigator}opened(){return this._state==r.State.Open}selected(){return!!this._port}getName(){if(!this._port)return"None";switch(this._port.getInfo().usbProductId){case 21971:return"CH343";case 30084:return"CH340S";case 29986:case 29987:return"CH340";case 21778:case 21795:case 21892:return"CH341";case 1026:case 1027:case 1028:case 1029:case 24577:case 1538:case 24592:return"FT232";case 38144:case 258:case 1281:case 32937:case 6e4:case 60001:case 60003:return"CP210x"}return"Unknown"}async select(){try{await this.close(),this._port=null;let e=await navigator.serial.getPorts();for(let t of e)await t.forget();await t(50),await navigator.serial.requestPort(),await this._setLastPort()}catch(t){this._error(t)}return this.onselect(this.getName()),this.cfg.auto_open&&this.open(),this.selected()}async open(){return!!(await navigator.serial.getPorts()).length&&(this.cfg.reconnect&&(this.retry=!0),this._open(),!0)}async _open(){if(this._state===r.State.Closed){this._change(r.State.Opening);try{if(await this._setLastPort(),!this._port)throw new Error("No port");if(this._state===r.State.Closing)return;if(await this._port.open({baudRate:this.cfg.baud}),this._state===r.State.Closing)return void await this._port.close();for(this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._sender.reset(),this._change(r.State.Open);this._state===r.State.Open;){const{value:t,done:e}=await this.reader.read();if(e)break;if(t){try{this.onbin?.(t)}catch(t){this._error(t)}this.ontext&&this.splitter.write(this._decoder.decode(t,{stream:!0}))}}}catch(t){this._error(t)}this._sender.reset();try{this.reader&&this.reader.releaseLock()}catch(t){}try{this.writer&&this.writer.releaseLock()}catch(t){}this.reader=null,this.writer=null;try{this._port&&await this._port.close()}catch(t){}this._change(r.State.Closed),this.retry&&setTimeout(()=>{this.retry&&this._open()},this.cfg.reconnect)}}async close(){return this.retry=!1,this._sender.reset(),await this._close(),!0}async _close(){switch(this._state){case r.State.Closed:return;case r.State.Opening:this._change(r.State.Closing);break;case r.State.Open:if(this._change(r.State.Closing),this.reader)try{await this.reader.cancel()}catch(t){}}let e=0;for(;this._state===r.State.Closing;)if(await t(10),++e>200){this._error("Close timeout"),this._change(r.State.Closed);break}}async sendText(t){return this.sendBin((new TextEncoder).encode(t))}async sendBin(t){return!(!this.opened()||!this.writer)&&this._sender.runNothrow(async()=>!(!this.opened()||!this.writer||(await this.writer.write(t),0)))}_port=null;_decoder=new TextDecoder;_sender=new s;_state=r.State.Closed;async _setLastPort(){let t=await navigator.serial.getPorts();this._port=t.length?t[0]:null}_error(t){this.onerror("[SerialJS] "+t)}_change(t){if(this._state!==t)switch(this._state=t,this.onchange(t),t){case r.State.Open:this.onopen();break;case r.State.Closed:this.onclose()}}}export{r as default};
1
+ const t=t=>new Promise(e=>setTimeout(e,t));class e{ontext=null;constructor(t=/\r?\n/,e=!1){this.eol=t,this.skip=e}reset(){this._buf=""}write(t){if(!this.ontext)return;if(!this.eol)return void this.ontext(t);this._buf+=t;let e=this._buf.split(this.eol);1!=e.length&&(e[e.length-1].length?this._buf=e.pop():(this._buf="",e.pop()),this.skip&&(e.shift(),this.skip=!1),e.forEach(t=>this.ontext(t)))}_buf=""}class s{constructor(){this._session=0,this.reset()}run(t){const e=this._session,s=this._queue.then(()=>{if(e!==this._session)throw new Error("Queue empty");return t()});return this._queue=s.catch(()=>{}),s}runNothrow(t){return this.run(t).catch(()=>null)}reset(){this._session++,this._queue=Promise.resolve()}}class r{static State={Closed:"closed",Opening:"opening",Open:"open",Closing:"closing"};onbin=null;ontext=null;onopen(){}onclose(){}onchange(t){}onselect(t){}onerror(t){}constructor(t={}){this.cfg={eol:/\r?\n/,baud:115200,dataBits:8,stopBits:1,parity:"none",bufferSize:255,flowControl:"none",auto_open:!1,reconnect:1e3,forgetOtherPorts:!0,...t},this.restore().then(()=>this.onselect(this.getName())),this.splitter=new e(this.cfg.eol),this.splitter.ontext=t=>{try{this.ontext?.(t)}catch(t){this._error(t)}}}config(t={}){this.cfg={...this.cfg,...t},this.splitter.eol=this.cfg.eol}static supported(){return"serial"in navigator}opened(){return this._state==r.State.Open}selected(){return!!this._port}getInfo(){return this._port?.getInfo()??null}getName(){if(!this._port)return"None";switch(this.getInfo().usbProductId){case 21971:return"CH343";case 30084:return"CH340S";case 29986:case 29987:return"CH340";case 21778:case 21795:case 21892:return"CH341";case 1026:case 1027:case 1028:case 1029:case 24577:case 1538:case 24592:return"FT232";case 38144:case 258:case 1281:case 32937:case 6e4:case 60001:case 60003:return"CP210x"}return"Unknown"}async select(){try{await this.close();const t=await navigator.serial.requestPort();if(this.cfg.forgetOtherPorts){const e=await navigator.serial.getPorts();for(const s of e)s!==t&&await s.forget()}this._port=t}catch(t){this._error(t)}return this.onselect(this.getName()),this.cfg.auto_open?this.open():this.selected()}async getPorts(){return navigator.serial.getPorts()}async restore(){const t=await this.getPorts();return this._port=this.cfg.forgetOtherPorts?t[0]??null:1===t.length?t[0]:null,this.selected()}async forget(){if(await this.close(),!this._port)return!1;const t=this._port;this._port=null;try{return await t.forget(),this.onselect(this.getName()),!0}catch(e){return this._port=t,this._error(e),!1}}async open(){if(this.opened())return!0;if(this._openPromise)return this._openPromise;if(this._state!==r.State.Closed)return!1;this.cfg.reconnect&&(this.retry=!0);const t=this._connect();this._openPromise=t;const e=await t;return this._openPromise===t&&(this._openPromise=null),e}async _connect(){this._change(r.State.Opening);try{if(this._port||await this.restore(),!this._port)throw new Error("No port");return this._state===r.State.Closing?(await this._cleanup(),!1):(await this._port.open(this._openOptions()),this._state===r.State.Closing?(await this._cleanup(),!1):(this.writer=this._port.writable.getWriter(),this.reader=this._port.readable.getReader(),this._sender.reset(),this._change(r.State.Open),this._listen(),!0))}catch(t){this._error(t)}return await this._cleanup(),!1}async _listen(){try{for(;this._state===r.State.Open;){const{value:t,done:e}=await this.reader.read();if(e)break;if(t){try{this.onbin?.(t)}catch(t){this._error(t)}this.ontext&&this.splitter.write(this._decoder.decode(t,{stream:!0}))}}}catch(t){this._error(t)}await this._cleanup()}async _cleanup(){this._sender.reset();try{this.reader&&this.reader.releaseLock()}catch(t){}try{this.writer&&this.writer.releaseLock()}catch(t){}this.reader=null,this.writer=null;try{this._port&&await this._port.close()}catch(t){}this._change(r.State.Closed),this.retry&&setTimeout(()=>{this.retry&&this.open()},this.cfg.reconnect)}async close(){return this.retry=!1,this._sender.reset(),await this._close(),!0}async _close(){switch(this._state){case r.State.Closed:return;case r.State.Opening:this._change(r.State.Closing);break;case r.State.Open:if(this._change(r.State.Closing),this.reader)try{await this.reader.cancel()}catch(t){}}let e=0;for(;this._state===r.State.Closing;)if(await t(10),++e>200){this._error("Close timeout"),this._change(r.State.Closed);break}}async sendText(t){return this.sendBin((new TextEncoder).encode(t))}async sendBin(t){if(!this.opened()||!this.writer)return!1;try{return await this._sender.run(async()=>!(!this.opened()||!this.writer||(await this.writer.write(t),0)))}catch(t){return this.opened()&&this._error(t),!1}}_port=null;_decoder=new TextDecoder;_sender=new s;_state=r.State.Closed;_openPromise=null;_openOptions(){return{baudRate:this.cfg.baud,dataBits:this.cfg.dataBits,stopBits:this.cfg.stopBits,parity:this.cfg.parity,bufferSize:this.cfg.bufferSize,flowControl:this.cfg.flowControl}}_error(t){this.onerror("[SerialJS] "+t)}_change(t){if(this._state!==t)switch(this._state=t,this.onchange(t),t){case r.State.Open:this.onopen();break;case r.State.Closed:this.onclose()}}}export{r as default};