From 3f8ccb1b865481ac7051b6ae2e46700adc02e599 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Tue, 10 Feb 2026 15:43:53 +0100 Subject: [PATCH 1/3] Claude slop impl --- src/lib/signalr/handlers/DeviceStatus.ts | 1 + src/lib/signalr/handlers/OtaInstallFailed.ts | 4 +- .../signalr/handlers/OtaInstallSucceeded.ts | 3 + src/lib/signalr/handlers/OtaRollback.ts | 4 +- src/lib/stores/HubsStore.ts | 1 + .../hubs/[hubId=guid]/update/+page.svelte | 346 ++++++++++++++---- 6 files changed, 290 insertions(+), 69 deletions(-) diff --git a/src/lib/signalr/handlers/DeviceStatus.ts b/src/lib/signalr/handlers/DeviceStatus.ts index 64270d3b..78dc9218 100644 --- a/src/lib/signalr/handlers/DeviceStatus.ts +++ b/src/lib/signalr/handlers/DeviceStatus.ts @@ -16,6 +16,7 @@ export function handleSignalrDeviceStatus(array: unknown) { isOnline: entry.online, firmwareVersion: entry.firmwareVersion, otaInstall: null, + otaResult: null, }); }); diff --git a/src/lib/signalr/handlers/OtaInstallFailed.ts b/src/lib/signalr/handlers/OtaInstallFailed.ts index aafd9692..2603bc34 100644 --- a/src/lib/signalr/handlers/OtaInstallFailed.ts +++ b/src/lib/signalr/handlers/OtaInstallFailed.ts @@ -21,8 +21,10 @@ export function handleSignalrOtaInstallFailed( const hub = hubs.get(hubId); if (hub && hub.otaInstall?.id === updateId) { hub.otaInstall = null; - //hub.otaError = { fatal, message }; + hub.otaResult = { success: false, message }; } return hubs; }); + + toast.error(`Hub firmware update failed: ${message}`); } diff --git a/src/lib/signalr/handlers/OtaInstallSucceeded.ts b/src/lib/signalr/handlers/OtaInstallSucceeded.ts index e58d8cc6..dcf869df 100644 --- a/src/lib/signalr/handlers/OtaInstallSucceeded.ts +++ b/src/lib/signalr/handlers/OtaInstallSucceeded.ts @@ -16,7 +16,10 @@ export function handleSignalrOtaInstallSucceeded(hubId: unknown, updateId: unkno const hub = hubs.get(hubId); if (hub && hub.otaInstall?.id === updateId) { hub.otaInstall = null; + hub.otaResult = { success: true, message: 'Update completed successfully' }; } return hubs; }); + + toast.success('Hub firmware update completed successfully!'); } diff --git a/src/lib/signalr/handlers/OtaRollback.ts b/src/lib/signalr/handlers/OtaRollback.ts index 64d9fa2d..6380e790 100644 --- a/src/lib/signalr/handlers/OtaRollback.ts +++ b/src/lib/signalr/handlers/OtaRollback.ts @@ -16,8 +16,10 @@ export function handleSignalrOtaRollback(hubId: unknown, updateId: unknown): voi const hub = hubs.get(hubId); if (hub && hub.otaInstall?.id === updateId) { hub.otaInstall = null; - //hub.otaError = { fatal: false, message: 'Rollback performed' }; + hub.otaResult = { success: false, message: 'Device rolled back to previous version' }; } return hubs; }); + + toast.warning('Hub firmware rolled back to previous version'); } diff --git a/src/lib/stores/HubsStore.ts b/src/lib/stores/HubsStore.ts index c4cfa75e..bc1caba5 100644 --- a/src/lib/stores/HubsStore.ts +++ b/src/lib/stores/HubsStore.ts @@ -15,6 +15,7 @@ export type HubOnlineState = { task: OtaUpdateProgressTask; progress: number; } | null; + otaResult: { success: boolean; message: string } | null; }; export const OwnHubsStore = writable>(new Map()); diff --git a/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte b/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte index 13c7b3f7..e17202af 100644 --- a/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte +++ b/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte @@ -1,28 +1,117 @@ - - - Update Hub + confirmOpen, (o) => (confirmOpen = o)}> + + + Confirm Firmware Update + + Update {hubName} to version {version}? + {#if version && !version.includes('stable')} +

+ This version may not be from the stable channel and could contain bugs. +

+ {/if} +
+
+ + + + +
+
+ + + + Update {hubName} + Manage firmware updates for this hub. - + {#if hub} -
+ +
+ Status: {#if hub.isOnline} -

Status: Online

+ Online {:else} -

Status: Offline

+ Offline + {/if} + {#if hub.firmwareVersion} + + Firmware: {hub.firmwareVersion} + {/if} -

Firmware Version: {hub.firmwareVersion}

- + + {#if hub.otaResult} +
+ {#if hub.otaResult.success} + +
+

{hub.otaResult.message}

+ +
+ {:else if hub.otaResult.message.includes('rolled back')} + +
+

{hub.otaResult.message}

+ +
+ {:else} + +
+

{hub.otaResult.message}

+ +
+ {/if} +
+ {/if} - + + {#if !isUpdating && !hub.otaResult} +
+
+

Firmware Channel

+ +
-

Progress

-
- Total - - Task - -
-

{hub.otaInstall?.task}

-

Flashing...

- -
-

Logs

- + + + {#if !hub.isOnline} +

Hub must be online to start an update.

+ {/if} +
+ {/if} + + + {#if isUpdating} +
+

+ Updating to {hub.otaInstall?.version}... +

+
+
+ Total + +
+
+ Task + +
+
+

{currentTaskLabel}

+
+ {/if} + + +
+
+

Update History

+ +
+ + {#if otaLogs.length === 0} +

+ No update history found for this hub. +

+ {:else} + + + + ID + Started + Status + Version + Message + + + + {#each otaLogs as otaLog (otaLog.id)} + + + {NumberToHexPadded(otaLog.id, 8)} + + + {formatRelativeTime(otaLog.startedAt)} + + + + {otaLog.status} + + + {otaLog.version} + + {otaLog.message ?? '-'} + + + {/each} + + + {/if}
- - - - ID - Started At - Status - Version - - - - {#each otaLogs as otaLog (otaLog.id)} - - - {NumberToHexPadded(otaLog.id, 8)} - - {otaLog.startedAt.toDateString()} - - {otaLog.status} - - {otaLog.version} - - {/each} - - + {:else if isLoading} +

Loading hub information...

+ {:else} +

Hub not found.

{/if} From 7985abaf750391d40eb1dfef21f672943361d1dd Mon Sep 17 00:00:00 2001 From: LucHeart Date: Wed, 4 Mar 2026 21:00:33 +0100 Subject: [PATCH 2/3] finish ota page --- src/lib/signalr/handlers/DeviceStatus.ts | 5 +- .../hubs/[hubId=guid]/update/+page.svelte | 117 +++++++++++++----- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/lib/signalr/handlers/DeviceStatus.ts b/src/lib/signalr/handlers/DeviceStatus.ts index 78dc9218..7e1cea13 100644 --- a/src/lib/signalr/handlers/DeviceStatus.ts +++ b/src/lib/signalr/handlers/DeviceStatus.ts @@ -11,12 +11,13 @@ export function handleSignalrDeviceStatus(array: unknown) { OnlineHubsStore.update((state) => { array.forEach((entry) => { + const existing = state.get(entry.device); state.set(entry.device, { hubId: entry.device, isOnline: entry.online, firmwareVersion: entry.firmwareVersion, - otaInstall: null, - otaResult: null, + otaInstall: existing?.otaInstall ?? null, + otaResult: existing?.otaResult ?? null, }); }); diff --git a/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte b/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte index e17202af..225958df 100644 --- a/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte +++ b/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte @@ -25,6 +25,7 @@ import { cn } from '$lib/utils'; import { NumberToHexPadded } from '$lib/utils/convert'; import { onMount } from 'svelte'; + import type { FirmwareChannel } from '$lib/api/firmwareCDN'; // Task weights for weighted total progress (7 tasks, sums to 100) const TASK_WEIGHTS = [4, 2, 22, 2, 49, 1, 20]; @@ -75,40 +76,106 @@ return 'just now'; } - let hub = $state(null); + let hubLoaded = $state(false); let otaLogs = $state([]); let version = $state(null); let confirmOpen = $state(false); + let channel = $state('stable'); + + let hub = $derived.by(() => { + if (!hubLoaded) return null; + const hubId = page.params.hubId ?? ''; + const h = $OnlineHubsStore.get(hubId); + if (!h) { + return { + hubId, + isOnline: false, + firmwareVersion: null, + otaInstall: null, + otaResult: null, + }; + } + // Spread to create a new reference so downstream deriveds re-evaluate on store updates + return { ...h }; + }); let hubName = $derived($OwnHubsStore.get(page.params.hubId ?? '')?.name ?? 'Unknown Hub'); let isUpdating = $derived(hub?.otaInstall !== null && hub?.otaInstall !== undefined); - let totalProgress = $derived( - hub?.otaInstall ? calcTotalProgress(hub.otaInstall.task, hub.otaInstall.progress) : 0 - ); + // Simulated reboot progress (last 20%) — hub goes offline so no more events + const REBOOT_DURATION_MS = 10_000; + const REBOOT_INTERVAL_MS = 100; + let rebootProgress = $state(0); + let rebootInterval: ReturnType | null = null; - let taskProgress = $derived((hub?.otaInstall?.progress ?? 0) * 100); + function stopRebootTimer() { + if (rebootInterval !== null) { + clearInterval(rebootInterval); + rebootInterval = null; + } + rebootProgress = 0; + } + + $effect(() => { + const isRebooting = hub?.otaInstall?.task === OtaUpdateProgressTask.Rebooting; + if (isRebooting && rebootInterval === null) { + rebootProgress = 0; + const step = REBOOT_INTERVAL_MS / REBOOT_DURATION_MS; + rebootInterval = setInterval(() => { + rebootProgress = Math.min(rebootProgress + step, 1); + if (rebootProgress >= 1 && rebootInterval !== null) { + clearInterval(rebootInterval); + rebootInterval = null; + } + }, REBOOT_INTERVAL_MS); + } else if (!isRebooting) { + stopRebootTimer(); + } + + return stopRebootTimer; + }); + + let totalProgress = $derived.by(() => { + if (!hub?.otaInstall) return 0; + if (hub.otaInstall.task === OtaUpdateProgressTask.Rebooting) { + return ( + TASK_OFFSETS[OtaUpdateProgressTask.Rebooting] + + rebootProgress * TASK_WEIGHTS[OtaUpdateProgressTask.Rebooting] + ); + } + return calcTotalProgress(hub.otaInstall.task, hub.otaInstall.progress); + }); + + let taskProgress = $derived.by(() => { + if (hub?.otaInstall?.task === OtaUpdateProgressTask.Rebooting) { + return rebootProgress * 100; + } + return (hub?.otaInstall?.progress ?? 0) * 100; + }); let currentTaskLabel = $derived( hub?.otaInstall ? (TASK_LABELS[hub.otaInstall.task] ?? 'Unknown Task') : '' ); function startUpdate() { - if ($SignalR_Connection === null || hub === null || version === null) return; + const hubId = page.params.hubId; + if ($SignalR_Connection === null || !hubId || hub === null || version === null) return; // Clear any previous result OnlineHubsStore.update((hubs) => { - const h = hubs.get(hub!.hubId); + const h = hubs.get(hubId); if (h) h.otaResult = null; return hubs; }); - serializeOtaInstallMessage($SignalR_Connection, hub.hubId, version); + serializeOtaInstallMessage($SignalR_Connection, hubId, version); confirmOpen = false; } function resetResult() { + const hubId = page.params.hubId; + if (!hubId) return; OnlineHubsStore.update((hubs) => { - const h = hubs.get(hub!.hubId); + const h = hubs.get(hubId); if (h) h.otaResult = null; return hubs; }); @@ -117,7 +184,7 @@ let isLoading = $state(false); function fetchOtaLogs(hubId: string | undefined) { if (hubId === undefined) { - hub = null; + hubLoaded = false; return; } @@ -126,34 +193,17 @@ .devicesOtaGetOtaUpdateHistory(hubId) .then((resp) => { if (resp.data === null) { - hub = null; + hubLoaded = false; return; } - hub = $OnlineHubsStore.get(hubId) ?? { - hubId, - isOnline: false, - firmwareVersion: null, - otaInstall: null, - otaResult: null, - }; + hubLoaded = true; otaLogs = resp.data; }) .catch(handleApiError) .finally(() => (isLoading = false)); } - // Keep hub state in sync with the store - $effect(() => { - const hubId = page.params.hubId; - if (hubId && hub) { - const storeHub = $OnlineHubsStore.get(hubId); - if (storeHub) { - hub = storeHub; - } - } - }); - $effect(() => fetchOtaLogs(page.params.hubId)); breadcrumbs.push('Hubs', '/hubs'); @@ -168,7 +218,7 @@ Confirm Firmware Update Update {hubName} to version {version}? - {#if version && !version.includes('stable')} + {#if channel !== 'stable'}

This version may not be from the stable channel and could contain bugs.

@@ -221,7 +271,10 @@

{hub.otaResult.message}

- +
+ + +
{:else if hub.otaResult.message.includes('rolled back')} @@ -244,7 +297,7 @@

Firmware Channel

- +