tasmota-esp-web-tools 11.0.0 → 11.0.1
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/connect.js +62 -14
- package/dist/const.d.ts +3 -11
- package/dist/flash.d.ts +2 -1
- package/dist/flash.js +103 -126
- package/dist/install-button.js +2 -2
- package/dist/install-dialog.d.ts +5 -10
- package/dist/install-dialog.js +304 -207
- package/{js/modules/index-t2Vsxqjj.js → dist/web/index-BK6drzlv.js} +1 -1
- package/dist/web/install-button.js +1 -1
- package/dist/web/install-dialog-Bvm8wyui.js +1239 -0
- package/dist/web/{styles-CWxbqh-J.js → styles-ntwzvT31.js} +1 -1
- package/eslint.config.js +22 -0
- package/{dist/web/index-t2Vsxqjj.js → js/modules/index-BK6drzlv.js} +1 -1
- package/js/modules/install-button.js +1 -1
- package/js/modules/install-dialog-Bvm8wyui.js +1239 -0
- package/js/modules/{styles-CWxbqh-J.js → styles-ntwzvT31.js} +1 -1
- package/package.json +15 -7
- package/dist/util/esp32s2-reconnect.d.ts +0 -22
- package/dist/util/esp32s2-reconnect.js +0 -45
- package/dist/web/install-dialog-BxDmCJNo.js +0 -1269
- package/js/modules/install-dialog-BxDmCJNo.js +0 -1269
package/dist/connect.js
CHANGED
|
@@ -1,29 +1,72 @@
|
|
|
1
|
+
import { connect as esptoolConnect } from "tasmota-webserial-esptool";
|
|
2
|
+
/**
|
|
3
|
+
* Detect if running on Android
|
|
4
|
+
*/
|
|
5
|
+
const isAndroid = () => {
|
|
6
|
+
const userAgent = navigator.userAgent || "";
|
|
7
|
+
return /Android/i.test(userAgent);
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Load WebUSB serial wrapper for Android
|
|
11
|
+
*/
|
|
12
|
+
const loadWebUSBSerial = async () => {
|
|
13
|
+
// Check if already loaded
|
|
14
|
+
if (globalThis.requestSerialPort) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Dynamically load the WebUSB serial script from the npm package
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const script = document.createElement("script");
|
|
20
|
+
script.type = "module";
|
|
21
|
+
script.src =
|
|
22
|
+
"https://unpkg.com/tasmota-webserial-esptool/js/webusb-serial.js";
|
|
23
|
+
script.onload = () => {
|
|
24
|
+
if (globalThis.requestSerialPort) {
|
|
25
|
+
resolve();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
reject(new Error("WebUSB serial script loaded but requestSerialPort not found"));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
script.onerror = () => reject(new Error("Failed to load WebUSB serial script"));
|
|
32
|
+
document.head.appendChild(script);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
1
35
|
export const connect = async (button) => {
|
|
2
36
|
import("./install-dialog.js");
|
|
3
|
-
|
|
37
|
+
// Android: Load WebUSB support first
|
|
38
|
+
if (isAndroid() && "usb" in navigator) {
|
|
39
|
+
try {
|
|
40
|
+
await loadWebUSBSerial();
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
alert(`Failed to load WebUSB support: ${err.message}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Use tasmota-webserial-esptool's connect() - handles ALL port logic
|
|
48
|
+
let esploader;
|
|
4
49
|
try {
|
|
5
|
-
|
|
50
|
+
esploader = await esptoolConnect({
|
|
51
|
+
log: () => { }, // Silent logger for connection
|
|
52
|
+
debug: () => { },
|
|
53
|
+
error: (msg) => console.error(msg),
|
|
54
|
+
});
|
|
6
55
|
}
|
|
7
56
|
catch (err) {
|
|
8
57
|
if (err.name === "NotFoundError") {
|
|
9
58
|
import("./no-port-picked/index").then((mod) => mod.openNoPortPickedDialog(() => connect(button)));
|
|
10
59
|
return;
|
|
11
60
|
}
|
|
12
|
-
alert(`
|
|
61
|
+
alert(`Connection failed: ${err.message}`);
|
|
13
62
|
return;
|
|
14
63
|
}
|
|
15
|
-
if (!
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
await port.open({ baudRate: 115200 });
|
|
20
|
-
}
|
|
21
|
-
catch (err) {
|
|
22
|
-
alert(err.message);
|
|
64
|
+
if (!esploader) {
|
|
65
|
+
alert("Failed to connect to device");
|
|
23
66
|
return;
|
|
24
67
|
}
|
|
25
68
|
const el = document.createElement("ewt-install-dialog");
|
|
26
|
-
el.
|
|
69
|
+
el.esploader = esploader; // Pass ESPLoader instead of port
|
|
27
70
|
el.manifestPath = button.manifest || button.getAttribute("manifest");
|
|
28
71
|
el.overrides = button.overrides;
|
|
29
72
|
el.firmwareFile = button.firmwareFile;
|
|
@@ -38,8 +81,13 @@ export const connect = async (button) => {
|
|
|
38
81
|
else if (button.baudRate !== undefined) {
|
|
39
82
|
el.baudRate = button.baudRate;
|
|
40
83
|
}
|
|
41
|
-
el.addEventListener("closed", () => {
|
|
42
|
-
|
|
84
|
+
el.addEventListener("closed", async () => {
|
|
85
|
+
try {
|
|
86
|
+
await esploader.disconnect();
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
// Ignore disconnect errors
|
|
90
|
+
}
|
|
43
91
|
}, { once: true });
|
|
44
92
|
document.body.appendChild(el);
|
|
45
93
|
};
|
package/dist/const.d.ts
CHANGED
|
@@ -72,13 +72,7 @@ export interface ErrorState extends BaseFlashState {
|
|
|
72
72
|
details: string | Error;
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
export
|
|
76
|
-
state: FlashStateType.ESP32_S2_USB_RECONNECT;
|
|
77
|
-
details: {
|
|
78
|
-
oldPort: SerialPort;
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
export type FlashState = InitializingState | ManifestState | PreparingState | ErasingState | WritingState | FinishedState | ErrorState | ESP32S2USBReconnectState;
|
|
75
|
+
export type FlashState = InitializingState | ManifestState | PreparingState | ErasingState | WritingState | FinishedState | ErrorState;
|
|
82
76
|
export declare const enum FlashStateType {
|
|
83
77
|
INITIALIZING = "initializing",
|
|
84
78
|
MANIFEST = "manifest",
|
|
@@ -86,16 +80,14 @@ export declare const enum FlashStateType {
|
|
|
86
80
|
ERASING = "erasing",
|
|
87
81
|
WRITING = "writing",
|
|
88
82
|
FINISHED = "finished",
|
|
89
|
-
ERROR = "error"
|
|
90
|
-
ESP32_S2_USB_RECONNECT = "esp32_s2_usb_reconnect"
|
|
83
|
+
ERROR = "error"
|
|
91
84
|
}
|
|
92
85
|
export declare const enum FlashError {
|
|
93
86
|
FAILED_INITIALIZING = "failed_initialize",
|
|
94
87
|
FAILED_MANIFEST_FETCH = "fetch_manifest_failed",
|
|
95
88
|
NOT_SUPPORTED = "not_supported",
|
|
96
89
|
FAILED_FIRMWARE_DOWNLOAD = "failed_firmware_download",
|
|
97
|
-
WRITE_FAILED = "write_failed"
|
|
98
|
-
ESP32_S2_USB_RECONNECT = "esp32_s2_usb_reconnect"
|
|
90
|
+
WRITE_FAILED = "write_failed"
|
|
99
91
|
}
|
|
100
92
|
declare global {
|
|
101
93
|
interface HTMLElementEventMap {
|
package/dist/flash.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Logger } from "tasmota-webserial-esptool";
|
|
2
2
|
import { FlashState } from "./const";
|
|
3
|
-
export declare const flash: (onEvent: (state: FlashState) => void,
|
|
3
|
+
export declare const flash: (onEvent: (state: FlashState) => void, esploader: any, // ESPLoader instance from tasmota-webserial-esptool
|
|
4
|
+
logger: Logger, manifestPath: string, eraseFirst: boolean, firmwareBuffer: Uint8Array, baudRate?: number) => Promise<void>;
|
package/dist/flash.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { ESPLoader } from "tasmota-webserial-esptool";
|
|
2
1
|
import { getChipFamilyName } from "./util/chip-family-name";
|
|
3
2
|
import { sleep } from "./util/sleep";
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export const flash = async (onEvent, esploader, // ESPLoader instance from tasmota-webserial-esptool
|
|
4
|
+
logger, manifestPath, eraseFirst, firmwareBuffer, baudRate) => {
|
|
6
5
|
let manifest;
|
|
7
6
|
let build;
|
|
8
7
|
let chipFamily;
|
|
9
8
|
let chipVariant = null;
|
|
10
|
-
const isS2NativeUSB = isESP32S2NativeUSB(port);
|
|
11
9
|
const fireStateEvent = (stateUpdate) => onEvent({
|
|
12
10
|
...stateUpdate,
|
|
13
11
|
manifest,
|
|
@@ -24,57 +22,31 @@ export const flash = async (onEvent, port, logger, manifestPath, eraseFirst, fir
|
|
|
24
22
|
manifestURL = new URL(manifestPath, location.toString()).toString();
|
|
25
23
|
manifestProm = fetch(manifestURL).then((resp) => resp.json());
|
|
26
24
|
}
|
|
27
|
-
|
|
25
|
+
// Use the provided ESPLoader instance - NO port logic here!
|
|
28
26
|
// For debugging
|
|
29
27
|
window.esploader = esploader;
|
|
30
|
-
// ESP32-S2 Native USB event handler - listen on ESPLoader instance
|
|
31
|
-
const handleESP32S2Reconnect = () => {
|
|
32
|
-
logger.log("ESP32-S2 Native USB disconnect detected - reconnection required");
|
|
33
|
-
};
|
|
34
|
-
// Register event listener for ESP32-S2 Native USB reconnect on ESPLoader
|
|
35
|
-
if (isS2NativeUSB) {
|
|
36
|
-
esploader.addEventListener("esp32s2-usb-reconnect", handleESP32S2Reconnect);
|
|
37
|
-
logger.log("ESP32-S2 Native USB detected - monitoring for port switch");
|
|
38
|
-
}
|
|
39
|
-
// Cleanup function to remove event listener
|
|
40
|
-
const cleanup = () => {
|
|
41
|
-
if (isS2NativeUSB) {
|
|
42
|
-
esploader.removeEventListener("esp32s2-usb-reconnect", handleESP32S2Reconnect);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
28
|
fireStateEvent({
|
|
46
29
|
state: "initializing" /* FlashStateType.INITIALIZING */,
|
|
47
30
|
message: "Initializing...",
|
|
48
31
|
details: { done: false },
|
|
49
32
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
cleanup();
|
|
58
|
-
// Close and forget the old port
|
|
59
|
-
await closeAndForgetPort(port);
|
|
60
|
-
// Fire reconnect event to trigger port reselection dialog
|
|
33
|
+
// Only initialize if not already done
|
|
34
|
+
if (!esploader.chipFamily) {
|
|
35
|
+
try {
|
|
36
|
+
await esploader.initialize();
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
logger.error(err);
|
|
61
40
|
fireStateEvent({
|
|
62
|
-
state: "
|
|
63
|
-
message: "
|
|
64
|
-
details: {
|
|
41
|
+
state: "error" /* FlashStateType.ERROR */,
|
|
42
|
+
message: "Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
|
|
43
|
+
details: { error: "failed_initialize" /* FlashError.FAILED_INITIALIZING */, details: err },
|
|
65
44
|
});
|
|
45
|
+
if (esploader.connected) {
|
|
46
|
+
await esploader.disconnect();
|
|
47
|
+
}
|
|
66
48
|
return;
|
|
67
49
|
}
|
|
68
|
-
cleanup();
|
|
69
|
-
fireStateEvent({
|
|
70
|
-
state: "error" /* FlashStateType.ERROR */,
|
|
71
|
-
message: "Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
|
|
72
|
-
details: { error: "failed_initialize" /* FlashError.FAILED_INITIALIZING */, details: err },
|
|
73
|
-
});
|
|
74
|
-
if (esploader.connected) {
|
|
75
|
-
await esploader.disconnect();
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
50
|
}
|
|
79
51
|
chipFamily = getChipFamilyName(esploader);
|
|
80
52
|
chipVariant = esploader.chipVariant;
|
|
@@ -92,7 +64,6 @@ export const flash = async (onEvent, port, logger, manifestPath, eraseFirst, fir
|
|
|
92
64
|
manifest = await manifestProm;
|
|
93
65
|
}
|
|
94
66
|
catch (err) {
|
|
95
|
-
cleanup();
|
|
96
67
|
fireStateEvent({
|
|
97
68
|
state: "error" /* FlashStateType.ERROR */,
|
|
98
69
|
message: `Unable to fetch manifest: ${err}`,
|
|
@@ -106,80 +77,74 @@ export const flash = async (onEvent, port, logger, manifestPath, eraseFirst, fir
|
|
|
106
77
|
if (b.chipFamily !== chipFamily) {
|
|
107
78
|
return false;
|
|
108
79
|
}
|
|
109
|
-
// If build specifies
|
|
110
|
-
if (b.chipVariant !==
|
|
111
|
-
return
|
|
80
|
+
// If build specifies chipVariant, it must match
|
|
81
|
+
if (b.chipVariant && b.chipVariant !== chipVariant) {
|
|
82
|
+
return false;
|
|
112
83
|
}
|
|
113
|
-
// If build doesn't specify chipVariant, it matches any variant
|
|
114
84
|
return true;
|
|
115
85
|
});
|
|
116
|
-
fireStateEvent({
|
|
117
|
-
state: "manifest" /* FlashStateType.MANIFEST */,
|
|
118
|
-
message: `Found manifest for ${manifest.name}`,
|
|
119
|
-
details: { done: true },
|
|
120
|
-
});
|
|
121
86
|
if (!build) {
|
|
122
|
-
const chipInfo = chipVariant
|
|
123
|
-
? `${chipFamily} (${chipVariant})`
|
|
124
|
-
: chipFamily;
|
|
125
|
-
cleanup();
|
|
126
87
|
fireStateEvent({
|
|
127
88
|
state: "error" /* FlashStateType.ERROR */,
|
|
128
|
-
message: `Your ${
|
|
129
|
-
details: { error: "not_supported" /* FlashError.NOT_SUPPORTED */, details:
|
|
89
|
+
message: `Your ${chipFamily}${chipVariant ? ` (${chipVariant})` : ""} is not supported by this firmware.`,
|
|
90
|
+
details: { error: "not_supported" /* FlashError.NOT_SUPPORTED */, details: chipFamily },
|
|
130
91
|
});
|
|
131
92
|
await esploader.disconnect();
|
|
132
93
|
return;
|
|
133
94
|
}
|
|
95
|
+
fireStateEvent({
|
|
96
|
+
state: "manifest" /* FlashStateType.MANIFEST */,
|
|
97
|
+
message: "Manifest fetched",
|
|
98
|
+
details: { done: true },
|
|
99
|
+
});
|
|
134
100
|
fireStateEvent({
|
|
135
101
|
state: "preparing" /* FlashStateType.PREPARING */,
|
|
136
102
|
message: "Preparing installation...",
|
|
137
103
|
details: { done: false },
|
|
138
104
|
});
|
|
105
|
+
// The esploader passed in is always a stub (from _ensureStub())
|
|
106
|
+
// Baudrate was already set in _ensureStub()
|
|
107
|
+
const espStub = esploader;
|
|
108
|
+
// Verify stub has chipFamily (should be copied in _ensureStub)
|
|
109
|
+
if (!espStub.chipFamily) {
|
|
110
|
+
logger.error("Stub missing chipFamily - this should not happen!");
|
|
111
|
+
fireStateEvent({
|
|
112
|
+
state: "error" /* FlashStateType.ERROR */,
|
|
113
|
+
message: "Internal error: Stub not properly initialized",
|
|
114
|
+
details: {
|
|
115
|
+
error: "failed_initialize" /* FlashError.FAILED_INITIALIZING */,
|
|
116
|
+
details: "Missing chipFamily",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
logger.log(`Using stub: IS_STUB=${espStub.IS_STUB}, chipFamily=${espStub.chipFamily}`);
|
|
122
|
+
// Fetch firmware files
|
|
139
123
|
const filePromises = build.parts.map(async (part) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!resp.ok) {
|
|
145
|
-
throw new Error(`Downlading firmware ${part.path} failed: ${resp.status}`);
|
|
146
|
-
}
|
|
147
|
-
return resp.arrayBuffer();
|
|
124
|
+
const url = new URL(part.path, manifestURL || location.toString()).toString();
|
|
125
|
+
const resp = await fetch(url);
|
|
126
|
+
if (!resp.ok) {
|
|
127
|
+
throw new Error(`Downlading firmware ${part.path} failed: ${resp.status}`);
|
|
148
128
|
}
|
|
149
|
-
|
|
150
|
-
return firmwareBuffer;
|
|
129
|
+
return resp.arrayBuffer();
|
|
151
130
|
});
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Default: No change (115200 baud - stub default)
|
|
156
|
-
// Can be set via baud-rate attribute in HTML (e.g., baud-rate="2000000")
|
|
157
|
-
if (baudRate !== undefined && baudRate > 115200) {
|
|
158
|
-
try {
|
|
159
|
-
await espStub.setBaudrate(baudRate);
|
|
160
|
-
}
|
|
161
|
-
catch (err) {
|
|
162
|
-
// If baud rate change fails, continue with default 115200
|
|
163
|
-
logger.log(`Could not change baud rate to ${baudRate}: ${err.message}`);
|
|
164
|
-
}
|
|
131
|
+
// If firmwareBuffer is provided, use it instead of fetching
|
|
132
|
+
if (firmwareBuffer) {
|
|
133
|
+
filePromises.push(Promise.resolve(firmwareBuffer.buffer));
|
|
165
134
|
}
|
|
166
135
|
const files = [];
|
|
167
136
|
let totalSize = 0;
|
|
168
137
|
for (const prom of filePromises) {
|
|
169
138
|
try {
|
|
170
139
|
const data = await prom;
|
|
171
|
-
files.push(data
|
|
140
|
+
files.push(data);
|
|
172
141
|
totalSize += data.byteLength;
|
|
173
142
|
}
|
|
174
143
|
catch (err) {
|
|
175
|
-
cleanup();
|
|
176
144
|
fireStateEvent({
|
|
177
145
|
state: "error" /* FlashStateType.ERROR */,
|
|
178
146
|
message: err.message,
|
|
179
|
-
details: {
|
|
180
|
-
error: "failed_firmware_download" /* FlashError.FAILED_FIRMWARE_DOWNLOAD */,
|
|
181
|
-
details: err.message,
|
|
182
|
-
},
|
|
147
|
+
details: { error: "failed_firmware_download" /* FlashError.FAILED_FIRMWARE_DOWNLOAD */, details: err },
|
|
183
148
|
});
|
|
184
149
|
await esploader.disconnect();
|
|
185
150
|
return;
|
|
@@ -190,78 +155,90 @@ export const flash = async (onEvent, port, logger, manifestPath, eraseFirst, fir
|
|
|
190
155
|
message: "Installation prepared",
|
|
191
156
|
details: { done: true },
|
|
192
157
|
});
|
|
158
|
+
// CRITICAL: Erase MUST be done BEFORE writing, if requested
|
|
193
159
|
if (eraseFirst) {
|
|
194
160
|
fireStateEvent({
|
|
195
161
|
state: "erasing" /* FlashStateType.ERASING */,
|
|
196
|
-
message: "Erasing
|
|
162
|
+
message: "Erasing flash...",
|
|
197
163
|
details: { done: false },
|
|
198
164
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
165
|
+
try {
|
|
166
|
+
logger.log("Erasing flash memory. Please wait...");
|
|
167
|
+
await espStub.eraseFlash();
|
|
168
|
+
logger.log("Flash erased successfully");
|
|
169
|
+
fireStateEvent({
|
|
170
|
+
state: "erasing" /* FlashStateType.ERASING */,
|
|
171
|
+
message: "Flash erased",
|
|
172
|
+
details: { done: true },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
logger.error(`Flash erase failed: ${err.message}`);
|
|
177
|
+
fireStateEvent({
|
|
178
|
+
state: "error" /* FlashStateType.ERROR */,
|
|
179
|
+
message: `Failed to erase flash: ${err.message}`,
|
|
180
|
+
details: { error: "write_failed" /* FlashError.WRITE_FAILED */, details: err },
|
|
181
|
+
});
|
|
182
|
+
await esploader.disconnect();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
205
185
|
}
|
|
206
|
-
let lastPct = 0;
|
|
207
186
|
fireStateEvent({
|
|
208
187
|
state: "writing" /* FlashStateType.WRITING */,
|
|
209
|
-
message: `Writing progress:
|
|
188
|
+
message: `Writing progress: 0 %`,
|
|
210
189
|
details: {
|
|
211
190
|
bytesTotal: totalSize,
|
|
212
191
|
bytesWritten: 0,
|
|
213
|
-
percentage:
|
|
192
|
+
percentage: 0,
|
|
214
193
|
},
|
|
215
194
|
});
|
|
216
|
-
let
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
195
|
+
let lastPct = 0;
|
|
196
|
+
let totalBytesWritten = 0;
|
|
197
|
+
try {
|
|
198
|
+
for (let i = 0; i < build.parts.length; i++) {
|
|
199
|
+
const part = build.parts[i];
|
|
200
|
+
const data = files[i];
|
|
201
|
+
await espStub.flashData(data, (bytesWritten, bytesTotal) => {
|
|
202
|
+
const newPct = Math.floor(((totalBytesWritten + bytesWritten) / totalSize) * 100);
|
|
223
203
|
if (newPct === lastPct) {
|
|
224
204
|
return;
|
|
225
205
|
}
|
|
226
206
|
lastPct = newPct;
|
|
227
207
|
fireStateEvent({
|
|
228
208
|
state: "writing" /* FlashStateType.WRITING */,
|
|
229
|
-
message: `Writing progress: ${newPct}%`,
|
|
209
|
+
message: `Writing progress: ${newPct} %`,
|
|
230
210
|
details: {
|
|
231
211
|
bytesTotal: totalSize,
|
|
232
|
-
bytesWritten:
|
|
212
|
+
bytesWritten: totalBytesWritten + bytesWritten,
|
|
233
213
|
percentage: newPct,
|
|
234
214
|
},
|
|
235
215
|
});
|
|
236
|
-
}, part.offset
|
|
216
|
+
}, part.offset);
|
|
217
|
+
totalBytesWritten += data.byteLength;
|
|
237
218
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
totalWritten += file.byteLength;
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
fireStateEvent({
|
|
222
|
+
state: "error" /* FlashStateType.ERROR */,
|
|
223
|
+
message: err.message,
|
|
224
|
+
details: { error: "write_failed" /* FlashError.WRITE_FAILED */, details: err },
|
|
225
|
+
});
|
|
226
|
+
await esploader.disconnect();
|
|
227
|
+
return;
|
|
249
228
|
}
|
|
250
229
|
fireStateEvent({
|
|
251
230
|
state: "writing" /* FlashStateType.WRITING */,
|
|
252
231
|
message: "Writing complete",
|
|
253
232
|
details: {
|
|
254
233
|
bytesTotal: totalSize,
|
|
255
|
-
bytesWritten:
|
|
234
|
+
bytesWritten: totalSize,
|
|
256
235
|
percentage: 100,
|
|
257
236
|
},
|
|
258
237
|
});
|
|
259
238
|
await sleep(100);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
await esploader.hardReset();
|
|
264
|
-
cleanup();
|
|
239
|
+
// DON'T release locks after flash!
|
|
240
|
+
// Keep the stub and locks so the port can be used again
|
|
241
|
+
// (e.g., for Improv, Manage Filesystem, or another flash)
|
|
265
242
|
fireStateEvent({
|
|
266
243
|
state: "finished" /* FlashStateType.FINISHED */,
|
|
267
244
|
message: "All done!",
|
package/dist/install-button.js
CHANGED
|
@@ -9,7 +9,7 @@ export class InstallButton extends HTMLElement {
|
|
|
9
9
|
this.toggleAttribute("install-unsupported", true);
|
|
10
10
|
this.renderRoot.innerHTML = !InstallButton.isAllowed
|
|
11
11
|
? "<slot name='not-allowed'>You can only install ESP devices on HTTPS websites or on the localhost.</slot>"
|
|
12
|
-
: "<slot name='unsupported'>Your browser does not support installing things on ESP devices. Use Google Chrome or Microsoft Edge.</slot>";
|
|
12
|
+
: "<slot name='unsupported'>Your browser does not support installing things on ESP devices. Use Google Chrome or Microsoft Edge (Desktop) or Chrome on Android with USB OTG.</slot>";
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
this.toggleAttribute("install-supported", true);
|
|
@@ -36,7 +36,7 @@ export class InstallButton extends HTMLElement {
|
|
|
36
36
|
this.renderRoot.append(slot);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
InstallButton.isSupported = "serial" in navigator;
|
|
39
|
+
InstallButton.isSupported = "serial" in navigator || "usb" in navigator;
|
|
40
40
|
InstallButton.isAllowed = window.isSecureContext;
|
|
41
41
|
InstallButton.style = `
|
|
42
42
|
button {
|
package/dist/install-dialog.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import "./pages/ewt-page-message";
|
|
|
14
14
|
import { Logger, Manifest } from "./const.js";
|
|
15
15
|
import { ImprovSerial } from "improv-wifi-serial-sdk/dist/serial";
|
|
16
16
|
export declare class EwtInstallDialog extends LitElement {
|
|
17
|
-
|
|
17
|
+
esploader: any;
|
|
18
18
|
manifestPath: string;
|
|
19
19
|
firmwareFile?: File;
|
|
20
20
|
baudRate?: number;
|
|
@@ -33,12 +33,15 @@ export declare class EwtInstallDialog extends LitElement {
|
|
|
33
33
|
private _wasProvisioned;
|
|
34
34
|
private _error?;
|
|
35
35
|
private _busy;
|
|
36
|
-
private _esp32s2ReconnectInProgress;
|
|
37
36
|
private _ssids?;
|
|
38
37
|
private _selectedSsid;
|
|
39
38
|
private _partitions?;
|
|
40
39
|
private _selectedPartition?;
|
|
41
40
|
private _espStub?;
|
|
41
|
+
private _improvChecked;
|
|
42
|
+
private get _isAndroid();
|
|
43
|
+
private _ensureStub;
|
|
44
|
+
private get _port();
|
|
42
45
|
protected render(): TemplateResult<1>;
|
|
43
46
|
_renderProgress(label: string | TemplateResult, progress?: number): TemplateResult<1>;
|
|
44
47
|
_renderError(label: string): [string, TemplateResult, boolean];
|
|
@@ -50,8 +53,6 @@ export declare class EwtInstallDialog extends LitElement {
|
|
|
50
53
|
_renderLogs(): [string | undefined, TemplateResult, boolean];
|
|
51
54
|
_renderPartitions(): [string | undefined, TemplateResult, boolean];
|
|
52
55
|
_renderLittleFS(): [string | undefined, TemplateResult, boolean, boolean];
|
|
53
|
-
_renderESP32S2Reconnect(): [string | undefined, TemplateResult, boolean];
|
|
54
|
-
private _handleESP32S2ReconnectClick;
|
|
55
56
|
private _readPartitionTable;
|
|
56
57
|
private _openFilesystem;
|
|
57
58
|
private _formatSize;
|
|
@@ -61,12 +62,6 @@ export declare class EwtInstallDialog extends LitElement {
|
|
|
61
62
|
private _focusFormElement;
|
|
62
63
|
private _initialize;
|
|
63
64
|
private _startInstall;
|
|
64
|
-
/**
|
|
65
|
-
* Handle ESP32-S2 Native USB reconnect.
|
|
66
|
-
* When the ESP32-S2 switches from ROM bootloader to USB CDC mode,
|
|
67
|
-
* the USB port changes and we need to let the user select the new port.
|
|
68
|
-
*/
|
|
69
|
-
private _handleESP32S2Reconnect;
|
|
70
65
|
private _confirmInstall;
|
|
71
66
|
_flashFilebuffer(fileBuffer: Uint8Array): Promise<void>;
|
|
72
67
|
private _doProvision;
|