Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Your Laptop's Battery Guardian

≡ Battery Guardian ≡
 
Configuration
├ Battery logging starts at 30% SOC
╰ Cronjob scheduled  ● Yes
 
Battery Status
├ Charger connected  ● Yes
├ Charging state     ● Charging
├ Power profile      ● Performance
╰ Battery capacity   ● Normal at 76% SOC
 
Invoke battery-guardian with one of these options:
├ --batterylog Open battery log
├ --help Show description, usage and license
├ --log  Open and follow runtime log; quit with Ctrl+C + Q
├ --scheduleAuto-schedule the cronjob (every minute)
├ --test Force-run the critical suspend sequence
├ --unschedule Remove the cronjob
╰ --watchWatch guardian, periodically updated; quit with Ctrl+C
 
  Script path: /home/martin/bin/battery-guardian

Table of Contents

Purpose

Battery Guardian is a proactive, headless background daemon designed to prevent hardware degradation and data corruption caused by deep battery discharge. It monitors the battery state-of-charge (SOC) directly through the Linux kernel's sysfs interface and safely suspends or powers off the machine before an abrupt hardware power loss occurs.
Battery Guardian is particularly dedicated to Linux Systems which do not support Hibernation due to their LUKS-encrypted system partition with a separate swap partition.

User Benefits

Battery Longevity

Encourages healthy charging habits by alerting the user at a specific discharge level.
Prevents the Lithium-Ion cells from reaching chemically damaging deep-discharge states.

Data Integrity

Replaces abrupt, destructive power loss with a controlled systemctl suspend or systemctl poweroff, ensuring file systems are cleanly unmounted and data in RAM is safely managed or discarded predictably.

Zero-Privilege Execution

Operates entirely within the user space using D-Bus session delegation, requiring no sudo privileges or root Crontab modifications.

Automation

Periodically runs in the background (via Cron) without user intervention.

Localization

Fully localized interfaces for English, German, and Thai.

Native Integration

Seamlessly interfaces with GNOME's notification system and PulseAudio/PipeWire for unobtrusive user alerts.

Prerequisites

To deploy Battery Guardian, the target environment requires:

OS/Environment

Any Linux distribution, based on Debian or Ubuntu with Bourne-again shell (v4.0 or higher recommended for associative array support) or compatible, such as ZORIN OS.
GNOME desktop environment, and Wayland display driver installed and in use.

Dependencies

  • gsettings
  • notify-send (libnotify)
  • pw-play (PipeWire) or paplay (PulseAudio)
  • upower
  • cron

System Configuration

The UPower daemon must be configured to delegate low-power states to the script. Edit /etc/UPower/UPower.conf to include strictly descending values:

PercentageLow=20
PercentageCritical=10
PercentageAction=5

Functionality

The script operates as a highly resilient state-machine triggered every minute via a user cronjob:

State Polling

Reads the raw hardware state from /sys/class/power_supply/BAT1/ and checks capacity percentage, charging state, and power profiles using /sys/class/power_supply and upower.

Graceful Intervention

If the kernel reports the battery capacity level as Low or Critical, it initiates a 20-second grace period. If the charger is not connected within this window, it drops a state-persistence marker and suspends the system to RAM.

Emergency Fallback

If the user manually wakes the system from a Guardian-initiated suspend while the battery is still below the critical threshold (6%), it bypasses standard critical power action and executes an emergency systemctl poweroff to physically protect the storage controller and battery cells.

Native Desktop Notifications

  • Sends a notification of charging started and logs this event once, if the battery state changes to charging from any other state;
  • Sends desktop notifications with urgency levels and plays sound alerts accordingly, when battery level falls below warning or critical thresholds (configurable inside the script);
  • Avoids repeated alerts when already charging or battery levels are above warning threshold;

Logging

  • Logs detailed battery discharging to a dated log file in $HOME/.var/log/battery-guardian_YYYY-MM-DD.log
  • Logs runtime events to $HOME/.var/battery-guardian.log.

Battery State Caching

Uses a cache file to remember the last battery state to detect transitions to charging state and avoids repeating notifications unnecessarily. Marker files are created for charging recommendation and suspend-to-RAM trigger to prevent repeating these actions at every run triggered by Cron;

Threshold Management

All thresholds are declared as variables and can be adapted by the user.

Battery Health Recommendation

Triggers audio-visual recommendation to connect the power supply below 30% SOC, more or less at the same time, the system default battery icon silently changes to orange battery-caution. Simultaneously, a battery log is created and appended every run triggered by Cron. A new battery log will be created every calendar day.

System Suspend Announcement

Audio-visually announces to suspend the system to RAM, and initiates suspend-to-RAM after a short grace period, if no power supply is connected to prevent data loss and preserve battery health.

Forced System Shutdown Fallback

  • Warning
    Audio-visual alerts at 8% SOC every run triggered by Cron.
  • Critical
    Final audio-visual announcement at 6% SOC of the imminent system failure due to the fully drained battery. System will be forced to power off, ensuring file systems are cleanly unmounted and data in RAM is safely managed or discarded predictably.

Autosuspend Test Mode

A simulation mode (--test) that mimics critical low-battery conditions to verify the suspend logic without actually draining the battery. Bypasses capacity checks and forces a simulated trigger to verify notification and audio routing.
WARNING: This simulates a critical state and will suspend your computer after the grace period unless you connect a power supply or you cancel the test (Ctrl+C) within the short grace period.

Multi-language Support

Detects the system language $LANG and switches interface text between generic English en (fallback), generic German de, and Thai th.

Code

Structure

The script uses a modular structure with extensive variable declarations for configuration and external files for localization.
Battery information is retrieved by acpi -b and parsed to extract state, level, and estimated time remaining (ETR):

ACPI_OUTPUT=$(acpi -b) 
STATE=$(echo "$ACPI_OUTPUT" | sed -E 's/^Battery [0-9]+: ([^,]+),.*/\1/') 
LEVEL=$(echo "$ACPI_OUTPUT" | grep -oP '\d+%' | tr -d '%') 
RAW_TIME=$(echo "$ACPI_OUTPUT" | grep -oP '\d{1,2}:\d{2}(:\d{2})?' | head -n1)

Configuration and Environment

The script defines critical thresholds and paths at the top for easy adjustment.

# Configuration
THRESHOLD_WARNING=8		# % SOC battery; system failure warning threshold
THRESHOLD_CRITICAL=6	 # % SOC battery; system failure critical threshold
SAFETY_MARGIN=4			# safety margin reduction from ETR
GRACE_PERIOD=20			# seconds before autosuspend
 
# Environment
BATTERY_ID="BAT1"
AUDIO_PLAYER="/usr/bin/pw-play"

Autosuspend Gatekeeper

This function determines whether to initiate a first-strike suspend based on hardware states, or delegate to the fallback protocol based on the state-marker.

autosuspend() {
	# Check if the system recently woke from a Guardian-initiated suspend
	if [[ -f "$TRIGGER_MARKER" ]]; then
		# Delegate to the emergency fallback protocol
		handle_fallback  
	# Evaluate primary hardware state: triggers if the kernel reports Low or Critical
	elif { [[ "$LEVEL_CAPACITY_LEVEL" == "Low" ]] || [[ "$LEVEL_CAPACITY_LEVEL" == "Critical" ]]; } && [[ "$CHS" != "Charging" ]]; then
		echo "${MSG[log_auto_crit]}" >> "$RUNTIME_LOG"
		# Initiate graceful suspend with a user warning
		trigger_autosuspend
	fi
}

Emergency Fallback

This block enforces the definitive end-of-line protocol if the system is woken up in a dangerously low state and the user did not connect a power supply.

handle_fallback() {
	# ... (State localization and safe-state exit logic omitted for brevity) ...  
	# Evaluate if the battery is at or below the hard limit (6%)
	if (( LEVEL <= THRESHOLD_CRITICAL )); then
		# Blast critical visual and audio warnings
		notify-send -u critical -i battery-empty "${MSG[fallback_fail_imm]}" "$NOTIFICATION_SHUTDOWN"
		"$AUDIO_PLAYER" "$SOUND_CRITICAL" &
		echo "${MSG[log_fb_fail_imm]}" >> "$RUNTIME_LOG"
		
		# Yield 5 seconds for UI elements to render
		sleep 5
		
		# Erase the persistence marker to prevent boot-loops
		rm -f "$TRIGGER_MARKER" 2>/dev/null
		
		# Delegate the poweroff to the active user systemd session.  
		# This leverages D-Bus IPC to bypass headless cron Polkit restrictions.  
		/usr/bin/systemd-run --user --no-ask-password --quiet systemctl poweroff
		exit 0
		
	# Issue severe warnings if woke between 7% and 8%
	elif (( LEVEL <= THRESHOLD_WARNING )); then
		notify-send -u critical -i battery-caution "${MSG[fallback_fail_warn]}" "$NOTIFICATION_SHUTDOWN"
		"$AUDIO_PLAYER" "$SOUND_WARNING" &
	fi
}

Hardware Abstraction

Battery Guardian dynamically resolves hardware paths, such as using upower to verify AC connection reliability.

CHARGER_CONNECTED="$(upower -i /org/freedesktop/UPower/devices/line_power_ACAD | grep -E 'online' | grep -E 'yes|no' | cut -d\:  -f2 | cut -c 15- || echo "Unknown")"

Localization Architecture

The external localization files use an associative array MSG to map keys to localized strings, ensuring easy translation updates. The logic probes presence of the fallback localization file and aborts if it is missing, prompting the user to ensure that language files are in same directory as the script.

declare -A MSG
SYSTEM_LANG="${LANG:0:2}"
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
SCRIPT_NAME="$(basename "$SCRIPT_PATH")"
 
LOCALE_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.${SYSTEM_LANG}"
FALLBACK_FILE="${SCRIPT_DIR}/${SCRIPT_NAME}.en"
 
if [[ -f "$LOCALE_FILE" ]]; then
 source "$LOCALE_FILE"
elif [[ -f "$FALLBACK_FILE" ]]; then
 source "$FALLBACK_FILE"
else
	printf "\n%b%b%b\n" "${FORMATTING_HEADER}" ":: Battery Guardian ::" "${FORMATTING_RESET}"
	printf "\n%b%b%b%b\n" "$INDICATOR_FAIL" "$FORMATTING_BOLD" "Localization error" "${FORMATTING_RESET}"
 printf "  Missing fallback file '$FALLBACK_FILE'.\n"
 printf "  Ensure language files are in same directory as script.\n\n"
 exit 1
fi

The default fallback language is generic English [en].

Installation

  1. Download
    Extract the battery-guardian bash script and the associated language files (.en, .de, .th) into a persistent user directory (e.g., ~/.local/bin/).

  2. Permissions
    Assign execution permissions to the script:
    chmod +x battery-guardian.

  3. Dependencies
    Ensure upower, acpi, and libnotify are installed via your package manager (e.g., sudo apt install upower acpi libnotify-bin).

  4. Scheduling
    Install the cron dispatcher by executing
    ./battery-guardian --schedule.

  5. Verification
    Verify the installation by checking the status:
    ./battery-guardian.

Usage

The script is designed to run autonomously. Manual invocation provides status readouts and management options.

  • ./battery-guardian
    Displays the current charging state, battery health, and configuration.

  • ./battery-guardian --schedule
    Installs the minute-tick Crontab entry.

  • ./battery-guardian --unschedule
    Removes the script from thshe Crontab.

  • ./battery-guardian --log
    Opens the runtime log in the default pager.

  • ./battery-guardian --batterylog
    Opens the detailed battery discharge tracking log in the default pager.

  • ./battery-guardian --test
    Bypasses capacity checks and forces a simulated trigger to verify notification and audio routing.
    WARNING: This simulates a critical state and will suspend your computer after the grace period unless you connect a power supply or you cancel the test (Ctrl+C) within the short grace period.

Troubleshooting

If the script fails to execute or notifications do not appear, utilize these debugging techniques:

Execution Tracing (set -x)

Insert set -x at the top of the bash script (below the shebang). This forces the shell to print every command and its expanded arguments to standard output before execution, allowing you to trace exactly where a logic gate fails.

Error Handling Modes (set -euo pipefail)

Graceful Handling (Default)

The script currently allows non-zero exit codes (like a failed cat command if a sysfs node is temporarily busy) to pass quietly without terminating the script.

Strict Handling

Uncommenting set -euo pipefail forces the script to abort immediately if any command fails (-e), if an undefined variable is referenced (-u), or if a command within a pipeline fails (-o pipefail). Use this strictly for debugging syntax or pathing errors.

Script fails silently

Ensure cron is running and you have the correct permissions. Check the log file: $HOME/.var/log/battery-guardian.log.

Audio Failure

Verify that pw-play is installed and the ALSA/PipeWire daemon is active in the user session.

Permission Denied on Shutdown or Suspend-to-RAM

Ensure the script is running within the user's crontab and not sudo crontab. The /usr/bin/systemd-run --user command requires the context of an active desktop seat to bypass Polkit restrictions.

Help

Executing ./battery-guardian --help outputs the embedded, localized Man-page style documentation. It details the script's core purpose, available flags, environment variables, disclaimer, and MIT License information. If invoked with invalid options, the script defaults to printing the standard status output alongside the available command syntax.

Appendix

Disclaimer

Use at your own risk. This script may not prevent battery damage or data loss on all systems due to variations and limitations in hardware and operating system. Test thoroughly; your system may unexpectedly suspend or power-off.
This script is provided “as is”; there is NO WARRANTY at all. This is free software: you are free to modify it to your needs and redistribute it (see MIT Licence).
Due to ongoing development, this documentation might not reflect latest minor code changes.

Author

Copyright (c) 2026 RML Tec Dev
Contributions and feedback are welcome via rmltecdev@pm.me

License

MIT Licence

About

Suspend system to RAM to protect user data and preserve battery health on GNOME/Wayland systems. Includes CLI-based management for status checks and installation.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages