From 825e3d3560447582f6a371c68d10f8086d203408 Mon Sep 17 00:00:00 2001 From: Kevin Cantrell Date: Thu, 30 Apr 2026 01:03:19 +0900 Subject: [PATCH] moving to better timeer updates --- .../dashboard/DashboardDeviceCards.svelte | 42 +++++++++++++++---- .../dashboard/dashboard-device-data.ts | 9 ++++ src/lib/components/dashboard/device-cards.ts | 1 + src/lib/devices/relay-control.ts | 2 +- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/lib/components/dashboard/DashboardDeviceCards.svelte b/src/lib/components/dashboard/DashboardDeviceCards.svelte index c45ef056..09ebeeba 100644 --- a/src/lib/components/dashboard/DashboardDeviceCards.svelte +++ b/src/lib/components/dashboard/DashboardDeviceCards.svelte @@ -142,12 +142,15 @@ }); } - // On mount and whenever locationCards or the access token become available, - // check localStorage for any cards that were already expanded and pre-fetch their data. + // Pre-fetch data for any cards that were already expanded in localStorage. + // Scoped to visibleLocationCards so off-screen cards don't fan out fetches at + // page load — as the user scrolls and visibleLocationCount grows, this effect + // re-runs and picks up the newly-visible expanded cards. Already-fetched ones + // are dedup'd via the marker in expandedDetailRowsByDevEui. $effect(() => { - if (!app.accessToken || locationCards.length === 0) return; + if (!app.accessToken || visibleLocationCards.length === 0) return; - for (const card of locationCards) { + for (const card of visibleLocationCards) { for (const sensor of card.sensors) { if (isSensorExpandedInStorage(card, sensor)) { void handleSensorExpand(sensor); @@ -436,8 +439,12 @@ const freshData = await fetchAndMergeLatestDeviceData(devEui); if (!freshData) { - // Remove loading marker so the user can retry by collapsing and re-expanding - delete expandedDetailRowsByDevEui[devEui]; + // Leave the marker in place. Deleting it would let the auto-expand $effect + // (which re-runs whenever locationCards re-derives — every 30s from reactiveNow, + // plus on every app.devices mutation) re-fire handleSensorExpand on every tick, + // hammering the API with retries that compound when the server returns 429s. + // The marker stays null (showing "Loading…" rows); the periodic 10-min refresh + // uses a different endpoint and will replace these rows when it next fires. return; } @@ -445,13 +452,33 @@ const liveDevice = resolveLiveDashboardDevice(sensor.sourceDevice) ?? sensor.sourceDevice; const typeConfig = resolveDeviceTypeConfig(liveDevice, app.deviceTypeLookup); + // /latest-data can return an older created_at than what's already in app.devices + // (the two endpoints query different tables for relay-style devices). The merge + // in fetchAndMergeLatestDeviceData prefers the newer one, so use the live device's + // created_at for the "Last Update" row — but fall back to the response's value + // if liveDevice somehow lacks a valid date, so the row builder always has input. + const liveCreatedAt = liveDevice?.created_at; + const liveCreatedAtMs = + liveCreatedAt instanceof Date ? liveCreatedAt.getTime() : NaN; + const createdAtForRow = Number.isFinite(liveCreatedAtMs) + ? liveCreatedAt + : freshData.created_at; expandedDetailRowsByDevEui[devEui] = buildDeviceExpandedDetailRows( - freshData, + { ...freshData, created_at: createdAtForRow }, typeConfig, sensor.sensor.label ); } + function handleSensorCollapse(sensor: DashboardSensorCardEntry) { + // Allow the user to retry a failed/stuck expansion by collapsing and + // re-expanding. We only clear the marker when it's null (loading/failed) — + // successful fetches (rows array) stay cached so re-expanding doesn't refetch. + if (expandedDetailRowsByDevEui[sensor.devEui] === null) { + delete expandedDetailRowsByDevEui[sensor.devEui]; + } + } + async function refreshSingleDevice(device: IDevice | null | undefined) { const targetDevice = resolveLiveDashboardDevice(device); const devEui = targetDevice?.dev_eui; @@ -541,6 +568,7 @@ expireAfterMinutes={sensor?.sourceDevice?.raw_data?.default_upload_interval ?? 10} class="dashboard-device-cards__sensor-card" onExpand={() => handleSensorExpand(sensor)} + onCollapse={() => handleSensorCollapse(sensor)} >
diff --git a/src/lib/components/dashboard/dashboard-device-data.ts b/src/lib/components/dashboard/dashboard-device-data.ts index 2973f8ab..8b1cb135 100644 --- a/src/lib/components/dashboard/dashboard-device-data.ts +++ b/src/lib/components/dashboard/dashboard-device-data.ts @@ -52,6 +52,14 @@ function preferIncomingLocationId(incoming: number, current: number): number { return incoming > 0 ? incoming : current; } +function preferNewerCreatedAt(incoming: Date, current: Date): Date { + const incomingMs = incoming instanceof Date ? incoming.getTime() : NaN; + const currentMs = current instanceof Date ? current.getTime() : NaN; + if (!Number.isFinite(incomingMs)) return current; + if (!Number.isFinite(currentMs)) return incoming; + return incomingMs > currentMs ? incoming : current; +} + function getDeviceDataTable( device: DeviceDto | DevicePrimaryDataDto | Record ): string { @@ -244,6 +252,7 @@ export function mergeDashboardDevices( alert_count: latestDevice.alert_count ?? device.alert_count ?? 0, // Preserve device_type_id (metadata device has it, primary data refresh may not) device_type_id: device.device_type_id ?? latestDevice.device_type_id, + created_at: preferNewerCreatedAt(latestDevice.created_at, device.created_at), raw_data: latestDevice.raw_data ? { ...device.raw_data, ...latestDevice.raw_data } : device.raw_data diff --git a/src/lib/components/dashboard/device-cards.ts b/src/lib/components/dashboard/device-cards.ts index 3cfb9054..88864138 100644 --- a/src/lib/components/dashboard/device-cards.ts +++ b/src/lib/components/dashboard/device-cards.ts @@ -223,6 +223,7 @@ function buildDashboardSensorCardEntry( const rawPrimary = device.raw_data?.[typeConfig?.primary_data_key]; const rawSecondary = device.raw_data?.[typeConfig?.secondary_data_key] || null; + console.log(device); return { id: `sensor:${device.dev_eui}`, storageKey: getSensorStorageKey(device), diff --git a/src/lib/devices/relay-control.ts b/src/lib/devices/relay-control.ts index bbfe8b0e..c10661b2 100644 --- a/src/lib/devices/relay-control.ts +++ b/src/lib/devices/relay-control.ts @@ -12,7 +12,7 @@ import type { RelayTelemetryRow } from './relay-types'; -export const RELAY_VERIFICATION_DELAY_MS = 15_000; +export const RELAY_VERIFICATION_DELAY_MS = 10_000; export const RELAY_REFRESH_INTERVAL_MS = 30_000; export const MIN_RELAY_PULSE_DURATION_SECONDS = 15; export const MAX_RELAY_PULSE_DURATION_SECONDS = 4_294_967;