@@ -422,6 +422,10 @@ <h3>Maintenance</h3>
422422let battGaugesBuilt = false ;
423423const charts = { } ;
424424const API = '' ;
425+ let historyAbort = null ;
426+ let lastHistoryFetch = 0 ;
427+ const HISTORY_INTERVAL_MS = 30000 ;
428+ const FETCH_TIMEOUT_MS = 10000 ;
425429
426430// ── Formatters ──
427431function 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 ──
982992async 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