Using the Shelly Plug to monitor Starlink's power consumption

I recently wrote about using a Raspberry Pi to remotely monitor an Internet connection, and in my case, to monitor Starlink (SpaceX's satellite Internet service).

Power Consumption Grafana dashboard with Shelly Plug US power usage coming through

One other important thing I wanted to monitor was how much power Starlink used over time, and I was considering just manually taking a reading off my Kill-A-Watt every morning, but that's boring. And not very accurate since it's one point in time per day.

Shelly Plug US

Digging around, I found out about the Shelly Plug US, which had its own little built-in HTTP API and a supposedly-accurate power monitor. It's based on the ESP8266, and you can even flash alternative firmware like Tasmota for more control over the device.

Getting Shelly Plug US

At the time of writing, the Shelly Plug US seemed to be stuck in pre-order status, but there were some available on eBay, so I bought one from someone who seemed reputable there.

When it arrived, it was in a very tidy little box hardly larger than the plug itself, and I am happy to report it's not a fake or knockoff (I'm always a little nervous buying IoT devices from eBay. Though Amazon has also been vying for the 'who should I trust the least' slot lately).

Setting up the Shelly Plug

I didn't want to create a Shelly Cloud account, use the Shelly App, and all that jazz. I'd rather not have some of my home's electrical infrastructure controllable over the Internet by default.

So instead, I did the following:

  1. Plug in the Shelly Plug, and plug a load into it (in this case, my Starlink power brick, which powers the Starlink dish and router).
  2. Press the power button on it to turn it on.
  3. On my Mac, connect to the shellyplug___ network (the Plug is in AP mode by default).
  4. Wait for a DHCP address on my Mac, then connect to the .1 of that CIDR (in my case, I connected to 192.168.33.1 in my browser).

At this point, I got to the Shelly Plug web UI:

Shelly Plug US - Web UI

I went to the 'Internet & Security' pane, and set the Plug to connect to my existing WiFi network (note: the Plug only operates on 2.4 GHz, so don't try connecting to a 5 GHz-only network!):

Shelly Plug - WiFi Client Mode connection settings

Then I used fing (you could also use nmap) to find the new Shelly plug once it had connected to my WiFi network. If you set a static IP you could skip this step.

I connected back to it again, then updated my DHCP and DNS servers to add a static IP address and DNS mapping for the plug, so it would be easy to connect to it inside my house at shelly1.geerli.net.

Note: I also updated the device's firmware at this point, and set a Web UI access password. It's better than nothing, but in general, if you don't trust the company that makes the device (and even if you do...), don't buy it, or segregate it on a separate WiFi network.

Getting power consumption

I dug into the Shelly Plug API to see how I could get the one valuable bit of information I wanted out of it: the current power consumption.

And it looks like I can get that data through the endpoint /meter/0, so I confirmed as much with curl:

$ curl http://admin:[email protected]/meter/0
{"power":85.05,"overpower":0.00,"is_valid":true,"timestamp":1618501251,"counters":[90.138, 26.141, 0.000],"total":116}

The power is returned in Watts, and the total is "Total energy consumed by the attached electrical appliance in Watt-minute", which I'm guessing is reset every now and then. No problem, we'll be using the spot value from power, and we can aggregate the data to get things like daily/weekly/monthly totals later.

Monitoring the data with Prometheus and Grafana

All right, so I have a way to get the data from the Shelly plug, but I want to add a graph of this data over time, to see if there are any trends (e.g. does Dishy consume more energy at night, or on a cloudy or stormy day?), and to have a 100% accurate calculation of the kilowatt-hours of energy it consumes (which can be converted into a cost estimation for other people interested in Starlink).

I initially dug into the Prometheus documentation for how to generate metrics using their official client libraries (see Writing exporters), but it seemed like that documentation—and most everything else on the internet like it—was laser-focused on "getting app metric data out of your [Go|Python|Ruby|etc.] app", and not on the use case of "write a really simple scraper that drops a couple values into Prometheus."

# HELP my_metric A metric I'm interested in monitoring, like power in Watts.
# TYPE my_metric gauge
my_metric 79

So I just looked at the expected metrics endpoint output format (above), pulled out my golden hammer (PHP), and wrote a 52-line PHP script (see my shelly-plug-prometheus project) that:

  1. Grabs some environment vars for connection configuration.
  2. Requests the metrics endpoint for the Shelly Plug.
  3. Parses out relevant metrics.
  4. Returns that data at the /metrics endpoint.

And being a super-lazy developer (lazy could also be called 'efficient'), I didn't want to spend the extra 5 minutes building a Docker image and maintaining that, so I built the PHP script so I could just run it inside an instance of the official PHP Docker image like so:

docker run -d -p 9924:80 --name shelly-plug \
  -e SHELLY_HOSTNAME='my-shelly-plug-host-or-ip' \
  -e SHELLY_HTTP_USERNAME='username' \
  -e SHELLY_HTTP_PASSWORD='password' \
  -v "$PWD":/var/www/html \
  php:8-apache

I added configuration to start the Shelly Plug monitoring container to my internet-pi repository, deployed it to the Raspberry Pi that dutifully reminds me how often my main ISP (Spectrum) does not give me near the performance I pay for, and then built a dashboard that shows me power usage:

Power Consumption Grafana dashboard with Shelly Plug US power usage coming through

That dashboard is in code here: https://github.com/geerlingguy/internet-monitoring/blob/master/grafana/provisioning/dashboards/power-consumption.json.

The one thing missing is a display of kWh for the selected time period (or just a graph of kWh per day or something like that). My Grafana-foo is not yet good enough to figure out how to put the math involved in translating point-in-time Watt readings (or one average) into a kWh display using Prometheus data (it seems like the people who do power monitoring with Grafana mostly use InfluxDB and the query syntax there is more familiar to me).

Yes, I know I could use Home Assistant to do all this, but I don't right now and I'm still waiting for more maturation in the whole "Connected Home" space before I trust putting even more things in my living space into some central API!

And yes, I know I could also configure MQTT to do some of this stuff, but I don't currently use MQTT so it's as much work (for now) to create a separate exporter and manage the data myself.

Conclusion

Since I found the Shelly Plug, I'm happy enough with it. It saved me from trying to build some sort of Pi or Pico-connected power measurement device, though I'm surprised more manufacturers don't have power-monitoring smart plugs.

I mean, P3 International, makers of the Kill-A-Watt, would be a shoe in for 'easily incorporate the already-measured data into a dashboard', I would think.

As it is, there are only a few smart plugs that do power monitoring on the device / appliance-level. Otherwise you have to build a whole home monitoring solution in your electrical box, and you can only really get the data there on a circuit-level.

I'll be reviewing SpaceX's Starlink more in-depth soon, but for now, it seems to use about 90W continuously, on average, all day every day, with very few spikes above 100W, and seemingly no power consumption correlation to transmit/receive activity or weather.

Comments

Wow, that's a crazy amount of power! Especially for a "green" company like Tesla. With that power, I can run my DSL modem 24/7 AND drive my electric car 6000 km every year.

What on Earth (wink) is it doing with 90W? Is there a heater in there?

I think for the first version of the dish, power optimization wasn't really a goal. It takes a lot of power to operate the phased array antenna to track satellites constantly, while transmitting and receiving, alongside all the other supporting bits inside the dish. In some ways, I'd call the thing more a full-fledged outdoor computer that happens to be in a shape resembling a dish, rather than a dish like you'd get for DirecTV.

This doesn't bode well for using Starlink at my offgrid cabin. My little 3000W solar system already gets pushed to the limit. I guess I could fire up the generator whenever I need the internet but that's a bit of a pita.

Going to try to mod this by subbing the Shelly for the more available BN-LINK WiFi Heavy Duty Smart Plug Outlet available on Amazon. Will report back if successful.

Russ — I, and probably others, would like to hear about your efforts even if you are not successful. Knowing what doesn’t work / what was attempted is almost as valuable as hearing about what is successful :-)

I did something similar with zwave a while back. I have some power strips, dimmers, applicate controllers, & a mains monitor, and they all have power monitoring capabilities. I wrote a small node.js script to make a Prometheus exporter that would log the device's stats including instance power usage & total kWh. https://github.com/rektide/prom-ozz-zwave

How would you get this playbook working with more than one Shelly plug? Would you just need to add another port in the docker-compose file?

Just wondering, have you had any issues over time of the plugs becoming unresponsive and having to unplug/plug back in to "reboot" them? Looking for replacement "smart" plugs that are able to be controlled via mqtt or rest api (locally) (or even power strips) .. the power monitoring would definitely be bonus for monitoring like you describe, but also be able to detect if something is left on (for example).. Not being familiar with Prometheus was assuming that you were using Influxdb (time series db) behind your Grafana.

I wrote a script in Python for exposing the power consumption of my shelly plug s, the metrics are exposed on port 8000

#!/usr/bin/python
# -*- coding:utf-8 -*-
import ctypes
import logging
import time
#import smbus
import requests
import re

from prometheus_client import Gauge, start_http_server
#from systemd.journal import JournalHandler
#from systemd import journal
from systemd.journal import JournaldLogHandler

#SCRIPT_PATH=$(dirname "$(realpath "$0")")

# Setup logging to the Systemd Journal
log = logging.getLogger('Shelly_Plug_Power')
#log.addHandler(journal.JournaldLogHandler())
log.addHandler(JournaldLogHandler())
log.setLevel(logging.INFO)

# The time in seconds between sensor reads
READ_INTERVAL = 60.0

# Create Prometheus gauges for humidity and temperature in
# Celsius and Fahrenheit
gt = Gauge('power',
'power', ['scale'])

# Initialize the labels for the temperature scale
gt.labels('Watt')

def read_shelly():
try:
#HTTP Anfrage an Shelly und Weitergabe an Prometheus
response=requests.get("http://IP OF YOUR SHELLY/meter/0")
powertxt=re.search('"power":(.*),"overpower"',response.text)
# print(response.text)
# print(powertxt.group(1))
power=float(powertxt.group(1))
print(type(power))
except RuntimeError as e:
# GPIO access may require sudo permissions
# Other RuntimeError exceptions may occur, but
# are common. Just try again.
log.error("RuntimeError: {}".format(e))

if power is not None:
gt.labels('Watt').set(power)

log.info("Power:{0:0.1f}*W".format(power))

# time.sleep(READ_INTERVAL)

if __name__ == "__main__":
# Expose metrics
metrics_port = 8000
start_http_server(metrics_port)
print("Serving sensor metrics on :{}".format(metrics_port))
log.info("Serving sensor metrics on :{}".format(metrics_port))

while True:
read_shelly()
time.sleep(60)

I think the kWh should be easy to do with a sum_over_time() Prometheus expression ?
e.g. if you scrape every minute, you can do sum_over_time(metric[1h]) * 60