quickshell: add estimated time remaining to battery dropdown

Reads energy_now/energy_full to calculate hours remaining.
Shows "Xh Ym left" when discharging, "Xh Ym to full" when
charging, alongside the wattage draw.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
rope 2026-05-26 17:22:05 +01:00
parent dca7d49a5f
commit 1d45734a04

View file

@ -683,6 +683,9 @@ in
property bool charging: false property bool charging: false
property string batteryIcon: "\u{f008e}" property string batteryIcon: "\u{f008e}"
property real powerDraw: 0.0 property real powerDraw: 0.0
property real energyNow: 0.0
property real energyFull: 0.0
property string timeRemaining: ""
property string powerProfile: "balanced" property string powerProfile: "balanced"
function updateIcon() { function updateIcon() {
@ -705,21 +708,38 @@ in
Process { Process {
id: batteryProc id: batteryProc
command: ["sh", "-c", "cat /sys/class/power_supply/BAT0/capacity; cat /sys/class/power_supply/BAT0/status; cat /sys/class/power_supply/BAT0/power_now 2>/dev/null || echo 0"] command: ["sh", "-c", "cat /sys/class/power_supply/BAT0/capacity; cat /sys/class/power_supply/BAT0/status; cat /sys/class/power_supply/BAT0/power_now 2>/dev/null || echo 0; cat /sys/class/power_supply/BAT0/energy_now 2>/dev/null || echo 0; cat /sys/class/power_supply/BAT0/energy_full 2>/dev/null || echo 0"]
stdout: SplitParser { stdout: SplitParser {
property int lineNum: 0 property int lineNum: 0
onRead: data => { onRead: data => {
let trimmed = data.trim(); let trimmed = data.trim();
let num = parseInt(trimmed);
lineNum++; lineNum++;
if (lineNum === 1) { if (lineNum === 1) {
let num = parseInt(trimmed);
if (!isNaN(num)) batteryWidget.batteryLevel = num; if (!isNaN(num)) batteryWidget.batteryLevel = num;
} else if (lineNum === 2) { } else if (lineNum === 2) {
batteryWidget.charging = (trimmed === "Charging"); batteryWidget.charging = (trimmed === "Charging");
} else if (lineNum === 3) { } else if (lineNum === 3) {
let uw = parseInt(trimmed); if (!isNaN(num)) batteryWidget.powerDraw = num / 1000000.0;
if (!isNaN(uw)) batteryWidget.powerDraw = uw / 1000000.0; } else if (lineNum === 4) {
if (!isNaN(num)) batteryWidget.energyNow = num / 1000000.0;
} else if (lineNum === 5) {
if (!isNaN(num)) batteryWidget.energyFull = num / 1000000.0;
lineNum = 0; lineNum = 0;
// Calculate time remaining
if (batteryWidget.powerDraw > 0.5) {
let hours;
if (batteryWidget.charging) {
hours = (batteryWidget.energyFull - batteryWidget.energyNow) / batteryWidget.powerDraw;
} else {
hours = batteryWidget.energyNow / batteryWidget.powerDraw;
}
let h = Math.floor(hours);
let m = Math.round((hours - h) * 60);
batteryWidget.timeRemaining = h + "h " + m + "m";
} else {
batteryWidget.timeRemaining = "";
}
} }
batteryWidget.updateIcon(); batteryWidget.updateIcon();
} }
@ -1235,6 +1255,7 @@ in
} }
Text { Text {
text: batteryWidget.powerDraw.toFixed(1) + " W" text: batteryWidget.powerDraw.toFixed(1) + " W"
+ (batteryWidget.timeRemaining !== "" ? " \u2022 " + batteryWidget.timeRemaining + (batteryWidget.charging ? " to full" : " left") : "")
color: "#${c.base04}" color: "#${c.base04}"
font.family: "FiraMono Nerd Font" font.family: "FiraMono Nerd Font"
font.pixelSize: 11 font.pixelSize: 11