Working on i3, you have to define a lot of functionalities yourself, or install the program that does it for you. One of those functionalities is to be alerted in case of a low battery. Let’s explore two options, the very simple and the very customizable, so that you can choose what fits you best. You’ll learn more about linux going the custom route, but it will take longer to get running.
Note: This post was written to work on Arch Linux, and you may have to adapt it to your distribution to get it to work.
Option 1: Installing cbatticon ๐
If you don’t want to bother creating the functionality yourself, you just need to install and
run cbatticon
. Installing this tiny program is as easy as
$ pacman -S cbatticon
Once installed, run it on i3 startup by adding the following line to your i3 configuration file
(~/.config/i3/config
).
exec cbatticon
You’ll see a new little icon in your tray bar with the battery status, and will get notifications when the battery gets low, along with multiple other ones, like battery in charge or discharge.
Option 2: Creating your custom notification ๐
Since I did not want an extra tray icon, and that some of cbatticons
illustrations were not to my
liking, I decided to go the custom route to completely get rid of it, keeping only the charge
percentage in my status bar. I also wanted to understand how I could read those numbers to
understand my system a little better.
You will learn a few things about your linux system:
- The
notify-send
command, which allows you to send notifications to your desktop - The
/sys/class/power_supply/BAT0
folder, containing information about the battery of your laptop - Systemd one shot services, which allow you to run commands or program once as services
- Systemd timers, which let you run those one shot services at given intervals
- Systemd services for users, that let any user create and manage their own services.
Reading information for the battery ๐
First, let’s see how we can get the information about the charge of the battery.
There is the acpi
command that allows you to get this information easily from the command line,
but it is more suited for human users, and would need us to parse its output to get the percentage
of battery remaining.
$ acpi
Battery 0: Charging, 31%, 01:24:19 until charged
However, we can easily get this information by simply reading it in a directory that is populated
nicely by the kernel for us to get information about the system. All that is related to the battery
should live in /sys/class/power_supply/BAT0
(the BAT0
part may be different on your machine,
ensure you adapt this part to your system).
Let’s see what’s in there:
$ ls /sys/class/power_supply/BAT0
alarm
capacity
capacity_level
charge_full
charge_full_design
charge_now
current_now
cycle_count
device
hwmon3
manufacturer
model_name
power
present
serial_number
status
subsystem
technology
type
uevent
voltage_min_design
voltage_now
The files that jump out to me right away are charge_now
, charge_full
and status
.
# /sys/class/power_supply/BAT0/status
Discharging
# /sys/class/power_supply/BAT0/charge_now
5003000
# /sys/class/power_supply/BAT0/charge_full
6277000
Right now, I’m at 79% battery, unplugged, so these values check out (5003000 / 6277000 = 0.79). But
I’d rather not do any calculations on my own… I looked at the other files, and found the charge
percentage in capacity
# /sys/class/power_supply/BAT0/capacity
79
I checked that this values did decrease with the battery and it does. All good !
We can then start a script to display the remaining battery when it is discharging and under 20%. I
installed this script in /usr/local/bin/alert-battery
#!/bin/bash
bat_files="/sys/class/power_supply/BAT0"
bat_status=$(cat "${bat_files}/status")
capacity=$(cat "${bat_files}/capacity")
echo "${capacity}"
if [[ "${bat_status}"=="Discharging" && ${capacity} -le 20 ]]; then
echo "Battery alert - ${capacity}%"
fi
When we run the script, modifying the capacity threshold to trigger the output
$ /usr/local/bin/alert-battery
Battery alert - 79%
Creating a desktop notification ๐
Now that we have the conditions for sending a notification encoded, let’s send it to the desktop
using the notify-send
command. Let’s try it out first
$ notify-send "Amazing notification"
Pretty easy !
We can also give a description of the notification, and an icon
$ notify-send \
"Amazing notification" \
"A very good description of the notification" \
--icon=notification-icon.png
Let’s add a notification to our script with a low battery
icon (which I like to put inside
/usr/local/share/icons
) and the amount of battery remaining:
#!/bin/bash
bat_files="/sys/class/power_supply/BAT0"
bat_status=$(cat ${bat_files}/status)
capacity=$(cat "${bat_files}/capacity")
if [[ ${bat_status}=="Discharging" && ${capacity} -le 20 ]]; then
echo "Battery alert - ${capacity}%"
notify-send \
--icon=/usr/local/share/icons/battery_low_dark.png \
"Low battery" \
"Only ${capacity}% battery remaining"
fi
The result looks quite nice and simple (well the percentage does not match…)
Running the script every 5 minutes ๐
In ArchLinux, there is no crontab by default to run scripts periodically. To run such jobs, you need to create a systemd timer which will trigger a systemd service that will run once when called. While this is more steps than a simple cronjob, it allows for easier debugging and management, since you can run the service manually at any time. You also get a saner environment and log management for free.
Let’s start by creating the service and running it manually. We are going to create a new service
which will be user managed, since it only applies to the current user’s desktop. Create an
empty file for this service using the systemctl edit
, to make it in the right place
$ systemctl edit --user --force --full alert-battery.service
This will open your editor, which you can fill with the following service definition:
[Unit]
Description=Alert in case of low remaining battery
[Service]
Type=oneshot
ExecStart=/usr/local/bin/alert-battery
[Install]
WantedBy=graphical.target
Once you save and quit, this file will be written to ~/.config/systemd/user/alert-battery.service
.
The two most important statements of this file for us are Type=oneshot
, which ensures that the
script is run once then quits, and ExecStart=/usr/local/bin/alert-battery
letting our service
know which script to run.
You can then make the system pick up this new service by running
$ systemctl --user daemon-reload
And run the service manually (make sure to modify the low battery threshold for the notification to appear)
$ systemctl --user start alert-battery.service
You should see the notification appear, and you can see the status of your service by running
$ systemctl --user status alert-battery.service
โ alert-battery.service - Alert in case of low remaining battery
Loaded: loaded (/home/user/.config/systemd/user/alert-battery.service; disabled; vendor preset: enabled)
Active: inactive (dead) since Tue 2020-12-29 11:35:51 CET; 4min 33s ago
Process: 2177341 ExecStart=/usr/local/bin/alert-battery (code=exited, status=0/SUCCESS)
Main PID: 2177341 (code=exited, status=0/SUCCESS)
dรฉc. 29 11:35:51 machine systemd[2078]: Starting Alert in case of low remaining battery...
dรฉc. 29 11:35:51 machine systemd[2078]: alert-battery.service: Succeeded.
dรฉc. 29 11:35:51 machine systemd[2078]: Finished Alert in case of low remaining battery.
Now that we are confident our service works, we can create the timer to trigger it every 5 minutes.
Let’s create the file manually. Create ~/.config/systemd/user/alert-battery.timer
, with the
following configuration
[Unit]
Description=Check on battery every 5 minutes to warn the user in case of low battery
[Timer]
OnActiveSec=5m
OnUnitActiveSec=5m
[Install]
WantedBy=timers.target
The option OnActiveSec=5m
configures our timer to first trigger 5 minutes after our service
started, and OnUnitActiveSec=5m
to activate again 5 minutes after our latest execution.
Start your service, and enable it to make sure that it will run after rebooting:
$ systemctl --user service start alert-battery.timer
$ systemctl --user service enable alert-battery.timer
Then list all of your activated timers to confirm that it is running
$ systemctl --user list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2020-12-27 21:14:54 CET 3min 20s left n/a n/a alert-battery.timer alert-battery.service
1 timers listed.
Pass --all to see loaded but inactive timers, too.
And you’re good to go ! Make sure to notice next time you’re low on battery if you do get the notification, ensuring that everything is running smoothly.
What’s next ? ๐
From this example, we can extract a method on how to run programs periodically. You could run other recurring tasks, like backups or updates. You just need to create a working script, the service running it and finally trigger the service with a timer.
While this takes more time to create, you can customize it however you want. You could send yourself an email when battery is low (why would you want to do that is an other question), reduce brightess, or even stop some processes. If you think this should trigger something else, you can code it yourself and get it done.
How can I help ? ๐
I’m going to make more examples to improve your experience with customized window managers. There often are some quality of life processes that we expect from our desktop experience but don’t always take the time to create because it could be a lot of research. Those tools are what desktop environment brings on top of the window manager, for ease of use.
What would you like to learn about next ? Drop me a message on Twitter (@mrngilles) and let me know.