#!/usr/bin/env perl use strict; use warnings; use 5.010; use JSON; open my $i3status, '-|', 'i3status'; print scalar(<$i3status>); print scalar(<$i3status>); while (<$i3status>) { my $comma = s/^,//; my $data = decode_json($_); my %line = map { $_->{name} => $_ } @$data; my $line = [ grep { $_->{name} eq 'wireless' || $_->{name} eq 'ethernet' ? $_->{instance} ne $_->{full_text} : $_->{name} ne 'load' } @$data ]; my $discharge_rate; my $battery_index = 0; for my $item (@$line) { if ($item->{name} eq 'wireless' || $item->{name} eq 'ethernet') { my $iface = $item->{instance}; my ($up, $down) = map { net_rate($iface, $_) } 'rx', 'tx'; my $rate = sprintf("%6s/%6s", map { human_bytes($_) } $up, $down); $item->{full_text} =~ s{^($iface:)}{$1 $rate}; $item->{color} = $up + $down >= 512000 ? '#FF0000' : $up + $down >= 51200 ? '#FFFF00' : $up + $down >= 5120 ? '#00FF00' : '#FFFFFF'; } if ($item->{name} eq 'cpu_usage') { my ($val) = $line{load}{full_text} * 100 / ncpu(); $item->{color} = $val >= 50 ? '#FF0000' : $val >= 25 ? '#FFFF00' : $val >= 5 ? '#00FF00' : '#FFFFFF'; } if ($item->{name} eq 'battery') { my ($state, $val) = $item->{full_text} =~ /(\w+) (\d+(?:\.\d+))%/; ($discharge_rate) = $item->{full_text} =~ /\(.* (.*)W\)/; $discharge_rate *= -1 if $state ne 'BAT'; $item->{full_text} =~ s/\((.*) (.*)W\)/\($1\)/; $item->{color} = $val >= 80 ? '#00FF00' : $state ne 'BAT' ? '#FFFFFF' : $val >= 40 ? '#FFFFFF' : $val >= 15 ? '#FFFF00' : $val >= 5 ? '#CD0000' : '#FF0000'; } $battery_index++ unless defined $discharge_rate; } if (defined $discharge_rate) { splice @$line, $battery_index + 1, 0, { name => 'battery_discharge', full_text => sprintf("%0.2fW", $discharge_rate), color => ( $discharge_rate < 8 ? '#FFFFFF' : $discharge_rate < 12 ? '#00FF00' : $discharge_rate < 20 ? '#FFFF00' : '#FF0000' ), }; } my $weather_file = "$ENV{HOME}/.weather"; if (-r $weather_file && -f $weather_file) { my $weather = slurp($weather_file); my ($temp) = $weather =~ /\d+%.*?(-?\d+)F.*?-?\d+F/; unshift @$line, { name => 'weather', full_text => $weather, color => ( $temp >= 90 ? '#FF0000' : $temp >= 78 ? '#FFFF00' : $temp >= 73 ? '#00FF00' : $temp >= 55 ? '#FFFFFF' : $temp >= 32 ? '#CDCDFF' : '#0000FF' ), }; } unshift @$line, { name => 'title', full_text => get_title() }; # XXX split battery discharge rate to a separate entry, so it can be # colored separately say(($comma ? ',' : ''), encode_json($line)); } print <$i3status>; sub get_title { my $node = find(decode_json(`i3-msg -t get_tree`)); if ($node->{window}) { return $node->{name}; } else { return '-none-'; } } sub find { my ($t) = @_; if ($t->{focused}) { return $t; } for my $subtree (@{ $t->{nodes} }, @{ $t->{floating_nodes} }) { my $found = find($subtree); return $found if $found; } return; } { my %last_bytes; sub net_rate { my ($iface, $which) = @_; my $stat_file = "/sys/class/net/$iface/statistics/${which}_bytes"; chomp(my $bytes = slurp($stat_file)); my $prev = $last_bytes{$iface}{$which}; $last_bytes{$iface}{$which} = $bytes; return 0 unless defined $prev; return $bytes - $prev; } } sub ncpu { scalar grep { !/^#/ } `lscpu -p=cpu` } sub human_bytes { my ($bytes) = @_; my @prefixes = ('', qw(k M G T)); my $prefix_index = 0; while ($bytes > 512) { $bytes /= 1024; $prefix_index++; } my $prec = ($prefix_index == 0 ? 0 : 1); return sprintf("%0.${prec}f%sB", $bytes, $prefixes[$prefix_index]); } sub slurp { open my $fh, '<:encoding(UTF-8)', $_[0] or die "Couldn't open $_[0]: $!"; if (wantarray) { <$fh> } else { local $/; <$fh> } }