Help with delaying script and implementing checks before completion

Hello everyone,

I was wondering if any of you could help me with this. I am an absolute beginner and don't know how to program, but I can follow a tutorial and tweak code sometimes. My understanding of programing is limitted to what for and while loops do, and how if then else logic works. That being said:

My setup is that I have a raspberry pi hooked up to an APC UPS via USB. When mains power goes out, the UPS triggers a "powerout" script which, in turn, sends me an email notification and calls me to my home number and my mobile phone using a SIP account, and a pre-recorded message plays when the call is answered.

I've got this working following various tutorials readily available on the internet.

However, there is a huge problem with this setup as when there is a little fluctuation in the mains power, the UPS turns to battery power in matter of miliseconds and the script is triggered regardless of the fact that power may return in a couple more miliseconds. Therefore I tend to receive calls alerting me of a power failure in the middle of the night and it is really inconvenient.

Therefore, I was hoping that you guys could help me tweak the script so that it waits for, let's say, 10 seconds, then checks again the UPS status, and, only if the UPS is still running on batteries after those 10 secs. places the phone call.

Here is the script which gets triggered when the power goes out:

# -*- coding: utf-8 -*-
#!/bin/sh
#
# This shell script if placed in /etc/apcupsd
# will be called by /etc/apcupsd/apccontrol when the UPS
# goes on batteries.
# We send an email message to root to notify him.
#

#HOSTNAME=`hostname`
#MSG="$HOSTNAME UPS $1 Power Failure !!!"
#
#(
#   echo "Subject: $MSG"
#   echo " "
#   echo "$MSG"
#   echo " "
#   /sbin/apcaccess status
#) | $APCUPSD_MAIL -s "$MSG" $SYSADMIN

# Added this new script from anites.com
#  ANG 8-23-2014

#!/usr/bin/env python

import smtplib
import email.mime.text
import syslog
import os

syslog.openlog('[UPS]')
def log(msg):
    syslog.syslog(str(msg))

GMAIL_ADDRESS = 'FROM ADDRESS GOES HERE'
GMAIL_PASSWORD = 'GMAIL PASSWORD GOES HERE'

from_email = GMAIL_ADDRESS
to_emails = ["RECIPIENT EMAIL ADDRESS GOES HERE"]  # email address

msg_subject = "MSG SUBJECT GOES HERE"
msg_text = "MSG TEXT GOES HERE"

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

retvalue=os.system("python /etc/apcupsd/alerthome.py")
print retvalue

#exit 0

As you can see, the calling home part is dealt with at the very end of the script by calling a python script (alerthome.py) and Ideally, I would like to pause execution of the script for 10 seconds before that line of code, then somehow check the ups status and, if the power is still out, place the call, but if the power has already returned, exit the script before calling alerthome.py

I imagine it will be easy to pause execution for 10 secs. but the part I really need help with is checking the UPS status.

Also, I guess it could be done by either parsing the results of "cat /var/log/apcupsd.events" where the output is something like:

[...]
2017-06-08 12:34:16 +0200  Power failure.
2017-06-08 12:34:22 +0200  Running on UPS batteries.
2017-06-08 12:37:04 +0200  Mains returned. No longer on UPS batteries.
2017-06-08 12:37:04 +0200  Power is back. UPS running on mains.
2017-06-08 12:37:14 +0200  Power failure.
2017-06-08 12:37:20 +0200  Running on UPS batteries.
2017-06-08 13:17:57 +0200  Mains returned. No longer on UPS batteries.
2017-06-08 13:17:57 +0200  Power is back. UPS running on mains.

As you can see those lasts ones were legit power outages but it is not always the case. What I would need to do is check whether the last line says either "Power failure." or "Running on UPS batteries." and, in that case place the call, or whether it reads "Mains returned. No longer on UPS batteries." or "Power is back. UPS running on mains." and in that case abort the phone call, but I don't know how to do it.

Another option would be to get the script to call "apcaccess" which produces an output like the following one:

APC      : 001,028,0733
DATE     : 2017-06-18 11:22:35 +0200  
HOSTNAME : raspberrypi
VERSION  : 3.14.12 (29 March 2014) debian
UPSNAME  : APC-phcy
CABLE    : USB Cable
DRIVER   : USB UPS Driver
UPSMODE  : Stand Alone
STARTTIME: 2017-01-16 20:40:23 +0100  
MODEL    : Smart-UPS 2200 
STATUS   : ONLINE 
BCHARGE  : 100.0 Percent
TIMELEFT : 372.0 Minutes
MBATTCHG : 5 Percent
MINTIMEL : 3 Minutes
MAXTIME  : 0 Seconds
ALARMDEL : 30 Seconds
BATTV    : 54.5 Volts
NUMXFERS : 28
XONBATT  : 2017-06-08 12:37:14 +0200  
TONBATT  : 0 Seconds
CUMONBATT: 11121 Seconds
XOFFBATT : 2017-06-08 13:17:57 +0200  
STATFLAG : 0x05000008
MANDATE  : 2016-02-23
SERIALNO : AS1608151711  
NOMBATTV : 48.0 Volts
FIRMWARE : UPS 09.3 / ID=18
END APC  : 2017-06-18 11:22:37 +0200

As you can see, the idea would be similar, I would need to parse this output (which I don't know how to do) to get the value of the "STATUS" line. which will either read ONLINE if the ups is running on mains power, and something else (I don't remember it right now) if it is running on batteries.

Thank you very much in advance. Any help will be much appreciated. Have a nice weekend :smiley:

Either method is possible; I'd prefer the second with apcaccess as it describes the actual status of the UPS while the log could be delayed / overwritten / have other, meaningless lines. You also seem to have it in your script but commented out. Unfortunately, I don't know apcaccess and so can't tell which info indicates a "power out" status and maybe more (mayhap on top of "STATUS: ONLINE", one of the bits in STATFLAG might give deeper insight?).
Assuming it were the STATUS line, and stealing from your script, try (untested, and not sure the sh shell will correctly run this, you may need e.g. bash )

sleep 10
/sbin/apcaccess status > /tmp/TMP$$
! grep -q "STATUS   : ONLINE" /tmp/TMP$$ && echo mail -s $MSG  $SYSADMIN </tmp/TMP$$
rm /tmp/TMP$$

Remove the echo when happy with the result.

Final comment: the "shebang" in your script #!/bin/sh needs to be in the very first line to become effective, i.e. determines the shell to be run to execute your code.

1 Like

Thank you sooo much for your prompt response.

I'll surely give it a try and let you know how it went although I might not be able to fully test it for a couple of weeks since I have this set up in a remote location and I need to be able to unplug the ups to test it.

Thanks again :wink:

---------- Post updated 19-06-17 at 02:19 PM ---------- Previous update was 18-06-17 at 02:29 PM ----------

Thank you very much @RudiC it works perfectrly well!

I forgot to mention that the main script was a python script and I could not integrate your code directly. Instead, I have placed it on a separate .sh which handles the logic for either calling or not calling based on the status of the UPS after 10 seconds and I call that one from the main script.

Thanks again:)