From 63077186a48806a6c27cdcb9e2cf6506eef8a08f Mon Sep 17 00:00:00 2001 From: Stephan Strittmatter Date: Thu, 20 Aug 2026 07:01:19 +0200 Subject: [PATCH] fix(web): stop telemetry poll from overwriting config edits loadTelemetry() polls /api/status every 2s and unconditionally wrote the Pool/Time tab form fields from the status JSON, even while the user was authenticated and editing. Any edit reverted within 2 seconds, and if the poll fired between the edit and the Save click, the save even persisted the reverted original value. Only populate these fields when unauthenticated (read-only display); /api/config via loadConfig() is the authoritative source once logged in. --- data/web/app.js | 57 +++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/data/web/app.js b/data/web/app.js index da5f49f0..4594f9a7 100644 --- a/data/web/app.js +++ b/data/web/app.js @@ -169,33 +169,40 @@ async function loadTelemetry() { updateAuthUI(); // ── Pool & Time Tab (read-only params from /api/status) ── - const statusFields = [ - ['loopInterval', data.loop_interval], - ['tempMaxPool', data.temp_max_pool], - ['tempMinSolar', data.temp_min_solar], - ['tempHysteresis', data.temp_hysteresis], - ['tempCircThreshold', data.temp_circ_threshold], - ['tempCircFactor', data.temp_circ_factor], - ['tempCircMaxRuntime', data.temp_circ_max_runtime], - ['timezone', data.timezone], - ['timeLossGreen', data.time_loss_green_hours], - ['timeLossRed', data.time_loss_red_hours], - ['ntpServer', data.ntp_server], - ]; - for (const [id, val] of statusFields) { - if (val != null) { - const el = document.getElementById(id); - if (el) el.value = val; + // Only populate these form fields when NOT authenticated: /api/config is + // the authoritative source once logged in (loadConfig fills them), and a + // telemetry poll here would otherwise overwrite the user's in-progress + // edits every 2 seconds. When unauthenticated the fields are disabled, + // so this read-only display is harmless. + if (!isAuthenticated) { + const statusFields = [ + ['loopInterval', data.loop_interval], + ['tempMaxPool', data.temp_max_pool], + ['tempMinSolar', data.temp_min_solar], + ['tempHysteresis', data.temp_hysteresis], + ['tempCircThreshold', data.temp_circ_threshold], + ['tempCircFactor', data.temp_circ_factor], + ['tempCircMaxRuntime', data.temp_circ_max_runtime], + ['timezone', data.timezone], + ['timeLossGreen', data.time_loss_green_hours], + ['timeLossRed', data.time_loss_red_hours], + ['ntpServer', data.ntp_server], + ]; + for (const [id, val] of statusFields) { + if (val != null) { + const el = document.getElementById(id); + if (el) el.value = val; + } } - } - // Timer start/end fields on Pool tab - if (data.timer_start_h != null) { - const pad2 = (n) => n.toString().padStart(2, '0'); - const stEl = document.getElementById('timerStart'); - if (stEl) stEl.value = pad2(data.timer_start_h) + ':' + pad2(data.timer_start_m); - const etEl = document.getElementById('timerEnd'); - if (etEl) etEl.value = pad2(data.timer_end_h) + ':' + pad2(data.timer_end_m); + // Timer start/end fields on Pool tab + if (data.timer_start_h != null) { + const pad2 = (n) => n.toString().padStart(2, '0'); + const stEl = document.getElementById('timerStart'); + if (stEl) stEl.value = pad2(data.timer_start_h) + ':' + pad2(data.timer_start_m); + const etEl = document.getElementById('timerEnd'); + if (etEl) etEl.value = pad2(data.timer_end_h) + ':' + pad2(data.timer_end_m); + } } // AP-Mode: WiFi-Tab anzeigen (nur einmalig — nicht bei jedem Poll erzwingen,