Skip to content

fix(battery): report when no charge threshold exists instead of faking success - #3385

Open
Mario-Mohar wants to merge 2 commits into
AvengeMedia:masterfrom
Mario-Mohar:fix/battery-charge-limit-unsupported
Open

fix(battery): report when no charge threshold exists instead of faking success#3385
Mario-Mohar wants to merge 2 commits into
AvengeMedia:masterfrom
Mario-Mohar:fix/battery-charge-limit-unsupported

Conversation

@Mario-Mohar

Copy link
Copy Markdown
Contributor

Description

On hardware that exposes none of the three sysfs charge threshold files, Apply to Hardware wrote nothing and still reported success. The script walks every BAT*, matches none of the three if/elif branches, and sh exits 0, which onExited could not tell apart from a real write.

This is not a rare corner. Lenovo IdeaPad and ThinkBook models on ideapad_laptop expose charge_types (Fast / Standard / Long_Life) instead, and the kernel says so itself: "conservation_mode attribute has been deprecated, see charge_types". I have one of those machines here, so everything below was checked on the affected hardware rather than reasoned about.

Three changes:

The script now says whether it wrote anything. The if/elif chain became a loop over the same three names in the same order, so the priority does not change, with found=1 set when a branch matches and exit 2 when none did. A failed write still exits 1, so "this machine has no interface" and "the write failed" stay apart.

The toast tells the truth. Exit code 2 gives "Charge limit not supported on this hardware" instead of the success toast.

The button is not offered when it cannot work. A probe runs once when the tab loads and hides the Apply button if none of the three files exists, with a line naming the reason in its place. The exit code 2 branch stays as the authority, since the probe runs once and a battery can be removed or a driver reloaded after that.

What I deliberately left alone: the slider. The issue suggests disabling it too, but batteryChargeLimit also drives the "Charge Limit Reached" notification in BatteryService.qml, which works on this hardware and is independent of sysfs. Disabling the slider would break a working feature to tidy up a broken one.

Not in this PR: charge_types support. That interface is a mode selector, not a percentage, and the cap behind Long_Life is firmware defined, so it needs its own control and a decision about how it sits next to a 50-100 slider. That is the follow-up the issue describes as separable, and it seems better to agree on the shape of it first.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that changes existing behavior)
  • Refactor / internal cleanup
  • Documentation
  • Other

Related issues

Fixes #3278

Screenshots / video

Taken on the affected machine, a Lenovo IdeaPad whose BAT0 has charge_types and none of the three threshold files, in a nested Hyprland running this branch.

protection card before and after

Checklist

  • My code follows the conventions in CONTRIBUTING.md
  • I have tested my changes locally
  • New user-facing strings are wrapped in I18n.tr() with translator context, reusing existing terms where possible
  • Go changes: ran make fmt, added/updated tests, make test passes, and go mod tidy is clean
  • QML changes: ran make lint-qml with no new warnings
  • I have opened a corresponding pull request in dlx-docs to document any new behaviors: https://github.com/AvengeMedia/DankLinux-Docs

Notes on those boxes. Two new terms, Charge limit not supported on this hardware and No writable charge threshold file was found under /sys/class/power_supply., both with a translator comment, and the second is shared by the toast and the inline line rather than duplicated. I ran extract_translations.py to confirm they land as expected and then reverted en.json and template.json, since those are yours to sync. No Go changes. make lint-qml wants a .qmlls.ini from a full install, so I ran /usr/lib/qt6/bin/qmllint on the file instead: nothing beyond the unresolved qs.* imports every file produces, and the multiline-strings count is unchanged from master, which is why the probe command is written on one line. No docs PR, the change removes a wrong message rather than adding behavior to document.

How it was verified

The apply script and the probe were run against a fake power_supply tree as well as the real one:

case apply probe
this machine, only charge_types 2, nothing written 1, button hidden
charge_control_end_threshold present 0, value written 0
two batteries, different file each 0, both written 0
file present but not writable 1, error toast 0
several files present only charge_control_limit_max written, as before 0
no BAT* at all 2 1

@bbedward

Copy link
Copy Markdown
Collaborator

/claude review


StyledText {
width: parent.width
visible: Qt.platform.os === "linux" && !root.chargeLimitSupported

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On a machine with no battery at all (desktop, VM) the probe exits 1, so chargeLimitSupported becomes false and this line now shows "No writable charge threshold file was found under /sys/class/power_supply." to every desktop user who opens the Battery tab. The tab is always listed in SettingsTabs.qml (no battery gating), and the Status card already handles this case with visible: BatteryService.batteryAvailable (line 152).

Gate on battery presence so the explanation only appears when there actually is a battery whose thresholds can't be written:

Suggested change
visible: Qt.platform.os === "linux" && !root.chargeLimitSupported
visible: Qt.platform.os === "linux" && BatteryService.batteryAvailable && !root.chargeLimitSupported

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Claude review

Solid fix — the script logic, exit-code split and I18n usage all check out; one minor UX gap on batteryless machines.

  • New "No writable charge threshold file..." line shows on desktops/VMs with no battery at all, quickshell/Modules/Settings/BatteryTab.qml:240

Checked: apply/probe shell logic (glob, per-battery break, write failure vs. no-interface exit codes), Process lifecycle and binding order, I18n.tr(term, comment) form against Common/I18n.qml and extract_translations.py, en.json/template.json untouched, Theme tokens, Dank* widget usage. Model: claude-opus-5.

Follow-up to the review on AvengeMedia#3385: the new explanation showed on
desktops and VMs with no battery at all, where it reads as a fault
report about hardware the machine was never going to have.

Both the explanation and the apply button now require
BatteryService.batteryAvailable. Hiding the button there is the same
point the pull request makes elsewhere: offering a control that cannot
work is what produced the false success in the first place. A machine
without a battery has no charge threshold to write to, so the limit was
never applicable.

BatteryService.batteryAvailable is already the gate used twice in this
file for the same reason.
@Mario-Mohar

Copy link
Copy Markdown
Contributor Author

Good catch on the batteryless case, fixed in 3835c2d.

The explanation was gated on !chargeLimitSupported alone, which is also false on a desktop or VM that never had a battery. There it reads as a fault report about hardware the machine was never going to have.

Both the explanation and the apply button now also require BatteryService.batteryAvailable. Hiding the button there is the same point this PR makes elsewhere: offering a control that cannot work is what produced the false success in the first place, and a machine without a battery has no charge threshold to write to either. BatteryService.batteryAvailable is already the gate used twice in this file for the same reason.

make lint-qml needs the Quickshell tooling VFS, which I did not want to spin up on this machine, so I ran qmllint on the file alone: no syntax errors, and the unqualified-access warning on the new line is the same one the file already produces 24 times for BatteryService.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Battery charge limit: "Apply to Hardware" falsely reports success when no writable sysfs threshold exists (Lenovo IdeaPad uses charge_types/Long_Life)

2 participants