diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index cd70193..b58f402 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -233,6 +233,31 @@ 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 formatHours(hours){ + if(typeof hours!=="number" || !isFinite(hours) || hours<=0){ + return "Idle"; + } + + const totalMinutes=Math.round(hours*60); + + if(totalMinutes < 1){ + return "<1m"; + } + + const h=Math.floor(totalMinutes/60); + const m=totalMinutes%60; + + if(h===0){ + return `${m}m`; + } + + if(m===0){ + return `${h}h`; + } + + return `${h}h ${m}m`; +} + function batteryTimeInfo(b){ if(!b.connected) return {label:"Runtime", value:"--"}; @@ -243,15 +268,15 @@ function batteryTimeInfo(b){ 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 {label:"Time to full", value:formatHours((capacity-remaining)/current)}; } if(typeof b.runtime_hours==="number" && b.runtime_hours>0){ - return {label:"Runtime", value:fmt(b.runtime_hours,1)+"h"}; + return {label:"Runtime", value:formatHours(b.runtime_hours)}; } if(typeof current==="number" && current<-.1 && typeof remaining==="number"){ - return {label:"Runtime", value:fmt(remaining/Math.abs(current),1)+"h"}; + return {label:"Runtime", value:formatHours(remaining/Math.abs(current))}; } return {label:"Battery time", value:"Idle"};