diff --git a/dragonbat b/dragonbat new file mode 100755 index 0000000..1b0fa5a --- /dev/null +++ b/dragonbat @@ -0,0 +1,157 @@ +#!/usr/bin/perl + +use warnings FATAL => 'all'; +use strict; + +use Data::Dumper; + +# turn on autoflush +$| = 1; + +sub booleq { + my ($s) = @_; + return sub { return $_[0] eq $s }; +} + +sub boolne { + my ($s) = @_; + return sub { return $_[0] ne $s }; +} + +sub numwh { + my ($s) = @_; + my ($n) = $s =~ m/^(.*) Wh$/; + return $n; +} + +sub htime { + my ($s) = @_; + if ($s =~ m/^([0-9.]+) hours$/) { + return int($1 * 60); + } elsif ($s =~ m/^([0-9.]+) minutes$/) { + return int($1); + } else { + die "unknown time: $s"; + } +} + +sub stime { + my ($n) = @_; + my $h = $n / 60; + my $m = $n % 60; + return sprintf("%02d:%02d", $h, $m); +} + +sub perc { + my ($n, $d) = @_; + return sprintf("%.1f%%", $n * 100 / $d); +} + +sub charge_time { + my ($data) = @_; + return $data->{full} ? "--:--" : stime($data->{tcharge}); +} + +sub drain_time { + my ($data) = @_; + return stime($data->{tdrain}); +} + +sub main { + if (@ARGV) { + my $t = $ARGV[0]; + do { + run(); + select(undef, undef, undef, $t); + } while (999); + } else { + run(); + } +} + +sub run { + my $data = init(); + #print Dumper($data) . "\n"; + display1($data, 5); +} + +sub mac_meta { + return ( + "ExternalConnected" => [["connected", booleq("Yes")]], + "AvgTimeToEmpty" => [["tdrain", \&int]], + "AvgTimeToFull" => [["tcharge", \&int]], + "MaxCapacity" => [["max", \&int]], + "CurrentCapacity" => [["cur", \&int]], + "FullyCharged" => [["full", booleq("Yes")]], + "IsCharging" => [["charging", booleq("Yes")]], + ); +} + +sub linux_meta { + return ( + "energy" => [["cur", \&numwh]], + "energy-full" => [["max", \&numwh]], + "time to empty" => [["tdrain", \&htime]], + "time to full" => [["tcharge", \&htime]], + "state" => [ + ["connected", boolne("discharging")], + ["full", booleq("fully-charged")], + ], + ) +} + +my $is_linux = $^O eq "linux"; + +my %meta = $is_linux ? linux_meta() : mac_meta(); + +sub mac_cmd { + return "ioreg -rc AppleSmartBattery"; +} + +sub mac_re { + return qr/^ *"(\w+)" = (.+)$/; +} + +sub linux_cmd { + return "upower -i `upower -e | grep battery`"; +} + +sub linux_re { + return qr/^ *([^:]+): +(.+)$/; +} + +sub init { + my $data = {}; + $data->{tcharge} = 0; + my $cmd = $is_linux ? linux_cmd() : mac_cmd(); + open(my $fh, "-|", $cmd); + my $re = $is_linux ? linux_re() : mac_re(); + while (my $line = <$fh>) { + my ($key, $val) = $line =~ m/$re/; + next unless $key && $val; + my $r = $meta{$key}; + next unless $r; + for my $p (@$r) { + $data->{$p->[0]} = $p->[1]->($val); + } + } + return $data; +} + +sub icon { + my ($data) = @_; + return $data->{full} ? "☀" : "⚡" if $data->{connected}; + return $data->{tdrain} < 30 ? "⚠" : "⌸"; +} + +sub display1 { + my ($data, $n) = @_; + my $c = $data->{connected}; + my $p = perc($data->{cur}, $data->{max}); + my $icon = icon($data); + my $f = $c ? \&charge_time : \&drain_time; + my $t = $f->($data); + printf("$icon %s (%s)\n", $t, $p); +} + +main();