From af8de8fa1e1aef0401e0ed7bb140a8985a4626f7 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 5 Jun 2026 01:11:29 -0600 Subject: [PATCH] Show charging time to full in WebUI --- docs/API.md | 4 ++- docs/project-state.md | 2 +- .../overland-controller.ino | 33 ++++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/docs/API.md b/docs/API.md index 8b3437f..52a48ec 100644 --- a/docs/API.md +++ b/docs/API.md @@ -91,7 +91,9 @@ json { "source": "jbd_bms", "connected": true, "soc": 70, "voltage": 13. Additional fields may include: -text cell_count cell_voltages cell_min_voltage cell_max_voltage cell_delta_mv runtime_hours ntc_count cells_valid +text cell_count cell_voltages cell_min_voltage cell_max_voltage cell_delta_mv runtime_hours ntc_count cells_valid + +`runtime_hours` is discharge runtime from the ESP32 status payload. The WebUI derives charging time-to-full from `capacity_ah`, `remaining_ah`, and positive incoming `current` without changing the API shape. --- diff --git a/docs/project-state.md b/docs/project-state.md index 1e91353..7264d7e 100644 --- a/docs/project-state.md +++ b/docs/project-state.md @@ -67,7 +67,7 @@ Shows: - Battery SOC - Voltage - Current -- Estimated runtime +- Estimated runtime / charging time-to-full - Temperature sensors - Relay controls - Network status diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 328e638..cd70193 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -79,7 +79,7 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba
Voltage
--
Current
--
-
Runtime
--
+
Runtime
--
@@ -233,13 +233,28 @@ input{width:100%;border:1px solid var(--line);border-radius:12px;padding:12px;ba const $=id=>document.getElementById(id); function fmt(n,d=1){return typeof n==="number"?n.toFixed(d):"--"} function temp(v){return typeof v==="number"?fmt(v,1)+"°F":"Offline"} -function runtimeText(b){ - if(!b.connected) return "--"; - if(typeof b.runtime_hours==="number" && b.runtime_hours>0) return fmt(b.runtime_hours,1)+"h"; - if(typeof b.current==="number" && b.current<-.1 && typeof b.remaining_ah==="number"){ - return fmt(b.remaining_ah/Math.abs(b.current),1)+"h"; +function batteryTimeInfo(b){ + if(!b.connected) return {label:"Runtime", value:"--"}; + + const current = b.current; + const remaining = b.remaining_ah; + const capacity = b.capacity_ah; + + if(typeof current==="number" && current>.1 && + typeof remaining==="number" && typeof capacity==="number" && + capacity>remaining){ + return {label:"Time to full", value:fmt((capacity-remaining)/current,1)+"h"}; } - return "Idle"; + + if(typeof b.runtime_hours==="number" && b.runtime_hours>0){ + return {label:"Runtime", value:fmt(b.runtime_hours,1)+"h"}; + } + + if(typeof current==="number" && current<-.1 && typeof remaining==="number"){ + return {label:"Runtime", value:fmt(remaining/Math.abs(current),1)+"h"}; + } + + return {label:"Battery time", value:"Idle"}; } function showTab(name){ ["overview","battery","config"].forEach(t=>{ @@ -269,7 +284,9 @@ function render(data){ $("socbar").style.width=Math.max(0,Math.min(100,b.soc||0))+"%"; $("voltage").textContent=fmt(b.voltage,2)+" V"; $("current").textContent=fmt(b.current,2)+" A"; - $("runtime").textContent=runtimeText(b); + const timeInfo=batteryTimeInfo(b); + $("runtimeLabel").textContent=timeInfo.label; + $("runtime").textContent=timeInfo.value; $("bmsState").textContent=b.connected?"BMS Online":"BMS Offline"; $("bmsState").className=b.connected?"medium good":"medium bad"; $("battSource").textContent=b.source||"--";