[TUTORIAL] Networked NUT for Cyberpower UPS


Recommended Posts

 

I should start with the disclaimer that I have no particular expertise with NUT so this guide is not definitive.

 

I have a Cyberpower UPS (CP685AVR) powering a Raspberry Pi and my Unraid Server.

 

Initially I installed apcupsd on the Pi, connected it to the UPS (via USB) and setup Unraid as slave. Install was fairly straightforward and worked well except for automatic restart after shutdown - when power's restored connected servers must be booted manually. (NOTE: If your CyberPower UPS is connected directly to your unRAID server, enabling Turn off UPS after shutdown in settings not only won't work but may cause the UPS to cut power without warning, forcing a parity check.)

 

I settled on NUT running as master on the Pi (Debian-based) connected to the UPS via USB, with Unraid and all other clients as slaves.

 

Overview

We can trigger shutdown one of two ways - after a set amount of time on battery or at a set battery level.

 

The timer method is more robust because if, after a power outage and shutdown, power's restored and the system boots but it's lost again, unraid may pick up the battery-level shutdown signal before the startup process completes which seems risky. And that may happen several times. 

 

Instead we'll set NUT to shutdown clients after 20 minutes on battery (approximately 20% of my runtime capacity) so in theory it can handle a couple of these power's restored/power's lost cycles.

 

Low battery will still trigger shutdown but the goal's to avoid a low battery condition.

 

Let's get started:

 

On Raspberry Pi

sudo apt install nut

# Verify UPS is visible with

lsusb

# Configure

sudo emacs /etc/nut/ups.conf

# My configuration block in ups.conf

[cyberpower]
    driver = usbhid-ups
    port = auto
    desc = "UPS CP685AVR"
    offdelay = 20
    ondelay = 0
    ignorelb
    override.battery.charge.low = 20
    override.battery.charge.warning = 40
    pollinterval = 15

A few notes about these settings:

  • offdelay says turn UPS off X seconds after master server shuts off, required for automatic restart
  • ondelay should be 30 (10s greater than off delay) but a cyberpower bug requires 0 to avoid unexpected power cuts (similar to apcupsd behavior)
  • a shorter pollinterval is required to prevent the Pi (and maybe other debian systems) from losing contact
  • ignorelb allows custom overrides for battery.charge

 

# Start

upsdrvctl start

# Configure nut upsd

sudo emacs /etc/nut/upsd.conf

# Add network request listener replacing <Pi IP> with your Pi's IP address

LISTEN <Pi IP> 3493

# User setup

sudo emacs /etc/nut/upsd.users

# Add users for admin, monitoring, local and remote users replacing pwd with your preferred password

[admin]
        password = pwd
        actions = SET
        instcmds = ALL
[local]
        password  = pwd
        upsmon master
[remote]
        password  = pwd
        upsmon slave
[monuser]               
        password  = pwd
        upsmon slave

# Configure monitoring

sudo emacs /etc/nut/upsmon.conf

# Change the following values:

 

# Increase slave shutdown wait period to 10 minutes (worst case)

HOSTSYNC 600

# Works with pollinterval setting to prevent USB disconnection

DEADTIME 25

# Alerting twice/day about a battery change is excessive, make it once

RBWARNTIME 86400

# To be safe give clients an additional 90 seconds to fully shutdown                                                                                                             

FINALDELAY 90

# Add monitor replacing pwd with the password you specified for [local] above

MONITOR cyberpower@localhost 1 local pwd master

# Timed shutdown - see upssched.conf for details                                                                                                                                    

NOTIFYCMD /sbin/upssched
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT SYSLOG+WALL+EXEC

 

# Set the schedules in upssched.conf:

sudo emacs /etc/nut/upssched.conf

# Verify it has the line

CMDSCRIPT /bin/upssched-cmd

# Add:

# Command pipe and lock-file
PIPEFN /var/run/nut/upssched.pipe
LOCKFN /var/run/nut/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 20 minutes on battery (20 * 60 = 1200)
AT ONBATT * START-TIMER onbatt_shutdown 1200

# 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

 

# Customize the executable called by upssched

sudo emacs /bin/upssched-cmd

# ...by replacing existing code with the following (substituting the password you set for admin earlier for <pwd>)

# START: User-specific settings                            
#
UPS_USERNAME="admin"
UPS_PASSWORD="<pwd>"
UPS_LINK="cyberpower@localhost"
# END  

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 (20) 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

# Verify it's executable by all:

ls -l /bin/upssched-cmd

 

# Schedule the (weekly) quick test mentioned earlier

sudo crontab -e

# Add

# Weekly UPS self-test
0  2  *   *   MON       /bin/upscmd -u admin -p <password from upsd.users>  cyberpower@localhost test.battery.start.quick

 

# Configure nut as netserver

sudo emacs /etc/nut/nut.conf

# Change MODE

MODE=netserver

 

Now reboot the Pi to verify the NUT services launch on startup

 

# Confirm services are running

sudo service nut-server status
sudo service nut-client status

# Confirm communication with UPS - this should return a bunch of info

upsc cyberpower

 

On Unraid

 

# Install NUT from CA

 

# Configure as follows using the Pi IP, monuser password and remote password specified earlier

# NOTE: Whether in the case of a timed shutdown or an emergency low battery shutdown

#            we're relying on the Pi (NUT server) to send the signal. Ideally the shutdown triggers here 

 #           won't be used.

1208822292_ScreenShot2021-01-23at5_33_32AM.thumb.png.3bb0b7cbf27e6b7b8330e729573cfe3a.png

 

Miscellaneous 

 

# Test shutdown.

# NOTE: This will shutdown your system so it's safest if you stop the array first. 

#           But stopping is most of the required shutdown time, so after you confirm

#           this works you might want to test again without stopping the array.

sudo upsmon -c fsd

 

Optional

 

# Cyberpower uses status codes not recognized by the NUT plugin which as far as I can tell only affect display but I want my dashboard to be pretty so let's customize the display script (NOTE: this will get overwritten, and possibly cause conflicts, on plugin update)

mkdir /boot/extras/nut
cp /usr/local/emhttp/plugins/nut/include/nut_status.php /boot/extras/nut/nut_status.php
vi /boot/extras/nut/nut_status.php

# Replace the state array with the following

$state = [
  'OL'             => 'Online',
  'OL CHRG'        => '<span class="orange-text">Online: charging</span>',
  'OL CHRG LB'     => '<span class="orange-text">Online: charging</span>',
  'OL DISCHRG'     => '<span class="orange-text">Online: discharging</span>',
  'OL DISCHRG LB'  => '<span class="orange-text">Online: discharging</span>',
  'OL BOOST'       => '<span class="red-text">Online: low voltage</span>',
  'OB DISCHRG'     => 'Offline: On battery',
  'OB LB'          => 'Offline: Low battery'
];

 

# If you prefer the look of the built-in UPS dashboard, the next two sections (presented in diff form) mimic it

# diff /usr/local/emhttp/plugins/nut/include/nut_status.php.orig /usr/local/emhttp/plugins/nut/include/nut_status.php
44,45c48,49
<       $runtime   = gmdate("H:i:s", $val);
<       $status[2] = strtok($val/60,' ')<=5 && !in_array('ups.status: OL', $rows) ? "<td $red>$runtime</td>" : "<td $green>$runtime</td>";
---
>       $runtime   = strtok($val/60,' ');
>       $status[2] = strtok($val/60,' ')<=5 && !in_array('ups.status: OL', $rows) ? "<td $red>".intval($runtime)."m</td>" : "<td $green>".intval($runtime)."m</td>";
49a54,56
>     case 'input.voltage':
>       $voltage   = intval(strtok($val,' '));
>       break;
64c71
<   $status[3] = $power==0 ? "<td $red>${power}w</td>" : "<td $green>${power}w</td>";
---
>   $status[3] = $voltage<114 ? "<td $orange>${voltage}V</td>" : "<td $green>${voltage}V</td>";
# diff /usr/local/emhttp/plugins/nut/nutFooter.page.orig /usr/local/emhttp/plugins/nut/nutFooter.page
151a152
> <span class='ups'>Input voltage:</span><span class='nut_nompower'></span><br>
154d154
< <span class='ups'>Nominal power:</span><span class='nut_nompower'></span><br>

 

# Set your go file to overwrite the default files with the customized version on boot

vi /boot/config/go

# Add to the end:

# Customize nut (UPS) display
cp /usr/local/emhttp/plugins/nut/include/nut_status.php /usr/local/emhttp/plugins/nut/include/nut_status.php.orig
cp /boot/extras/nut/nut_status.php /usr/local/emhttp/plugins/nut/include/nut_status.php
# display voltage instead of max power
cp /usr/local/emhttp/plugins/nut/nutFooter.page /usr/local/emhttp/plugins/nut/nutFooter.page.orig
cp /boot/extras/nut/nutFooter.page /usr/local/emhttp/plugins/nut/nutFooter.page

 

 

All done. Automatic shutdown of the servers and UPS work and (assuming proper BIOS settings) they'll all restart when power's restored.

 

Edit 7/6/2020: Increase offdelay to account for slow unraid shutdown 

Edit 1/23/2020: Misc reliability tweaks, shift from battery level-based shutdown to timer-based shutdown

Edited by CS01-HS
Link to comment
24 minutes ago, CS01-HS said:

# Cyberpower uses status codes not recognized by NUT which as far as I can tell only affect display but I want my dashboard to be pretty so I'll customize the display script (NOTE: this will get overwritten and possibly cause conflicts on plugin update)


mkdir /boot/extras/nut

cp /usr/local/emhttp/plugins/nut/include/nut_status.php /boot/extras/nut/nut_status.php

vi /boot/extras/nut/nut_status.php

# Replace the state array with the following


$state = [
  'OL'             => 'Online',
  'OL CHRG'        => '<span class="orange-text">Online: charging</span>',
  'OL CHRG LB'     => '<span class="orange-text">Online: charging</span>',
  'OL DISCHRG'     => '<span class="orange-text">Online: discharging</span>',
  'OL DISCHRG LB'  => '<span class="orange-text">Online: discharging</span>',
  'OB DISCHRG'     => 'Offline: On battery',
  'OB LB'          => 'Offline: Low battery'
];

👍

Link to comment
  • 4 weeks later...

Not sure why you had issues with apcupsd or if it is a Pi related problem.. but I use apcupsd for my Windows pc and don't have any (known) problems.

 

Perhaps you need to reverse your setup to lessen the problems.  Have unraid as your master and let the Pi read from it instead of the other way around.

 

My conf on the Windows pc is pretty simple.. in fact I only need the IP:PORT to unraid and everything goes smoothly.

 

To be fair, I haven't had any power outages that lasted long enough to need to shut down the servers, so can't say for sure if they would start up again.. which seems to be your main problem..............  is your BIOS on the unraid server set to power on after a power loss?

Link to comment
  • 4 months later...

Would you mind helping me get this setup in a standalone setting? It seems to remove some options when I begin the service by clicking apply. It will keep all other options in the ups.conf but these:

    offdelay = 300

    ondelay = 0

    ignorelb

My setup is just an unraid server with a CP1500PFCLCD plugged in via usb. I would love to enable "Turn off UPS after shutdown" but ups.delay.shutdown (which should be set to 300 with the custom settings) keeps getting set to 20. This kills the power to the server before a clean shutdown.

 

I have tried manual mode as well. It will accept all of these settings BUT when the server is rebooted the service does not auto start. Am I missing something is the go file? I cannot find documentation about manual mode.

 

Any help would be appreciated!

Link to comment

Are you sure it won't work with the built-in "UPS Settings" ? Support seems to vary model-to-model and mine's a low-end unit.

 

Otherwise I can't help much with customization of the NUT plugin. The reason I connected it to the Pi was to allow me to tweak it. Maybe post to that thread?

Link to comment

I have tried the built-in setting and I get the same results form that previous post you linked. Enabling "Turn off UPS after shutdown" my UPS will go into "Schedule Mode" and count down from 60 min. When it hits 0 nothing happens, it doesn't turn it off or anything. I also posted in the [Plugin] NUT thread about some things I just tried too if you're interested. I tried to force some setting upon boot but it seems NUT will randomly reset the variable for shutdown and start that were set in the ups.conf file. For now I've uninstalled and I'm back on the built-in without the UPS shutdown enabled.

 

  • Like 1
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.