
Sometimes while working on my laptop, when not connected to the AC power source, I get notified when power is critically low. I wrote this script which notifies me for every steps of 10% change in power level (for more details, please refer to the script code). For notification I preferred using zenity to notify-send as I frequently miss out to check such toast notifications while busy.
UPDATE: Now, also shows charging/discharging rates.
Script name: /home/username/bin/notification_battery_discharge.sh ;
Contents:
#!/bin/bash
notification_battery_discharge.sh (v1.0)
declare __SCRIPT_NAME="${0##*/}"
Configuration options:
declare -i __DISPLAY_CHARGING_NOTIFICATIONS=1 ## where, 1=true, 0=false
declare __FILENAME="/sys/class/power_supply/BAT1/uevent"
declare __FILE_LOG="/tmp/${__SCRIPT_NAME}.log"
declare -i __FLAG__BATTERY_CHARGING_STATE=0 ## 0=Discharging, 1=Charging
declare -i __FLAG__BATTERY_CHARGING_STATE_TOGGLED=0 ## 0=notToggled, 1=toggled during current iteration
declare -i __FLAG__STARTING_BATTERY_PERCENTAGE_SET=0
declare -ri __TIME_SLEEP_SECONDS=60
declare __CURR_POWER_SUPPLY_STATUS=""
declare -i __CURR_BATTERY_PERCENTAGE_LEVEL=0
declare -i __STARTING_BATTERY_PERCENTAGE_LEVEL=0
declare -i __DIFF_BATTERY_PERCENTAGE_LEVEL=0
declare -i __LOWER_LEVEL=0
declare -i __UPPER_LEVEL=0
declare -i __SECONDS_STARTING=${SECONDS}
declare -i __SECONDS_NOW=0
declare -i __SECONDS_DIFF=0
declare __RATE_OF_DISCHARGE_PER_MIN=0
declare __RATE_OF_DISCHARGE_PER_HOUR=0
declare -A __LEVEL
__LEVEL["90-100"]=0 ## where, 1=true; 0=false
__LEVEL["80-90"]=0
__LEVEL["70-80"]=0
__LEVEL["60-70"]=0
__LEVEL["50-60"]=0
__LEVEL["40-50"]=0
__LEVEL["30-40"]=0
__LEVEL["20-30"]=0
__LEVEL["10-20"]=0
__LEVEL["0-10"]=0
declare -A __LEVEL_MSG_DISPLAYED
__LEVEL_MSG_DISPLAYED["90-100"]=0 ## where, 1=true; 0=false
__LEVEL_MSG_DISPLAYED["80-90"]=0
__LEVEL_MSG_DISPLAYED["70-80"]=0
__LEVEL_MSG_DISPLAYED["60-70"]=0
__LEVEL_MSG_DISPLAYED["50-60"]=0
__LEVEL_MSG_DISPLAYED["40-50"]=0
__LEVEL_MSG_DISPLAYED["30-40"]=0
__LEVEL_MSG_DISPLAYED["20-30"]=0
__LEVEL_MSG_DISPLAYED["10-20"]=0
__LEVEL_MSG_DISPLAYED["0-10"]=0
function setBatteryChargingStatusFlag() {
declare -i power_supply_status_initial=${__FLAG__BATTERY_CHARGING_STATE}
if [[ "${__CURR_POWER_SUPPLY_STATUS}" == "Discharging" ]] ; then
__FLAG__BATTERY_CHARGING_STATE=0
fi
if [[ "${__CURR_POWER_SUPPLY_STATUS}" == "Charging" || "${__CURR_POWER_SUPPLY_STATUS}" == "Full" ]] ; then
__FLAG__BATTERY_CHARGING_STATE=1
fi
declare -i power_supply_status_latter=${__FLAG__BATTERY_CHARGING_STATE}
if [[ ${power_supply_status_initial} -ne ${power_supply_status_latter} ]] ; then
__FLAG__BATTERY_CHARGING_STATE_TOGGLED=1
else
__FLAG__BATTERY_CHARGING_STATE_TOGGLED=0
fi
}
function checkLevel() {
declare myLevel="${1}" ## $1 is required to be level string e.g. "80-90"
if [[ ${__LEVEL[${myLevel}]} -eq 1 && ${__LEVEL_MSG_DISPLAYED[${myLevel}]} -eq 0 ]] ; then
if [[ "${__CURR_POWER_SUPPLY_STATUS}" != "Discharging" ]] ; then
if [[ ${__DISPLAY_CHARGING_NOTIFICATIONS} -eq 1 ]] ; then
## Charging notification:
zenity --info --title="Battery Status: ${__CURR_POWER_SUPPLY_STATUS} ($(date +%Y-%m-%d' '%H:%M))" --text="The power supply is currently ${__CURR_POWER_SUPPLY_STATUS} at level ${__CURR_BATTERY_PERCENTAGE_LEVEL}%. Charging rate is ${__RATE_OF_DISCHARGE_PER_HOUR}%/hour (${__RATE_OF_DISCHARGE_PER_MIN}%/minute)." &
fi
else
## Discharging notification:
zenity --error --title="Battery Status: ${__CURR_POWER_SUPPLY_STATUS} ($(date +%Y-%m-%d' '%H:%M))" --text="The power supply is currently ${__CURR_POWER_SUPPLY_STATUS} at level ${__CURR_BATTERY_PERCENTAGE_LEVEL}%. Discharging rate is ${__RATE_OF_DISCHARGE_PER_HOUR}%/hour (${__RATE_OF_DISCHARGE_PER_MIN}%/minute)." &
fi
__LEVEL_MSG_DISPLAYED[${myLevel}]=1
fi
if [[ ${__LEVEL[${myLevel}]} -eq 0 && ${__LEVEL_MSG_DISPLAYED[${myLevel}]} -eq 1 ]] ; then
__LEVEL_MSG_DISPLAYED[${myLevel}]=0
fi
}
Execution start:
while : ; do
while read ; do
if [[ "${REPLY}" =~ ^POWER_SUPPLY_STATUS=(.*)$ ]] ; then
__CURR_POWER_SUPPLY_STATUS=${BASH_REMATCH[1]}
fi
## Set __CURR_BATTERY_PERCENTAGE_LEVEL :
if [[ "${REPLY}" =~ ^POWER_SUPPLY_CAPACITY=(.*)$ ]] ; then
__CURR_BATTERY_PERCENTAGE_LEVEL=${BASH_REMATCH[1]}
fi
done < "${__FILENAME}"
if [[ ${__FLAG__STARTING_BATTERY_PERCENTAGE_SET} -eq 0 ]] ; then
__FLAG__STARTING_BATTERY_PERCENTAGE_SET=1
__STARTING_BATTERY_PERCENTAGE_LEVEL=${__CURR_BATTERY_PERCENTAGE_LEVEL}
fi
## Now, since we have __CURR_POWER_SUPPLY_STATUS variable set, we call function:
setBatteryChargingStatusFlag
if [[ ${__FLAG__BATTERY_CHARGING_STATE_TOGGLED} -eq 1 ]] ; then
__SECONDS_STARTING=${SECONDS}
__STARTING_BATTERY_PERCENTAGE_LEVEL=${__CURR_BATTERY_PERCENTAGE_LEVEL}
fi
if [[ "${__CURR_POWER_SUPPLY_STATUS}" == "Discharging" ]] ; then
__SECONDS_NOW=${SECONDS}
let "__SECONDS_DIFF = __SECONDS_NOW - __SECONDS_STARTING"
if [[ ${__SECONDS_DIFF} -eq 0 ]] ; then
__SECONDS_DIFF=1 ## Assign minimum value of __SECONDS_DIFF as 1 instead of zero.
fi
## For Discharging, starting percentage is higher than later percentage, so:
let "__DIFF_BATTERY_PERCENTAGE_LEVEL = __STARTING_BATTERY_PERCENTAGE_LEVEL - __CURR_BATTERY_PERCENTAGE_LEVEL"
__RATE_OF_DISCHARGE_PER_MIN="$( echo "scale=3; ( ${__DIFF_BATTERY_PERCENTAGE_LEVEL} * 60 ) / ${__SECONDS_DIFF}" | bc )"
__RATE_OF_DISCHARGE_PER_HOUR="$( echo "scale=3; ( ${__DIFF_BATTERY_PERCENTAGE_LEVEL} * 60 * 60 ) / ${__SECONDS_DIFF}" | bc )"
# echo "$(date +%H:%M:%S) :: Discharging :: __RATE_OF_DISCHARGE_PER_MIN=$__RATE_OF_DISCHARGE_PER_MIN , __RATE_OF_DISCHARGE_PER_HOUR=$__RATE_OF_DISCHARGE_PER_HOUR" >> "$__FILE_LOG"
fi
if [[ "${__CURR_POWER_SUPPLY_STATUS}" == "Charging" ]] ; then
__SECONDS_NOW=${SECONDS}
let "__SECONDS_DIFF = __SECONDS_NOW - __SECONDS_STARTING"
if [[ ${__SECONDS_DIFF} -eq 0 ]] ; then
__SECONDS_DIFF=1 ## Assign minimum value of __SECONDS_DIFF as 1 instead of zero.
fi
## For Charging, starting percentage is lower than later percentage. Also,
## for charging, the __RATE_OF_DISCHARGE_PER_MIN and __RATE_OF_DISCHARGE_PER_HOUR
## actually reflect rate of charging instead.
let "__DIFF_BATTERY_PERCENTAGE_LEVEL = __CURR_BATTERY_PERCENTAGE_LEVEL - __STARTING_BATTERY_PERCENTAGE_LEVEL"
__RATE_OF_DISCHARGE_PER_MIN="$( echo "scale=3; ( ${__DIFF_BATTERY_PERCENTAGE_LEVEL} * 60 ) / ${__SECONDS_DIFF}" | bc )"
__RATE_OF_DISCHARGE_PER_HOUR="$( echo "scale=3; ( ${__DIFF_BATTERY_PERCENTAGE_LEVEL} * 60 * 60 ) / ${__SECONDS_DIFF}" | bc )"
# echo "$(date +%H:%M:%S) :: Charging :: __RATE_OF_CHARGING_PER_MIN=$__RATE_OF_DISCHARGE_PER_MIN , __RATE_OF_CHARGING_PER_HOUR=$__RATE_OF_DISCHARGE_PER_HOUR" >> "$__FILE_LOG"
fi
__LOWER_LEVEL=90
__UPPER_LEVEL=100
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=80
__UPPER_LEVEL=90
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=70
__UPPER_LEVEL=80
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=60
__UPPER_LEVEL=70
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=50
__UPPER_LEVEL=60
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=40
__UPPER_LEVEL=50
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=30
__UPPER_LEVEL=40
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=20
__UPPER_LEVEL=30
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=10
__UPPER_LEVEL=20
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
__LOWER_LEVEL=0
__UPPER_LEVEL=10
if [[ ${__CURR_BATTERY_PERCENTAGE_LEVEL} -ge ${__LOWER_LEVEL} && ${__CURR_BATTERY_PERCENTAGE_LEVEL} -lt ${__UPPER_LEVEL} ]] ; then
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=1
else
__LEVEL["${__LOWER_LEVEL}-${__UPPER_LEVEL}"]=0
fi
checkLevel "90-100"
checkLevel "80-90"
checkLevel "70-80"
checkLevel "60-70"
checkLevel "50-60"
checkLevel "40-50"
checkLevel "30-40"
checkLevel "20-30"
checkLevel "10-20"
checkLevel "0-10"
sleep ${__TIME_SLEEP_SECONDS}s
done
End of script.
Configuration: Appropriately fix the __FILENAME variable to point to /sys/class/power_supply/BAT0/uevent or /sys/class/power_supply/BAT1/uevent or something else as needed. If you don't want the notification during charging use __DISPLAY_CHARGING_NOTIFICATIONS=0 .
The script can be started manually by running bash /home/username/bin/notification_battery_discharge.sh .
Setup:
For automatic startup, create a .desktop file in $HOME/.config/autostart e.g. /home/username/.config/autostart/notification_battery_discharge.desktop with the following content:
[Desktop Entry]
Name=Notification of Battery Discharge
Exec="/home/username/bin/notification_battery_discharge.sh"
Type=Application
If required run chmod +x /home/username/.config/autostart/notification_battery_discharge.desktop . Then log out, and log back in, the script should start its notification process.
Alternatively, for automatic start up using systemd, create a .service file in $HOME/.config/systemd/user/ directory e.g. $HOME/.config/systemd/user/notification_battery_discharge.service with the following content:
[Unit]
Description=Display notification at different levels of battery discharging/charging.
After=graphical-session.target
[Service]
Type=oneshot
ExecStart=-/bin/bash -c '/home/username/bin/notification_battery_discharge.sh'
[Install]
WantedBy=graphical-session.target
Then use the command systemctl --user enable notification_battery_discharge.service then log out, and log back in. The notification process should start.