#! /bin/sh

# bat-mon v1.0
# Monitor battery activity
# Copyright (c) 2016 Raphaël Halimi <raphael.halimi@gmail.com>

# Source shell-script-helper
. /lib/shell-script-helper


#
# Variables
#

# Configuration files
SYSTEM_CONFIG_FILE="/etc/$(basename "$0").conf"
USER_CONFIG_FILE="$HOME/.$(basename "$0").conf"

# Options defaults
REFRESH=1
SCALE=1

# Data path
NATIVE_PATH="/sys/class/power_supply"
SMAPI_PATH="/sys/devices/platform/smapi"
[ -d "$SMAPI_PATH" ] && NATIVE=0 || NATIVE=1


#
# Functions
#

print_usage () {
  printf "Usage: %s [OPTION]...\n" "$(basename "$0")"
  printf "Monitor battery activity\n"
  printf "\nOPTIONS:\n"
  print_option "-r REFRESH" "Refresh interval (in seconds), default is 1"
  print_option "-s SCALE" "Digits after the decimal point, default is 1"
  print_option "-n" "Force use of native kernel interface even if SMAPI is available"
  print_option "-v" "Verbose mode"
  print_option "-d" "Debug mode"
  print_option "-h" "Print this help message"
}


#
# Configuration files
#

[ -e "$SYSTEM_CONFIG_FILE" ] && . "$SYSTEM_CONFIG_FILE"
[ -e "$USER_CONFIG_FILE" ] && . "$USER_CONFIG_FILE"


#
# Options processing
#

while getopts "nr:s:vdh" OPTION ; do
  case $OPTION in
    n) NATIVE=1 ;;
    r) REFRESH="$OPTARG" ;;
    s) SCALE="$OPTARG" ;;
    v) enable_verbose ;;
    d) enable_debug ;;
    h) print_usage ; exit 0 ;;
    *) print_usage ; exit 1 ;;
  esac
done ; shift $((OPTIND-1))
print_debug "NATIVE=$NATIVE" "REFRESH=$REFRESH" "SCALE=$SCALE"


#
# Checks
#

# This script needs bc
which bc > /dev/null || die "Please install bc"

# Force NATIVE=1 if user set NATIVE=0 and SMAPI is not available
[ "$NATIVE" -eq 0 ] && [ ! -d "$SMAPI_PATH" ] && NATIVE=1


#
# MAIN
#

# Interrupt from keyboard *is* expected
trap - INT

# Choose some settings according to MODE
if [ "$NATIVE" -eq 1 ] ; then
  DATA_PATH="$NATIVE_PATH"
  PRESENT="present"
  SYSFILE_LIST="manufacturer model_name technology status energy_now capacity power_now voltage_now voltage_min_design cycle_count energy_full_design energy_full" \
  ENERGY_FULL="energy_full"
  ENERGY_FULL_DESIGN="energy_full_design"
  CONVERT=1000000
else
  DATA_PATH="$SMAPI_PATH"
  PRESENT="installed"
  SYSFILE_LIST="manufacturer model chemistry manufacture_date first_use_date state remaining_capacity remaining_percent start_charge_thresh stop_charge_thresh power_now current_now remaining_running_time_now power_avg current_avg remaining_running_time temperature voltage design_voltage cycle_count design_capacity last_full_capacity"
  ENERGY_FULL="last_full_capacity"
  ENERGY_FULL_DESIGN="design_capacity"
  CONVERT=1000
fi

print_debug "DATA_PATH=$DATA_PATH" "PRESENT=$PRESENT" "SYSFILE_LIST=$SYSFILE_LIST" "ENERGY_FULL=$ENERGY_FULL" "ENERGY_FULL_DESIGN=$ENERGY_FULL_DESIGN" "CONVERT=$CONVERT"

# Battery slots
BAT_MAX=$(($(find "$DATA_PATH" -maxdepth 1 -name "BAT*" | wc -l)-1))

print_debug "BAT_MAX=$BAT_MAX"

[ "$BAT_MAX" -eq -1 ] && die "No battery found"

# Main loop
while true ; do 
  [ "$DEBUG" -eq 0 ] && clear

  # Loop through all batteries found
  BAT=0 ; while [ "$BAT" -le "$BAT_MAX" ] ; do
    print_debug "BAT=$BAT"

    # Display values only if battery is installed (obviously)
    if [ "$(cat "$DATA_PATH/BAT$BAT/$PRESENT")" -eq 1 ] ; then

      printf "##### %s #####\n" "BAT$BAT"
      print_verbose "Reading values from $DATA_PATH/BAT$BAT"

      # Files to read
      for SYSFILE in $SYSFILE_LIST ; do
        print_debug "SYSFILE=$SYSFILE"

        # Names and values
        SYSFILE_PRETTY_NAME="$(printf "%s" "$SYSFILE" | sed -r -e "s/^([[:alpha:]])/\U\1\E/" -e "s/_/ /g" -e "s/(thresh)/\1hold/")"
        SYSFILE_VALUE="$(cat "$DATA_PATH/BAT$BAT/$SYSFILE")"
        print_debug "SYSFILE_PRETTY_NAME=$SYSFILE_PRETTY_NAME" "SYSFILE_VALUE=$SYSFILE_VALUE"

        # Units
        case "$SYSFILE" in
          temperature)
            UNIT="°C" ;;
          power*)
            UNIT="W" ;;
          current*)
            UNIT="A" ;;
          *voltage*)
            UNIT="V" ;;
          *_capacity|energy*)
            UNIT="Wh" ;;
          *percent|*thresh|capacity)
            UNIT="%" ;;
          *time*)
            [ "$SYSFILE_VALUE" = "not_discharging" ] && UNIT="" || UNIT="min" ;;
          *)
            UNIT="" ;;
        esac
        print_debug "UNIT=$UNIT"

        # Conversions
        case "$UNIT" in
          W*|A|V|*C)
            { SYSFILE_VALUE="$(printf "scale=%i; %i/%i\n" "$SCALE" "$SYSFILE_VALUE" "$CONVERT" | bc -q)"; print_debug "SYSFILE_VALUE=$SYSFILE_VALUE"; } ;;
        esac

        # Sections
        case "$SYSFILE" in
          manufacturer|stat*|start_charge_thresh|power*|voltage|voltage_now|temperature|cycle_count)
            printf "\n" ;;
        esac

        # Display
        case "$UNIT" in

          # Strings
          '')
            printf "%-30s%s\n" "$SYSFILE_PRETTY_NAME" "$SYSFILE_VALUE" ;;

          # Decimals
          W*|A|V|*C)
            printf "%-30s%g %s\n" "$SYSFILE_PRETTY_NAME" "$SYSFILE_VALUE" "$UNIT" ;;

          # Integers
          %|min)
            printf "%-30s%i %s\n" "$SYSFILE_PRETTY_NAME" "$SYSFILE_VALUE" "$UNIT" ;;
          
          # Possible error handling
          *)
            printf "%-30s %s %s (unexpected unit)\n" "$SYSFILE_PRETTY_NAME" "$SYSFILE_VALUE" "$UNIT" ;;
        esac

      done

      # Health calculation (last_full_capacity * 100 / design_capacity)
      HEALTH="$(printf "scale=%i; %i*100/%i\n" "$SCALE" "$(cat "$DATA_PATH/BAT$BAT/$ENERGY_FULL")" "$(cat "$DATA_PATH/BAT$BAT/$ENERGY_FULL_DESIGN")" | bc -q)"
      printf "%-30s%.${SCALE}f %%\n\n" "Health" "$HEALTH"

    else
      print_verbose "Skipping BAT$BAT, not installed"
    fi

    # Increment battery counter
    BAT=$((BAT+1))

  done

  printf "Press Ctrl-C to stop."
  sleep "$REFRESH"
done
