1 Commits

Author SHA1 Message Date
df3d8a16de update deployment 2024-09-29 18:33:15 +02:00
8 changed files with 61 additions and 209 deletions

View File

@@ -41,10 +41,16 @@ jobs:
echo "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
- 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/
- name: Publish Branch
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${GITHUB_REF##*/}
- name: Publish Dev
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
run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${{ github.sha }}

View File

@@ -440,80 +440,46 @@ export class CharaDevice {
return Number(await this.send(1, "RAM").then(([bytes]) => bytes));
}
async updateFirmware(file: File | Blob): Promise<void> {
while (this.lock) {
await this.lock;
}
let resolveLock: (result: true) => void;
this.lock = new Promise<true>((resolve) => {
resolveLock = resolve;
async updateFirmware(file: File): Promise<void> {
const size = file.size;
// use separate serial connection
await this.port.open({ baudRate: this.baudRate });
const decoderStream = new TextDecoderStream();
this.port.readable!.pipeTo(decoderStream.writable);
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 {
if (this.suspendDebounceId) {
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);
}
await writer.write(new TextEncoder().encode(`RST OTA\r\n`));
} finally {
delete this.lock;
resolveLock!(true);
writer.releaseLock();
}
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;
});
}
}

View 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;

View File

@@ -1,14 +1,11 @@
import type { LayoutLoad } from "./$types";
import { browser } from "$app/environment";
import { charaFileFromUriComponent } from "$lib/share/share-url";
import { themeBase, themeColor, themeSuccessBase } from "$lib/style/theme";
export const load = (async ({ url, data, fetch }) => {
const importFile = browser && new URLSearchParams(url.search).get("import");
return {
themeSuccessBase,
themeBase,
themeColor,
...data,
importFile: importFile
? await charaFileFromUriComponent(importFile, fetch)
: undefined,

View File

@@ -4,29 +4,6 @@
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(
$serialPort
? `${$serialPort.device.toLowerCase()}_${$serialPort.chipset.toLowerCase()}`
@@ -101,34 +78,7 @@
<section>
<h3>OTA Upate</h3>
{#if data.ota}
{@const buttonError = error || (!success && isCorrectDevice === false)}
<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}
<p>OTA update</p>
{:else}
<em>There are no OTA files for this device.</em>
{/if}
@@ -151,7 +101,6 @@
<h4>Via Serial</h4>
<p>WIP</p>
</section>
ading 0 Chordmaps.
</div>
<style lang="scss">
@@ -160,84 +109,6 @@
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 {
color: var(--md-sys-color-outline);
margin-block: 3em;

View File

@@ -1,5 +1,5 @@
// noinspection ES6PreferShortImport
import { themeColor } from "./src/lib/style/theme";
import { themeColor } from "./src/lib/style/theme.server";
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { SvelteKitPWA } from "@vite-pwa/sveltekit";