Skip to content
freemobile-munin 5.83 KiB
Newer Older
#!/usr/bin/env perl
# -*- perl -*-
=head1 NAME
freemobile - A plugin to monitor a freemobile subscription
Florent Fourcot's avatar
Florent Fourcot committed
=head1 INSTALLATION
Florent Fourcot's avatar
Florent Fourcot committed
Create a link to this script in /etc/munin/plugins/ :
$ ln -s /path/to/freemobile-munin /etc/munin/plugins/freemobile
theo's avatar
theo committed

Florent Fourcot's avatar
Florent Fourcot committed
You need to configure the plugin like that:
theo's avatar
theo committed
    [freemobile]
    user florent
Florent Fourcot's avatar
Florent Fourcot committed
    env.HOME /home/florent
    env.phonenumber 06ABCDEFGH
Florent Fourcot's avatar
Florent Fourcot committed
    env.freemonitored voice sms mms data specialvoice
    env.cache_expire 3600
    timeout 30

C<user> I<required>: user with freemobile backend configured

C<env.HOME> I<required>: path to user home
C<env.phonenumber> (optional): add your phone number if you have more than one
subscription for this account.

Florent Fourcot's avatar
Florent Fourcot committed
C<env.freemonitored> (optional): default only 'voice sms'
The full list of monitored options is :
 * voice sms mms data specialvoice voicetoint voiceint smsint mmsint dataint
C<env.cache_expire 3600> (optional): cache interval in second, or time
Florent Fourcot's avatar
Florent Fourcot committed
between two connection to the website. The cache interval is 3 hours by default.
C<timeout 30> (optional): Munin internal option. The plugin can be slow,
30s is recommended.
theo's avatar
theo committed

=head1 LICENSE

Florent's avatar
Florent committed

theo's avatar
theo committed
use strict;
theo's avatar
theo committed
use Carp;
use English qw(-no_match_vars);
#use encoding 'iso-8859-15';    # Munin doesn't like utf-8 :-(
theo's avatar
theo committed
my @monitored = split / /, $ENV{'freemonitored'} || 'voice sms';
my $cachedir  = $ENV{'HOME'} . '/.config/weboob/munin/';
my $cachefile = "$cachedir/freemobile-munin";
theo's avatar
theo committed

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;
theo's avatar
theo committed
my $cache_fh;
theo's avatar
theo committed
    'voice'        => 'Voix en France (min)',
    'voicetoint'   => 'Voix vers l\'international (min)',
Florent Fourcot's avatar
Florent Fourcot committed
    'specialvoice' => 'Numéros spéciaux (min)',
theo's avatar
theo committed
    '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',
);
theo's avatar
theo committed
    'voice'        => 3,
    'voicetoint'   => 3,
theo's avatar
theo committed
    'sms'          => 5,
    'mms'          => 6,
    'data'         => 7,
    'voiceint'     => 8,
    'smsint'       => 9,
    'mmsint'       => 10,
    'dataint'      => 11,
);
theo's avatar
theo committed
    'voice'        => 'National : (\d+)h(\d+)min(\d+)s',
    'voicetoint'   => 'International : (\d+)h(\d+)min(\d+)s',
    'specialvoice' => '\| (\d+)h(\d+) min (\d+)s',
Florent Fourcot's avatar
Florent Fourcot committed
    'sms'          => 'Conso SMS \s+ \| (\d+) \/ (.*)',
theo's avatar
theo committed
    'mms'          => 'Vous avez consommé (\d+) MMS',
Florent's avatar
Florent committed
    'data'         => 'Vous avez consommé ([\d\-\.]+) (Mo|Go)',
theo's avatar
theo committed
    'voiceint'     => 'Appels émis (\d+)h(\d+)min(\d+)s',
    'smsint'       => 'Conso SMS (international)  \| (\d+)',
    'mmsint'       => 'Vous avez consommé (\d+) MMS',
Florent's avatar
Florent committed
    'dataint'      => 'Vous avez consommé ([\d\-\.]+) (Mo|Go)',
theo's avatar
theo committed
);
theo's avatar
theo committed
    'voice'        => 'postvoice',
    'voicetoint'   => 'postvoice',
    'specialvoice' => 'postvoice',
theo's avatar
theo committed
    'sms'          => 'simplepost',
    'mms'          => 'simplepost',
Florent's avatar
Florent committed
    'data'         => 'datapost',
theo's avatar
theo committed
    'voiceint'     => 'postvoice',
    'smsint'       => 'simplepost',
    'mmsint'       => 'simplepost',
Florent's avatar
Florent committed
    'dataint'      => 'datapost',
theo's avatar
theo committed
);

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;
}

Florent's avatar
Florent committed
sub datapost {
    my @args = @_;
    my $multi = 1;
    my $unit = $args[1];
    if ($unit eq "Go") {
Florent's avatar
Florent committed
        $multi = 1024;
    }
    $multi = $args[0] * $multi;
    doubleprint "$multi \n";
    return 0;
Florent's avatar
Florent committed
}

theo's avatar
theo committed
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
theo's avatar
theo committed
        print "$_.label $label{$_}\n";
theo's avatar
theo committed
    return 0;
theo's avatar
theo committed

sub fetch {
    my @cache_data;
theo's avatar
theo committed
    # Check if cache exist and not older than the refresh threshold.
    if ( open $cache_fh, '<', $cachefile ) {
        @cache_data = <$cache_fh>;
theo's avatar
theo committed
        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
theo's avatar
theo committed
    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";
theo's avatar
theo committed
    print {$cache_fh} time . "\n";

    foreach my $monit (@monitored) {
        doubleprint "$monit.value ";
        if ( my @results = $lines[ $linenum{$monit} ] =~ /$regexp{$monit}/ ) {
            my $postfunction = $post{$monit};
theo's avatar
theo committed
            &{ \&$postfunction }(@results);
theo's avatar
theo committed
            doubleprint "0 \n";
theo's avatar
theo committed
    close $cache_fh or croak "unable to close: $ERRNO";
    return 0;
# Create the munin cache dir if missing
if ( !-d $cachedir ) {
    mkdir $cachedir;
}

theo's avatar
theo committed
if ( $ARGV[0] and $ARGV[0] eq 'config' ) {
    config;
theo's avatar
theo committed
else {
    fetch;
theo's avatar
theo committed
__END__