UPDATE (February 15, 2022): Updated with instructions for adding a second UPS
Since I don’t want my OH installation to be corrupted by a power failure, I purchased a CyberPower UPS, and connected it to my Raspberry Pi using Network UPS Tools (NUT). There are various topics in the community about NUT, and this one was the most helpful for my purposes.
With everything I’ve learned, here are some instructions to help others get started. Note that this guide is for is a simple setup with the UPS only having to turn off a single computer (the Raspberry Pi running OH and NUT). You can also have NUT turn off other computers, but I don’t have experience with that. However, the link I reference below for installing NUT talks about it in detail.
1. Choosing a UPS
My primary concern when a power failure occurs is to shut down the Raspberry Pi, so that the SD card doesn’t get corrupted by immediate power loss. I’m less concerned about OH continuing to run during the outage, since it doesn’t have anything to control.
This meant that my minimum requirement was a UPS with a USB data port and NUT compatibility. Battery run-time was a secondary benefit…it just has to run long enough to shut down the RPi.
I chose a CyberPower ST625VA. It’s the smallest CyberPower I could find with a USB port (at the time of writing), and with my RPi, modem, and mesh router in the battery-supported outlets, it’ll keep OH and my WiFi running for about two hours.
There’s an old and outdated compatibility list on networkupstools.org. My UPS isn’t on it, but many of the CyberPower devices use the same “usbhid-ups” driver, and that worked for me.
2. Install NUT on your Raspberry Pi
Once your UPS is plugged in and connected to your Raspberry Pi via USB, you’ll need to install NUT. Use SSH to access your Raspberry Pi, then follow the instructions in this excellent blog post by Mel Grubb. But first, read my notes below the link.
Notes
- The guide says to use
nano
to edit theups.conf
andnut.conf
files. However, nowadays it is strongly recommended to usevisudo
to edit configuration files. - On an RPi4 running Debian Buster, I get syntax errors when I save the
ups.conf
andnut.conf
files…even if I don’t edit the original contents. After confirming that the contents were as they should be, I saved the files and everything comes out fine. I’m not sure whysudo
is throwing the errors. - After saving
nut.conf
, the service did not work until I rebooted my RPi4. So if you have any issues, I suggest rebooting before trying anything else. - If you’re using a CyberPower UPS, I also recommend looking at this post to avoid the “Error: Data Stale” issue: NUT & CyberPower UPS | /dev/urandom
- If you’re only trying to shut down one Raspberry Pi, you can stop at “Email notifications (optional)”. If you want to shut down multiple computers, there are additional instructions on how to do that.
3. Connect OH to NUT
You could stop at this point and your RPi will be protected from power loss. NUT will detect a power failure, monitor the battery, and shut down the RPI when the battery is nearly dead. But where’s the fun in that? With the NUT binding, you can see the status of your UPS and get notifications when your power is out/restored.
There are also ways to have OH control NUT, but I’m not interested. I’m just going to let NUT do its job and keep an eye on it with OH.
Install the Network UPS Tools Binding
In the openHAB MainUI, go to Administration → Settings → Bindings and install the “Network UPS Tools Binding”.
Create a thing for your UPS
If you want to configure via text files, add a ups.things
file with the following thing:
Thing networkupstools:ups:ups1 [ device="yourupsname", host="localhost", refresh=60, username="admin", password="yourpasswordhere", refresh="60" ]
Alternatively, you can add the thing via MainUI. openHAB 3.x won’t autodiscover your UPS, so you’ll need to manually add the thing.
- Go to Administration → Settings → Things.
- Click the “+” button to add a thing, then choose the “Network UPS Tools Binding” and “Network UPS Tool”.
The details will be the same as the text config above (I call my device “openhabups”).
Once this is done, OH will be able to get status updates from NUT.
Create items
Before we can create items, we need to know what data can be read from your UPS. SSH into your RPi and type:
upsc yourupsname
My UPS gives me a list that looks something like this:
Init SSL without certificate database
battery.charge: 100
battery.charge.low: 10
battery.charge.warning: 20
battery.mfr.date: CPS
battery.runtime: 6322
battery.runtime.low: 300
battery.type: PbAcid
battery.voltage: 13.9
battery.voltage.nominal: 12
...
ups.status: OL
ups.timer.shutdown: -60
ups.timer.start: -60
ups.vendorid: 0764
And based on this list, here are the items I’ve set up. You can also set these items up in MainUI, but I’ve left them as text files.
String UPS_Status "Status [MAP(ups.map):%s]" <poweroutlet_us>{networkupstools="ups1:ups.status"}
Number UPS_Battery_Runtime "Runtime [JS(duration_from_seconds.js):%s]" <time>{networkupstools="ups1:battery.runtime"}
Number UPS_Battery_Charge "Battery Charge [%.0f%%]" <batterylevel> {networkupstools="ups1:battery.charge"}
With these three items, I can have OH notify me when there’s a power outage (UPS_Status changes from “OL” to “OB DISCHRG”), and again when power is back. While the power’s out, I can monitor the remaining runtime and battery charge in my sitemap. No need to shut down the RPi, because NUT will do that for me if it becomes necessary.
Transformations
I’ve used a couple of transformations in my items, so that they’ll display nicely in my sitemap.
The “ups.map” file referenced in the first item converts the CyberPower outputs for display on my site map as follows:
OL=AC power
OB\ DISCHRG=Battery discharging
OL\ CHRG=Battery charging
=Unknown
In the second item, I reference “duration_from_seconds.js”. This is a JavaScript that converts seconds to hours/minutes/seconds. I originally used a script I found in Network UPS Tools (nut) setup issues, but have since changed to using this one from Stack Overflow. They both work, but VS Code told me that the substr
method is deprecated, so I thought I’d find something different in case it stops working.
(function convertSeconds(seconds) {
var convert = function(x) { return (x < 10) ? "0"+x : x; }
return convert(parseInt(seconds / (60*60))) + ":" +
convert(parseInt(seconds / 60 % 60)) + ":" +
convert(seconds % 60)
})(input)
To use these transformations, you’ll need to add the MAP Transformation and Javascript Transformation bindings via MainUI, and create the ups.map and duration_from_seconds.js files in the conf/transform directory. I’m not going to go into more detail than that, since this guide isn’t about transformation.
3.1 Adding a second UPS
Since writing this tutorial, I’ve added a second UPS for another RPi that I use for my 3D printer (with OctoPrint) and laser printer (with CUPS). I set up NUT on the RPi exactly as before, then configured a thing in OH.
Thing networkupstools:ups:ups2 [ device="newupsname", host="RPi's IP address", refresh=60, username="admin", password="newpasswordhere", refresh="60" ]
It all looked good, but when I saved the thing I got an error saying that the connection was refused. I realized that NUT was configured to only listen for connections internally, so it was rejecting the request from my OH server. I needed to tell NUT to listen for connections on port 3493.
I went back to Mel Grubb’s tutorial and found the answer in the " Reconfigure NUT on the server" section. As stated in the tutorial, I had to edit /etc/nut/upsd.conf
and add two lines:
Listen 127.0.0.1 3493
This line tells NUT to continue listening to local requests (127.0.0.1), ensuring that the NUT monitoring client can still access the NUT server.
Listen 192.xxx.xxx.xxx 3493
This line tells NUT that it should also listen to requests sent to my print server’s IP address.
I saved these changes, restarted the NUT server, and OH was able to connect. Then I just repeated the steps above to make items for my new UPS.
4. Get notified about power outages
Here are the two rules I use to get notified when there’s a power failure at home. You’ll need to have the openHAB Cloud Connector installed and working to use the sendNotification action.
There are other ways to set up these rules (Blocky, ECMA, etc.), but for now I’m sticking with Rules DSL. I’ll update this tutorial if I revise them in the future.
rule "Power outtage"
when
Item UPS_Status changed from "OL" to "OB DISCHRG"
then
sendNotification("me@email.com", "UPS has switched to backup battery")
end
rule "Power returned"
when
Item UPS_Status changed from "OB DISCHRG" to "OL"
then
sendNotification("me@email.com", "Power has returned to UPS")
end
That’s pretty much it. If you’ve also installed NUT, please let me know if you have any suggestions or corrections to improve this guide.