Skip to content

Instantly share code, notes, and snippets.

@jpstroop
Last active June 1, 2021 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpstroop/42144ce954407a0bd2436a2eaa45d13b to your computer and use it in GitHub Desktop.
Save jpstroop/42144ce954407a0bd2436a2eaa45d13b to your computer and use it in GitHub Desktop.
My Notes on Configuring NUT for Home Use

Setting up Network UPS Tools (NUT) at Home

Note that these instructions use offensive master/slave terminology because they are still necessary keywords in the NUT configuration files. There is work in progress to change this. I am going to use server and client whenever I can, but you can't avoid master/slave in upsd.users or upsmon.conf.

Given two UPS's:

And a Raspberry Pi, I'm setting up my home network and critical hardware to shutdown gracefully when the power fails.

Become root, Install NUT and Dependencies

(commands assume you are root and in /etc/nut going forward)

$ sudo su
$ apt-get install nut nut-client nut-server usbutils
$ cd /etc/nut/

I also normalized permissions as follows:

$ chmod 640 /etc/nut/*
$ chown root:nut /etc/nut/*

I'm certain things can and should be tightened up further; this is fine for home.

Check that the Devices are Visible

root@rpi41:/home/pi# lsusb
Bus 002 Device 001: ...
Bus 001 Device 004: ID 0764:0601 Cyber Power System, Inc. PR1500LCDRT2U UPS # <----
Bus 001 Device 003: ID 0764:0601 Cyber Power System, Inc. PR1500LCDRT2U UPS # <----
Bus 001 Device 002: ...
Bus 001 Device 001: ...

Configure ups.conf

In ups.conf:

[cp850]
        driver = usbhid-ups
        port = auto
        desc = "CyberPower CP850PFCLCD - Network"
        offdelay = 20
        ondelay = 0
        ignorelb
        override.battery.charge.low = 20
        override.battery.charge.warning = 40
        pollinterval = 15
[cp1000]
        driver = usbhid-ups
        port = auto
        desc = "CyberPower CP850PFCLCD - Office"
        offdelay = 20
        ondelay = 0
        ignorelb
        override.battery.charge.low = 20
        override.battery.charge.warning = 40
        pollinterval = 15

Also in that file, comment out:

maxretry = 3

at the very bottom. It doesn't seem to work with the usbhid-ups driver.

Now restart. I don't know why this is necessary, but it is.

When you're back, become root and cd to /etc/nut again.

Check the nut server status with:

$ systemctl status nut-server

Output should be something like:

<date time> <host> upsd[564]: Startup successful
<date time> <host> systemd[1]: Started Network UPS Tools - power devices information server.
<date time> <host> upsd[564]: User upsserver@::1 logged into UPS [cp1000]
<date time> <host> upsd[564]: User upsserver@::1 logged into UPS [cp850]
<date time> <host> upsd[564]: Data for UPS [cp850] is stale - check driver
<date time> <host> upsd[564]: Data for UPS [cp1000] is stale - check driver
<date time> <host> upsd[564]: UPS [cp1000] data is no longer stale
<date time> <host> upsd[564]: UPS [cp850] data is no longer stale

Note that if you need to make changes you should restart with:

$ upsdrvctl stop
$ upsdrvctl start

And after stopping and starting you should see messages like this to stdout (or stderr, not sure):

Broadcast message from nut@<host> (somewhere) (<date time>):

Communications with UPS cp1000@localhost lost [or established!]


Broadcast message from nut@<host> (somewhere) (<date time>):

Communications with UPS cp850@localhost lost  [or established!]

Now test the settings are correct by running upsc, which is a small program that exists to check communications:

$ upsc cp1000@localhost

Init SSL without certificate database
battery.charge: 100
battery.charge.low: 10
battery.charge.warning: 20
battery.mfr.date: CPS
[... it goes on]

Do the same for cp850@localhost

Configure the Server

In nut.conf, set :

MODE=netserver

In upsd.conf, add:

LISTEN 127.0.0.1 3493
LISTEN 192.168.1.67 3493 # SET TO THE IP OF THIS RASPBERRY PI
LISTEN ::1 3493

In upsd.users:

[upsserver]
        password = <make up a pw>
        upsmon master
[upclient]
        password = <make up a different pw>
        upsmon slave

Note that these are not Linux users, they're just roles in the NUT environment.

In upsmon.conf, add:

MONITOR cp1000@localhost 1 upsserver <upsserver password> master
MONITOR cp850@localhost 1 upsserver <upsserver password> master

and find the DEADTIME config further down and set it to DEADTIME 25

This tutorial has some alternative settings for upsmon.conf. The timings there may need adjustment.

Configure the Schedules:

In upssched.conf, make the entire contents as follows:

CMDSCRIPT /bin/upssched-cmd

# 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

Then, in /bin/upssched-cmd, make the contents look like this:

UPS_USERNAME="upsserver"
UPS_PASSWORD="<upserver password from upsd.users>"
CP850_LINK="cp850r@localhost"
CP1000_LINK="cp1000@localhost"

case $1 in
    onbatt)
        # make sure beeper is enabled
        upscmd -u ${UPS_USERNAME} -p ${UPS_PASSWORD} ${CP850_LINK} beeper.enable
        upscmd -u ${UPS_USERNAME} -p ${UPS_PASSWORD} ${CP1000_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} ${CP850_LINK} beeper.mute
         upscmd -u ${UPS_USERNAME} -p ${UPS_PASSWORD} ${CP1000_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

Configure the Clients

.. TODO

Essential Commands

upsdrvctl stop
upsdrvctl start
systemctl status nut-server
systemctl restart nut-server
systemctl status nut-client
systemctl restart nut-client
upsc <id>@localhost

Additional Hints and Bibliography

It took a lot of RTFM and about a a half dozen web tutorials to figure this all out. Even though it turned out to be quite simple in the end, inconsistences abound. No doubt your mileage will vary with my writeup as well. My two best tips are:

  1. When in doubt, restart
  2. The users in upsd.user are not system user accounts. They are just roles in the NUT environment. This threw me for about my first hour.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment