diff --git a/bin/omarchy-power-present b/bin/omarchy-power-present index 9ae756ca..5a9257fb 100755 --- a/bin/omarchy-power-present +++ b/bin/omarchy-power-present @@ -2,8 +2,13 @@ # omarchy:summary=Returns true if external power is connected. -for ac in /sys/class/power_supply/AC* /sys/class/power_supply/ADP*; do - [[ -r $ac/online && $(cat "$ac/online") == "1" ]] && exit 0 +power_supply_path="${OMARCHY_POWER_SUPPLY_PATH:-/sys/class/power_supply}" + +for supply in "$power_supply_path"/*; do + [[ -r $supply/online && -r $supply/type ]] || continue + type=$(<"$supply/type") + [[ $type == "Mains" || $type == "USB" ]] || continue + [[ $(<"$supply/online") == "1" ]] && exit 0 done exit 1 diff --git a/shell/plugins/panels/power/Model.js b/shell/plugins/panels/power/Model.js index 553d9aef..8a11dfe7 100644 --- a/shell/plugins/panels/power/Model.js +++ b/shell/plugins/panels/power/Model.js @@ -74,9 +74,7 @@ function batteryIcon(device, onBattery, states) { if (threshold) return defaultIcons[index] if (d.state === states.FullyCharged) return "󰂅" - if (d.state === states.Charging) return chargingIcons[index] - if (d.state === states.Discharging) return defaultIcons[index] - if (!onBattery) return "" + if (!onBattery) return chargingIcons[index] return defaultIcons[index] } @@ -86,8 +84,8 @@ function modeLabel(device, onBattery, states) { var percentage = d.isPresent ? d.percentage : 0 if (chargeThresholdActive(d, onBattery, states)) return "Threshold" + if (onBattery) return "On battery" if (!onBattery && percentage >= 1) return "Fully charged" - if (onBattery || d.state === states.Discharging) return "On battery" return "Charging" } diff --git a/shell/plugins/panels/power/Panel.qml b/shell/plugins/panels/power/Panel.qml index 92d54dfa..1f0f3b79 100644 --- a/shell/plugins/panels/power/Panel.qml +++ b/shell/plugins/panels/power/Panel.qml @@ -59,7 +59,7 @@ Panel { } readonly property bool discharging: { var device = UPower.displayDevice - return !!(device && device.isPresent && (UPower.onBattery || device.state === UPowerDeviceState.Discharging)) + return !!(device && device.isPresent && UPower.onBattery) } readonly property bool chargeThresholdActive: { var device = UPower.displayDevice @@ -76,7 +76,7 @@ Panel { readonly property bool charging: { var d = UPower.displayDevice - return d && d.isPresent && d.state === UPowerDeviceState.Charging && !root.chargeThresholdActive + return d && d.isPresent && !UPower.onBattery && !root.batteryFlowIdle } readonly property color batteryFillColor: { diff --git a/test/shell.d/power-present-test.sh b/test/shell.d/power-present-test.sh new file mode 100644 index 00000000..acb6aabb --- /dev/null +++ b/test/shell.d/power-present-test.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +tmp_dir=$(mktemp -d) +trap 'rm -rf "$tmp_dir"' EXIT + +write_supply() { + local name="$1" + local type="$2" + local online="$3" + + mkdir -p "$tmp_dir/$name" + printf '%s\n' "$type" >"$tmp_dir/$name/type" + printf '%s\n' "$online" >"$tmp_dir/$name/online" +} + +write_supply AC Mains 0 +write_supply USBC USB 1 + +OMARCHY_POWER_SUPPLY_PATH="$tmp_dir" "$ROOT/bin/omarchy-power-present" || + fail "power present detects online USB-C supply" +pass "power present detects online USB-C supply" + +printf '0\n' >"$tmp_dir/USBC/online" + +if OMARCHY_POWER_SUPPLY_PATH="$tmp_dir" "$ROOT/bin/omarchy-power-present"; then + fail "power present rejects offline supplies" +fi +pass "power present rejects offline supplies" + +write_supply AC Mains 1 + +OMARCHY_POWER_SUPPLY_PATH="$tmp_dir" "$ROOT/bin/omarchy-power-present" || + fail "power present detects online mains supply" +pass "power present detects online mains supply" diff --git a/test/shell.d/power-test.sh b/test/shell.d/power-test.sh index 66ec5166..281792cc 100644 --- a/test/shell.d/power-test.sh +++ b/test/shell.d/power-test.sh @@ -27,6 +27,16 @@ assert(!power.chargeThresholdActive({ isPresent: true, percentage: 0.8, state: s assert(!power.chargeThresholdActive({ isPresent: true, percentage: 0.5, state: states.Discharging }, false, states), 'power does not flag discharging as threshold') assertEqual(power.modeLabel({ isPresent: true, percentage: 1, state: states.FullyCharged }, false, states), 'Fully charged', 'power labels full battery') assertEqual(power.modeLabel({ isPresent: true, percentage: 0.5, state: states.Discharging }, true, states), 'On battery', 'power labels battery mode') -assertEqual(power.modeLabel({ isPresent: true, percentage: 0.5, state: states.Discharging }, false, states), 'On battery', 'power labels discharging battery mode') +assertEqual(power.modeLabel({ isPresent: true, percentage: 0.5, state: states.Discharging }, false, states), 'Charging', 'power treats external power as newer than stale discharging state') assert(power.batteryIcon({ isPresent: true, percentage: 0.4, state: states.Charging }, false, states).length > 0, 'power maps battery icons') +assertEqual( + power.batteryIcon({ isPresent: true, percentage: 0.4, state: states.Discharging }, false, states), + power.batteryIcon({ isPresent: true, percentage: 0.4, state: states.Charging, changeRate: 1.0, timeToFull: 120 }, false, states), + 'power shows charging icon when external power is present before battery state refreshes' +) +assertEqual( + power.batteryIcon({ isPresent: true, percentage: 0.4, state: states.Charging }, true, states), + power.batteryIcon({ isPresent: true, percentage: 0.4, state: states.Discharging }, true, states), + 'power shows battery icon when unplugged before battery state refreshes' +) JS