From 9860b097589389e029b1320380cc2e630f5165ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Beaupr=C3=A9?= Date: Fri, 21 Mar 2014 07:49:11 -0400 Subject: [PATCH 1/3] add plugin to monitor "smart meters" power usage this connects munin with the RTLAMR project, and allows readout of AMR (Automatic Meter Reporting) data over the airwaves This plugin will draw some nice graphs of your neighbors (and yourself, coincidentally) power consumption. You can also see the adoption of the AMR in your neighborhood and density of signal. --- plugins/amr/amr.pl | 190 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100755 plugins/amr/amr.pl diff --git a/plugins/amr/amr.pl b/plugins/amr/amr.pl new file mode 100755 index 00000000..d8b254f7 --- /dev/null +++ b/plugins/amr/amr.pl @@ -0,0 +1,190 @@ +#! /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; + +sub autoconf +{ + if (-d $LOGDIR) { + if (-f $logfile) { + print "yes\n"; + exit 0; + } else { + print "no (logfile not found)\n"; + } + } else { + print "no (could not find logdir)\n"; + } + exit 1; +} + +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 AMR\n"; + + my $first = 1; + foreach my $station (sort keys %stations) { + my $name = clean_fieldname('station power ' . $station); + printf "%s.label station %d\n", $name, $station; + printf "%s.type COUNTER\n", $name; + if ($first) { + printf "%s.draw AREA\n", $name; + $first = 0; + } + else { + printf "%s.draw STACK\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 AMR\n"; + + my $first = 0; + foreach my $station (sort keys %stations) { + my $name = clean_fieldname('station meter ' . $station); + printf "%s.label station %d\n", $name, $station; + 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 AMR\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 AMR\n"; + foreach my $station (sort keys %stations) { + my $name = clean_fieldname('station signals ' . $station); + printf "%s.label station %d\n", $name, $station; + 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 + # loosing 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}; +} From 2ecac880e90b503e1e55f0251b632e91f78fa9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Beaupr=C3=A9?= Date: Fri, 21 Mar 2014 08:07:58 -0400 Subject: [PATCH 2/3] anonymise labels data is still stored with the full identifiers so this change is backwards compatible but at least we are not retransmiting data off the airwaves and it is anonymised in some way --- plugins/amr/amr.pl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/amr/amr.pl b/plugins/amr/amr.pl index d8b254f7..8adbe6c8 100755 --- a/plugins/amr/amr.pl +++ b/plugins/amr/amr.pl @@ -47,6 +47,7 @@ my $logfile = $LOGDIR .'/'. $LOGFILE; my %stations; # number of signals sent per station my %signals; +my $count; sub autoconf { @@ -72,17 +73,16 @@ sub config print "graph_total Total\n"; print "graph_category AMR\n"; - my $first = 1; + $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station power ' . $station); - printf "%s.label station %d\n", $name, $station; + printf "%s.label station %d\n", $name, $count; printf "%s.type COUNTER\n", $name; - if ($first) { - printf "%s.draw AREA\n", $name; - $first = 0; + if ($count++) { + printf "%s.draw STACK\n", $name; } else { - printf "%s.draw STACK\n", $name; + printf "%s.draw AREA\n", $name; } printf "%s.min 0\n", $name; } @@ -94,10 +94,10 @@ sub config print "graph_scale no\n"; print "graph_category AMR\n"; - my $first = 0; + $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station meter ' . $station); - printf "%s.label station %d\n", $name, $station; + printf "%s.label station %d\n", $name, $count++; printf "%s.type GAUGE\n", $name; printf "%s.min 0\n", $name; } @@ -115,9 +115,10 @@ sub config print "graph_vlabel signals / \${graph_period}\n"; print "graph_period minute\n"; print "graph_category AMR\n"; + $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station signals ' . $station); - printf "%s.label station %d\n", $name, $station; + printf "%s.label station %d\n", $name, $count++; printf "%s.type ABSOLUTE\n", $name; } From b44c20b49527229f0a2425c9e14c832805213a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Beaupr=C3=A9?= Date: Fri, 21 Mar 2014 08:09:18 -0400 Subject: [PATCH 3/3] follow file naming convention here --- plugins/amr/{amr.pl => amr} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/amr/{amr.pl => amr} (100%) diff --git a/plugins/amr/amr.pl b/plugins/amr/amr similarity index 100% rename from plugins/amr/amr.pl rename to plugins/amr/amr