webui: preserve config edits during polling

This commit is contained in:
2026-06-08 09:13:24 -06:00
parent 730da5482f
commit c43f121dc8
2 changed files with 50 additions and 3 deletions
@@ -401,6 +401,24 @@ function statusUrl(){
return needsFullStatus ? api("/status") : api("/status?fields="+STATUS_FIELDS_OVERVIEW); return needsFullStatus ? api("/status") : api("/status?fields="+STATUS_FIELDS_OVERVIEW);
} }
const configTouched={};
function markConfigFieldTouched(id){
configTouched[id]=true;
}
function setConfigValue(id,value){
const el=$(id);
if(!el) return;
if(configTouched[id] || document.activeElement===el) return;
el.value=value ?? "";
}
function clearConfigTouched(ids){
ids.forEach(id=>{delete configTouched[id];});
}
function mergeStatus(previous,next){ function mergeStatus(previous,next){
if(!previous) return next; if(!previous) return next;
return Object.assign({},previous,next); return Object.assign({},previous,next);
@@ -426,9 +444,7 @@ function renderApConfig(data){
const n=data.network||{}; const n=data.network||{};
if($("apCurrentSsid")) $("apCurrentSsid").textContent=n.ap_ssid||"--"; if($("apCurrentSsid")) $("apCurrentSsid").textContent=n.ap_ssid||"--";
if($("apSecurity")) $("apSecurity").textContent=n.ap_auth||"--"; if($("apSecurity")) $("apSecurity").textContent=n.ap_auth||"--";
if($("apSsidInput") && !$("apSsidInput").dataset.touched){ setConfigValue("apSsidInput",n.ap_ssid||"");
$("apSsidInput").value=n.ap_ssid||"";
}
} }
async function saveApConfig(){ async function saveApConfig(){
@@ -466,6 +482,7 @@ async function saveApConfig(){
$("apPasswordInput").value=""; $("apPasswordInput").value="";
alert("AP settings saved. Reconnect to the new WiFi network if this page stops updating."); alert("AP settings saved. Reconnect to the new WiFi network if this page stops updating.");
clearConfigTouched(["apSsidInput","apPasswordInput"]);
} }
function renderWeatherBadge(data){ function renderWeatherBadge(data){
@@ -699,6 +716,7 @@ async function saveDeviceConfig(){
await fetch(api("/config/save"),{method:"POST"}); await fetch(api("/config/save"),{method:"POST"});
await load(); await load();
alert("Device name saved"); alert("Device name saved");
clearConfigTouched(["deviceNameInput"]);
} }
async function saveBmsFullConfig(){ async function saveBmsFullConfig(){
@@ -725,6 +743,7 @@ async function saveBmsFullConfig(){
await fetch(api("/config/save"),{method:"POST"}); await fetch(api("/config/save"),{method:"POST"});
await load(); await load();
alert("BMS config saved"); alert("BMS config saved");
clearConfigTouched(["bmsNameInput","bmsAddressInput","bmsAddressTypeInput"]);
} }
async function scanTemps(){ async function scanTemps(){
@@ -896,6 +915,7 @@ async function saveTempConfig(){
await fetch(api("/config/save"),{method:"POST"}); await fetch(api("/config/save"),{method:"POST"});
await load(); await load();
alert("Temperature config saved"); alert("Temperature config saved");
clearConfigTouched(["tempEnabledCount"]);
} }
async function loadWifiConfig(){ async function loadWifiConfig(){
@@ -921,6 +941,7 @@ async function saveWifi(){
await fetch(api("/config/wifi"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({networks})}); await fetch(api("/config/wifi"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({networks})});
await loadWifiConfig(); await loadWifiConfig();
await load(); await load();
clearConfigTouched(["w1s","w1p","w1r","w2s","w2p","w2r","w3s","w3p","w3r"]);
} }
async function connectWifi(){ async function connectWifi(){
await fetch(api("/wifi/connect"),{method:"POST"}); await fetch(api("/wifi/connect"),{method:"POST"});
@@ -937,6 +958,18 @@ async function load(){
} }
load(); load();
loadWifiConfig(); loadWifiConfig();
document.addEventListener("input",e=>{
if(e.target && e.target.id && (
e.target.closest("#configPage") ||
e.target.id.startsWith("w") ||
e.target.id.startsWith("ap") ||
e.target.id.startsWith("bms") ||
e.target.id.startsWith("device") ||
e.target.id.startsWith("temp")
)){
markConfigFieldTouched(e.target.id);
}
});
setInterval(load,3000); setInterval(load,3000);
</script> </script>
</body> </body>
+14
View File
@@ -460,3 +460,17 @@ def test_temp_config_lookup_is_available_to_http_handlers():
helper_pos = source.index("int findTempConfigIndexById(") helper_pos = source.index("int findTempConfigIndexById(")
first_uart_guard_pos = source.index("#if DASHBOARD_UART_ENABLED") first_uart_guard_pos = source.index("#if DASHBOARD_UART_ENABLED")
assert helper_pos < first_uart_guard_pos assert helper_pos < first_uart_guard_pos
def test_embedded_webui_preserves_config_input_while_polling():
source = firmware_source()
assert "const configTouched={};" in source
assert "function markConfigFieldTouched(id)" in source
assert "function setConfigValue(id,value)" in source
assert "document.activeElement===el" in source
assert 'document.addEventListener("input"' in source
assert 'setConfigValue("apSsidInput"' in source
assert 'setConfigValue("deviceNameInput"' in source
assert 'setConfigValue("bmsNameInput"' in source
assert 'setConfigValue("tempEnabledCount"' in source