mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-22 18:02:42 +00:00
Compare commits
1 Commits
1f4604bcbc
...
df3d8a16de
| Author | SHA1 | Date | |
|---|---|---|---|
|
df3d8a16de
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -41,10 +41,16 @@ jobs:
|
|||||||
echo "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
echo "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Publish Stable
|
- name: Publish Stable
|
||||||
if: ${{ github.ref == 'refs/heads/v*' }}
|
if: ${{ github.ref == 'ref/head/v*' }}
|
||||||
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/www/
|
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/www/
|
||||||
|
|
||||||
- name: Publish Branch
|
- name: Publish Dev
|
||||||
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${GITHUB_REF##*/}
|
if: ${{ github.ref == 'ref/head/main' }}
|
||||||
|
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/dev/
|
||||||
|
|
||||||
|
- name: Publish Tag
|
||||||
|
if: ${{ github.ref == 'ref/head/v*' }}
|
||||||
|
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${GITHUB_REF##*/v}
|
||||||
|
|
||||||
- name: Publish Commit
|
- name: Publish Commit
|
||||||
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${{ github.sha }}
|
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${{ github.sha }}
|
||||||
|
|||||||
@@ -440,80 +440,46 @@ export class CharaDevice {
|
|||||||
return Number(await this.send(1, "RAM").then(([bytes]) => bytes));
|
return Number(await this.send(1, "RAM").then(([bytes]) => bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFirmware(file: File | Blob): Promise<void> {
|
async updateFirmware(file: File): Promise<void> {
|
||||||
while (this.lock) {
|
const size = file.size;
|
||||||
await this.lock;
|
// use separate serial connection
|
||||||
}
|
await this.port.open({ baudRate: this.baudRate });
|
||||||
let resolveLock: (result: true) => void;
|
const decoderStream = new TextDecoderStream();
|
||||||
this.lock = new Promise<true>((resolve) => {
|
this.port.readable!.pipeTo(decoderStream.writable);
|
||||||
resolveLock = resolve;
|
|
||||||
|
const reader = decoderStream
|
||||||
|
.readable!.pipeThrough(new TransformStream(new LineBreakTransformer()))
|
||||||
|
.getReader();
|
||||||
|
serialLog.update((it) => {
|
||||||
|
it.push({
|
||||||
|
type: "system",
|
||||||
|
value: "Starting firmware update",
|
||||||
|
});
|
||||||
|
return it;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const writer = this.port.writable!.getWriter();
|
||||||
try {
|
try {
|
||||||
if (this.suspendDebounceId) {
|
await writer.write(new TextEncoder().encode(`RST OTA\r\n`));
|
||||||
clearTimeout(this.suspendDebounceId);
|
|
||||||
} else {
|
|
||||||
await this.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
serialLog.update((it) => {
|
|
||||||
it.push({
|
|
||||||
type: "system",
|
|
||||||
value: "OTA Update",
|
|
||||||
});
|
|
||||||
return it;
|
|
||||||
});
|
|
||||||
|
|
||||||
const writer = this.port.writable!.getWriter();
|
|
||||||
try {
|
|
||||||
await writer.write(new TextEncoder().encode(`RST OTA\r\n`));
|
|
||||||
serialLog.update((it) => {
|
|
||||||
it.push({
|
|
||||||
type: "input",
|
|
||||||
value: "RST OTA",
|
|
||||||
});
|
|
||||||
return it;
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
writer.releaseLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the device to be ready
|
|
||||||
const signal = await this.reader.read();
|
|
||||||
serialLog.update((it) => {
|
|
||||||
it.push({
|
|
||||||
type: "output",
|
|
||||||
value: signal.value!.trim(),
|
|
||||||
});
|
|
||||||
return it;
|
|
||||||
});
|
|
||||||
|
|
||||||
await file.stream().pipeTo(this.port.writable!);
|
|
||||||
|
|
||||||
serialLog.update((it) => {
|
|
||||||
it.push({
|
|
||||||
type: "input",
|
|
||||||
value: `...${file.size} bytes`,
|
|
||||||
});
|
|
||||||
return it;
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = (await this.reader.read()).value!.trim();
|
|
||||||
serialLog.update((it) => {
|
|
||||||
it.push({
|
|
||||||
type: "output",
|
|
||||||
value: result!,
|
|
||||||
});
|
|
||||||
return it;
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.suspend();
|
|
||||||
|
|
||||||
if (result !== "OTA OK") {
|
|
||||||
throw new Error(result);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
delete this.lock;
|
writer.releaseLock();
|
||||||
resolveLock!(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log((await reader.read()).value);
|
||||||
|
|
||||||
|
await file.stream().pipeTo(this.port.writable!);
|
||||||
|
|
||||||
|
console.log((await reader.read()).value);
|
||||||
|
|
||||||
|
await reader.cancel();
|
||||||
|
reader.releaseLock();
|
||||||
|
await this.port.close();
|
||||||
|
serialLog.update((it) => {
|
||||||
|
it.push({
|
||||||
|
type: "system",
|
||||||
|
value: "Success?",
|
||||||
|
});
|
||||||
|
return it;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/routes/(app)/+layout.server.ts
Normal file
12
src/routes/(app)/+layout.server.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
themeBase,
|
||||||
|
themeColor,
|
||||||
|
themeSuccessBase,
|
||||||
|
} from "$lib/style/theme.server";
|
||||||
|
import type { LayoutServerLoad } from "./$types";
|
||||||
|
|
||||||
|
export const load = (async () => ({
|
||||||
|
themeSuccessBase,
|
||||||
|
themeBase,
|
||||||
|
themeColor,
|
||||||
|
})) satisfies LayoutServerLoad;
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
import type { LayoutLoad } from "./$types";
|
import type { LayoutLoad } from "./$types";
|
||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import { charaFileFromUriComponent } from "$lib/share/share-url";
|
import { charaFileFromUriComponent } from "$lib/share/share-url";
|
||||||
import { themeBase, themeColor, themeSuccessBase } from "$lib/style/theme";
|
|
||||||
|
|
||||||
export const load = (async ({ url, data, fetch }) => {
|
export const load = (async ({ url, data, fetch }) => {
|
||||||
const importFile = browser && new URLSearchParams(url.search).get("import");
|
const importFile = browser && new URLSearchParams(url.search).get("import");
|
||||||
return {
|
return {
|
||||||
themeSuccessBase,
|
...data,
|
||||||
themeBase,
|
|
||||||
themeColor,
|
|
||||||
importFile: importFile
|
importFile: importFile
|
||||||
? await charaFileFromUriComponent(importFile, fetch)
|
? await charaFileFromUriComponent(importFile, fetch)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|||||||
@@ -4,29 +4,6 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
let working = $state(false);
|
|
||||||
let success = $state(false);
|
|
||||||
let error = $state<Error | undefined>(undefined);
|
|
||||||
|
|
||||||
async function update() {
|
|
||||||
working = true;
|
|
||||||
error = undefined;
|
|
||||||
success = false;
|
|
||||||
const port = $serialPort!;
|
|
||||||
$serialPort = undefined;
|
|
||||||
try {
|
|
||||||
const file = await fetch(otaUrl!).then((it) => it.blob());
|
|
||||||
|
|
||||||
await port.updateFirmware(file);
|
|
||||||
|
|
||||||
success = true;
|
|
||||||
} catch (e) {
|
|
||||||
error = e as Error;
|
|
||||||
} finally {
|
|
||||||
working = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentDevice = $derived(
|
let currentDevice = $derived(
|
||||||
$serialPort
|
$serialPort
|
||||||
? `${$serialPort.device.toLowerCase()}_${$serialPort.chipset.toLowerCase()}`
|
? `${$serialPort.device.toLowerCase()}_${$serialPort.chipset.toLowerCase()}`
|
||||||
@@ -101,34 +78,7 @@
|
|||||||
<section>
|
<section>
|
||||||
<h3>OTA Upate</h3>
|
<h3>OTA Upate</h3>
|
||||||
{#if data.ota}
|
{#if data.ota}
|
||||||
{@const buttonError = error || (!success && isCorrectDevice === false)}
|
<p>OTA update</p>
|
||||||
<button
|
|
||||||
class:working
|
|
||||||
class:primary={!buttonError}
|
|
||||||
class:error={buttonError}
|
|
||||||
disabled={working || $serialPort === undefined || !isCorrectDevice}
|
|
||||||
onclick={update}>Apply Update</button
|
|
||||||
>
|
|
||||||
{#if $serialPort && isCorrectDevice}
|
|
||||||
<div transition:slide>
|
|
||||||
Your device is ready and compatible. Click the button to perform the
|
|
||||||
update.
|
|
||||||
</div>
|
|
||||||
{:else if $serialPort && isCorrectDevice === false}
|
|
||||||
<div class="error" transition:slide>
|
|
||||||
Your device is incompatible with the selected update.
|
|
||||||
</div>
|
|
||||||
{:else if success}
|
|
||||||
<div class="primary" transition:slide>Update successful</div>
|
|
||||||
{:else if error}
|
|
||||||
<div class="error" transition:slide>{error.message}</div>
|
|
||||||
{:else if working}
|
|
||||||
<div class="primary" transition:slide>Updating your device...</div>
|
|
||||||
{:else}
|
|
||||||
<div class="primary" transition:slide>
|
|
||||||
Connect your device to continue
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
<em>There are no OTA files for this device.</em>
|
<em>There are no OTA files for this device.</em>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -151,7 +101,6 @@
|
|||||||
<h4>Via Serial</h4>
|
<h4>Via Serial</h4>
|
||||||
<p>WIP</p>
|
<p>WIP</p>
|
||||||
</section>
|
</section>
|
||||||
ading 0 Chordmaps.
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -160,84 +109,6 @@
|
|||||||
transition: color 200ms ease;
|
transition: color 200ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary {
|
|
||||||
color: var(--md-sys-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: var(--md-sys-color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotate {
|
|
||||||
0% {
|
|
||||||
transform: rotate(120deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
20% {
|
|
||||||
transform: rotate(120deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
60% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(270deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
height: 42px;
|
|
||||||
|
|
||||||
border: 2px solid currentcolor;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
outline: 2px dashed currentcolor;
|
|
||||||
outline-offset: 4px;
|
|
||||||
|
|
||||||
background: var(--md-sys-color-background);
|
|
||||||
transition:
|
|
||||||
border 200ms ease,
|
|
||||||
color 200ms ease;
|
|
||||||
|
|
||||||
margin: 6px;
|
|
||||||
margin-block: 16px;
|
|
||||||
|
|
||||||
&.primary {
|
|
||||||
color: var(--md-sys-color-primary);
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.working {
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.working::before {
|
|
||||||
z-index: -1;
|
|
||||||
position: absolute;
|
|
||||||
background: var(--md-sys-color-background);
|
|
||||||
width: calc(100% - 4px);
|
|
||||||
height: calc(100% - 4px);
|
|
||||||
border-radius: 8px;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
&.working::after {
|
|
||||||
z-index: -2;
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
background: var(--md-sys-color-primary);
|
|
||||||
animation: rotate 1s ease-out forwards infinite;
|
|
||||||
height: 30%;
|
|
||||||
width: 120%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
color: var(--md-sys-color-outline);
|
color: var(--md-sys-color-outline);
|
||||||
margin-block: 3em;
|
margin-block: 3em;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// noinspection ES6PreferShortImport
|
// noinspection ES6PreferShortImport
|
||||||
import { themeColor } from "./src/lib/style/theme";
|
import { themeColor } from "./src/lib/style/theme.server";
|
||||||
import { sveltekit } from "@sveltejs/kit/vite";
|
import { sveltekit } from "@sveltejs/kit/vite";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { SvelteKitPWA } from "@vite-pwa/sveltekit";
|
import { SvelteKitPWA } from "@vite-pwa/sveltekit";
|
||||||
|
|||||||
Reference in New Issue
Block a user