UNPKG

34 kBJavaScriptView Raw
1import { __decorate } from "tslib";
2import { LitElement, html, css } from "lit";
3import { state } from "lit/decorators.js";
4import "./components/ew-text-button";
5import "./components/ew-list";
6import "./components/ew-list-item";
7import "./components/ew-divider";
8import "./components/ew-checkbox";
9import "./components/ewt-console";
10import "./components/ew-dialog";
11import "./components/ew-icon-button";
12import "./components/ew-filled-text-field";
13import "./components/ew-filled-select";
14import "./components/ew-select-option";
15import "./pages/ewt-page-progress";
16import "./pages/ewt-page-message";
17import { closeIcon, listItemConsole, listItemEraseUserData, listItemFundDevelopment, listItemHomeAssistant, listItemInstallIcon, listItemVisitDevice, listItemWifi, refreshIcon, } from "./components/svg";
18import { ImprovSerial } from "improv-wifi-serial-sdk/dist/serial";
19import { ImprovSerialCurrentState, PortNotReady, } from "improv-wifi-serial-sdk/dist/const";
20import { flash } from "./flash";
21import { textDownload } from "./util/file-download";
22import { fireEvent } from "./util/fire-event";
23import { sleep } from "./util/sleep";
24import { downloadManifest } from "./util/manifest";
25import { dialogStyles } from "./styles";
26import { version } from "./version";
27console.log(`ESP Web Tools ${version} by Open Home Foundation; https://esphome.github.io/esp-web-tools/`);
28const ERROR_ICON = "⚠️";
29const OK_ICON = "🎉";
30export class EwtInstallDialog extends LitElement {
31 constructor() {
32 super(...arguments);
33 this.logger = console;
34 this._state = "DASHBOARD";
35 this._installErase = false;
36 this._installConfirmed = false;
37 this._provisionForce = false;
38 this._wasProvisioned = false;
39 this._busy = false;
40 // Name of Ssid. Null = other
41 this._selectedSsid = null;
42 this._bodyOverflow = null;
43 this._handleDisconnect = () => {
44 this._state = "ERROR";
45 this._error = "Disconnected";
46 };
47 }
48 render() {
49 if (!this.port) {
50 return html ``;
51 }
52 let heading;
53 let content;
54 let allowClosing = false;
55 // During installation phase we temporarily remove the client
56 if (this._client === undefined &&
57 this._state !== "INSTALL" &&
58 this._state !== "LOGS") {
59 if (this._error) {
60 [heading, content] = this._renderError(this._error);
61 }
62 else {
63 content = this._renderProgress("Connecting");
64 }
65 }
66 else if (this._state === "INSTALL") {
67 [heading, content, allowClosing] = this._renderInstall();
68 }
69 else if (this._state === "ASK_ERASE") {
70 [heading, content] = this._renderAskErase();
71 }
72 else if (this._state === "ERROR") {
73 [heading, content] = this._renderError(this._error);
74 }
75 else if (this._state === "DASHBOARD") {
76 [heading, content, allowClosing] = this._client
77 ? this._renderDashboard()
78 : this._renderDashboardNoImprov();
79 }
80 else if (this._state === "PROVISION") {
81 [heading, content] = this._renderProvision();
82 }
83 else if (this._state === "LOGS") {
84 [heading, content] = this._renderLogs();
85 }
86 return html `
87 <ew-dialog
88 open
89 .heading=${heading}
90 @cancel=${this._preventDefault}
91 @closed=${this._handleClose}
92 >
93 ${heading ? html `<div slot="headline">${heading}</div>` : ""}
94 ${allowClosing
95 ? html `
96 <ew-icon-button slot="headline" @click=${this._closeDialog}>
97 ${closeIcon}
98 </ew-icon-button>
99 `
100 : ""}
101 ${content}
102 </ew-dialog>
103 `;
104 }
105 _renderProgress(label, progress) {
106 return html `
107 <ewt-page-progress
108 slot="content"
109 .label=${label}
110 .progress=${progress}
111 ></ewt-page-progress>
112 `;
113 }
114 _renderError(label) {
115 const heading = "Error";
116 const content = html `
117 <ewt-page-message
118 slot="content"
119 .icon=${ERROR_ICON}
120 .label=${label}
121 ></ewt-page-message>
122 <div slot="actions">
123 <ew-text-button @click=${this._closeDialog}>Close</ew-text-button>
124 </div>
125 `;
126 return [heading, content];
127 }
128 _renderDashboard() {
129 const heading = this._manifest.name;
130 let content;
131 let allowClosing = true;
132 content = html `
133 <div slot="content">
134 <ew-list>
135 <ew-list-item>
136 <div slot="headline">Connected to ${this._info.name}</div>
137 <div slot="supporting-text">
138 ${this._info.firmware}&nbsp;${this._info.version}
139 (${this._info.chipFamily})
140 </div>
141 </ew-list-item>
142 ${!this._isSameVersion
143 ? html `
144 <ew-list-item
145 type="button"
146 @click=${() => {
147 if (this._isSameFirmware) {
148 this._startInstall(false);
149 }
150 else if (this._manifest.new_install_prompt_erase) {
151 this._state = "ASK_ERASE";
152 }
153 else {
154 this._startInstall(true);
155 }
156 }}
157 >
158 ${listItemInstallIcon}
159 <div slot="headline">
160 ${!this._isSameFirmware
161 ? `Install ${this._manifest.name}`
162 : `Update ${this._manifest.name}`}
163 </div>
164 </ew-list-item>
165 `
166 : ""}
167 ${this._client.nextUrl === undefined
168 ? ""
169 : html `
170 <ew-list-item
171 type="link"
172 href=${this._client.nextUrl}
173 target="_blank"
174 >
175 ${listItemVisitDevice}
176 <div slot="headline">Visit Device</div>
177 </ew-list-item>
178 `}
179 ${!this._manifest.home_assistant_domain ||
180 this._client.state !== ImprovSerialCurrentState.PROVISIONED
181 ? ""
182 : html `
183 <ew-list-item
184 type="link"
185 href=${`https://my.home-assistant.io/redirect/config_flow_start/?domain=${this._manifest.home_assistant_domain}`}
186 target="_blank"
187 >
188 ${listItemHomeAssistant}
189 <div slot="headline">Add to Home Assistant</div>
190 </ew-list-item>
191 `}
192 <ew-list-item
193 type="button"
194 @click=${() => {
195 this._state = "PROVISION";
196 if (this._client.state === ImprovSerialCurrentState.PROVISIONED) {
197 this._provisionForce = true;
198 }
199 }}
200 >
201 ${listItemWifi}
202 <div slot="headline">
203 ${this._client.state === ImprovSerialCurrentState.READY
204 ? "Connect to Wi-Fi"
205 : "Change Wi-Fi"}
206 </div>
207 </ew-list-item>
208 <ew-list-item
209 type="button"
210 @click=${async () => {
211 const client = this._client;
212 if (client) {
213 await this._closeClientWithoutEvents(client);
214 await sleep(100);
215 }
216 // Also set `null` back to undefined.
217 this._client = undefined;
218 this._state = "LOGS";
219 }}
220 >
221 ${listItemConsole}
222 <div slot="headline">Logs & Console</div>
223 </ew-list-item>
224 ${this._isSameFirmware && this._manifest.funding_url
225 ? html `
226 <ew-list-item
227 type="link"
228 href=${this._manifest.funding_url}
229 target="_blank"
230 >
231 ${listItemFundDevelopment}
232 <div slot="headline">Fund Development</div>
233 </ew-list-item>
234 `
235 : ""}
236 ${this._isSameVersion
237 ? html `
238 <ew-list-item
239 type="button"
240 class="danger"
241 @click=${() => this._startInstall(true)}
242 >
243 ${listItemEraseUserData}
244 <div slot="headline">Erase User Data</div>
245 </ew-list-item>
246 `
247 : ""}
248 </ew-list>
249 </div>
250 `;
251 return [heading, content, allowClosing];
252 }
253 _renderDashboardNoImprov() {
254 const heading = this._manifest.name;
255 let content;
256 let allowClosing = true;
257 content = html `
258 <div slot="content">
259 <ew-list>
260 <ew-list-item
261 type="button"
262 @click=${() => {
263 if (this._manifest.new_install_prompt_erase) {
264 this._state = "ASK_ERASE";
265 }
266 else {
267 // Default is to erase a device that does not support Improv Serial
268 this._startInstall(true);
269 }
270 }}
271 >
272 ${listItemInstallIcon}
273 <div slot="headline">${`Install ${this._manifest.name}`}</div>
274 </ew-list-item>
275 <ew-list-item
276 type="button"
277 @click=${async () => {
278 // Also set `null` back to undefined.
279 this._client = undefined;
280 this._state = "LOGS";
281 }}
282 >
283 ${listItemConsole}
284 <div slot="headline">Logs & Console</div>
285 </ew-list-item>
286 </ew-list>
287 </div>
288 `;
289 return [heading, content, allowClosing];
290 }
291 _renderProvision() {
292 var _a;
293 let heading = "Configure Wi-Fi";
294 let content;
295 if (this._busy) {
296 return [
297 heading,
298 this._renderProgress(this._ssids === undefined
299 ? "Scanning for networks"
300 : "Trying to connect"),
301 ];
302 }
303 if (!this._provisionForce &&
304 this._client.state === ImprovSerialCurrentState.PROVISIONED) {
305 heading = undefined;
306 const showSetupLinks = !this._wasProvisioned &&
307 (this._client.nextUrl !== undefined ||
308 "home_assistant_domain" in this._manifest);
309 content = html `
310 <div slot="content">
311 <ewt-page-message
312 .icon=${OK_ICON}
313 label="Device connected to the network!"
314 ></ewt-page-message>
315 ${showSetupLinks
316 ? html `
317 <ew-list>
318 ${this._client.nextUrl === undefined
319 ? ""
320 : html `
321 <ew-list-item
322 type="link"
323 href=${this._client.nextUrl}
324 target="_blank"
325 @click=${() => {
326 this._state = "DASHBOARD";
327 }}
328 >
329 ${listItemVisitDevice}
330 <div slot="headline">Visit Device</div>
331 </ew-list-item>
332 `}
333 ${!this._manifest.home_assistant_domain
334 ? ""
335 : html `
336 <ew-list-item
337 type="link"
338 href=${`https://my.home-assistant.io/redirect/config_flow_start/?domain=${this._manifest.home_assistant_domain}`}
339 target="_blank"
340 @click=${() => {
341 this._state = "DASHBOARD";
342 }}
343 >
344 ${listItemHomeAssistant}
345 <div slot="headline">Add to Home Assistant</div>
346 </ew-list-item>
347 `}
348 <ew-list-item
349 type="button"
350 @click=${() => {
351 this._state = "DASHBOARD";
352 }}
353 >
354 <div slot="start" class="fake-icon"></div>
355 <div slot="headline">Skip</div>
356 </ew-list-item>
357 </ew-list>
358 `
359 : ""}
360 </div>
361
362 ${!showSetupLinks
363 ? html `
364 <div slot="actions">
365 <ew-text-button
366 @click=${() => {
367 this._state = "DASHBOARD";
368 }}
369 >
370 Continue
371 </ew-text-button>
372 </div>
373 `
374 : ""}
375 `;
376 }
377 else {
378 let error;
379 switch (this._client.error) {
380 case 3 /* ImprovSerialErrorState.UNABLE_TO_CONNECT */:
381 error = "Unable to connect";
382 break;
383 case 254 /* ImprovSerialErrorState.TIMEOUT */:
384 error = "Timeout";
385 break;
386 case 0 /* ImprovSerialErrorState.NO_ERROR */:
387 // Happens when list SSIDs not supported.
388 case 2 /* ImprovSerialErrorState.UNKNOWN_RPC_COMMAND */:
389 break;
390 default:
391 error = `Unknown error (${this._client.error})`;
392 }
393 const selectedSsid = (_a = this._ssids) === null || _a === void 0 ? void 0 : _a.find((info) => info.name === this._selectedSsid);
394 content = html `
395 <ew-icon-button slot="headline" @click=${this._updateSsids}>
396 ${refreshIcon}
397 </ew-icon-button>
398 <div slot="content">
399 <div>Connect your device to the network to start using it.</div>
400 ${error ? html `<p class="error">${error}</p>` : ""}
401 ${this._ssids !== null
402 ? html `
403 <ew-filled-select
404 menu-positioning="fixed"
405 label="Network"
406 @change=${(ev) => {
407 const index = ev.target.selectedIndex;
408 // The "Join Other" item is always the last item.
409 this._selectedSsid =
410 index === this._ssids.length
411 ? null
412 : this._ssids[index].name;
413 }}
414 >
415 ${this._ssids.map((info) => html `
416 <ew-select-option
417 .selected=${selectedSsid === info}
418 .value=${info.name}
419 >
420 ${info.name}
421 </ew-select-option>
422 `)}
423 <ew-divider></ew-divider>
424 <ew-select-option .selected=${!selectedSsid}>
425 Join other…
426 </ew-select-option>
427 </ew-filled-select>
428 `
429 : ""}
430 ${
431 // Show input box if command not supported or "Join Other" selected
432 !selectedSsid
433 ? html `
434 <ew-filled-text-field
435 label="Network Name"
436 name="ssid"
437 ></ew-filled-text-field>
438 `
439 : ""}
440 ${!selectedSsid || selectedSsid.secured
441 ? html `
442 <ew-filled-text-field
443 label="Password"
444 name="password"
445 type="password"
446 ></ew-filled-text-field>
447 `
448 : ""}
449 </div>
450 <div slot="actions">
451 <ew-text-button
452 @click=${() => {
453 this._state = "DASHBOARD";
454 }}
455 >
456 ${this._installState && this._installErase ? "Skip" : "Back"}
457 </ew-text-button>
458 <ew-text-button @click=${this._doProvision}>Connect</ew-text-button>
459 </div>
460 `;
461 }
462 return [heading, content];
463 }
464 _renderAskErase() {
465 const heading = "Erase device";
466 const content = html `
467 <div slot="content">
468 <div>
469 Do you want to erase the device before installing
470 ${this._manifest.name}? All data on the device will be lost.
471 </div>
472 <label class="formfield">
473 <ew-checkbox touch-target="wrapper" class="danger"></ew-checkbox>
474 Erase device
475 </label>
476 </div>
477 <div slot="actions">
478 <ew-text-button
479 @click=${() => {
480 this._state = "DASHBOARD";
481 }}
482 >
483 Back
484 </ew-text-button>
485 <ew-text-button
486 @click=${() => {
487 const checkbox = this.shadowRoot.querySelector("ew-checkbox");
488 this._startInstall(checkbox.checked);
489 }}
490 >
491 Next
492 </ew-text-button>
493 </div>
494 `;
495 return [heading, content];
496 }
497 _renderInstall() {
498 let heading;
499 let content;
500 const allowClosing = false;
501 const isUpdate = !this._installErase && this._isSameFirmware;
502 if (!this._installConfirmed && this._isSameVersion) {
503 heading = "Erase User Data";
504 content = html `
505 <div slot="content">
506 Do you want to reset your device and erase all user data from your
507 device?
508 </div>
509 <div slot="actions">
510 <ew-text-button class="danger" @click=${this._confirmInstall}>
511 Erase User Data
512 </ew-text-button>
513 </div>
514 `;
515 }
516 else if (!this._installConfirmed) {
517 heading = "Confirm Installation";
518 const action = isUpdate ? "update to" : "install";
519 content = html `
520 <div slot="content">
521 ${isUpdate
522 ? html `Your device is running
523 ${this._info.firmware}&nbsp;${this._info.version}.<br /><br />`
524 : ""}
525 Do you want to ${action}
526 ${this._manifest.name}&nbsp;${this._manifest.version}?
527 ${this._installErase
528 ? html `<br /><br />All data on the device will be erased.`
529 : ""}
530 </div>
531 <div slot="actions">
532 <ew-text-button
533 @click=${() => {
534 this._state = "DASHBOARD";
535 }}
536 >
537 Back
538 </ew-text-button>
539 <ew-text-button @click=${this._confirmInstall}>
540 Install
541 </ew-text-button>
542 </div>
543 `;
544 }
545 else if (!this._installState ||
546 this._installState.state === "initializing" /* FlashStateType.INITIALIZING */ ||
547 this._installState.state === "preparing" /* FlashStateType.PREPARING */) {
548 heading = "Installing";
549 content = this._renderProgress("Preparing installation");
550 }
551 else if (this._installState.state === "erasing" /* FlashStateType.ERASING */) {
552 heading = "Installing";
553 content = this._renderProgress("Erasing");
554 }
555 else if (this._installState.state === "writing" /* FlashStateType.WRITING */ ||
556 // When we're finished, keep showing this screen with 100% written
557 // until Improv is initialized / not detected.
558 (this._installState.state === "finished" /* FlashStateType.FINISHED */ &&
559 this._client === undefined)) {
560 heading = "Installing";
561 let percentage;
562 let undeterminateLabel;
563 if (this._installState.state === "finished" /* FlashStateType.FINISHED */) {
564 // We're done writing and detecting improv, show spinner
565 undeterminateLabel = "Wrapping up";
566 }
567 else if (this._installState.details.percentage < 4) {
568 // We're writing the firmware under 4%, show spinner or else we don't show any pixels
569 undeterminateLabel = "Installing";
570 }
571 else {
572 // We're writing the firmware over 4%, show progress bar
573 percentage = this._installState.details.percentage;
574 }
575 content = this._renderProgress(html `
576 ${undeterminateLabel ? html `${undeterminateLabel}<br />` : ""}
577 <br />
578 This will take
579 ${this._installState.chipFamily === "ESP8266"
580 ? "a minute"
581 : "2 minutes"}.<br />
582 Keep this page visible to prevent slow down
583 `, percentage);
584 }
585 else if (this._installState.state === "finished" /* FlashStateType.FINISHED */) {
586 heading = undefined;
587 const supportsImprov = this._client !== null;
588 content = html `
589 <ewt-page-message
590 slot="content"
591 .icon=${OK_ICON}
592 label="Installation complete!"
593 ></ewt-page-message>
594
595 <div slot="actions">
596 <ew-text-button
597 @click=${() => {
598 this._state =
599 supportsImprov && this._installErase
600 ? "PROVISION"
601 : "DASHBOARD";
602 }}
603 >
604 Next
605 </ew-text-button>
606 </div>
607 `;
608 }
609 else if (this._installState.state === "error" /* FlashStateType.ERROR */) {
610 heading = "Installation failed";
611 content = html `
612 <ewt-page-message
613 slot="content"
614 .icon=${ERROR_ICON}
615 .label=${this._installState.message}
616 ></ewt-page-message>
617 <div slot="actions">
618 <ew-text-button
619 @click=${async () => {
620 this._initialize();
621 this._state = "DASHBOARD";
622 }}
623 >
624 Back
625 </ew-text-button>
626 </div>
627 `;
628 }
629 return [heading, content, allowClosing];
630 }
631 _renderLogs() {
632 let heading = `Logs`;
633 let content;
634 content = html `
635 <div slot="content">
636 <ewt-console .port=${this.port} .logger=${this.logger}></ewt-console>
637 </div>
638 <div slot="actions">
639 <ew-text-button
640 @click=${async () => {
641 await this.shadowRoot.querySelector("ewt-console").reset();
642 }}
643 >
644 Reset Device
645 </ew-text-button>
646 <ew-text-button
647 @click=${() => {
648 textDownload(this.shadowRoot.querySelector("ewt-console").logs(), `esp-web-tools-logs.txt`);
649 this.shadowRoot.querySelector("ewt-console").reset();
650 }}
651 >
652 Download Logs
653 </ew-text-button>
654 <ew-text-button
655 @click=${async () => {
656 await this.shadowRoot.querySelector("ewt-console").disconnect();
657 this._state = "DASHBOARD";
658 this._initialize();
659 }}
660 >
661 Back
662 </ew-text-button>
663 </div>
664 `;
665 return [heading, content];
666 }
667 willUpdate(changedProps) {
668 if (!changedProps.has("_state")) {
669 return;
670 }
671 // Clear errors when changing between pages unless we change
672 // to the error page.
673 if (this._state !== "ERROR") {
674 this._error = undefined;
675 }
676 // Scan for SSIDs on provision
677 if (this._state === "PROVISION") {
678 this._updateSsids();
679 }
680 else {
681 // Reset this value if we leave provisioning.
682 this._provisionForce = false;
683 }
684 if (this._state === "INSTALL") {
685 this._installConfirmed = false;
686 this._installState = undefined;
687 }
688 }
689 async _updateSsids(tries = 0) {
690 const oldSsids = this._ssids;
691 this._ssids = undefined;
692 this._busy = true;
693 let ssids;
694 try {
695 ssids = await this._client.scan();
696 }
697 catch (err) {
698 // When we fail while loading, pick "Join other"
699 if (this._ssids === undefined) {
700 this._ssids = null;
701 this._selectedSsid = null;
702 }
703 this._busy = false;
704 return;
705 }
706 // We will retry a few times if we don't get any results
707 if (ssids.length === 0 && tries < 3) {
708 console.log("SCHEDULE RETRY", tries);
709 setTimeout(() => this._updateSsids(tries + 1), 1000);
710 return;
711 }
712 if (oldSsids) {
713 // If we had a previous list, ensure the selection is still valid
714 if (this._selectedSsid &&
715 !ssids.find((s) => s.name === this._selectedSsid)) {
716 this._selectedSsid = ssids[0].name;
717 }
718 }
719 else {
720 this._selectedSsid = ssids.length ? ssids[0].name : null;
721 }
722 this._ssids = ssids;
723 this._busy = false;
724 }
725 firstUpdated(changedProps) {
726 super.firstUpdated(changedProps);
727 this._bodyOverflow = document.body.style.overflow;
728 document.body.style.overflow = "hidden";
729 this._initialize();
730 }
731 updated(changedProps) {
732 super.updated(changedProps);
733 if (changedProps.has("_state")) {
734 this.setAttribute("state", this._state);
735 }
736 if (this._state !== "PROVISION") {
737 return;
738 }
739 if (changedProps.has("_selectedSsid") && this._selectedSsid === null) {
740 // If we pick "Join other", select SSID input.
741 this._focusFormElement("ew-filled-text-field[name=ssid]");
742 }
743 else if (changedProps.has("_ssids")) {
744 // Form is shown when SSIDs are loaded/marked not supported
745 this._focusFormElement();
746 }
747 }
748 _focusFormElement(selector = "ew-filled-text-field, ew-filled-select") {
749 const formEl = this.shadowRoot.querySelector(selector);
750 if (formEl) {
751 formEl.updateComplete.then(() => setTimeout(() => formEl.focus(), 100));
752 }
753 }
754 async _initialize(justInstalled = false) {
755 if (this.port.readable === null || this.port.writable === null) {
756 this._state = "ERROR";
757 this._error =
758 "Serial port is not readable/writable. Close any other application using it and try again.";
759 return;
760 }
761 try {
762 this._manifest = await downloadManifest(this.manifestPath);
763 }
764 catch (err) {
765 this._state = "ERROR";
766 this._error = "Failed to download manifest";
767 return;
768 }
769 if (this._manifest.new_install_improv_wait_time === 0) {
770 this._client = null;
771 return;
772 }
773 const client = new ImprovSerial(this.port, this.logger);
774 client.addEventListener("state-changed", () => {
775 this.requestUpdate();
776 });
777 client.addEventListener("error-changed", () => this.requestUpdate());
778 try {
779 // If a device was just installed, give new firmware 10 seconds (overridable) to
780 // format the rest of the flash and do other stuff.
781 const timeout = !justInstalled
782 ? 1000
783 : this._manifest.new_install_improv_wait_time !== undefined
784 ? this._manifest.new_install_improv_wait_time * 1000
785 : 10000;
786 this._info = await client.initialize(timeout);
787 this._client = client;
788 client.addEventListener("disconnect", this._handleDisconnect);
789 }
790 catch (err) {
791 // Clear old value
792 this._info = undefined;
793 if (err instanceof PortNotReady) {
794 this._state = "ERROR";
795 this._error =
796 "Serial port is not ready. Close any other application using it and try again.";
797 }
798 else {
799 this._client = null; // not supported
800 this.logger.error("Improv initialization failed.", err);
801 }
802 }
803 }
804 _startInstall(erase) {
805 this._state = "INSTALL";
806 this._installErase = erase;
807 this._installConfirmed = false;
808 }
809 async _confirmInstall() {
810 this._installConfirmed = true;
811 this._installState = undefined;
812 if (this._client) {
813 await this._closeClientWithoutEvents(this._client);
814 }
815 this._client = undefined;
816 // Close port. ESPLoader likes opening it.
817 await this.port.close();
818 flash((state) => {
819 this._installState = state;
820 if (state.state === "finished" /* FlashStateType.FINISHED */) {
821 sleep(100)
822 // Flashing closes the port
823 .then(() => this.port.open({ baudRate: 115200 }))
824 .then(() => this._initialize(true))
825 .then(() => this.requestUpdate());
826 }
827 else if (state.state === "error" /* FlashStateType.ERROR */) {
828 sleep(100)
829 // Flashing closes the port
830 .then(() => this.port.open({ baudRate: 115200 }));
831 }
832 }, this.port, this.manifestPath, this._manifest, this._installErase);
833 }
834 async _doProvision() {
835 var _a;
836 this._busy = true;
837 this._wasProvisioned =
838 this._client.state === ImprovSerialCurrentState.PROVISIONED;
839 const ssid = this._selectedSsid === null
840 ? this.shadowRoot.querySelector("ew-filled-text-field[name=ssid]").value
841 : this._selectedSsid;
842 const password = ((_a = this.shadowRoot.querySelector("ew-filled-text-field[name=password]")) === null || _a === void 0 ? void 0 : _a.value) || "";
843 try {
844 await this._client.provision(ssid, password, 30000);
845 }
846 catch (err) {
847 return;
848 }
849 finally {
850 this._busy = false;
851 this._provisionForce = false;
852 }
853 }
854 _closeDialog() {
855 this.shadowRoot.querySelector("ew-dialog").close();
856 }
857 async _handleClose() {
858 if (this._client) {
859 await this._closeClientWithoutEvents(this._client);
860 }
861 fireEvent(this, "closed");
862 document.body.style.overflow = this._bodyOverflow;
863 this.parentNode.removeChild(this);
864 }
865 /**
866 * Return if the device runs same firmware as manifest.
867 */
868 get _isSameFirmware() {
869 var _a;
870 return !this._info
871 ? false
872 : ((_a = this.overrides) === null || _a === void 0 ? void 0 : _a.checkSameFirmware)
873 ? this.overrides.checkSameFirmware(this._manifest, this._info)
874 : this._info.firmware === this._manifest.name;
875 }
876 /**
877 * Return if the device runs same firmware and version as manifest.
878 */
879 get _isSameVersion() {
880 return (this._isSameFirmware && this._info.version === this._manifest.version);
881 }
882 async _closeClientWithoutEvents(client) {
883 client.removeEventListener("disconnect", this._handleDisconnect);
884 await client.close();
885 }
886 _preventDefault(ev) {
887 ev.preventDefault();
888 }
889}
890EwtInstallDialog.styles = [
891 dialogStyles,
892 css `
893 :host {
894 --mdc-dialog-max-width: 390px;
895 }
896 div[slot="headline"] {
897 padding-right: 48px;
898 }
899 ew-icon-button[slot="headline"] {
900 position: absolute;
901 right: 4px;
902 top: 8px;
903 }
904 ew-icon-button[slot="headline"] svg {
905 padding: 8px;
906 color: var(--text-color);
907 }
908 .dialog-nav svg {
909 color: var(--text-color);
910 }
911 .table-row {
912 display: flex;
913 }
914 .table-row.last {
915 margin-bottom: 16px;
916 }
917 .table-row svg {
918 width: 20px;
919 margin-right: 8px;
920 }
921 ew-filled-text-field,
922 ew-filled-select {
923 display: block;
924 margin-top: 16px;
925 }
926 label.formfield {
927 display: inline-flex;
928 align-items: center;
929 padding-right: 8px;
930 }
931 ew-list {
932 margin: 0 -24px;
933 padding: 0;
934 }
935 ew-list-item svg {
936 height: 24px;
937 }
938 ewt-page-message + ew-list {
939 padding-top: 16px;
940 }
941 .fake-icon {
942 width: 24px;
943 }
944 .error {
945 color: var(--danger-color);
946 }
947 .danger {
948 --mdc-theme-primary: var(--danger-color);
949 --mdc-theme-secondary: var(--danger-color);
950 --md-sys-color-primary: var(--danger-color);
951 --md-sys-color-on-surface: var(--danger-color);
952 }
953 button.link {
954 background: none;
955 color: inherit;
956 border: none;
957 padding: 0;
958 font: inherit;
959 text-align: left;
960 text-decoration: underline;
961 cursor: pointer;
962 }
963 :host([state="LOGS"]) ew-dialog {
964 max-width: 90vw;
965 max-height: 90vh;
966 }
967 ewt-console {
968 width: calc(80vw - 48px);
969 height: calc(90vh - 168px);
970 }
971 `,
972];
973__decorate([
974 state()
975], EwtInstallDialog.prototype, "_client", void 0);
976__decorate([
977 state()
978], EwtInstallDialog.prototype, "_state", void 0);
979__decorate([
980 state()
981], EwtInstallDialog.prototype, "_installErase", void 0);
982__decorate([
983 state()
984], EwtInstallDialog.prototype, "_installConfirmed", void 0);
985__decorate([
986 state()
987], EwtInstallDialog.prototype, "_installState", void 0);
988__decorate([
989 state()
990], EwtInstallDialog.prototype, "_provisionForce", void 0);
991__decorate([
992 state()
993], EwtInstallDialog.prototype, "_error", void 0);
994__decorate([
995 state()
996], EwtInstallDialog.prototype, "_busy", void 0);
997__decorate([
998 state()
999], EwtInstallDialog.prototype, "_ssids", void 0);
1000__decorate([
1001 state()
1002], EwtInstallDialog.prototype, "_selectedSsid", void 0);
1003customElements.define("ewt-install-dialog", EwtInstallDialog);