#! /usr/bin/perl # Copyright (C) 2014 Antoine Beaupré # Copyright (C) 2008 Joey Schulze # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. # Munin plugin to monitor power usage in smart meters in the 900MHz ISM band # Inspired by the postfix_mailstats plugin from munin-contrib # Supported configuration: # # [amr] # user root # group adm # env.logdir /var/log # env.logfile rtlamr.log use strict; use warnings; use Munin::Plugin; my $LOGDIR = $ENV{'logdir'} || '/var/log'; my $LOGFILE = $ENV{'logfile'} || 'rtlamr.log'; my $unit = $ENV{'unit'} || 'J'; if ($unit ne 'J' and $unit ne 'Wh') { print "invalid unit, reverting to Joule"; $unit = 'J'; } my $logfile = $LOGDIR .'/'. $LOGFILE; # station id => power consumption (in kWh) my %stations; # number of signals sent per station my %signals; my $count; sub autoconf { if (-d $LOGDIR) { if (-f $logfile) { print "yes\n"; } else { print "no (logfile not found)\n"; } } else { print "no (could not find logdir)\n"; } exit 0; } sub config { print "multigraph amr_power\n"; print "graph_title Power consumption\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Watt\n"; print "graph_total Total\n"; print "graph_category sensors\n"; $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station power ' . $station); printf "%s.label station %d\n", $name, $count; printf "%s.type COUNTER\n", $name; if ($count++) { printf "%s.draw STACK\n", $name; } else { printf "%s.draw AREA\n", $name; } printf "%s.min 0\n", $name; } print "multigraph amr_meter\n"; print "graph_title Meter reading\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel kWh\n"; print "graph_scale no\n"; print "graph_category sensors\n"; $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station meter ' . $station); printf "%s.label station %d\n", $name, $count++; printf "%s.type GAUGE\n", $name; printf "%s.min 0\n", $name; } print "multigraph amr_stations\n"; print "graph_title Known AMR stations\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel stations\n"; print "graph_category sensors\n"; print "stations.label number of stations\n"; print "multigraph amr_signals\n"; print "graph_title Number of signals received\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel signals / \${graph_period}\n"; print "graph_period minute\n"; print "graph_category sensors\n"; $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station signals ' . $station); printf "%s.label station %d\n", $name, $count++; printf "%s.type ABSOLUTE\n", $name; } exit 0; } sub parse { my $logfile = shift; my $pos = shift; my ($log,$rotated) = tail_open $logfile, $pos; while (<$log>) { # \d protects us against HTML injection here, be careful when changing if (m,SCM:\{ID:(\d+) +.* +Consumption: +(\d+) +,) { $stations{$1} = $2; $signals{$1}++; } } return tail_close $log; } need_multigraph(); autoconf if $#ARGV > -1 && $ARGV[0] eq "autoconf"; my @state_vector = restore_state; my $pos = shift @state_vector || 0; %stations = @state_vector; $pos = parse $logfile, $pos; if ($#ARGV > -1 && $ARGV[0] eq "config") { config; # don't save position on config so next run will reparse it } else { # this may not scale so well with large graphs, and is useful only # for debugging, when you want to run this repeatedly without # losing data # this will also mean that stations that disappear will remain forever save_state $pos, %stations; } print "multigraph amr_power\n"; foreach my $station (sort keys %stations) { # the number from the counter is "Wh over 5 minutes", we want to # give munin joules so he can generate watts by dividing by the period # 1 Wh = W * 3600 s # = 3600 s * J / s # = 3.6 kJ my $power = $stations{$station} * 3600; if ($unit eq 'Wh') { # since we want absolute, we'll multiply our result by 300, the assumed time period $power = $stations{$station} * 300; } printf "%s.value %d\n", clean_fieldname('station power ' . $station), $power; } print "multigraph amr_meter\n"; foreach my $station (sort keys %stations) { printf "%s.value %d\n", clean_fieldname('station meter ' . $station), $stations{$station}; } print "multigraph amr_stations\n"; printf "stations.value %d\n", scalar keys %stations; print "multigraph amr_signals\n"; foreach my $station (sort keys %signals) { printf "%s.value %d\n", clean_fieldname('station signals ' . $station), $signals{$station}; }