165 lines
3.4 KiB
Perl
Executable File
165 lines
3.4 KiB
Perl
Executable File
#!/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 "--:--" if $data->{full} || $data->{tcharge} == 0;
|
|
return stime($data->{tcharge});
|
|
}
|
|
|
|
sub drain_time {
|
|
my ($data) = @_;
|
|
return "--:--" if $data->{tdrain} == 0;
|
|
return stime($data->{tdrain});
|
|
}
|
|
|
|
sub main {
|
|
if (@ARGV) {
|
|
my $t = $ARGV[0];
|
|
# require a positive number of seconds (e.g. 2, 4.5, 0.2, ...)
|
|
unless ($t =~ m/^([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]*[1-9][0-9]*)$/) {
|
|
print "usage: $0 [POLL-SECS]\n";
|
|
exit(1);
|
|
}
|
|
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();
|