mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-04-19 04:38:54 +00:00
feat: factory kit
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json",
|
||||
"baseLocale": "en",
|
||||
"adapter": "svelte"
|
||||
}
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json",
|
||||
"baseLocale": "en",
|
||||
"adapter": "svelte"
|
||||
}
|
||||
@@ -107,6 +107,7 @@ const config = {
|
||||
"delete_sweep",
|
||||
"print",
|
||||
"restore_from_trash",
|
||||
"factory",
|
||||
"history",
|
||||
"history_toggle_off",
|
||||
"text_to_speech",
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"glob": "^11.0.3",
|
||||
"js-yaml": "^4.1.1",
|
||||
"jsdom": "^26.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier-plugin-css-order": "^2.1.2",
|
||||
|
||||
72
pnpm-lock.yaml
generated
72
pnpm-lock.yaml
generated
@@ -125,6 +125,9 @@ importers:
|
||||
jsdom:
|
||||
specifier: ^26.1.0
|
||||
version: 26.1.0
|
||||
jszip:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
npm-run-all:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
@@ -2562,7 +2565,7 @@ packages:
|
||||
|
||||
glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
|
||||
global-dirs@3.0.1:
|
||||
resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
|
||||
@@ -2698,6 +2701,9 @@ packages:
|
||||
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
|
||||
immutable@5.1.1:
|
||||
resolution: {integrity: sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==}
|
||||
|
||||
@@ -2936,6 +2942,9 @@ packages:
|
||||
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
isarray@1.0.0:
|
||||
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
|
||||
|
||||
isarray@2.0.5:
|
||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||
|
||||
@@ -3016,6 +3025,9 @@ packages:
|
||||
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
|
||||
engines: {'0': node >=0.6.0}
|
||||
|
||||
jszip@3.10.1:
|
||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||
|
||||
keyv@5.5.5:
|
||||
resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==}
|
||||
|
||||
@@ -3044,6 +3056,9 @@ packages:
|
||||
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
lie@3.3.0:
|
||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
@@ -3269,6 +3284,9 @@ packages:
|
||||
pako@0.2.9:
|
||||
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
pako@2.1.0:
|
||||
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
|
||||
|
||||
@@ -3429,6 +3447,9 @@ packages:
|
||||
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
|
||||
engines: {node: ^14.13.1 || >=16.0.0}
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
||||
process@0.11.10:
|
||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
@@ -3464,6 +3485,9 @@ packages:
|
||||
resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
readable-stream@2.3.8:
|
||||
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
|
||||
|
||||
readdirp@4.0.2:
|
||||
resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
@@ -3573,6 +3597,9 @@ packages:
|
||||
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
@@ -3631,6 +3658,9 @@ packages:
|
||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
setimmediate@1.0.5:
|
||||
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
|
||||
|
||||
shebang-command@1.2.0:
|
||||
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3789,6 +3819,9 @@ packages:
|
||||
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
string_decoder@1.1.1:
|
||||
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
|
||||
|
||||
stringify-object@3.3.0:
|
||||
resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -7280,6 +7313,8 @@ snapshots:
|
||||
|
||||
ignore@7.0.5: {}
|
||||
|
||||
immediate@3.0.6: {}
|
||||
|
||||
immutable@5.1.1: {}
|
||||
|
||||
import-fresh@3.3.0:
|
||||
@@ -7510,6 +7545,8 @@ snapshots:
|
||||
call-bound: 1.0.4
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
isarray@1.0.0: {}
|
||||
|
||||
isarray@2.0.5: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
@@ -7596,6 +7633,13 @@ snapshots:
|
||||
json-schema: 0.4.0
|
||||
verror: 1.10.0
|
||||
|
||||
jszip@3.10.1:
|
||||
dependencies:
|
||||
lie: 3.3.0
|
||||
pako: 1.0.11
|
||||
readable-stream: 2.3.8
|
||||
setimmediate: 1.0.5
|
||||
|
||||
keyv@5.5.5:
|
||||
dependencies:
|
||||
'@keyv/serialize': 1.1.1
|
||||
@@ -7614,6 +7658,10 @@ snapshots:
|
||||
|
||||
leven@3.1.0: {}
|
||||
|
||||
lie@3.3.0:
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
listr2@3.14.0(enquirer@2.4.1):
|
||||
@@ -7823,6 +7871,8 @@ snapshots:
|
||||
|
||||
pako@0.2.9: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
pako@2.1.0: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
@@ -7948,6 +7998,8 @@ snapshots:
|
||||
|
||||
pretty-bytes@6.1.1: {}
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
|
||||
process@0.11.10: {}
|
||||
|
||||
proxy-from-env@1.0.0: {}
|
||||
@@ -7981,6 +8033,16 @@ snapshots:
|
||||
normalize-package-data: 2.5.0
|
||||
path-type: 3.0.0
|
||||
|
||||
readable-stream@2.3.8:
|
||||
dependencies:
|
||||
core-util-is: 1.0.2
|
||||
inherits: 2.0.4
|
||||
isarray: 1.0.0
|
||||
process-nextick-args: 2.0.1
|
||||
safe-buffer: 5.1.2
|
||||
string_decoder: 1.1.1
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readdirp@4.0.2: {}
|
||||
|
||||
reflect.getprototypeof@1.0.10:
|
||||
@@ -8127,6 +8189,8 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
isarray: 2.0.5
|
||||
|
||||
safe-buffer@5.1.2: {}
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
safe-push-apply@1.0.0:
|
||||
@@ -8194,6 +8258,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
setimmediate@1.0.5: {}
|
||||
|
||||
shebang-command@1.2.0:
|
||||
dependencies:
|
||||
shebang-regex: 1.0.0
|
||||
@@ -8410,6 +8476,10 @@ snapshots:
|
||||
define-properties: 1.2.1
|
||||
es-object-atoms: 1.0.0
|
||||
|
||||
string_decoder@1.1.1:
|
||||
dependencies:
|
||||
safe-buffer: 5.1.2
|
||||
|
||||
stringify-object@3.3.0:
|
||||
dependencies:
|
||||
get-own-enumerable-property-symbols: 3.0.2
|
||||
|
||||
121
src/lib/assets/factory-flash.ps1
Normal file
121
src/lib/assets/factory-flash.ps1
Normal file
@@ -0,0 +1,121 @@
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
|
||||
# Function to create a form for COM port selection
|
||||
function Show-ComPortSelectionForm {
|
||||
# Create the form
|
||||
$form = New-Object System.Windows.Forms.Form
|
||||
$form.Text = "Select a COM Port"
|
||||
$form.Size = New-Object System.Drawing.Size(400, 400) # Set the size of the form
|
||||
$form.StartPosition = "CenterScreen"
|
||||
|
||||
# Create a label to display a message
|
||||
$infoLabel = New-Object System.Windows.Forms.Label
|
||||
$infoLabel.Location = New-Object System.Drawing.Point(20, 20)
|
||||
$infoLabel.Size = New-Object System.Drawing.Size(340, 30)
|
||||
$infoLabel.Text = "Select a COM port."
|
||||
|
||||
# Create a ListBox to hold the COM port items
|
||||
$comPortListBox = New-Object System.Windows.Forms.ListBox
|
||||
$comPortListBox.Location = New-Object System.Drawing.Point(20, 60)
|
||||
$comPortListBox.Size = New-Object System.Drawing.Size(340, 200)
|
||||
|
||||
# Create a label for status output
|
||||
$statusLabel = New-Object System.Windows.Forms.Label
|
||||
$statusLabel.Location = New-Object System.Drawing.Point(20, 280)
|
||||
$statusLabel.Size = New-Object System.Drawing.Size(340, 60)
|
||||
$statusLabel.Text = "Status: Ready"
|
||||
$statusLabel.ForeColor = [System.Drawing.Color]::Black
|
||||
|
||||
# Function to populate COM ports
|
||||
function PopulateComPorts {
|
||||
# Clear existing items
|
||||
$comPortListBox.Items.Clear()
|
||||
|
||||
# Get all COM ports with "OK" status
|
||||
$comPorts = Get-PnpDevice | Where-Object { $_.Class -eq 'Ports' -and $_.Status -eq 'OK' }
|
||||
|
||||
# Add full friendly name to the ListBox
|
||||
foreach ($comPort in $comPorts) {
|
||||
$comPortListBox.Items.Add($comPort.FriendlyName)
|
||||
}
|
||||
}
|
||||
|
||||
# Populate COM ports initially
|
||||
PopulateComPorts
|
||||
|
||||
# Event handler for mouse click
|
||||
$comPortListBox.Add_MouseClick({
|
||||
ProcessSelection
|
||||
})
|
||||
|
||||
# Event handler for "Enter" key press
|
||||
$comPortListBox.Add_KeyDown({
|
||||
param($sender, $e)
|
||||
if ($e.KeyCode -eq 'Enter') {
|
||||
ProcessSelection
|
||||
}
|
||||
})
|
||||
|
||||
# Function to process selection
|
||||
function ProcessSelection {
|
||||
if ($comPortListBox.SelectedItem) {
|
||||
# Get the selected full friendly name
|
||||
$selectedFriendlyName = $comPortListBox.SelectedItem
|
||||
|
||||
# Extract the COMx part from the friendly name
|
||||
$selectedFriendlyName -match '(COM\d+)' | Out-Null
|
||||
$selectedComPort = $Matches[1]
|
||||
|
||||
if ($selectedComPort) {
|
||||
# Clear the status label
|
||||
$statusLabel.Text = "Status: Executing..."
|
||||
$statusLabel.ForeColor = [System.Drawing.Color]::Black
|
||||
|
||||
# Run the batch file with the extracted COM port (e.g., COM3)
|
||||
$process = Start-Process cmd.exe -ArgumentList "/c .\flash.bat $selectedComPort" -NoNewWindow -PassThru -Wait
|
||||
$exitCode = $process.ExitCode
|
||||
|
||||
if ($exitCode -eq 0) {
|
||||
# Successful execution
|
||||
$statusLabel.Text = "Status: Execution completed successfully."
|
||||
$statusLabel.ForeColor = [System.Drawing.Color]::Green
|
||||
} else {
|
||||
# Error occurred
|
||||
$statusLabel.Text = "Status: Error occurred during execution."
|
||||
$statusLabel.ForeColor = [System.Drawing.Color]::Red
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create a timer for automatic updates
|
||||
$timer = New-Object System.Windows.Forms.Timer
|
||||
$timer.Interval = 1000 # Set interval to 1 second
|
||||
|
||||
# Timer Tick event to refresh the COM port items
|
||||
$timer.Add_Tick({
|
||||
PopulateComPorts # Refresh the COM port items
|
||||
})
|
||||
|
||||
# Start the timer when the form is shown
|
||||
$form.Add_Shown({
|
||||
$timer.Start()
|
||||
})
|
||||
|
||||
# Stop the timer when the form is closed
|
||||
$form.Add_FormClosing({
|
||||
$timer.Stop()
|
||||
})
|
||||
|
||||
# Add controls to the form
|
||||
$form.Controls.Add($infoLabel)
|
||||
$form.Controls.Add($comPortListBox) # Add the ListBox to the form
|
||||
$form.Controls.Add($statusLabel) # Add the status label
|
||||
|
||||
# Show the form
|
||||
$form.ShowDialog()
|
||||
}
|
||||
|
||||
# Show the COM port selection dialog
|
||||
Show-ComPortSelectionForm
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { lt as semverLt } from "semver";
|
||||
import type { LoaderOptions, ESPLoader } from "esptool-js";
|
||||
import ProgressButton from "$lib/ProgressButton.svelte";
|
||||
import FactoryKit from "./FactoryKit.svelte";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -354,6 +355,10 @@
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
{#if data.meta.update.esptool}
|
||||
<FactoryKit meta={data.meta as any} />
|
||||
{/if}
|
||||
|
||||
{#if false && data.meta.update.esptool}
|
||||
<section>
|
||||
<h3>Factory Flash (WIP)</h3>
|
||||
|
||||
64
src/routes/(app)/ccos/[device]/[version]/FactoryKit.svelte
Normal file
64
src/routes/(app)/ccos/[device]/[version]/FactoryKit.svelte
Normal file
@@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import ProgressButton from "$lib/ProgressButton.svelte";
|
||||
import {
|
||||
assembleFactoryKit,
|
||||
type VersionMetaWithEsptool,
|
||||
} from "./factory-kit";
|
||||
|
||||
let { meta }: { meta: VersionMetaWithEsptool } = $props();
|
||||
|
||||
const osChoices = ["Windows", "Linux", "MacOS"] as const;
|
||||
let os: (typeof osChoices)[number] = $state(osChoices[0]);
|
||||
|
||||
let working = $state(false);
|
||||
let progress = $state(0);
|
||||
let error: string | undefined = $state(undefined);
|
||||
|
||||
async function download() {
|
||||
progress = 0;
|
||||
error = undefined;
|
||||
working = true;
|
||||
try {
|
||||
const result = await assembleFactoryKit(meta, os, (value) => {
|
||||
progress = value;
|
||||
});
|
||||
const url = URL.createObjectURL(result);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `factory-kit-${meta.device}-${meta.version}-${os}.zip`;
|
||||
a.click();
|
||||
} catch (err) {
|
||||
error = (err as Error).message;
|
||||
} finally {
|
||||
working = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<h3><span class="icon">factory</span> Factory Kit</h3>
|
||||
<div class="os-selection">
|
||||
{#each osChoices as value}
|
||||
<label
|
||||
><input type="radio" name="os" {value} bind:group={os} />{value}</label
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
<ProgressButton onclick={download} {progress} {working} {error}
|
||||
><span class="icon">download</span> Download</ProgressButton
|
||||
>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
.os-selection {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
142
src/routes/(app)/ccos/[device]/[version]/factory-kit.ts
Normal file
142
src/routes/(app)/ccos/[device]/[version]/factory-kit.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import type { VersionMeta } from "$lib/meta/types/meta";
|
||||
|
||||
async function progressFetch(
|
||||
url: string,
|
||||
onProgress: (progress: number) => void,
|
||||
) {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open("GET", url, true);
|
||||
request.responseType = "arraybuffer";
|
||||
request.onprogress = (event) => {
|
||||
if (event.total > 0) {
|
||||
onProgress(event.loaded / event.total);
|
||||
}
|
||||
};
|
||||
const result = new Promise<ArrayBuffer>((resolve, reject) => {
|
||||
request.onload = () => {
|
||||
onProgress(1);
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
resolve(request.response);
|
||||
} else {
|
||||
reject(new Error(`Failed to fetch ${url}: ${request.statusText}`));
|
||||
}
|
||||
};
|
||||
});
|
||||
request.send();
|
||||
return result;
|
||||
}
|
||||
export type VersionMetaWithEsptool = VersionMeta & {
|
||||
update: { esptool: Exclude<VersionMeta["update"]["esptool"], undefined> };
|
||||
};
|
||||
|
||||
export async function assembleFactoryKit(
|
||||
meta: VersionMetaWithEsptool,
|
||||
os: "Windows" | "Linux" | "MacOS",
|
||||
onProgress: (progress: number) => void,
|
||||
): Promise<Blob> {
|
||||
let otherProgress: number[] = new Array(
|
||||
Object.keys(meta.update.esptool.files).length,
|
||||
).fill(0);
|
||||
let esptoolProgress = 0;
|
||||
let compressProgress = 0;
|
||||
function reportProgress() {
|
||||
const total =
|
||||
0.1 * (otherProgress.reduce((a, b) => a + b) / otherProgress.length) +
|
||||
0.5 * esptoolProgress +
|
||||
0.4 * compressProgress;
|
||||
onProgress(total);
|
||||
}
|
||||
const esptool =
|
||||
os === "Windows"
|
||||
? progressFetch(`/esptool-v5.2.0-windows-amd64.zip`, (progress) => {
|
||||
esptoolProgress = progress;
|
||||
reportProgress();
|
||||
})
|
||||
: undefined;
|
||||
const files = Object.values(meta.update.esptool.files).map(
|
||||
(file, i) =>
|
||||
[
|
||||
file,
|
||||
progressFetch(`${meta.path}/${file}`, (progress) => {
|
||||
otherProgress[i] = progress;
|
||||
reportProgress();
|
||||
}),
|
||||
] as const,
|
||||
);
|
||||
console.log(files);
|
||||
const JSZip = await import("jszip").then((m) => m.default);
|
||||
const zip = new JSZip();
|
||||
const esptoolZipPromise = esptool
|
||||
? esptool.then((it) => JSZip.loadAsync(it))
|
||||
: undefined;
|
||||
for (const [file, dataPromise] of files) {
|
||||
zip.file(file, dataPromise);
|
||||
}
|
||||
|
||||
const esptoolScript = ".\\esptool\\esptool.exe";
|
||||
const port = "%1";
|
||||
const ext = os === "Windows" ? "bat" : "sh";
|
||||
zip.file(
|
||||
`flash.${ext}`,
|
||||
[
|
||||
esptoolScript,
|
||||
"--chip",
|
||||
meta.update.esptool.chip,
|
||||
"--port",
|
||||
port,
|
||||
"--baud",
|
||||
meta.update.esptool.baud,
|
||||
"--before",
|
||||
meta.update.esptool.before,
|
||||
"--after",
|
||||
meta.update.esptool.after,
|
||||
"write_flash",
|
||||
"-z",
|
||||
"--flash_mode",
|
||||
meta.update.esptool.flash_mode,
|
||||
"--flash_freq",
|
||||
meta.update.esptool.flash_freq,
|
||||
"--flash_size",
|
||||
meta.update.esptool.flash_size,
|
||||
...Object.entries(meta.update.esptool.files).flatMap(
|
||||
([address, file]) => [address, file],
|
||||
),
|
||||
].join(" "),
|
||||
{ unixPermissions: "755" },
|
||||
);
|
||||
zip.file(`${meta.device}-${meta.version}`, "");
|
||||
if (os === "Windows") {
|
||||
zip.file(
|
||||
"factory-flash.ps1",
|
||||
import("$lib/assets/factory-flash.ps1?raw").then((m) => m.default),
|
||||
);
|
||||
}
|
||||
zip.file(
|
||||
"README.txt",
|
||||
[
|
||||
`Factory Kit for ${meta.device} ${meta.version}`,
|
||||
"",
|
||||
os !== "Windows"
|
||||
? "Requires esptool, please download from https://github.com/espressif/esptool/releases"
|
||||
: "",
|
||||
os === "Windows"
|
||||
? 'Right click factory-flash.ps1 and select "Run with PowerShell'
|
||||
: `Run flash.sh with \`./flash.sh <port>\``,
|
||||
].join("\n"),
|
||||
);
|
||||
if (esptoolZipPromise) {
|
||||
const esptoolZip = await esptoolZipPromise;
|
||||
for (const [path, file] of Object.entries(esptoolZip.files)) {
|
||||
if (!file.dir) {
|
||||
zip.file(
|
||||
path.replace(/^esptool[^\/]*\//, "esptool/"),
|
||||
file.async("arraybuffer"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return zip.generateAsync({ type: "blob" }, ({ percent }) => {
|
||||
compressProgress = percent / 100;
|
||||
reportProgress();
|
||||
});
|
||||
}
|
||||
BIN
static/esptool-v5.2.0-windows-amd64.zip
Normal file
BIN
static/esptool-v5.2.0-windows-amd64.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user