Skip to content

Commit dc32f19

Browse files
committed
fix(dashboard): throttle expensive /api/history queries, abort stale requests, keep chart data on failure
1 parent be983a5 commit dc32f19

1 file changed

Lines changed: 41 additions & 15 deletions

File tree

‎api/static/index.html‎

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,10 @@ <h3>Maintenance</h3>
422422
let battGaugesBuilt = false;
423423
const charts = {};
424424
const API = '';
425+
let historyAbort = null;
426+
let lastHistoryFetch = 0;
427+
const HISTORY_INTERVAL_MS = 30000;
428+
const FETCH_TIMEOUT_MS = 10000;
425429

426430
// ── Formatters ──
427431
function fmt(n, d=1) { if (n == null) return '—'; return Number(n).toFixed(d); }
@@ -978,32 +982,51 @@ <h3>Maintenance</h3>
978982
}
979983
}
980984

985+
async function fetchJson(url) {
986+
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
987+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
988+
return res.json();
989+
}
990+
981991
// ── Data fetching ──
982992
async function refresh() {
983993
try {
984-
// Use WebSocket summary if connected, otherwise fall back to HTTP
994+
// Live summary every interval (WebSocket preferred, HTTP fallback)
985995
if (!window._wsConnected) {
986-
const statusRes = await fetch(API + '/api/summary');
987-
applySummary(await statusRes.json());
996+
const status = await fetchJson(API + '/api/summary');
997+
applySummary(status);
988998
}
999+
} catch (e) {
1000+
console.error('Summary refresh failed:', e);
1001+
}
9891002

990-
// Fetch history in parallel (slower) for charts
991-
const fetches = [
992-
fetch(API + `/api/history?minutes=${overviewChartRange}&fields=pv1_power,pv2_power,charge_power,discharge_power,grid_import_power,grid_export_power,soc,battery_voltage,battery_current,eps_power,temp_inverter,temp_battery,temp_radiator_1`)
993-
];
994-
if (currentTab === 'charts') {
995-
fetches.push(fetch(API + `/api/history?minutes=${chartRange}&fields=pv1_power,pv2_power,charge_power,discharge_power,grid_import_power,grid_export_power,soc,battery_voltage,battery_current,eps_power,temp_inverter,temp_battery,temp_radiator_1`));
996-
}
1003+
// History queries are expensive on a Pi; only run every 30 s unless forced.
1004+
const now = Date.now();
1005+
if (now - lastHistoryFetch < HISTORY_INTERVAL_MS) return;
1006+
if (historyAbort) historyAbort.abort();
1007+
historyAbort = new AbortController();
1008+
lastHistoryFetch = now;
9971009

998-
const results = await Promise.all(fetches);
999-
const overviewHistory = await results[0].json();
1010+
try {
1011+
const overviewHistory = await fetchJson(
1012+
API + `/api/history?minutes=${overviewChartRange}&fields=pv1_power,pv2_power,charge_power,discharge_power,grid_import_power,grid_export_power,soc,battery_voltage,battery_current,eps_power,temp_inverter,temp_battery,temp_radiator_1`
1013+
);
10001014
renderOverviewChart(overviewHistory);
1015+
} catch (e) {
1016+
console.error('Overview history failed:', e);
1017+
// Keep existing chart data on failure so the graph doesn't blank out.
1018+
}
10011019

1002-
if (currentTab === 'charts' && results[1]) {
1003-
const chartsHistory = await results[1].json();
1020+
if (currentTab === 'charts') {
1021+
try {
1022+
const chartsHistory = await fetchJson(
1023+
API + `/api/history?minutes=${chartRange}&fields=pv1_power,pv2_power,charge_power,discharge_power,grid_import_power,grid_export_power,soc,battery_voltage,battery_current,eps_power,temp_inverter,temp_battery,temp_radiator_1`
1024+
);
10041025
renderCharts(chartsHistory);
1026+
} catch (e) {
1027+
console.error('Charts history failed:', e);
10051028
}
1006-
} catch(e) { console.error('Refresh failed:', e); }
1029+
}
10071030
}
10081031

10091032
// ── Settings fetch (for gauge max values) ──
@@ -1028,6 +1051,7 @@ <h3>Maintenance</h3>
10281051
currentTab = tab.dataset.tab;
10291052
document.getElementById('tab-' + currentTab).classList.add('active');
10301053
if (currentTab === 'totals') loadEnergyHistory('daily', 14);
1054+
lastHistoryFetch = 0;
10311055
refresh();
10321056
});
10331057
});
@@ -1038,6 +1062,7 @@ <h3>Maintenance</h3>
10381062
document.querySelectorAll('#chartTimeRange .time-btn').forEach(b => b.classList.remove('active'));
10391063
btn.classList.add('active');
10401064
chartRange = parseInt(btn.dataset.min);
1065+
lastHistoryFetch = 0;
10411066
refresh();
10421067
});
10431068
});
@@ -1048,6 +1073,7 @@ <h3>Maintenance</h3>
10481073
document.querySelectorAll('#overviewTimeRange .time-btn').forEach(b => b.classList.remove('active'));
10491074
btn.classList.add('active');
10501075
overviewChartRange = parseInt(btn.dataset.min);
1076+
lastHistoryFetch = 0;
10511077
refresh();
10521078
});
10531079
});

0 commit comments

Comments
 (0)