Graphing Electricity Usage with Current Cost and Munin

Many years ago I bought a Current Cost meter. Infact it was so long ago now that they have called my version The Classic. I wanted to see how much electrical energy all my servers, home computers and gadgets were using.

The Current Cost package contains two main parts, a transmitter and the display. The transmitter is hooked around your house/offices main power in BEFORE the consumer meter. The consumer meter is the big box that is usually under your stairs. The meter constantly sends power usage to the display which shows the watts currently being used, watts over time and, with a little bit of input from the user how much your energy usage is going to cost you over time.

The reason I went with a Current Cost meter is becuase it has a serial connection on the bottom of the display unit. One annoyance is that it uses a RJ45 connector so you either need to stump up for a custom cable from Current Cost or make your own (google it, there are a number of sites with pinouts and instructions).

I already monitor pretty much every aspect of my servers using Munin so it seemed like a natural solution for graphing the Current Cost data over time.

Sending data to Munin is fairly easy and it has a simple plugin interface. There are some complexities to it as Munin uses RRDTool to render it’s graphs and the options for RRDTool are complex and confusing.

The results are graphs like this:

Watts over the Day

showing watts used over time

Temperature over the Day

showing temparature at or near the Current Cost display

OK they are not very pretty but that’s not the point. I can see that right now as I am typing this my house is consuming about 440 watts. At this rate, per hour I am using 440Wh (watt hours) or 0.44 kWh which is (on average) 0.44 * £0.15 ~ £0.06 per hour

Now as you can see, through the day we get massive spikes, they are usually boiling water for coffee or cooking.

The most interesting thing for me is that having the data before me modifies my behaviour. For example I finally unplugged my Wii today, 4 months after getting my WiiU. It’s been there quietly sucking up a few kWh per day. It hasn’t cost me a lot but as they always say:

If we all did it we’d save the planet!

That is a direct quote from Al Gore!

So here is the python code that you can use as a munin plugin

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python
# Script to extract data from a CurrentCost meter over the serial line
# tested on FreeBSD 9.0 2013/1/22
# dazza

import sys
from datetime import datetime
import xml.etree.ElementTree as et


class DataBlock(object):
    """class to encapsulate the inforkation from a blob of data from the
    currentcost box"""
    def __init__(self, elemtree):
        ft = elemtree.findtext

        self.dsb = int(ft("date/dsb"))
        today = datetime.today()
        self.date = datetime(today.year, today.month, today.day,
                             hour=int(ft("date/hr")),
                             minute=int(ft("date/min")),
                             second=int(ft("date/sec")))
        self.src = ft("src/name")
        self.src_id = int(ft("src/id"))
        self.src_type = ft("src/type")
        self.source_ver = ft("src/sver")
        self.temp = float(ft("tmpr"))
        self.watts = []
        for idx, channelid in enumerate([1, 2, 3]):
            self.watts.append(float(ft("ch%d/watts" % channelid)))

    def __str__(self):
        return "DSB: %(dsb)s\nDATE: %(date)s\nSRC: %(src)s\nSRCID: %(src_id)s\nSRC_TYPE: %(src_type)s\nSOURCE_VER: %(source_ver)s\nTEMP: %(temp)s\nCH: %(watts)s" % self.__dict__


def _readvalues():
    """read a blob of data from the serial connection to the CurrentCost"""
    f = open("/dev/cuaU0")
    db = None
    while True:
        msgstr = f.readline()
        if "<msg>" in msgstr and "</msg>" in msgstr:
            try:
                msg = msgstr[msgstr.find("<msg"):msgstr.find("</msg>") + 6]
                elemtree = et.XML(msg)
                db = DataBlock(elemtree)
                break
                msgstr = msgstr[msgstr.find("</msg>") + 7:]
                break
            except Exception, e:
                print "Exception: " + str(e)
                msgstr = ""
                pass
    f.close()
    return db


def _config_watts():
    """set the configuration for the watts graph"""
    print "graph_title Energy Usage (watts)"
    print "graph_vlabel Watts"
    print "watts_1.label Watts Ch. 1"
    print "watts_2.label Watts Ch. 2"
    print "watts_3.label Watts Ch. 3"
    print "graph_args -l 0"
    print "graph_scale no"
    print "graph_category Home"


def _config_temp():
    """set the config for the temperature graph"""
    print "graph_title Temperature (C)"
    print "graph_vlabel C"
    print "temp.label Temp"
    print "graph_args -l 0"
    print "graph_scale no"
    print "graph_category Home"


def _watts():
    """emit the values fo the watts graph"""
    db = _readvalues()
    print "watts_1.value", db.watts[0]
    print "watts_2.value", db.watts[1]
    print "watts_3.value", db.watts[2]


def _temp():
    """emit the values for the temperature graph"""
    db = _readvalues()
    print "temp.value", db.temp

command = sys.argv[1] if len(sys.argv) > 1 else ""
graphtype = sys.argv[0].split('_')[1]

if command == "config":
    if graphtype == "watts":
        _config_watts()
    elif graphtype == "temperature":
        _config_temp()
    else:
        raise Exception("Unknown graph type %s" % graphtype)
else:
    if graphtype == "watts":
        _watts()
    elif graphtype == "temperature":
        _temp()
    else:
        raise Exception("Unknown graph type %s" % graphtype)

There is a lot going on here so I’ll summerise the code:

  1. Munin plugins are usually symbolic links to scripts. This script gathers either the watts being used or the room temperature depending on the name of the symbolic link. So if the link is called currentcost_watts it will return information about watts, if it’s currentcost_temperature it will return temperature information. This is normal Munin pratice. (see line 88)
  2. Munin passes in the argument “config” if it wants the plugin to output the RRDGraph config for the data. (see line 87)
  3. The _readvalues() function is hardcoded to read from FreeBSD’s serial device /dev/cuaU0 your Current Cost meter might appear somewhere else.
  4. The _readvalues() function returns a DataBlock class. Reading from serial means you get partial data at times so the code iterates over the serial stream until it’s get all the data needed for ONE DataBlock. Because the munin script is being called once every 5 minutes it doesn’t matter too much.
  5. Where there is a lot of power being used I seem to get values appearing on Channel 2. The Current Cost meter I have can read from three meters. I only have one meter so I don’t know how this is happening. I assume it’s a bug in the Current Cost meter but it could be in my code.

I hope this helps someone, good luck getting your energy bills down. I wish there was a similar bit of hardware to measure natural gas usage.

be seeing you.

Comments