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