Power Consumption Monitoring with Graphite & Alexa

A few posts ago I wrote a simple guide on how to read power usage stats from CurrentCost CT Clamps using Software Defined Radio. This is great but how can I view historic power consumption or integrate this data with other stuff?

First we need somewhere to store the data produced by the sensor. In my last post I covered configuring a Graphite server, Graphite is a time series database a bit like RRDTool but with a modern API and more flexibility. I built this in mind of my growing collection of Internet of Things sensors and the time series data they are churning out on a daily basis. Lets write a script to take the power stats output by rtl_433 and insert them into Graphite:

#!/usr/bin/env python3

import subprocess
import threading
import json
import time
import graphyte

# Configuration values.
device_id = 1636
rtl_433_cmd = "/usr/local/bin/rtl_433 -F json"
graphite_server = '172.20.1.189'

# No further user configurable stuff after this line.
graphyte.init(graphite_server, prefix='sensors.%s' % device_id)


def shovel_results():
    for line in iter(p.stdout.readline, b''):
        try:
            data = json.loads(line)
        except:
            data = None

        if not data:
            print("Data from RTL433 is invalid")
        else:
            try:
                dev_id = data['dev_id']
                power = data['power0']
                print("Received valid packet from %s, power usage: %s watts"
                      % (dev_id, power))

                if device_id == dev_id:
                    print("Submiting to Graphite")
                    graphyte.send('power', power)
                else:
                    print("Device ID doesn't match, disregarding")

            except:
                print("Received packet from non-compliant device")


p = subprocess.Popen(rtl_433_cmd.split(),
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     universal_newlines=True)

t = threading.Thread(target=shovel_results)
t.start()


try:
    while True:
        time.sleep(1)
        if p.poll() is not None:
            break

finally:
    p.terminate()

The script is very simplistic and wraps the rtl_433 command, it filters any invalid lines and any device which does not have a dev_id that matches the device id defined in the configuration segment at the top of the script. This is useful if your neighbour also has a CurrentCost and you don’t want to record their readings, it also ensures we do not record erroneous values from other sensors which operate in the 433MHz range like door bells and tyre pressure monitors. Obviously swap out the graphite_server IP address with the IP of your own Graphite installation. Now run the script and go and make a coffee or something and give it chance to dump some power usage data into Graphite.

After a few minutes you can check Graphite to see how the data looks, open up your Graphite web console and expand the tree to reveal “sensors.<device_id>”. Select the power series by clicking on it and change the date range for the drawn graph to the last hour, you should see that time series data has started to get populated with your current power usage in Watts.

As you can see my Graphite instance now has some data for the power series for my CurrentCost CT Clamp. Ignore the unusual straight line, thats a few minutes where I stopped the script to make some tweaks. If you want your power consumption to be recorded at all times then feel free to add this to your SysVInit or SystemD scripts to start at boot time and run continually as a service.

As you can see you now have a decent way of storing your historic power usage and Graphite offers up a render API so you can integrate the graphs or raw data with other services or scripts. In my case I thought it would be cool to be able to ask my Amazon Echo “Alexa, ask Home App for my current power usage.”. Home App is a small API and mobile app I have been developing to support the custom home automation stuff in my house, the API is written in Python using the Flask framework, lets get Amazon Alexa to query the API and fetch the current power usage.

First you’ll need an Amazon account and to login to the Amazon developer portal at https://developer.amazon.com, navigate to the Alexa tab and click Get Started under the Alexa Skills Kit. Obviously I have already registered and started working on my skill, but you will probably have an empty list presented if you haven’t worked with Alexa before. Click Add a New Skill to start building your own skill. You should create a skill with the current characteristics...

  • Custom Interaction Model
  • A reasonable inovation name, in my case "Home App"
  • Set the language to your preferred language
  • Say no to the skill being an audio player or video app.

This will give us the basics to create a plain speech skill with no fancy stuff like sound effects.

Click next and move onto the Interaction Model, luckily for us the intent schema is super basic for this skill and we do not need to configure any slots or anything, just one intent and the utterances for calling the intent. Intents are defined using a simple JSON data structure, I added an intent called GetCurrentPowerUsageIntent, remember this, it’ll be used later in our API:

{
  "intents": [
    {
      "intent": "GetCurrentPowerUsageIntent"
    }
  ]
}

Then in the sample utterances field define what you’d like to say to call this intent, in my case I wanted to say “Alexa, ask Home App for my current power usage.”. The trigger word is already handled for you and so is the skill name, so we only care about the end of the phrase “for my current power usage”. Utterances are defined as follows, you can have multiple utterances per intent:

GetCurrentPowerUsageIntent for my current power usage.

Once you have entered all of the utterances click save and move onto the next tab.

Here you need to configure where Alexa will call to execute your intent, this can be either an AWS Lambda function or a custom HTTPS URL, if you use an SSL enabled URL you must have a valid certificate and hence your own domain name. I tend to use LetsEncrypt to get a free certificate, you can also use self signed certificates if you wish although you must upload your cert to the Alexa Developer console so Alexa can verify your endpoint. In my case as my Graphite server and my Home App API are hosted on my HP Microserver in my house and it’s not linked to an AWS VPC in anyway I have pointed Alexa to my API endpoint.

Ok, once this page is completed the Alexa skill is now configured and you can run it in test mode, of course it has no API to reach out to so the skill will always fail, lets move onto writing a small Flask App to serve up the response to Alexa with the current power usage.

Either create a new VirtualEnv or install to your systems site-packages the following prerequisites from PyPi. I personally prefer to have each project in a VirtualEnv but I’ll leave this up to you:

pip install flask flask-ask requests

Flask-Ask is a Python package which allows you to easily create Alexa skills within a Flask App, it offers a bunch of decorators and functions which handle all of the Alexa requests and responses without having to intricately know the Alexa API. Once the requirements are installed you can make a super simple one module Flask App to begin working with Alexa…

Once again you’ll need to configure your Graphite server IP address and it’s Web UI port number and the CurrentCost Device ID in the configuration segment of the Flask App:

import requests
import json
from flask import Flask
from flask_ask import Ask
from flask_ask import statement
from flask_ask import question

GRAPHITE_SERVER = '172.20.1.189:8000'
DEVICE_ID = 1636

APP = Flask(__name__)
ASK = Ask(APP, '/api/alexa')


@ASK.launch
def launch():
    speech_text = "Welcome to Home App, what would you like to do?"
    return question(speech_text).simple_card('Home App', speech_text)


@ASK.intent('GetCurrentPowerUsageIntent')
def get_current_power_usage():
    try:
        resp = requests.get('http://%s/render/?target=sensors.%s.power&'
                            'from=-1minutes&format=json'
                            % (GRAPHITE_SERVER, DEVICE_ID))
        data = json.loads(resp.text)
        data = data[0]['datapoints']

        for reading in data:
            if reading[0]:
                if reading[0] > 1000:
                    kilowatts = reading[0]/1000
                    return statement("Current power usage is %s kilowatts."
                                     % kilowatts)
                else:
                    watts = int(reading[0])
                    return statement("Current power usage is %s watts."
                                     % watts)

    except:
        return statement("Sorry something went wrong when I tried to get the "
                         "current power usage.")

if __name__ == "__main__":
    APP.run('0.0.0.0', debug=True)

The App is very simple, it hosts a small web server which when requested by Alexa on the /api/alexa endpoint responds with the current power usage providing the GetCurrentPowerUsageIntent intent is called, this is fetched on the fly using requests from your Graphite server and some basic manipulation performed to make the read back more human friendly. You can also call the launch intent by saying “Alexa, start Home App” or whatever your skill invocation phrase was, in this case Alexa will ask you “Welcome to Home App, what would you like to do?” and wait for a response to which you can say “get current power usage.”.

To use this with Alexa you’ll need to host the Flask App publicly on the internet with a valid SSL certificate. I will leave it to you to setup your preferred web server and configure your preferred certificate, although if there is reasonable demand in the comments following this post I may provide a follow up tutorial on configuring the app using UWSGI, NGINX and LetsEncrypt.

Once the API is online and you can hit it by visiting https://yourhostname.yourdomain.com/api/alexa in your browser you can continue to use the tools in the Alexa developer console to test your skill. Navigate to the test tab in the developer console and in the enter utterance box type something like “Alexa, ask <skill invocation phrase> for my current power usage.” If everything went to plan you should see something like the following returned...

You can also click the listen button to hear what it sounds like with Alexa performing the read back. As you can see in my case Alexa replied with “Current power usage is 240 watts.”. If it failed to retrieve your power usage troubleshoot as necessary.

Now your skill is enabled in test mode you should be able to ask your Amazon Echo the same phrase as entered into the test tools, and it should provide the required response, in test mode it will only work in your Amazon account which is great for my use case. Of course if you choose to you could develop out the skill to allow people to send their own CurrentCost readings to your Graphite server and allow them to pair their Amazon account to their CurrentCost device ID and offer a public skill. If you do not have a real Amazon Echo device but wanted to test out your skill with speech recognition and response then checkout https://www.echosim.io for a virtual in browser Echo, however be warned its feature set is limited.

I hope this provided a sample of how to integrate Amazon Alexa with one of your IoT sensors, of course you can expand this to offer a lot more functionality and use slots to allow inputs via voice if you have any IoT devices you’d like to control via your Echo.

By @Robert Putt in
Tags : #internet, #iot, #electronics, #technology,