Files
omarchy/shell/plugins/services/battery/BatteryModel.js
T
David Heinemeier HanssonandClaude Opus 5.5 1b1cc5ecb5 Track the power profile through Quickshell instead of polling busctl (#15027)
* Track the power profile through Quickshell instead of polling busctl

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Recover the power profile when the daemon was down at shell start

Addresses Codex review: Quickshell never attaches its PowerProfiles listener if the daemon failed at startup. One-off reads on startup, power source changes and an IPC refresh from omarchy-powerprofiles-set cover that, still with no timer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Don't let a stale power profile read win over a change signal or drop a refresh

Addresses Codex re-review: a busctl read in flight could overwrite a newer PowerProfiles signal, and refreshes requested while a read ran were dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test power profile transitions and stale reads as behavior, not source text

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:01:49 +09:00

51 lines
1.6 KiB
JavaScript

function batteryPercentage(device) {
if (!device || !device.isPresent) return -1
return Math.round(Number(device.percentage || 0) * 100)
}
function isDischarging(device, onBattery, dischargingState) {
return !!(device && device.isPresent && onBattery && device.state === dischargingState)
}
function shouldWarnLowBattery(device, onBattery, dischargingState, threshold, alreadyNotified) {
var level = batteryPercentage(device)
if (level < 0) return { level: level, notify: false, notifiedLowBattery: false }
var low = isDischarging(device, onBattery, dischargingState) && level <= threshold
return {
level: level,
notify: low && !alreadyNotified,
notifiedLowBattery: low
}
}
function parseActiveProfile(text) {
// busctl --json=short prints {"type":"s","data":"balanced"}; an empty or
// malformed reply (daemon not running) reads as no active profile.
try {
return String(JSON.parse(text).data || "").trim()
} catch (e) {
return ""
}
}
function profileAfterRead(current, replyText, signalsAtStart, signalsNow) {
// A change signal that arrived during the read is newer than its reply.
return signalsAtStart === signalsNow ? parseActiveProfile(replyText) : current
}
function isPowerSaverOnBattery(onBattery, profile) {
return !!onBattery && profile === "power-saver"
}
if (typeof module !== "undefined") {
module.exports = {
batteryPercentage: batteryPercentage,
isDischarging: isDischarging,
shouldWarnLowBattery: shouldWarnLowBattery,
parseActiveProfile: parseActiveProfile,
profileAfterRead: profileAfterRead,
isPowerSaverOnBattery: isPowerSaverOnBattery
}
}