Browse Source

WeMo post.

dev
Chris Smith 8 years ago
parent
commit
35af9c17cc

+ 1
- 1
Gemfile View File

@@ -1,2 +1,2 @@
1 1
 source 'https://rubygems.org'
2
-gem 'github-pages'
2
+gem 'github-pages', group: :jekyll_plugins

+ 2
- 4
_posts/2016-04-10-sense-api.md View File

@@ -4,8 +4,6 @@ title: Reverse engineering the Sense API
4 4
 strapline: Who needs API docs when you can do a MITM attack?
5 5
 ---
6 6
 
7
-### Meet Sense
8
-
9 7
 <div class="image right">
10 8
  <img src="/res/images/sense/sense.jpg" alt="Sense">
11 9
 </div>
@@ -15,8 +13,6 @@ conjunction with a little 'pill' attached to your pillow, monitors your sleeping
15 13
 any environmental conditions that might hamper them. Android and iOS apps show you your sleep
16 14
 history, and offer suggestions for improvements.
17 15
 
18
-<!--more-->
19
-
20 16
 Sense was <a href="https://www.kickstarter.com/projects/hello/sense-know-more-sleep-better/description">Kickstarted</a>
21 17
 in August 2014, raising over 2.4 million US dollars, and shipped to backers in mid 2015. The
22 18
 campaign blurb included this snippet:
@@ -35,6 +31,8 @@ campaign blurb included this snippet:
35 31
 Sounds great! But a year after shipping, there's no sign of an API, and some of us who enjoy
36 32
 tinkering are getting a bit restless...
37 33
 
34
+<!--more-->
35
+
38 36
 ### Reverse engineering to the rescue
39 37
 
40 38
 Data from the Sense hardware is transmitted off onto the Internet somewhere, and then pulled

+ 194
- 0
_posts/2016-05-02-monitoring-power-with-wemo.md View File

@@ -0,0 +1,194 @@
1
+---
2
+layout: post
3
+title: Monitoring power draw with WeMo Insight Switches
4
+strapline: Fun with SOAP and rrdtool
5
+---
6
+
7
+<div class="image right">
8
+ <img src="/res/images/wemo/switch.jpg" alt="WeMo Insight Switch">
9
+</div>
10
+
11
+I recently picked up a couple of <a href="http://www.belkin.com/uk/p/P-F7C029/">Belkin's WeMo
12
+Insight Switches</a> to monitor power usage for my PC and networking equipment. WeMo is Belkin's
13
+home automation brand, and the switches allow you to toggle power on and off with an app, and
14
+monitor power usage.
15
+
16
+The WeMo Android app is pretty dismal. It's slow, doesn't look great, and crashed about a dozen
17
+times during the setup process for each of my two switches. It also doesn't provide much
18
+information at all about power: you can see average power draw and current power draw, and that's
19
+basically it.
20
+
21
+Belkin has provided an option to e-mail yourself a spreadsheet with historical power data, and can
22
+even do it on a regularly scheduled basis, but that's not really a nice solution if you want
23
+up-to-date power stats. Even if you were happy with data arriving in batch, having to get hold
24
+of an e-mail attachment and parse out a weirdly formatted spreadsheet doesn't make for easy
25
+automation. It also relies on Belkin supporting the service indefinitely, which isn't necessarily
26
+going to happen.
27
+
28
+<!--more-->
29
+
30
+### Enter the SOAP API
31
+
32
+Fortunately, the devices themselves expose an API to get the data. Once a switch is setup with the
33
+WeMo app, it connects to a WiFi network. You can discover WeMo devices on the network using a UPnP
34
+broadcast, and then from there a list of supported services. Each service has a number of SOAP
35
+actions, with nice obvious names, and parameters documented in the XML service description.
36
+
37
+For the insight switches, there's a service at `/upnp/control/insight1` which has a number of
38
+actions including `GetPower` and `GetTodayKWH`. Sending a SOAP request isn't too difficult
39
+(albeit nowhere near as nice as a REST JSON API) &mdash; here's a sample request I made using
40
+the <a href="https://chrome.google.com/webstore/detail/dhc-rest-client/aejoelaoggembcahagimdiliamlcdmfm/">DHC chrome extension</a>:
41
+
42
+{% highlight http %}
43
+POST /upnp/control/insight1 HTTP/1.1
44
+Accept: */*
45
+Accept-Encoding: gzip, deflate
46
+Accept-Language: en-GB,en;q=0.8
47
+Content-Type: text/xml
48
+Origin: chrome-extension://aejoelaoggembcahagimdiliamlcdmfm
49
+SOAPACTION: "urn:Belkin:service:insight:1#GetPower"
50
+User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.29 Safari/537.36
51
+
52
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
53
+    <s:Body>
54
+        <u:GetPower xmlns:u="urn:Belkin:service:insight:1"/>
55
+    </s:Body>
56
+</s:Envelope>
57
+{% endhighlight %}
58
+
59
+The name of the action (in the SOAPACTION header) and the XML namespace in the body are both
60
+constructed from information in the service definition. The result that comes back is:
61
+
62
+{% highlight xml %}
63
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
64
+    <s:Body>
65
+        <u:GetPowerResponse xmlns:u="urn:Belkin:service:insight:1">
66
+            <InstantPower>92170</InstantPower>
67
+        </u:GetPowerResponse>
68
+    </s:Body>
69
+</s:Envelope>
70
+{% endhighlight %}
71
+
72
+The current power draw in milliwatts is returned in the `<InstantPower>` argument.
73
+
74
+### Putting the data to work
75
+
76
+So now we have a way to pull the current power draw, a nice thing to do would be to plot a graph
77
+of it to see the variation over time. This is the short of thing you'd expect to see in the Belkin
78
+app, but it's sadly missing. After a bit of research I settled on the tried and true
79
+<a href="http://oss.oetiker.ch/rrdtool/">rrdtool</a> to store data and generate graphs. I created a
80
+new database to store the values from the two switches:
81
+
82
+{% highlight bash %}
83
+rrdtool create power.rrd \
84
+    --start now \
85
+    --step 60 \
86
+    DS:wemoComputer:GAUGE:120:U:U \
87
+    DS:wemoNetworking:GAUGE:120:U:U \
88
+    RRA:AVERAGE:0.5:1:1440 \
89
+    RRA:AVERAGE:0.5:10:1008 \
90
+    RRA:AVERAGE:0.5:30:1488 \
91
+    RRA:AVERAGE:0.5:120:1488 \
92
+    RRA:AVERAGE:0.5:360:1488 \
93
+    RRA:AVERAGE:0.5:1440:36500
94
+{% endhighlight %}
95
+
96
+This creates a database file which expects values to be given every 60 seconds for two data series:
97
+'wemoComputer' and 'wemoNetworking'. These are gauge types (i.e., a value we read off a gauge,
98
+rather than a counter that keeps going up) with no minimum and maximum ('U').
99
+
100
+It then defines a series of round-robin archives. Each of these stores a fixed number of entries,
101
+and the oldest is overwritten when they become full. The interesting arguments are the step count
102
+(second to last) and number of samples (last argument). The first one takes every sample and keeps
103
+1,440 of them (i.e., a full day at 1-minute resolution); the last one has a step of 1,440 and keeps
104
+36500 of them (i.e., 100 years at 1-day resolution). The use of RRAs ensures that rrdtool can retain
105
+enough data to create historical graphs, while providing a fixed, finite maximum file size. With
106
+sufficient RRA coverage you can graph most timescales and have data available at a reasonable
107
+resolution.
108
+
109
+Next, I created a small python script to retrieve the power using SOAP:
110
+
111
+{% highlight python %}
112
+#!/usr/bin/python3
113
+
114
+import requests
115
+from xml.etree import ElementTree
116
+
117
+def get_power(ip):
118
+    headers = {'Content-type': 'text/xml', 'SOAPACTION': '"urn:Belkin:service:insight:1#GetPower"'}
119
+    payload = '''<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
120
+                             s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
121
+                  <s:Body>
122
+                   <u:GetPower xmlns:u="urn:Belkin:service:insight:1"/>
123
+                  </s:Body>
124
+                 </s:Envelope>'''
125
+    r = requests.post("http://%s:49153/upnp/control/insight1" % ip, headers=headers, data=payload)
126
+    et = ElementTree.fromstring(r.text)
127
+    return et.find('.//InstantPower').text
128
+{% endhighlight %}
129
+
130
+I then have a dictionary of IP addresses to data series names, and the script polls each one in
131
+turn and then executes an `rrdtool update` query to add the items to the database. I have this
132
+script running every minute via cron.
133
+
134
+After leaving the script to run for a bit and gather data, it's time to make some graphs. I use
135
+the following to create a graph with a background gradient:
136
+
137
+{% highlight bash %}
138
+rrdtool graph desk-1d.png
139
+        -o -X0 -w800 -h500 \
140
+        -u 2000 -l 20 -r \
141
+        DEF:raw=power.rrd:wemoComputer:AVERAGE \
142
+        CDEF:power=raw,1000,/ \
143
+        CDEF:powerz=power,2000,LT,power,2000,IF CDEF:powerzNoUnk=power,UN,0,powerz,IF AREA:powerzNoUnk#ff0000 \
144
+        CDEF:powery=power,1500,LT,power,1500,IF CDEF:poweryNoUnk=power,UN,0,powery,IF AREA:poweryNoUnk#ff0000 \
145
+        CDEF:powerx=power,1000,LT,power,1000,IF CDEF:powerxNoUnk=power,UN,0,powerx,IF AREA:powerxNoUnk#ff0000 \
146
+        CDEF:powerw=power,900,LT,power,900,IF CDEF:powerwNoUnk=power,UN,0,powerw,IF AREA:powerwNoUnk#ff0000 \
147
+        CDEF:powerv=power,800,LT,power,800,IF CDEF:powervNoUnk=power,UN,0,powerv,IF AREA:powervNoUnk#ff1b00 \
148
+        CDEF:poweru=power,700,LT,power,700,IF CDEF:poweruNoUnk=power,UN,0,poweru,IF AREA:poweruNoUnk#ff4100 \
149
+        CDEF:powert=power,600,LT,power,600,IF CDEF:powertNoUnk=power,UN,0,powert,IF AREA:powertNoUnk#ff6600 \
150
+        CDEF:powers=power,400,LT,power,400,IF CDEF:powersNoUnk=power,UN,0,powers,IF AREA:powersNoUnk#ff8e00 \
151
+        CDEF:powerr=power,200,LT,power,200,IF CDEF:powerrNoUnk=power,UN,0,powerr,IF AREA:powerrNoUnk#ffb500 \
152
+        CDEF:powerq=power,180,LT,power,180,IF CDEF:powerqNoUnk=power,UN,0,powerq,IF AREA:powerqNoUnk#ffdb00 \
153
+        CDEF:powerp=power,160,LT,power,160,IF CDEF:powerpNoUnk=power,UN,0,powerp,IF AREA:powerpNoUnk#fdff00 \
154
+        CDEF:powero=power,140,LT,power,140,IF CDEF:poweroNoUnk=power,UN,0,powero,IF AREA:poweroNoUnk#d7ff00 \
155
+        CDEF:powern=power,120,LT,power,120,IF CDEF:powernNoUnk=power,UN,0,powern,IF AREA:powernNoUnk#b0ff00 \
156
+        CDEF:powerm=power,100,LT,power,100,IF CDEF:powermNoUnk=power,UN,0,powerm,IF AREA:powermNoUnk#8aff00 \
157
+        CDEF:powerl=power,90,LT,power,90,IF CDEF:powerlNoUnk=power,UN,0,powerl,IF AREA:powerlNoUnk#65ff00 \
158
+        CDEF:powerk=power,80,LT,power,80,IF CDEF:powerkNoUnk=power,UN,0,powerk,IF AREA:powerkNoUnk#3eff00 \
159
+        CDEF:powerj=power,70,LT,power,70,IF CDEF:powerjNoUnk=power,UN,0,powerj,IF AREA:powerjNoUnk#17ff00 \
160
+        CDEF:poweri=power,60,LT,power,60,IF CDEF:poweriNoUnk=power,UN,0,poweri,IF AREA:poweriNoUnk#00ff10 \
161
+        CDEF:powerh=power,50,LT,power,50,IF CDEF:powerhNoUnk=power,UN,0,powerh,IF AREA:powerhNoUnk#00ff36 \
162
+        CDEF:powerg=power,40,LT,power,40,IF CDEF:powergNoUnk=power,UN,0,powerg,IF AREA:powergNoUnk#00ff5c \
163
+        CDEF:powerf=power,30,LT,power,30,IF CDEF:powerfNoUnk=power,UN,0,powerf,IF AREA:powerfNoUnk#00ff83 \
164
+        CDEF:powere=power,20,LT,power,20,IF CDEF:powereNoUnk=power,UN,0,powere,IF AREA:powereNoUnk#00ffa8 \
165
+        CDEF:powerd=power,0,LT,power,0,IF CDEF:powerdNoUnk=power,UN,0,powerd,IF AREA:powerdNoUnk#00ffd0 \
166
+        LINE:power#080
167
+{% endhighlight %}
168
+
169
+This seems a bit unweildy, but it's fairly straight forward. The options tell rrdtool to create
170
+a graph with a canvas size of 800x500 pixels, a lower limit of 20W, upper limit of 2kW, and a
171
+log scale. The first `CDEF` divides our raw value (which was in millwatts) by 1000 to get a value
172
+in watts. rrdtool uses reverse polish notation like some calculators, so you provide the arguments
173
+before the operator.
174
+
175
+The big block of `CDEF`/`CDEF`/`AREA` parameters creates a series of area plots in different colours
176
+according to the power level. They're in descending order so the smaller areas are drawn on top of
177
+the larger layers. This results in a graph that looks like this:
178
+
179
+<img src="/res/images/wemo/desk-1d.png" alt="Graph of power usage over a day">
180
+
181
+This graph shows the total power for all the things plugged in at my desk. You can see the idle
182
+power draw is around 60W. When I'm using the computer it jumps up to around 130W, and when the
183
+computer is under heavy load (playing games, for example) it goes up even further to the 200W mark.
184
+With a couple of small tweaks to the rrdtool command, I also have a graph showing the entire week:
185
+
186
+<img src="/res/images/wemo/desk-1w.png" alt="Graph of power usage over a week">
187
+
188
+The two huge spikes near the start of the data are caused by a heater under my desk. They're also
189
+one of the main reasons I chose to plot the graphs with a logarithmic scale. With a linear scale
190
+between 20W and 2kW, the graph would be completely dominated by these large spikes &mdash; in fact
191
+the difference in height between the two spikes would take almost half the graph! The
192
+relative difference between the normal values of 60-200W and the heater's value of 1.8kW isn't
193
+actually that interesting, and certainly not worth using about 80% of the graph to demonstrate. The
194
+log scale helps to compress this, and emphasises the difference in the smaller values more.

BIN
res/images/wemo/desk-1d.png View File


BIN
res/images/wemo/desk-1w.png View File


BIN
res/images/wemo/switch.jpg View File


Loading…
Cancel
Save