8

I want to make a tool that alerts when battery is below 40% or above 60% so I can start/stop charging accordingly.

I know some tools alert when battery is low, that's not enough. Is there a tool that does it when bat is high?

I would like to write a script - either shell or python, that can do this. I know the command for checking bat status:

upower -i /org/freedesktop/UPower/devices/battery_BAT0

But don't know how to "listen" to the battery so that whenever it's status changes I can automatically perform an action. A link to docs will be nice, a tutorial even better.

2 Answers2

20

If I understood your question correctly, you want to check the battery status in every X interval of time. It's easiest way that you run it in the background, inside a while loop:

while true
 do
    # Any script or command
    sleep [number of seconds]
 done

And if you want to run the script on startup and from then on every 5 minutes, so the construction would be:

  • Add a line in /etc/rc.local to call your script (your battery_status.sh) + "&" to make it exit.
  • Add the commands you want to run in the battery_status.sh to execute in a while loop (inside battery_status.sh).

Note that if you want to run it from cron, you need to set the full path, since cron runs with a limited set of environment variables.

Example

#!/bin/bash
while true
do
    battery_level=`acpi -b | grep -P -o '[0-9]+(?=%)'`
    if [ $battery_level -ge 60 ]; then
       notify-send "Battery is above 60%!" "Charging: ${battery_level}%"    
    elif [ $battery_level -le 40 ]; then
       notify-send "Battery is lower 40%!" "Charging: ${battery_level}%"    
    fi
sleep 300 # 300 seconds or 5 minutes

done

Save this file by named battery_status.sh in your favorite location (mine is home directory) and add this line in /etc/rc.local file (in my example, just replace your battery_status.sh location by /home/username/):

sh /home/username/battery_status.sh &

That's all. Reboot and see the magic.

If you don't have instell acpi, just install that using sudo apt-get install acpi

One BONUS

If you want to run make this script responsible with AC adapter,you don't need an extra variable to check that run for one time. If your AC adapter is plugged and battery charged above 60% it makes a alert to "Unplug your adapter!" until you don't unplug that. If alert tell you

                             enter image description here

just unplug AC adapter then you will see the message alert don't appear again until your battery charge down to 40%. Then another message alert and tell you

                             enter image description here

if you don't unplug AC adapter on above 60% or don't plug AC adapter below 40%, the alert message shown every 5 minutes (you can adjust that for yourself in code, see sleep [seconds]) show and Will remind you.

 #!/bin/bash
while true
do
    export DISPLAY=:0.0
    battery_level=`acpi -b | grep -P -o '[0-9]+(?=%)'`
    if on_ac_power; then
        if [ $battery_level -ge 60 ]; then
            notify-send "Battery charging above 60%. Please unplug your AC adapter!" "Charging: ${battery_level}% "
            sleep 20
            if on_ac_power; then
                gnome-screensaver-command -l   ## lock the screen if you don't unplug AC adapter after 20 seconds
            fi
         fi
    else
         if [ $battery_level -le 40 ]; then
            notify-send "Battery is lower 40%. Need to charging! Please plug your AC adapter." "Charging: ${battery_level}%"
            sleep 20
            if ! on_ac_power; then
                gnome-screensaver-command -l   ## lock the screen if you don't plug AC adapter after 20 seconds
            fi
         fi
    fi

    sleep 300 # 300 seconds or 5 minutes
done

αғsнιη
  • 35,660
  • I'm getting the following error when running the script.

    " ./battery-monitor.sh: line 8: [: too many arguments"

    OS: Ubuntu 20.04

    – Aneeez Jan 18 '21 at 05:19
  • @Aneeez Hi, please copy and paste the code into https://shellcheck.net to verify the code – αғsнιη Jan 18 '21 at 05:39
  • When I run acpi -b in terminal, I get the following output.

    Battery 0: Discharging, 94%, 09:19:19 remaining Battery 1: Discharging, 0%, rate information unavailable

    So, acpi -b | grep -P -o '[0-9]+(?=%)' would give the output 93 0. I think that's the problem. Is there anyway I can filter out only battery 0? (I don't know why this command shows 2 batteries)

    – Aneeez Jan 18 '21 at 06:33
  • 1
    Got it. acpi -b | grep -P -o -m1 '[0-9]+(?=%)' will return only the battery 0 value. – Aneeez Jan 18 '21 at 06:59
9

You cannot write a tutorial to create one script. That would be similar to a general python tutorial, which you can find on many places on the web.

What I did is write a small script, with comments to explain what I did, to check battery charge every 10 seconds. That might be overdone. Change the line time.sleep(10) to set the interval (in seconds). The commands can be changed of course, I made it send a notification as it is.

enter image description here

I made it run the message, or whatever command you give it, only run once per incident, so if charge exceeds 80% for example, the command only runs once, until the next time it exceeds defined values.

How to use

The script is meant to run in the background; copy it into an empty file, save it as check_battery.py make it executable and make it run on login: Dash > Startup Applications > Add, add the command:

/path/to/check_battery.py

The script

#!/usr/bin/env python3

import subprocess
import time

def read_status():
    """
    This function reads the output of your command, finds the line with
    'percentage' (line 17, where first line = 0) and reads the figure
    """
    command = "upower -i $(upower -e | grep BAT) | grep --color=never -E percentage|xargs|cut -d' ' -f2|sed s/%//"
    get_batterydata = subprocess.Popen(["/bin/bash", "-c", command], stdout=subprocess.PIPE)
    return get_batterydata.communicate()[0].decode("utf-8").replace("\n", "")

def take_action():
    """
    When the charge is over 60% or below 40%, I assume the action does
    not have to be repeated every 10 seconds. As it is, it only runs
    1 time if charge exceeds the values. Then only if it exceeds the
    limit again.
    """
    # the two commands to run if charged over 80% or below 60%
    command_above = "notify-send 'charged over 80'%"
    command_below = "notify-send 'charged below 80%'"
    times = 0
    while True:
        charge = int(read_status())
        if charge > 60:
            if times == 0:
                subprocess.Popen(["/bin/bash", "-c", command_above])
                times = 1
        elif charge < 40:
            if times == 0:
                subprocess.Popen(["/bin/bash", "-c", command_below])
                times = 1
        else:
            times = 0
        time.sleep(10)

take_action()
αғsнιη
  • 35,660
Jacob Vlijm
  • 83,767