Révision a806f7d8
Add support for CC02 devices
Thanks to David Edmondson <dme@dme.org>, currentcost now supports CC02
devices. To make use of this make sure to set env.metertype to "CC02".
| plugins/other/currentcost | ||
|---|---|---|
| 37 | 37 |
|
| 38 | 38 |
This configuration section shows the defaults of the plugin: |
| 39 | 39 |
|
| 40 |
[currentcost] |
|
| 41 |
env.device /dev/ttyUSB0 |
|
| 42 |
env.baud 2400 |
|
| 43 |
env.tick 6 |
|
| 44 |
env.currency £ |
|
| 45 |
env.rate1 13.9 |
|
| 46 |
env.rate1qty 900 |
|
| 47 |
env.rate2 8.2 |
|
| 48 |
env.nightrate 0 |
|
| 49 |
env.nighthours 23:30-06:30 |
|
| 50 |
env.standingcharge 0.0 |
|
| 40 |
[currentcost] |
|
| 41 |
env.device /dev/ttyUSB0 |
|
| 42 |
env.baud 2400 |
|
| 43 |
env.tick 6 |
|
| 44 |
env.currency £ |
|
| 45 |
env.rate1 13.9 |
|
| 46 |
nenv.rate1qty 900 |
|
| 47 |
env.rate2 8.2 |
|
| 48 |
env.nightrate 0 |
|
| 49 |
env.nighthours 23:30-06:30 |
|
| 50 |
env.standingcharge 0.0 |
|
| 51 |
env.metertype CC128 |
|
| 51 | 52 |
|
| 52 | 53 |
The configuration can be broken down into the following subsections: |
| 53 | 54 |
|
| ... | ... | |
| 101 | 102 |
|
| 102 | 103 |
The standing charge in hundreths of a C<env.currency> per month. If you do not have a standing charge, set this to 0. |
| 103 | 104 |
|
| 105 |
=item env.metertype |
|
| 106 |
|
|
| 107 |
The type of the meter. Currently "CC128" and "CC02" are supported. |
|
| 108 |
|
|
| 104 | 109 |
=back |
| 105 | 110 |
|
| 106 | 111 |
=head1 MAGIC MARKERS |
| 107 | 112 |
|
| 108 |
#%# family=manual contrib
|
|
| 109 |
#%# capabilities=multigraph |
|
| 113 |
#%# family=auto contrib
|
|
| 114 |
#%# capabilities=multigraph autoconf
|
|
| 110 | 115 |
|
| 111 | 116 |
=head1 AUTHOR |
| 112 | 117 |
|
| ... | ... | |
| 118 | 123 |
use warnings; |
| 119 | 124 |
use utf8; |
| 120 | 125 |
use Munin::Plugin; |
| 121 |
use Data::Dump qw{pp};
|
|
| 122 | 126 |
|
| 123 | 127 |
need_multigraph(); |
| 124 | 128 |
|
| 125 |
my $device_node = $ENV{device} || "/dev/ttyUSB0";
|
|
| 126 |
my $baud_rate = $ENV{baud} || "2400"; # or 9600 or 57600
|
|
| 127 |
my $tick_rate = $ENV{tick} || "6";
|
|
| 128 |
|
|
| 129 |
my $device_node = $ENV{device} || "/dev/ttyUSB0";
|
|
| 130 |
my $baud_rate = $ENV{baud} || "2400"; # or 9600 or 57600
|
|
| 131 |
my $tick_rate = $ENV{tick} || "6";
|
|
| 129 | 132 |
# Tick_Rate is how long to consider data valid for (in seconds) |
| 130 | 133 |
|
| 131 | 134 |
# Costs |
| 132 |
my $currency = $ENV{currency} || "£"; # £ or €
|
|
| 133 |
my $rate1 = $ENV{rate1} || "13.9"; # in pence/cents
|
|
| 134 |
my $rate1qty = $ENV{rate1qty} || "900"; # in kWh, 0 to use fixed rate2
|
|
| 135 |
my $rate2 = $ENV{rate2} || "8.2"; # in pence/cents
|
|
| 136 |
my $nightrate = $ENV{nightrate} || "0"; # 0 = disabled
|
|
| 137 |
my $nighthours = $ENV{nighthours} || "23:30-06:30";
|
|
| 138 |
my $standingcharge = $ENV{standingcharge} || "0.0"; # pence/cents per month
|
|
| 135 |
my $currency = $ENV{currency} || "£"; # £ or €
|
|
| 136 |
my $rate1 = $ENV{rate1} || "13.9"; # in pence/cents
|
|
| 137 |
my $rate1qty = $ENV{rate1qty} || "900"; # in kWh, 0 to use fixed rate2
|
|
| 138 |
my $rate2 = $ENV{rate2} || "8.2"; # in pence/cents
|
|
| 139 |
my $nightrate = $ENV{nightrate} || "0"; # 0 = disabled
|
|
| 140 |
my $nighthours = $ENV{nighthours} || "23:30-06:30";
|
|
| 141 |
my $standingcharge = $ENV{standingcharge} || "0.0"; # pence/cents per month
|
|
| 142 |
my $metertype = $ENV{metertype} || "CC128"; # or "CC02"
|
|
| 143 |
|
|
| 144 |
my $MUNIN_DEBUG = $ENV{MUNIN_DEBUG} || 0; # Set by munin-run
|
|
| 139 | 145 |
|
| 140 | 146 |
my $ret; |
| 141 | 147 |
if ( !eval "require XML::Simple;" ) {
|
| ... | ... | |
| 168 | 174 |
if ( !@lastread |
| 169 | 175 |
or time >= Time::Local::timelocal(@lastread) + ($tick_rate) ) |
| 170 | 176 |
{
|
| 177 |
print "# Saving Data (Data is " |
|
| 178 |
. ( time - Time::Local::timelocal(@lastread) ) |
|
| 179 |
. " seconds old)\n" |
|
| 180 |
if $MUNIN_DEBUG; |
|
| 171 | 181 |
@lastread = localtime(time); |
| 172 | 182 |
|
| 173 | 183 |
my @save_vector; |
| ... | ... | |
| 198 | 208 |
{
|
| 199 | 209 |
|
| 200 | 210 |
# Data is stale |
| 211 |
print "# Data is stale ("
|
|
| 212 |
. ( time - Time::Local::timelocal(@lastread) ) |
|
| 213 |
. " seconds). Re-reading device\n" |
|
| 214 |
if $MUNIN_DEBUG; |
|
| 215 |
|
|
| 201 | 216 |
eval {
|
| 202 | 217 |
|
| 203 | 218 |
# Fetch the XML |
| 204 |
my @temparray = collect_cc128_data( $device_node, $baud_rate ); |
|
| 219 |
my @temparray; |
|
| 220 |
if ( $metertype eq "CC128" ) {
|
|
| 221 |
@temparray = collect_cc128_data( $device_node, $baud_rate ); |
|
| 222 |
} |
|
| 223 |
elsif ( $metertype eq "CC02" ) {
|
|
| 224 |
@temparray = collect_cc02_data( $device_node, $baud_rate ); |
|
| 225 |
} |
|
| 226 |
else {
|
|
| 227 |
die "Unknown meter type $metertype."; |
|
| 228 |
} |
|
| 205 | 229 |
|
| 206 |
# Read the time so we know whether to reset daily/monthly/yearly counters |
|
| 230 |
# Read the time so we know whether to reset |
|
| 231 |
# daily/monthly/yearly counters |
|
| 207 | 232 |
my @now = localtime(time); |
| 208 | 233 |
my %is_new; |
| 209 | 234 |
$is_new{daily} = ( $now[3] != $lastread[3] ) ? 1 : 0;
|
| ... | ... | |
| 222 | 247 |
if ( $is_new{$period} ) {
|
| 223 | 248 |
|
| 224 | 249 |
# Start of a new period, reset the counter |
| 250 |
print "# Start of a new $period period." . |
|
| 251 |
" Resetting counter\n" if $MUNIN_DEBUG; |
|
| 225 | 252 |
$temparray[$i]->{data}[$j]->{$period} = 0;
|
| 226 | 253 |
} |
| 227 | 254 |
else {
|
| ... | ... | |
| 266 | 293 |
if ( $now_time >= $start_time |
| 267 | 294 |
or $now_time <= $stop_time ) |
| 268 | 295 |
{
|
| 296 |
print "# Night rate is ACTIVE\n" if $MUNIN_DEBUG; |
|
| 269 | 297 |
return 1; |
| 270 | 298 |
} |
| 271 | 299 |
else {
|
| 300 |
print "# Night rate is enabled, but NOT active\n" if $MUNIN_DEBUG; |
|
| 272 | 301 |
return 0; |
| 273 | 302 |
} |
| 274 | 303 |
} |
| ... | ... | |
| 279 | 308 |
|
| 280 | 309 |
=head2 Classic format |
| 281 | 310 |
|
| 282 |
Note: I can't find an official spec for this format so, for now, this plugin doesn't support it.
|
|
| 311 |
As per L<http://cumbers.wordpress.com/2008/05/07/breakdown-of-currentcost-xml-output/>:
|
|
| 283 | 312 |
|
| 284 | 313 |
<msg> |
| 285 | 314 |
<date> |
| 286 |
<dsb>00014</dsb> |
|
| 287 |
<hr>14</hr> |
|
| 315 |
<dsb>00014</dsb> days since birth
|
|
| 316 |
<hr>14</hr> the time
|
|
| 288 | 317 |
<min>07</min> |
| 289 | 318 |
<sec>07</sec> |
| 290 | 319 |
</date> |
| 291 | 320 |
<src> |
| 292 |
<name>CC02</name> |
|
| 293 |
<id>03280</id> |
|
| 294 |
<type>1</type> |
|
| 295 |
<sver>0.07</sver> |
|
| 321 |
<name>CC02</name> name of this device
|
|
| 322 |
<id>03280</id> communication channel for device
|
|
| 323 |
<type>1</type> hardware version of the device
|
|
| 324 |
<sver>0.07</sver> software version
|
|
| 296 | 325 |
</src> |
| 297 | 326 |
<ch1> |
| 298 |
<watts>00080</watts> |
|
| 327 |
<watts>00080</watts> value from the first channel clamp
|
|
| 299 | 328 |
</ch1> |
| 300 | 329 |
<ch2> |
| 301 |
<watts>00000</watts> |
|
| 330 |
<watts>00000</watts> value from the second channel clamp
|
|
| 302 | 331 |
</ch2> |
| 303 | 332 |
<ch3> |
| 304 |
<watts>00000</watts> |
|
| 333 |
<watts>00000</watts> value from the third channel clamp
|
|
| 305 | 334 |
</ch3> |
| 306 |
<tmpr>28.8</tmpr> |
|
| 335 |
<tmpr>28.8</tmpr> current temperature (degrees celsius)
|
|
| 307 | 336 |
<hist> |
| 308 | 337 |
<hrs> |
| 309 |
<h02>000.0</h02> |
|
| 338 |
<h02>000.0</h02> total Kwh used in 2 hour blocks
|
|
| 310 | 339 |
<h04>000.1</h04> |
| 311 | 340 |
<h06>000.1</h06> |
| 312 | 341 |
<h08>000.0</h08> |
| ... | ... | |
| 321 | 350 |
<h26>000.0</h26> |
| 322 | 351 |
</hrs> |
| 323 | 352 |
<days> |
| 324 |
<d01>0000</d01> |
|
| 353 |
<d01>0000</d01> total Kwh used per day(s)
|
|
| 325 | 354 |
<d02>0000</d02> |
| 326 | 355 |
<d03>0000</d03> |
| 327 | 356 |
<d04>0000</d04> |
| ... | ... | |
| 354 | 383 |
<d31>0000</d31> |
| 355 | 384 |
</days> |
| 356 | 385 |
<mths> |
| 357 |
<m01>0000</m01> |
|
| 386 |
<m01>0000</m01> total Kwh used per month(s)
|
|
| 358 | 387 |
<m02>0000</m02> |
| 359 | 388 |
<m03>0000</m03> |
| 360 | 389 |
<m04>0000</m04> |
| ... | ... | |
| 368 | 397 |
<m12>0000</m12> |
| 369 | 398 |
</mths> |
| 370 | 399 |
<yrs> |
| 371 |
<y1>0000000</y1> |
|
| 400 |
<y1>0000000</y1> total Kwh used per year(s)
|
|
| 372 | 401 |
<y2>0000000</y2> |
| 373 | 402 |
<y3>0000000</y3> |
| 374 | 403 |
<y4>0000000</y4> |
| ... | ... | |
| 419 | 448 |
my %seen_sensors; |
| 420 | 449 |
|
| 421 | 450 |
while (<$ttydev>) {
|
| 451 |
print "# Read from device: $_\n" if $MUNIN_DEBUG; |
|
| 422 | 452 |
if (m{(<msg>.*</msg>)}) {
|
| 423 | 453 |
my $xmlref = XML::Simple::XMLin( $1, KeepRoot => 1 ); |
| 424 | 454 |
|
| 425 | 455 |
my $sensor = $xmlref->{msg}->{sensor};
|
| 456 |
print "# Parsing Sensor $sensor\n" if $MUNIN_DEBUG; |
|
| 426 | 457 |
next unless defined $sensor; |
| 427 | 458 |
if ( defined $seen_sensors{$sensor} ) {
|
| 428 | 459 |
|
| 429 | 460 |
# We've seen this sensor before. |
| 430 | 461 |
# Time to stop reading data |
| 462 |
print "# Hello again, Sensor $sensor\n" if $MUNIN_DEBUG; |
|
| 431 | 463 |
last; |
| 432 | 464 |
} |
| 433 | 465 |
$seen_sensors{$sensor} = 1;
|
| ... | ... | |
| 441 | 473 |
my $channel = $1; |
| 442 | 474 |
my $unit = ( keys %{ $xmlref->{msg}->{"ch$channel"} } )[0];
|
| 443 | 475 |
my $val = $xmlref->{msg}->{"ch$channel"}->{$unit};
|
| 476 |
print "# Channel $channel, Unit $unit, Value $val\n" |
|
| 477 |
if $MUNIN_DEBUG; |
|
| 478 |
push @temparr, |
|
| 479 |
{
|
|
| 480 |
"channel" => $channel, |
|
| 481 |
"unit" => $unit, |
|
| 482 |
"value" => $val |
|
| 483 |
}; |
|
| 484 |
} |
|
| 485 |
} |
|
| 486 |
$temphash->{data} = \@temparr;
|
|
| 487 |
|
|
| 488 |
push @cc_data_arr, $temphash; |
|
| 489 |
} |
|
| 490 |
} |
|
| 491 |
close($ttydev); |
|
| 492 |
|
|
| 493 |
return @cc_data_arr; |
|
| 494 |
} |
|
| 495 |
|
|
| 496 |
sub collect_cc02_data {
|
|
| 497 |
# Function supplied by David Edmondson <dme@dme.org> |
|
| 498 |
|
|
| 499 |
# Read data from the serial port |
|
| 500 |
my ( $port, $baud ) = @_; |
|
| 501 |
|
|
| 502 |
my $tty = Device::SerialPort->new($port) || die "Can't open $port: $!"; |
|
| 503 |
$tty->baudrate($baud) || die "Can't set serial baudrate"; |
|
| 504 |
$tty->parity("none") || die "Can't set serial parity";
|
|
| 505 |
$tty->databits(8) || die "Can't set serial databits"; |
|
| 506 |
$tty->handshake("none") || die "Can't set serial handshake";
|
|
| 507 |
$tty->write_settings || die "Can't set serial parameters"; |
|
| 508 |
|
|
| 509 |
open( my $ttydev, "<", $port ) || die "Can't open $port: $?"; |
|
| 510 |
|
|
| 511 |
my @cc_data_arr; |
|
| 512 |
|
|
| 513 |
while (<$ttydev>) {
|
|
| 514 |
if (m{(<msg>.*</msg>)}) {
|
|
| 515 |
my $xmlref = XML::Simple::XMLin( $1, KeepRoot => 1 ); |
|
| 516 |
|
|
| 517 |
my $temphash; |
|
| 518 |
$temphash->{sensor} = 0; # Only one sensor.
|
|
| 519 |
$temphash->{temp} = $xmlref->{msg}->{tmpr};
|
|
| 520 |
my @temparr; |
|
| 521 |
foreach my $key ( keys %{ $xmlref->{msg} } ) {
|
|
| 522 |
if ( $key =~ /ch(\d+)/ ) {
|
|
| 523 |
my $channel = $1; |
|
| 524 |
my $unit = ( keys %{ $xmlref->{msg}->{"ch$channel"} } )[0];
|
|
| 525 |
my $val = $xmlref->{msg}->{"ch$channel"}->{$unit};
|
|
| 444 | 526 |
push @temparr, |
| 445 | 527 |
{
|
| 446 | 528 |
"channel" => $channel, |
| ... | ... | |
| 452 | 534 |
$temphash->{data} = \@temparr;
|
| 453 | 535 |
|
| 454 | 536 |
push @cc_data_arr, $temphash; |
| 537 |
|
|
| 538 |
last; |
|
| 455 | 539 |
} |
| 456 | 540 |
} |
| 457 | 541 |
close($ttydev); |
| ... | ... | |
| 475 | 559 |
} |
| 476 | 560 |
elsif ( $unit != $key->{unit} ) {
|
| 477 | 561 |
print STDERR |
| 478 |
"Conflicting units ($unit and $key->{unit}) on sensor $datum->{sensor}";
|
|
| 562 |
"# Conflicting units ($unit and $key->{unit}) on sensor $datum->{sensor}";
|
|
| 479 | 563 |
} |
| 480 | 564 |
} |
| 481 | 565 |
|
| ... | ... | |
| 512 | 596 |
${fieldname}_n.min 0
|
| 513 | 597 |
${fieldname}_t.cdef ${fieldname}_n,UN,${fieldname},${fieldname},IF,3600,TRENDNAN
|
| 514 | 598 |
EOF |
| 515 |
} else {
|
|
| 516 |
print "${fieldname}_t.cdef ${fieldname},3600,TRENDNAN\n";
|
|
| 517 |
} |
|
| 599 |
} |
|
| 600 |
else {
|
|
| 601 |
print "${fieldname}_t.cdef ${fieldname},3600,TRENDNAN\n";
|
|
| 602 |
} |
|
| 518 | 603 |
} |
| 519 | 604 |
|
| 520 | 605 |
# Output the Root cumulative graph |
| ... | ... | |
| 542 | 627 |
EOF |
| 543 | 628 |
} |
| 544 | 629 |
print "\n$confstr"; |
| 545 |
print <<EOF;
|
|
| 630 |
print <<EOF; |
|
| 546 | 631 |
fudge.label (fudge) |
| 547 | 632 |
fudge.type GAUGE |
| 548 | 633 |
fudge.graph yes |
Formats disponibles : Unified diff