header image

Pulling Electrical Data off a TED MTU

Problem

I got a TED (The Energy Detective) MTU (Measuring Transmitting Unit) for cheap and hooked it up a few years ago. If you're not familiar, that consists of putting little inductive clamps around the main lines that feed your electrical panel from the meter. Those clamps are hooked into a little black box called a CT (Current Transmitter) that is powered by one of the circuits in the box. That box measures your home's energy usage (instantaneous current) using induction and also measures your line voltage (since it's wired in anyway). It then transmits this data over a circuit to the MTU. The MTU records it and runs a little webserver where you can access that data. Here's a picture of all this stuff, if you're curious:

Kinda cool... somewhat useful. e.g. if you have a laptop around you can fire up a web browser and look at your current and historical energy use. You can also tell it about your power rates and it will estimate your bill.

The webpage the little box hosts is pretty nice but I found that I never really looked at it. It is also heavyweight javascript that takes a long time to render in an iframe on my raspberry pi. In short, I wanted to make a display that would tell me this same information quickly, on a "dedicated display" (Note: the TED people also sell displays, if you're not interested in rolling your own). This is an article about how to roll your own, though (for you, if you care and for me, in case I ever have to redo it).

Getting the Data

One of the nice things about this TED stuff is they have a nice RESTful XML API you can poll to read raw data off that little webserver box and have documented it. This is really cool -- I love when products make their functionality available to programmers without forcing you into a closed ecosystem. Here's a sample of the XML data for the /api/LiveData.xml result. Because that XML data is available, I was able to write a little python script to poll it every minute (in a cronjob), stuff it into a RRD (Round Robin Database), and build my own display for the data.

Here's the script to fetch the data off the TED server over the REST API:

#!/usr/local/bin/python

import rrdtool
import urllib2
import xml.etree.ElementTree as ET

DATABASE = 'ted_readings.rrd'

f = urllib2.urlopen('http://ted/api/LiveData.xml')
raw = f.read()
cooked = ET.fromstring(raw)
f.close()

voltage = int(cooked.find("Voltage").find("Total").find("VoltageNow").text)
power = int(cooked.find("Power").find("Total").find("PowerNow").text)
cost = int(cooked.find("Cost").find("Total").find("CostNow").text)

if voltage < 0 or voltage > 400:
    voltage = 0
if power < 0 or power > 200000:
    power = 0
if cost < 0 or cost > 1000:
    cost = 0

template = "voltage:power:cost"
update = "N:%d:%d:%d" % (voltage, power, cost)
rrdtool.update(DATABASE, "--template", template, update)

This reads the raw data from that REST API and stuffs it into a RRD file. I'm using a program called rrdtool to store the historical timeseries data and also to generate graphs of the data. This is a tool I've used before, e.g. in the cabin temperature project, and I really like. It's somewhat complicated (read the man pages and look at the examples) but it works well, is flexible, has python and perl library support and basically does a nice job.

For reference, this is how I created the RRD file I'm populating here. It creates three gauges (voltage, power and cost) corresponding to the three data series I care about from the TED device and keeps them for different time windows. It also makes available the last N raw readings (N=10000). The resultant file is something like 15M of disk space.

% rrdtool create ted_readings.rrd \
        --start 1371018212 \
        --step 60 \
        DS:voltage:GAUGE:900:0:500 \
        DS:power:GAUGE:900:0:1000000 \
        DS:cost:GAUGE:900:0:100000 \
        RRA:AVERAGE:0.5:1:525600 \
        RRA:AVERAGE:0.25:60:87600 \
        RRA:LAST:0:1:10000 \
        RRA:LAST:0.1:20:10000

Making Pretty Graphs

The purpose of this project was to make the current electrical usage more visible than hiding it on a webserver that I never really looked at. I wanted to make it into a page on my kitchen kiosk that would pop up now and again to tell me how much energy I'm using right now and also give me a sense of what is "normal baseline" electrical usage. To do this, I generate four graphs out of the data in the RRD using a script:

#!/bin/bash

RRDTOOL=/usr/local/bin/rrdtool
WWW=/path/to/my/webserver/root

$RRDTOOL graph $WWW/ted_current.png \
  --title "Home Electrical Use, ~Live" \
  --vertical-label "watts" \
  --right-axis 0.025:1 --right-axis-label "volts" \
  --lower-limit 500 \
  --start now-20m --end now \
  --width=640 --height=480 \
  --step=60 \
  --slope-mode \
  --force-rules-legend \
  --watermark "`date`" \
  --font DEFAULT:9: \
  DEF:base_voltage=/home/scott/cron/ted_readings.rrd:voltage:AVERAGE \
  DEF:last_voltage=/home/scott/cron/ted_readings.rrd:voltage:LAST \
  CDEF:voltage=base_voltage,40,* \
  DEF:power=/home/scott/cron/ted_readings.rrd:power:AVERAGE \
  DEF:last_power=/home/scott/cron/ted_readings.rrd:power:LAST \
  DEF:cost=/home/scott/cron/ted_readings.rrd:cost:AVERAGE \
  GPRINT:last_voltage:LAST:"current line\: %3.0lf volts" \
  GPRINT:last_power:LAST:"current usage\: %3.0lg%s watts" \
  AREA:1100#DFFFDF \
  HRULE:1100#009900:"baseline" \
  LINE3:power#800000:"power" \
  LINE3:voltage#000099:"voltage"

$RRDTOOL graph $WWW/ted_one_day.png \
  --title "Home Electrical Use, Past Day" \
  --vertical-label "watts" \
  --right-axis 0.025:1 --right-axis-label "volts" \
  --lower-limit 500 \
  --start now-1d --end now \
  --width=640 --height=480 \
  --step=300 \
  --slope-mode \
  --force-rules-legend \
  --watermark "`date`" \
  --font DEFAULT:9: \
  DEF:base_voltage=/home/scott/cron/ted_readings.rrd:voltage:AVERAGE \
  DEF:last_voltage=/home/scott/cron/ted_readings.rrd:voltage:LAST \
  CDEF:voltage=base_voltage,40,* \
  DEF:power=/home/scott/cron/ted_readings.rrd:power:AVERAGE \
  DEF:last_power=/home/scott/cron/ted_readings.rrd:power:LAST \
  DEF:cost=/home/scott/cron/ted_readings.rrd:cost:AVERAGE \
  GPRINT:last_voltage:LAST:"current line\: %3.0lf volts" \
  GPRINT:last_power:LAST:"current usage\: %3.0lg%s watts" \
  AREA:1100#DFFFDF \
  HRULE:1100#009900:"baseline" \
  LINE3:power#800000:"power" \
  LINE3:voltage#000099:"voltage"

$RRDTOOL graph $WWW/ted_one_week.png \
  --title "Home Electrical Use, Past Week" \
  --vertical-label "watts" \
  --right-axis 0.025:1 --right-axis-label "volts" \
  --lower-limit 500 \
  --start now-7d --end now \
  --width=640 --height=480 \
  --step=1850 \
  --slope-mode \
  --force-rules-legend \
  --watermark "`date`" \
  --font DEFAULT:9: \
  DEF:base_voltage=/home/scott/cron/ted_readings.rrd:voltage:AVERAGE \
  DEF:last_voltage=/home/scott/cron/ted_readings.rrd:voltage:LAST \
  CDEF:voltage=base_voltage,40,* \
  DEF:power=/home/scott/cron/ted_readings.rrd:power:AVERAGE \
  DEF:last_power=/home/scott/cron/ted_readings.rrd:power:LAST \
  DEF:cost=/home/scott/cron/ted_readings.rrd:cost:AVERAGE \
  GPRINT:last_voltage:LAST:"current line\: %3.0lf volts" \
  GPRINT:last_power:LAST:"current usage\: %3.0lg%s watts" \
  AREA:1100#DFFFDF \
  HRULE:1100#009900:"baseline" \
  LINE3:power#800000:"power" \
  LINE3:voltage#000099:"voltage"

$RRDTOOL graph $WWW/ted_one_month.png \
  --title "Home Electrical Use, Past Month" \
  --vertical-label "watts" \
  --right-axis 0.025:1 --right-axis-label "volts" \
  --lower-limit 500 \
  --start now-1m --end now \
  --width=640 --height=480 \
  --step=8400 \
  --slope-mode \
  --force-rules-legend \
  --watermark "`date`" \
  --font DEFAULT:9: \
  DEF:base_voltage=/home/scott/cron/ted_readings.rrd:voltage:AVERAGE \
  DEF:last_voltage=/home/scott/cron/ted_readings.rrd:voltage:LAST \
  CDEF:voltage=base_voltage,40,* \
  DEF:power=/home/scott/cron/ted_readings.rrd:power:AVERAGE \
  DEF:last_power=/home/scott/cron/ted_readings.rrd:power:LAST \
  DEF:cost=/home/scott/cron/ted_readings.rrd:cost:AVERAGE \
  GPRINT:last_voltage:LAST:"current line\: %3.0lf volts" \
  GPRINT:last_power:LAST:"current usage\: %3.0lg%s watts" \
  AREA:1100#DFFFDF \
  HRULE:1100#009900:"baseline" \
  LINE3:power#800000:"power" \
  LINE3:voltage#000099:"voltage"
    

These generate graphs like this one (not live):

You can take a look at the rrdgraph, rrdgraph_data, and rrdgraph_examples man pages to figure most of this out but I want to explain one thing that was a little tricky: the right axis.

It's easy to make a right axis on your graph; just use the --right-axis and --right-axis-label arguments. The former takes a scaling factor that defines the scale of the right axis as a proportion of the main left axis. In my case, I wanted voltage on the right axis. Nominal voltage is 120 volts while nominal instantaneous power usage is something like 1k watts. Therefore I made the scaling factor 0.1 (1/10).

What wasn't easy was to figure out how to get one of the lines on the graph to "use" the right axis. As far as I can tell, there's no simple way to do it. All lines always plot relative to the left (main) axis. Instead, what you do is to scale the data you're graphing to match the scale of the axis you want to use. In my case, voltage had to be multiplied by 10. That way it would plot on the right spot relative to the left axis and "look like" it was using the left axis. That's the purpose of these parts:

  DEF:base_voltage=/home/scott/cron/ted_readings.rrd:voltage:AVERAGE \
  CDEF:voltage=base_voltage,10,* \
  LINE2:voltage#000099:"voltage" \

That's pulling in a data series called "base_voltage" then deriving another series called "voltage" by multiplying base_voltage by 10. The scaled out "voltage" is the line I then plot on the graph. It will plot relative to the left (main) power axis but appear to be correct relative to the right (alternate) voltage axis since that axis is 1/10th the scale of the main one.