Peanuts and Cyanide
God I hate NUT so much
TLDR: I hate NUT with even more than all of my being, so I set up Peanut with a custom monitoring script.
I would say that electricity is the single most important component of any home lab. Without it you have some fancy metal desk ornaments, and losing it can turn your compute power into ornaments permanently. Ok, that is incredibly unlikely, but improper shutdown can lead to data corruption and issues like that. That's why we have an Uninterruptible Power Supply (UPS) which has batteries that can keep things running long enough to shut down safely. The challenge here is that you need to know when the UPS is on battery to know when to shut down. Fortunately, there is a powerful tool that handles this for many consumer and enterprise UPS systems. Unfortunately, this tool is Network UPS Tools (NUT), and I hate it with every fiber of my being. I don't know why, it's complicated but it's by no means the worst thing I've ever had to do, but I just can't stand it. I've had forays into configuring NUT in the past which all ended in me whistling a little ditty and ignoring the problem after contemplating how hard cyanide would be to obtain.
At some point having 'power loss handling' on your to-do list for over a year begins to provide some small motivation, and after a bit of half-assed googling across several days I stumbled upon PeaNUT, which seems to be precisely what I have been looking for all along. It is simple, but it means I don't have to configure NUT myself, and that's is my only criteria at this point. So I look into it a little bit and come up with a configuration that should probably work and deploy it on my Unraid server which both UPS are connected to over USB.
My compose.yml:
services:
nut-upsd-1500:
image: instantlinux/nut-upsd:2.8.2-r2
container_name: cp1500
devices:
- /dev/bus/usb
# Optionally expose NUT server port directly
# ports:
# - "3493:3493"
environment:
API_USER: ${NUT_USER}
API_PASSWORD: ${NUT_PASSWORD}
DRIVER: usbhid-ups
NAME: cyberpower-1500
DESCRIPTION: 1500VA CyberPower UPS
SERVER: master
MAXAGE: 30 # required for cyberpower devices
SERIAL: XXXXXXXXXXXX
restart: unless-stopped
nut-upsd-900:
image: instantlinux/nut-upsd:2.8.2-r2
container_name: cp900
devices:
- /dev/bus/usb
# Optionally expose NUT server port directly
# ports:
# - "3493:3493"
environment:
API_USER: ${NUT_USER}
API_PASSWORD: ${NUT_PASSWORD}
DRIVER: usbhid-ups
NAME: cyberpower-900
DESCRIPTION: 900VA CyberPower UPS
SERVER: master
MAXAGE: 30 # required for cyberpower devices
SERIAL: XXXXXXXXXXXX
restart: unless-stopped
peanut:
image: brandawg93/peanut:5.9.1
container_name: peanut
volumes:
- /opt/docker/peanut:/config
- ./settings.yml:/config/settings.yml
ports:
- 8088:8080
environment:
WEB_PORT: 8080
restart: unless-stopped
And settings.yml:
NUT_SERVERS:
- HOST: cp1500
PORT: 3493
USER: ${NUT_USER}
PASSWORD: ${NUT_PASSWORD}
- HOST: cp900
PORT: 3493
USER: ${NUT_USER}
PASSWORD: ${NUT_PASSWORD}Of course it can't be that easy, and for some reason the 900VA unit just will not be recognized in the upsd container. I kept just getting insufficient permissions on everything from libusb, and my efforts at troubleshooting were becoming tiring quickly. My supremely elegant solution, of course, was to plug them into a different server and move the stack. After confirming that the docker container recognized both units correctly, I moved the stack in Komodo and of course now the peanut container is broken because for some reason Komodo refused to properly pull the repo and settings.yml was getting created as a directory instead of the file. After some time troubleshooting this as well, I felt like Sensei from 'No Longer Allowed in Another World' and so I just manually fixed the files. That particular problem now lives hidden in the depths of my mind cabinet until it inevitable and inexplicably becomes a problem two years from now.
After somehow fighting with the supposedly simpler option for several hours and getting it to finally work, I felt pretty proud of myself. I also absolutely did not want to be working on this crap anymore, so I added some entries in Uptime Kuma for monitoring and called it a day.
For the UPS status I used the peanut API /api/v1/devices/cyberpower-900/var/ups.status and a json query $[0] in ["OL", "OL CHRG", "CHRG"] expecting a value of true. I looked at the other possible values for ups.status as well, but decided I want to be notified in case of pretty much any abnormal status.
TIME SKIP!!! I'm writing this like 6 months after the previous section. Having two UPS connections to one server turned out to cause a lot of headache, so I now just have Unraid monitoring the main one and I'm in the process of consolidating everything onto my Unraid server anyway. That's a whole other post I won't get into, but yeah take my word for it and use multiple hosts if you have to monitor multiple UPS devices. There is a chance that other UPS drivers (APC etc.) would work better, but I have Cyberpower so that doesn't help me much. Anyway, I gave up and went a much simpler route but maybe the above will help you in your fruitless endeavors.