Skip to content

Add plugin: Battery & Power Management#864

Open
Piero-93 wants to merge 17 commits into
noctalia-dev:mainfrom
Piero-93:feature/battery-wattage-bar-widget
Open

Add plugin: Battery & Power Management#864
Piero-93 wants to merge 17 commits into
noctalia-dev:mainfrom
Piero-93:feature/battery-wattage-bar-widget

Conversation

@Piero-93
Copy link
Copy Markdown

@Piero-93 Piero-93 commented May 17, 2026

Description

This PR introduces the Battery & Power Mangement plugin for the Noctalia desktop shell. It provides real-time battery diagnostics and power optimization directly from the panel.

Features Included

  • Live Diagnostics: Displays current battery percentage, status, and precise power consumption or charge rate dynamically calculated in Watts (W).
  • Power Profile Selector: Native UI triggers via powerprofilesctl backend to alternate between power-saver, balanced, and performance profiles.
  • Hardware Charge Threshold Slider: A multi-step reactive custom QML slider targeting /sys/class/power_supply/BAT0/charge_control_end_threshold (clamped between 50% and 100%).
  • Documentation: Added a dedicated README.html detailing setup instructions, prerequisites, and mandatory udev configurations for unprivileged sysfs writes.

Testing Status

  • Verified QML reactivity and property bindings between BarWidget.qml and BatteryPopup.qml.
  • Confirmed sysfs read operations fallback safely during initial backend initialization cycles.
  • Tested on Niri

@github-actions

This comment was marked as resolved.

@Piero-93 Piero-93 force-pushed the feature/battery-wattage-bar-widget branch from 7fa2f19 to 9593250 Compare May 17, 2026 17:21
@github-actions

This comment was marked as resolved.

@Piero-93 Piero-93 force-pushed the feature/battery-wattage-bar-widget branch from 9593250 to a5f28c9 Compare May 17, 2026 17:23
Copy link
Copy Markdown
Collaborator

@spiros132 spiros132 left a comment

Choose a reason for hiding this comment

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

Some feedback about the PR! :)

# ------------------------------
set -e

RULE_FILE="99-battery-threshold.rules"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this rules file supposed to be in this directory as well?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The file was missing. Now it's fixed, no need for the file, the script writes it itself.

Comment thread battery-and-power-management/README.md Outdated

## Installation & Setup

### 1\. Copy the Assets
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This part isn't correct, the user needs only to go to their settings and install the plugin from there.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed

Comment thread battery-and-power-management/README.md Outdated
Once permissions are initialized, trigger a reload or restart your Noctalia desktop interface instance to initialize the components:

killall quickshell
noctalia
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is not at all how you re-run noctalia.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed, setting the udev rule requires logout and login so it was redundant


readonly property string screenName: screen ? (screen.name ?? "") : ""
readonly property real capsuleHeight: (typeof Style !== "undefined" && typeof Style.getCapsuleHeightForScreen === "function") ? Style.getCapsuleHeightForScreen(root.screenName) : 26
readonly property real barFontSize: (typeof Style !== "undefined" && typeof Style.getBarFontSizeForScreen === "function") ? Style.getBarFontSizeForScreen(root.screenName) : 10
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Style should never be undefined. You don't need to check if it is or not.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed

readonly property string screenName: screen ? (screen.name ?? "") : ""
readonly property real capsuleHeight: (typeof Style !== "undefined" && typeof Style.getCapsuleHeightForScreen === "function") ? Style.getCapsuleHeightForScreen(root.screenName) : 26
readonly property real barFontSize: (typeof Style !== "undefined" && typeof Style.getBarFontSizeForScreen === "function") ? Style.getBarFontSizeForScreen(root.screenName) : 10
readonly property string fixedFont: (typeof Settings !== "undefined" && Settings.data?.ui?.fontFixed) ? Settings.data.ui.fontFixed : "monospace"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here neither, Style will always be defined if the imports are correct.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed

Layout.preferredHeight: 64 * Style.uiScaleRatio

color: (typeof Color !== "undefined") ? Color.mSurfaceVariant : "#313244"
radius: (typeof Style !== "undefined") ? (Style.capsuleRadius ?? Style.radiusM) : 6
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same thing as before here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed

if (root.mainWidget.batStatus === "Charging") {
return "Time to full: " + root.mainWidget.timeRemaining;
} else if (root.mainWidget.batStatus === "Discharging") {
return "Remaining: " + root.mainWidget.timeRemaining;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't these be included in the translation?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, included translations. English and Italian

}

Item {
id: customSlider
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a reason why this custom slider is needed? Why not use the noctalia slider?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The noctalia default slider was a bit too big. But I fixed the poupo width and used the default slider.

pointSize: Style.fontSizeS
color: parent.active
? ((typeof Color !== "undefined") ? Color.mOnPrimary : "#ffffff")
: (parent.containsMouse ? ((typeof Color !== "undefined") ? Color.mPrimary : "#3355ff") : ((typeof Color !== "undefined") ? Color.mOnSurfaceVariant : "#a6adc8"))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As before, Color should never be undefined.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed

"System",
"Indicator",
"Utility",
"Battery"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There's no tag called Battery

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

"New tags can be added on a case-by-case basis. If your plugin doesn't fit the existing tags, feel free to propose a new one in your pull request."
I thought that this was the correct way to propose a new tag. I'll remove this by now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No no you are correct, although I personally would have expected to see a I would like to propose to add the Battery tag in the PR description.

Maybe Battery would be a good fit as a tag since there are some plugins that have to do with battery. If you want add the tag back and I'll merge it in with the new tag.

@spiros132 spiros132 marked this pull request as draft May 21, 2026 22:01
@Piero-93
Copy link
Copy Markdown
Author

Thanks for the feedback. I'll work on them!

@Piero-93 Piero-93 marked this pull request as ready for review May 22, 2026 13:06
@Piero-93 Piero-93 marked this pull request as draft May 22, 2026 13:09
@Piero-93 Piero-93 marked this pull request as ready for review May 23, 2026 09:51
Copy link
Copy Markdown
Collaborator

@spiros132 spiros132 left a comment

Choose a reason for hiding this comment

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

Looks great, just some minor things :)

id: profileSetter
onExited: (code) => {
updatePowerProfile();
if (code !== 0) console.warn("Error writing battery threshold.");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do not use console.warn, always use Logger

return root.mainWidget.wattNum.toFixed(1) + " W";
}
if (root.mainWidget.batStatus === pluginApi?.tr("battery.status_charging")) return pluginApi?.tr("battery.time_to_full") + root.mainWidget.timeRemaining;
else if (root.mainWidget.batStatus === pluginApi?.tr("battery.status_discharging")) return pluginApi?.tr("battery.remaining") + root.mainWidget.timeRemaining;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

On these two, instead of using string concatenation, prefer to use translation interpolation.

@spiros132 spiros132 marked this pull request as draft May 24, 2026 13:28
@Piero-93 Piero-93 marked this pull request as ready for review May 29, 2026 08:33
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