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