Files
open-battery-information-web/desktop/serial-shim.js
T
2026-08-19 21:35:27 +03:00

100 lines
2.6 KiB
JavaScript

/* Desktop-only Web Serial shim.
In the Electron app, the main process owns the serial port (via the
`serialport` package) and streams bytes over IPC. This file re-exposes
that transport through the same navigator.serial API the OBI web code
uses, so js/common.js and js/interfaces/arduino_obi.js run unchanged. */
(function () {
"use strict";
if (!window.serialAPI) return;
const api = window.serialAPI;
class DesktopSerialPort {
constructor(info) {
this._info = info;
this._open = false;
this._stream = null;
this._controller = null;
this._queue = [];
this._unsubscribe = null;
}
getInfo() {
return {
usbVendorId: this._info.vendorId || 0,
usbProductId: this._info.productId || 0,
};
}
isOpen() {
return this._open;
}
async open(options) {
if (this._open) return;
await api.open(this._info, (options && options.baudRate) || 9600);
this._open = true;
this._unsubscribe = api.onData((portPath, buf) => {
if (portPath !== this._info.path) return;
const data = new Uint8Array(buf);
if (this._controller) this._controller.enqueue(data);
else this._queue.push(data);
});
}
get readable() {
if (!this._open) throw new Error('InvalidStateError: port not open');
if (!this._stream) {
this._stream = new ReadableStream({
start: (controller) => {
this._controller = controller;
for (const chunk of this._queue) controller.enqueue(chunk);
this._queue = [];
},
cancel: () => {
this._controller = null;
},
});
}
return this._stream;
}
get writable() {
if (!this._open) throw new Error('InvalidStateError: port not open');
return new WritableStream({
write: (chunk) =>
api.write(
this._info,
chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk)
),
});
}
async setSignals(signals) {
await api.setSignals(this._info, !!(signals && signals.dataTerminalReady));
}
async close() {
await api.close(this._info);
if (this._unsubscribe) this._unsubscribe();
this._unsubscribe = null;
this._open = false;
}
async forget() {}
}
Object.defineProperty(navigator, 'serial', {
configurable: true,
enumerable: true,
value: {
getPorts: async () => [],
requestPort: async () => {
const info = await api.requestPort();
return new DesktopSerialPort(info);
},
},
});
})();