The same Power Saver / Balanced / Performance choice GNOME's Power panel offers, and the daemon behind it was already running here -- it simply had no control anywhere in Panama. This machine has been sitting on "performance" with nothing to say so. Talks to the net.hadess.PowerProfiles interface rather than to a binary. Fedora 44 implements it with tuned-ppd instead of power-profiles-daemon, and powerprofilesctl is not installed at all, so anything shelling out to that command would have found nothing while the service was right there. Setting a profile needs no privileges: the daemon accepts a property write from the active session user. Not a stored preference. The daemon owns the profile, it survives Panama restarts, and anything else on the system can change it, so a copy in settings.json would drift -- the same reasoning as monitor brightness. PerformanceDegraded is surfaced because it is what makes the setting a lie: a thermally throttled machine reports "performance" while behaving otherwise, and that is worth saying out loud. The contract stubs busctl, because the real daemon is a system service shared with everything else on the machine and a test that flipped the daily driver to power-saver and then died would leave it there. It pins the parsing in particular: busctl renders the Profiles property flat, so profile names and driver names arrive in one stream, and a pattern loose enough to match both reports the driver as an extra profile. On this machine the driver is called "tuned", which reads exactly like a plausible fourth profile -- verified the contract catches that, and catches an unvalidated name reaching the system service. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
86 lines
3.3 KiB
Bash
Executable File
86 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# System power profile, via the PowerProfiles D-Bus API.
|
|
#
|
|
# GNOME's Power panel offers Balanced / Performance / Power Saver; this is the
|
|
# same daemon behind it. On Fedora 44 the implementation is tuned-ppd rather
|
|
# than power-profiles-daemon, but it serves the same net.hadess.PowerProfiles
|
|
# interface, which is why this talks to the interface rather than to either
|
|
# binary -- powerprofilesctl is not even installed here.
|
|
#
|
|
# Setting a profile needs no privileges: the daemon accepts a property write
|
|
# from the active session user.
|
|
#
|
|
# Usage:
|
|
# panama-power-profile list -> {"profiles":[...],"active":"...","degraded":"..."}
|
|
# panama-power-profile set <name>
|
|
#
|
|
# PerformanceDegraded is reported because it is the one thing that makes the
|
|
# choice a lie: a thermally throttled laptop reports "performance" while
|
|
# behaving otherwise, and GNOME surfaces exactly this. It is an empty string
|
|
# when nothing is wrong.
|
|
|
|
set -uo pipefail
|
|
|
|
readonly BUS_NAME=net.hadess.PowerProfiles
|
|
readonly OBJECT=/net/hadess/PowerProfiles
|
|
|
|
emit_error() {
|
|
printf '{"profiles":[],"active":"","degraded":"","error":%s}\n' "$(jq -Rn --arg e "$1" '$e')"
|
|
exit 0
|
|
}
|
|
|
|
command -v busctl >/dev/null 2>&1 || emit_error 'busctl is not available'
|
|
|
|
property() {
|
|
busctl get-property "$BUS_NAME" "$OBJECT" "$BUS_NAME" "$1" 2>/dev/null
|
|
}
|
|
|
|
cmd_list() {
|
|
# A machine with no power-profiles daemon is a normal state -- plenty of
|
|
# desktops have none -- so it is reported rather than treated as a failure.
|
|
busctl status "$BUS_NAME" >/dev/null 2>&1 \
|
|
|| emit_error 'No power profile service is running. GNOME uses power-profiles-daemon; Fedora ships tuned-ppd.'
|
|
|
|
local active degraded profiles
|
|
active="$(property ActiveProfile | sed 's/^s //; s/"//g')"
|
|
degraded="$(property PerformanceDegraded | sed 's/^s //; s/"//g')"
|
|
|
|
# Profiles is an array of dicts, which busctl renders flat:
|
|
# v aa{sv} 3 2 "Profile" s "power-saver" "Driver" s "tuned" 2 "Profile" ...
|
|
# so each profile is the string following its own "Profile" marker. Matching
|
|
# the marker matters: "Driver" values sit in the same stream, and on this
|
|
# machine the driver is called "tuned", which a looser pattern happily
|
|
# reports as a fourth profile that does not exist.
|
|
profiles="$(property Profiles \
|
|
| grep -oE '"Profile" s "[a-z-]+"' \
|
|
| sed 's/.*s "//; s/"$//' \
|
|
| awk '!seen[$0]++')"
|
|
|
|
[[ -n "$profiles" ]] || emit_error 'The power profile service reported no profiles.'
|
|
|
|
jq -cn \
|
|
--arg active "$active" \
|
|
--arg degraded "$degraded" \
|
|
--argjson profiles "$(printf '%s\n' "$profiles" | jq -Rn '[inputs | select(length > 0)]')" \
|
|
'{profiles: $profiles, active: $active, degraded: $degraded, error: ""}'
|
|
}
|
|
|
|
cmd_set() {
|
|
local profile="${1:-}"
|
|
# Constrained rather than passed through: this reaches a system service.
|
|
[[ "$profile" =~ ^[a-z-]+$ ]] || {
|
|
printf 'panama-power-profile: refusing a profile name with unexpected characters\n' >&2
|
|
return 2
|
|
}
|
|
busctl set-property "$BUS_NAME" "$OBJECT" "$BUS_NAME" ActiveProfile s "$profile" 2>&1 >/dev/null \
|
|
| head -2 >&2
|
|
return 0
|
|
}
|
|
|
|
case "${1:-list}" in
|
|
list) cmd_list ;;
|
|
set) shift; cmd_set "${1:-}" ;;
|
|
*) printf 'usage: panama-power-profile [list|set <profile>]\n' >&2; exit 2 ;;
|
|
esac
|