#!/usr/bin/perl -w # Quick hack to get a battery status display. # Can use several ways to get the info. # # Copyright 2005 Johan Vromans, Squirrel Consultancy. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. use strict; use Tk; use Tk::ProgressBar; my $status = 0; my $max = 100; my $reader = \&get_info; my $state = 0; my $testing = 0; if ( @ARGV && $ARGV[0] =~ /^--?test$/i ) { shift; $testing = 1; $reader->(); } my $mw = MainWindow->new; $mw->title("Battery: Starting"); my $p = $mw->ProgressBar (-troughcolor => 'LightSkyBlue3', -borderwidth => 0, -length => 120, -width => 8, -variable => \$status, -colors => [0 => 'red', 10 => 'yellow', 20 => 'green'], -anchor => 'w', -from => 0, -to => $max, )->pack(-expand => 1); # Check every 10 seconds. $reader->(); $mw->repeat (10000, $reader); MainLoop; sub get_info { # Try all methods until we get a result. for ( \&read_sbs, \&read_legacy, \&read_from_battery2, \&read_from_sb, ) { print STDERR ("Trying: ", $_->(1), " ... ") if $testing; if ( $_->() ) { # No more retrying. $reader = $_; print STDERR ("Using: ", $_->(1), "\n") unless $testing; return; } print STDERR ("\n") if $testing; } exit(0); } ################ read_sbs ################ # Reads the state from the new SBS /proc API. # # Expected output: # # /proc/acpi/sbs/SBS0/SB0/state # # present: yes # charging state: charging # current: 456 mA # average current 459 mA # voltage: 16817 mV # temperature: 31.2 C # relative charge: 96% # absolute charge: 94% # remaining capacity: 4117 mAh # run time to empty: n/a # average time to empty: n/a # average time to full: 0h 48m sub read_sbs { return "/proc/acpi/sbs" if @_; my $state = cat("/proc/acpi/sbs/SBS0/SB0/state"); my $chstate = ""; $chstate = $1 if $state =~ /^charging state:\s+(.*)/mi; my $current = 0; $current = $1 if $state =~ /^current:\s+(\d+)/mi; $current = -$current if $chstate eq "discharging"; $current = 0 if $chstate eq "charged"; my $charge = 0; $charge = $1 if $state =~ /^relative charge:\s+(\d+)%/mi; my $time; $time = $2 if $current && $state =~ /^average time to (empty|full):\s+(\d.*)/mi; update($current, $charge, $time); } ################ read_legacy ################ # Reads the legacy /proc/acpi/battery status. # # Expected output: # # /proc/acpi/battery/BAT0/state # # present: yes # capacity state: ok # charging state: charging # present rate: 337 mA # remaining capacity: 4168 mAh # present voltage: 16834 mV # # /proc/acpi/battery/BAT0/info # # present: yes # design capacity: 4400 mAh # last full capacity: 4270 mAh # battery technology: rechargeable # design voltage: 14800 mV # model number: 01ZL # serial number: 40504 # battery type: LION # OEM info: SANYO # # NOTE: The 'time' results are not very accurate. sub read_legacy { return "/proc/acpi/battery" if @_; my $state = cat("/proc/acpi/battery/BAT0/state"); my $info = cat("/proc/acpi/battery/BAT0/info"); my $chstate = ""; $chstate = $1 if $state =~ /^charging state:\s+(.*)/mi; my $current = 0; $current = $1 if $state =~ /^present rate:\s+(\d+)/mi; $current = -$current if $chstate eq "discharging"; my $charge = 0; $charge = $1 if $state =~ /^remaining capacity:\s+(\d+)/mi; $charge = int(100*($charge/$1)+0.5) if $info =~ /^last full capacity:\s+(\d+)/mi; my $time; if ( $chstate eq "discharging" ) { $state =~ /^remaining capacity:\s+(\d+)/mi; my $rem = $1; $time = int(60 * ($rem / -$current)); } elsif ( $chstate eq "charging" ) { $info =~ /^design capacity:\s+(\d+)/mi; my $lfc = $1; $state =~ /^remaining capacity:\s+(\d+)/mi; my $rem = $1; $time = int(60 * ($lfc - $rem) / $current); } elsif ( $chstate eq "charged" ) { } update($current, $charge, $time); } ################ read_from_sb ################ # Uses self modified version of Pedro Venda's modified version of # Bruno Ducrot's version of the smart battery reader (not included). # # Note that the output of the command, "sb 0 10" must be piped into # this script. # # Regular output is (one line every 10 seconds) three numbers: # # -982 66 241 # # -982 current # 66 %fill # 241 time to full (dis)charge. sub read_from_sb { return "stdin(sb)" if @_; while ( 1 ) { my $rin = ''; vec($rin, fileno(STDIN), 1) = 1; unless ( select($rin, undef, undef, 0) ) { return undef; } my $line = ; next unless $line =~ /^(-?\d+)\s+(\d+)\s+(\d+)/; return update($1, $2, $3); } } ################ read_from_smartbattery2 ################ # Uses Pedro Venda's modified version of Bruno Ducrot's version # of the smart battery reader (not included). # Regular output of `smartbattery 0` is: # # status: discharging initialized # mode: capacity in 10 mW/mWh # design voltage: 14800 mV # design charge capacity: 4400 mAh or mWh # absolute charge: 90% # full charge capacity: 4270 mAh or mWh # relative charge: 93% # current: -908 mA # voltage: 16271 mV # remain: 3966 mA # average time to empty: 261 minutes # average time to full: 65535 minutes # temperature: 24.2 C # cycle count: 12 sub read_from_battery2 { my $prog = "smartbattery2 0"; return "prog($prog)" if @_; my $res = `$prog`; return undef unless $res; my ($current, $charge, $time, $tp); $current = $1 if $res =~ /current:\s+(-?\d+)\s+ma/i; $tp = $current < 0 ? 'empty' : 'full'; $charge = $1 if $res =~ /relative charge:\s+(\d+)%/i; $time = $1 if $res =~ /average time to $tp:\s+(\d+)\s+minutes/i; undef $time if $time == 65535; update($current, $charge, $time); } sub update { my ($current, $charge, $time) = @_; if ( $time && $time =~ /^\d+$/ ) { my $hh = int($time / 60); my $mm = $time - $hh*60; $time = ""; $time .= $hh . "h" if $hh; $time .= " " if $mm && $hh; $time .= $mm . "m" if $mm; } if ( $testing ) { print STDERR ("update($current, $charge" . ( defined $time ? ", $time" : "") . ")\n"); return undef; } return undef unless $current or $charge; if ( $current > 0 ) { $status = 0; } elsif ( $current < 0 ) { $status = $charge; } else { $status = $max; } $mw->title("Battery: " . ($time ? "$time ($status%)" : "$status%")); # This is a kludge to get the ProgressBar to display uniform, but # different colours depending on the fill status. if ( $status >= 30 ) { if ( $state >= 30 ) { # Ok } else { $p->{Configure}{-colors} = [ 0, 'green' ]; $p->{Configure}{-troughcolor} = 'darkgreen'; Tk::ProgressBar::_layoutRequest($p, 1); $state = 30; } } elsif ( $status >= 10 ) { if ( $state >= 10 && $state < 30 ) { # Ok } else { $p->{Configure}{-colors} = [ 0, 'yellow' ]; $p->{Configure}{-troughcolor} = 'orange'; Tk::ProgressBar::_layoutRequest($p, 1); $state = 10; } } elsif ( $state >= 10 ) { $p->{Configure}{-colors} = [ 0, 'red' ]; $p->{Configure}{-troughcolor} = 'darkred'; Tk::ProgressBar::_layoutRequest($p, 1); $state = 0; } return 1; } sub cat { my $file = shift; local($/) = undef; my $ret = ""; my $cat; open($cat, $file) and $ret = <$cat> and close($cat); $ret; }