Skip to content

Commit 4566ae0

Browse files
fhirschmannclaude
andcommitted
feat: show battery level in web interface navbar
Broadcast voltage, charge level and low-battery state with the websocket pong and render a battery indicator in the navbar with a one-shot warning toast when the battery runs low. Only visible on builds with battery measurement enabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 45b340a commit 4566ae0

5 files changed

Lines changed: 55 additions & 2 deletions

File tree

‎html/locales/de.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"shutdowninfo": "ESPuino wird jetzt ausgeschaltet...<br>Die Seite lädt sich nach dem Wiedereinschalten automatisch neu.",
1919
"toast": {
2020
"rfidDetect": "RFID Tag mit {{rfidId}} erkannt.",
21+
"batteryLow": "Akku schwach: noch {{level}} %",
2122
"success": "Aktion erfolgreich ausgeführt.",
2223
"conlost": "Die Verbindung zum ESPuino ist unterbrochen! Bitte Seite neu laden.",
2324
"dropout": "Langsamer Stream, Aussetzer möglich.",

‎html/locales/en.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"shutdowninfo": "ESPuino is now switched off...<br>Page reloads automatically when the ESPuino is switched on again.",
1919
"toast": {
2020
"rfidDetect": "RFID tag {{rfidId}} detected.",
21+
"batteryLow": "Battery low: {{level}} % remaining",
2122
"success": "Action performed successfully.",
2223
"conlost": "Connection to ESPuino broken! Please reload website.",
2324
"dropout": "slow stream, dropouts are possible.",

‎html/locales/fr.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"shutdowninfo": "ESPuino est maintenant éteint...<br>La page se recharge automatiquement lorsque l'ESPuino est rallumé.",
1919
"toast": {
2020
"rfidDetect": "Étiquette RFID {{rfidId}} détectée.",
21+
"batteryLow": "Batterie faible : {{level}} % restants",
2122
"success": "Action effectuée avec succès.",
2223
"conlost": "Connexion à l'ESPuino perdue! Veuillez recharger la page.",
2324
"dropout": "flux lent, des interruptions sont possibles.",

‎html/management.html‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,19 @@
218218
text-shadow: 0 0 8px rgba(0, 240, 255, .6);
219219
}
220220

221+
#batteryIndicator {
222+
color: var(--cp-green);
223+
font-family: "Share Tech Mono", monospace;
224+
font-size: .9rem;
225+
text-shadow: 0 0 8px rgba(10, 255, 157, .5);
226+
}
227+
228+
#batteryIndicator.battery-low {
229+
color: var(--cp-magenta);
230+
text-shadow: 0 0 8px rgba(255, 42, 109, .6);
231+
animation: cp-flicker 3s linear infinite;
232+
}
233+
221234
/* ---------- dropdown menu ---------- */
222235
.dropdown-menu {
223236
background: var(--cp-panel);
@@ -1023,6 +1036,9 @@
10231036
<span id="navbar-heading" data-i18n="title"></span>
10241037
</a>
10251038
<div class="collapse navbar-collapse justify-content-end">
1039+
<span class="navbar-text" id="batteryIndicator" data-visible="false">
1040+
<i class="fas fa-battery-three-quarters" id="batteryIcon"></i><span id="batteryLevel"></span>
1041+
</span>
10261042
<ul class="navbar-nav">
10271043
<li class="nav-item">
10281044
<button class="btn dropdown-toggle border-0 text-white" id="menuToggle"
@@ -3193,6 +3209,9 @@ <h5 class="modal-title" data-i18n="tools.nvs.erase.title"></h5>
31933209
if ("ftpStatus" in socketMsg) {
31943210
updateFtpButton(socketMsg.ftpStatus.running);
31953211
}
3212+
if ("battery" in socketMsg) {
3213+
updateBatteryIndicator(socketMsg.battery);
3214+
}
31963215
if ("volume" in socketMsg) {
31973216
volumeSlider.setValue(parseInt(socketMsg.volume));
31983217
}
@@ -4260,6 +4279,33 @@ <h5 class="modal-title" data-i18n="tools.nvs.erase.title"></h5>
42604279
}
42614280
return dDisplay + hDisplay + mDisplay;
42624281
}
4282+
var batteryLowWarned = false;
4283+
function updateBatteryIndicator(battery) {
4284+
const indicator = document.getElementById('batteryIndicator');
4285+
const level = Math.round(battery.level);
4286+
let icon = 'fa-battery-empty';
4287+
if (level >= 87) {
4288+
icon = 'fa-battery-full';
4289+
} else if (level >= 62) {
4290+
icon = 'fa-battery-three-quarters';
4291+
} else if (level >= 37) {
4292+
icon = 'fa-battery-half';
4293+
} else if (level >= 12) {
4294+
icon = 'fa-battery-quarter';
4295+
}
4296+
document.getElementById('batteryIcon').className = 'fas ' + icon;
4297+
document.getElementById('batteryLevel').textContent = level + '%';
4298+
indicator.title = battery.voltage.toFixed(2) + ' V';
4299+
indicator.classList.toggle('battery-low', battery.low === true);
4300+
indicator.setAttribute('data-visible', true);
4301+
if (battery.low && !batteryLowWarned) {
4302+
batteryLowWarned = true;
4303+
toaster.warning(i18next.t('toast.batteryLow', { level: level }));
4304+
} else if (!battery.low) {
4305+
batteryLowWarned = false;
4306+
}
4307+
}
4308+
42634309
async function refreshSdCapacity() {
42644310
try {
42654311
const info = await (await fetch("http://" + host + "/info?section=sdcard")).json();

‎src/Web.cpp‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,8 +1590,12 @@ void Web_SendWebsocketData(uint32_t client, WebsocketCodeType code) {
15901590
} else if (code == WebsocketCodeType::Pong) {
15911591
object["pong"] = "pong";
15921592
object["rssi"] = Wlan_GetRssi();
1593-
// todo: battery percent + loading status +++
1594-
// object["battery"] = Battery_GetVoltage();
1593+
#ifdef BATTERY_MEASURE_ENABLE
1594+
JsonObject batteryObj = object["battery"].to<JsonObject>();
1595+
batteryObj["voltage"] = Battery_GetVoltage();
1596+
batteryObj["level"] = Battery_EstimateLevel() * 100;
1597+
batteryObj["low"] = Battery_IsLow();
1598+
#endif
15951599
} else if (code == WebsocketCodeType::OperationMode) {
15961600
object["opmode"] = System_GetOperationMode();
15971601
} else if (code == WebsocketCodeType::TrackInfo) {

0 commit comments

Comments
 (0)