#! /usr/bin/perl -w
# $Header: /auto/homes/pb22/pl/RCS/bat.pl,v 1.8 2006/10/21 08:42:44 pb22 Exp $
# Use "pod2text" for info

=head1 NAME

cl-apm - show the state of the battery using ACPI info if available

=head1 SYNOPSIS

cl-apm

=head1 OPTIONS

=over 4

=item B<--verbose>

generate comments as to what it is doing.

=item B<--help>

generate help info.

=back

=head1 DESCRIPTION

perl command to simulate the B<apm> command for acpi based machines.
If B</proc/acpi> isn't found, try apm-real, apm-FC2, apm-FC3, apm-bin and
finally apm (it tries to detect a loop).
As such, rename the real B<apm> before installing this commend as apm
if you might ever want to run an apm kernel.

If B<SHOWRATE> is set in the command environment, it also shows the rate of
(dis)charge.

If B<TRACE> is set in the command environment, it displays info from which
it calcualted its numbers.
The command line flag B<--verbose> over rides this, and sets it to 7.

It may be installed as B<apm> if the machine will never use real APM.
/usr/sbin/apmlog will look for it, and try to get it to give best info when it can.

=cut

use Getopt::Long;
use Pod::Usage;
%opts = (
	"verbose"	=> \$verbose,
	"help"		=> \$help,
);

GetOptions (%opts) || pod2usage();
pod2usage(-verbose => 2) if $help;

# Found /proc/acpi/battery/BAT0/state
# present:                 yes
# capacity state:          ok
# charging state:          discharging
# present rate:            15248 mW
# remaining capacity:      26360 mWh
# present voltage:         11066 mV

# Found /proc/acpi/battery/BAT0/info
# present:                 yes
# design capacity:         71590 mWh
# last full capacity:      71590 mWh
# battery technology:      rechargeable
# design voltage:          11100 mV
# design capacity warning: 3000 mWh
# design capacity low:     1000 mWh
# capacity granularity 1:  200 mWh
# capacity granularity 2:  200 mWh
# model number:            DELL 0004P2
# serial number:           20009
# battery type:            LION
# OEM info:                Sony

$trace	= ($verbose) ? 7 : $ENV{'TRACE'} || 0 unless defined($trace);
$showrate	= $ENV{'SHOWRATE'} || 0;	# try to get battery rate
$acpi	= "/proc/acpi";
$proc_bat = "$acpi/battery/BAT";
$state	= "state";
$info	= "info";
$charge	= "charge";
$looping= "AM_IN_APM_LOOP";			# If set, is looping
@apmcmds = ("/usr/bin/apm-real", "/usr/bin/apm-FC2", "/usr/bin/apm-bin", "/usr/bin/apm");
$rate	= "rate";
$remain	= "remain";
$capacity	= "capacity";
%state	= ();
%batteries=();
$totrate	= 0;				# sum of battery rates

if (! -d $acpi) {
	exit(1) if $ENV{$looping};
	$ENV{$looping} = 1;
	for $bin (@apmcmds) {
		exec $bin, @ARGV if -x $bin;
	}
	exit(1);
}

for $b (0 .. 100) {
	local ($bat) = $proc_bat . $b;
	next if ! $b && ! -d $bat;
	last unless -d $bat;
	print STDERR "Found $bat\n" if $trace > 0;
	next unless &read_state($bat, $state);
	next unless &read_info($bat, $info);
	$batteries{$b} = 1;
}

if ($trace > 0) {
	foreach $key (sort keys %state) {
		printf "%-20s %s\n", $key, $state{$key};
	}
}

foreach $key (sort keys %batteries) {
	# capacity           71590 mWh
	# charge             discharging
	# rate               14943 mW
	# remain             24180 mWh
	local($discharge,$percent,$time,$total,$tothm)	= ("?", "?", "?", "?", "?");
	if ($state{"$key-$charge"} =~ /discharging/) {
		$discharge = "-";
	}
	elsif ($state{"$key-$charge"} =~ /^charging/) {
		$discharge = "+";
	}
	elsif ($state{"$key-$charge"} =~ /^charged/) {
		$discharge = "#";
	}
	if ($state{"$key-$rate"} =~ /^(\d+)/) {
		$ratex = $1;
		$totrate += $ratex;
		if ($state{"$key-$capacity"} =~ /^(\d+)/) {
			$capacityx = $1;
			$mins = ($ratex) ? int($capacityx * 60 / $ratex + 0.5) : 0;
			$total = sprintf "%3.1f", $mins / 60;
		}
	}
	if ($state{"$key-$rate"} =~ /^(\d+)/) {
		$ratex = $1;
		if ($state{"$key-$remain"} =~ /^(\d+)/) {
			$remainx = $1;
			$mins = ($ratex) ? int($remainx * 60 / $ratex + 0.5) : 0;
			$time = $mins;
			$tothm = sprintf "%d:%02d", int($mins/60), $mins % 60;
		}
	}
	if ($state{"$key-$capacity"} =~ /^(\d+)/) {
		$capacityx = $1;
		if ($state{"$key-$remain"} =~ /^(\d+)/) {
			$remainx = $1;
			$percent = sprintf "%5.1f", ($remainx * 100) / $capacityx;
			$percent =~ s/^ *//;
		}
	}
	#printf "%d %s %3s%% %3sm %3sh\n", $key, $discharge, $percent, $time, $total;
	$extra = ($showrate && $totrate) ? " ${totrate}mW" : "";
	printf "AC o%s-line, battery status high: %s%% (%s)%s\n", ($discharge eq "+") ? "n" : ($discharge eq "-") ? "ff" : ($discharge eq "#") ? "n" : $discharge, $percent, $tothm, $extra;
}

# AC (on|off)-line, battery status high: 100% (3:20)
# 1.16 1.2 0x02 CHARGE=0x01 0x00 0x01 100% 98 min


sub read_state {
	local($dir, $file) = @_;
	open(STATE, "$dir/$file") || return 0;
	print STDERR "Found $dir/$file\n" if $trace > 1;
	while (<STATE>) {
		print if $trace > 2;
		return 0 if /^present\s*:\s*no$/;
		if (/^charging state\s*:\s*(.*)$/) {
			$state{"$b-$charge"} = $1;
			next;
		}
		if (/^present rate\s*:\s*(.*)$/) {
			$state{"$b-$rate"} = $1;
			next;
		}
		if (/^remaining capacity\s*:\s*(.*)$/) {
			$state{"$b-$remain"} = $1;
			next;
		}
	}
	close(STATE) || return 0;
	return 1;
}

sub read_info {
	local($dir, $file) = @_;
	open(INFO, "$dir/$file") || return 0;
	print STDERR "Found $dir/$file\n" if $trace > 1;
	while (<INFO>) {
		print if $trace > 2;
		if (/^last full capacity\s*:\s*(.*)$/) {
			$state{"$b-$capacity"} = $1;
			next;
		}
	}
	close(INFO) || return 0;
	return 1;
}
