<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.thinkwiki.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ttsec</id>
	<title>ThinkWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.thinkwiki.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ttsec"/>
	<link rel="alternate" type="text/html" href="https://www.thinkwiki.org/wiki/Special:Contributions/Ttsec"/>
	<updated>2026-05-25T14:01:36Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.12</generator>
	<entry>
		<id>https://www.thinkwiki.org/w/index.php?title=Code/tp-bat-balance&amp;diff=44069</id>
		<title>Code/tp-bat-balance</title>
		<link rel="alternate" type="text/html" href="https://www.thinkwiki.org/w/index.php?title=Code/tp-bat-balance&amp;diff=44069"/>
		<updated>2009-09-07T22:36:04Z</updated>

		<summary type="html">&lt;p&gt;Ttsec: Added case where program started without battery in slot or bay would break program.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#!/usr/bin/perl&lt;br /&gt;
# Keep two ThinkPad batteries (system battery and UltraBay) at similar charge levels&lt;br /&gt;
# during discharge by switching back and forth. This reduces wear on the UltraBay&lt;br /&gt;
# battery, compared to the hardware's default strategy of fully draining the UltraBay&lt;br /&gt;
# battery before switching to the system battery.&lt;br /&gt;
# WARNING: This script is experimental and uses undocumented hardware features.&lt;br /&gt;
# WARNING: If this script crashes, your battery may be forced to keep draining until empty.&lt;br /&gt;
# Distributed under the terms of the GNU General Public License v2 or later.&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use File::Slurp;&lt;br /&gt;
&lt;br /&gt;
my $thresh = 3; # difference between battery charge levels that justifies switching (hysteresis)&lt;br /&gt;
&lt;br /&gt;
my $default_discharge = 0; # the battery that's discharged as first priority by the BIOS&lt;br /&gt;
my $smapi_dir = '/sys/devices/platform/smapi';&lt;br /&gt;
&lt;br /&gt;
my $ac_connected;&lt;br /&gt;
my @bat_installed;&lt;br /&gt;
my @bat_remaining;&lt;br /&gt;
my @bat_state;&lt;br /&gt;
my @bat_power_avg;&lt;br /&gt;
my @bat_force_discharge;&lt;br /&gt;
&lt;br /&gt;
$SIG{'INT'} = $SIG{'QUIT'} = $SIG{'TERM'} = sub { die(&amp;quot;# Killed by SIG$_[0]\n&amp;quot;); };&lt;br /&gt;
&lt;br /&gt;
sub read_chomp_file {&lt;br /&gt;
  my ($filename) = @_;&lt;br /&gt;
  my ($x) = read_file($filename) or die &amp;quot;Cannot read $filename\n&amp;quot;;&lt;br /&gt;
  chomp($x);&lt;br /&gt;
  return $x;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub read_status {&lt;br /&gt;
  $ac_connected = read_chomp_file(&amp;quot;$smapi_dir/ac_connected&amp;quot;);&lt;br /&gt;
  for my $b (0..1) {&lt;br /&gt;
    $bat_installed[$b] = read_chomp_file(&amp;quot;$smapi_dir/BAT$b/installed&amp;quot;);&lt;br /&gt;
    $bat_force_discharge[$b] = read_chomp_file(&amp;quot;$smapi_dir/BAT$b/force_discharge&amp;quot;);&lt;br /&gt;
    if ($bat_installed[$b]) {&lt;br /&gt;
      $bat_remaining[$b] = read_chomp_file(&amp;quot;$smapi_dir/BAT$b/remaining_percent&amp;quot;);&lt;br /&gt;
      $bat_state[$b] = read_chomp_file(&amp;quot;$smapi_dir/BAT$b/state&amp;quot;);&lt;br /&gt;
      $bat_power_avg[$b] = read_chomp_file(&amp;quot;$smapi_dir/BAT$b/power_avg&amp;quot;) / 1000.0;&lt;br /&gt;
    }&lt;br /&gt;
    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.&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub print_status {&lt;br /&gt;
  print &amp;quot;   &amp;quot;;&lt;br /&gt;
  sub print_bat {&lt;br /&gt;
    my ($b) = @_;&lt;br /&gt;
    my ($ll,$lr,$rl,$rr) = $b ? ('-','&amp;gt;','&amp;lt;','-') : ('&amp;lt;','-','-','&amp;gt;');&lt;br /&gt;
    my $icon = sprintf(&amp;quot;[%3s]&amp;quot;, $bat_installed[$b] ? $bat_remaining[$b].&amp;quot;%&amp;quot; : &amp;quot;&amp;quot;);&lt;br /&gt;
    my $arrow;&lt;br /&gt;
    my $state = $bat_state[$b];&lt;br /&gt;
    if ($state eq 'charging') {&lt;br /&gt;
      $arrow = sprintf(&amp;quot;$ll--%4.1f--$lr&amp;quot;, $bat_power_avg[$b]);&lt;br /&gt;
    } elsif ($state eq 'discharging') {&lt;br /&gt;
      $arrow = sprintf(&amp;quot;$rl--%4.1f--$rr&amp;quot;, -$bat_power_avg[$b]);&lt;br /&gt;
    } elsif ($state eq 'idle' || $state eq 'none') {  #Added none to cover case with no battery in slot when program was started.&lt;br /&gt;
      $arrow = &amp;quot;          &amp;quot;;&lt;br /&gt;
    } else {&lt;br /&gt;
      die &amp;quot;Unknown state $state for battery $b&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    print($b ? &amp;quot;$arrow$icon&amp;quot; : &amp;quot;$icon$arrow&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  print_bat(0);&lt;br /&gt;
  print($ac_connected ? ' {AC} ' : ' {  } ');&lt;br /&gt;
  print_bat(1);&lt;br /&gt;
  print(&amp;quot;\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub choose_discharge {&lt;br /&gt;
  # Choose which battery to discharge&lt;br /&gt;
&lt;br /&gt;
  sub set_force_discharge {&lt;br /&gt;
    my ($b,$on) = @_;&lt;br /&gt;
    return if $b!=$default_discharge; # the non-default battery will be discharged only when necessary anyway&lt;br /&gt;
    return if $bat_force_discharge[$b]==$on;&lt;br /&gt;
    write_file(&amp;quot;$smapi_dir/BAT$b/force_discharge&amp;quot;, ($on?'1':'0')) or die (&amp;quot;Cannot write to $smapi_dir/BAT$b/force_discharge: $!\n&amp;quot;);&lt;br /&gt;
    print(&amp;quot;# setting force_discharge on battery $b to $on\n&amp;quot;);&lt;br /&gt;
    $bat_force_discharge[$b] = $on;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ($ac_connected || !$bat_installed[0] || !$bat_installed[1]) {&lt;br /&gt;
    for $b (0..1) {&lt;br /&gt;
      set_force_discharge($b,0);&lt;br /&gt;
    }&lt;br /&gt;
  } else {&lt;br /&gt;
    if ($bat_remaining[0] &amp;gt; $bat_remaining[1] + $thresh) {&lt;br /&gt;
      set_force_discharge(0,1);&lt;br /&gt;
      set_force_discharge(1,0);&lt;br /&gt;
    } elsif ($bat_remaining[1] &amp;gt; $bat_remaining[0] + $thresh) {&lt;br /&gt;
      set_force_discharge(0,0);&lt;br /&gt;
      set_force_discharge(1,1);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
while (1) {&lt;br /&gt;
  read_status;&lt;br /&gt;
  print_status;&lt;br /&gt;
  choose_discharge;&lt;br /&gt;
  sleep(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
END {&lt;br /&gt;
  print(&amp;quot;# Cleanup\n&amp;quot;);&lt;br /&gt;
  write_file(&amp;quot;$smapi_dir/BAT0/force_discharge&amp;quot;, ('0'));&lt;br /&gt;
  write_file(&amp;quot;$smapi_dir/BAT1/force_discharge&amp;quot;, ('0'));&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Ttsec</name></author>
		
	</entry>
</feed>