Monitoring a UPS with a Raspberry Pi Zero W

Here on Whidbey Island in Washington state, we live in an area where the power goes out regularly. Heavy winds and/or snow/ice break breaches or fell trees onto power lines and… darkness. Sadness. Often, the power “blinks”, or at least we think it did, but have no way to verify. And we could start a graceful shutdown if we knew it had happened and could automate. After having recently installed a Raspberry Pi based Home Assistant solution in the house, It became even more important for me to have some indication of power outages, as well as monitor voltage. Over the years, I have accumulated 3 Costco Uninterruptible Power Supplies (UPSes):

I looked at the “PowerPanel” software the vendor (CyberPower) provides. They do have a version of this for Linux, but only x86/x64 architecture support: No ARM flavors. I was also aware of Network UPS Tools. At first I thought it was similar to a PC: I’d have to get the PowerPanel “driver” from the vendor, and then NUT could talk to it. WRONG! NUT has built-in drivers! And I found a great article, on one of my favorite sites: Medium. This should be easy, right?! While that article was well written, it just didn’t work for me. And I got frustrated following a blind script, when I wanted to understand how each of these settings fit together as a whole to accomplish my goal. I gave up that night. I knew the only way through this was to start from scratch and build up layer by layer… verifying it worked at each step, learning and understanding as I go. Then I would actually understand what I was doing.

With renewed caffeine in my veins the following evening, I tried again, starting from the ground up, reading both the relevant RPIZero W documentation and the NUT documentation. I built out my understanding of all of this, documented it, and got it running. This document and associated info is the result of those efforts. Best wishes and I hope you find this easier than I did originally!

– Mike Porter

NUT Overview

The Network UPS Tools (NUT) are built with a “layered” approach to interfacing with your UPS/s. From the “bottom” of the stack to the top, we find:

  • First is the hardware layer. This includes the proper cabling and power supply/s required.
  • Second Is the OS layer. Here this is mainly about getting the USB software at the OS/OS Driver level set up correctly.
  • Third is the “start” of NUT: the NUT “Driver” layer, aka “ups” (defined in ups.conf) In this layer, you define your UPS/s, and the parameters NUT needs to communicate
  • Fourth, is the “server” layer for NUT, aka “upsd “ (UPS daemon) (defined in upsd.conf, upsd.users, and hosts.cpnf) 
  • Finally, there are the various clients that call into the NUT server, there is a “server” layer. For example, with the command line interface (CLI) upsc. Or the built-in UPS Monitor process, aka “upsmon”. Or Nginx. Anything that calls into that NUT server.

In this documentation, I will walk you through:

  • Step 1: Prepare the Raspberry Pi Zero W Hardware
  • Step 2: Prepare and Configure Boot SD Card
  • Step 3: Prepare and Install NUT Packages
  • Step 4: Configure and Test NUT Driver
  • Step 5: Configure and Test NUT Server
  • Step 6: Configure and Test NUT Client – upsmon
  • Step 7: Configure and Test NUT Client – NGinx (OPTIONAL)

Step by Step Installation Overview

In order to try and lay out a “map” of the pieces and parts that are involved, I used the overview graphic, and added along the stack visuals the individual configuration files and the parameters I needed to set for this installation. The install process starts at the bottom of the graphic, and works its way to the top. We will verify each level before proceeding to the next step. 

Step 1: Prepare the Raspberry Pi Zero W Hardware

NOTE: This was the correct cable for my UPS. Yours might have a different interface, and/or require a different driver.

  1. Power Supply for Raspberry Pi Zero W: In this case, I can either use one of the two front USB 2.1 amp outlets that are backed by the UPS (which avoids consuming a UPS outlet on the back), Or use a separate supply. In this case, I’m using a separate cord solely because of the cable length.
  2. You can use any case that works for you. I happened to have this one from CanaKit lying around and it was perfect for my needs!
  3. You will need a “USB To Go” adapter. It will allow you to plug in the UPS cable to the Raspberry Pi. Specifically, it is a combination (for my UPS) of the following cables:
PictureDescription / Link
USB 2.0 Micro USB Male to USB Female OTG AdapterThis was the one I purchased.
USB 2.0 USB 2.0 A-Male to B-Male CordI have tons of these lying around

Step 2: Prepare and Configure Boot SD Card

  1. Image with the latest Raspberry Pi OS “Lite” version, 32-bit
  2. Eject SDCard & Re-Insert
  3. Then, add to the BOOT partition:
    1. Empty ssh file
    2. Copy prepared wpa_supplicant.conf file, containing:

country=US

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

update_config=1

network={

    ssid=”<<NameOfYourWIFI>>”     <- Change this to yours

    psk=”<<SuperSecretPassword>>” <- Change this to yours

    key_mgmt=WPA-PSK               

}

  1. Safely eject CD card and insert into Raspberry Pi
  1. BOOT
  2. Find YOUR Raspberry Pi’s IP address [mine=192.168.0.201]  <- Not sure how? Go here
  3. ssh pi@192.168.0.201                <- This is mine. Use YOUR IP address
  4. Passwd                              <- Please?! At LEAST change the 

                                                                                  root password!

  1. sudo raspi-config
    1. 1 System Options / S4 Hostname

[rpzw-ups01]        

  1. 5 Localisation Options / L1 Locale (2 screens)
    1. 1st screen, UNSELECT en_GB.UTF-8 UTF-8, then
    2. select en_US.UTF-8 UTF8
    3. 2nd screen, move from none to en_US.UTF-8
  2. 5 Localisation Options / L2 Timezone
    1. 1st screen, US
    2. 2nd screen, Pacific Ocean
  3. 5 Localisation Options / L4 WLAN country
    1. [US]
  1. REBOOT
  2. sudo apt-get update          <- ~ 1 minute
  3. sudo apt-get full-upgrade    <- ~ 17-18 minutes, YMMV
  4. Plug in UPS cable between the Raspberry Pi and the UPS
  5. REBOOT

You now have a Raspberry Pi Zero W with a full updated image! Congratulations! You should verify the OS & Hardware, and their ability to communicate with the UPS:

To verify: lsusb -t

NOTE: Class=Human Interface Device… so NUT driver should be usbhid-ups

Also note, Driver=dwc_otg… USB OTG is working!!! Several articles I found that assumed this was the problem. Not for me.

Step 3: Prepare and Install NUT Packages

  1. Create User & Group
    1. sudo adduser ups  [password: mikehome]
    2. sudo addgroup nut
    3. sudo adduser ups nut
  2. sudo apt-get install nut nut-client nut-server   <- ~ 2 minutes
  3. Create a place to store UPS state and data
    1. sudo mkdir -p /var/nut/run
    2. sudo chmod 0770 /var/nut/run
    3. sudo chown root:nut /var/nut/run

Step 4: Configure NUT Driver

  1. Edit /etc/nut/ups.conf to configure you UPS
    1. sudo nano /etc/nut/ups.conf
    2. Add:

[ups01]

     driver = usbhid-ups

     port = auto

     pollinterval = 15       <- Required See this.

     desc = “CP1500 AVR UPS”

  1. Start UPS Driver
    1. sudo systemctl start nut-driver

To verify: sudo systemctl status nut-driver

Step 5: Configure and Test NUT Server

  1. Edit /etc/nut/upsd.conf to configure you UPS Server
    1. sudo nano /etc/nut/upsd.conf
    2. Add:

LISTEN 127.0.0.1 3493

LISTEN ::1 3493

LISTEN 192.168.0.201 3493

  1. Uncomment :  STATEPATH /var/run/nut
  2. Edit /etc/nut/upsd.users
    1. sudo nano /etc/nut/upsd.users
    2. Uncomment:

[admin]

   password = mypass

   actions = SET

   instcmds = ALL

[upsmon]

   password  = pass

   upsmon master

  1. Edit /etc/nut/hosts.conf
    1. sudo nano /etc/nut/hosts.conf
    2. Add:

MONITOR ups01@192.168.0.201 “CP1500 AVR UPS”

  1. Edit /etc/nut/nut.conf
    1. sudo nano /etc/nut/nut.conf
    2. Set mode to:

MODE=standalone

  1. For security, you should consider locking down these files. For testing, it will work without this, but for any attempt to “secure” this server, this is a best practice:
    1. cd /etc/nut
    2. sudo chown root:nut upsd.conf upsd.users
    3. sudo chmod 0640 upsd.conf upsd.users
    4. cd ~
  2. REBOOT

To verify: sudo systemctl restart nut-server, then sudo systemctl status nut-server

Step 6: Configure and Test NUT Client – upsmon

  1. Edit /etc/nut/upsmon.conf to configure you UPS monitor client
    1. sudo nano/etc/nut/upsmon.conf
    2. Add: (must match /etc/nut/upsd.users upsmon user/pass)

MONITOR ups01@localhost 1 upsmon pass master

To verify: sudo systemctl restart nut-monitor, then sudo systemctl status nut-monitor

Step 7: Configure and Test NUT Client – Nginx (OPTIONAL)

While I wasn’t really looking for a web integration, this was so simple and lightweight, it was worth it. The idea came from: 

https://loganmarchione.com/2017/02/raspberry-pi-ups-monitor-with-nginx-web-monitoring/, although it assumed a working nginx install and assumed that folks know how the nginx.conf & sites-enabled/default works. I decided to flesh this out and it worked the first time.

  1. Install Nginx & NUT Integration
    1. sudo apt-get install nginx                    <- ~ 2 minutes
    2. sudo apt-get install nut-cgi fcgiwrap   <- ~ 2 minutes

NOTE: You should see this prompt pop up:

Go ahead and select Y, to install the package maintainer’s version. When the install is complete, you will then need to re-edit the /etc/nut/hosts.conf to add the line we inserted and got overwritten above in Step 5, Item 3, as shown here:

  1. Edit /etc/nut/hosts.conf
  1. sudo nano /etc/nut/hosts.conf
  2. Add:

MONITOR ups01@192.168.0.201 “CP1500 AVR UPS”

  1. Edit the /etc/nginx/sites-enabled/default file:
    1. sudo nano /etc/nginx/sites-enabled/default
    2. Modify these lines:

      listen 80 default_server;

        listen [::]:80 default_server;

To

        listen 8080 default_server;

        listen [::]:8080 default_server;

  1. Insert the blue portion only inside of the existing server {} section. DO NOT copy the red parts, and make sure you are pasting this INSIDE of the server {} section in this file! So where do I paste the blue stuff? Immediately after the last comments after the 8080 ports above: right under the line that read: # include snippets/snakeoil.conf;

server {

        location /nut {

        alias /usr/share/nut/www/;

        try_files $uri $uri/ /index.html;

        }

        location /cgi-bin/ {

                gzip off;

                root /usr/lib;

                include fastcgi_params;

                fastcgi_pass unix:/var/run/fcgiwrap.socket;

                fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;

        }

}

  1. Set the permissions and ownership properly, and start the services:
    1. sudo chmod 644 /etc/nut/hosts.conf
    2. sudo chmod 644 /etc/nut/*.html
    3. sudo chown www-data:www-data /usr/lib/cgi-bin/nut/*.cgi
    4. sudo systemctl restart fcgiwrap.service
    5. sudo systemctl restart fcgiwrap.socket
    6. sudo systemctl restart nginx

To verify: Go to a browser and enter: http://192.168.0.201:8080/nut (use your Raspberry Pi’s IP address) and you should see the following pages:

Click on the Statistics link and see: 

Click on the UPS link in the System column from above, and see: 

Going Further

Home Assistant Integration

I used the first “older, smaller” SD Card I found: a  SanDisk 32GB. After all of this software was installed and running, this was my df profile:


Document Information

Project UPS Monitor

Description Monitoring a CyberPower UPS with a Raspberry Pi Zero W, with Web Server &  Home Assistant Integration

Created 2021-02-17 by MIke Porter

Update History

Date/sChange/s
2021-02-17 – 2021-02-19Created graphics outlines and re-organized content. Generally cleaned it up considerably from the original notes.

Leave a Reply