#!/usr/bin/env perl # -*- perl -*- =head1 NAME freemobile - A plugin to monitor a freemobile subscription =head1 INSTALLATION Create a link to this script in /etc/munin/plugins/ : $ ln -s /path/to/freemobile-munin /etc/munin/plugins/freemobile =head1 CONFIGURATION You need to configure the plugin like that: [freemobile] user florent env.HOME /home/florent env.phonenumber 06ABCDEFGH env.freemonitored voice sms mms data specialvoice env.cache_expire 3600 timeout 30 C I: user with freemobile backend configured C I: path to user home C (optional): add your phone number if you have more than one subscription for this account. C (optional): default only 'voice sms' The full list of monitored options is : * voice sms mms data specialvoice voicetoint voiceint smsint mmsint dataint C (optional): cache interval in second, or time between two connection to the website. The cache interval is 3 hours by default. C (optional): Munin internal option. The plugin can be slow, 30s is recommended. =head1 LICENSE LGPLv3 =cut use strict; use warnings; use Carp; use English qw(-no_match_vars); #use encoding 'iso-8859-15'; # Munin doesn't like utf-8 :-( use Encode; my @monitored = split / /, $ENV{'freemonitored'} || 'voice sms'; my $cachedir = $ENV{'HOME'} . '/.config/weboob/munin/'; my $cachefile = "$cachedir/freemobile-munin"; my $refreshtime = $ENV{'cache_expire'} || 10_800; my $phone = $ENV{'phonenumber'}; my $account = ''; if (length($phone) > 0) { $account = $phone . '@freemobile'; } my $weboob = 'boobill -f table -b freemobile details ' . $account; my $cache_fh; my %label = ( 'voice' => 'Voix en France (min)', 'voicetoint' => 'Voix vers l\'international (min)', 'specialvoice' => 'Numéros spéciaux (min)', 'sms' => 'SMS en France', 'mms' => 'MMS en France', 'data' => 'Data en France', 'voiceint' => 'Voix à l\'international (min)', 'smsint' => 'SMS à l\'international', 'mmsint' => 'MMS à l\'international', 'dataint' => 'Data à l\'international', ); my %linenum = ( 'voice' => 3, 'voicetoint' => 3, 'specialvoice' => 4, 'sms' => 5, 'mms' => 6, 'data' => 7, 'voiceint' => 8, 'smsint' => 9, 'mmsint' => 10, 'dataint' => 11, ); my %regexp = ( 'voice' => 'National : (\d+)h(\d+)min(\d+)s', 'voicetoint' => 'International : (\d+)h(\d+)min(\d+)s', 'specialvoice' => '\| (\d+)h(\d+) min (\d+)s', 'sms' => 'Conso SMS \s+ \| (\d+) \/ (.*)', 'mms' => 'Vous avez consommé (\d+) MMS', 'data' => 'Vous avez consommé ([\d\-\.]+) (Mo|Go)', 'voiceint' => 'Appels émis (\d+)h(\d+)min(\d+)s', 'smsint' => 'Conso SMS (international) \| (\d+)', 'mmsint' => 'Vous avez consommé (\d+) MMS', 'dataint' => 'Vous avez consommé ([\d\-\.]+) (Mo|Go)', ); my %post = ( 'voice' => 'postvoice', 'voicetoint' => 'postvoice', 'specialvoice' => 'postvoice', 'sms' => 'simplepost', 'mms' => 'simplepost', 'data' => 'datapost', 'voiceint' => 'postvoice', 'smsint' => 'simplepost', 'mmsint' => 'simplepost', 'dataint' => 'datapost', ); sub doubleprint { my $var = shift; print {$cache_fh} $var; print $var; return 0; } sub postvoice { my @args = @_; my $minutes = $args[0] * 60 + $args[1] + $args[2] / 60; doubleprint "$minutes \n"; return 0; } sub simplepost { my @args = @_; doubleprint "$args[0] \n"; return 0; } sub datapost { my @args = @_; my $multi = 1; my $unit = $args[1]; if ($unit eq "Go") { $multi = 1024; } $multi = $args[0] * $multi; doubleprint "$multi \n"; return 0; } sub config { binmode STDOUT, ':encoding(iso-8859-1)'; print <<'EOF'; graph_title Conso Free graph_vlabel Suivi conso du forfait Free Mobile graph_category weboob graph_args -l 0 EOF foreach (@monitored) { print "$_.label $label{$_}\n"; } return 0; } sub fetch { my @cache_data; # Check if cache exist and not older than the refresh threshold. if ( open $cache_fh, '<', $cachefile ) { @cache_data = <$cache_fh>; close $cache_fh or croak "unable to close: $ERRNO"; # File not empty? if ( @cache_data > 0 ) { # Data is still fresh. Display cached values and exit. if ( time - $cache_data[0] < $refreshtime ) { print join q{}, @cache_data[ 1 .. $#cache_data ]; exit 0; } } } # execute weboob open my $data, q(-|), $weboob or croak "Couldn't execute program: $ERRNO"; my @lines = <$data>; close $data or carp "unable to close: $ERRNO"; # If error output, print the cache (if exist) and exit if ( @lines == 0 ) { if ( @cache_data > 0 ) { print join q{}, @cache_data[ 1 .. $#cache_data ]; exit 0; } exit 1; } # Open cache for writing open $cache_fh, '>', $cachefile or croak "Failed to open file $cachefile"; print {$cache_fh} time . "\n"; foreach my $monit (@monitored) { doubleprint "$monit.value "; if ( my @results = $lines[ $linenum{$monit} ] =~ /$regexp{$monit}/ ) { my $postfunction = $post{$monit}; &{ \&$postfunction }(@results); } else { doubleprint "0 \n"; } } close $cache_fh or croak "unable to close: $ERRNO"; return 0; } # Create the munin cache dir if missing if ( !-d $cachedir ) { mkdir $cachedir; } if ( $ARGV[0] and $ARGV[0] eq 'config' ) { config; } else { fetch; } __END__