Whilst wrangling some kettle cords on this very busy desk I was reminded of that unused USB port on the back of my UPS and thought "these are a lot of devices for one 15 amp circuit, I should probably measure my consumption".
I have two Cyber Power CP1500 connected to mains power; one for my three workstations and bench gear, the other powers my three servers. I have exactly one power outlet in my office (ikr!) which I'm hoping to remediate in 2023.
Proof-of-concept
I connected the UPS USB cable to my laptop - unfortunately there is no officially supported PowerPanel® for Arch but a simple yay -yS powerpanel was a quick and easy fix.
I enabled and started the service with
sudo systemctl enable pwrstatd.service
sudo systemctl start pwrstatd.service
Then to make sure it's working

It worked, neat!
My next step is to see if there are any pre-built solutions for monitoring power consumption over time. Unfortunately I couldn't find anything in nut and the only other officially supported solution was to connect the UPS to my Apple computer and use the GUI - we can do better.
The pwrstat help text does not contain any means to produce machine-friendly output so it's scripting time.
I chose Python for the PoC because 1) it's ubiquitous and 2) it's less ugly than bash - perhaps I'll riig in the near future.
Anyway, here's what I came up with
#!/usr/bin/env python3
import re
import subprocess
import time
# we will configure node_exporter to include this file in it's output
promfile = "/var/lib/node_exporter/ups.prom"
while True :
# run the pwrstat command, capture output, decode to utf-8
output = subprocess.check_output(["pwrstat", "-status"]).decode('utf-8')
# split command output by line into a list, ignore empty lines
lines = [x for x in output.split("\n") if x]
# we can re-use this variable, not sure if this is `pythonic` though
output = ""
for line in lines :
# we don't care about lines with colons
if ":" in line : continue
# replace all periods (.) with a single colon, lowercase everything
line = re.sub('\.+', ':', line).lower()
# split keys and values, strip padding whitespace
parts = [x.strip() for x in line.split(":")]
# EAFP : skip non-numeric values
try :
key = "ups_metrics_%s" % parts[0].replace(" ", "_")
value = float(parts[1].split()[0])
output += "# TYPE %s gauge\n%s %f\n" % (key, key, value)
# we don't care about text values (for now?)
except :
pass
""" not sure if opening and closing the file every second is a good idea or not,
should probably refactor this to limit i/o ¯\_(ツ)_/¯
""""
with open(promfile, "w") as fh :
fh.write(output)
fh.close()
time.sleep(1)
Place in /usr/local/bin/ups_metrics, set execution bit and run it (nb: there wont be any terminal output)
chmod +x /usr/local/bin/ups_metrics
sudo /usr/local/bin/ups_metrics
Time to test
curl -sSL http://localhost:9100/metrics | grep "ups_metrics"
# HELP ups_metrics_battery_capacity Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_battery_capacity gauge
ups_metrics_battery_capacity 100
# HELP ups_metrics_load Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_load gauge
ups_metrics_load 261
# HELP ups_metrics_output_voltage Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_output_voltage gauge
ups_metrics_output_voltage 122
# HELP ups_metrics_rating_power Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_rating_power gauge
ups_metrics_rating_power 900
# HELP ups_metrics_rating_voltage Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_rating_voltage gauge
ups_metrics_rating_voltage 120
# HELP ups_metrics_remaining_runtime Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_remaining_runtime gauge
ups_metrics_remaining_runtime 22
# HELP ups_metrics_utility_voltage Metric read from /var/lib/node_exporter/ups.prom
# TYPE ups_metrics_utility_voltage gauge
ups_metrics_utility_voltage 122
Success!
Installing as a system service
I created the file /etc/systemd/system/upsmetrics.service with the following content
[Unit]
Description=upsmetrics
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/ups_metrics
Restart=on-failure
RestartSec=30
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Then, reload systemd and enable the upsmetrics service
sudo systemctl daemon-reload
sudo systemctl enable upsmetrics
sudo systemctl start upsmetrics
I followed these instructions to install node_exporter (if you installed using a package manager your service file and paths may differ) -
here's my /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter --collector.textfile.directory=/var/lib/node_exporter
[Install]
WantedBy=multi-user.target
The significant line here is
ExecStart=/usr/local/bin/node_exporter --collector.textfile.directory=/var/lib/node_exporter
Which looks for /var/lib/node_exporter/*.prom files - once I verified the service was running and could see my UPS metrics in the /metrics endpoint I created this simple dashboard (I will write about the temperature metric in a future article).

Hope someone finds this helpful :)
:wq