Difference between revisions of "Code/tp-bat-balance"

From ThinkWiki
Jump to: navigation, search
(Battery balancing script)
 
m (Added case where program started without battery in slot or bay would break program.)
Line 43: Line 43:
 
       $bat_power_avg[$b] = read_chomp_file("$smapi_dir/BAT$b/power_avg") / 1000.0;
 
       $bat_power_avg[$b] = read_chomp_file("$smapi_dir/BAT$b/power_avg") / 1000.0;
 
     }
 
     }
 +
    else { $bat_state[$b] = 'none'; }  #This var needs to always have a value for print_bat to not break. This covers the case of starting the program without a battery in the bay/slot.
 
   }
 
   }
 
}
 
}
Line 58: Line 59:
 
     } elsif ($state eq 'discharging') {
 
     } elsif ($state eq 'discharging') {
 
       $arrow = sprintf("$rl--%4.1f--$rr", -$bat_power_avg[$b]);
 
       $arrow = sprintf("$rl--%4.1f--$rr", -$bat_power_avg[$b]);
     } elsif ($state eq 'idle') {
+
     } elsif ($state eq 'idle' || $state eq 'none') { #Added none to cover case with no battery in slot when program was started.
 
       $arrow = "          ";
 
       $arrow = "          ";
 
     } else {
 
     } else {

Revision as of 23:36, 7 September 2009

  1. !/usr/bin/perl
  2. Keep two ThinkPad batteries (system battery and UltraBay) at similar charge levels
  3. during discharge by switching back and forth. This reduces wear on the UltraBay
  4. battery, compared to the hardware's default strategy of fully draining the UltraBay
  5. battery before switching to the system battery.
  6. WARNING: This script is experimental and uses undocumented hardware features.
  7. WARNING: If this script crashes, your battery may be forced to keep draining until empty.
  8. Distributed under the terms of the GNU General Public License v2 or later.

use strict; use warnings; use File::Slurp;

my $thresh = 3; # difference between battery charge levels that justifies switching (hysteresis)

my $default_discharge = 0; # the battery that's discharged as first priority by the BIOS my $smapi_dir = '/sys/devices/platform/smapi';

my $ac_connected; my @bat_installed; my @bat_remaining; my @bat_state; my @bat_power_avg; my @bat_force_discharge;

$SIG{'INT'} = $SIG{'QUIT'} = $SIG{'TERM'} = sub { die("# Killed by SIG$_[0]\n"); };

sub read_chomp_file {

 my ($filename) = @_;
 my ($x) = read_file($filename) or die "Cannot read $filename\n";
 chomp($x);
 return $x;

}

sub read_status {

 $ac_connected = read_chomp_file("$smapi_dir/ac_connected");
 for my $b (0..1) {
   $bat_installed[$b] = read_chomp_file("$smapi_dir/BAT$b/installed");
   $bat_force_discharge[$b] = read_chomp_file("$smapi_dir/BAT$b/force_discharge");
   if ($bat_installed[$b]) {
     $bat_remaining[$b] = read_chomp_file("$smapi_dir/BAT$b/remaining_percent");
     $bat_state[$b] = read_chomp_file("$smapi_dir/BAT$b/state");
     $bat_power_avg[$b] = read_chomp_file("$smapi_dir/BAT$b/power_avg") / 1000.0;
   }
   else { $bat_state[$b] = 'none'; }  #This var needs to always have a value for print_bat to not break. This covers the case of starting the program without a battery in the bay/slot.
 }

}

sub print_status {

 print "   ";
 sub print_bat {
   my ($b) = @_;
   my ($ll,$lr,$rl,$rr) = $b ? ('-','>','<','-') : ('<','-','-','>');
   my $icon = sprintf("[%3s]", $bat_installed[$b] ? $bat_remaining[$b]."%" : "");
   my $arrow;
   my $state = $bat_state[$b];
   if ($state eq 'charging') {
     $arrow = sprintf("$ll--%4.1f--$lr", $bat_power_avg[$b]);
   } elsif ($state eq 'discharging') {
     $arrow = sprintf("$rl--%4.1f--$rr", -$bat_power_avg[$b]);
   } elsif ($state eq 'idle' || $state eq 'none') {  #Added none to cover case with no battery in slot when program was started.
     $arrow = "          ";
   } else {
     die "Unknown state $state for battery $b";
   }
   print($b ? "$arrow$icon" : "$icon$arrow");
 }
 print_bat(0);
 print($ac_connected ? ' {AC} ' : ' {  } ');
 print_bat(1);
 print("\n");

}

sub choose_discharge {

 # Choose which battery to discharge
 sub set_force_discharge {
   my ($b,$on) = @_;
   return if $b!=$default_discharge; # the non-default battery will be discharged only when necessary anyway
   return if $bat_force_discharge[$b]==$on;
   write_file("$smapi_dir/BAT$b/force_discharge", ($on?'1':'0')) or die ("Cannot write to $smapi_dir/BAT$b/force_discharge: $!\n");
   print("# setting force_discharge on battery $b to $on\n");
   $bat_force_discharge[$b] = $on;
 }
 if ($ac_connected || !$bat_installed[0] || !$bat_installed[1]) {
   for $b (0..1) {
     set_force_discharge($b,0);
   }
 } else {
   if ($bat_remaining[0] > $bat_remaining[1] + $thresh) {
     set_force_discharge(0,1);
     set_force_discharge(1,0);
   } elsif ($bat_remaining[1] > $bat_remaining[0] + $thresh) {
     set_force_discharge(0,0);
     set_force_discharge(1,1);
   }
 }

}

while (1) {

 read_status;
 print_status;
 choose_discharge;
 sleep(5);

}

END {

 print("# Cleanup\n");
 write_file("$smapi_dir/BAT0/force_discharge", ('0'));
 write_file("$smapi_dir/BAT1/force_discharge", ('0'));

}