mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-02-24 01:52:04 +00:00
feat: support factory flashing
This commit is contained in:
@@ -110,6 +110,9 @@ const config = {
|
|||||||
"experiment",
|
"experiment",
|
||||||
"code",
|
"code",
|
||||||
"dictionary",
|
"dictionary",
|
||||||
|
"developer_board",
|
||||||
|
"developer_board_off",
|
||||||
|
"memory",
|
||||||
],
|
],
|
||||||
codePoints: {
|
codePoints: {
|
||||||
speed: "e9e4",
|
speed: "e9e4",
|
||||||
|
|||||||
@@ -62,6 +62,7 @@
|
|||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"cypress": "^13.13.2",
|
"cypress": "^13.13.2",
|
||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
|
"esptool-js": "^0.4.7",
|
||||||
"flexsearch": "^0.7.43",
|
"flexsearch": "^0.7.43",
|
||||||
"fontkit": "^2.0.4",
|
"fontkit": "^2.0.4",
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
|
|||||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -92,6 +92,9 @@ importers:
|
|||||||
d3:
|
d3:
|
||||||
specifier: ^7.9.0
|
specifier: ^7.9.0
|
||||||
version: 7.9.0
|
version: 7.9.0
|
||||||
|
esptool-js:
|
||||||
|
specifier: ^0.4.7
|
||||||
|
version: 0.4.7
|
||||||
flexsearch:
|
flexsearch:
|
||||||
specifier: ^0.7.43
|
specifier: ^0.7.43
|
||||||
version: 0.7.43
|
version: 0.7.43
|
||||||
@@ -1648,6 +1651,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|
||||||
|
atob-lite@2.0.0:
|
||||||
|
resolution: {integrity: sha512-LEeSAWeh2Gfa2FtlQE1shxQ8zi5F9GHarrGKz08TMdODD5T4eH6BMsvtnhbWZ+XQn+Gb6om/917ucvRu7l7ukw==}
|
||||||
|
|
||||||
autoprefixer@10.4.20:
|
autoprefixer@10.4.20:
|
||||||
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
|
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
@@ -2253,6 +2259,9 @@ packages:
|
|||||||
esm-env@1.0.0:
|
esm-env@1.0.0:
|
||||||
resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==}
|
resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==}
|
||||||
|
|
||||||
|
esptool-js@0.4.7:
|
||||||
|
resolution: {integrity: sha512-xVwtSVDRsvjXSEvNFrorgJfB71RFFkZkL+hs7O7gW5hgPrKGywZxo2U5LJddzkJ6eE31QinNVyywc0OaSntZCw==}
|
||||||
|
|
||||||
esrap@1.2.2:
|
esrap@1.2.2:
|
||||||
resolution: {integrity: sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==}
|
resolution: {integrity: sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==}
|
||||||
|
|
||||||
@@ -3087,6 +3096,9 @@ packages:
|
|||||||
pako@0.2.9:
|
pako@0.2.9:
|
||||||
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
|
||||||
|
|
||||||
|
pako@2.1.0:
|
||||||
|
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -5802,6 +5814,8 @@ snapshots:
|
|||||||
|
|
||||||
at-least-node@1.0.0: {}
|
at-least-node@1.0.0: {}
|
||||||
|
|
||||||
|
atob-lite@2.0.0: {}
|
||||||
|
|
||||||
autoprefixer@10.4.20(postcss@8.4.39):
|
autoprefixer@10.4.20(postcss@8.4.39):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.24.2
|
browserslist: 4.24.2
|
||||||
@@ -6525,6 +6539,12 @@ snapshots:
|
|||||||
|
|
||||||
esm-env@1.0.0: {}
|
esm-env@1.0.0: {}
|
||||||
|
|
||||||
|
esptool-js@0.4.7:
|
||||||
|
dependencies:
|
||||||
|
atob-lite: 2.0.0
|
||||||
|
pako: 2.1.0
|
||||||
|
tslib: 2.6.3
|
||||||
|
|
||||||
esrap@1.2.2:
|
esrap@1.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/sourcemap-codec': 1.5.0
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
@@ -7342,6 +7362,8 @@ snapshots:
|
|||||||
|
|
||||||
pako@0.2.9: {}
|
pako@0.2.9: {}
|
||||||
|
|
||||||
|
pako@2.1.0: {}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { downloadBackup } from "$lib/backup/backup";
|
import { downloadBackup } from "$lib/backup/backup";
|
||||||
import { initSerial, serialPort } from "$lib/serial/connection";
|
import { initSerial, serialPort } from "$lib/serial/connection";
|
||||||
import { fade, slide } from "svelte/transition";
|
import { fade, slide } from "svelte/transition";
|
||||||
|
import type { LoaderOptions, ESPLoader } from "esptool-js";
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
@@ -9,7 +10,12 @@
|
|||||||
let success = $state(false);
|
let success = $state(false);
|
||||||
let error = $state<Error | undefined>(undefined);
|
let error = $state<Error | undefined>(undefined);
|
||||||
|
|
||||||
|
let terminalOutput = $state("");
|
||||||
|
|
||||||
let step = $state(0);
|
let step = $state(0);
|
||||||
|
let eraseAll = $state(false);
|
||||||
|
|
||||||
|
let espLoader;
|
||||||
|
|
||||||
async function update() {
|
async function update() {
|
||||||
working = true;
|
working = true;
|
||||||
@@ -18,7 +24,9 @@
|
|||||||
const port = $serialPort!;
|
const port = $serialPort!;
|
||||||
$serialPort = undefined;
|
$serialPort = undefined;
|
||||||
try {
|
try {
|
||||||
const file = await fetch(otaUrl!).then((it) => it.blob());
|
const file = await fetch(
|
||||||
|
`${data.meta.path}/${data.meta.update.ota!}`,
|
||||||
|
).then((it) => it.blob());
|
||||||
|
|
||||||
await port.updateFirmware(file);
|
await port.updateFirmware(file);
|
||||||
|
|
||||||
@@ -36,18 +44,7 @@
|
|||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
let isCorrectDevice = $derived(
|
let isCorrectDevice = $derived(
|
||||||
currentDevice ? currentDevice === data.device : undefined,
|
currentDevice ? currentDevice === data.meta.target : undefined,
|
||||||
);
|
|
||||||
|
|
||||||
let uf2Url = $derived(
|
|
||||||
data.uf2
|
|
||||||
? `${import.meta.env.VITE_FIRMWARE_URL}${data.device}/${data.version}/${data.uf2.name}`
|
|
||||||
: undefined,
|
|
||||||
);
|
|
||||||
let otaUrl = $derived(
|
|
||||||
data.ota
|
|
||||||
? `${import.meta.env.VITE_FIRMWARE_URL}${data.device}/${data.version}/${data.ota.name}`
|
|
||||||
: undefined,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,10 +81,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getFileSystem() {
|
async function getFileSystem() {
|
||||||
if (!uf2Url) return;
|
if (!data.meta.update.uf2) return;
|
||||||
const uf2Promise = fetch(uf2Url).then((it) => it.blob());
|
const uf2Promise = fetch(
|
||||||
|
`${data.meta.path}/${data.meta.update.uf2.name}`,
|
||||||
|
).then((it) => it.blob());
|
||||||
const handle = await window.showSaveFilePicker({
|
const handle = await window.showSaveFilePicker({
|
||||||
id: `${data.device}-update`,
|
id: `${data.meta.target}-update`,
|
||||||
suggestedName: "CURRENT.UF2",
|
suggestedName: "CURRENT.UF2",
|
||||||
excludeAcceptAllOption: true,
|
excludeAcceptAllOption: true,
|
||||||
types: [
|
types: [
|
||||||
@@ -102,21 +101,82 @@
|
|||||||
await uf2.stream().pipeTo(writable);
|
await uf2.stream().pipeTo(writable);
|
||||||
step = 4;
|
step = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function espBootloader() {
|
||||||
|
$serialPort?.forget();
|
||||||
|
const port = await navigator.serial.requestPort();
|
||||||
|
port.open({ baudRate: 1200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectEsp(port: SerialPort): Promise<ESPLoader> {
|
||||||
|
const esptool = data.meta.update.esptool!;
|
||||||
|
const { Transport, ESPLoader } = await import("esptool-js");
|
||||||
|
const espLoader = new ESPLoader({
|
||||||
|
transport: new Transport(port),
|
||||||
|
baudrate: Number(esptool.baud),
|
||||||
|
romBaudrate: Number(esptool.baud),
|
||||||
|
terminal: {
|
||||||
|
clean: () => {
|
||||||
|
terminalOutput = "";
|
||||||
|
},
|
||||||
|
writeLine: (data) => {
|
||||||
|
terminalOutput += data + "\n";
|
||||||
|
},
|
||||||
|
write: (data) => {
|
||||||
|
terminalOutput += data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies LoaderOptions);
|
||||||
|
await espLoader.connect(esptool.before);
|
||||||
|
await espLoader.runStub();
|
||||||
|
|
||||||
|
return espLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function flashImages() {
|
||||||
|
const port = await navigator.serial.requestPort();
|
||||||
|
try {
|
||||||
|
const esptool = data.meta.update.esptool!;
|
||||||
|
espLoader = await connectEsp(port);
|
||||||
|
const fileArray = await Promise.all(
|
||||||
|
Object.entries(esptool.files).map(([offset, name]) =>
|
||||||
|
fetch(`${data.meta.path}/${name}`)
|
||||||
|
.then((it) => it.blob())
|
||||||
|
.then((it) => it.text())
|
||||||
|
.then((it) => ({
|
||||||
|
address: Number(offset),
|
||||||
|
data: it,
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await espLoader.writeFlash({
|
||||||
|
flashSize: esptool.flash_size,
|
||||||
|
flashMode: esptool.flash_mode,
|
||||||
|
flashFreq: esptool.flash_freq,
|
||||||
|
compress: true,
|
||||||
|
eraseAll,
|
||||||
|
fileArray,
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
port.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>
|
<h2>
|
||||||
<a class="inline-link" href="/ccos">CCOS</a> /
|
<a class="inline-link" href="/ccos">CCOS</a> /
|
||||||
<a
|
<a
|
||||||
href="/ccos/{data.device}"
|
href="/ccos/{data.meta.target}"
|
||||||
class="device inline-link"
|
class="device inline-link"
|
||||||
class:correct-device={isCorrectDevice === true}
|
class:correct-device={isCorrectDevice === true}
|
||||||
class:incorrect-device={isCorrectDevice === false}>{data.device}</a
|
class:incorrect-device={isCorrectDevice === false}>{data.meta.target}</a
|
||||||
>
|
>
|
||||||
/ <em class="version">{data.version}</em>
|
/ <em class="version">{data.meta.version}</em>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{#if data.ota && !data.device.endsWith("m0")}
|
{#if data.meta.update.ota && !data.meta.target.endsWith("m0")}
|
||||||
{@const buttonError = error || (!success && isCorrectDevice === false)}
|
{@const buttonError = error || (!success && isCorrectDevice === false)}
|
||||||
<section>
|
<section>
|
||||||
<button
|
<button
|
||||||
@@ -136,7 +196,7 @@
|
|||||||
{$serialPort.chipset}</b
|
{$serialPort.chipset}</b
|
||||||
>
|
>
|
||||||
will be updated from <b class="version">{$serialPort.version}</b> to
|
will be updated from <b class="version">{$serialPort.version}</b> to
|
||||||
<b class="version">{data.version}</b>
|
<b class="version">{data.meta.version}</b>
|
||||||
</div>
|
</div>
|
||||||
{:else if $serialPort && isCorrectDevice === false}
|
{:else if $serialPort && isCorrectDevice === false}
|
||||||
<div class="error" transition:slide>
|
<div class="error" transition:slide>
|
||||||
@@ -158,27 +218,6 @@
|
|||||||
<h3>Manual Update</h3>
|
<h3>Manual Update</h3>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<ul class="files">
|
|
||||||
{#if data.uf2}
|
|
||||||
<li>
|
|
||||||
<a target="_blank" download href={uf2Url}
|
|
||||||
>{data.uf2.name} <span class="icon">download</span><span class="size"
|
|
||||||
>{toByteUnit(data.uf2.size)}</span
|
|
||||||
></a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
{#if data.ota}
|
|
||||||
<li>
|
|
||||||
<a target="_blank" download href={otaUrl}
|
|
||||||
>{data.ota.name} <span class="icon">download</span><span class="size"
|
|
||||||
>{toByteUnit(data.uf2.size)}</span
|
|
||||||
></a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{#if isCorrectDevice === false}
|
{#if isCorrectDevice === false}
|
||||||
<div transition:slide class="incorrect-device">
|
<div transition:slide class="incorrect-device">
|
||||||
These files are incompatible with your device
|
These files are incompatible with your device
|
||||||
@@ -186,7 +225,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h4>UF2 Instructions</h4>
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<button class="inline-button" onclick={connect}
|
<button class="inline-button" onclick={connect}
|
||||||
@@ -227,6 +265,34 @@
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{#if data.meta.update.esptool}
|
||||||
|
<section>
|
||||||
|
<h3>Factory Flash</h3>
|
||||||
|
<p>
|
||||||
|
If everything else fails, you can go through the same process that is
|
||||||
|
being used in the factory.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This will temporarily brick your device if the process is not done
|
||||||
|
completely or incorrectly.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="esp-buttons">
|
||||||
|
<button onclick={espBootloader}
|
||||||
|
><span class="icon">memory</span>ESP Bootloader</button
|
||||||
|
>
|
||||||
|
<button onclick={flashImages}
|
||||||
|
><span class="icon">developer_board</span>Flash Images</button
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
><input type="checkbox" id="erase" bind:checked={eraseAll} />Erase All</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre>{terminalOutput}</pre>
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -239,6 +305,10 @@
|
|||||||
margin-block-start: 4em;
|
margin-block-start: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.primary {
|
.primary {
|
||||||
color: var(--md-sys-color-primary);
|
color: var(--md-sys-color-primary);
|
||||||
}
|
}
|
||||||
@@ -249,6 +319,7 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
width: calc(min(100%, 16cm));
|
width: calc(min(100%, 16cm));
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes rotate {
|
@keyframes rotate {
|
||||||
@@ -402,4 +473,8 @@
|
|||||||
.incorrect-device {
|
.incorrect-device {
|
||||||
color: var(--md-sys-color-error);
|
color: var(--md-sys-color-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.esp-buttons {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,20 +1,49 @@
|
|||||||
import type { PageLoad } from "./$types";
|
import type { PageLoad } from "./$types";
|
||||||
import type { FileListing, Listing } from "../../listing";
|
import type { FileListing, Listing } from "../../listing";
|
||||||
|
import type { VersionMeta } from "./meta";
|
||||||
|
|
||||||
export const load = (async ({ fetch, params }) => {
|
export const load = (async ({ fetch, params }) => {
|
||||||
const result = await fetch(
|
const result = await fetch(
|
||||||
`${import.meta.env.VITE_FIRMWARE_URL}/${params.device}/${params.version}/`,
|
`${import.meta.env.VITE_FIRMWARE_URL}/${params.device}/${params.version}/`,
|
||||||
);
|
);
|
||||||
const data: Listing[] = await result.json();
|
const data: Listing[] = await result.json();
|
||||||
|
const meta: VersionMeta | undefined = data.some(
|
||||||
|
(entry) => entry.type === "file" && entry.name === "meta.json",
|
||||||
|
)
|
||||||
|
? await fetch(
|
||||||
|
`${import.meta.env.VITE_FIRMWARE_URL}/${params.device}/${params.version}/meta.json`,
|
||||||
|
).then((res) => res.json())
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uf2: data.find(
|
meta: {
|
||||||
(entry) => entry.type === "file" && entry.name === "CURRENT.UF2",
|
version: meta?.version ?? params.version,
|
||||||
) as FileListing,
|
target: meta?.target ?? params.device,
|
||||||
ota: data.find(
|
path: `${import.meta.env.VITE_FIRMWARE_URL}${params.device}/${params.version}`,
|
||||||
(entry) => entry.type === "file" && entry.name === "firmware.bin",
|
git_commit: meta?.git_commit ?? "",
|
||||||
),
|
git_is_dirty: meta?.git_is_dirty ?? false,
|
||||||
version: params.version,
|
git_date: meta?.git_date ?? data[0]?.mtime ?? "",
|
||||||
device: params.device,
|
public_build: meta?.public_build ?? !params.version.startsWith("."),
|
||||||
|
development_mode: meta?.development_mode ?? 0,
|
||||||
|
update: {
|
||||||
|
uf2:
|
||||||
|
(data.find(
|
||||||
|
(entry) =>
|
||||||
|
entry.type === "file" &&
|
||||||
|
entry.name === (meta?.update?.uf2 ?? "CURRENT.UF2"),
|
||||||
|
) as FileListing) ?? undefined,
|
||||||
|
ota:
|
||||||
|
data.find(
|
||||||
|
(entry) =>
|
||||||
|
entry.type === "file" &&
|
||||||
|
entry.name === (meta?.update?.ota ?? "firmware.bin"),
|
||||||
|
) ?? undefined,
|
||||||
|
esptool: meta?.update?.esptool ?? undefined,
|
||||||
|
},
|
||||||
|
files: data.filter(
|
||||||
|
(entry) =>
|
||||||
|
entry.type === "file" && (!meta?.files || entry.name in meta.files),
|
||||||
|
) as FileListing[],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}) satisfies PageLoad;
|
}) satisfies PageLoad;
|
||||||
|
|||||||
26
src/routes/(app)/ccos/[device]/[version]/meta.ts
Normal file
26
src/routes/(app)/ccos/[device]/[version]/meta.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export interface VersionMeta {
|
||||||
|
version: string;
|
||||||
|
target: string;
|
||||||
|
git_commit: string;
|
||||||
|
git_is_dirty: boolean;
|
||||||
|
git_date: string;
|
||||||
|
public_build: boolean;
|
||||||
|
development_mode: number;
|
||||||
|
update: {
|
||||||
|
ota: string | null;
|
||||||
|
uf2: string | null;
|
||||||
|
esptool: EspToolData | null;
|
||||||
|
};
|
||||||
|
files: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EspToolData {
|
||||||
|
chip: string;
|
||||||
|
baud: string;
|
||||||
|
before: string;
|
||||||
|
after: string;
|
||||||
|
flash_mode: string;
|
||||||
|
flash_freq: string;
|
||||||
|
flash_size: string;
|
||||||
|
files: Record<string, string>;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user