mirror of
https://github.com/mfactory-osaka/ESPTimeCast.git
synced 2026-02-19 11:54:56 -05:00
Display total runtime in Web UI
This commit is contained in:
@@ -1102,6 +1102,26 @@ void setupWebServer() {
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
});
|
||||
|
||||
server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (!LittleFS.exists("/uptime.dat")) {
|
||||
request->send(200, "text/plain", "No uptime recorded yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
File f = LittleFS.open("/uptime.dat", "r");
|
||||
if (!f) {
|
||||
request->send(500, "text/plain", "Error reading uptime file.");
|
||||
return;
|
||||
}
|
||||
|
||||
String content = f.readString();
|
||||
f.close();
|
||||
|
||||
unsigned long seconds = content.toInt();
|
||||
String formatted = formatUptime(seconds);
|
||||
request->send(200, "text/plain", formatted);
|
||||
});
|
||||
|
||||
server.on("/generate_204", HTTP_GET, handleCaptivePortal); // Android
|
||||
server.on("/fwlink", HTTP_GET, handleCaptivePortal); // Windows
|
||||
server.on("/hotspot-detect.html", HTTP_GET, handleCaptivePortal); // iOS/macOS
|
||||
@@ -1111,13 +1131,21 @@ void setupWebServer() {
|
||||
}
|
||||
|
||||
void handleCaptivePortal(AsyncWebServerRequest *request) {
|
||||
Serial.print(F("[WEBSERVER] Captive Portal Redirecting: "));
|
||||
Serial.print(F("[WEBSERVER] Captive Portal triggered for URL: "));
|
||||
Serial.println(request->url());
|
||||
request->redirect(String("http://") + WiFi.softAPIP().toString() + "/");
|
||||
|
||||
if (isAPMode) {
|
||||
IPAddress apIP = WiFi.softAPIP();
|
||||
String redirectUrl = "http://" + apIP.toString() + "/";
|
||||
Serial.print(F("[WEBSERVER] Redirecting to captive portal: "));
|
||||
Serial.println(redirectUrl);
|
||||
request->redirect(redirectUrl);
|
||||
} else {
|
||||
Serial.println(F("[WEBSERVER] Not in AP mode — sending 404"));
|
||||
request->send(404, "text/plain", "Not found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
String normalizeWeatherDescription(String str) {
|
||||
// Serbian Cyrillic → Latin
|
||||
str.replace("а", "a");
|
||||
|
||||
@@ -630,6 +630,7 @@ textarea::placeholder {
|
||||
|
||||
<div class="footer">
|
||||
Project by: <a href="https://www.instagram.com/mfactory.osaka" target="_blank" rel="noopener noreferrer">M-Factory</a><br>
|
||||
Device uptime: <span id="uptimeDisplay">Loading...</span>
|
||||
</div>
|
||||
|
||||
<div id="savingMessage"></div>
|
||||
@@ -1355,6 +1356,61 @@ apiInput.addEventListener('blur', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Uptime
|
||||
|
||||
let uptimeSeconds = 0;
|
||||
let uptimeTimer;
|
||||
|
||||
// Fetch uptime from ESP
|
||||
function fetchUptime() {
|
||||
fetch('/uptime')
|
||||
.then(res => res.text())
|
||||
.then(text => {
|
||||
//console.log('Uptime response:', text);
|
||||
uptimeSeconds = parseUptimeToSeconds(text);
|
||||
updateUptimeDisplay();
|
||||
if (uptimeTimer) clearInterval(uptimeTimer);
|
||||
uptimeTimer = setInterval(() => {
|
||||
uptimeSeconds++;
|
||||
updateUptimeDisplay();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(err => console.error('Error fetching /uptime:', err));
|
||||
}
|
||||
|
||||
// Convert "14:56:54" or "1 day 3:12:33" → total seconds
|
||||
function parseUptimeToSeconds(text) {
|
||||
let days = 0, h = 0, m = 0, s = 0;
|
||||
const dayMatch = text.match(/(\d+)\s*day/);
|
||||
if (dayMatch) days = parseInt(dayMatch[1]);
|
||||
const timeMatch = text.match(/(\d+):(\d+):(\d+)/);
|
||||
if (timeMatch) {
|
||||
h = parseInt(timeMatch[1]);
|
||||
m = parseInt(timeMatch[2]);
|
||||
s = parseInt(timeMatch[3]);
|
||||
}
|
||||
return days * 86400 + h * 3600 + m * 60 + s;
|
||||
}
|
||||
|
||||
// Format seconds → same "D days HH:MM:SS" style
|
||||
function formatUptime(seconds) {
|
||||
const days = Math.floor(seconds / 86400);
|
||||
seconds %= 86400;
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = seconds % 60;
|
||||
return `${days > 0 ? days + ' day' + (days > 1 ? 's ' : ' ') : ''}${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
|
||||
}
|
||||
|
||||
// Update the text on screen
|
||||
function updateUptimeDisplay() {
|
||||
document.getElementById('uptimeDisplay').textContent = formatUptime(uptimeSeconds);
|
||||
}
|
||||
|
||||
// Start it up
|
||||
fetchUptime();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1105,6 +1105,25 @@ void setupWebServer() {
|
||||
request->send(200, "application/json", "{\"ok\":true}");
|
||||
});
|
||||
|
||||
server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
if (!LittleFS.exists("/uptime.dat")) {
|
||||
request->send(200, "text/plain", "No uptime recorded yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
File f = LittleFS.open("/uptime.dat", "r");
|
||||
if (!f) {
|
||||
request->send(500, "text/plain", "Error reading uptime file.");
|
||||
return;
|
||||
}
|
||||
|
||||
String content = f.readString();
|
||||
|
||||
unsigned long seconds = content.toInt();
|
||||
String formatted = formatUptime(seconds);
|
||||
request->send(200, "text/plain", formatted);
|
||||
});
|
||||
|
||||
server.on("/generate_204", HTTP_GET, handleCaptivePortal); // Android
|
||||
server.on("/fwlink", HTTP_GET, handleCaptivePortal); // Windows
|
||||
server.on("/hotspot-detect.html", HTTP_GET, handleCaptivePortal); // iOS/macOS
|
||||
@@ -1114,9 +1133,19 @@ void setupWebServer() {
|
||||
}
|
||||
|
||||
void handleCaptivePortal(AsyncWebServerRequest *request) {
|
||||
Serial.print(F("[WEBSERVER] Captive Portal Redirecting: "));
|
||||
Serial.print(F("[WEBSERVER] Captive Portal triggered for URL: "));
|
||||
Serial.println(request->url());
|
||||
request->redirect(String("http://") + WiFi.softAPIP().toString() + "/");
|
||||
|
||||
if (isAPMode) {
|
||||
IPAddress apIP = WiFi.softAPIP();
|
||||
String redirectUrl = "http://" + apIP.toString() + "/";
|
||||
Serial.print(F("[WEBSERVER] Redirecting to captive portal: "));
|
||||
Serial.println(redirectUrl);
|
||||
request->redirect(redirectUrl);
|
||||
} else {
|
||||
Serial.println(F("[WEBSERVER] Not in AP mode — sending 404"));
|
||||
request->send(404, "text/plain", "Not found");
|
||||
}
|
||||
}
|
||||
|
||||
String normalizeWeatherDescription(String str) {
|
||||
|
||||
@@ -630,6 +630,7 @@ textarea::placeholder {
|
||||
|
||||
<div class="footer">
|
||||
Project by: <a href="https://www.instagram.com/mfactory.osaka" target="_blank" rel="noopener noreferrer">M-Factory</a><br>
|
||||
Device uptime: <span id="uptimeDisplay">Loading...</span>
|
||||
</div>
|
||||
|
||||
<div id="savingMessage"></div>
|
||||
@@ -1355,6 +1356,61 @@ apiInput.addEventListener('blur', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Uptime
|
||||
|
||||
let uptimeSeconds = 0;
|
||||
let uptimeTimer;
|
||||
|
||||
// Fetch uptime from ESP
|
||||
function fetchUptime() {
|
||||
fetch('/uptime')
|
||||
.then(res => res.text())
|
||||
.then(text => {
|
||||
//console.log('Uptime response:', text);
|
||||
uptimeSeconds = parseUptimeToSeconds(text);
|
||||
updateUptimeDisplay();
|
||||
if (uptimeTimer) clearInterval(uptimeTimer);
|
||||
uptimeTimer = setInterval(() => {
|
||||
uptimeSeconds++;
|
||||
updateUptimeDisplay();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(err => console.error('Error fetching /uptime:', err));
|
||||
}
|
||||
|
||||
// Convert "14:56:54" or "1 day 3:12:33" → total seconds
|
||||
function parseUptimeToSeconds(text) {
|
||||
let days = 0, h = 0, m = 0, s = 0;
|
||||
const dayMatch = text.match(/(\d+)\s*day/);
|
||||
if (dayMatch) days = parseInt(dayMatch[1]);
|
||||
const timeMatch = text.match(/(\d+):(\d+):(\d+)/);
|
||||
if (timeMatch) {
|
||||
h = parseInt(timeMatch[1]);
|
||||
m = parseInt(timeMatch[2]);
|
||||
s = parseInt(timeMatch[3]);
|
||||
}
|
||||
return days * 86400 + h * 3600 + m * 60 + s;
|
||||
}
|
||||
|
||||
// Format seconds → same "D days HH:MM:SS" style
|
||||
function formatUptime(seconds) {
|
||||
const days = Math.floor(seconds / 86400);
|
||||
seconds %= 86400;
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = seconds % 60;
|
||||
return `${days > 0 ? days + ' day' + (days > 1 ? 's ' : ' ') : ''}${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
|
||||
}
|
||||
|
||||
// Update the text on screen
|
||||
function updateUptimeDisplay() {
|
||||
document.getElementById('uptimeDisplay').textContent = formatUptime(uptimeSeconds);
|
||||
}
|
||||
|
||||
// Start it up
|
||||
fetchUptime();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user