diff --git a/src/install-web/index.ts b/src/install-web/index.ts index 9ace72d2..8cc9997a 100644 --- a/src/install-web/index.ts +++ b/src/install-web/index.ts @@ -1,7 +1,39 @@ +import type { IEspLoaderTerminal } from "esptool-js"; import { openNoPortPickedDialog } from "../no-port-picked"; import { createESPLoader } from "../web-serial/create-esploader"; import type { ESPHomeInstallWebDialog } from "./install-web-dialog"; +export class ESPLoaderTerminalStream { + public stream: ReadableStream; + public terminal: IEspLoaderTerminal; + private controller?: ReadableStreamDefaultController; + + constructor() { + const _this = this; + this.stream = new ReadableStream({ + start(controller) { + _this.controller = controller; + }, + }); + + this.terminal = { + clean: () => { + this.controller?.enqueue("\n\n\n"); + }, + writeLine: (data: string) => { + console.log(data); + this.controller?.enqueue(`${data}\n`); + }, + write: (data: string) => { + console.log(data); + // Fix "Connecting..." line break + if (data == "\n\r") this.controller?.enqueue("\n"); + else this.controller?.enqueue(data); + }, + }; + } +} + export const preloadInstallWebDialog = () => import("./install-web-dialog"); export const openInstallWebDialog = async ( @@ -30,7 +62,10 @@ export const openInstallWebDialog = async ( return; } } - const esploader = createESPLoader(port); + + const terminalStream = new ESPLoaderTerminalStream(); + + const esploader = createESPLoader(port, terminalStream.terminal); if (onDialogOpen) { onDialogOpen(); @@ -39,5 +74,6 @@ export const openInstallWebDialog = async ( const dialog = document.createElement("esphome-install-web-dialog"); dialog.params = params; dialog.esploader = esploader; + dialog.stream = terminalStream.stream; document.body.append(dialog); }; diff --git a/src/install-web/install-console.ts b/src/install-web/install-console.ts new file mode 100644 index 00000000..33c5d664 --- /dev/null +++ b/src/install-web/install-console.ts @@ -0,0 +1,62 @@ +import { ColoredConsole, coloredConsoleStyles } from "../util/console-color"; +import { Logger } from "../const"; + +export class InstallConsole extends HTMLElement { + public port!: SerialPort; + public logger!: Logger; + + private _console?: ColoredConsole; + + public logs(): string { + return this._console?.logs() || ""; + } + + public addLine(line: string) { + this._console?.addLine(line); + } + + public connectedCallback() { + if (this._console) { + return; + } + const shadowRoot = this.attachShadow({ mode: "open" }); + + shadowRoot.innerHTML = ` + +
+ `; + + this._console = new ColoredConsole(this.shadowRoot!.querySelector("div")!); + } +} + +customElements.define("install-console", InstallConsole); + +declare global { + interface HTMLElementTagNameMap { + "install-console": InstallConsole; + } +} diff --git a/src/install-web/install-web-dialog.ts b/src/install-web/install-web-dialog.ts index adff2de9..477c0840 100644 --- a/src/install-web/install-web-dialog.ts +++ b/src/install-web/install-web-dialog.ts @@ -1,9 +1,11 @@ import { LitElement, html, PropertyValues, css, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators.js"; +import { customElement, property, query, state } from "lit/decorators.js"; import "@material/mwc-dialog"; import "@material/mwc-circular-progress"; import "@material/mwc-button"; import type { ESPLoader } from "esptool-js"; +import "./install-console"; +import type { InstallConsole } from "./install-console"; import { compileConfiguration, Configuration, @@ -43,6 +45,9 @@ export class ESPHomeInstallWebDialog extends LitElement { }; @property() public esploader!: ESPLoader; + @property() public stream?: ReadableStream; + + @query("install-console") private _console!: InstallConsole; @state() private _writeProgress?: number; @@ -56,6 +61,22 @@ export class ESPHomeInstallWebDialog extends LitElement { private _platform?: SupportedPlatforms; + @property() private _showLogs = false; + + async connectedCallback() { + super.connectedCallback(); + + await this.updateComplete; + + this.stream?.pipeTo( + new WritableStream({ + write: (chunk) => { + this._console.addLine(chunk); + }, + }), + ); + } + protected render() { let heading; let content; @@ -106,6 +127,8 @@ export class ESPHomeInstallWebDialog extends LitElement { } } + hideActions = false; + return html` ${content} + + `; } @@ -305,6 +336,10 @@ export class ESPHomeInstallWebDialog extends LitElement { return await getConfigurationFiles(configuration); } + private _toggleLogs() { + this._showLogs = !this._showLogs; + } + private _close() { this.shadowRoot!.querySelector("mwc-dialog")!.close(); } @@ -319,6 +354,18 @@ export class ESPHomeInstallWebDialog extends LitElement { static styles = [ esphomeDialogStyles, css` + install-console { + width: 500px; + height: 300px; + margin-top: 16px; + overflow: hidden; + transition: all 0.2s ease-in-out; + } + install-console.hidden { + margin-top: 0; + width: 230px; + height: 0; + } mwc-list-item { margin: 0 -20px; } diff --git a/src/web-serial/create-esploader.ts b/src/web-serial/create-esploader.ts index bcd23e8d..d7b26880 100644 --- a/src/web-serial/create-esploader.ts +++ b/src/web-serial/create-esploader.ts @@ -1,11 +1,15 @@ -import { Transport, ESPLoader } from "esptool-js"; +import { Transport, ESPLoader, IEspLoaderTerminal } from "esptool-js"; -export const createESPLoader = (port: SerialPort) => { +export const createESPLoader = ( + port: SerialPort, + terminal?: IEspLoaderTerminal, +) => { const transport = new Transport(port); return new ESPLoader({ transport, baudrate: 115200, romBaudrate: 115200, enableTracing: false, + terminal, }); };