tasmota-esp-web-tools 9.0.2 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ewt-littlefs-manager.d.ts +43 -0
- package/dist/components/ewt-littlefs-manager.js +819 -0
- package/dist/flash.js +4 -30
- package/dist/install-dialog.d.ts +11 -0
- package/dist/install-dialog.js +336 -0
- package/dist/partition.d.ts +26 -0
- package/dist/partition.js +129 -0
- package/dist/util/esp32s2-reconnect.d.ts +22 -0
- package/dist/util/esp32s2-reconnect.js +45 -0
- package/dist/util/partition.d.ts +4 -0
- package/dist/util/partition.js +59 -0
- package/{js/modules/index-BK6drzlv.js → dist/web/index-t2Vsxqjj.js} +1 -1
- package/dist/web/install-button.js +1 -1
- package/dist/web/{install-dialog-Xo7JovVA.js → install-dialog-CVebVk1R.js} +515 -36
- package/dist/web/{styles-ntwzvT31.js → styles-CWxbqh-J.js} +1 -1
- package/dist/web/wasm/littlefs/index.d.ts +91 -0
- package/dist/web/wasm/littlefs/index.js +611 -0
- package/dist/web/wasm/littlefs/littlefs.js +505 -0
- package/dist/web/wasm/littlefs/littlefs.wasm +0 -0
- package/{dist/web/index-BK6drzlv.js → js/modules/index-t2Vsxqjj.js} +1 -1
- package/js/modules/install-button.js +1 -1
- package/js/modules/{install-dialog-Xo7JovVA.js → install-dialog-CVebVk1R.js} +515 -36
- package/js/modules/{styles-ntwzvT31.js → styles-CWxbqh-J.js} +1 -1
- package/js/modules/wasm/littlefs/index.d.ts +91 -0
- package/js/modules/wasm/littlefs/index.js +611 -0
- package/js/modules/wasm/littlefs/littlefs.js +505 -0
- package/js/modules/wasm/littlefs/littlefs.wasm +0 -0
- package/package.json +3 -2
- package/.devcontainer/Dockerfile +0 -16
- package/.devcontainer/devcontainer.json +0 -21
- package/.github/dependabot.yml +0 -10
- package/.github/workflows/build_upload.yml +0 -78
- package/.github/workflows/ci.yml +0 -24
- package/.prettierignore +0 -1
- package/BAUD_RATE_CONFIGURATION.md +0 -291
- package/BAUD_RATE_IMPROVEMENT.md +0 -195
- package/CHANGELOG_IMPROVEMENTS.md +0 -246
- package/ESP32_S2_USB_RECONNECT.md +0 -108
- package/FINAL_SUMMARY.md +0 -153
- package/example-baud-rate.html +0 -276
- package/manifest-example-p4-variants.json +0 -61
- package/rollup.config.mjs +0 -36
- package/script/build +0 -8
- package/script/develop +0 -17
- package/src/components/ewt-button.ts +0 -25
- package/src/components/ewt-checkbox.ts +0 -14
- package/src/components/ewt-circular-progress.ts +0 -14
- package/src/components/ewt-console.ts +0 -166
- package/src/components/ewt-dialog.ts +0 -22
- package/src/components/ewt-formfield.ts +0 -14
- package/src/components/ewt-icon-button.ts +0 -14
- package/src/components/ewt-list-item.ts +0 -14
- package/src/components/ewt-select.ts +0 -23
- package/src/components/ewt-textfield.ts +0 -23
- package/src/components/svg.ts +0 -27
- package/src/connect.ts +0 -55
- package/src/const.ts +0 -122
- package/src/flash.ts +0 -361
- package/src/install-button.ts +0 -126
- package/src/install-dialog.ts +0 -1114
- package/src/no-port-picked/index.ts +0 -10
- package/src/no-port-picked/no-port-picked-dialog.ts +0 -167
- package/src/pages/ewt-page-message.ts +0 -39
- package/src/pages/ewt-page-progress.ts +0 -44
- package/src/styles.ts +0 -34
- package/src/util/chip-family-name.ts +0 -46
- package/src/util/console-color.ts +0 -284
- package/src/util/file-download.ts +0 -17
- package/src/util/fire-event.ts +0 -20
- package/src/util/line-break-transformer.ts +0 -20
- package/src/util/manifest.ts +0 -18
- package/src/util/sleep.ts +0 -2
- package/static/logos/2smart.png +0 -0
- package/static/logos/canairio.png +0 -0
- package/static/logos/clockwise.png +0 -0
- package/static/logos/espeasy.png +0 -0
- package/static/logos/esphome.svg +0 -1
- package/static/logos/luciferin_logo.png +0 -0
- package/static/logos/squeezelite-esp32.png +0 -0
- package/static/logos/tasmota.svg +0 -1
- package/static/logos/wled.png +0 -0
- package/static/screenshots/dashboard.png +0 -0
- package/static/screenshots/logs.png +0 -0
- package/static/social.png +0 -0
- package/tsconfig.json +0 -20
package/src/install-dialog.ts
DELETED
|
@@ -1,1114 +0,0 @@
|
|
|
1
|
-
import { LitElement, html, PropertyValues, css, TemplateResult } from "lit";
|
|
2
|
-
import { state } from "lit/decorators.js";
|
|
3
|
-
import "./components/ewt-button";
|
|
4
|
-
import "./components/ewt-checkbox";
|
|
5
|
-
import "./components/ewt-console";
|
|
6
|
-
import "./components/ewt-dialog";
|
|
7
|
-
import "./components/ewt-formfield";
|
|
8
|
-
import "./components/ewt-icon-button";
|
|
9
|
-
import "./components/ewt-textfield";
|
|
10
|
-
import type { EwtTextfield } from "./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 { Logger, Manifest, FlashStateType, FlashState } from "./const.js";
|
|
17
|
-
import { ImprovSerial, Ssid } from "improv-wifi-serial-sdk/dist/serial";
|
|
18
|
-
import {
|
|
19
|
-
ImprovSerialCurrentState,
|
|
20
|
-
ImprovSerialErrorState,
|
|
21
|
-
PortNotReady,
|
|
22
|
-
} from "improv-wifi-serial-sdk/dist/const";
|
|
23
|
-
import { flash } from "./flash";
|
|
24
|
-
import { textDownload } from "./util/file-download";
|
|
25
|
-
import { fireEvent } from "./util/fire-event";
|
|
26
|
-
import { sleep } from "./util/sleep";
|
|
27
|
-
import { downloadManifest } from "./util/manifest";
|
|
28
|
-
import { dialogStyles } from "./styles";
|
|
29
|
-
|
|
30
|
-
const ERROR_ICON = "⚠️";
|
|
31
|
-
const OK_ICON = "🎉";
|
|
32
|
-
|
|
33
|
-
export class EwtInstallDialog extends LitElement {
|
|
34
|
-
public port!: SerialPort;
|
|
35
|
-
|
|
36
|
-
public manifestPath!: string;
|
|
37
|
-
|
|
38
|
-
public firmwareFile?: File;
|
|
39
|
-
|
|
40
|
-
public baudRate?: number;
|
|
41
|
-
|
|
42
|
-
public logger: Logger = console;
|
|
43
|
-
|
|
44
|
-
public overrides?: {
|
|
45
|
-
checkSameFirmware?: (
|
|
46
|
-
manifest: Manifest,
|
|
47
|
-
deviceImprov: ImprovSerial["info"],
|
|
48
|
-
) => boolean;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
private _manifest!: Manifest;
|
|
52
|
-
|
|
53
|
-
private _info?: ImprovSerial["info"];
|
|
54
|
-
|
|
55
|
-
// null = NOT_SUPPORTED
|
|
56
|
-
@state() private _client?: ImprovSerial | null;
|
|
57
|
-
|
|
58
|
-
@state() private _state:
|
|
59
|
-
| "ERROR"
|
|
60
|
-
| "DASHBOARD"
|
|
61
|
-
| "PROVISION"
|
|
62
|
-
| "INSTALL"
|
|
63
|
-
| "ASK_ERASE"
|
|
64
|
-
| "LOGS" = "DASHBOARD";
|
|
65
|
-
|
|
66
|
-
@state() private _installErase = false;
|
|
67
|
-
@state() private _installConfirmed = false;
|
|
68
|
-
@state() private _installState?: FlashState;
|
|
69
|
-
|
|
70
|
-
@state() private _provisionForce = false;
|
|
71
|
-
private _wasProvisioned = false;
|
|
72
|
-
|
|
73
|
-
@state() private _error?: string;
|
|
74
|
-
|
|
75
|
-
@state() private _busy = false;
|
|
76
|
-
|
|
77
|
-
// Prevent recursive ESP32-S2 reconnect attempts
|
|
78
|
-
private _esp32s2ReconnectInProgress = false;
|
|
79
|
-
|
|
80
|
-
// undefined = not loaded
|
|
81
|
-
// null = not available
|
|
82
|
-
@state() private _ssids?: Ssid[] | null;
|
|
83
|
-
|
|
84
|
-
// -1 = custom
|
|
85
|
-
@state() private _selectedSsid = -1;
|
|
86
|
-
|
|
87
|
-
protected render() {
|
|
88
|
-
if (!this.port) {
|
|
89
|
-
return html``;
|
|
90
|
-
}
|
|
91
|
-
let heading: string | undefined;
|
|
92
|
-
let content: TemplateResult;
|
|
93
|
-
let hideActions = false;
|
|
94
|
-
let allowClosing = false;
|
|
95
|
-
|
|
96
|
-
// During installation phase we temporarily remove the client
|
|
97
|
-
if (
|
|
98
|
-
this._client === undefined &&
|
|
99
|
-
this._state !== "INSTALL" &&
|
|
100
|
-
this._state !== "LOGS"
|
|
101
|
-
) {
|
|
102
|
-
if (this._error) {
|
|
103
|
-
[heading, content, hideActions] = this._renderError(this._error);
|
|
104
|
-
} else {
|
|
105
|
-
content = this._renderProgress("Connecting");
|
|
106
|
-
hideActions = true;
|
|
107
|
-
}
|
|
108
|
-
} else if (this._state === "INSTALL") {
|
|
109
|
-
[heading, content, hideActions, allowClosing] = this._renderInstall();
|
|
110
|
-
} else if (this._state === "ASK_ERASE") {
|
|
111
|
-
[heading, content] = this._renderAskErase();
|
|
112
|
-
} else if (this._state === "ERROR") {
|
|
113
|
-
[heading, content, hideActions] = this._renderError(this._error!);
|
|
114
|
-
} else if (this._state === "DASHBOARD") {
|
|
115
|
-
[heading, content, hideActions, allowClosing] = this._client
|
|
116
|
-
? this._renderDashboard()
|
|
117
|
-
: this._renderDashboardNoImprov();
|
|
118
|
-
} else if (this._state === "PROVISION") {
|
|
119
|
-
[heading, content, hideActions] = this._renderProvision();
|
|
120
|
-
} else if (this._state === "LOGS") {
|
|
121
|
-
[heading, content, hideActions] = this._renderLogs();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return html`
|
|
125
|
-
<ewt-dialog
|
|
126
|
-
open
|
|
127
|
-
.heading=${heading!}
|
|
128
|
-
scrimClickAction
|
|
129
|
-
@closed=${this._handleClose}
|
|
130
|
-
.hideActions=${hideActions}
|
|
131
|
-
>
|
|
132
|
-
${heading && allowClosing
|
|
133
|
-
? html`
|
|
134
|
-
<ewt-icon-button dialogAction="close">
|
|
135
|
-
${closeIcon}
|
|
136
|
-
</ewt-icon-button>
|
|
137
|
-
`
|
|
138
|
-
: ""}
|
|
139
|
-
${content!}
|
|
140
|
-
</ewt-dialog>
|
|
141
|
-
`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
_renderProgress(label: string | TemplateResult, progress?: number) {
|
|
145
|
-
return html`
|
|
146
|
-
<ewt-page-progress
|
|
147
|
-
.label=${label}
|
|
148
|
-
.progress=${progress}
|
|
149
|
-
></ewt-page-progress>
|
|
150
|
-
`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
_renderError(label: string): [string, TemplateResult, boolean] {
|
|
154
|
-
const heading = "Error";
|
|
155
|
-
const content = html`
|
|
156
|
-
<ewt-page-message .icon=${ERROR_ICON} .label=${label}></ewt-page-message>
|
|
157
|
-
<ewt-button
|
|
158
|
-
slot="primaryAction"
|
|
159
|
-
dialogAction="ok"
|
|
160
|
-
label="Close"
|
|
161
|
-
></ewt-button>
|
|
162
|
-
`;
|
|
163
|
-
const hideActions = false;
|
|
164
|
-
return [heading, content, hideActions];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
_renderDashboard(): [string, TemplateResult, boolean, boolean] {
|
|
168
|
-
const heading = this._info!.name;
|
|
169
|
-
let content: TemplateResult;
|
|
170
|
-
let hideActions = true;
|
|
171
|
-
let allowClosing = true;
|
|
172
|
-
|
|
173
|
-
content = html`
|
|
174
|
-
<div class="table-row">
|
|
175
|
-
${firmwareIcon}
|
|
176
|
-
<div>${this._info!.firmware} ${this._info!.version}</div>
|
|
177
|
-
</div>
|
|
178
|
-
<div class="table-row last">
|
|
179
|
-
${chipIcon}
|
|
180
|
-
<div>${this._info!.chipFamily}</div>
|
|
181
|
-
</div>
|
|
182
|
-
<div class="dashboard-buttons">
|
|
183
|
-
${!this._isSameVersion
|
|
184
|
-
? html`
|
|
185
|
-
<div>
|
|
186
|
-
<ewt-button
|
|
187
|
-
text-left
|
|
188
|
-
.label=${!this._isSameFirmware
|
|
189
|
-
? `Install ${this._manifest.name}`
|
|
190
|
-
: `Update ${this._manifest.name}`}
|
|
191
|
-
@click=${() => {
|
|
192
|
-
if (this._isSameFirmware) {
|
|
193
|
-
this._startInstall(false);
|
|
194
|
-
} else if (this._manifest.new_install_prompt_erase) {
|
|
195
|
-
this._state = "ASK_ERASE";
|
|
196
|
-
} else {
|
|
197
|
-
this._startInstall(true);
|
|
198
|
-
}
|
|
199
|
-
}}
|
|
200
|
-
></ewt-button>
|
|
201
|
-
</div>
|
|
202
|
-
`
|
|
203
|
-
: ""}
|
|
204
|
-
${this._client!.nextUrl === undefined
|
|
205
|
-
? ""
|
|
206
|
-
: html`
|
|
207
|
-
<div>
|
|
208
|
-
<a
|
|
209
|
-
href=${this._client!.nextUrl}
|
|
210
|
-
class="has-button"
|
|
211
|
-
target="_blank"
|
|
212
|
-
>
|
|
213
|
-
<ewt-button label="Visit Device"></ewt-button>
|
|
214
|
-
</a>
|
|
215
|
-
</div>
|
|
216
|
-
`}
|
|
217
|
-
${!this._manifest.home_assistant_domain ||
|
|
218
|
-
this._client!.state !== ImprovSerialCurrentState.PROVISIONED
|
|
219
|
-
? ""
|
|
220
|
-
: html`
|
|
221
|
-
<div>
|
|
222
|
-
<a
|
|
223
|
-
href=${`https://my.home-assistant.io/redirect/config_flow_start/?domain=${this._manifest.home_assistant_domain}`}
|
|
224
|
-
class="has-button"
|
|
225
|
-
target="_blank"
|
|
226
|
-
>
|
|
227
|
-
<ewt-button label="Add to Home Assistant"></ewt-button>
|
|
228
|
-
</a>
|
|
229
|
-
</div>
|
|
230
|
-
`}
|
|
231
|
-
<div>
|
|
232
|
-
<ewt-button
|
|
233
|
-
.label=${this._client!.state === ImprovSerialCurrentState.READY
|
|
234
|
-
? "Connect to Wi-Fi"
|
|
235
|
-
: "Change Wi-Fi"}
|
|
236
|
-
@click=${() => {
|
|
237
|
-
this._state = "PROVISION";
|
|
238
|
-
if (
|
|
239
|
-
this._client!.state === ImprovSerialCurrentState.PROVISIONED
|
|
240
|
-
) {
|
|
241
|
-
this._provisionForce = true;
|
|
242
|
-
}
|
|
243
|
-
}}
|
|
244
|
-
></ewt-button>
|
|
245
|
-
</div>
|
|
246
|
-
<div>
|
|
247
|
-
<ewt-button
|
|
248
|
-
label="Logs & Console"
|
|
249
|
-
@click=${async () => {
|
|
250
|
-
const client = this._client;
|
|
251
|
-
if (client) {
|
|
252
|
-
await this._closeClientWithoutEvents(client);
|
|
253
|
-
await sleep(100);
|
|
254
|
-
}
|
|
255
|
-
// Also set `null` back to undefined.
|
|
256
|
-
this._client = undefined;
|
|
257
|
-
this._state = "LOGS";
|
|
258
|
-
}}
|
|
259
|
-
></ewt-button>
|
|
260
|
-
</div>
|
|
261
|
-
${this._isSameFirmware && this._manifest.funding_url
|
|
262
|
-
? html`
|
|
263
|
-
<div>
|
|
264
|
-
<a
|
|
265
|
-
class="button"
|
|
266
|
-
href=${this._manifest.funding_url}
|
|
267
|
-
target="_blank"
|
|
268
|
-
>
|
|
269
|
-
<ewt-button label="Fund Development"></ewt-button>
|
|
270
|
-
</a>
|
|
271
|
-
</div>
|
|
272
|
-
`
|
|
273
|
-
: ""}
|
|
274
|
-
${this._isSameVersion
|
|
275
|
-
? html`
|
|
276
|
-
<div>
|
|
277
|
-
<ewt-button
|
|
278
|
-
class="danger"
|
|
279
|
-
label="Erase User Data"
|
|
280
|
-
@click=${() => this._startInstall(true)}
|
|
281
|
-
></ewt-button>
|
|
282
|
-
</div>
|
|
283
|
-
`
|
|
284
|
-
: ""}
|
|
285
|
-
</div>
|
|
286
|
-
`;
|
|
287
|
-
|
|
288
|
-
return [heading, content, hideActions, allowClosing];
|
|
289
|
-
}
|
|
290
|
-
_renderDashboardNoImprov(): [string, TemplateResult, boolean, boolean] {
|
|
291
|
-
const heading = "Device Dashboard";
|
|
292
|
-
let content: TemplateResult;
|
|
293
|
-
let hideActions = true;
|
|
294
|
-
let allowClosing = true;
|
|
295
|
-
|
|
296
|
-
content = html`
|
|
297
|
-
<div class="dashboard-buttons">
|
|
298
|
-
<div>
|
|
299
|
-
<ewt-button
|
|
300
|
-
text-left
|
|
301
|
-
.label=${`Install ${this._manifest.name}`}
|
|
302
|
-
@click=${() => {
|
|
303
|
-
if (this._manifest.new_install_prompt_erase) {
|
|
304
|
-
this._state = "ASK_ERASE";
|
|
305
|
-
} else {
|
|
306
|
-
// Default is to erase a device that does not support Improv Serial
|
|
307
|
-
this._startInstall(true);
|
|
308
|
-
}
|
|
309
|
-
}}
|
|
310
|
-
></ewt-button>
|
|
311
|
-
</div>
|
|
312
|
-
|
|
313
|
-
<div>
|
|
314
|
-
<ewt-button
|
|
315
|
-
label="Logs & Console"
|
|
316
|
-
@click=${async () => {
|
|
317
|
-
// Also set `null` back to undefined.
|
|
318
|
-
this._client = undefined;
|
|
319
|
-
this._state = "LOGS";
|
|
320
|
-
}}
|
|
321
|
-
></ewt-button>
|
|
322
|
-
</div>
|
|
323
|
-
</div>
|
|
324
|
-
`;
|
|
325
|
-
|
|
326
|
-
return [heading, content, hideActions, allowClosing];
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
_renderProvision(): [string | undefined, TemplateResult, boolean] {
|
|
330
|
-
let heading: string | undefined = "Configure Wi-Fi";
|
|
331
|
-
let content: TemplateResult;
|
|
332
|
-
let hideActions = false;
|
|
333
|
-
|
|
334
|
-
if (this._busy) {
|
|
335
|
-
return [
|
|
336
|
-
heading,
|
|
337
|
-
this._renderProgress(
|
|
338
|
-
this._ssids === undefined
|
|
339
|
-
? "Scanning for networks"
|
|
340
|
-
: "Trying to connect",
|
|
341
|
-
),
|
|
342
|
-
true,
|
|
343
|
-
];
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
!this._provisionForce &&
|
|
348
|
-
this._client!.state === ImprovSerialCurrentState.PROVISIONED
|
|
349
|
-
) {
|
|
350
|
-
heading = undefined;
|
|
351
|
-
const showSetupLinks =
|
|
352
|
-
!this._wasProvisioned &&
|
|
353
|
-
(this._client!.nextUrl !== undefined ||
|
|
354
|
-
"home_assistant_domain" in this._manifest);
|
|
355
|
-
hideActions = showSetupLinks;
|
|
356
|
-
content = html`
|
|
357
|
-
<ewt-page-message
|
|
358
|
-
.icon=${OK_ICON}
|
|
359
|
-
label="Device connected to the network!"
|
|
360
|
-
></ewt-page-message>
|
|
361
|
-
${showSetupLinks
|
|
362
|
-
? html`
|
|
363
|
-
<div class="dashboard-buttons">
|
|
364
|
-
${this._client!.nextUrl === undefined
|
|
365
|
-
? ""
|
|
366
|
-
: html`
|
|
367
|
-
<div>
|
|
368
|
-
<a
|
|
369
|
-
href=${this._client!.nextUrl}
|
|
370
|
-
class="has-button"
|
|
371
|
-
target="_blank"
|
|
372
|
-
@click=${() => {
|
|
373
|
-
this._state = "DASHBOARD";
|
|
374
|
-
}}
|
|
375
|
-
>
|
|
376
|
-
<ewt-button label="Visit Device"></ewt-button>
|
|
377
|
-
</a>
|
|
378
|
-
</div>
|
|
379
|
-
`}
|
|
380
|
-
${!this._manifest.home_assistant_domain
|
|
381
|
-
? ""
|
|
382
|
-
: html`
|
|
383
|
-
<div>
|
|
384
|
-
<a
|
|
385
|
-
href=${`https://my.home-assistant.io/redirect/config_flow_start/?domain=${this._manifest.home_assistant_domain}`}
|
|
386
|
-
class="has-button"
|
|
387
|
-
target="_blank"
|
|
388
|
-
@click=${() => {
|
|
389
|
-
this._state = "DASHBOARD";
|
|
390
|
-
}}
|
|
391
|
-
>
|
|
392
|
-
<ewt-button
|
|
393
|
-
label="Add to Home Assistant"
|
|
394
|
-
></ewt-button>
|
|
395
|
-
</a>
|
|
396
|
-
</div>
|
|
397
|
-
`}
|
|
398
|
-
<div>
|
|
399
|
-
<ewt-button
|
|
400
|
-
label="Skip"
|
|
401
|
-
@click=${() => {
|
|
402
|
-
this._state = "DASHBOARD";
|
|
403
|
-
}}
|
|
404
|
-
></ewt-button>
|
|
405
|
-
</div>
|
|
406
|
-
</div>
|
|
407
|
-
`
|
|
408
|
-
: html`
|
|
409
|
-
<ewt-button
|
|
410
|
-
slot="primaryAction"
|
|
411
|
-
label="Continue"
|
|
412
|
-
@click=${() => {
|
|
413
|
-
this._state = "DASHBOARD";
|
|
414
|
-
}}
|
|
415
|
-
></ewt-button>
|
|
416
|
-
`}
|
|
417
|
-
`;
|
|
418
|
-
} else {
|
|
419
|
-
let error: string | undefined;
|
|
420
|
-
|
|
421
|
-
switch (this._client!.error) {
|
|
422
|
-
case ImprovSerialErrorState.UNABLE_TO_CONNECT:
|
|
423
|
-
error = "Unable to connect";
|
|
424
|
-
break;
|
|
425
|
-
|
|
426
|
-
case ImprovSerialErrorState.NO_ERROR:
|
|
427
|
-
// Happens when list SSIDs not supported.
|
|
428
|
-
case ImprovSerialErrorState.UNKNOWN_RPC_COMMAND:
|
|
429
|
-
break;
|
|
430
|
-
|
|
431
|
-
default:
|
|
432
|
-
error = `Unknown error (${this._client!.error})`;
|
|
433
|
-
}
|
|
434
|
-
content = html`
|
|
435
|
-
<div>
|
|
436
|
-
Enter the credentials of the Wi-Fi network that you want your device
|
|
437
|
-
to connect to.
|
|
438
|
-
</div>
|
|
439
|
-
${error ? html`<p class="error">${error}</p>` : ""}
|
|
440
|
-
${this._ssids !== null
|
|
441
|
-
? html`
|
|
442
|
-
<ewt-select
|
|
443
|
-
fixedMenuPosition
|
|
444
|
-
label="Network"
|
|
445
|
-
@selected=${(ev: { detail: { index: number } }) => {
|
|
446
|
-
const index = ev.detail.index;
|
|
447
|
-
// The "Join Other" item is always the last item.
|
|
448
|
-
this._selectedSsid =
|
|
449
|
-
index === this._ssids!.length ? -1 : index;
|
|
450
|
-
}}
|
|
451
|
-
@closed=${(ev: Event) => ev.stopPropagation()}
|
|
452
|
-
>
|
|
453
|
-
${this._ssids!.map(
|
|
454
|
-
(info, idx) => html`
|
|
455
|
-
<ewt-list-item
|
|
456
|
-
.selected=${this._selectedSsid === idx}
|
|
457
|
-
value=${idx}
|
|
458
|
-
>
|
|
459
|
-
${info.name}
|
|
460
|
-
</ewt-list-item>
|
|
461
|
-
`,
|
|
462
|
-
)}
|
|
463
|
-
<ewt-list-item
|
|
464
|
-
.selected=${this._selectedSsid === -1}
|
|
465
|
-
value="-1"
|
|
466
|
-
>
|
|
467
|
-
Join other…
|
|
468
|
-
</ewt-list-item>
|
|
469
|
-
</ewt-select>
|
|
470
|
-
`
|
|
471
|
-
: ""}
|
|
472
|
-
${
|
|
473
|
-
// Show input box if command not supported or "Join Other" selected
|
|
474
|
-
this._selectedSsid === -1
|
|
475
|
-
? html`
|
|
476
|
-
<ewt-textfield label="Network Name" name="ssid"></ewt-textfield>
|
|
477
|
-
`
|
|
478
|
-
: ""
|
|
479
|
-
}
|
|
480
|
-
<ewt-textfield
|
|
481
|
-
label="Password"
|
|
482
|
-
name="password"
|
|
483
|
-
type="password"
|
|
484
|
-
></ewt-textfield>
|
|
485
|
-
<ewt-button
|
|
486
|
-
slot="primaryAction"
|
|
487
|
-
label="Connect"
|
|
488
|
-
@click=${this._doProvision}
|
|
489
|
-
></ewt-button>
|
|
490
|
-
<ewt-button
|
|
491
|
-
slot="secondaryAction"
|
|
492
|
-
.label=${this._installState && this._installErase ? "Skip" : "Back"}
|
|
493
|
-
@click=${() => {
|
|
494
|
-
this._state = "DASHBOARD";
|
|
495
|
-
}}
|
|
496
|
-
></ewt-button>
|
|
497
|
-
`;
|
|
498
|
-
}
|
|
499
|
-
return [heading, content, hideActions];
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
_renderAskErase(): [string | undefined, TemplateResult] {
|
|
503
|
-
const heading = "Erase device";
|
|
504
|
-
const content = html`
|
|
505
|
-
<div>
|
|
506
|
-
Do you want to erase the device before installing
|
|
507
|
-
${this._manifest.name}? All data on the device will be lost.
|
|
508
|
-
</div>
|
|
509
|
-
<ewt-formfield label="Erase device" class="danger">
|
|
510
|
-
<ewt-checkbox></ewt-checkbox>
|
|
511
|
-
</ewt-formfield>
|
|
512
|
-
<ewt-button
|
|
513
|
-
slot="primaryAction"
|
|
514
|
-
label="Next"
|
|
515
|
-
@click=${() => {
|
|
516
|
-
const checkbox = this.shadowRoot!.querySelector("ewt-checkbox")!;
|
|
517
|
-
this._startInstall(checkbox.checked);
|
|
518
|
-
}}
|
|
519
|
-
></ewt-button>
|
|
520
|
-
<ewt-button
|
|
521
|
-
slot="secondaryAction"
|
|
522
|
-
label="Back"
|
|
523
|
-
@click=${() => {
|
|
524
|
-
this._state = "DASHBOARD";
|
|
525
|
-
}}
|
|
526
|
-
></ewt-button>
|
|
527
|
-
`;
|
|
528
|
-
|
|
529
|
-
return [heading, content];
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
_renderInstall(): [string | undefined, TemplateResult, boolean, boolean] {
|
|
533
|
-
let heading: string | undefined;
|
|
534
|
-
let content: TemplateResult;
|
|
535
|
-
let hideActions = false;
|
|
536
|
-
const allowClosing = false;
|
|
537
|
-
|
|
538
|
-
const isUpdate = !this._installErase && this._isSameFirmware;
|
|
539
|
-
|
|
540
|
-
if (!this._installConfirmed && this._isSameVersion) {
|
|
541
|
-
heading = "Erase User Data";
|
|
542
|
-
content = html`
|
|
543
|
-
Do you want to reset your device and erase all user data from your
|
|
544
|
-
device?
|
|
545
|
-
<ewt-button
|
|
546
|
-
class="danger"
|
|
547
|
-
slot="primaryAction"
|
|
548
|
-
label="Erase User Data"
|
|
549
|
-
@click=${this._confirmInstall}
|
|
550
|
-
></ewt-button>
|
|
551
|
-
`;
|
|
552
|
-
} else if (!this._installConfirmed) {
|
|
553
|
-
heading = "Confirm Installation";
|
|
554
|
-
const action = isUpdate ? "update to" : "install";
|
|
555
|
-
content = html`
|
|
556
|
-
${isUpdate
|
|
557
|
-
? html`Your device is running
|
|
558
|
-
${this._info!.firmware} ${this._info!.version}.<br /><br />`
|
|
559
|
-
: ""}
|
|
560
|
-
Do you want to ${action}
|
|
561
|
-
${this._manifest.name} ${this._manifest.version}?
|
|
562
|
-
${this._installErase
|
|
563
|
-
? html`<br /><br />All data on the device will be erased.`
|
|
564
|
-
: ""}
|
|
565
|
-
<ewt-button
|
|
566
|
-
slot="primaryAction"
|
|
567
|
-
label="Install"
|
|
568
|
-
@click=${this._confirmInstall}
|
|
569
|
-
></ewt-button>
|
|
570
|
-
<ewt-button
|
|
571
|
-
slot="secondaryAction"
|
|
572
|
-
label="Back"
|
|
573
|
-
@click=${() => {
|
|
574
|
-
this._state = "DASHBOARD";
|
|
575
|
-
}}
|
|
576
|
-
></ewt-button>
|
|
577
|
-
`;
|
|
578
|
-
} else if (
|
|
579
|
-
!this._installState ||
|
|
580
|
-
this._installState.state === FlashStateType.INITIALIZING ||
|
|
581
|
-
this._installState.state === FlashStateType.MANIFEST ||
|
|
582
|
-
this._installState.state === FlashStateType.PREPARING
|
|
583
|
-
) {
|
|
584
|
-
heading = "Installing";
|
|
585
|
-
content = this._renderProgress("Preparing installation");
|
|
586
|
-
hideActions = true;
|
|
587
|
-
} else if (this._installState.state === FlashStateType.ERASING) {
|
|
588
|
-
heading = "Installing";
|
|
589
|
-
content = this._renderProgress("Erasing");
|
|
590
|
-
hideActions = true;
|
|
591
|
-
} else if (
|
|
592
|
-
this._installState.state === FlashStateType.WRITING ||
|
|
593
|
-
// When we're finished, keep showing this screen with 100% written
|
|
594
|
-
// until Improv is initialized / not detected.
|
|
595
|
-
(this._installState.state === FlashStateType.FINISHED &&
|
|
596
|
-
this._client === undefined)
|
|
597
|
-
) {
|
|
598
|
-
heading = "Installing";
|
|
599
|
-
let percentage: number | undefined;
|
|
600
|
-
let undeterminateLabel: string | undefined;
|
|
601
|
-
if (this._installState.state === FlashStateType.FINISHED) {
|
|
602
|
-
// We're done writing and detecting improv, show spinner
|
|
603
|
-
undeterminateLabel = "Wrapping up";
|
|
604
|
-
} else if (this._installState.details.percentage < 4) {
|
|
605
|
-
// We're writing the firmware under 4%, show spinner or else we don't show any pixels
|
|
606
|
-
undeterminateLabel = "Installing";
|
|
607
|
-
} else {
|
|
608
|
-
// We're writing the firmware over 4%, show progress bar
|
|
609
|
-
percentage = this._installState.details.percentage;
|
|
610
|
-
}
|
|
611
|
-
content = this._renderProgress(
|
|
612
|
-
html`
|
|
613
|
-
${undeterminateLabel ? html`${undeterminateLabel}<br />` : ""}
|
|
614
|
-
<br />
|
|
615
|
-
This will take
|
|
616
|
-
${this._installState.chipFamily === "ESP8266"
|
|
617
|
-
? "a minute"
|
|
618
|
-
: "2 minutes"}.<br />
|
|
619
|
-
Keep this page visible to prevent slow down
|
|
620
|
-
`,
|
|
621
|
-
percentage,
|
|
622
|
-
);
|
|
623
|
-
hideActions = true;
|
|
624
|
-
} else if (this._installState.state === FlashStateType.FINISHED) {
|
|
625
|
-
heading = undefined;
|
|
626
|
-
const supportsImprov = this._client !== null;
|
|
627
|
-
content = html`
|
|
628
|
-
<ewt-page-message
|
|
629
|
-
.icon=${OK_ICON}
|
|
630
|
-
label="Installation complete!"
|
|
631
|
-
></ewt-page-message>
|
|
632
|
-
<ewt-button
|
|
633
|
-
slot="primaryAction"
|
|
634
|
-
label="Next"
|
|
635
|
-
@click=${() => {
|
|
636
|
-
this._state =
|
|
637
|
-
supportsImprov && this._installErase ? "PROVISION" : "DASHBOARD";
|
|
638
|
-
}}
|
|
639
|
-
></ewt-button>
|
|
640
|
-
`;
|
|
641
|
-
} else if (this._installState.state === FlashStateType.ERROR) {
|
|
642
|
-
heading = "Installation failed";
|
|
643
|
-
content = html`
|
|
644
|
-
<ewt-page-message
|
|
645
|
-
.icon=${ERROR_ICON}
|
|
646
|
-
.label=${this._installState.message}
|
|
647
|
-
></ewt-page-message>
|
|
648
|
-
<ewt-button
|
|
649
|
-
slot="primaryAction"
|
|
650
|
-
label="Back"
|
|
651
|
-
@click=${async () => {
|
|
652
|
-
this._initialize();
|
|
653
|
-
this._state = "DASHBOARD";
|
|
654
|
-
}}
|
|
655
|
-
></ewt-button>
|
|
656
|
-
`;
|
|
657
|
-
} else if (
|
|
658
|
-
this._installState.state === FlashStateType.ESP32_S2_USB_RECONNECT
|
|
659
|
-
) {
|
|
660
|
-
// ESP32-S2 Native USB has switched to CDC mode - need to select new port
|
|
661
|
-
heading = "ESP32-S2 USB Port Changed";
|
|
662
|
-
content = html`
|
|
663
|
-
<ewt-page-message
|
|
664
|
-
.icon=${"⚠️"}
|
|
665
|
-
.label=${"The ESP32-S2 has switched from ROM bootloader to USB CDC mode. Please select the new USB port to continue."}
|
|
666
|
-
></ewt-page-message>
|
|
667
|
-
<ewt-button
|
|
668
|
-
slot="primaryAction"
|
|
669
|
-
label="Select New Port"
|
|
670
|
-
@click=${this._handleESP32S2Reconnect}
|
|
671
|
-
></ewt-button>
|
|
672
|
-
<ewt-button
|
|
673
|
-
slot="secondaryAction"
|
|
674
|
-
label="Cancel"
|
|
675
|
-
@click=${() => {
|
|
676
|
-
this._state = "DASHBOARD";
|
|
677
|
-
this._installState = undefined;
|
|
678
|
-
}}
|
|
679
|
-
></ewt-button>
|
|
680
|
-
`;
|
|
681
|
-
}
|
|
682
|
-
return [heading, content!, hideActions, allowClosing];
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
_renderLogs(): [string | undefined, TemplateResult, boolean] {
|
|
686
|
-
let heading: string | undefined = `Logs`;
|
|
687
|
-
let content: TemplateResult;
|
|
688
|
-
let hideActions = false;
|
|
689
|
-
|
|
690
|
-
content = html`
|
|
691
|
-
<ewt-console .port=${this.port} .logger=${this.logger}></ewt-console>
|
|
692
|
-
<ewt-button
|
|
693
|
-
slot="primaryAction"
|
|
694
|
-
label="Back"
|
|
695
|
-
@click=${async () => {
|
|
696
|
-
await this.shadowRoot!.querySelector("ewt-console")!.disconnect();
|
|
697
|
-
this._state = "DASHBOARD";
|
|
698
|
-
this._initialize();
|
|
699
|
-
}}
|
|
700
|
-
></ewt-button>
|
|
701
|
-
<ewt-button
|
|
702
|
-
slot="secondaryAction"
|
|
703
|
-
label="Download Logs"
|
|
704
|
-
@click=${() => {
|
|
705
|
-
textDownload(
|
|
706
|
-
this.shadowRoot!.querySelector("ewt-console")!.logs(),
|
|
707
|
-
`esp-web-tools-logs.txt`,
|
|
708
|
-
);
|
|
709
|
-
|
|
710
|
-
this.shadowRoot!.querySelector("ewt-console")!.reset();
|
|
711
|
-
}}
|
|
712
|
-
></ewt-button>
|
|
713
|
-
<ewt-button
|
|
714
|
-
slot="secondaryAction"
|
|
715
|
-
label="Reset Device"
|
|
716
|
-
@click=${async () => {
|
|
717
|
-
await this.shadowRoot!.querySelector("ewt-console")!.reset();
|
|
718
|
-
}}
|
|
719
|
-
></ewt-button>
|
|
720
|
-
`;
|
|
721
|
-
|
|
722
|
-
return [heading, content!, hideActions];
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
public override willUpdate(changedProps: PropertyValues) {
|
|
726
|
-
if (!changedProps.has("_state")) {
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
// Clear errors when changing between pages unless we change
|
|
730
|
-
// to the error page.
|
|
731
|
-
if (this._state !== "ERROR") {
|
|
732
|
-
this._error = undefined;
|
|
733
|
-
}
|
|
734
|
-
// Scan for SSIDs on provision
|
|
735
|
-
if (this._state === "PROVISION") {
|
|
736
|
-
this._ssids = undefined;
|
|
737
|
-
this._busy = true;
|
|
738
|
-
this._client!.scan().then(
|
|
739
|
-
(ssids) => {
|
|
740
|
-
this._busy = false;
|
|
741
|
-
this._ssids = ssids;
|
|
742
|
-
this._selectedSsid = ssids.length ? 0 : -1;
|
|
743
|
-
},
|
|
744
|
-
() => {
|
|
745
|
-
this._busy = false;
|
|
746
|
-
this._ssids = null;
|
|
747
|
-
this._selectedSsid = -1;
|
|
748
|
-
},
|
|
749
|
-
);
|
|
750
|
-
} else {
|
|
751
|
-
// Reset this value if we leave provisioning.
|
|
752
|
-
this._provisionForce = false;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
if (this._state === "INSTALL") {
|
|
756
|
-
this._installConfirmed = false;
|
|
757
|
-
this._installState = undefined;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
protected override firstUpdated(changedProps: PropertyValues) {
|
|
762
|
-
super.firstUpdated(changedProps);
|
|
763
|
-
this._initialize();
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
protected override updated(changedProps: PropertyValues) {
|
|
767
|
-
super.updated(changedProps);
|
|
768
|
-
|
|
769
|
-
if (changedProps.has("_state")) {
|
|
770
|
-
this.setAttribute("state", this._state);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
if (this._state !== "PROVISION") {
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
if (changedProps.has("_selectedSsid") && this._selectedSsid === -1) {
|
|
778
|
-
// If we pick "Join other", select SSID input.
|
|
779
|
-
this._focusFormElement("ewt-textfield[name=ssid]");
|
|
780
|
-
} else if (changedProps.has("_ssids")) {
|
|
781
|
-
// Form is shown when SSIDs are loaded/marked not supported
|
|
782
|
-
this._focusFormElement();
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
private _focusFormElement(selector = "ewt-textfield, ewt-select") {
|
|
787
|
-
const formEl = this.shadowRoot!.querySelector(
|
|
788
|
-
selector,
|
|
789
|
-
) as LitElement | null;
|
|
790
|
-
if (formEl) {
|
|
791
|
-
formEl.updateComplete.then(() => setTimeout(() => formEl.focus(), 100));
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
private async _initialize(justInstalled = false) {
|
|
796
|
-
if (this.port.readable === null || this.port.writable === null) {
|
|
797
|
-
this._state = "ERROR";
|
|
798
|
-
this._error =
|
|
799
|
-
"Serial port is not readable/writable. Close any other application using it and try again.";
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
try {
|
|
804
|
-
// If local file upload via browser is used, we already provide a manifest as a JSON string and not a URL to it
|
|
805
|
-
this._manifest = JSON.parse(this.manifestPath);
|
|
806
|
-
} catch {
|
|
807
|
-
// Standard procedure - download manifest.json with provided URL
|
|
808
|
-
try {
|
|
809
|
-
this._manifest = await downloadManifest(this.manifestPath);
|
|
810
|
-
} catch (err: any) {
|
|
811
|
-
this._state = "ERROR";
|
|
812
|
-
this._error = "Failed to download manifest";
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
if (this._manifest.new_install_improv_wait_time === 0) {
|
|
818
|
-
this._client = null;
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
const client = new ImprovSerial(this.port!, this.logger);
|
|
823
|
-
client.addEventListener("state-changed", () => {
|
|
824
|
-
this.requestUpdate();
|
|
825
|
-
});
|
|
826
|
-
client.addEventListener("error-changed", () => this.requestUpdate());
|
|
827
|
-
try {
|
|
828
|
-
// If a device was just installed, give new firmware 10 seconds (overridable) to
|
|
829
|
-
// format the rest of the flash and do other stuff.
|
|
830
|
-
const timeout = !justInstalled
|
|
831
|
-
? 1000
|
|
832
|
-
: this._manifest.new_install_improv_wait_time !== undefined
|
|
833
|
-
? this._manifest.new_install_improv_wait_time * 1000
|
|
834
|
-
: 10000;
|
|
835
|
-
this._info = await client.initialize(timeout);
|
|
836
|
-
this._client = client;
|
|
837
|
-
client.addEventListener("disconnect", this._handleDisconnect);
|
|
838
|
-
} catch (err: any) {
|
|
839
|
-
// Clear old value
|
|
840
|
-
this._info = undefined;
|
|
841
|
-
if (err instanceof PortNotReady) {
|
|
842
|
-
this._state = "ERROR";
|
|
843
|
-
this._error =
|
|
844
|
-
"Serial port is not ready. Close any other application using it and try again.";
|
|
845
|
-
} else {
|
|
846
|
-
this._client = null; // not supported
|
|
847
|
-
this.logger.error("Improv initialization failed.", err);
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
private _startInstall(erase: boolean) {
|
|
853
|
-
this._state = "INSTALL";
|
|
854
|
-
this._installErase = erase;
|
|
855
|
-
this._installConfirmed = false;
|
|
856
|
-
// Reset reconnect flag when starting a fresh installation
|
|
857
|
-
this._esp32s2ReconnectInProgress = false;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
/**
|
|
861
|
-
* Handle ESP32-S2 Native USB reconnect.
|
|
862
|
-
* When the ESP32-S2 switches from ROM bootloader to USB CDC mode,
|
|
863
|
-
* the USB port changes and we need to let the user select the new port.
|
|
864
|
-
*/
|
|
865
|
-
private async _handleESP32S2Reconnect() {
|
|
866
|
-
// Prevent recursive reconnect attempts
|
|
867
|
-
if (this._esp32s2ReconnectInProgress) {
|
|
868
|
-
this.logger.log("ESP32-S2 reconnect already in progress, ignoring");
|
|
869
|
-
this._error = "Reconnection failed. Please try again manually.";
|
|
870
|
-
this._state = "ERROR";
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
this._esp32s2ReconnectInProgress = true;
|
|
875
|
-
|
|
876
|
-
try {
|
|
877
|
-
// Close the old port if still accessible
|
|
878
|
-
try {
|
|
879
|
-
await this.port.close();
|
|
880
|
-
} catch {
|
|
881
|
-
// Port may already be closed
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
// Forget the old port to allow reselection
|
|
885
|
-
try {
|
|
886
|
-
await this.port.forget();
|
|
887
|
-
} catch {
|
|
888
|
-
// Forget may not be supported or port already released
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// Request new port from user
|
|
892
|
-
const newPort = await navigator.serial.requestPort();
|
|
893
|
-
|
|
894
|
-
// Open the new port
|
|
895
|
-
await newPort.open({ baudRate: 115200 });
|
|
896
|
-
|
|
897
|
-
// Update the port reference
|
|
898
|
-
this.port = newPort;
|
|
899
|
-
|
|
900
|
-
// Reset install state and restart installation
|
|
901
|
-
this._installState = undefined;
|
|
902
|
-
this._installConfirmed = false;
|
|
903
|
-
|
|
904
|
-
// Restart the install process with the same erase setting
|
|
905
|
-
this._confirmInstall();
|
|
906
|
-
} catch (err: any) {
|
|
907
|
-
this._esp32s2ReconnectInProgress = false;
|
|
908
|
-
|
|
909
|
-
if ((err as DOMException).name === "NotFoundError") {
|
|
910
|
-
// User cancelled port selection - stay on reconnect screen
|
|
911
|
-
this.logger.log("User cancelled port selection");
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// Show error and go back to dashboard
|
|
916
|
-
this._error = `Failed to reconnect: ${err.message}`;
|
|
917
|
-
this._state = "ERROR";
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
private async _confirmInstall() {
|
|
922
|
-
this._installConfirmed = true;
|
|
923
|
-
this._installState = undefined;
|
|
924
|
-
// Reset reconnect flag when starting a new installation
|
|
925
|
-
this._esp32s2ReconnectInProgress = false;
|
|
926
|
-
|
|
927
|
-
if (this._client) {
|
|
928
|
-
await this._closeClientWithoutEvents(this._client);
|
|
929
|
-
}
|
|
930
|
-
this._client = undefined;
|
|
931
|
-
|
|
932
|
-
if (this.firmwareFile != undefined) {
|
|
933
|
-
// If a uploaded File was provided -> create Uint8Array of content
|
|
934
|
-
new Blob([this.firmwareFile])
|
|
935
|
-
.arrayBuffer()
|
|
936
|
-
.then((b) => this._flashFilebuffer(new Uint8Array(b)));
|
|
937
|
-
} else {
|
|
938
|
-
// Use "standard way" with URL to manifest and firmware binary
|
|
939
|
-
flash(
|
|
940
|
-
(state) => {
|
|
941
|
-
this._installState = state;
|
|
942
|
-
|
|
943
|
-
if (state.state === FlashStateType.FINISHED) {
|
|
944
|
-
sleep(100)
|
|
945
|
-
.then(() => this._initialize(true))
|
|
946
|
-
.then(() => this.requestUpdate());
|
|
947
|
-
}
|
|
948
|
-
},
|
|
949
|
-
this.port,
|
|
950
|
-
this.logger,
|
|
951
|
-
this.manifestPath,
|
|
952
|
-
this._installErase,
|
|
953
|
-
new Uint8Array(0),
|
|
954
|
-
this.baudRate,
|
|
955
|
-
);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
async _flashFilebuffer(fileBuffer: Uint8Array) {
|
|
960
|
-
flash(
|
|
961
|
-
(state) => {
|
|
962
|
-
this._installState = state;
|
|
963
|
-
|
|
964
|
-
if (state.state === FlashStateType.FINISHED) {
|
|
965
|
-
sleep(100)
|
|
966
|
-
.then(() => this._initialize(true))
|
|
967
|
-
.then(() => this.requestUpdate());
|
|
968
|
-
}
|
|
969
|
-
},
|
|
970
|
-
this.port,
|
|
971
|
-
this.logger,
|
|
972
|
-
this.manifestPath,
|
|
973
|
-
this._installErase,
|
|
974
|
-
fileBuffer,
|
|
975
|
-
this.baudRate,
|
|
976
|
-
);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
private async _doProvision() {
|
|
980
|
-
this._busy = true;
|
|
981
|
-
this._wasProvisioned =
|
|
982
|
-
this._client!.state === ImprovSerialCurrentState.PROVISIONED;
|
|
983
|
-
const ssid =
|
|
984
|
-
this._selectedSsid === -1
|
|
985
|
-
? (
|
|
986
|
-
this.shadowRoot!.querySelector(
|
|
987
|
-
"ewt-textfield[name=ssid]",
|
|
988
|
-
) as EwtTextfield
|
|
989
|
-
).value
|
|
990
|
-
: this._ssids![this._selectedSsid].name;
|
|
991
|
-
const password = (
|
|
992
|
-
this.shadowRoot!.querySelector(
|
|
993
|
-
"ewt-textfield[name=password]",
|
|
994
|
-
) as EwtTextfield
|
|
995
|
-
).value;
|
|
996
|
-
try {
|
|
997
|
-
await this._client!.provision(ssid, password);
|
|
998
|
-
} catch (err: any) {
|
|
999
|
-
return;
|
|
1000
|
-
} finally {
|
|
1001
|
-
this._busy = false;
|
|
1002
|
-
this._provisionForce = false;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
private _handleDisconnect = () => {
|
|
1007
|
-
this._state = "ERROR";
|
|
1008
|
-
this._error = "Disconnected";
|
|
1009
|
-
};
|
|
1010
|
-
|
|
1011
|
-
private async _handleClose() {
|
|
1012
|
-
if (this._client) {
|
|
1013
|
-
await this._closeClientWithoutEvents(this._client);
|
|
1014
|
-
}
|
|
1015
|
-
fireEvent(this, "closed" as any);
|
|
1016
|
-
this.parentNode!.removeChild(this);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
* Return if the device runs same firmware as manifest.
|
|
1021
|
-
*/
|
|
1022
|
-
private get _isSameFirmware() {
|
|
1023
|
-
return !this._info
|
|
1024
|
-
? false
|
|
1025
|
-
: this.overrides?.checkSameFirmware
|
|
1026
|
-
? this.overrides.checkSameFirmware(this._manifest, this._info)
|
|
1027
|
-
: this._info.firmware === this._manifest.name;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
/**
|
|
1031
|
-
* Return if the device runs same firmware and version as manifest.
|
|
1032
|
-
*/
|
|
1033
|
-
private get _isSameVersion() {
|
|
1034
|
-
return (
|
|
1035
|
-
this._isSameFirmware && this._info!.version === this._manifest.version
|
|
1036
|
-
);
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
private async _closeClientWithoutEvents(client: ImprovSerial) {
|
|
1040
|
-
client.removeEventListener("disconnect", this._handleDisconnect);
|
|
1041
|
-
await client.close();
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
static styles = [
|
|
1045
|
-
dialogStyles,
|
|
1046
|
-
css`
|
|
1047
|
-
:host {
|
|
1048
|
-
--mdc-dialog-max-width: 390px;
|
|
1049
|
-
}
|
|
1050
|
-
ewt-icon-button {
|
|
1051
|
-
position: absolute;
|
|
1052
|
-
right: 4px;
|
|
1053
|
-
top: 10px;
|
|
1054
|
-
}
|
|
1055
|
-
.table-row {
|
|
1056
|
-
display: flex;
|
|
1057
|
-
}
|
|
1058
|
-
.table-row.last {
|
|
1059
|
-
margin-bottom: 16px;
|
|
1060
|
-
}
|
|
1061
|
-
.table-row svg {
|
|
1062
|
-
width: 20px;
|
|
1063
|
-
margin-right: 8px;
|
|
1064
|
-
}
|
|
1065
|
-
ewt-textfield,
|
|
1066
|
-
ewt-select {
|
|
1067
|
-
display: block;
|
|
1068
|
-
margin-top: 16px;
|
|
1069
|
-
}
|
|
1070
|
-
.dashboard-buttons {
|
|
1071
|
-
margin: 0 0 -16px -8px;
|
|
1072
|
-
}
|
|
1073
|
-
.dashboard-buttons div {
|
|
1074
|
-
display: block;
|
|
1075
|
-
margin: 4px 0;
|
|
1076
|
-
}
|
|
1077
|
-
a.has-button {
|
|
1078
|
-
text-decoration: none;
|
|
1079
|
-
}
|
|
1080
|
-
.error {
|
|
1081
|
-
color: var(--improv-danger-color);
|
|
1082
|
-
}
|
|
1083
|
-
.danger {
|
|
1084
|
-
--mdc-theme-primary: var(--improv-danger-color);
|
|
1085
|
-
--mdc-theme-secondary: var(--improv-danger-color);
|
|
1086
|
-
}
|
|
1087
|
-
button.link {
|
|
1088
|
-
background: none;
|
|
1089
|
-
color: inherit;
|
|
1090
|
-
border: none;
|
|
1091
|
-
padding: 0;
|
|
1092
|
-
font: inherit;
|
|
1093
|
-
text-align: left;
|
|
1094
|
-
text-decoration: underline;
|
|
1095
|
-
cursor: pointer;
|
|
1096
|
-
}
|
|
1097
|
-
:host([state="LOGS"]) ewt-dialog {
|
|
1098
|
-
--mdc-dialog-max-width: 90vw;
|
|
1099
|
-
}
|
|
1100
|
-
ewt-console {
|
|
1101
|
-
width: calc(80vw - 48px);
|
|
1102
|
-
height: 80vh;
|
|
1103
|
-
}
|
|
1104
|
-
`,
|
|
1105
|
-
];
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
customElements.define("ewt-install-dialog", EwtInstallDialog);
|
|
1109
|
-
|
|
1110
|
-
declare global {
|
|
1111
|
-
interface HTMLElementTagNameMap {
|
|
1112
|
-
"ewt-install-dialog": EwtInstallDialog;
|
|
1113
|
-
}
|
|
1114
|
-
}
|