DEV Community

loading...

Linux Battery Percentage Correction

Sam Erickson
Hello, I am a computer science student at Vancouver Island University. I have a passion for teaching others about things that I am passionate about.
Originally published at samerickson.me on ・2 min read

The problem

The problem I had in my initial arch linux installation on my macbook pro, was the battery. Fully charged it was showing 84%, and if I then immediately booted into macOS, it would show 100%. So what is going on there?

The fix

I did some digging, and found lots of forms where users were asking the same question, but with no real answer as to why or any solution. I started looking into how my computer was getting all the battery information.

In i3blocks, the command was cat /sys/class/power_supply/BAT0/capacity. Naturally, I cd into that directory to get a look at what is going on. I found the following files:

.
├── alarm
├── capacity
├── charge_full
├── charge_full_design
├── charge_now
├── current_avg
├── current_now
├── cycle_count
├── device -> ../../../ACPI0002:00
├── manufacturer
├── model_name
├── power/
├── present
├── status
├── subsystem -> ../../../../../../../../../../class/power_supply
├── technology
├── temp
├── type
├── uevent
├── voltage_min_design
└── voltage_now
Enter fullscreen mode Exit fullscreen mode

The four files I was interested in are:

  • capacity
  • charge_full
  • charge_full_design
  • charge_now

When you divide charge_now by charge_full_design you get capacity, and when you divide charge_now by charge_full you get what macOS displays as the current battery level.

So I changed the command in my i3blocks config to run the following script:

#!/bin/bash

charge_current="$(cat /sys/class/power_supply/BAT0/charge_now)"
charge_full="$(cat /sys/class/power_supply/BAT0/charge_full)"
current=$(echo "scale=2;$charge_current/$charge_full" | bc | cut -c 2-)

echo "$current%"
Enter fullscreen mode Exit fullscreen mode

I will admit it is a little bit slow, and I later read the i3blocks documentation and found their suggested battery module code:

#!/bin/bash

BAT=$(acpi -b | grep -E -o '[0-9][0-9]?%')

# Full and short texts
echo "Battery: $BAT"
echo "BAT: $BAT"

# Set urgent flag below 5% or use orange below 20%
[ ${BAT%?} -le 5 ] && exit 33
[ ${BAT%?} -le 20 ] && echo "#FF8000"

exit 0
Enter fullscreen mode Exit fullscreen mode

Which works just as well, but with no completion time difference, and one more dependency. This would have saved me a little bit of time, but I am happy with my fix for now.

Why is this the case?

Different versions of batteries have different strengths and weaknesses, in the case of rechargeable lithium-ion batteries, like those found in laptops, they are only designed for a certain amount of cycles before they are used up. With each charge, you lose effectiveness, the max charge from the factory does not match the actual max charge. This causes linux to provide an incorrect battery reading as the battery degrades.

This is helpful when you want to know the current life state of your battery,
but not helpful if you want to know how much time you have before you need to
recharge.

Summary

There are lots of ways to check the battery status in linux, you just need to
find the one that suits your needs.

Discussion (2)

Collapse
nealmcb profile image
Neal McBurnett

Note that the "let" command in bash lets you avoid the need for bc. And here's how to make a function out of the code, so it is a bit more efficient, and is easy to run on even a chromebook. :)

function battery_level {                                                                                                                            
 charge_current="$(cat /sys/class/power_supply/BAT0/charge_now)"                                                                                    
 charge_full="$(cat /sys/class/power_supply/BAT0/charge_full)"                                                                                      
 let "current=100*$charge_current/$charge_full"                                                                                                     
 echo "$current%"                                                                                                                                   
}                                                                                                                                                   
Collapse
samerickson profile image
Sam Erickson Author

Thanks for the tip. I had no idea you could do arithmetic using let.