Skip to content

feat(battery-threshold): add multi-battery support and per-battery controls - #388

Draft
biplobsd wants to merge 2 commits into
noctalia-dev:mainfrom
biplobsd:main
Draft

feat(battery-threshold): add multi-battery support and per-battery controls#388
biplobsd wants to merge 2 commits into
noctalia-dev:mainfrom
biplobsd:main

Conversation

@biplobsd

Copy link
Copy Markdown

Plugin

  • Id: damian-ds7/battery-threshold
  • New plugin
  • Update to an existing plugin (version bumped in plugin.toml)

What it does

Adds multi-battery support to damian-ds7/battery-threshold (e.g. for ThinkPads and dual-battery laptops with BAT0 and BAT1):

  • Auto-detects all batteries in /sys/class/power_supply that expose charge_control_end_threshold.
  • Shows dedicated cards for each detected battery with live capacity, charging status, and independent threshold sliders in a smooth scrollable panel.
  • Adds quick actions to sync all batteries ("Set all to 80%", "100% (Max)").
  • Adds a rich multi-line hover tooltip on the bar widget showing each battery's health and threshold limit.
  • Extends IPC to support targeting specific batteries (e.g. set BAT1 80 or set 80).
  • Persists per-battery threshold limits across reboots in thresholds.json (with backward-compatible fallback).

External dependencies

The plugin shells out to standard Linux utilities listed in dependencies in plugin.toml:

  • test: To verify write permissions on /sys/class/power_supply/*/charge_control_end_threshold.
  • sudo, bash, readlink, cat, getent, groupadd, usermod, udevadm, chgrp, chmod: In setup_rules.sh to configure the battery_ctl udev rule and grant non-root write access.

Testing

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Noctalia version tested against: v5.0.0
  • Plugin API level: 3

Screenshots / Videos

Tested on dual-battery laptop (ThinkPad with BAT0 and BAT1). Multi-battery panel controls and widget tooltips render and control each battery independently.
image
image

Checklist

  • The directory name matches the part of id after the / in plugin.toml exactly.
  • It ships plugin.toml, README.md, thumbnail.webp, and translations/en.json.
  • README.md follows the README template, documents every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
  • I created thumbnail.webp with the thumbnail generator.
  • version follows semver and is bumped in this PR; plugin_api is the oldest API level this plugin requires.
  • Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
  • I did not edit catalog.toml; CI generates it.
  • This PR touches exactly one plugin directory.

Code review attestation

Plugins run as trusted, unsandboxed Luau in the user's session. Confirm:

  • The code is readable and not obfuscated, minified, or generated.
  • It does not download and execute remote code.
  • Every network call, filesystem write, and spawned process is something the description above accounts for.
  • I have the right to publish this code under the license declared in plugin.toml.

@github-actions

Copy link
Copy Markdown
Contributor

CC @damian-ds7

@ItsLemmy
ItsLemmy marked this pull request as draft August 17, 2026 12:24
@damian-ds7

Copy link
Copy Markdown
Contributor

Sorry for the delay, a few notes:

  • why is there an update interval that keeps re-writing the threshold every 5s?
    don't think it's needed once it's already set, and when it does fail to write
    there it's not logged either, unlike the initial set (service.luau:320-324,
    interval set at service.luau:469)

  • the part in check_status where different properties are read from files
    could probably be extracted to a helper, the same pattern is repeated multiple
    times there (service.luau:265-294, one block per model_name/manufacturer/
    capacity/status/threshold)

  • I don't think all_writable is ever used, it's just set to true/false but
    never read (service.luau:260, 331)

  • check_status could generally be refactored a bit beyond just the file read
    helpers, e.g. the part in the pending_checks == 0 branch would probably also
    fit in a helper (service.luau:335-361)

  • I don't think this plugin should be reading/tracking capacity/status at
    all honestly, feels out of scope for something that's just supposed to manage
    the threshold, and noctalia already has a widget that shows that. it's also
    wasted overhead, for single-battery setups it's read every 5s but never even
    displayed anywhere (read at service.luau:277-287, only rendered in the
    multi-battery branches at widget.luau:39-44 and panel.luau:108-115)

  • also check_status re-implements the same write logic as
    set_battery_threshold instead of just using it, probably why the failed
    writes aren't logged there in the first place:

    -- set_battery_threshold, service.luau:172-196 (logs + notifies on failure)
    local ok, err = noctalia.writeFile(threshold_file, tostring(v) .. "\n")
    if ok then
        ...
    else
        noctalia.log(...)
        noctalia.notifyError(...)
    end
    
    -- check_status, service.luau:320-327 (same write, no failure handling)
    local ok, _ = noctalia.writeFile(threshold_file, tostring(target_threshold) .. "\n")
    if ok then
        bat_info.current_threshold = target_threshold
    end

@damian-ds7

Copy link
Copy Markdown
Contributor

And one more minor nitpick, I don't have any multibattery laptops so I simply not be aware of the use case, but is useful to be able to set batteries to different levels? Wouldn't it generally make sense to just set all of them to the same level? Is the option to set individually needed? It would probably simplify code quite a bit if there was a single threshold tracked at all times applied to all discovered (or explicitly given by the user) batteries

@biplobsd

Copy link
Copy Markdown
Author

Sorry for creating this PR directly without first opening an issue. I’ve removed all unnecessary code and only made modifications needed for multiple battery detections. Thanks for reviewing my code, and also for creating the plugin. Please check my changes again and let me know if there are any remaining issues or improvements needed. Since the battery feature is so critical, it requires careful review.

@damian-ds7

Copy link
Copy Markdown
Contributor

Thanks, I will try to check out the changes before the weekend, sorry for the delay but I don't have too much free time right now

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.

2 participants