Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions quickshell/Services/BatteryService.qml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ Singleton {
property bool _hasNotifiedCriticalBattery: false
property bool _hasNotifiedChargeLimit: false

// AC plug detection (onBattery) can precede the battery device reporting
// Charging by a few seconds. Timestamp plug-ins so alerts can be
// suppressed only for a short grace period instead of whenever on AC.
property var _lastPluggedInTime: 0
readonly property int _pluggedInGracePeriod: 5000

function _recentlyPluggedIn() {
return isPluggedIn && _lastPluggedInTime > 0 && Date.now() - _lastPluggedInTime < _pluggedInGracePeriod;
}

function _syncLastIsCharging() {
if (_hasKnownChargingState)
_lastIsCharging = _currentIsCharging;
Expand All @@ -139,7 +149,11 @@ Singleton {
on_HasKnownChargingStateChanged: _syncLastIsCharging()
on_CurrentIsChargingChanged: _syncLastIsCharging()

Component.onCompleted: _syncLastIsCharging()
Component.onCompleted: {
_syncLastIsCharging();
if (isPluggedIn)
_lastPluggedInTime = Date.now();
}

function sendAlert(title, message, isWarning, category, notificationType) {
if (notificationType === 1) {
Expand All @@ -163,7 +177,10 @@ Singleton {
_hasNotifiedChargeLimit = false;
}

if (isCharging) {
// Suppress low/critical alerts while charging or right after plugging
// in. A level drop long after plug-in means the battery isn't
// actually charging (e.g. faulty charger), so alert as usual then.
if (isCharging || _recentlyPluggedIn()) {
_hasNotifiedLowBattery = false;
_hasNotifiedCriticalBattery = false;
return;
Expand Down Expand Up @@ -215,6 +232,9 @@ Singleton {
}

onIsPluggedInChanged: {
if (isPluggedIn)
_lastPluggedInTime = Date.now();

if (suppressSound || !batteryAvailable) {
previousPluggedState = isPluggedIn;
return;
Expand All @@ -231,6 +251,9 @@ Singleton {
applyPowerProfile();

if (isPluggedIn) {
_hasNotifiedLowBattery = false;
_hasNotifiedCriticalBattery = false;
Comment on lines +254 to +255

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.

Clearing these flags on plug-in re-arms the alert and can reintroduce the duplicate this PR is fixing (and in a case that worked before).

Trace: alert fires at 20% (_hasNotifiedLowBattery = true) → user plugs in → these lines reset the flag and _lastPluggedInTime starts the 5s window → if the device takes longer than 5s to report Charging (the comment at L134 says it can be "a few seconds", and UPower refresh intervals can be ~30s), the next batteryLevelChanged sees isCharging === false and _recentlyPluggedIn() === false and sends "Low Battery" again. Without this reset the stale true flag would have suppressed it regardless of the grace window.

The reset isn't needed for the normal path either: onIsChargingChanged (L226-228) already clears both flags once charging actually starts. Dropping these two lines keeps the grace period as a pure suppression and makes the fix strictly safer.


const dismissLow = SettingsData.batteryLowNotificationType === 1 && SettingsData.notificationTimeoutNormal === 0;
const dismissCritical = SettingsData.batteryCriticalNotificationType === 1 && SettingsData.notificationTimeoutCritical === 0;

Expand Down
Loading