#!/bin/bash
#
# simbamon -- a simple battery monitor
#
# Author: Hamish Cunningham <hamish@gate.ac.uk>
# This code is copyright Hamish Cunningham and the University of Sheffield
# and is licenced under GPL 3 or any later version.
#
### BEGIN INIT INFO
# Provides:          simbamon
# Short-Description: SimBaMon: a simple battery monitor daemon
# Description:       This script is implements the daemon for
#                    SimBaMon (a simple battery monitor)
### END INIT INFO

# standard locals
P="$0"
USAGE="`basename ${P}` [-h(elp)] [-d(ebug)] [-s(imulate)]"
OPTIONSTRING=hds

# define LSB log_* functions.
. /lib/lsb/init-functions

# message & exit if exit num present
usage() { log_failure_msg Usage: $USAGE; [ ! -z "$1" ] && exit $1; }

# defaults
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="SimBaMon: a simple battery monitor daemon"
NAME=simbamon
DNAME=simbamond
DAEMON=/usr/sbin/$NAME
PIDFILE=/var/run/$DNAME.pid
LMESS='battery level'
SHUTDOWN='shutdown -h now'

# options defaults
DEBUG=off
SIMUL=off

# process options
[ ! -z "$*" ] && log_daemon_msg ${NAME} options: $*
while getopts $OPTIONSTRING OPTION
do
  case $OPTION in
    h)	usage 1 ;;
    d)	DEBUG=on ;;
    s)	SIMUL=on ;;
    *)	usage 2 ;;
  esac
done 
shift `expr $OPTIND - 1`

# read configuration variable file if it is present
if [ -r /etc/default/$DNAME ] 
then
  . /etc/default/$DNAME
else
  logger "$0: no config data found - fatal error"
  exit 1
fi

# pretty date/time function
pdate() { date +%b-%d-%Y-%T; }

# tell the world
logger "=========== $0: running at `pdate` ==========="
logger "${NAME}: monitor frequency is ${MONITOR_FREQUENCY} seconds"
logger "${NAME}: DEBUG is ${DEBUG}, SIMUL is ${SIMUL}"

# check for gpio command (which may be a function defined in config)
GPIO=
if [ "`type -t gpio`" = "function" ]
then
  GPIO=gpio
else
  GPIO=`which gpio`
fi
if [ -z "${GPIO}" ]
then
  log_failure_msg "${NAME}: cannot find gpio command: failing" >&2
  logger          "${NAME}: cannot find gpio command: failing"
  exit 1
fi

# put the relevant pins into read mode
gpio mode $IO_A in
gpio mode $IO_B in
gpio mode $IO_C in

# current level; -1 means unset
BAT_LEVEL=-1

# helper to check the level
get-bat-level() {
  BAT_LEVEL_BASE2=`gpio read ${IO_A}``gpio read ${IO_B}``gpio read ${IO_C}`
  BAT_LEVEL=`echo "ibase=2;${BAT_LEVEL_BASE2}" |bc`
  echo ${BAT_LEVEL}
}

# do the work; i and j index loop iterations for e.g. logging
i=0; j=0
while :
do
  i=`expr ${i} + 1`
  j=`expr ${j} + 1`

  # on first boot we delay work a little, in case the user wants to tell us 
  # not to shutdown on critical battery, for example
  [ ! -f "$PREVIOUSLY_RUN_INDICATOR" ] && {
    date > $PREVIOUSLY_RUN_INDICATOR 
    logger "${NAME}: first run after boot, sleeping for $BOOT_DELAY"
    sleep $BOOT_DELAY
  }

  # check the level and act if necessary
  BAT_LEVEL=`get-bat-level`
  [ $BAT_LEVEL != 0 -a $BAT_LEVEL -le $BAT_WARNING ] && \
    logger "${NAME}: ${LMESS} is at or below warning level (${BAT_LEVEL})"
  if   [ $BAT_LEVEL -eq $POWER_OFF ]
  then
    wall <<< "${NAME}: power off requested: shutting down now!"
    logger "${NAME}: shutting down (POWER_OFF)"
    bash -c "$SHUTDOWN"
  elif [ $BAT_LEVEL -eq $BAT_SHUTDOWN ]
  then
    wall <<< "${NAME}: battery empty: shutting down now!!!"
    logger "${NAME}: shutting down (BAT_SHUTDOWN)"
    bash -c "$SHUTDOWN"
  elif [ $BAT_LEVEL -eq $BAT_CRITICAL ]
  then
    wall <<< \
      "${NAME}: ${LMESS} critical! shutting down in ${SHUT_DELAY} seconds!"
    logger "${NAME}: shutting down (BAT_CRITICAL, SHUT_DELAY=${SHUT_DELAY})"
    sleep $SHUT_DELAY
    
    # check if things have improved (new battery pack for example)
    BAT_LEVEL=`get-bat-level`
    [ $BAT_LEVEL -gt $BAT_CRITICAL ] && {
      wall <<< "${NAME}: battery levels have risen! cancelling shutdown"
      logger   "${NAME}: battery levels have risen! cancelling shutdown"
      continue
    }
    bash -c "$SHUTDOWN"
  elif [ $BAT_LEVEL -eq $BAT_WARNING ]
  then
    wall <<< "${NAME}: ${LMESS} is low! connect new battery or shut down"
    logger "${NAME}: BAT_WARNING; sleeping ${WARNING_INTERVAL}..."

    # sleep for WARNING_INTERVAL, unless something changes (check every 10)
    ITERATIONS=`expr $WARNING_INTERVAL / 10`
    for(( CURR_ITER=0; CURR_ITER < $ITERATIONS; CURR_ITER++ )) 
    do
      sleep 10
      BAT_LEVEL=`get-bat-level`
      [ ! $BAT_LEVEL -eq $BAT_WARNING ] && break
    done
  fi

  # routine log messages
  [ ${j} -eq ${LOG_INTERVAL} ] && \
    logger "${NAME}: ${LMESS} is ${BAT_LEVEL} at `pdate` (i=${i})" && j=0

  # take a break
  sleep ${MONITOR_FREQUENCY}
done &

# create PIDFILE
echo $! >${PIDFILE}
