diff --git a/src/App.vue b/src/App.vue index bf41c23..8731c80 100644 --- a/src/App.vue +++ b/src/App.vue @@ -239,6 +239,7 @@ import LogoText from "./assets/Logo_Text.svg?component"; import LogoTextDevelop from "./assets/Logo_Develop_Text.svg?component"; import { Log } from "./log"; import { WebSerial } from "./store/serial/webserial"; +import { settings } from "./store/serial/settings"; export default defineComponent({ name: "app", @@ -314,6 +315,9 @@ export default defineComponent({ return updater.updatePreparing() || updater.updatePending(); }, hasBrowserSupport() { + if (settings.websocketUrl()) { + return true; + } return navigator.usb && WebSerial; }, logDownloadAnchorRef(): HTMLAnchorElement { diff --git a/src/store/serial/serial.ts b/src/store/serial/serial.ts index 079d7c3..b15f1e4 100644 --- a/src/store/serial/serial.ts +++ b/src/store/serial/serial.ts @@ -116,6 +116,7 @@ export class Serial { private waitingCommands: Promise = Promise.resolve({} as any); private port?: SerialPort; + private ws?: WebSocket; private writer?: WritableStreamDefaultWriter; private reader?: ReadableStreamDefaultReader; @@ -124,21 +125,27 @@ export class Serial { private errorCallback?: any; public async connect(errorCallback: any = console.warn): Promise { - const port = await WebSerial.requestPort({ - filters: SERIAL_FILTERS, - }); - await this._connectPort(port, errorCallback); + const ws = settings.websocketUrl(); + if (ws) { + await this._connectWebsocket(ws, errorCallback); + } else { + const port = await WebSerial.requestPort({ filters: SERIAL_FILTERS }); + await this._connectPort(port, errorCallback); + } return await this.get(QuicVal.Info, 10_000); } public async connectFirstPort( errorCallback: any = console.warn, ): Promise { - const ports = await WebSerial.getPorts(); - if (!ports.length) { - throw new Error("no ports"); + const ws = settings.websocketUrl(); + if (ws) { + await this._connectWebsocket(ws, errorCallback); + } else { + const ports = await WebSerial.getPorts(); + if (!ports.length) throw new Error("no ports"); + await this._connectPort(ports[0], errorCallback); } - await this._connectPort(ports[0], errorCallback); return await this.get(QuicVal.Info, 10_000); } @@ -163,6 +170,39 @@ export class Serial { this.reader = transform.readable.getReader(); } + private async _connectWebsocket( + host: string, + errorCallback: any = console.warn, + ) { + const transform = new QuicStream(); + const writer = transform.writable.getWriter(); + this.reader = transform.readable.getReader(); + + this.errorCallback = errorCallback; + this.waitingCommands = Promise.resolve({} as any); + + this.transfromClosed = Promise.resolve({} as any); + + return new Promise((resolve, reject) => { + this.ws = new WebSocket("wss://" + host + "/ws"); + this.ws.binaryType = "arraybuffer"; + this.ws.onopen = (e) => { + resolve(); + }; + this.ws.onmessage = async (e) => { + writer.write(new Uint8Array(e.data)); + }; + this.ws.onclose = (e) => { + errorCallback(e); + this.close(); + }; + this.ws.onerror = (e) => { + errorCallback(e); + reject(e); + }; + }); + } + public async softReboot() { await this.write(stringToUint8Array(SOFT_REBOOT_MAGIC)); await this.close(); @@ -282,7 +322,9 @@ export class Serial { } private async write(array: Uint8Array) { - if (this.writer) { + if (this.ws) { + await this.ws?.send(array); + } else if (this.writer) { await this.writer.write(array); } else { throw new Error("no serial writer"); diff --git a/src/store/serial/settings.ts b/src/store/serial/settings.ts index 1c99035..3e9a2a9 100644 --- a/src/store/serial/settings.ts +++ b/src/store/serial/settings.ts @@ -13,5 +13,8 @@ const desktopSerialSettings = { }; export const settings = { + websocketUrl() { + return new URL(document.location.toString()).searchParams.get("ws"); + }, serial: isAndroid ? androidSerialSettings : desktopSerialSettings, };