|CyberPower LCD "Smart" UPS|
Short Story of Education the Hard Way
(note to reader: If you are just interested just in the UPS monitoring details, skip ahead...)
What are we monitoring with the UPS? Well, we have a small chest style deep freezer in our garage. We store steaks and other expensive meats in it. Anytime there is an excellent sale, my wife stocks up on meats. A few months ago, something caused the GFCI to trip, and we didn't notice until a few days latter when the rancid smell reached us. We estimated over $500 worth of meat lost in one fell swoop. So the wife tasked me with coming up with a solution to avoid a repeat in the future.
So my solution was to use a "smart" UPS, one that I could actively read the metrics from, and send a email notification in the event of a power failure. The second benefit, or so I had thought, is the UPS could keep the fridge powered for short term power loss. I already have a CyberPower UPS connected to my file server, and it works great for battery backed power outages, monitoring battery reserves, and shutting down the server if the reserves deplete.
A bit of painful education. I wanted to make sure I purchased the "right" size of UPS, so I pulled out my Kill A Watt power meter and connected it to the refrigerator. During the occasional freeze cycle it appeared to pull about 460-490 Watts, and lasted less than a minute. At idle, it rated closer to 10 Watts. I rationalized that the UPS should be able to handle a few of these cycles before failing out.
When I looked online for consumer UPS, a majority measured in VA. So how does one get VA from Watts? Well, I cheated. The Kill A Watt also has a VA meter, so I selected that, and it showed around 560-580 VA. I then found a 600 VA CyberPower UPS. I admit that I realized 580 to 600 is not a lot of wiggle room, but the next battery size up jumped quite a bit in price. I crossed my fingers and ordered it. I should have done a bit more research.
The UPS arrived and I connected it to the freezer. The freezer's motor kicked on and the UPS went offline. Clearly I was wrong.
When I actually searched for VA to Watt conversion calculations online, I found that a good estimate is closer to 50% - 60%. I would have needed almost 800 VA to be on the safe side. Even this may not have been sufficient, as I learned that the initial motor spike can be quite high.
Lesson learned: You would probably need an industrial sized UPS to battery back a simple freezer.
Well, the battery protection was a bust, but at least I could still use the UPS to monitor for a power failure. Unfortunately, had I known this, I could have saved a few bucks, and just purchased the cheaper smaller "smart" UPS. The Raspberry Pi could run for days off even the smallest one.
So onto the technical stuff...
The setup. The Raspberry Pi lives out in the garage, on top of the deep freezer. I have a USB powered hub connected to the UPS, in one of the battery backed up slots. The USB hub then powers the Raspberry Pi (to avoid having to waste two power adapters). To talk to the USB hub I have a second USB cable connected to the data side of the Pi. To connect to the network, I have a WiFi dongle connected to the USB powered hub. To try and keep it as clean as possible, I have Raspberry Pi inside a plastic container, with holes for air flow. Finally, to get as much use of the Raspberry Pi, it also serves double duty and controls/monitors the garage door.
There are numerous "UPS" monitoring packages available, but the one I have had the most success with, is the one from APC called "apcupsd - APC UPS Power Management (daemon)". Although it was designed for APC branded UPS, it works quite well with numerous other UPS.
I tend to purchase the CyperPower UPS line with the LCD display. I am a sucker for the LCD that provides quick visual metrics, of the UPS. The CyperPower UPS connects to a PC through USB and works well with the apcupsd daemon.
Luckily the package is already included in the Raspbian repositories, so installation is this simple:
# apcupsd installation apt-get update apt-get install apcupsd
The next step is to modify the configuration scripts, so apcupsd can find the UPS. Use your editor and modify /etc/apcupsd/apcupsd.conf with the following changes.
# vim /etc/apcupsd/apcupsd.conf #UPSCABLE smart UPSCABLE usb #UPSTYPE apcsmart UPSTYPE usb #DEVICE /dev/ttyS0 DEVICE
These changes tell apcupsd that the UPS is connected via USB.
Next we want to test our connection to the UPS. This can be achieved with the 'acptest' program.
# apctest 2013-09-14 14:49:10 apctest 3.14.10 (13 September 2011) debian Checking configuration ... Attached to driver: usb sharenet.type = Network & ShareUPS Disabled cable.type = USB Cable mode.type = USB UPS Driver Setting up the port ... Doing prep_device() ... You are using a USB cable type, so I'm entering USB test mode Hello, this is the apcupsd Cable Test program. This part of apctest is for testing USB UPSes. Getting UPS capabilities...SUCCESS
If you see the "SUCCESS", everything is great. There isn't much here that you will want to change, so let's quit this program and start the apcupsd daemon.
First, enable the service:
# vim /etc/default/apcupsd ISCONFIGURED=yes
Next, restart the service:
# service apcupsd restart Starting UPS power management: apcupsd.
To have apcupsd auto start at boot:
# update-rc.d apcupsd defaults
Next, we can query the UPS to see the various metrics available to us using the 'apcaccess' application. The properties that apcupsd supports is quite large, and each UPS uses different properties, so your results may vary.
# apcaccess APC : 001,032,0781 DATE : 2013-09-14 14:54:31 -0600 HOSTNAME : pi-ups VERSION : 3.14.10 (13 September 2011) debian UPSNAME : pi-ups CABLE : USB Cable DRIVER : USB UPS Driver UPSMODE : Stand Alone STARTTIME: 2013-09-14 14:54:30 -0600 MODEL : UPS CP600 STATUS : ONLINE LINEV : 120.0 Volts LOADPCT : 0.0 Percent Load Capacity BCHARGE : 100.0 Percent TIMELEFT : 82.0 Minutes MBATTCHG : 5 Percent MINTIMEL : 3 Minutes MAXTIME : 0 Seconds OUTPUTV : 120.0 Volts DWAKE : 000 Seconds LOTRANS : 100.0 Volts HITRANS : 140.0 Volts ALARMDEL : 30 seconds NUMXFERS : 0 TONBATT : 0 seconds CUMONBATT: 0 seconds XOFFBATT : N/A SELFTEST : OK STATFLAG : 0x07000008 Status Flag SERIALNO : AEC7101.251 NOMINV : 120 Volts NOMPOWER : 340 Watts END APC : 2013-09-14 14:55:02 -0600
Now that we can query the UPS, let's work with the apcupsd "event" scripts. This is how we will get an email sent when a power failure occurs. The event scripts can all be found in the /etc/apcupsd/ folder. All events are controlled by the /etc/apcupsd/apccontrol master script, so take a quick peak there if you want to know what else can be modified.
The two scripts we are interested in are the "onbattery" and "offbattery" scripts. The "onbattery" is triggered during a power failure, and "offbattery" is triggered when power returns.
By default these scripts are just simple bash scripts that send a message to the PC's console and send a local email. Instead of using local mail, we will use Gmail to send our notification, and write the script in Python. One other side benefit of having these scripts written in Python, is I can also integrate Xively and then update Xively with the various data points of my UPS.
Let's backup the current scripts, before modifying them:
cp /etc/apcupsd/onbattery /etc/apcupsd/onbattery.original cp /etc/apcupsd/offbattery /etc/apcupsd/offbattery.original
This is the powery failure script I use:
#!/usr/bin/env python import smtplib import email.mime.text import syslog syslog.openlog('[UPS]') def log(msg): syslog.syslog(str(msg)) GMAIL_ADDRESS = 'firstname.lastname@example.org' GMAIL_PASSWORD = 'xxx' from_email = GMAIL_ADDRESS to_emails = ["email@example.com"] # cell phone address msg_subject = "ALERT: UPS Power Failure" msg_text = "Auto Notification" log(msg_subject) msg = email.mime.text.MIMEText(msg_text) msg['Subject'] = msg_subject msg['From'] = from_email msg['To'] = ", ".join(to_emails) s = smtplib.SMTP_SSL('smtp.gmail.com', '465') s.login(GMAIL_ADDRESS, GMAIL_PASSWORD) s.sendmail(from_email, to_emails, msg.as_string()) s.quit()
And for when power returns:
#!/usr/bin/env python import smtplib import email.mime.text import syslog syslog.openlog('[UPS]') def log(msg): syslog.syslog(str(msg)) GMAIL_ADDRESS = 'firstname.lastname@example.org' GMAIL_PASSWORD = 'xxx' from_email = GMAIL_ADDRESS to_emails = ["email@example.com"] # cell phone address msg_subject = "OK: UPS Power Recovered" msg_text = "Auto Notification" log(msg_subject) msg = email.mime.text.MIMEText(msg_text) msg['Subject'] = msg_subject msg['From'] = from_email msg['To'] = ", ".join(to_emails) s = smtplib.SMTP_SSL('smtp.gmail.com', '465') s.login(GMAIL_ADDRESS, GMAIL_PASSWORD) s.sendmail(from_email, to_emails, msg.as_string()) s.quit()
Finally, we test the scripts locally to make sure the email notification is sent. If everything looks good, test a power failure by disconnecting the UPS from the wall. Within a few seconds, the apcupsd daemon should trigger the onbattery event and our email should be sent. Now we will immediately know the next time the UPS looses wall power.
So, what if the power loss is not local to just the UPS? An external monitoring service could be used. Something like Nagios running from the cloud works great for this, but I will save that for another time.