diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 8cc5cb6..01d6227 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -918,6 +918,23 @@ async function relay(id,on){ },1500); } } +function formatUptime(totalSeconds){ + totalSeconds=Math.max(0,Math.floor(Number(totalSeconds)||0)); + const days=Math.floor(totalSeconds/86400); + totalSeconds%=86400; + const hours=Math.floor(totalSeconds/3600); + totalSeconds%=3600; + const minutes=Math.floor(totalSeconds/60); + const seconds=totalSeconds%60; + + const parts=[]; + if(days) parts.push(days+"d"); + if(days||hours) parts.push(hours+"h"); + if(days||hours||minutes) parts.push(minutes+"m"); + parts.push(seconds+"s"); + return parts.join(" "); +} + function render(data){ data=mergeStatus(window.lastStatus,data); window.lastStatus=data; @@ -996,7 +1013,7 @@ function render(data){ $("wifiIp").textContent=n.sta_ip||"--"; $("apIp").textContent=n.ap_ip||"--"; $("fw").textContent=(s.firmware_name||"--")+" "+(s.firmware_version||""); - $("uptime").textContent=(s.uptime_seconds||0)+" sec"; + $("uptime").textContent=formatUptime(s.uptime_seconds||0); renderConfigControls(data); populateSetupFields(data); } diff --git a/tests/test_http_api_contract.py b/tests/test_http_api_contract.py index a544422..02cf10e 100644 --- a/tests/test_http_api_contract.py +++ b/tests/test_http_api_contract.py @@ -552,3 +552,15 @@ def test_embedded_webui_temp_config_uses_mobile_cards(): assert "Weather Badge" in body assert "configrow tempConfigRow" not in body assert "Address:" not in body + + +def test_embedded_webui_formats_uptime(): + source = firmware_source() + + assert "function formatUptime(totalSeconds)" in source + assert 'parts.push(days+"d")' in source + assert 'parts.push(hours+"h")' in source + assert 'parts.push(minutes+"m")' in source + assert 'parts.push(seconds+"s")' in source + assert 'formatUptime(s.uptime_seconds||0)' in source + assert '+" sec"' not in source