These are my notes on how I set up my Fedora Linux desktop to listen to a Cyberpower UPS to known when to shutdown when there is a power outage. This is only for one machine connected directly to the UPS through usb and doesn't cover when there are multiple machines that needs to be shutdown.
The simplest way to configure your Linux system to work with a Cyberpower UPS is probably to use Cyberpower's PowerPanel for Linux software, but I didn't use it since I wanted to see if I can use non-proprietary software. Instead, I used the open-source Network UPS Tools (NUT) but it required a bit of manual work to get it configured.
All I want to do is to have my PC shutdown a few minutes after the power go out of the house so I bought a Cyberpower UPS and use the direct USB connection for it to communicate with the PC.
Once connected, check that Linux can see the UPS:
# lsusb
You should see the UPS listed among the list of USB devices.
Install the NUT software:
# sudo dnf install nut nut-client
This will install NUT and there are 5 key configuration files to be aware of:
- ups.conf - settings for UPS driver
- upsd.conf - settings for UPS daemon
- upsd.users - access control file for the UPS daemon
- upsmon.conf - settings for the UPS monitoring daemon
- upssched.conf - settings for scheduler daemon
Once NUT is installed, you can use one of its tools to identify the UPS and it will give you info that you can use in the config files:
# sudo nut-scanner
Add the information to the /etc/ups/ups.conf
[cps1500avr]
driver = "usbhid-ups"
port = "auto"
vendorid = "0764"
productid = "0501"
product = "ST Series"
vendor = "CPS"
bus = "004"
The cps1500avr can be anything you want. It is the name you'll be using to identify the UPS. "usbhid-ups" is the driver for Cyberpower UPS. All of this info came from the nut-scanner.
Add a udev rule, /etc/udev/rules.d/50-usp.rules, so that Linux understand the UPS:
SUBSYSTEM=="usb", ATTR{idVendor}=="0764", ATTR{idProduct}=="0501", GROUP="nut" TAG+="systemd", ENV{SYSTEMD_WANTS}+="nut-server.service nut-monitor.service"
The TAG+ part is something I found on this blog for an issue with the system service not seeing the UPS and thus not able to start when the system starts up.
Reload the UDEV rule:
# sudo udevadm control --reload-rules
# sudo udevadm trigger
Test that the driver can start and then start/enable the service:
# sudo upsdrvctl start
# sudo systemctl start nut-driver-enumerator.service
# sudo systemctl enable nut-driver-enumerator.service
Now we need to add the NUT user that will be used to monitor the UPS to /etc/ups/upsd.users
[nutmon]
password = <password>
uspmon primary
action = SET
instcmds = ALL
"nutmon" can be anything you like and then you can start the server and ask it to print out the UPS info that it sees.
# sudo systemctl start nut-server.service
# sudo systemctl enable nut-server.service
# upsc cps1500avr
Edit /etc/ups/upsmon.conf by adding:
MONITOR cps1500avr@localhost 1 nutmon <password> primary
Start and enable the monitoring service
# sudo systemctl start nut-monitor.service
# sudo system enable nut-monitor.service
Finally, we need to tell NUT what to do when certain events happens such as when the battery is low or when it notices that the power is out. Edit /etc/ups/upssched.conf (thanks to this article for the example)
# Gives the script to run the commands for various signals
CMDSCRIPT /usr/bin/upssched-cmd
PIPEFN /var/lib/ups/upssched.pipe
LOCKFN /var/lib/ups/upssched.lock
# Send alerts immediately on change in line power
AT ONBATT * EXECUTE onbatt
AT ONLINE * EXECUTE onpower
# (Optional) Silence the beeper after 2 minutes
AT ONBATT * START-TIMER mute_beeper 120
AT ONLINE * CANCEL-TIMER mute_beeper
# Shutdown after 5 minutes on battery (5 * 60 = 300)
AT ONBATT * START-TIMER onbatt_shutdown 300
# Cancel timer if power's restored
AT ONLINE * CANCEL-TIMER onbatt_shutdown
# Battery replacement indicated by cron'd quick test
AT REPLBATT * EXECUTE replace_batt
Then edit the command script you put in (in my case it is /usr/bin/upssched-cmd):
UPS_USERNAME="nutmon"
UPS_PASSWORD="<password>"
UPS_LINK="cps1500avr@localhost"
case $1 in
onbatt)
# make sure beeper is enabled
upscmd -u ${UPS_USERNAME} -p ${UPS_PASSWORD} ${UPS_LINK} beeper.enable
# alert
message="Power outage, on battery"
logger -t upssched-cmd "$message"
;;
onpower)
message="Power restored"
logger -t upssched-cmd "$message"
;;
mute_beeper)
message="(2) minute limit exceeded, muting beeper"
upscmd -u ${UPS_USERNAME} -p ${UPS_PASSWORD} ${UPS_LINK} beeper.mute
;;
onbatt_shutdown)
message="Triggering shutdown after (5) minutes on battery"
logger -t upssched-cmd "$message"
/sbin/upsmon -c fsd
;;
replace_batt)
message="Quick self-test indicates battery requires replacement"
logger -t upssched-cmd "$message"
;;
*)
logger -t upssched-cmd "Unrecognized command: $1"
;;
esac
Test that it will shutdown by sending it the same shutdown signal from the UPS:
If all goes well, get the service to start on boot:
# sudo systemctl enable nut.target
# sudo systemctl enable nut-driver.target
From now on, if the power outtage lasts more then 5 minutes (i.e. the UPS is running on battery), your system will shut down. You'll need to manually turn the system back on when the power returns.
Notes
- I'm not sure if something automatically started and enabled the specific driver (nut-driver@cps1500avr.service) or if I did it and just forgot to note it down.
- This article from the Archlinux wiki described a problem that sounds like the same problem that the UDEV TAG part addresses but uses a different approach.