TJKeller.xyz lowbat: a Minimal Battery Level Monitor Daemon, Written in C
August 5, 2024
Tags: Linux My Software

lowbat

lowbat is a tiny (~150 lines) low battery level warning daemon for Linux written in C. It will notify the user when the battery level falls below a certain threshold. It is released by me under the MIT license.

Its only exotic dependency is libnotify, the C equivelent of notify-send. You will also need some libnotify compatible notification daemon running on your system. I assume you have some idea of what this means since this software is completely useless unless you set up your own desktop environment already. Dunst is a pretty good option.

Other than that, it just uses dirent for dynamic battery detection, plus the usual stdio etc.

You can clone or view the git repository here:
https://git.tjkeller.xyz/lowbat

Simply compile lowbat.c using make and install it somewhere in your path. Then call the executable in your xinitrc or xprofile.

How it works

lowbat is configured by default to check the system battery level every 3 seconds. It is able to detect each battery installed and the system, and calculate the remaining capacity of all batteries combined. Batteries added to the system during runtime will also be detected.

Each battery should have a capacity_full and capacity_now attribute. To get the correct capacity, you simply sum the attributes and divide now over full.

The daemon will send a warning notification the user when their battery level drops below 20% while the batteries are discharging, and another critical warning at 5%.

The current battery level will also be displayed in stdout.

Batteries are detected as power supplies using the Linux sysfs-class-power psuedo-filesystem (which you can learn more about here). This is the main reason that dirent is a dependency. Batteries are found by searching for unique attributes ‘files’ that other ‘power supply’ devices don’t have. Absolutely no fanciness is involved, lowbat simply loops over the directories just as God intended.

There are no runtime flags or configuration files. Simply modify the source code to suit your needs.

shlowbat

shlowbat (or sh lowbat), as the name would imply, is a functionally identical version of lowbat that is written in posix shell. It is also hereby released, by me, under the MIT license.

Since it is so incredibly small, here is the entire source code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh
psupath=/sys/class/power_supply
warning_level=0  # 0 = not warned, 1 = warned, 2 = warned critical

sum_props() {
	for prop in $(cat $psupath/*/$1); do sum=$(( $sum + $prop )); done
	echo $sum
}
is_discharging() { cat $psupath/*/status | grep "Discharging" > /dev/null ; }

while true; do
	capacity=$(( $(sum_props energy_now) * 100 / $(sum_props energy_full) ))

	if is_discharging; then
		if   [ $capacity -le  5 ] && [ $warning_level -lt 2 ]; then
			notify_send "Critical Low Battery Warning" "${capacity}% remains"
		elif [ $capacity -le 20 ] && [ $warning_level -lt 1 ]; then
			notify_send "Low Battery Warning" "${capacity}% remains"
		fi
	else
		warning_level=0
	fi

	printf "\r%d%%" $capacity
	sleep 3
done

As you can see, this version is tiny and does not require compilation. I wrote this to be posix compliant, but rewriting it in bash may be advantageous since it may be possible to eliminate some of the process substitution and usage of cat.

It also does happen to be massively slower than the C version in case you were wondering, but you really should’ve been able to gather that from the name as well. The cpu time used by shlowbat in a basic benchmark is ~70 times that of lowbat. Since this is a background daemon that runs at all times, I would much rather use the version that uses 70x less cpu time.

FreeBSD / OpenBSD?

I currently have no plans to port lowbat to BSD systems, as I do not use any BSD’s on any laptops. However, I’m sure it would be extremely simple. I would be happy to merge any diff that uses the #if defined([__os__]) directive around the os specific functions and libraries to add support.

On the BSD’s, the most logical way to read battery info is through the sysctl library that comes as standard with BSD systems. Here, I even found the relevant man pages to learn about this library on both FreeBSD and OpenBSD (although I read neither). You’re welcome!

Please see my contact page to find out how you can submit a diff to me.

Inspiration

This incredibly bad and overly complicated solution by Eric Murphy. Why use cron for this? Hard systemd dependence with udev hooks? Temporary files? Dbus? Running with root permissions? For a low battery notification? This solution seriously sucks!

No shade at Eric though, he is a webdev afterall :)

But really, this implementation is so overly complex that one has to wonder how somebody competent enough to put that all together could also be dumb enough to think it is the most straightforward method. Truly the sort of thing that could only be dreamed up by the mind of an Arch Linux user.