Skip to content

omarchy #9586 sysfs battery thresholds - #9612

Open
kvnloo wants to merge 4 commits into
omacom:quattrofrom
kvnloo:fix/9586-battery-sysfs-threshold
Open

kvnloo wants to merge 4 commits into
omacom:quattrofrom
kvnloo:fix/9586-battery-sysfs-threshold

Conversation

@kvnloo

@kvnloo kvnloo commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What

  • omarchy-battery-status reads live sysfs charge_control_{start,end}_threshold before UPower.
  • Falls back to BAT* glob, then UPower if sysfs is empty.
  • test/shell.d/battery-status-test.sh: UPower 75/80 vs sysfs 45/65 reports Holding at 45-65%; no-sysfs still uses UPower; 5 ok.

Why

Fixes #9586. ThinkPad UPower reports a built-in 75/80 while ChargeThresholdEnabled is false, so TLP/sysfs thresholds never appeared.

@sayem314

sayem314 commented Sep 6, 2026

Copy link
Copy Markdown

@robosayem review this pr, and check if any dupe pr exist related to this. and list issues this pr will close.

@robosayem robosayem left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reproduced the fix locally against a stub upower: with sysfs at 45/65 the script reports threshold\t45-65% over UPower's 75/80, without sysfs it falls back to UPower, and the holding line renders correctly. The core ordering (native device file, BAT* glob, UPower last) is right, and the moved fallback makes the previously dead glob code reachable. This fixes the ThinkPad case in #9586.

One moderate fix requested before merge

The fallbacks are per-field, which breaks end-only sysfs devices — the ASUS machines from #7374, where only charge_control_end_threshold exists. There, end=60 comes from sysfs while threshold_start falls back to UPower's fabricated 75, and the renderer takes the start-end branch, producing 75-60% (an inverted range) instead of 60%. The renderer already has a correct end-only branch, so the fix is to make UPower an all-or-nothing fallback: only read the UPower pair when sysfs exposed neither attribute. #7374's own proposed fix says the same ("don't fabricate a start threshold without a sysfs start file"). The PR's test only covers both-present and both-absent — a mixed case (end sysfs present, start absent) would have caught this.

Nit, non-blocking

Some drivers report charge_control_end_threshold = 0 to mean disabled — that renders as 0-0%. Treating 0 as absent (fall through to UPower) would be safer, but it can also be left to a follow-up.

Duplicate check

No duplicate: this is the only open PR changing the threshold source. Neighbouring open PRs touch other battery dimensions — #7915/#7895 (pending-charge hold heuristic), #7293 (charge status source), #6845/#7138/#7186/#8864/#9106 (multi-battery), #10076 (percentage source) — all complementary.

What this closes

  • Fully: #9586.
  • Partially: #7374 (fixes the 75-80-vs-real-limit symptom, not the bogus 0 cycle-count half) and #10344 (fixes the UPower-first source half, not the Model.chargeThresholdActive panel heuristic). Neither should auto-close on this PR alone unless the maintainers want to split them.

With the mixed-source fix, this is a clean approve from me.

@kvnloo

kvnloo commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Addressed @robosayem's mixed-source fix on 0c9ad0b:

  • UPower thresholds are now an all-or-nothing fallback (only when sysfs exposed neither start nor end), so end-only ASUS sysfs (charge_control_end_threshold=60) renders 60% instead of an inverted 75-60%.
  • Added a regression test for that end-only + UPower-start-present case.

Left for follow-up (non-blocking nit): treating sysfs 0 as absent / disabled.

@kvnloo
kvnloo force-pushed the fix/9586-battery-sysfs-threshold branch from 0c9ad0b to 461dabe Compare September 21, 2026 09:21
@kvnloo

kvnloo commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Verified against current quattro (fail-then-pass on scoped shell tests). Ready for maintainer review.

Scoped: battery-status-test (sysfs-first).

@omarchybot omarchybot added bug Something isn't working compatibility Make Omarchy work better on everything! labels Sep 27, 2026
@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed at 461dabe against quattro (f45461a), which has not touched either file since this branch forked.

What was checked. On a disposable Omarchy worker, I wrote a separate reproduction that stubs UPower to report the hwdb default of 75/80 with charge-threshold-enabled: no. It covers three cases: sysfs 45/65 (the ThinkPad case from #9586), sysfs end-only 60 (the ASUS case from #7374), and no sysfs files. On quattro, the first two show Holding at 75-80%. On this head they show 45-65% and 60%. With the fix reverted, they show 75-80% again. The third case falls back to UPower in all three versions. ./test/cli passes, and test/shell.d/battery-status-test.sh passes 6 of 6 on the head. Its new sysfs assertion fails against quattro's script. The change is proportionate: it reads sysfs first and uses UPower only when sysfs exposes neither threshold. Anything smaller would mix a UPower start with a sysfs end.

Second opinion. Codex Medium reviewed the same head. It agreed that the ordering fixes the bug and that the size is right (independence not guaranteed). It raised two edge cases. Neither blocks this:

  • The existing per-field BAT* glob could combine a start threshold from one battery with an end threshold from another. This needs one battery that exposes only an end threshold beside another that exposes only a start. The glob was already in quattro.
  • A driver reporting end threshold 0 to mean "disabled" would read as "Holding at 0%". This is the follow-up you already noted, and I have not found a driver that does it.

Related. #13362 and #13967 edit the same threshold-reading lines but still read UPower first. They overlap with this PR and will conflict with it, but they do not fix this bug. This PR fixes the charge-limit half of #7374 but not its cycle-count half, so I have not linked it to close that issue.

What it waits on. A stub only shows that the script handles the values the reports describe. It can't show that a real machine reports them. @ScytheAkira (ThinkPad, #9586) or @chickymonkeys (ASUS, #7374), could you run this on your laptop while plugged in and held at its limit, then reply here with the output?

curl -fsSL https://raw.githubusercontent.com/kvnloo/omarchy/461dabea12a305d92e472dbd84cdba7d47293a24/bin/omarchy-battery-status -o /tmp/omarchy-battery-status-9612
bash /tmp/omarchy-battery-status-9612 --shell
cat /sys/class/power_supply/BAT*/charge_control_*_threshold

The threshold line should match your sysfs values rather than 75-80%. This only downloads and runs the script once and changes nothing on your machine. After that confirmation, this goes to the maintainer.

@chickymonkeys

Copy link
Copy Markdown

@omarchybot I confirm that the threshold line matches the sysfs values rather than 75-80%, tested from terminal with the indicated snippet.

@omarchybot omarchybot added verified Omarchy Triage has verified that this issue is ready for final review ready Good to merge labels Oct 2, 2026
@omarchybot

Copy link
Copy Markdown
Collaborator

Thanks @chickymonkeys, that settles the hardware side: on your ASUS the threshold line now matches what sysfs holds rather than UPower's 75-80%.

I re-ran the check against current quattro (821ae58), which still has not touched either file. Head is still 461dabe. On a disposable Omarchy worker, with UPower stubbed to its 75/80 default and charge-threshold-enabled: no, quattro and this branch with the fix reverted both report 75-80% for a ThinkPad-style 45/65 sysfs and an ASUS-style end-only 60. This head reports 45-65% and 60%, and with no sysfs files it still falls back to UPower's 75-80%. ./test/cli passes, and test/shell.d/battery-status-test.sh passes 6 of 6 here while its new sysfs assertion fails against quattro's script.

Codex Medium reviewed this head again and found nothing that should block it. It reviewed the two edge cases from my last comment again. The cross-battery BAT* glob was already in quattro, and no real machine is known to hit the mixed case. The end-threshold-0 case does not reach ThinkPads: thinkpad_acpi reports a firmware stop value of 0 as 100 in sysfs. It also suggested two optional test improvements: a BAT1 fixture with conflicting BAT0 values, and clearing the earlier current and voltage fixtures so the power rate matches the UPower stub.

This now waits on the maintainer. #7374 stays open for its cycle-count half.

omarchybot and others added 2 commits November 1, 2026 02:09
quattro now reads the thresholds from the battery UPower resolved and counts the time to full up to that battery's own limit. Port the fix onto that: this battery's sysfs comes first, UPower stands in only when sysfs exposes neither threshold, and the BAT* glob stays the last resort, so the charge limit for the time to full is the live sysfs one too. The read_sysfs_threshold helper goes, since quattro's inline reads do the same, and the tests move onto quattro's fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A battery with only an end threshold in sysfs took its start from the first BAT* that had one, so a second battery with a start of 75 turned an ASUS end-only 60 into 75-60%, the inverted range the sysfs-first order exists to avoid. The glob now stands in only when neither this battery's sysfs nor UPower gave a threshold. The sysfs test also checks that the time to full counts up to the sysfs limit rather than UPower's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: muse-wonder via pi, thinking high <noreply@omacom.io>
@greptile-apps

greptile-apps Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] This PR appears safe to merge; no actionable issues were found.

Summary

omarchy-battery-status now reads the selected battery’s live sysfs thresholds before UPower.

  • Battery status follows the live charge limits.

Reviews (1) · Last reviewed commit: "Fill a missing threshold pair from the B..." · Reviewed by Greptile

@omarchybot

Copy link
Copy Markdown
Collaborator

This branch had stopped merging into quattro: #15285 rewrote the same threshold reads so they use the battery UPower resolved and time a charge to that battery's own limit. I merged quattro into it and kept your order on top of that. This battery's sysfs comes first, UPower stands in only when sysfs has neither threshold, and the BAT* glob comes last. The time to full now counts to the sysfs limit too. Two commits are now on your branch, ending at 3a8bf92. The second one makes the glob fill a missing pair only as a pair. Before that change, an end-only 60 next to a second battery with a start of 75 still showed 75-60%.

What was checked. All of this ran on a disposable Omarchy worker, with UPower stubbed to the hwdb's 75/80 and charge-threshold-enabled: no:

  • Reproduction. With sysfs 45/65, quattro shows 75-80% and times the charge to 80%. With an end-only 60, it shows 75-80% again. This head shows 45-65% (38m to full in the fixture) and 60%. With no sysfs files, it falls back to UPower's 75/80.
  • Tests. test/shell.d/battery-status-test.sh passes 9 of 9 and ./test/cli passes. The new assertions fail against quattro's script and against the merge without the pairing commit.

Codex Medium and muse-wonder (pi, thinking high) reviewed it in two rounds. In the first round, muse-wonder found the cross-battery pairing above. The second round found nothing on this head.

There is a competing fix, and it covers more. #16685 by @ThatRetiredDude reads sysfs first too. It also handles two cases from #7374, #10193 and #17137 that this branch still gets wrong:

I asked both reviewers to compare the two without giving them my conclusion. muse-wonder came to the same verdict and found no regression in #16685 on any of the reported machines. Codex could not read #16685's diff, but it confirmed both gaps in this branch. Independence is not guaranteed, since both read the same workspace.

The older fix normally wins, but these are reported machines this branch misses, so I am recommending #16685 to the maintainer instead of this one. What this branch adds is the end-only test with a second battery's start. From reading its diff, #16685 should already handle that case, because it reads the start only from the battery that has the end. I have not run it there. The choice between the two is the maintainer's. I have not worked #16685 yet; it gets its own review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working compatibility Make Omarchy work better on everything!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

omarchy-battery-status shows UPower's fixed ThinkPad default (75/80) instead of the real active charge threshold

5 participants