#!/usr/bin/env bash
#
# * Battery Watchdog (see help text below).
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
#
#                               ###
#
# btw depends these packages:
#   `acpi`          ; <https://sourceforge.net/projects/acpiclient/>
#   `libnotify`     ; <https://salsa.debian.org/gnome-team/libnotify>
#
# Installation:
#   Arch Linux:
#       # pacman -S acpi        (commynity repo)
#       # pacman -S libnotyfy   (extra repo)
#   Debian/Ubuntu:
#       # apt-get install acpi
#       # apt-get install libnotify

btw_ver() {
    echo btw 1.0; exit 0
}

btw_help() {
    cat <<- 'EOF'
btw - Battery Watchdog. Send notification if critical battery level reached.

Usage:  btw [-v|--version] [-h|--help] [-c|--crit <val>]
            [-p|--preriod <seconds>] [-l|--log <file>]
            [-s|--summary <summary>] [-b|--body <body>] [-w|--watch]

Options:
    -v, --version           print version and exit.
    -h, --help              print this help message and exit.
    -c, --crit <val>        battery critical value (in percents), default: 10
    -p, --preriod <seconds> battery check out period in seconds, default: 30
    -l, --log <file>        log file, default: ~/.cache/btwatchdog.log
    -s, --summary <summary> custom notification title text.
    -b, --body <body>       custom notification body text.
    -w, --watch             follow to log file ('tail -f'), use '--log' to
                            set log file path.

Run watchdog in background. See application autostart options for your desktop
environment. For example:

Add to your '~/.xinitrc':
    btw &

or create '~/.config/autostart/btw.desktop' if you have GNOME Desktop:
    [Desktop Entry]
    Name=Laptop battery watchdog (btw)
    Exec=btw
    Type=Application

Logging

btw writes battery status log in log file. Set file by '--log' option.
Log format: '[date time] [state] [level]%'
There is:   [date time]     `date +'%Y-%m-%d %H:%M:%S'` command output.
            [state]         battery status, can be 'Charging' or 'Discharging'.
            [level]         battery current level in percents.

Notifications

btw will send notification at every battery checkout while current battery
level in lower than critical value. For example, with command:
    btw --crit 10 --period 30
you will recieve notification every 30 seconds until battery is under 10%.

You can set up custom notification text via '--summary' and '--body' options.
Variables can be used in notification:
    level       current battery level in percents.
    state       current battery status.
For example:
    btw --summary 'Battery is very low!' --body 'Status: ${state,,}, ${level}%'

License: The Unlicense <https://unlicense.org/>
EOF
    exit 0
}

# Default values.
period=30       # check battery every `period` seconds
critical=10     # battery limit (in %)
clevel=100      # current battery value (in %, temporary)
summary='Extremely low battery: ${level}%'
body='Check the charger, currently is ${state,,}'

lock=/tmp/btwatchdog.lock    # lock file
log=$HOME/.cache/btwatchdog.log      # log
mkdir -p $HOME/.cache || { echo "Cannot write log file: $HOME/.cache" >&2; }

btw_notify() {
    notify-send --urgency=critical --icon=battery-empty --category=System \
        "$(eval echo "$summary")" "$(eval echo "$body")"
}

btw_isdigit() {
    if [[ "$1" =~ [0-9]+ ]]; then
        return 0
    else
        echo 'Value is not integer!' >&2; exit 1
    fi
}

# Transform long options to short ones
for arg in "$@"; do
    shift
    case "$arg" in
        --crit)     set -- "$@" "-c" ;;
        --period)   set -- "$@" "-p" ;;
        --log)      set -- "$@" "-l" ;;
        --summary)  set -- "$@" "-s" ;;
        --body)     set -- "$@" "-b" ;;
        --watch)    set -- "$@" "-w" ;;
        --help)     set -- "$@" "-h" ;;
        --version)  set -- "$@" "-v" ;;
        *)          set -- "$@" "$arg";;
    esac
done

# Parse short opts
while getopts c:p:l:s:b:whv opt; do
    case "$opt" in
        c)  btw_isdigit "$OPTARG"; critical="$OPTARG";;
        p)  btw_isdigit "$OPTARG"; period="$OPTARG" ;;
        l)  log="$OPTARG";;
        s)  summary="$OPTARG";;
        b)  body="$OPTARG";;
        w)  watchdog=1;;
        h)  btw_help;;
        v)  btw_ver;;
    esac
done

if [ "$watchdog" ]; then
    tail --follow "$log"
    exit "$?"
fi

while true; do
    battery=$(acpi -b | awk '{print $3 " " $4}' | sed -E 's/,|%//g;')
    level=${battery##* }  # e.g. "49" (in percents)
    state=${battery%% *}  # e.g. "Charging"

    if [ "$level" -lt "$critical" ]; then
        if [ ! -f "$lock" ]; then
            btw_notify
            touch "$lock"
        fi
        # Warn in every percent less
        if [ "$level" -lt "$clevel" ]; then
            btw_notify
            touch "$lock"
            clevel="$level"
        fi
    else
        [ -f "$lock" ] && rm "$lock"
    fi

    echo "$(date +'%Y-%m-%d %H:%M:%S') ${state,,} ${level}%" >> "$log"
    sleep $period
done
